From 4070f6f1ea88cdb773ddaa7079a7a24f8f0c012d Mon Sep 17 00:00:00 2001 From: Sean Fong Date: Mon, 10 Jun 2024 21:47:53 +0930 Subject: [PATCH] Rewrite existing cypress tests in Playwright --- .github/workflows/main.yml | 24 +++++++ apps/smart-forms-app/.gitignore | 6 +- apps/smart-forms-app/e2e/_pre-run.spec.ts | 70 +++++++++++++++++++ apps/smart-forms-app/e2e/dashboard.spec.ts | 56 ++++++++++++++++ apps/smart-forms-app/e2e/globals.ts | 21 ++++++ apps/smart-forms-app/e2e/saving.spec.ts | 76 +++++++++++++++++++++ apps/smart-forms-app/package.json | 1 + apps/smart-forms-app/playwright.config.ts | 78 ++++++++++++++++++++++ package-lock.json | 60 +++++++++++++++++ 9 files changed, 391 insertions(+), 1 deletion(-) create mode 100644 apps/smart-forms-app/e2e/_pre-run.spec.ts create mode 100644 apps/smart-forms-app/e2e/dashboard.spec.ts create mode 100644 apps/smart-forms-app/e2e/globals.ts create mode 100644 apps/smart-forms-app/e2e/saving.spec.ts create mode 100644 apps/smart-forms-app/playwright.config.ts diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bd0151710..6ff86c3ad 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -52,6 +52,30 @@ jobs: CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + playwright-test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 16 + cache: npm + - 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: always() + with: + build: npm run build -w apps/smart-forms-app + start: npm run preview -w apps/smart-forms-app + name: playwright-report + path: playwright-report/ + retention-days: 30 + lint: name: Lint runs-on: ubuntu-latest diff --git a/apps/smart-forms-app/.gitignore b/apps/smart-forms-app/.gitignore index 6e5a38b01..2f0fb72ee 100644 --- a/apps/smart-forms-app/.gitignore +++ b/apps/smart-forms-app/.gitignore @@ -26,4 +26,8 @@ yarn-error.log* # cypress non-test files cypress/screenshots cypress/videos -temp \ No newline at end of file +temp +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/apps/smart-forms-app/e2e/_pre-run.spec.ts b/apps/smart-forms-app/e2e/_pre-run.spec.ts new file mode 100644 index 000000000..ca455639a --- /dev/null +++ b/apps/smart-forms-app/e2e/_pre-run.spec.ts @@ -0,0 +1,70 @@ +/* + * Copyright 2024 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) ABN 41 687 119 230. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, test } from '@playwright/test'; +import { PLAYWRIGHT_APP_URL, PLAYWRIGHT_EHR_URL, PLAYWRIGHT_FORMS_SERVER_URL } from './globals'; + +test('launch without questionnaire context, select a questionnaire and create a new response', async ({ + page +}) => { + // Launch app without questionnaire context + const fetchQPromise = page.waitForResponse( + `${PLAYWRIGHT_FORMS_SERVER_URL}/Questionnaire?_count=100&_sort=-date&` + ); + const launchUrl = `${PLAYWRIGHT_APP_URL}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIiIsZmFsc2Vd`; + await page.goto(launchUrl); + expect((await fetchQPromise).status()).toBe(200); + + // Search MBS715 title + const fetchQByTitlePromise = page.waitForResponse( + `${PLAYWRIGHT_FORMS_SERVER_URL}/Questionnaire?_count=100&_sort=-date&title:contains=Aboriginal%20and%20Torres%20Strait%20Islander%20Health%20Check` + ); + await page + .getByTestId('search-field-questionnaires') + .locator('input') + .fill('Aboriginal and Torres Strait Islander Health Check'); + await fetchQByTitlePromise; + + // Open first MBS715 questionnaire + const populatePromise = page.waitForResponse( + new RegExp(/^https:\/\/proxy\.smartforms\.io\/v\/r4\/fhir\/(Observation|Condition)\?.+$/) + ); + await page + .getByTestId('questionnaire-list-row') + .getByText('Aboriginal and Torres Strait Islander Health Check') + .first() + .click(); + await page.getByTestId('button-create-response').click(); + expect((await populatePromise).status()).toBe(200); + + // Test radio item + await expect(page.getByTestId('q-item-choice-radio-answer-value-set-box')).toContainText( + 'Eligible for health check' + ); + await page.getByTestId('q-item-choice-radio-answer-value-set-box').first().click(); + await expect(page.getByTestId('updating-indicator')).toBeInViewport(); + + // Save progress + const savePromise = page.waitForResponse(`${PLAYWRIGHT_EHR_URL}/QuestionnaireResponse`); + await page.getByTestId('renderer-operation-item').getByText('Save Progress').click(); + const saveResponse = await savePromise; + expect(saveResponse.status()).toBe(201); + await expect(page.getByText('Response saved')).toBeInViewport(); + + // Go back to questionnaires + await page.getByTestId('renderer-operation-item').getByText('Back to Questionnaires').click(); +}); diff --git a/apps/smart-forms-app/e2e/dashboard.spec.ts b/apps/smart-forms-app/e2e/dashboard.spec.ts new file mode 100644 index 000000000..213772438 --- /dev/null +++ b/apps/smart-forms-app/e2e/dashboard.spec.ts @@ -0,0 +1,56 @@ +/* + * Copyright 2024 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) ABN 41 687 119 230. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, test } from '@playwright/test'; +import { PLAYWRIGHT_APP_URL, PLAYWRIGHT_FORMS_SERVER_URL } from './globals'; + +const questionnaireTitle = 'Aboriginal and Torres Strait Islander Health Check'; + +test.beforeEach(async ({ page }) => { + // Launch app without questionnaire context + const fetchQPromise = page.waitForResponse( + `${PLAYWRIGHT_FORMS_SERVER_URL}/Questionnaire?_count=100&_sort=-date&` + ); + const launchUrl = `${PLAYWRIGHT_APP_URL}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIiIsZmFsc2Vd`; + await page.goto(launchUrl); + expect((await fetchQPromise).status()).toBe(200); + + // Wait for responses to load + const fetchQRPromise = page.waitForResponse( + new RegExp(/^https:\/\/proxy\.smartforms\.io\/v\/r4\/fhir\/QuestionnaireResponse\?.+$/) + ); + await page.getByTestId('questionnaire-list-row').getByText(questionnaireTitle).first().click(); + const fetchQRResponse = await fetchQRPromise; + expect(fetchQRResponse.status()).toBe(200); +}); + +test('View response from MBS715', async ({ page }) => { + await expect(page.getByTestId('button-view-responses')).toBeEnabled(); + await page.getByTestId('button-view-responses').click(); + + // Open responses page + await expect(page.getByTestId('responses-list-toolbar')).toContainText(questionnaireTitle); + await page.getByTestId('response-list-row').getByText(questionnaireTitle).first().click(); + + // Open response + await expect(page.getByTestId('button-open-response')).toBeEnabled(); + await page.getByTestId('button-open-response').click(); + + // + await expect(page).toHaveURL(`${PLAYWRIGHT_APP_URL}/viewer`); + await expect(page.getByTestId('response-preview-box')).toContainText(questionnaireTitle); +}); diff --git a/apps/smart-forms-app/e2e/globals.ts b/apps/smart-forms-app/e2e/globals.ts new file mode 100644 index 000000000..6f702bde6 --- /dev/null +++ b/apps/smart-forms-app/e2e/globals.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2024 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) ABN 41 687 119 230. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const PLAYWRIGHT_EHR_URL = 'https://proxy.smartforms.io/v/r4/fhir'; +export const PLAYWRIGHT_FORMS_SERVER_URL = 'https://smartforms.csiro.au/api/fhir'; + +export const PLAYWRIGHT_APP_URL = 'http://localhost:4173'; diff --git a/apps/smart-forms-app/e2e/saving.spec.ts b/apps/smart-forms-app/e2e/saving.spec.ts new file mode 100644 index 000000000..5079dac8f --- /dev/null +++ b/apps/smart-forms-app/e2e/saving.spec.ts @@ -0,0 +1,76 @@ +/* + * Copyright 2024 Commonwealth Scientific and Industrial Research + * Organisation (CSIRO) ABN 41 687 119 230. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { expect, test } from '@playwright/test'; +import { PLAYWRIGHT_APP_URL, PLAYWRIGHT_EHR_URL } from './globals'; + +const questionnaireTitle = 'Aboriginal and Torres Strait Islander Health Check'; + +test.beforeEach(async ({ page }) => { + // Open first MBS715 questionnaire via launch context + const populatePromise = page.waitForResponse( + new RegExp(/^https:\/\/proxy\.smartforms\.io\/v\/r4\/fhir\/(Observation|Condition)\?.+$/) + ); + const launchUrl = `${PLAYWRIGHT_APP_URL}/launch?iss=https%3A%2F%2Fproxy.smartforms.io%2Fv%2Fr4%2Ffhir&launch=WzAsInBhdC1zZiIsInByaW1hcnktcGV0ZXIiLCJBVVRPIiwwLDAsMCwiZmhpclVzZXIgb25saW5lX2FjY2VzcyBvcGVuaWQgcHJvZmlsZSBwYXRpZW50L0NvbmRpdGlvbi5ycyBwYXRpZW50L09ic2VydmF0aW9uLnJzIGxhdW5jaCBwYXRpZW50L0VuY291bnRlci5ycyBwYXRpZW50L1F1ZXN0aW9ubmFpcmVSZXNwb25zZS5jcnVkcyBwYXRpZW50L1BhdGllbnQucnMiLCJodHRwOi8vbG9jYWxob3N0OjQxNzMvIiwiYTU3ZDkwZTMtNWY2OS00YjkyLWFhMmUtMjk5MjE4MDg2M2MxIiwiIiwiIiwiIiwiIiwwLDEsIntcInJvbGVcIjpcInF1ZXN0aW9ubmFpcmUtcmVuZGVyLW9uLWxhdW5jaFwiLFwiY2Fub25pY2FsXCI6XCJodHRwOi8vd3d3LmhlYWx0aC5nb3YuYXUvYXNzZXNzbWVudHMvbWJzLzcxNXwwLjEuMC1hc3NlbWJsZWRcIixcInR5cGVcIjpcIlF1ZXN0aW9ubmFpcmVcIn0iLGZhbHNlXQ`; + await page.goto(launchUrl); + const populateResponse = await populatePromise; + expect(populateResponse.status()).toBe(200); +}); + +test('Saving a response as draft then final', async ({ page }) => { + // Go to Consent tab and click on a boolean item + await page + .getByTestId('renderer-tab-list') + .locator('.MuiButtonBase-root') + .getByText('Consent') + .click(); + await page.getByTestId('q-item-boolean-box').first().locator('input').first().click(); + + // Save as draft + const saveDraftPromise = page.waitForResponse(`${PLAYWRIGHT_EHR_URL}/QuestionnaireResponse`); + await page.getByTestId('renderer-operation-item').getByText('Save Progress').click(); + const saveDraftResponse = await saveDraftPromise; + expect(saveDraftResponse.status()).toBe(201); + await expect(page.getByText('Response saved')).toBeInViewport(); + + // Select response in responses page + await page.getByTestId('renderer-operation-item').getByText('View Existing Responses').click(); + await page.getByTestId('response-list-row').getByText('in-progress').first().click(); + await expect(page.getByTestId('button-open-response')).toBeEnabled(); + await page.getByTestId('button-open-response').click(); + + // View response in viewer + await expect(page).toHaveURL(`${PLAYWRIGHT_APP_URL}/viewer`); + await expect(page.getByTestId('response-preview-box')).toContainText( + 'Aboriginal and Torres Strait Islander Health Check' + ); + + // Re-open the response + await page.getByTestId('renderer-operation-item').getByText('Edit Response').click(); + await expect(page).toHaveURL(`${PLAYWRIGHT_APP_URL}/renderer`); + + // Save as final + const saveFinalPromise = page.waitForResponse((response) => + response.url().startsWith(`${PLAYWRIGHT_EHR_URL}/QuestionnaireResponse/`) + ); + await page.getByTestId('renderer-operation-item').getByText('Save as Final').click(); + await page.getByTestId('save-as-final-button').click(); + const saveFinalResponse = await saveFinalPromise; + expect(saveFinalResponse.status()).toBe(200); + await expect(page.getByText('Response saved as final')).toBeInViewport(); + await expect(page).toHaveURL(`${PLAYWRIGHT_APP_URL}/dashboard/existing`); +}); diff --git a/apps/smart-forms-app/package.json b/apps/smart-forms-app/package.json index e99005b46..e56198e5b 100644 --- a/apps/smart-forms-app/package.json +++ b/apps/smart-forms-app/package.json @@ -77,6 +77,7 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", + "@playwright/test": "^1.44.1", "@sentry/cli": "^2.20.6", "@storybook/addon-essentials": "^8.0.6", "@storybook/addon-interactions": "^7.4.6", diff --git a/apps/smart-forms-app/playwright.config.ts b/apps/smart-forms-app/playwright.config.ts new file mode 100644 index 000000000..611b1a7e3 --- /dev/null +++ b/apps/smart-forms-app/playwright.config.ts @@ -0,0 +1,78 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './e2e', + /* 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://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + testIdAttribute: 'data-test' + }, + + /* 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 start', + url: 'http://localhost:4173', + reuseExistingServer: !process.env.CI + } +}); diff --git a/package-lock.json b/package-lock.json index 6590f1ded..0b8c932fa 100644 --- a/package-lock.json +++ b/package-lock.json @@ -101,6 +101,7 @@ }, "devDependencies": { "@jest/globals": "^29.5.0", + "@playwright/test": "^1.44.1", "@sentry/cli": "^2.20.6", "@storybook/addon-essentials": "^8.0.6", "@storybook/addon-interactions": "^7.4.6", @@ -6447,6 +6448,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@playwright/test": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz", + "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==", + "dev": true, + "dependencies": { + "playwright": "1.44.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -35523,6 +35539,50 @@ "node": ">=4" } }, + "node_modules/playwright": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz", + "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==", + "dev": true, + "dependencies": { + "playwright-core": "1.44.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.44.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz", + "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/polished": { "version": "4.2.2", "dev": true,