From 27dfc2793eefddd41deaa50d3fcd2abfe2e1af08 Mon Sep 17 00:00:00 2001 From: Chris Bayliss Date: Thu, 24 Sep 2026 09:48:39 +1000 Subject: [PATCH 1/6] adds Playwright setup adds e2e tests for `/` and `/issues` --- .github/workflows/playwright.yml | 38 ++++++++ .gitignore | 8 ++ package-lock.json | 46 ++++++++++ package.json | 2 + playwright.config.ts | 70 ++++++++++++++ tests/homepage.spec.ts | 74 +++++++++++++++ tests/issues.spec.ts | 151 +++++++++++++++++++++++++++++++ 7 files changed, 389 insertions(+) create mode 100644 .github/workflows/playwright.yml create mode 100644 playwright.config.ts create mode 100644 tests/homepage.spec.ts create mode 100644 tests/issues.spec.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..cf6f223 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,38 @@ +name: Playwright Tests +on: + push: + branches: [ ampscz ] + pull_request: + branches: [ ampscz ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + - uses: actions/cache@v4 + with: + # See here for caching with `yarn`, `bun` or other package managers https://github.com/actions/cache/blob/main/examples.md or you can leverage caching with actions/setup-node https://github.com/actions/setup-node + path: | + ~/.npm + ${{ github.workspace }}/.next/cache + # Generate a new cache whenever packages or source files change. + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }} + # If source files changed but packages didn't, rebuild from a prior cache. + restore-keys: | + ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}- + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v4 + if: ${{ !cancelled() }} + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore index 5ef6a52..546c174 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,11 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts + +# Playwright +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +/playwright/.auth/ diff --git a/package-lock.json b/package-lock.json index 66b7d03..4c26709 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", + "@playwright/test": "^1.63.0", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/pg": "^8.11.11", @@ -2261,6 +2262,22 @@ "node": ">=12.4.0" } }, + "node_modules/@playwright/test": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.63.0.tgz", + "integrity": "sha512-oxMK4vllB9RK5NQ2l1pq1IfOf2AvnEuj/vYGDj0H2nMtmtZpKtCwt/l00GEO6xjGfpBNAvjovvYdCm50dRQkpQ==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", @@ -8196,6 +8213,35 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz", + "integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==", + "devOptional": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.63.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.63.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz", + "integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", diff --git a/package.json b/package.json index 9ff3f6f..1afdf2f 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "build": "next build", "start": "next start", "lint": "next lint", + "test:e2e": "playwright test", "preview": "next build && next start" }, "dependencies": { @@ -49,6 +50,7 @@ }, "devDependencies": { "@eslint/eslintrc": "^3", + "@playwright/test": "^1.63.0", "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/pg": "^8.11.11", diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..3e43956 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,70 @@ +import {defineConfig, devices} from '@playwright/test'; + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './tests', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('')`. */ + baseURL: 'http://localhost:3000', + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: {...devices['Desktop Chrome']}, + }, + + { + name: 'firefox', + use: {...devices['Desktop Firefox']}, + }, + + { + name: 'webkit', + use: {...devices['Desktop Safari']}, + }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + webServer: { + command: 'npm run preview', + url: 'http://localhost:3000', + reuseExistingServer: !process.env.CI, + }, +}); diff --git a/tests/homepage.spec.ts b/tests/homepage.spec.ts new file mode 100644 index 0000000..2f545ee --- /dev/null +++ b/tests/homepage.spec.ts @@ -0,0 +1,74 @@ +import {expect, test} from '@playwright/test'; + +test.describe("landing page", async () => { + test('test', async ({page}) => { + await page.goto('/'); + await expect(page.getByRole('link', {name: 'Home'})).toBeVisible(); + await expect(page.getByRole('link', {name: 'AV QC Portal v0.2.0'})).toBeVisible(); + await expect(page.getByRole('main')).toMatchAriaSnapshot(` + - heading "๐Ÿ‘‹ Welcome to AV QC Portal" [level=2] + - paragraph: + - text: This web portal is actively being developed as a companion to the + - link "AV QC pipeline": + - /url: https://github.com/dptools/dpinterview + - text: project, designed to streamline audiovisual quality control processes. + - link "๐Ÿ” Quality Control Monitor and manage quality issues in your audio/video recordings with more intuitive interfaces.": + - /url: /issues + - heading "๐Ÿ” Quality Control" [level=4] + - paragraph: Monitor and manage quality issues in your audio/video recordings with more intuitive interfaces. + - link "๐Ÿ“Š Real-time Monitoring Track performance metrics and health indicators of your audio/video processing systems in real time. [Superset]": + - /url: http://localhost:8088 + - heading "๐Ÿ“Š Real-time Monitoring" [level=4] + - paragraph: Track performance metrics and health indicators of your audio/video processing systems in real time. [Superset] + - paragraph: + - text: This project is under active development. Check back for updates or contribute on + - link "GitHub": + - /url: https://github.com/dheshanm/dpinterview-web + - text: . + `); + await expect(page.locator('body')).toMatchAriaSnapshot(` + - text: Navigation + - list: + - listitem: + - link "Issues": + - /url: /issues + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Interviews": + - /url: /interviews + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Audio Journals": + - /url: /journals + - img + - text: "" + `); + await expect(page.locator('body')).toMatchAriaSnapshot(` + - link "AV QC Portal v0.2.0": + - /url: / + - img + - text: "" + `); + await expect(page.locator('body')).toMatchAriaSnapshot(` + - list: + - listitem: + - link "GitHub": + - /url: https://github.com/dheshanm/dpinterview-web + - img + - text: "" + - listitem: + - link "Superset": + - /url: http://localhost:8088 + - img + - text: "" + `); + }); +}); \ No newline at end of file diff --git a/tests/issues.spec.ts b/tests/issues.spec.ts new file mode 100644 index 0000000..8f9253e --- /dev/null +++ b/tests/issues.spec.ts @@ -0,0 +1,151 @@ +import {expect, test} from '@playwright/test'; + +test.describe("issues page", async () => { + test.beforeEach(async ({page}) => { + await page.goto("/issues"); + }); + + test('test main content', async ({page}) => { + await expect(page.getByRole('main')).toMatchAriaSnapshot(` + - heading "Issues" [level=2] + - paragraph: This section covers various issues raised / detected by the AV QC pipeline. + - link "๐Ÿ“น Multi-Part Interviews Mark parts of the interview to process, or ignore.": + - /url: /issues/multiPart + - heading "๐Ÿ“น Multi-Part Interviews" [level=4] + - paragraph: Mark parts of the interview to process, or ignore. + - link "๐Ÿ“น Multi-Combined Audio Interviews Help identify the audio files that should be transcribed (and what should be ignored)": + - /url: /issues/multiCombinedAudio + - heading "๐Ÿ“น Multi-Combined Audio Interviews" [level=4] + - paragraph: Help identify the audio files that should be transcribed (and what should be ignored) + - link "๐ŸŽง Unlabelled Diarized Audio Label unlabelled audio files with the correct roles, for further downstream processing.": + - /url: /issues/unlabelledAudio + - heading "๐ŸŽง Unlabelled Diarized Audio" [level=4] + - paragraph: Label unlabelled audio files with the correct roles, for further downstream processing. + - link "๐Ÿ“ Missing Interviews List interviews with Runsheets marked as conducted, but no data associated with them.": + - /url: /issues/missing + - heading "๐Ÿ“ Missing Interviews" [level=4] + - paragraph: List interviews with Runsheets marked as conducted, but no data associated with them. + - link "๐Ÿ“œ Missing Transcripts List interviews with video / audio data, but no associated transcripts.": + - /url: /issues/noTranscript + - heading "๐Ÿ“œ Missing Transcripts" [level=4] + - paragraph: List interviews with video / audio data, but no associated transcripts. + - link "๐Ÿ“œ Missing Runsheets List interviews with video / audio data, but no associated runsheets.": + - /url: /issues/noRunsheet + - heading "๐Ÿ“œ Missing Runsheets" [level=4] + - paragraph: List interviews with video / audio data, but no associated runsheets. + - link "๐ŸŽ™๏ธ Failed Audio QC Combined audio that failed pre-transcription QC (silence, clipping, voice activity) and is not being transcribed.": + - /url: /issues/audioQcFailed + - heading "๐ŸŽ™๏ธ Failed Audio QC" [level=4] + - paragraph: Combined audio that failed pre-transcription QC (silence, clipping, voice activity) and is not being transcribed. + - link "๐Ÿ“ค Pending Transcription Push Audio that passed QC but has not yet been pushed to TranscribeMe.": + - /url: /issues/pendingTranscriptionPush + - heading "๐Ÿ“ค Pending Transcription Push" [level=4] + - paragraph: Audio that passed QC but has not yet been pushed to TranscribeMe. + - link "โณ Awaiting Vendor Transcription Audio pushed to TranscribeMe with no transcript delivered back yet.": + - /url: /issues/awaitingVendorTranscription + - heading "โณ Awaiting Vendor Transcription" [level=4] + - paragraph: Audio pushed to TranscribeMe with no transcript delivered back yet. + - link "๐Ÿ“ฅ Transcript Not Imported Transcripts delivered by TranscribeMe that have not yet appeared in transcript_files.": + - /url: /issues/transcriptNotImported + - heading "๐Ÿ“ฅ Transcript Not Imported" [level=4] + - paragraph: Transcripts delivered by TranscribeMe that have not yet appeared in transcript_files. + - link "๐Ÿงพ Pipeline Failures Errors raised across pipeline stages/crawlers, with occurrence counts and resolution tracking.": + - /url: /issues/pipelineFailures + - heading "๐Ÿงพ Pipeline Failures" [level=4] + - paragraph: Errors raised across pipeline stages/crawlers, with occurrence counts and resolution tracking. + - link "๐Ÿ”— Runsheet Match Match malformed interview files (datetime_parse failures) to missing runsheet entries, subject by subject.": + - /url: /issues/runsheetMatch + - heading "๐Ÿ”— Runsheet Match" [level=4] + - paragraph: Match malformed interview files (datetime_parse failures) to missing runsheet entries, subject by subject. + - link "๐Ÿ“‹ Override Ledger Audit trail of manual overrides (audio QC bypasses, runsheet datetime matches) - which files were addressed and by what mechanism.": + - /url: /issues/overrideLedger + - heading "๐Ÿ“‹ Override Ledger" [level=4] + - paragraph: Audit trail of manual overrides (audio QC bypasses, runsheet datetime matches) - which files were addressed and by what mechanism. + - paragraph: This project is under active development. If you need more issues catalogued, please reach out to developers. + `); + }); + test('main + breadcrumbs snapshot', async ({page}) => { + await expect(page.locator('body')).toMatchAriaSnapshot(` + - text: Navigation + - list: + - listitem: + - link "Issues": + - /url: /issues + - img + - text: "" + - button "Toggle" [expanded]: + - img + - text: "" + - list: + - listitem: + - link "Multi-Part Interviews": + - /url: /issues/multiPart + - listitem: + - link "Multi-Combined Audio Files": + - /url: /issues/multiCombinedAudio + - listitem: + - link "Unlabelled Audio": + - /url: /issues/unlabelledAudio + - listitem: + - link "Missing Interviews": + - /url: /issues/missing + - listitem: + - link "Missing Runsheets": + - /url: /issues/noRunsheet + - listitem: + - link "Missing Transcripts": + - /url: /issues/noTranscript + - listitem: + - link "Failed Audio QC": + - /url: /issues/audioQcFailed + - listitem: + - link "Pending Transcription Push": + - /url: /issues/pendingTranscriptionPush + - listitem: + - link "Awaiting Vendor Transcription": + - /url: /issues/awaitingVendorTranscription + - listitem: + - link "Transcript Not Imported": + - /url: /issues/transcriptNotImported + - listitem: + - link "Pipeline Failures": + - /url: /issues/pipelineFailures + - listitem: + - link "Runsheet Match": + - /url: /issues/runsheetMatch + - listitem: + - link "Override Ledger": + - /url: /issues/overrideLedger + - listitem: + - link "Interviews": + - /url: /interviews + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Audio Journals": + - /url: /journals + - img + - text: "" + `); + await expect(page.getByRole('main')).toMatchAriaSnapshot(` + - heading "๐Ÿ“น Multi-Part Interviews" [level=4] + - paragraph: Mark parts of the interview to process, or ignore. + `); + }); + + test('test issue type links', async ({page}) => { + await expect(page.getByRole('link', {name: '๐Ÿ“น Multi-Combined Audio'})).toBeVisible(); + await expect(page.getByRole('link', {name: '๐ŸŽง Unlabelled Diarized Audio'})).toBeVisible(); + await expect(page.getByRole('link', {name: '๐Ÿ“ Missing Interviews List'})).toBeVisible(); + await expect(page.getByRole('link', {name: '๐ŸŽ™๏ธ Failed Audio QC Combined'})).toBeVisible(); + await expect(page.getByRole('link', {name: '๐Ÿ“ค Pending Transcription Push'})).toBeVisible(); + await expect(page.getByRole('link', {name: 'โณ Awaiting Vendor'})).toBeVisible(); + await expect(page.getByRole('link', {name: '๐Ÿ“ฅ Transcript Not Imported'})).toBeVisible(); + await expect(page.getByRole('link', {name: '๐Ÿงพ Pipeline Failures Errors'})).toBeVisible(); + await expect(page.getByRole('link', {name: '๐Ÿ”— Runsheet Match Match'})).toBeVisible(); + await expect(page.getByRole('link', {name: '๐Ÿ“‹ Override Ledger Audit'})).toBeVisible(); + }); +}); \ No newline at end of file From 130c0d54aadef6dfe7e2c52c5575f9d7bbefc389 Mon Sep 17 00:00:00 2001 From: Chris Bayliss Date: Thu, 24 Sep 2026 11:06:51 +1000 Subject: [PATCH 2/6] adds interviews page e2e tests --- playwright.config.ts | 1 + tests/interviews.spec.ts | 94 +++++++++++++++++++++++++++++++++++++++ tests/pages/index.ts | 3 ++ tests/pages/interviews.ts | 43 ++++++++++++++++++ tests/playwright.ts | 16 +++++++ 5 files changed, 157 insertions(+) create mode 100644 tests/interviews.spec.ts create mode 100644 tests/pages/index.ts create mode 100644 tests/pages/interviews.ts create mode 100644 tests/playwright.ts diff --git a/playwright.config.ts b/playwright.config.ts index 3e43956..6073b45 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -21,6 +21,7 @@ export default defineConfig({ baseURL: 'http://localhost:3000', /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', + screenshot: 'only-on-failure', }, /* Configure projects for major browsers */ diff --git a/tests/interviews.spec.ts b/tests/interviews.spec.ts new file mode 100644 index 0000000..1e56069 --- /dev/null +++ b/tests/interviews.spec.ts @@ -0,0 +1,94 @@ +import {expect} from '@playwright/test'; +import {test} from "./playwright"; + +test.describe("Interviews page", async () => { + + test('breadcrumbs match snapshot', async ({interviews}) => { + await expect(interviews.main).toMatchAriaSnapshot(` + - button "Toggle Sidebar": + - img + - text: "" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home": + - /url: / + - listitem: + - link "Interviews" [disabled] + `); + }); + + test('sidebar matches snapshot', async ({interviews}) => { + await expect(interviews.sidebar).toMatchAriaSnapshot(` + - text: Navigation + - list: + - listitem: + - link "Issues": + - /url: /issues + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Interviews": + - /url: /interviews + - img + - text: "" + - button "Toggle" [expanded]: + - img + - text: "" + - list: + - listitem: + - link "Pending QC": + - /url: /interviews/qc/pending + - listitem: + - link "Completed QC": + - /url: /interviews/qc/completed + - listitem: + - link "Audio Journals": + - /url: /journals + - img + - text: "" + `); + }); + + test('alert text is present', async ({interviews}) => { + await expect(interviews.main).toMatchAriaSnapshot(` + - alert: + - paragraph: Please use Superset Dashboard to look at the Aggregated Interview data. + `); + }); + + test.describe("interviews table", async () => { + + test('info text is present', async ({interviews}) => { + await expect(interviews.main).toMatchAriaSnapshot(`- paragraph: /The following \\d+ interviews have been identified for processing\\./`); + }); + + test('controls are present', async ({interviews}) => { + await expect(interviews.selectColumnsButton).toBeVisible(); + await expect(interviews.filtersButton).toBeVisible(); + await expect(interviews.densityButton).toBeVisible(); + await expect(interviews.exportButton).toBeVisible(); + }); + + test('columns are present', async ({interviews}) => { + await expect(interviews.selectRowHeader).toBeVisible(); + await expect(interviews.nameColumn).toBeVisible(); + await expect(interviews.typeColumn).toBeVisible(); + await expect(interviews.subjectColumn).toBeVisible(); + await expect(interviews.studyColumn).toBeVisible(); + }); + + test('table footer is present', async ({interviews}) => { + await expect(interviews.main).toMatchAriaSnapshot(` + - paragraph: "Rows per page:" + - 'combobox /Rows per page: \\d+/' + - paragraph: /1โ€“\\d+ of \\d+/ + - button "Go to previous page" [disabled] + - button "Go to next page" + `); + }); + }); +}); \ No newline at end of file diff --git a/tests/pages/index.ts b/tests/pages/index.ts new file mode 100644 index 0000000..9ad0840 --- /dev/null +++ b/tests/pages/index.ts @@ -0,0 +1,3 @@ +import {InterviewsPageModel} from "./interviews"; + +export {InterviewsPageModel}; \ No newline at end of file diff --git a/tests/pages/interviews.ts b/tests/pages/interviews.ts new file mode 100644 index 0000000..61c2f11 --- /dev/null +++ b/tests/pages/interviews.ts @@ -0,0 +1,43 @@ +import {expect, Locator, Page} from '@playwright/test'; + + +export class InterviewsPageModel { + + readonly breadcrumbs: Locator; + readonly sidebar: Locator; + readonly main: Locator; + readonly page: Page; + readonly selectColumnsButton: Locator; + readonly filtersButton: Locator; + readonly densityButton: Locator; + readonly exportButton: Locator; + + readonly selectRowHeader : Locator; + readonly nameColumn: Locator; + readonly typeColumn: Locator; + readonly subjectColumn: Locator; + readonly studyColumn: Locator; + + constructor(page: Page) { + this.page = page; + this.breadcrumbs = page.locator('header'); + this.sidebar = page.locator('body'); + this.main = page.getByRole('main'); + + this.selectColumnsButton = page.getByRole('button', {name: 'Select columns'}); + this.filtersButton = page.getByRole('button', {name: 'Show filters'}); + this.densityButton = page.getByRole('button', {name: 'Density'}); + this.exportButton = page.getByRole('button', {name: 'Export'}); + this.selectRowHeader = page.getByRole('checkbox', {name: 'Select all rows'}); + + this.nameColumn = page.getByRole('columnheader', {name: 'Interview Name'}); + this.typeColumn = page.getByRole('columnheader', {name: 'Interview Type'}); + this.subjectColumn = page.getByRole('columnheader', {name: 'Subject ID'}); + this.studyColumn = page.getByRole('columnheader', {name: 'Study ID'}); + } + + async goto() { + await this.page.goto("/interviews"); + } + +} \ No newline at end of file diff --git a/tests/playwright.ts b/tests/playwright.ts new file mode 100644 index 0000000..842d2a3 --- /dev/null +++ b/tests/playwright.ts @@ -0,0 +1,16 @@ +import {test as baseTest} from '@playwright/test'; +import {InterviewsPageModel} from "./pages"; + + +interface PageFixtures { + interviews: InterviewsPageModel; +} + +export const test = baseTest.extend({ + interviews: async ({page}, use) => { + const interview = new InterviewsPageModel(page); + await interview.goto(); + use(interview); + // clean up + }, +}) \ No newline at end of file From 2917cae2687a3f905963065184922a0f0dced39a Mon Sep 17 00:00:00 2001 From: Chris Bayliss Date: Thu, 24 Sep 2026 11:49:15 +1000 Subject: [PATCH 3/6] feat: extracts ARIA snapshots into separate files feat: adds landing page fixture feat: updates landing page E2E tests to use fixture fix: adds ARIA landmark and label to app sidebar component to make it easier to find via 'getByRole' --- playwright.config.ts | 8 ++ src/app/layout.tsx | 2 +- src/components/app-sidebar.tsx | 2 +- ...page-breadcrumbs-match-snapshot-1.aria.yml | 7 ++ ...g-page-sidebar-matches-snapshot-1.aria.yml | 42 ++++++++++ .../landing-page-test-1.aria.yml | 28 +++++++ ...page-breadcrumbs-match-snapshot-1.aria.yml | 10 +++ ...s-page-sidebar-matches-snapshot-1.aria.yml | 49 +++++++++++ ...page-breadcrumbs-match-snapshot-1.aria.yml | 10 +++ ...s-page-sidebar-matches-snapshot-1.aria.yml | 42 ++++++++++ tests/homepage.spec.ts | 84 ++++--------------- tests/interviews.spec.ts | 50 +---------- tests/journals.spec.ts | 21 +++++ tests/pages/home.ts | 28 +++++++ tests/pages/index.ts | 10 ++- tests/pages/interviews.ts | 20 ++--- tests/pages/journals.ts | 16 ++++ tests/pages/page.ts | 31 +++++++ tests/playwright.ts | 21 ++++- 19 files changed, 347 insertions(+), 134 deletions(-) create mode 100644 tests/__snapshots__/homepage.spec.ts/landing-page-breadcrumbs-match-snapshot-1.aria.yml create mode 100644 tests/__snapshots__/homepage.spec.ts/landing-page-sidebar-matches-snapshot-1.aria.yml create mode 100644 tests/__snapshots__/homepage.spec.ts/landing-page-test-1.aria.yml create mode 100644 tests/__snapshots__/interviews.spec.ts/Interviews-page-breadcrumbs-match-snapshot-1.aria.yml create mode 100644 tests/__snapshots__/interviews.spec.ts/Interviews-page-sidebar-matches-snapshot-1.aria.yml create mode 100644 tests/__snapshots__/journals.spec.ts/Audio-journals-page-breadcrumbs-match-snapshot-1.aria.yml create mode 100644 tests/__snapshots__/journals.spec.ts/Audio-journals-page-sidebar-matches-snapshot-1.aria.yml create mode 100644 tests/journals.spec.ts create mode 100644 tests/pages/home.ts create mode 100644 tests/pages/journals.ts create mode 100644 tests/pages/page.ts diff --git a/playwright.config.ts b/playwright.config.ts index 6073b45..ae75537 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -23,6 +23,14 @@ export default defineConfig({ trace: 'on-first-retry', screenshot: 'only-on-failure', }, + expect: { + toHaveScreenshot: { + pathTemplate: '{testDir}/__screenshots__{/projectName}/{testFilePath}/{arg}{ext}', + }, + toMatchAriaSnapshot: { + pathTemplate: '{testDir}/__snapshots__/{testFilePath}/{arg}{ext}', + } + }, /* Configure projects for major browsers */ projects: [ diff --git a/src/app/layout.tsx b/src/app/layout.tsx index a4ac2e1..ee7798c 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -36,7 +36,7 @@ export default function RootLayout({ className={`${geistSans.variable} ${geistMono.variable} antialiased`} > - + {/* */} diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx index 3fdf088..32953c4 100644 --- a/src/components/app-sidebar.tsx +++ b/src/components/app-sidebar.tsx @@ -164,7 +164,7 @@ export function AppSidebar({ ...props }: React.ComponentProps) { } return ( - + diff --git a/tests/__snapshots__/homepage.spec.ts/landing-page-breadcrumbs-match-snapshot-1.aria.yml b/tests/__snapshots__/homepage.spec.ts/landing-page-breadcrumbs-match-snapshot-1.aria.yml new file mode 100644 index 0000000..dc9c2dc --- /dev/null +++ b/tests/__snapshots__/homepage.spec.ts/landing-page-breadcrumbs-match-snapshot-1.aria.yml @@ -0,0 +1,7 @@ +- button "Toggle Sidebar": + - img + - text: "" +- navigation "breadcrumb": + - list: + - listitem: + - link "Home" [disabled] \ No newline at end of file diff --git a/tests/__snapshots__/homepage.spec.ts/landing-page-sidebar-matches-snapshot-1.aria.yml b/tests/__snapshots__/homepage.spec.ts/landing-page-sidebar-matches-snapshot-1.aria.yml new file mode 100644 index 0000000..9491bcb --- /dev/null +++ b/tests/__snapshots__/homepage.spec.ts/landing-page-sidebar-matches-snapshot-1.aria.yml @@ -0,0 +1,42 @@ +- navigation "sidebar": + - list: + - listitem: + - link "AV QC Portal v0.2.0": + - /url: / + - img + - text: "" + - text: Navigation + - list: + - listitem: + - link "Issues": + - /url: /issues + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Interviews": + - /url: /interviews + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Audio Journals": + - /url: /journals + - img + - text: "" + - list: + - listitem: + - link "GitHub": + - /url: https://github.com/dheshanm/dpinterview-web + - img + - text: "" + - listitem: + - link "Superset": + - /url: http://localhost:8088 + - img + - text: "" + - button "Toggle Sidebar" \ No newline at end of file diff --git a/tests/__snapshots__/homepage.spec.ts/landing-page-test-1.aria.yml b/tests/__snapshots__/homepage.spec.ts/landing-page-test-1.aria.yml new file mode 100644 index 0000000..ecbd1e1 --- /dev/null +++ b/tests/__snapshots__/homepage.spec.ts/landing-page-test-1.aria.yml @@ -0,0 +1,28 @@ +- main: + - button "Toggle Sidebar": + - img + - text: "" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" [disabled] + - heading "๐Ÿ‘‹ Welcome to AV QC Portal" [level=2] + - paragraph: + - text: This web portal is actively being developed as a companion to the + - link "AV QC pipeline": + - /url: https://github.com/dptools/dpinterview + - text: project, designed to streamline audiovisual quality control processes. + - link "๐Ÿ” Quality Control Monitor and manage quality issues in your audio/video recordings with more intuitive interfaces.": + - /url: /issues + - heading "๐Ÿ” Quality Control" [level=4] + - paragraph: Monitor and manage quality issues in your audio/video recordings with more intuitive interfaces. + - link "๐Ÿ“Š Real-time Monitoring Track performance metrics and health indicators of your audio/video processing systems in real time. [Superset]": + - /url: http://localhost:8088 + - heading "๐Ÿ“Š Real-time Monitoring" [level=4] + - paragraph: Track performance metrics and health indicators of your audio/video processing systems in real time. [Superset] + - paragraph: + - text: This project is under active development. Check back for updates or contribute on + - link "GitHub": + - /url: https://github.com/dheshanm/dpinterview-web + - text: . + - region "Notifications alt+T" \ No newline at end of file diff --git a/tests/__snapshots__/interviews.spec.ts/Interviews-page-breadcrumbs-match-snapshot-1.aria.yml b/tests/__snapshots__/interviews.spec.ts/Interviews-page-breadcrumbs-match-snapshot-1.aria.yml new file mode 100644 index 0000000..b264ae1 --- /dev/null +++ b/tests/__snapshots__/interviews.spec.ts/Interviews-page-breadcrumbs-match-snapshot-1.aria.yml @@ -0,0 +1,10 @@ +- button "Toggle Sidebar": + - img + - text: "" +- navigation "breadcrumb": + - list: + - listitem: + - link "Home": + - /url: / + - listitem: + - link "Interviews" [disabled] \ No newline at end of file diff --git a/tests/__snapshots__/interviews.spec.ts/Interviews-page-sidebar-matches-snapshot-1.aria.yml b/tests/__snapshots__/interviews.spec.ts/Interviews-page-sidebar-matches-snapshot-1.aria.yml new file mode 100644 index 0000000..ecbe11b --- /dev/null +++ b/tests/__snapshots__/interviews.spec.ts/Interviews-page-sidebar-matches-snapshot-1.aria.yml @@ -0,0 +1,49 @@ +- navigation "sidebar": + - list: + - listitem: + - link "AV QC Portal v0.2.0": + - /url: / + - img + - text: "" + - text: Navigation + - list: + - listitem: + - link "Issues": + - /url: /issues + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Interviews": + - /url: /interviews + - img + - text: "" + - button "Toggle" [expanded]: + - img + - text: "" + - list: + - listitem: + - link "Pending QC": + - /url: /interviews/qc/pending + - listitem: + - link "Completed QC": + - /url: /interviews/qc/completed + - listitem: + - link "Audio Journals": + - /url: /journals + - img + - text: "" + - list: + - listitem: + - link "GitHub": + - /url: https://github.com/dheshanm/dpinterview-web + - img + - text: "" + - listitem: + - link "Superset": + - /url: http://localhost:8088 + - img + - text: "" + - button "Toggle Sidebar" \ No newline at end of file diff --git a/tests/__snapshots__/journals.spec.ts/Audio-journals-page-breadcrumbs-match-snapshot-1.aria.yml b/tests/__snapshots__/journals.spec.ts/Audio-journals-page-breadcrumbs-match-snapshot-1.aria.yml new file mode 100644 index 0000000..f228e76 --- /dev/null +++ b/tests/__snapshots__/journals.spec.ts/Audio-journals-page-breadcrumbs-match-snapshot-1.aria.yml @@ -0,0 +1,10 @@ +- button "Toggle Sidebar": + - img + - text: "" +- navigation "breadcrumb": + - list: + - listitem: + - link "Home": + - /url: / + - listitem: + - link "Audio Journals" [disabled] \ No newline at end of file diff --git a/tests/__snapshots__/journals.spec.ts/Audio-journals-page-sidebar-matches-snapshot-1.aria.yml b/tests/__snapshots__/journals.spec.ts/Audio-journals-page-sidebar-matches-snapshot-1.aria.yml new file mode 100644 index 0000000..9491bcb --- /dev/null +++ b/tests/__snapshots__/journals.spec.ts/Audio-journals-page-sidebar-matches-snapshot-1.aria.yml @@ -0,0 +1,42 @@ +- navigation "sidebar": + - list: + - listitem: + - link "AV QC Portal v0.2.0": + - /url: / + - img + - text: "" + - text: Navigation + - list: + - listitem: + - link "Issues": + - /url: /issues + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Interviews": + - /url: /interviews + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Audio Journals": + - /url: /journals + - img + - text: "" + - list: + - listitem: + - link "GitHub": + - /url: https://github.com/dheshanm/dpinterview-web + - img + - text: "" + - listitem: + - link "Superset": + - /url: http://localhost:8088 + - img + - text: "" + - button "Toggle Sidebar" \ No newline at end of file diff --git a/tests/homepage.spec.ts b/tests/homepage.spec.ts index 2f545ee..30e2d68 100644 --- a/tests/homepage.spec.ts +++ b/tests/homepage.spec.ts @@ -1,74 +1,18 @@ -import {expect, test} from '@playwright/test'; +import {expect} from '@playwright/test'; +import {test} from "./playwright"; test.describe("landing page", async () => { - test('test', async ({page}) => { - await page.goto('/'); - await expect(page.getByRole('link', {name: 'Home'})).toBeVisible(); - await expect(page.getByRole('link', {name: 'AV QC Portal v0.2.0'})).toBeVisible(); - await expect(page.getByRole('main')).toMatchAriaSnapshot(` - - heading "๐Ÿ‘‹ Welcome to AV QC Portal" [level=2] - - paragraph: - - text: This web portal is actively being developed as a companion to the - - link "AV QC pipeline": - - /url: https://github.com/dptools/dpinterview - - text: project, designed to streamline audiovisual quality control processes. - - link "๐Ÿ” Quality Control Monitor and manage quality issues in your audio/video recordings with more intuitive interfaces.": - - /url: /issues - - heading "๐Ÿ” Quality Control" [level=4] - - paragraph: Monitor and manage quality issues in your audio/video recordings with more intuitive interfaces. - - link "๐Ÿ“Š Real-time Monitoring Track performance metrics and health indicators of your audio/video processing systems in real time. [Superset]": - - /url: http://localhost:8088 - - heading "๐Ÿ“Š Real-time Monitoring" [level=4] - - paragraph: Track performance metrics and health indicators of your audio/video processing systems in real time. [Superset] - - paragraph: - - text: This project is under active development. Check back for updates or contribute on - - link "GitHub": - - /url: https://github.com/dheshanm/dpinterview-web - - text: . - `); - await expect(page.locator('body')).toMatchAriaSnapshot(` - - text: Navigation - - list: - - listitem: - - link "Issues": - - /url: /issues - - img - - text: "" - - button "Toggle": - - img - - text: "" - - listitem: - - link "Interviews": - - /url: /interviews - - img - - text: "" - - button "Toggle": - - img - - text: "" - - listitem: - - link "Audio Journals": - - /url: /journals - - img - - text: "" - `); - await expect(page.locator('body')).toMatchAriaSnapshot(` - - link "AV QC Portal v0.2.0": - - /url: / - - img - - text: "" - `); - await expect(page.locator('body')).toMatchAriaSnapshot(` - - list: - - listitem: - - link "GitHub": - - /url: https://github.com/dheshanm/dpinterview-web - - img - - text: "" - - listitem: - - link "Superset": - - /url: http://localhost:8088 - - img - - text: "" - `); + + test("breadcrumbs match snapshot", async ({home}) => { + await expect(home.breadcrumbs).toMatchAriaSnapshot(); + }); + + test("sidebar matches snapshot", async ({home}) => { + await expect(home.sidebar).toMatchAriaSnapshot(); }); + + test('main content matches snapshot', async ({home}) => { + await expect(home.main).toMatchAriaSnapshot(); + }); + }); \ No newline at end of file diff --git a/tests/interviews.spec.ts b/tests/interviews.spec.ts index 1e56069..9ba6dce 100644 --- a/tests/interviews.spec.ts +++ b/tests/interviews.spec.ts @@ -3,54 +3,12 @@ import {test} from "./playwright"; test.describe("Interviews page", async () => { - test('breadcrumbs match snapshot', async ({interviews}) => { - await expect(interviews.main).toMatchAriaSnapshot(` - - button "Toggle Sidebar": - - img - - text: "" - - navigation "breadcrumb": - - list: - - listitem: - - link "Home": - - /url: / - - listitem: - - link "Interviews" [disabled] - `); + test("breadcrumbs match snapshot", async ({interviews}) => { + await expect(interviews.breadcrumbs).toMatchAriaSnapshot(); }); - test('sidebar matches snapshot', async ({interviews}) => { - await expect(interviews.sidebar).toMatchAriaSnapshot(` - - text: Navigation - - list: - - listitem: - - link "Issues": - - /url: /issues - - img - - text: "" - - button "Toggle": - - img - - text: "" - - listitem: - - link "Interviews": - - /url: /interviews - - img - - text: "" - - button "Toggle" [expanded]: - - img - - text: "" - - list: - - listitem: - - link "Pending QC": - - /url: /interviews/qc/pending - - listitem: - - link "Completed QC": - - /url: /interviews/qc/completed - - listitem: - - link "Audio Journals": - - /url: /journals - - img - - text: "" - `); + test("sidebar matches snapshot", async ({interviews}) => { + await expect(interviews.sidebar).toMatchAriaSnapshot(); }); test('alert text is present', async ({interviews}) => { diff --git a/tests/journals.spec.ts b/tests/journals.spec.ts new file mode 100644 index 0000000..10d223f --- /dev/null +++ b/tests/journals.spec.ts @@ -0,0 +1,21 @@ +import {expect} from '@playwright/test'; +import {test} from "./playwright"; + +test.describe("Audio journals page", async () => { + // test("",async ()=>{}); + + test("breadcrumbs match snapshot", async ({journals}) => { + await expect(journals.breadcrumbs).toMatchAriaSnapshot(); + }); + + test("sidebar matches snapshot", async ({journals}) => { + await expect(journals.sidebar).toMatchAriaSnapshot(); + }); + + test.describe("main content", async () => { + test("is present",async ({journals})=>{ + await expect(journals.main).toBeInViewport(); + }); + }); + +}); \ No newline at end of file diff --git a/tests/pages/home.ts b/tests/pages/home.ts new file mode 100644 index 0000000..139ec16 --- /dev/null +++ b/tests/pages/home.ts @@ -0,0 +1,28 @@ +import {Locator, Page} from '@playwright/test'; +import {AbstractPageModel} from "./page"; + + +export class HomePageModel extends AbstractPageModel { + + static readonly path = "/"; + + readonly selectColumnsButton: Locator; + readonly filtersButton: Locator; + readonly densityButton: Locator; + readonly exportButton: Locator; + + readonly selectRowHeader: Locator; + readonly nameColumn: Locator; + readonly typeColumn: Locator; + readonly subjectColumn: Locator; + readonly studyColumn: Locator; + + constructor(page: Page) { + super(page); + } + + async goto() { + return this.page.goto(HomePageModel.path); + } + +} \ No newline at end of file diff --git a/tests/pages/index.ts b/tests/pages/index.ts index 9ad0840..4f603d3 100644 --- a/tests/pages/index.ts +++ b/tests/pages/index.ts @@ -1,3 +1,11 @@ +import {PageModel} from "./page"; +import {HomePageModel} from "./home"; import {InterviewsPageModel} from "./interviews"; +import {JournalsPage} from "./journals"; -export {InterviewsPageModel}; \ No newline at end of file +export { + HomePageModel, + InterviewsPageModel, + JournalsPage, + type PageModel, +}; \ No newline at end of file diff --git a/tests/pages/interviews.ts b/tests/pages/interviews.ts index 61c2f11..f05d70e 100644 --- a/tests/pages/interviews.ts +++ b/tests/pages/interviews.ts @@ -1,28 +1,24 @@ -import {expect, Locator, Page} from '@playwright/test'; +import {Locator, Page} from '@playwright/test'; +import {AbstractPageModel} from "./page"; -export class InterviewsPageModel { +export class InterviewsPageModel extends AbstractPageModel { + + static readonly path = "/interviews"; - readonly breadcrumbs: Locator; - readonly sidebar: Locator; - readonly main: Locator; - readonly page: Page; readonly selectColumnsButton: Locator; readonly filtersButton: Locator; readonly densityButton: Locator; readonly exportButton: Locator; - readonly selectRowHeader : Locator; + readonly selectRowHeader: Locator; readonly nameColumn: Locator; readonly typeColumn: Locator; readonly subjectColumn: Locator; readonly studyColumn: Locator; constructor(page: Page) { - this.page = page; - this.breadcrumbs = page.locator('header'); - this.sidebar = page.locator('body'); - this.main = page.getByRole('main'); + super(page); this.selectColumnsButton = page.getByRole('button', {name: 'Select columns'}); this.filtersButton = page.getByRole('button', {name: 'Show filters'}); @@ -37,7 +33,7 @@ export class InterviewsPageModel { } async goto() { - await this.page.goto("/interviews"); + return this.page.goto(InterviewsPageModel.path); } } \ No newline at end of file diff --git a/tests/pages/journals.ts b/tests/pages/journals.ts new file mode 100644 index 0000000..00eea63 --- /dev/null +++ b/tests/pages/journals.ts @@ -0,0 +1,16 @@ +import {Locator, Page} from "@playwright/test"; +import {AbstractPageModel} from "./page"; + +export class JournalsPage extends AbstractPageModel { + + constructor(page: Page) { + super(page); + } + + async goto() { + return this.page.goto("/journals") + } + + + +} \ No newline at end of file diff --git a/tests/pages/page.ts b/tests/pages/page.ts new file mode 100644 index 0000000..cd61bd0 --- /dev/null +++ b/tests/pages/page.ts @@ -0,0 +1,31 @@ +import {Locator, Page, Response} from "@playwright/test"; + + +export interface PageModel { + + readonly page: Page; + readonly breadcrumbs: Locator; + readonly sidebar: Locator; + readonly main: Locator; + + goto() : Promise + +} + +export abstract class AbstractPageModel implements PageModel { + readonly breadcrumbs: Locator; + readonly main: Locator; + readonly sidebar: Locator; + readonly page: Page; + + protected constructor(page: Page) { + this.page = page; + this.breadcrumbs = page.locator('header'); + this.sidebar = page.getByRole("navigation",{name:"sidebar"}); + this.main = page.getByRole('main'); + + } + + abstract goto(): Promise; + +} \ No newline at end of file diff --git a/tests/playwright.ts b/tests/playwright.ts index 842d2a3..d85a8b8 100644 --- a/tests/playwright.ts +++ b/tests/playwright.ts @@ -1,16 +1,31 @@ import {test as baseTest} from '@playwright/test'; -import {InterviewsPageModel} from "./pages"; - +import {HomePageModel, InterviewsPageModel, JournalsPage, PageModel} from "./pages"; interface PageFixtures { interviews: InterviewsPageModel; + journals: JournalsPage, + home: HomePageModel, } +type UseFunction = (x: T) => void; + export const test = baseTest.extend({ - interviews: async ({page}, use) => { + interviews: async ({page}, use: UseFunction) => { const interview = new InterviewsPageModel(page); await interview.goto(); use(interview); // clean up }, + journals: async ({page}, use: UseFunction) => { + const journalsPage = new JournalsPage(page); + await journalsPage.goto(); + use(journalsPage); + // clean up + }, + home: async ({page}, use: UseFunction) => { + const homePage = new HomePageModel(page); + await homePage.goto(); + use(homePage); + // clean up + }, }) \ No newline at end of file From 1b78444489ceeefb95b5e45d9a0078319ea0d044 Mon Sep 17 00:00:00 2001 From: Chris Bayliss Date: Thu, 24 Sep 2026 14:32:26 +1000 Subject: [PATCH 4/6] adds `@tests` path alias adds ARIA snapshot for homepage main content --- ...e-main-content-matches-snapshot-1.aria.yml | 28 +++++++++++++++++++ tests/homepage.spec.ts | 2 +- tests/interviews.spec.ts | 2 +- tests/journals.spec.ts | 2 +- tests/pages/home.ts | 16 ++--------- tests/pages/interviews.ts | 2 +- tests/pages/journals.ts | 2 +- tsconfig.json | 26 ++++++++++++++--- 8 files changed, 57 insertions(+), 23 deletions(-) create mode 100644 tests/__snapshots__/homepage.spec.ts/landing-page-main-content-matches-snapshot-1.aria.yml diff --git a/tests/__snapshots__/homepage.spec.ts/landing-page-main-content-matches-snapshot-1.aria.yml b/tests/__snapshots__/homepage.spec.ts/landing-page-main-content-matches-snapshot-1.aria.yml new file mode 100644 index 0000000..ecbd1e1 --- /dev/null +++ b/tests/__snapshots__/homepage.spec.ts/landing-page-main-content-matches-snapshot-1.aria.yml @@ -0,0 +1,28 @@ +- main: + - button "Toggle Sidebar": + - img + - text: "" + - navigation "breadcrumb": + - list: + - listitem: + - link "Home" [disabled] + - heading "๐Ÿ‘‹ Welcome to AV QC Portal" [level=2] + - paragraph: + - text: This web portal is actively being developed as a companion to the + - link "AV QC pipeline": + - /url: https://github.com/dptools/dpinterview + - text: project, designed to streamline audiovisual quality control processes. + - link "๐Ÿ” Quality Control Monitor and manage quality issues in your audio/video recordings with more intuitive interfaces.": + - /url: /issues + - heading "๐Ÿ” Quality Control" [level=4] + - paragraph: Monitor and manage quality issues in your audio/video recordings with more intuitive interfaces. + - link "๐Ÿ“Š Real-time Monitoring Track performance metrics and health indicators of your audio/video processing systems in real time. [Superset]": + - /url: http://localhost:8088 + - heading "๐Ÿ“Š Real-time Monitoring" [level=4] + - paragraph: Track performance metrics and health indicators of your audio/video processing systems in real time. [Superset] + - paragraph: + - text: This project is under active development. Check back for updates or contribute on + - link "GitHub": + - /url: https://github.com/dheshanm/dpinterview-web + - text: . + - region "Notifications alt+T" \ No newline at end of file diff --git a/tests/homepage.spec.ts b/tests/homepage.spec.ts index 30e2d68..b1ed1f0 100644 --- a/tests/homepage.spec.ts +++ b/tests/homepage.spec.ts @@ -1,5 +1,5 @@ import {expect} from '@playwright/test'; -import {test} from "./playwright"; +import {test} from "@tests/playwright"; test.describe("landing page", async () => { diff --git a/tests/interviews.spec.ts b/tests/interviews.spec.ts index 9ba6dce..2181a25 100644 --- a/tests/interviews.spec.ts +++ b/tests/interviews.spec.ts @@ -1,5 +1,5 @@ import {expect} from '@playwright/test'; -import {test} from "./playwright"; +import {test} from "@tests/playwright"; test.describe("Interviews page", async () => { diff --git a/tests/journals.spec.ts b/tests/journals.spec.ts index 10d223f..21d8743 100644 --- a/tests/journals.spec.ts +++ b/tests/journals.spec.ts @@ -1,5 +1,5 @@ import {expect} from '@playwright/test'; -import {test} from "./playwright"; +import {test} from "@tests/playwright"; test.describe("Audio journals page", async () => { // test("",async ()=>{}); diff --git a/tests/pages/home.ts b/tests/pages/home.ts index 139ec16..e0e2cc6 100644 --- a/tests/pages/home.ts +++ b/tests/pages/home.ts @@ -1,22 +1,10 @@ -import {Locator, Page} from '@playwright/test'; -import {AbstractPageModel} from "./page"; - +import {Page} from '@playwright/test'; +import {AbstractPageModel} from "@tests/pages/page"; export class HomePageModel extends AbstractPageModel { static readonly path = "/"; - readonly selectColumnsButton: Locator; - readonly filtersButton: Locator; - readonly densityButton: Locator; - readonly exportButton: Locator; - - readonly selectRowHeader: Locator; - readonly nameColumn: Locator; - readonly typeColumn: Locator; - readonly subjectColumn: Locator; - readonly studyColumn: Locator; - constructor(page: Page) { super(page); } diff --git a/tests/pages/interviews.ts b/tests/pages/interviews.ts index f05d70e..346bb72 100644 --- a/tests/pages/interviews.ts +++ b/tests/pages/interviews.ts @@ -1,5 +1,5 @@ import {Locator, Page} from '@playwright/test'; -import {AbstractPageModel} from "./page"; +import {AbstractPageModel} from "@tests/pages/page"; export class InterviewsPageModel extends AbstractPageModel { diff --git a/tests/pages/journals.ts b/tests/pages/journals.ts index 00eea63..65cd1ea 100644 --- a/tests/pages/journals.ts +++ b/tests/pages/journals.ts @@ -1,5 +1,5 @@ import {Locator, Page} from "@playwright/test"; -import {AbstractPageModel} from "./page"; +import {AbstractPageModel} from "@tests/pages/page"; export class JournalsPage extends AbstractPageModel { diff --git a/tsconfig.json b/tsconfig.json index d7e05e5..8695abc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "ES2020", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -19,9 +23,23 @@ } ], "paths": { - "@/*": ["./src/*"] + "@/*": [ + "./src/*" + ], + "@tests/*": [ + "./tests/*" + ] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "src/**/*.ts", + "src/**/*.tsx", + "tests/**/*.ts", + "tests/**/*.tsx", + ".next/types/**/*.ts" + ], + "exclude": [ + "node_modules" + ] } From 4d678b1d044997ca922973545890751ad19d9396 Mon Sep 17 00:00:00 2001 From: Chris Bayliss Date: Thu, 24 Sep 2026 16:55:14 +1000 Subject: [PATCH 5/6] adds E2E tests for `/issues/multiPart` --- .../landing-page-sidebar-1.aria.yml | 82 +++++++++++++++++++ tests/issues/multiPart.spec.ts | 38 +++++++++ 2 files changed, 120 insertions(+) create mode 100644 tests/__snapshots__/issues/multiPart.spec.ts/landing-page-sidebar-1.aria.yml create mode 100644 tests/issues/multiPart.spec.ts diff --git a/tests/__snapshots__/issues/multiPart.spec.ts/landing-page-sidebar-1.aria.yml b/tests/__snapshots__/issues/multiPart.spec.ts/landing-page-sidebar-1.aria.yml new file mode 100644 index 0000000..5629a2a --- /dev/null +++ b/tests/__snapshots__/issues/multiPart.spec.ts/landing-page-sidebar-1.aria.yml @@ -0,0 +1,82 @@ +- navigation "sidebar": + - list: + - listitem: + - link "AV QC Portal v0.2.0": + - /url: / + - img + - text: "" + - text: Navigation + - list: + - listitem: + - link "Issues": + - /url: /issues + - img + - text: "" + - button "Toggle" [expanded]: + - img + - text: "" + - list: + - listitem: + - link "Multi-Part Interviews": + - /url: /issues/multiPart + - listitem: + - link "Multi-Combined Audio Files": + - /url: /issues/multiCombinedAudio + - listitem: + - link "Unlabelled Audio": + - /url: /issues/unlabelledAudio + - listitem: + - link "Missing Interviews": + - /url: /issues/missing + - listitem: + - link "Missing Runsheets": + - /url: /issues/noRunsheet + - listitem: + - link "Missing Transcripts": + - /url: /issues/noTranscript + - listitem: + - link "Failed Audio QC": + - /url: /issues/audioQcFailed + - listitem: + - link "Pending Transcription Push": + - /url: /issues/pendingTranscriptionPush + - listitem: + - link "Awaiting Vendor Transcription": + - /url: /issues/awaitingVendorTranscription + - listitem: + - link "Transcript Not Imported": + - /url: /issues/transcriptNotImported + - listitem: + - link "Pipeline Failures": + - /url: /issues/pipelineFailures + - listitem: + - link "Runsheet Match": + - /url: /issues/runsheetMatch + - listitem: + - link "Override Ledger": + - /url: /issues/overrideLedger + - listitem: + - link "Interviews": + - /url: /interviews + - img + - text: "" + - button "Toggle": + - img + - text: "" + - listitem: + - link "Audio Journals": + - /url: /journals + - img + - text: "" + - list: + - listitem: + - link "GitHub": + - /url: https://github.com/dheshanm/dpinterview-web + - img + - text: "" + - listitem: + - link "Superset": + - /url: http://localhost:8088 + - img + - text: "" + - button "Toggle Sidebar" \ No newline at end of file diff --git a/tests/issues/multiPart.spec.ts b/tests/issues/multiPart.spec.ts new file mode 100644 index 0000000..0f51074 --- /dev/null +++ b/tests/issues/multiPart.spec.ts @@ -0,0 +1,38 @@ +import {expect} from '@playwright/test'; +import {test} from "@tests/playwright"; + +test.describe("landing page", async () => { + test.beforeEach(async ({page}) => { + await page.goto("/issues/multiPart"); + }); + + test("info text", async ({page}) => { + await expect(page.getByRole('heading', {name: 'Interviews with Multiple Parts', level: 2})).toBeVisible(); + await expect(page.getByRole("paragraph") + .filter({has: page.getByText(/The following \d+ interviews have multiple parts:/)})) + .toBeAttached(); + }); + + test.describe("results table", async () => { + + test("controls menu present", async ({page}) => { + await expect(page.getByRole('button', {name: 'Select columns'})).toBeVisible(); + await expect(page.getByRole('button', {name: 'Show filters'})).toBeVisible(); + await expect(page.getByRole('button', {name: 'Density'})).toBeVisible(); + await expect(page.getByRole('button', {name: 'Export'})).toBeVisible(); + }); + + test("table headers present", async ({page}) => { + const gridElement = page.getByRole("grid"); + await expect(gridElement).toBeAttached(); + await expect(gridElement.getByRole('columnheader', {name: 'Interview Name'})).toBeVisible(); + await expect(gridElement.getByRole('columnheader', {name: 'Interview Type'})).toBeVisible(); + await expect(gridElement.getByRole('columnheader', {name: 'Subject ID'})).toBeVisible(); + await expect(gridElement.getByRole('columnheader', {name: 'Parts Count'})).toBeVisible(); + }); + }); + + test("sidebar", async ({page}) => { + await expect(page.getByRole('navigation', {name: 'sidebar'})).toMatchAriaSnapshot(); + }); +}); \ No newline at end of file From 5ed427531045f88a7a93aa16d34285f784044dd0 Mon Sep 17 00:00:00 2001 From: Chris Bayliss Date: Thu, 24 Sep 2026 17:59:26 +1000 Subject: [PATCH 6/6] removes Playwright github workflow --- .github/workflows/playwright.yml | 38 -------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 .github/workflows/playwright.yml diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml deleted file mode 100644 index cf6f223..0000000 --- a/.github/workflows/playwright.yml +++ /dev/null @@ -1,38 +0,0 @@ -name: Playwright Tests -on: - push: - branches: [ ampscz ] - pull_request: - branches: [ ampscz ] -jobs: - test: - timeout-minutes: 60 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: lts/* - - uses: actions/cache@v4 - with: - # See here for caching with `yarn`, `bun` or other package managers https://github.com/actions/cache/blob/main/examples.md or you can leverage caching with actions/setup-node https://github.com/actions/setup-node - path: | - ~/.npm - ${{ github.workspace }}/.next/cache - # Generate a new cache whenever packages or source files change. - key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}-${{ hashFiles('**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx') }} - # If source files changed but packages didn't, rebuild from a prior cache. - restore-keys: | - ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json') }}- - - name: Install dependencies - run: npm ci - - name: Install Playwright Browsers - run: npx playwright install --with-deps - - name: Run Playwright tests - run: npx playwright test - - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} - with: - name: playwright-report - path: playwright-report/ - retention-days: 30