From 1a9187747088acd71466c26d4b7a3ee0bb732a58 Mon Sep 17 00:00:00 2001 From: Debbie O'Brien Date: Mon, 3 Jul 2023 20:21:14 +0200 Subject: [PATCH] add Api mocking example (#5) --- .github/workflows/playwright.yml | 27 +++++++ .gitignore | 4 + README.md | 24 ++++-- ...cc30e4a440111abd545551113b7bf2e2cad209.bin | 70 ++++++++++++++++ hars/fruits.har | 81 +++++++++++++++++++ package-lock.json | 67 +++++++++++++++ package.json | 9 +++ playwright.config.ts | 77 ++++++++++++++++++ tests/api-mocking.spec.ts | 73 +++++++++++++++++ 9 files changed, 424 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/playwright.yml create mode 100644 .gitignore create mode 100644 hars/25cc30e4a440111abd545551113b7bf2e2cad209.bin create mode 100644 hars/fruits.har create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 playwright.config.ts create mode 100644 tests/api-mocking.spec.ts diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml new file mode 100644 index 0000000..5156520 --- /dev/null +++ b/.github/workflows/playwright.yml @@ -0,0 +1,27 @@ +name: Playwright Tests +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Install dependencies + run: npm ci + - name: Install Playwright Browsers + run: npx playwright install --with-deps + - name: Run Playwright tests + run: npx playwright test + - uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..75e854d --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/README.md b/README.md index 5cd7cec..bfc8eb6 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,22 @@ -# Project +# 🎭 Playwright Examples -> This repo has been populated by an initial template to help get you started. Please -> make sure to update the content to build a great experience for community-building. +This repo is used to demonstrate various testing scenarios with [Playwright](https://playwright.dev/) 🎭 with Node.js. -As the maintainer of this project, please make a few updates: +## Run Playwright example tests -- Improving this README.MD file to provide a great experience -- Updating SUPPORT.MD with content about this project's support experience -- Understanding the security reporting process in SECURITY.MD -- Remove this section from the README +### Install dependencies + +Start by cloning the repo and installing the dependencies: + +```bash +npm install +``` + +Use the [VS Code Extension](https://marketplace.visualstudio.com/items?itemName=ms-playwright.playwright) to run the tests in the tests folder from VS Code or run the following command in the terminal: + +```bash +npx playwright test --ui +``` ## Contributing diff --git a/hars/25cc30e4a440111abd545551113b7bf2e2cad209.bin b/hars/25cc30e4a440111abd545551113b7bf2e2cad209.bin new file mode 100644 index 0000000..1604ab2 --- /dev/null +++ b/hars/25cc30e4a440111abd545551113b7bf2e2cad209.bin @@ -0,0 +1,70 @@ +[ + { + "name": "Strawberry", + "id": 3 + }, + { + "name": "Banana", + "id": 1 + }, + { + "name": "Tomato", + "id": 5 + }, + { + "name": "Pear", + "id": 4 + }, + { + "name": "Blackberry", + "id": 64 + }, + { + "name": "Kiwi", + "id": 66 + }, + { + "name": "Pineapple", + "id": 10 + }, + { + "name": "Passionfruit", + "id": 70 + }, + { + "name": "Orange", + "id": 2 + }, + { + "name": "Raspberry", + "id": 23 + }, + { + "name": "Watermelon", + "id": 25 + }, + { + "name": "Lemon", + "id": 26 + }, + { + "name": "Mango", + "id": 27 + }, + { + "name": "Blueberry", + "id": 33 + }, + { + "name": "Apple", + "id": 6 + }, + { + "name": "Melon", + "id": 41 + }, + { + "name": "Lime", + "id": 44 + } +] diff --git a/hars/fruits.har b/hars/fruits.har new file mode 100644 index 0000000..bbbe7f9 --- /dev/null +++ b/hars/fruits.har @@ -0,0 +1,81 @@ +{ + "log": { + "version": "1.2", + "creator": { + "name": "Playwright", + "version": "1.35.1" + }, + "browser": { + "name": "chromium", + "version": "115.0.5790.24" + }, + "entries": [ + { + "startedDateTime": "2023-07-03T18:00:20.971Z", + "time": 0.903, + "request": { + "method": "GET", + "url": "https://demo.playwright.dev/api-mocking/api/v1/fruits", + "httpVersion": "HTTP/2.0", + "cookies": [], + "headers": [ + { "name": ":authority", "value": "demo.playwright.dev" }, + { "name": ":method", "value": "GET" }, + { "name": ":path", "value": "/api-mocking/api/v1/fruits" }, + { "name": ":scheme", "value": "https" }, + { "name": "accept", "value": "*/*" }, + { "name": "accept-encoding", "value": "gzip, deflate, br" }, + { "name": "accept-language", "value": "en-US" }, + { "name": "cache-control", "value": "max-age=0" }, + { "name": "referer", "value": "https://demo.playwright.dev/api-mocking/" }, + { "name": "sec-fetch-dest", "value": "empty" }, + { "name": "sec-fetch-mode", "value": "cors" }, + { "name": "sec-fetch-site", "value": "same-origin" }, + { "name": "user-agent", "value": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.5790.24 Safari/537.36" } + ], + "queryString": [], + "headersSize": -1, + "bodySize": -1 + }, + "response": { + "status": 200, + "statusText": "", + "httpVersion": "HTTP/2.0", + "cookies": [], + "headers": [ + { "name": "accept-ranges", "value": "bytes" }, + { "name": "access-control-allow-origin", "value": "*" }, + { "name": "age", "value": "0" }, + { "name": "cache-control", "value": "max-age=600" }, + { "name": "content-length", "value": "762" }, + { "name": "content-type", "value": "application/octet-stream" }, + { "name": "date", "value": "Mon, 03 Jul 2023 18:00:21 GMT" }, + { "name": "etag", "value": "\"6495697c-2fa\"" }, + { "name": "expires", "value": "Mon, 03 Jul 2023 18:10:21 GMT" }, + { "name": "last-modified", "value": "Fri, 23 Jun 2023 09:44:28 GMT" }, + { "name": "server", "value": "GitHub.com" }, + { "name": "vary", "value": "Accept-Encoding" }, + { "name": "via", "value": "1.1 varnish" }, + { "name": "x-cache", "value": "MISS" }, + { "name": "x-cache-hits", "value": "0" }, + { "name": "x-fastly-request-id", "value": "d56d393516eeca913cb814d35726d5c0a7a84e52" }, + { "name": "x-github-request-id", "value": "C544:114C7:2BA884C:2D0CD20:64A30CB3" }, + { "name": "x-proxy-cache", "value": "MISS" }, + { "name": "x-served-by", "value": "cache-mad2200145-MAD" }, + { "name": "x-timer", "value": "S1688407221.077434,VS0,VE135" } + ], + "content": { + "size": -1, + "mimeType": "application/octet-stream", + "_file": "25cc30e4a440111abd545551113b7bf2e2cad209.bin" + }, + "headersSize": -1, + "bodySize": -1, + "redirectURL": "" + }, + "cache": {}, + "timings": { "send": -1, "wait": -1, "receive": 0.903 } + } + ] + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..863a60f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,67 @@ +{ + "name": "playwright-examples", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "playwright-examples", + "version": "1.0.0", + "license": "ISC", + "devDependencies": { + "@playwright/test": "^1.35.1" + } + }, + "node_modules/@playwright/test": { + "version": "1.35.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.35.1.tgz", + "integrity": "sha512-b5YoFe6J9exsMYg0pQAobNDR85T1nLumUYgUTtKm4d21iX2L7WqKq9dW8NGJ+2vX0etZd+Y7UeuqsxDXm9+5ZA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "playwright-core": "1.35.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/@types/node": { + "version": "20.3.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.2.tgz", + "integrity": "sha512-vOBLVQeCQfIcF/2Y7eKFTqrMnizK5lRNQ7ykML/5RuwVXVWxYkgwS7xbt4B6fKCUPgbSL5FSsjHQpaGQP/dQmw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/playwright-core": { + "version": "1.35.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.35.1.tgz", + "integrity": "sha512-pNXb6CQ7OqmGDRspEjlxE49w+4YtR6a3X6mT1hZXeJHWmsEz7SunmvZeiG/+y1yyMZdHnnn73WKYdtV1er0Xyg==", + "dev": true, + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=16" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e026bbf --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "name": "playwright-examples", + "version": "0.0.1", + "description": "This repo is used to demonstrate various testing scenarios with [Playwright](https://playwright.dev/) 🎭 with node.js", + "license": "Apache-2.0", + "devDependencies": { + "@playwright/test": "^1.35.1" + } +} diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..301801e --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,77 @@ +import { defineConfig, devices } from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './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: 'http://127.0.0.1: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, + // }, +}); diff --git a/tests/api-mocking.spec.ts b/tests/api-mocking.spec.ts new file mode 100644 index 0000000..8e507af --- /dev/null +++ b/tests/api-mocking.spec.ts @@ -0,0 +1,73 @@ +import { test, expect, type Page } from '@playwright/test'; + +test.describe('Mocking an API call', () => { + + test('mocks a fruit and does not call api', async ({ page }) => { + // Mock the api call before navigating + await page.route('*/**/api/v1/fruits', async (route) => { + const json = [{ name: 'Strawberry', id: 21 }]; + await route.fulfill({ json }); + }); + // Go to the page + await page.goto('https://demo.playwright.dev/api-mocking'); + + // Assert that the Strawberry fruit is visible + await expect(page.getByText('Strawberry')).toBeVisible(); + }); + +}); + +test.describe('Intercepting the request and modifying it', () => { + + test('gets the json from api and adds a new fruit', async ({ page }) => { + // Get the response and add to it + await page.route('*/**/api/v1/fruits', async (route) => { + const response = await route.fetch(); + const json = await response.json(); + json.push({ name: 'Playwright', id: 100 }); + // Fulfill using the original response, while patching the response body + // with the given JSON object. + await route.fulfill({ response, json }); + }); + + // Go to the page + await page.goto('https://demo.playwright.dev/api-mocking'); + + // Assert that the new fruit is visible + await expect(page.getByText('Playwright', { exact: true })).toBeVisible(); + }); + +}); + +test.describe('Mocking with HAR files', () => { + + test('records or updates the HAR file', async ({ page }) => { + // Get the response from the HAR file + await page.routeFromHAR('./hars/fruits.har', { + url: '*/**/api/v1/fruits', + update: true, + }); + + // Go to the page + await page.goto('https://demo.playwright.dev/api-mocking'); + + // Assert that the Playwright fruit is visible + await expect(page.getByText('Strawberry')).toBeVisible(); + }); + + test('gets the json from HAR and checks the new fruit has been added', async ({ page }) => { + // Replay API requests from HAR. + // Either use a matching response from the HAR, + // or abort the request if nothing matches. + await page.routeFromHAR('./hars/fruits.har', { + url: '*/**/api/v1/fruits', + update: false, + }); + + // Go to the page + await page.goto('https://demo.playwright.dev/api-mocking'); + + // Assert that the Playwright fruit is visible + await expect(page.getByText('Strawberry')).toBeVisible(); + }); +});