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

feat(playwright): add playwright testing #33

Merged
merged 55 commits into from
Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
deb0c72
Testing e2e action
benjaminpaige May 18, 2023
146e4da
Testing e2e action
benjaminpaige May 18, 2023
7bfb7c0
remove connect command from ui service
benjaminpaige May 18, 2023
75024a9
remove connect command from ui service
benjaminpaige May 18, 2023
2233306
adjust workflow
benjaminpaige May 18, 2023
a791c6a
exclude e2e from jest
benjaminpaige May 18, 2023
6a11b19
exclude e2e from vitest config
benjaminpaige May 18, 2023
1102f37
update e2e wf action
benjaminpaige May 18, 2023
8605763
install prite browsers
benjaminpaige May 18, 2023
8554437
add envar in action
benjaminpaige May 18, 2023
c323004
export url in job
benjaminpaige May 18, 2023
ffd061a
export url in job
benjaminpaige May 18, 2023
05a5a60
export url in job
benjaminpaige May 18, 2023
7d1ab0c
export url in job
benjaminpaige May 18, 2023
918e008
export url in job
benjaminpaige May 18, 2023
47f1b31
export url in job
benjaminpaige May 18, 2023
dd50878
export url in job
benjaminpaige May 18, 2023
66a8894
export url in job
benjaminpaige May 18, 2023
3788da7
export url in job
benjaminpaige May 18, 2023
36c3651
export url in job
benjaminpaige May 18, 2023
96a2402
export url in job
benjaminpaige May 18, 2023
1e0a1fc
export url in job
benjaminpaige May 18, 2023
02d07e7
test
benjaminpaige May 18, 2023
2949e13
test
benjaminpaige May 18, 2023
d1725f6
test
benjaminpaige May 18, 2023
7b011e5
test
benjaminpaige May 18, 2023
c2911ad
test
benjaminpaige May 18, 2023
cf880ec
cleanup
benjaminpaige May 22, 2023
48176c3
Add placeholder api test
benjaminpaige May 22, 2023
24d9a11
add api sls script
benjaminpaige May 22, 2023
71a02c4
add stage name param
benjaminpaige May 22, 2023
2ab3563
refresh outputs
benjaminpaige May 22, 2023
b7ef03f
add creds to e2e step
benjaminpaige May 22, 2023
71e7340
cleanup
benjaminpaige May 22, 2023
051e809
add issues test
benjaminpaige May 23, 2023
11cb0f1
add crud issue testing
benjaminpaige May 23, 2023
be553c3
remove api testing
benjaminpaige May 25, 2023
dff5bb3
add and use e2e selectors dir
benjaminpaige May 26, 2023
f138c29
update pw add selectors and aliases
benjaminpaige May 30, 2023
b07c1cf
add type props to e2e test
benjaminpaige Jun 1, 2023
41116f2
Merge branch 'master' into play
benjaminpaige Jun 2, 2023
07f3300
update e2e selectors
benjaminpaige Jun 5, 2023
1cf49cd
remove stage from e2e workflow
benjaminpaige Jun 5, 2023
6eb1746
update selectors
benjaminpaige Jun 5, 2023
e045510
dont minify to test
benjaminpaige Jun 5, 2023
3f7f9ab
Set environment variable for production
benjaminpaige Jun 5, 2023
232981b
fix workflow formatting
benjaminpaige Jun 5, 2023
fe26c16
fix workflow formatting
benjaminpaige Jun 5, 2023
bb475e7
testing minify change
benjaminpaige Jun 5, 2023
977ecf4
update artifact path
benjaminpaige Jun 5, 2023
d2b2ca2
update minify logic
benjaminpaige Jun 6, 2023
cb1794e
update minify logic
benjaminpaige Jun 6, 2023
44af9fb
add docs
benjaminpaige Jun 6, 2023
4453b44
add comments
benjaminpaige Jun 6, 2023
dcd6f77
uncomment browsers
benjaminpaige Jun 7, 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
32 changes: 32 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jobs:
environment:
name: ${{ github.ref_name }}
url: ${{ steps.deployment-data.outputs.APPURL }}
outputs:
app-url: ${{ steps.deployment-data.outputs.APPURL }}
steps:
- name: Checkout
uses: actions/checkout@v3
Expand Down Expand Up @@ -83,6 +85,36 @@ jobs:
- name: Test
run: yarn test-ci

e2e:
timeout-minutes: 5
runs-on: ubuntu-20.04
needs:
- deploy
env:
baseurl: ${{ needs.deploy.outputs.app-url }}
if: ${{ github.ref != 'refs/heads/production' }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup
uses: ./.github/actions/setup
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_TO_ASSUME }}
aws-region: us-east-1
role-duration-seconds: 10800
- name: Install Playwright Browsers
run: yarn playwright install --with-deps
- name: Run e2e tests
run: run e2e
- uses: actions/upload-artifact@v3
if: always()
with:
name: playwright-report
path: src/services/ui/playwright-report/
retention-days: 30

