Skip to content

Commit

Permalink
🌟UI testing - Submit an enquiry form (#1464)
Browse files Browse the repository at this point in the history
* UI Testing - adding test cases

* Running example for create lead

* Refactoring the code

* Adding extension for playwright

* disabling prettier log error

* Adding env variable for target_URL for testing

* Adding key reference in PR and main Slot's config

* Updating yarn.lock conflict

* Removing test-examples from the project

* Flow - Adding enquiry flow to run the tests

* Updating the env variable

* Adding trailing double quote

* Updating secret with env variables for test run

* Adding mask to the secret

* Adding workflow_dispatch variable

* Refactoring code

* Changing the retention day

* Removing the pull request branches

* Matt's Feedback

* Adding doc for running it on local

* Updating host url to prod

* Renamed the variable name

* Update create-lead

* removing Id from util button

* Matt's feedback 2

* Update pages/api/create-lead.ts

Co-authored-by: Matt Wicks [SSW] <[email protected]>

* Update pages/api/create-lead.ts

Co-authored-by: Matt Wicks [SSW] <[email protected]>

* Update components/bookingButton/bookingButton.tsx

Co-authored-by: Matt Wicks [SSW] <[email protected]>

* Update components/bookingButton/bookingButton.tsx

Co-authored-by: Matt Wicks [SSW] <[email protected]>

* Updated filename to make it more descriptive

* Update .github/workflows/weekly-ui-tests.yml

* ♻️ general cleanup
- ui tests - renamed file to match component being tests
- ui tests - dry'd up code, preferred placeholders over attribute selectors where possible
- changed name of secret to bypass recaptcha (prefixed with recaptcha)
- playwright config - base url for tests should be set at this level
- readme - updated to run the ui tests the same way as the github action
- env file - updated example as it was out of sync with real settings

* lint fixes

---------

Co-authored-by: Matt Wicks [SSW] <[email protected]>
Co-authored-by: Matt Wicks <[email protected]>
  • Loading branch information
3 people authored Oct 5, 2023
1 parent 6417974 commit 895ae4a
Show file tree
Hide file tree
Showing 15 changed files with 314 additions and 18 deletions.
20 changes: 15 additions & 5 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,32 @@ NEXT_PUBLIC_TINA_BRANCH=***
# These are analytics tags set in the repository secrets
NEXT_PUBLIC_GOOGLE_GTM_ID=***
NEXT_PUBLIC_ZENDESK_CHAT_KEY=***
NEXT_PUBLIC_APP_INSIGHT_CONNECTION_STRING=***

# Integration endpoints
NEWSLETTERS_ENDPOINT=***
CREATE_LEAD_ENDPOINT=***

# displayed in the footer
NEXT_PUBLIC_GITHUB_RUN_DATE=2021-09-23T05:00:00.000Z

# Current site URL
SITE_URL=***

# Client-side recaptcha key, to be removed
GOOGLE_RECAPTCHA_SITE_KEY=***

# New server-side recaptcha key - Previously it was GOOGLE_RECAPTCHA_KEY_v2
# Recaptcha Keys
GOOGLE_RECAPTCHA_KEY=***
GOOGLE_RECAPTCHA_SITE_KEY=***
RECAPTCHA_BYPASS_SECRET=foobar

# Microsoft OAuth Keys
# Microsoft OAuth Keys
MICROSOFT_OAUTH_TENANT_ID=***
MICROSOFT_OAUTH_CLIENT_ID=***
MICROSOFT_OAUTH_CLIENT_SECRET=***

# SharePoint Site and List IDs
MICROSOFT_OAUTH_TENANT_ID=***
MICROSOFT_OAUTH_CLIENT_ID=***
MICROSOFT_OAUTH_CLIENT_SECRET=***
SHAREPOINT_SITE_ID=***
SHAREPOINT_EVENTS_LIST_ID=***
SHAREPOINT_EXTERNAL_PRESENTERS_LIST_ID=***
Expand Down
79 changes: 79 additions & 0 deletions .github/workflows/weekly-ui-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
name: Weekly UI Test - Enquiry Form

on:
# workflow_dispatch:

schedule:
# Monday at 9 PM UTC ~> 7 AM AEST - https://cron.help/#0_21_*_*_SUN
- cron: "0 21 * * SUN"
#pull_request:
# branches:
# - main
workflow_dispatch:
inputs:
HOST_URL:
type: string
description: "Host URL"
default: "https://ssw.com.au"

env:
GH_TOKEN: ${{ github.token }}

defaults:
run:
shell: pwsh

permissions:
id-token: write
contents: read

jobs:
test:
timeout-minutes: 10
runs-on: ubuntu-latest

steps:
- name: Setup node
uses: actions/setup-node@v3
with:
node-version-file: ".nvmrc"

- name: Load .env file
uses: xom9ikk/dotenv@v2
with:
path: ./.github

- name: Azure CLI - Login
uses: azure/login@v1
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}

