Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: playwright tests cancelled after timeout #6208

Closed
wants to merge 56 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
63a9a8f
ci: increase playwright workflow timeout
LinHuiqing Apr 27, 2023
62d2dd1
fix: email sort by time in ascending order
LinHuiqing Apr 27, 2023
5ac4337
ci: set timeout for download event wait
LinHuiqing Apr 27, 2023
b9fc785
fix: wait for possible modals to appear before esc
LinHuiqing Apr 27, 2023
58a8595
ci: set lower timeout for download event
LinHuiqing Apr 27, 2023
8a4f8fb
ci: run playwright tests excl encrypt-submission
LinHuiqing Apr 28, 2023
9cfa74a
ci: break playwright tests into different steps
LinHuiqing Apr 29, 2023
215acc4
ci: break playwright tests into different steps
LinHuiqing May 2, 2023
f71eab2
fix: forgot to add the commands themselves :/
LinHuiqing May 2, 2023
99aaaaf
ci: move first email test to the end
LinHuiqing May 2, 2023
4c909a6
ci: extract getByRole to separate step
LinHuiqing May 2, 2023
454ab6f
ci: temporarily use only 1 browser to debug faster
LinHuiqing May 2, 2023
fd6cc1a
ci: check if combobox was the deal
LinHuiqing May 2, 2023
95d15bc
ci: commit to combobox for fillDropdown input
LinHuiqing May 2, 2023
fcd0daa
fix: change id=show locator to combobox
LinHuiqing May 2, 2023
2b2e1d5
fix: use getByRole to pick logic type
LinHuiqing May 2, 2023
02db7d0
fix: remove closing of options in fillMultiDropdown
LinHuiqing May 3, 2023
e35d60c
ci: add enumerated encrypt-submission tests
LinHuiqing May 3, 2023
f6d4eff
ci: put encrypt-submission playwright tests back
LinHuiqing May 3, 2023
fb0093e
ci: remove download from playwright tests
LinHuiqing May 9, 2023
d303844
test: better selectors for dropdowns
justynoh May 9, 2023
c896f12
ci: increase playwright workflow timeout
LinHuiqing Apr 27, 2023
3c5b30c
fix: email sort by time in ascending order
LinHuiqing Apr 27, 2023
e075bd5
ci: set timeout for download event wait
LinHuiqing Apr 27, 2023
0eead37
fix: wait for possible modals to appear before esc
LinHuiqing Apr 27, 2023
8fd26da
ci: set lower timeout for download event
LinHuiqing Apr 27, 2023
2d289b9
ci: run playwright tests excl encrypt-submission
LinHuiqing Apr 28, 2023
0399901
ci: break playwright tests into different steps
LinHuiqing Apr 29, 2023
971a41d
ci: break playwright tests into different steps
LinHuiqing May 2, 2023
78f6fb1
fix: forgot to add the commands themselves :/
LinHuiqing May 2, 2023
44dd1ca
ci: move first email test to the end
LinHuiqing May 2, 2023
f79faa9
ci: extract getByRole to separate step
LinHuiqing May 2, 2023
0c05e28
ci: temporarily use only 1 browser to debug faster
LinHuiqing May 2, 2023
1a7d18f
ci: check if combobox was the deal
LinHuiqing May 2, 2023
62005cf
ci: commit to combobox for fillDropdown input
LinHuiqing May 2, 2023
2bd6ea0
fix: change id=show locator to combobox
LinHuiqing May 2, 2023
84ea65b
fix: use getByRole to pick logic type
LinHuiqing May 2, 2023
c352f0e
fix: remove closing of options in fillMultiDropdown
LinHuiqing May 3, 2023
2480de4
ci: add enumerated encrypt-submission tests
LinHuiqing May 3, 2023
066fd28
ci: put encrypt-submission playwright tests back
LinHuiqing May 3, 2023
682d1d2
fix: add ssn env site name for playwright
LinHuiqing May 9, 2023
0d65ebd
Merge branch 'fix/playwright' of https://github.com/opengovsg/FormSG …
justynoh May 9, 2023
3af498e
fix: typo for SSM_ENV_SITE_NAME
LinHuiqing May 9, 2023
83150f5
fix: inputLocator for fillMultiDropdown
LinHuiqing May 11, 2023
23e6580
fix: get parent group for conditions multiDropdown
LinHuiqing May 11, 2023
b04d782
fix: click first button for downshift found
LinHuiqing May 11, 2023
d4e7373
fix: try last instead for dropdown
LinHuiqing May 11, 2023
08dff29
ci: restore commented browsers
LinHuiqing May 11, 2023
6a1943a
ci: reduce number of playwright retries to 1
LinHuiqing May 11, 2023
19d08f4
ci: playwright test suite for each step
LinHuiqing May 11, 2023
9f3abe0
fix: adjust test timeouts
LinHuiqing May 12, 2023
96fffba
ci: split storage mode playwright tests
LinHuiqing May 12, 2023
27353a3
ci: rm webkit from playwright tests temporarily
LinHuiqing May 12, 2023
7d125b7
ci: restore webServer timeout for playwright to 120 * 1000
LinHuiqing May 12, 2023
01aebda
ci: make encrypt-submissions playwright tests 1 step
LinHuiqing May 12, 2023
87c5a40
fix: use combobox for field settings with dropdown
LinHuiqing May 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
branches: [develop, release-al2, master]
jobs:
test:
timeout-minutes: 60
timeout-minutes: 90
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand All @@ -22,8 +22,12 @@ jobs:
NODE_OPTIONS: '--max-old-space-size=4096 --openssl-legacy-provider'
REACT_APP_FORMSG_SDK_MODE: 'test'
run: npm run build
- name: Run Playwright tests
run: npx playwright test
- name: Run Playwright tests (login)
run: npx playwright test __tests__/e2e/login.spec.ts
- name: Run Playwright tests (email-submission)
run: npx playwright test __tests__/e2e/email-submission.spec.ts
- name: Run Playwright tests (encrypt-submission)
run: npx playwright test __tests__/e2e/encrypt-submission.spec.ts
- uses: actions/upload-artifact@v3
if: always()
with:
Expand Down
27 changes: 20 additions & 7 deletions __tests__/e2e/helpers/createForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ const addForm = async (
): Promise<AddFormReturn> => {
await page.goto(DASHBOARD_PAGE)

// Wait for all modals to render
await page.waitForTimeout(500)

// Press escape 5 times to get rid of any banners
await page.keyboard.press('Escape')
await page.keyboard.press('Escape')
Expand All @@ -109,7 +112,7 @@ const addForm = async (
}
if (responseMode === FormResponseMode.Encrypt) {
// Download the secret key and save it for the test.
const downloadPromise = page.waitForEvent('download')
const downloadPromise = page.waitForEvent('download', { timeout: 3000 })
await page.getByRole('button', { name: 'Download key' }).click()
const download = await downloadPromise
const path = await download.path()
Expand Down Expand Up @@ -459,7 +462,7 @@ const addBasicField = async (
case BasicField.Attachment:
await fillDropdown(
page,
page.getByRole('textbox', {
page.getByRole('combobox', {
name: 'Maximum size of individual attachment',
}),
`${field.attachmentSize} MB`,
Expand Down Expand Up @@ -590,12 +593,16 @@ const addBasicField = async (
case BasicField.Rating:
await fillDropdown(
page,
page.getByRole('textbox', { name: 'Number of steps' }),
page.getByRole('combobox', {
name: 'Number of steps',
}),
String(field.ratingOptions.steps),
)
await fillDropdown(
page,
page.getByRole('textbox', { name: 'Shape' }),
page.getByRole('combobox', {
name: 'Shape',
}),
field.ratingOptions.shape,
)
break
Expand Down Expand Up @@ -694,7 +701,11 @@ const addLogics = async (
const valueInput = page.locator(`id=conditions.${i}.value`)
switch (state) {
case LogicConditionState.Either:
await fillMultiDropdown(page, valueInput, value)
await fillMultiDropdown(
page,
page.getByRole('group').filter({ has: valueInput }),
value,
)
break
default:
switch (formFields[field].fieldType) {
Expand All @@ -712,13 +723,15 @@ const addLogics = async (
}
}

const logicTypeInput = page.locator('id=logicType')
const logicTypeInput = page.getByLabel('Then').first()
switch (logic.logicType) {
case LogicType.ShowFields:
await fillDropdown(page, logicTypeInput, 'Show field(s)')
await fillMultiDropdown(
page,
page.locator('id=show'),
page
.getByRole('group')
.filter({ has: page.getByLabel('Show').first() }),
logic.show.map((n) => getTitleWithQuestionNumber(formFields, n)),
)
break
Expand Down
114 changes: 57 additions & 57 deletions __tests__/e2e/helpers/verifySubmission.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { expect, Page } from '@playwright/test'
import { readFileSync } from 'fs'
// import { readFileSync } from 'fs'
import { BasicField, FormAuthType, FormResponseMode } from 'shared/types'

import { IFormSchema, SgidFieldTitle, SPCPFieldTitle } from 'src/types'

import {
ADMIN_EMAIL,
ADMIN_FORM_PAGE_RESPONSES,
ADMIN_FORM_PAGE_RESPONSES_INDIVIDUAL,
// ADMIN_FORM_PAGE_RESPONSES_INDIVIDUAL,
E2eFieldMetadata,
E2eFormResponseMode,
E2eSettingsOptions,
} from '../constants'
import {
expectAttachment,
expectContains,
expectToast,
// expectToast,
getAutoreplyEmail,
getResponseArray,
getResponseTitle,
Expand Down Expand Up @@ -158,64 +158,64 @@ export const verifyEncryptSubmission = async (
{
form,
secretKey,
responseId,
formFields,
}: VerifySubmissionBaseInputs & { secretKey: string },
}: // responseId,
// formFields,
VerifySubmissionBaseInputs & { secretKey: string },
): Promise<void> => {
// Go to the responses summary page and enter the secret key
await page.goto(ADMIN_FORM_PAGE_RESPONSES(form._id))
await page.getByLabel(/Enter or upload Secret Key/).fill(secretKey)
await page.getByRole('button', { name: 'Unlock responses' }).click()

// Try downloading CSV and checking contents
const downloadPromise = page.waitForEvent('download')
await page.getByRole('button', { name: 'Download' }).click()
await page.getByRole('menuitem', { name: 'CSV only' }).click()
const download = await downloadPromise
const path = await download.path()
if (!path) throw new Error('CSV download failed')

await expectToast(page, /Success\. 1\/1 response was decrypted\./)

const content = readFileSync(path).toString()
const expectSubmissionContains = expectContains(content)

expectSubmissionContains([responseId])
for (const field of formFields) {
const responseArray = getResponseArray(field, {
mode: FormResponseMode.Encrypt,
csv: true,
})
if (!responseArray) continue
expectSubmissionContains([field.title, ...responseArray])
}

// TODO: Attachments don't work in storage mode tests, so no need to download CSV with attachments.

// Ensure there is a cell with the response ID and click into it
await page.getByRole('cell', { name: responseId }).click()

// We should be at the individual response page now.
await expect(page).toHaveURL(
ADMIN_FORM_PAGE_RESPONSES_INDIVIDUAL(form._id, responseId),
)

// Expect all the content of the page
for (const field of formFields) {
const responseArray = getResponseArray(field, {
mode: FormResponseMode.Encrypt,
csv: false,
})
if (!responseArray) continue
const responseTitle = getResponseTitle(field, {
mode: FormResponseMode.Encrypt,
csv: false,
})
await expect(page.getByText(responseTitle)).toBeVisible()
for (const response of responseArray) {
if (response) {
await expect(page.getByText(response, { exact: true })).toBeVisible()
}
}
}
// // Try downloading CSV and checking contents
// const downloadPromise = page.waitForEvent('download', { timeout: 3000 })
// await page.getByRole('button', { name: 'Download' }).click()
// await page.getByRole('menuitem', { name: 'CSV only' }).click()
// const download = await downloadPromise
// const path = await download.path()
// if (!path) throw new Error('CSV download failed')

// await expectToast(page, /Success\. 1\/1 response was decrypted\./)

// const content = readFileSync(path).toString()
// const expectSubmissionContains = expectContains(content)

// expectSubmissionContains([responseId])
// for (const field of formFields) {
// const responseArray = getResponseArray(field, {
// mode: FormResponseMode.Encrypt,
// csv: true,
// })
// if (!responseArray) continue
// expectSubmissionContains([field.title, ...responseArray])
// }

// // TODO: Attachments don't work in storage mode tests, so no need to download CSV with attachments.

// // Ensure there is a cell with the response ID and click into it
// await page.getByRole('cell', { name: responseId }).click()

// // We should be at the individual response page now.
// await expect(page).toHaveURL(
// ADMIN_FORM_PAGE_RESPONSES_INDIVIDUAL(form._id, responseId),
// )

// // Expect all the content of the page
// for (const field of formFields) {
// const responseArray = getResponseArray(field, {
// mode: FormResponseMode.Encrypt,
// csv: false,
// })
// if (!responseArray) continue
// const responseTitle = getResponseTitle(field, {
// mode: FormResponseMode.Encrypt,
// csv: false,
// })
// await expect(page.getByText(responseTitle)).toBeVisible()
// for (const response of responseArray) {
// if (response) {
// await expect(page.getByText(response, { exact: true })).toBeVisible()
// }
// }
// }
}
1 change: 1 addition & 0 deletions __tests__/e2e/setup/.test-env
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ APP_URL=http://localhost:5000

SECRET_ENV=development
INTRANET_IP_LIST_PATH=tests/mock-intranet-ips.txt
SSM_ENV_SITE_NAME=test

REACT_SWITCH_ENV_FEEDBACK_FORM_ID_ADMIN=62da6a569ee8e90143b5da26
REACT_SWITCH_ENV_FEEDBACK_FORM_ID_RESPONDENT=62da6a569ee8e90143b5da26
Expand Down
26 changes: 16 additions & 10 deletions __tests__/e2e/utils/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,8 @@ export const fillDropdown = async (
input: Locator,
value: string,
): Promise<void> => {
await input.fill(value)
const menuId = await input.getAttribute('aria-controls')
const menu = page.locator(`id=${menuId}`)
// Scroll menu into view to avoid flakiness.
await menu.scrollIntoViewIfNeeded()
await menu.getByRole('option', { name: value }).click()
await input.click()
await page.getByRole('option', { name: value }).click()
}

/**
Expand All @@ -184,12 +180,22 @@ export const fillDropdown = async (
*/
export const fillMultiDropdown = async (
page: Page,
input: Locator,
inputScope: Locator,
values: string[],
): Promise<void> => {
for (const value of values) await fillDropdown(page, input, value)
// Multiselect dropdown, click the input again to close the popover
await input.click()
await inputScope
.getByRole('button', { name: 'Open dropdown options' })
.last()
.click()
for (const value of values) {
const option = page.getByRole('option', { name: value })
await option.scrollIntoViewIfNeeded()
await option.click()
}
await inputScope
.getByRole('button', { name: 'Close dropdown options' })
.last()
.click()
}

/**
Expand Down
4 changes: 1 addition & 3 deletions __tests__/e2e/utils/mail.ts
Copy link
Contributor Author

@LinHuiqing LinHuiqing May 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh btw the changes here corrects the retrieving of OTP emails a bit:

return inbox.filter(filterFn).sort((a, b) => a.time - b.time)

^this corrects the sorting of emails to ascending order of time! so that we can get the latest email when we use pop (not sure why this bug has survived so long?? maybe because we've just been deleting all the other emails)

await MAIL_CLIENT.deleteById(lastEmail.id)

^I removed the deleting of emails by client because... I don't think its needed? and its easier for debugging if we have all the emails in the inbox

Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ const getEmailsBy = async (
filterFn: (email: MailData) => boolean,
): Promise<MailData[]> => {
const inbox = await MAIL_CLIENT.getAll()
return inbox.filter(filterFn).sort((a, b) => (a.time > b.time ? -1 : 1))
return inbox.filter(filterFn).sort((a, b) => a.time - b.time)
}

/**
Expand All @@ -72,8 +72,6 @@ export const extractOtp = async (recipient: string): Promise<string> => {
const otp = lastEmail.html.match(/\d{6}/)?.[0]
if (!otp) throw Error('otp was not found in email')

await MAIL_CLIENT.deleteById(lastEmail.id)

return otp
}

Expand Down
16 changes: 8 additions & 8 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ dotenv.config({
const config: PlaywrightTestConfig = {
testDir: './__tests__/e2e',
/* Maximum time one test can run for. */
timeout: 120 * 1000,
timeout: 180 * 1000,
expect: {
/**
* Maximum time expect() should wait for the condition to be met.
Expand All @@ -26,7 +26,7 @@ const config: PlaywrightTestConfig = {
/* 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,
retries: process.env.CI ? 1 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
Expand Down Expand Up @@ -57,12 +57,12 @@ const config: PlaywrightTestConfig = {
},
},

{
name: 'webkit',
use: {
...devices['Desktop Safari'],
},
},
// {
// name: 'webkit',
// use: {
// ...devices['Desktop Safari'],
// },
// },

/* Test against mobile viewports. */
// {
Expand Down