Skip to content

Commit

Permalink
Merge branch 'develop' into feat/sample-submission
Browse files Browse the repository at this point in the history
  • Loading branch information
wanlingt committed May 15, 2023
2 parents a305b75 + c2cd33f commit fff7cab
Show file tree
Hide file tree
Showing 84 changed files with 16,337 additions and 18,509 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 5 additions & 1 deletion .template-env
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,8 @@ FORMSG_SDK_MODE=

## Per-minute, per-IP request limits applied to specific endpoints
# SUBMISSIONS_RATE_LIMIT=
# SEND_AUTH_OTP_RATE_LIMIT=
# SEND_AUTH_OTP_RATE_LIMIT=

# Used to check if BE Server is currently running on local development environment
# One of boolean: "true" | "false"
# USE_MOCK_TWILIO=
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,25 @@ All notable changes to this project will be documented in this file. Dates are d

Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).

#### [v6.49.0](https://github.com/opengovsg/FormSG/compare/v6.48.0...v6.49.0)

- feat: control feature flags through DB [`#6286`](https://github.com/opengovsg/FormSG/pull/6286)
- build: merge release v6.48.0 to develop [`#6295`](https://github.com/opengovsg/FormSG/pull/6295)
- feat: control payment methods through stripe dashboard [`#6272`](https://github.com/opengovsg/FormSG/pull/6272)
- feat: add business field mutation to form-level [`#6236`](https://github.com/opengovsg/FormSG/pull/6236)
- build: release v6.48.0 [`#6291`](https://github.com/opengovsg/FormSG/pull/6291)

#### [v6.48.0](https://github.com/opengovsg/FormSG/compare/v6.47.0...v6.48.0)

> 9 May 2023

- feat: update sms copy to remove link (policy) [`#6289`](https://github.com/opengovsg/FormSG/pull/6289)
- chore(deps-dev): bump @typescript-eslint/parser from 5.59.2 to 5.59.5 in /shared [`#6287`](https://github.com/opengovsg/FormSG/pull/6287)
- build: merge release v6.47.0 to develop [`#6281`](https://github.com/opengovsg/FormSG/pull/6281)
- fix: add missing env var in docker_compose.yml [`#6284`](https://github.com/opengovsg/FormSG/pull/6284)
- build: release v6.47.0 [`#6279`](https://github.com/opengovsg/FormSG/pull/6279)
- chore: bump version to v6.47.0 [`002b8b6`](https://github.com/opengovsg/FormSG/commit/002b8b6b66c6cc199921311a2ea1800644bcc332)
- chore: bump version to v6.48.0 [`074746f`](https://github.com/opengovsg/FormSG/commit/074746f55303ab1ac2fb7e398b04bf91451fa9a0)

#### [v6.47.0](https://github.com/opengovsg/FormSG/compare/v6.46.0...v6.47.0)

Expand Down
16 changes: 11 additions & 5 deletions __tests__/e2e/helpers/createForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,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 +590,12 @@ 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 +694,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 @@ -718,7 +722,9 @@ const addLogics = async (
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')
// 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()
// }
// }
// }
}
3 changes: 3 additions & 0 deletions __tests__/e2e/setup/.test-env
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,6 @@ MOCKPASS_UID=S8979373D # Not used by mockpass but keep in sync with MOCKPASS_UEN
MOCKPASS_UEN=123456789A
SP_RP_JWKS_ENDPOINT=http://localhost:5000/singpass/.well-known/jwks.json
CP_RP_JWKS_ENDPOINT=http://localhost:5000/api/v3/corppass/.well-known/jwks.json

# Payment env vars
SSM_ENV_SITE_NAME=test
18 changes: 14 additions & 4 deletions __tests__/e2e/utils/field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,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
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
12 changes: 10 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ services:
# https://github.com/opengovsg/formsg-javascript-sdk/blob/develop/src/resource/signing-keys.ts
- SIGNING_SECRET_KEY=HDBXpu+2/gu10bLHpy8HjpN89xbA6boH9GwibPGJA8BOXmB+zOUpxCP33/S5p8vBWlPokC7gLR0ca8urVwfMUQ==
# Mock Twilio credentials. SMSes do not work in dev environment.
- TWILIO_ACCOUNT_SID=ACmockTwilioAccountSid
- TWILIO_ACCOUNT_SID=AC00000000000000000000000000000000
- TWILIO_API_KEY=mockTwilioApiKey
- TWILIO_API_SECRET=mockTwilioApiSecret
- TWILIO_MESSAGING_SERVICE_SID=mockTwilioMsgSrvcSid
- TWILIO_MESSAGING_SERVICE_SID=MG00000000000000000000000000000000
- SP_OIDC_NDI_DISCOVERY_ENDPOINT=http://localhost:5156/singpass/v2/.well-known/openid-configuration
- SP_OIDC_NDI_JWKS_ENDPOINT=http://localhost:5156/singpass/v2/.well-known/keys
- SP_OIDC_RP_CLIENT_ID=rpClientId
Expand Down Expand Up @@ -166,6 +166,14 @@ services:
- STRIPE_API_KEY=secretKey
- STRIPE_DEVICE_NAME=StripeWebhookListener

mocktwilio:
image: stoplight/prism:4
container_name: formsg-mocktwilio
depends_on:
- backend
network_mode: 'service:backend' # reuse backend service's network stack so that it can resolve localhost:4010 to prismtwilio:4010
command: mock https://raw.githubusercontent.com/twilio/twilio-oai/main/spec/json/twilio_api_v2010.json

volumes:
mongodb_data:
driver: local
Loading

0 comments on commit fff7cab

Please sign in to comment.