- name: keyVault - Get Secret Key note
id: KeyVaultSecrets
run: |
# KV - Secret
$RECAPTCHA_BYPASS_SECRET = (az keyvault secret show --name SECRET-KEY-TO-BYPASS-RECAPTCHA --vault-name ${{ env.KEY_VAULT }} --query value -o tsv)
echo "::add-mask::$RECAPTCHA_BYPASS_SECRET"
echo "RECAPTCHA_BYPASS_SECRET=$RECAPTCHA_BYPASS_SECRET" >> $env:GITHUB_OUTPUT
Write-Host '✅ KV - Secret retrieved'
- name: Install dependencies
run: yarn

- name: Install Playwright Browsers
run: yarn playwright install --with-deps

- name: Run Playwright tests
run: yarn playwright test
env:
HOST_URL: ${{ inputs.HOST_URL }}
RECAPTCHA_BYPASS_SECRET: ${{ steps.KeyVaultSecrets.outputs.RECAPTCHA_BYPASS_SECRET }}

- uses: actions/upload-artifact@v2
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 14
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,7 @@ yarn-error.log*
# Partytown lib files
/public/~partytown

# Playwright output
/test-results/
/playwright-report/
/playwright/.cache/
13 changes: 7 additions & 6 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"recommendations": [
"bradlc.vscode-tailwindcss",
"esbenp.prettier-vscode",
"graphql.vscode-graphql",
"dbaeumer.vscode-eslint"
]
"recommendations": [
"bradlc.vscode-tailwindcss",
"esbenp.prettier-vscode",
"graphql.vscode-graphql",
"dbaeumer.vscode-eslint",
"ms-playwright.playwright"
]
}
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ syncyarnlock -s -k
- <http://localhost:3000/exit-admin> : log out of Tina Cloud
- <http://localhost:4001/altair/> : GraphQL playground to test queries and browse the API documentation

### UI Testing on local