cfn-nag:
runs-on: ubuntu-20.04
needs:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/github-pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }}
REPO_NAME: macpro-base-template
REPO_NAME: macpro-om-template
BRANCHES_TO_GENERATE: master
- name: Build with Jekyll
uses: actions/jekyll-build-pages@v1
Expand Down
35 changes: 35 additions & 0 deletions docs/docs/developer-guide/e2e-testing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
layout: default
title: End-to-end Testing
parent: Developer Guide
nav_order: 11

# e2e testing using Playwright
{: .no_toc }

## Table of Contents
{: .no_toc .text-delta }

- TOC
{:toc}

---

## What is Playwright
The Playwright testing framework provides a powerful set of tools and APIs for automating and testing web applications. This guide will introduce you to the basics of using the Playwright framework and its core concepts.

The Playwright framework allows you to write tests that simulate user interactions with web pages, such as clicking buttons, filling forms, and validating page content. It supports multiple web browsers, including Chromium, Firefox, and WebKit, enabling cross-browser testing.

## How do I run tests
As a developer you run e2e tests against a local running instance of the application. By using the `run ui --stage [stage]` command to deploy and init an instance of the application. A second terminal can be used to run the `run e2e` command which will run the playwright tests against the currently running instance of the ui on localhost:5000.

## How do I write tests
The e2e tests for the UI service are located in `src/services/ui/e2e` and defined in .spec files. Use the examples found to guide how to structure your tests.

When defining the selectors for elements make sure to define your selectors in the `e2e/selectors` directory. Defining selectors using react component names and properties is prefered to maintain consistent scalable testing. Other patterns can be used such as xpath, element type/name, and id/data-id/test-id. But should only be used when necessary.

## Running in the pipeline
An action has been added to the deploy github actions workflow that runs the e2e tests after the application deployment against the deployed frontend url. Note: This action will run in all environments except production.

## Additional Notes:

