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

[QAE-1183] add first playwright test #413

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
38 changes: 38 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: e2e

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
playwright:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.45.0-jammy
timeout-minutes: 60

steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- name: Use Node.js 20.10.0
uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
with:
node-version: '20.10.0'
cache: 'yarn'
- name: Install dependencies
run: |
rm -rf node_modules && rm -rf ./react-example/node_modules
yarn install --frozen-lockfile && yarn --cwd ./react-example install --frozen-lockfile
- name: Start React Sample App
run: yarn start-without-watch:all:react
- name: Run Playwright tests
run: yarn playwright test
- uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: playwright-report
path: playwright-report/
retention-days: 30
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@
},
"scripts": {
"start": "concurrently \"ttsc -w\" \"babel src --watch --out-dir ./dist --extensions '.ts,.tsx'\" \"webpack-dev-server --config webpack.dev.config.js\" -n 'tsc,babel,webpack' -k",
"start-without-watch": "concurrently \"webpack-dev-server --config webpack.dev.config.js\" -n 'tsc,webpack' -k",
"install:all": "yarn && cd example && yarn && cd ../react-example && yarn",
"start:all": "concurrently \"yarn start\" \"cd example && yarn start\" -n 'module,website' -k",
"start:all:react": "concurrently \"yarn start\" \"cd react-example && yarn start\" -n 'module,website' -k",
"start-without-watch:all:react": "concurrently \"yarn start-without-watch\" \"cd react-example && yarn start-without-watch\" -n 'module,website' -k",
"build": "ttsc && babel src --out-dir ./dist --extensions '.ts,.tsx' && webpack",
"build:node": "yarn build --config webpack.node.config.js",
"test": "jest --config jest.config.js --collectCoverage",
Expand Down Expand Up @@ -113,4 +115,4 @@
"eslint"
]
}
}
}
60 changes: 60 additions & 0 deletions react-example/e2e/pages/commercePage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { type Locator, type Page } from '@playwright/test';

export class CommercePage {
readonly page: Page;
readonly cartInput: Locator;
readonly purchaseInput: Locator;
readonly cartSubmitButton: Locator;
readonly purchaseSubmitButton: Locator;
readonly cartResponse: Locator;
readonly purchaseResponse: Locator;

constructor(page: Page) {
this.page = page;
this.cartInput = page.getByTestId('cart-input');
this.cartSubmitButton = page.getByTestId('cart-submit');
this.cartResponse = page.getByTestId('cart-response');
this.purchaseInput = page.getByTestId('purchase-input');
this.purchaseSubmitButton = page.getByTestId('purchase-submit');
this.purchaseResponse = page.getByTestId('purchase-response');
}

async goto() {
await this.page.goto('/commerce');
}

async mock200POSTPurchaseRequest() {
await this.page.route(
'https://api.iterable.com/api/commerce/trackPurchase',
async (route) => {
const json = {
msg: 'success mocked from playwright',
code: 'Success',
params: {
id: 'mock-playwright-id'
}
};
await route.fulfill({ json });
}
);
}

async mock400POSTPurchaseRequest() {
await this.page.route(
'https://api.iterable.com/api/commerce/trackPurchase',
async (route) => {
const json = {
code: 'GenericError',
msg: 'Client-side error mocked from playwright',
clientErrors: [
{
error: 'items[0].name is a required field',
field: 'items[0].name'
}
]
};
await route.fulfill({ json });
}
);
}
}
43 changes: 43 additions & 0 deletions react-example/e2e/test/commerce.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { test, expect } from '@playwright/test';
import { CommercePage } from '../pages/commercePage';