- 1 Make sure you have `CREATE_LEAD_ENDPOINT` environment variable in your `.env` - [Follow steps to setup](https://github.com/SSWConsulting/SSW.Website/wiki/Accessing-the-Third%E2%80%90Party-APIs-Locally)

- 2 Run `yarn playwright test --ui` in your terminal and make sure your local instance is running in the background.

## Pull Requests

Each Pull Request will be deployed to its own staging environment, the URL to the environment is available in the PR thread.
Expand Down
2 changes: 1 addition & 1 deletion components/bookingButton/bookingButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const BookingButton = ({ data }) => {

const showSuccessToast = () => {
toast.success(
<div className="text-left">
<div id="success-toaster" className="text-left">
Form submitted. We'll be in contact as soon as possible.
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion components/successToast/successToast.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "react-toastify/dist/ReactToastify.css";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";

const SuccessToast = () => {
return (
Expand Down
4 changes: 4 additions & 0 deletions infra/appSerivce-create-slot.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ var appSettings = [
name: 'SHAREPOINT_EXTERNAL_PRESENTERS_LIST_ID'
value: '@Microsoft.KeyVault(SecretUri=https://${keyVaultName}.vault.azure.net/secrets/SHAREPOINT-EXTERNAL-PRESENTERS-LIST-ID)'
}
{
name: 'RECAPTCHA_BYPASS_SECRET'
value: '@Microsoft.KeyVault(SecretUri=https://${keyVaultName}.vault.azure.net/secrets/SECRET-KEY-TO-BYPASS-RECAPTCHA)'
}
]


Expand Down
4 changes: 4 additions & 0 deletions infra/appService.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,10 @@ var appSettings = [
name: 'SHAREPOINT_EXTERNAL_PRESENTERS_LIST_ID'
value: '@Microsoft.KeyVault(SecretUri=https://${keyVaultName}.vault.azure.net/secrets/SHAREPOINT-EXTERNAL-PRESENTERS-LIST-ID)'
}
{
name: 'RECAPTCHA_BYPASS_SECRET'
value: '@Microsoft.KeyVault(SecretUri=https://${keyVaultName}.vault.azure.net/secrets/SECRET-KEY-TO-BYPASS-RECAPTCHA)'
}
]

resource appService 'Microsoft.Web/sites@2022-03-01' = {
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
},
"devDependencies": {
"@next/eslint-plugin-next": "^13.4.19",
"@playwright/test": "^1.38.1",
"@svgr/webpack": "^8.1.0",
"@tinacms/cli": "^1.5.29",
"@types/js-cookie": "^3.0.4",
Expand Down Expand Up @@ -51,6 +52,7 @@
"axios": "^1.5.1",
"classnames": "^2.3.2",
"dayjs": "^1.11.10",
"dotenv": "^16.3.1",
"eslint-config-next": "13.5.3",
"formik": "^2.4.5",
"next": "13.5.2",
Expand Down
24 changes: 22 additions & 2 deletions pages/api/create-lead.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,36 @@ import { invokePowerAutomateFlow } from "../../services/server/power-automate-fl

import { CustomError } from "../../services/server/customError";

const RECAPATCHA_VALIDATION_SUCCESS_RESULT = {
data: {
success: true,
},
status: 200,
};

export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
try {
if (req.method === "POST") {
// this is the code to validate with the recaptcha service
const { Recaptcha } = req.body;

const Note = req.body.Note;
// Note: bypassing recaptcha is intended for weekly testing the lead capture form only
const key_matched = Note.includes(process.env.RECAPTCHA_BYPASS_SECRET);

if (key_matched) {
req.body.Note = Note.replace(process.env.RECAPTCHA_BYPASS_SECRET, "");
}

// Documentation - Create Lead - https://sswcom.sharepoint.com/:w:/r/sites/SSWDevelopers/_layouts/15/Doc.aspx?sourcedoc=%7BE8A18D9B-DE74-47EC-B836-01A5AD193DCC%7D&file=Create-lead-Flow.docx&action=default&mobileredirect=true
if (Recaptcha) {
const recaptchaValidation = await validateRecaptcha(Recaptcha);
if (Recaptcha || key_matched) {
// Recaptcha value provided by google Recaptcha API
const recaptchaValidation = key_matched
? RECAPATCHA_VALIDATION_SUCCESS_RESULT
: await validateRecaptcha(Recaptcha);
// const recaptchaValidation = { data: { success: true } }; // uncomment this to bypass recaptcha for testing purpose
if (recaptchaValidation && recaptchaValidation.data.success) {
const createLeadFlow = await invokePowerAutomateFlow(
Expand Down
4 changes: 3 additions & 1 deletion pages/consulting/[filename].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ export default function ConsultingPage(

return (
<RecaptchaContext.Provider
value={{ recaptchaKey: props.env.GOOGLE_RECAPTCHA_SITE_KEY }}
value={{
recaptchaKey: props.env.GOOGLE_RECAPTCHA_SITE_KEY,
}}
>
<SEO seo={props.seo} />
<Layout>
Expand Down
79 changes: 79 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { defineConfig, devices } from "@playwright/test";

/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/

// eslint-disable-next-line @typescript-eslint/no-var-requires
require("dotenv").config();

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./ui-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: process.env.HOST_URL || "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 start',
// url: 'http://127.0.0.1:3000',
// reuseExistingServer: !process.env.CI,
// },
});
41 changes: 41 additions & 0 deletions ui-tests/components/bookingForm.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { expect, test } from "@playwright/test";

test("Can submit the booking form", async ({ page }) => {
await page.goto("/consulting/angular", { waitUntil: "networkidle" });

await page
.getByRole("button")
.getByText("Book a FREE Initial Meeting")
.first()
.click();

await page.waitForSelector(".react-responsive-modal-root form");

// note: preference would be to reference by label, but the names aren't consistent with the field names
// note: getByPlaceholder is doing a partial match (the asterisks are hardcoded into the placeholder text) 🤮
await page.getByPlaceholder("your full name").fill("🧪 Test");
await page.getByPlaceholder("your email").fill("[email protected]");
await page.getByPlaceholder("your phone").fill("0000000000");
await page.locator("select[name='location']").selectOption("Australia"); // placeholder looks like location, but its actually an unselectable option
await page.locator("select[name='states']").selectOption("100000001");
await page.getByPlaceholder("your company").fill("Test");
await page.locator("select[name='referralSource']").selectOption("14");
await page
.getByPlaceholder("how can we help you")
.fill(
`<br><br>Hi Account Managers,<br><br> This is a weekly test email to verify that the create lead flow is working. ${
process.env.RECAPTCHA_BYPASS_SECRET ?? "No Key found"
}`
);

await page.locator("button[type='submit']").first().click();

const successToastId = "#success-toaster";
await page.waitForSelector(successToastId);
const toastElement = await page.locator(successToastId).first();
const toastText = await toastElement.textContent();

await expect(toastText).toBe(
"Form submitted. We'll be in contact as soon as possible."
);
});
Loading

0 comments on commit 895ae4a

Please sign in to comment.