Playwright is very powerful but should be used only to test general purpose user journeys. A test should NOT be create to test every ticket or story. Please review the documentation for additional functionality: [Playwright Docs](https://playwright.dev/docs/intro)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"build:cli": "turbo build:cli",
"test:watch": "turbo test:watch",
"test:gui": "turbo test:gui",
"e2e": "turbo e2e",
"coverage": "vitest run --coverage",
"test-gui": "vitest --ui",
"test-tsc": "tsc --skipLibCheck --noEmit"
Expand Down
9 changes: 9 additions & 0 deletions src/cli/run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,15 @@ yargs(process.argv.slice(2))
);
}
)
.command(
"e2e",
"run e2e tests.",
{},
async () => {
await install_deps_for_services();
await runner.run_command_and_output(`e2e tests`, ["yarn", "e2e"], ".");
}
)
.command("test-gui", "open unit-testing gui for vitest.", {}, async () => {
await install_deps_for_services();
await runner.run_command_and_output(
Expand Down
6 changes: 3 additions & 3 deletions src/services/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@
"private": true,
"devDependencies": {
"@types/aws-lambda": "^8.10.111",
"aws-lambda": "^1.0.7",
"@typescript-eslint/eslint-plugin": "^5.59.0",
"@typescript-eslint/parser": "^5.59.0",
"aws-lambda": "^1.0.7",
"eslint": "^8.38.0",
"eslint-config-custom-server": "*"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "^3.276.0",
"libs": "*",
"shared-types": "*",
"zod": "^3.21.4",
"libs": "*"
"zod": "^3.21.4"
},
"version": "0.0.0",
"scripts": {
Expand Down
5 changes: 4 additions & 1 deletion src/services/ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,7 @@ dist-ssr
*.sln
*.sw?

.env.local
.env.local
/test-results/
/playwright-report/
/playwright/.cache/
33 changes: 33 additions & 0 deletions src/services/ui/e2e/selectors/components/AddIssueForm/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Page } from "@playwright/test";

export class AddIssueFormSelectors {
private page: Page;

constructor(page: Page) {
this.page = page;
}

get addButton() {
return this.page.locator("_react=Button[buttonText=\"Add\"]");
}

get submitButton() {
return this.page.locator("_react=Button[buttonText=\"Submit\"]");
}

get titleInput() {
return this.page.getByLabel("Title");
}

get descriptionInput() {
return this.page.getByLabel("Description");
}

get prioritySelect() {
return this.page.getByRole("combobox", { name: "Priority" });
}

get typeSelect() {
return this.page.getByRole("combobox", { name: "Type" });
}
}
3 changes: 3 additions & 0 deletions src/services/ui/e2e/selectors/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { AddIssueFormSelectors } from "./AddIssueForm/index";

export { AddIssueFormSelectors };
2 changes: 2 additions & 0 deletions src/services/ui/e2e/selectors/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from "./navigation/index";
export * from "./components/index";
19 changes: 19 additions & 0 deletions src/services/ui/e2e/selectors/navigation/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Page } from "@playwright/test";

export class NavSelectors {
private page: Page;

constructor(page: Page) {
this.page = page;
}

get issuesDropDown() {
return this.page.locator(
"_react=NavSection[section.buttonText = \"Issues\"]"
);
}

get allIssuesLink() {
return this.page.locator("_react=Link[text = \"All Issues\"]");
}
}
21 changes: 21 additions & 0 deletions src/services/ui/e2e/tests/home/index.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { test, expect } from "@playwright/test";
import * as $ from "@/selectors";

test("has title", async ({ page }) => {
await page.goto("/");

// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/CMS OM Template/);
});

test("get issues link", async ({ page }) => {
const navSelectors = new $.NavSelectors(page);
await page.goto("/");

// Click the issues link.
await navSelectors.issuesDropDown.click();
await navSelectors.allIssuesLink.click();

// Expects the URL to contain intro.
await expect(page).toHaveURL(/.*issues/);
});
57 changes: 57 additions & 0 deletions src/services/ui/e2e/tests/issues/issues.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { test, expect, Page } from "@playwright/test";
import { v4 as uuidv4 } from "uuid";
import * as $ from "@/selectors";

async function goToIssuesPage(page: Page) {
const navSelectors = new $.NavSelectors(page);
await page.goto("/");

// Click the issues link.
await navSelectors.issuesDropDown.click();
await navSelectors.allIssuesLink.click();
}

test("create issue should require description", async ({ page }) => {
const addIssuesFormSelectors = new $.AddIssueFormSelectors(page);
goToIssuesPage(page);

// fills in all inputs except description
await addIssuesFormSelectors.addButton.click();
await addIssuesFormSelectors.titleInput.fill("Here is a test title");
await addIssuesFormSelectors.prioritySelect.selectOption("medium");
await addIssuesFormSelectors.typeSelect.selectOption("other");
await addIssuesFormSelectors.submitButton.click();

// expects required error to be displayed
await expect(page.getByText("Description is required")).toBeVisible();
});

test("should be able to create and delete an issue", async ({ page }) => {
const addIssuesFormSelectors = new $.AddIssueFormSelectors(page);

const testDesc = uuidv4();
goToIssuesPage(page);

// completes and submits new issues form
await addIssuesFormSelectors.addButton.click();
await addIssuesFormSelectors.titleInput.fill("Here is a test title");
await addIssuesFormSelectors.descriptionInput.fill(testDesc);
await addIssuesFormSelectors.prioritySelect.selectOption("medium");
await addIssuesFormSelectors.typeSelect.selectOption("other");
await addIssuesFormSelectors.submitButton.click();

// expect the page to be rerouted
await expect(page).toHaveURL(/.*issues/);

// expect the issue row to exist
await expect(page.getByRole("cell", { name: testDesc })).toBeVisible();

// Select the "Delete" button using a single XPath selector
const buttonSelector = `//td[text()='${testDesc}']/following-sibling::td/button[@aria-label='Delete button']`;

// Click the "Delete" button
await page.click(buttonSelector);

// expect the row to be deleted
await expect(page.getByRole("cell", { name: testDesc })).not.toBeVisible();
});
Empty file.
7 changes: 5 additions & 2 deletions src/services/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
"type": "module",
"scripts": {
"dev": "vite",
"e2e": "playwright test",
"lint": "eslint 'src/**/*.{ts,tsx}'",
"build": "tsc && vite build",
"preview": "vite preview",
Expand All @@ -26,9 +27,11 @@
"react-hook-form": "^7.43.9",
"react-loader-spinner": "^5.3.4",
"react-router-dom": "^6.10.0",
"uuid": "^9.0.0",
"zod": "^3.21.4"
},
"devDependencies": {
"@playwright/test": "^1.34.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/react": "^18.0.28",
Expand All @@ -46,10 +49,10 @@
"postcss": "^8.4.21",
"serverless-cloudfront-invalidate": "^1.12.2",
"serverless-s3-sync": "^3.1.0",
"shared-types": "*",
"tailwindcss": "^3.3.1",
"typescript": "^4.9.3",
"vite": "^4.2.0",
"vitest": "^0.30.1",
"shared-types": "*"
"vitest": "^0.30.1"
}
}
44 changes: 44 additions & 0 deletions src/services/ui/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { defineConfig, devices } from "@playwright/test";

/**
* 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: process.env.baseurl || "http://localhost:5000",

/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: "on-first-retry",
},

/* Configure projects for major browsers */
// Note: we can test on multiple browsers and resolutions defined here
projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},
{
name: "firefox",
use: { ...devices["Desktop Firefox"] },
},

{
name: "webkit",
use: { ...devices["Desktop Safari"] },
},
],
});
1 change: 1 addition & 0 deletions src/services/ui/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ custom:
echo """
VITE_API_REGION=${param:ApiRegion}
VITE_API_URL=${param:ApiUrl}
VITE_NODE_ENV=${self:custom.stage}
""" > .env.local
yarn build
2 changes: 1 addition & 1 deletion src/services/ui/src/components/MainWrapper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import * as UI from "@enterprise-cmcs/macpro-ux-lib";
import cmsLogo from "@enterprise-cmcs/macpro-ux-lib/build/assets/img/logos/cms_logo.svg";
import { Outlet } from "react-router-dom";

export default function MainWrapper({ children }: React.PropsWithChildren) {
export default function MainWrapper() {
benjaminpaige marked this conversation as resolved.
Show resolved Hide resolved
return (
<div
style={{
Expand Down
Loading