test.describe('Commerce tests', () => {
test.beforeEach(async ({ page }) => {
const commercePage = new CommercePage(page);
await commercePage.goto();
});

test('200 POST purchase request', async ({ page }) => {
const commercePage = new CommercePage(page);
await commercePage.mock200POSTPurchaseRequest();

await commercePage.purchaseInput.fill('SomeItem');
await commercePage.purchaseSubmitButton.click();
await expect(commercePage.purchaseResponse).toContainText(
JSON.stringify({
msg: 'success mocked from playwright',
code: 'Success',
params: {
id: 'mock-playwright-id'
}
})
);
});

test('400 POST purchase request', async ({ page }) => {
const commercePage = new CommercePage(page);
await commercePage.mock400POSTPurchaseRequest();

await commercePage.purchaseInput.fill('SomeItem');
await commercePage.purchaseSubmitButton.click();
await expect(commercePage.purchaseResponse).toContainText(
JSON.stringify({
code: 'GenericError',
msg: 'Client-side error mocked from playwright',
clientErrors: [
{ error: 'items[0].name is a required field', field: 'items[0].name' }
]
})
);
});
});
6 changes: 3 additions & 3 deletions react-example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
],
"scripts": {
"build": "tsc && webpack",
"start": "concurrently \"tsc -w --pretty\" \"webpack-dev-server\" -n 'tsc,webpack' -k",
"start": "concurrently \"tsc -w --pretty\" \"webpack-dev-server\" -n 'tsc,webpack' -k --watch",
"start-without-watch": "concurrently \"tsc -w --pretty\" \"webpack-dev-server\" -n 'tsc,webpack' -k",
"test": "jest --config jest.config.js",
"format": "prettier --write \"src/**/*.{ts,tsx}\" \"src/**/*.js\"",
"typecheck": "tsc --noEmit true --emitDeclarationOnly false",
"lint": "eslint . --ext '.ts,.tsx'",
"cypress": "cypress open"
"lint": "eslint . --ext '.ts,.tsx'"
},
"devDependencies": {
"@babel/core": "^7.5.0",
Expand Down
5 changes: 2 additions & 3 deletions react-example/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ export default defineConfig({
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',

baseURL: 'http://127.0.0.1:8080',
testIdAttribute: 'data-test',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry'
},
Expand Down
22 changes: 18 additions & 4 deletions react-example/src/views/Commerce.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,34 +67,48 @@ export const Commerce: FC<Props> = () => {
<Form onSubmit={handleUpdateCart} data-qa-cart-submit>
<label htmlFor="item-1">Enter Item Name</label>
<TextField
data-test="cart-input"
value={cartItem}
onChange={(e) => setCartItem(e.target.value)}
id="item-1"
placeholder="e.g. keyboard"
data-qa-cart-input
/>
<Button disabled={isUpdatingCart} type="submit">
<Button
data-test="cart-submit"
disabled={isUpdatingCart}
type="submit"
>
Submit
</Button>
</Form>
<Response data-qa-cart-response>{updateCartResponse}</Response>
<Response data-test="cart-response" data-qa-cart-response>
{updateCartResponse}
</Response>
</EndpointWrapper>
<Heading>POST /trackPurchase</Heading>
<EndpointWrapper>
<Form onSubmit={handleTrackPurchase} data-qa-purchase-submit>
<label htmlFor="item-2">Enter Item Name</label>
<TextField
data-test="purchase-input"
value={purchaseItem}
onChange={(e) => setPurchaseItem(e.target.value)}
id="item-2"
placeholder="e.g. keyboard"
data-qa-purchase-input
/>
<Button disabled={isTrackingPurchase} type="submit">
<Button
data-test="purchase-submit"
disabled={isTrackingPurchase}
type="submit"
>
Submit
</Button>
</Form>
<Response data-qa-purchase-response>{trackPurchaseResponse}</Response>
<Response data-test="purchase-response" data-qa-purchase-response>
{trackPurchaseResponse}
</Response>
</EndpointWrapper>
</>
);
Expand Down
1 change: 1 addition & 0 deletions react-example/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ module.exports = {
filename: 'index.js',
path: path.resolve(__dirname, 'dist')
},
watch: process.argv.indexOf('--watch') > -1,
module: {
rules: [
{
Expand Down
Loading