feat: author first playable mystery
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
import { expect, test, type Page } from '@playwright/test'
|
||||
|
||||
async function waitForSave(page: Page, action: () => Promise<void>) {
|
||||
const response = page.waitForResponse(candidate => candidate.request().method() === 'PUT' && candidate.url().includes('/api/levels/') && candidate.ok())
|
||||
await action()
|
||||
await response
|
||||
}
|
||||
|
||||
async function classify(page: Page, name: string, kind: 'PERSON' | 'ORGANIZATION', summary: string) {
|
||||
await page.getByRole('button', { name: 'BRIEF', exact: true }).click()
|
||||
const concept = page.locator('.brief-concepts section').filter({ hasText: name })
|
||||
await concept.getByRole('button', { name: kind, exact: true }).click()
|
||||
await page.getByLabel('Party summary').fill(summary)
|
||||
await waitForSave(page, () => page.getByRole('button', { name: 'SAVE DOSSIER', exact: true }).click())
|
||||
}
|
||||
|
||||
async function createEvent(page: Page, title: string, narrative: string, occurredAt: string, sources: string[]) {
|
||||
await page.getByRole('button', { name: 'NEW EVENT', exact: true }).click()
|
||||
await page.getByLabel('Event title').fill(title)
|
||||
await page.getByLabel('Event narrative').fill(narrative)
|
||||
await page.getByLabel('Occurred at').fill(occurredAt)
|
||||
for (const source of sources) {
|
||||
await page.locator('.event-support-list .folder-member').filter({ hasText: source }).locator('input[type="checkbox"]').check()
|
||||
}
|
||||
await waitForSave(page, () => page.getByRole('button', { name: 'SAVE EVENT', exact: true }).click())
|
||||
}
|
||||
|
||||
test('a cloned Glass Harbor level can be solved without modifying its template', async ({ page, request }) => {
|
||||
const levels = await (await request.get('/api/levels')).json() as { id: string }[]
|
||||
const playable = levels.find(level => level.id.startsWith('glass-harbor-case-'))
|
||||
if (!playable) throw new Error('Glass Harbor playable clone was not seeded')
|
||||
await page.goto(`/?level=${playable.id}`)
|
||||
await expect(page.getByRole('heading', { name: 'The Glass Harbor Diversion' })).toBeVisible()
|
||||
await expect(page.locator('.evidence-card.folder')).toHaveCount(3)
|
||||
await expect(page.locator('.timeline .marker')).toHaveCount(8)
|
||||
|
||||
await classify(page, 'Mara Voss', 'PERSON', 'Restoration project officer who sponsored the Warehouse 3 access pass.')
|
||||
await classify(page, 'Elias Vale', 'PERSON', 'Driver of H&F 14; transported CO-771 to Warehouse 3.')
|
||||
await classify(page, 'Greyhaven Preservation Society', 'ORGANIZATION', 'Commissioned the North Quay restoration and owned the missing lens.')
|
||||
await classify(page, 'Calder Optical Works', 'ORGANIZATION', 'Supplier that released sealed crate CO-771 to the approved carrier.')
|
||||
await classify(page, 'Harbor & Fell Logistics', 'ORGANIZATION', 'Approved carrier whose vehicle delivered the crate to the wrong address.')
|
||||
await classify(page, 'Voss Antiquities Ltd', 'ORGANIZATION', 'Mara Voss’s company, based at Warehouse 3 and named as auction consignor.')
|
||||
|
||||
await createEvent(page, 'CO-771 left Calder for North Quay', 'Calder released the sealed lens to Elias Vale in H&F 14 with no alternate delivery authority.', '1987-10-16T16:40', ['Carrier Dispatch Manifest'])
|
||||
await createEvent(page, 'CO-771 was diverted to Warehouse 3', 'Mara Voss sponsored access; H&F 14 entered loaded and left empty. The next-day memorandum was retrospective.', '1987-10-17T22:08', ['Old Glass Harbor Gate Ledger', 'Warehouse 3 Security Photograph', 'Delivery Redirection Memorandum'])
|
||||
await createEvent(page, 'Voss Antiquities offered the lens for sale', 'The auction description and seller reference identify the missing assembly and the company that stood to profit.', '1987-10-24T12:00', ['Company Register Extract', 'Meridian Maritime Auction'])
|
||||
|
||||
page.once('dialog', dialog => dialog.accept('The redirection memorandum was written after the crate had already moved.'))
|
||||
await waitForSave(page, () => page.getByRole('button', { name: 'NEW NOTE', exact: true }).click())
|
||||
|
||||
const mara = page.locator('.evidence-card.party').filter({ has: page.getByRole('heading', { name: 'Mara Voss', exact: true }) })
|
||||
const movement = page.locator('.evidence-card.folder').filter({ hasText: 'MOVEMENT RECORDS' })
|
||||
await mara.click()
|
||||
await page.getByRole('button', { name: 'CONNECT', exact: true }).click()
|
||||
await waitForSave(page, () => movement.click())
|
||||
|
||||
await page.reload()
|
||||
await expect(page.locator('.evidence-card.party')).toHaveCount(6)
|
||||
await expect(page.locator('.evidence-card.event')).toHaveCount(3)
|
||||
await expect(page.locator('.evidence-card.note')).toHaveCount(1)
|
||||
await expect(page.locator('.story-strip')).toContainText('CO-771 was diverted to Warehouse 3')
|
||||
await expect(page.locator('.event-support-lines line')).toHaveCount(6)
|
||||
await expect(page.locator('.connections path')).toHaveCount(1)
|
||||
|
||||
const template = await (await request.get('/api/templates')).json() as { slug: string; currentVersion: number }[]
|
||||
expect(template).toContainEqual(expect.objectContaining({ slug: 'glass-harbor', currentVersion: 1 }))
|
||||
const authoringLevels = levels.filter(level => level.id.startsWith('glass-harbor-authoring-'))
|
||||
expect(authoringLevels).toHaveLength(1)
|
||||
const untouched = await (await request.get(`/api/levels/${authoringLevels[0].id}?edit=1`)).json()
|
||||
expect(untouched.evidence.filter((item: { type: string }) => item.type === 'party')).toHaveLength(0)
|
||||
expect(untouched.evidence.filter((item: { type: string }) => item.type === 'event')).toHaveLength(0)
|
||||
expect(untouched.brief.concepts.every((concept: { resolvedPartyExhibitId?: string }) => !concept.resolvedPartyExhibitId)).toBe(true)
|
||||
})
|
||||
Reference in New Issue
Block a user