docs(test runner): put more example in various sections (#6812)
This commit is contained in:
parent
98fc8b1739
commit
5fd15d8a5e
|
|
@ -1,12 +1,10 @@
|
||||||
---
|
---
|
||||||
id: test-advanced
|
id: test-advanced
|
||||||
title: "Advanced: Configuration"
|
title: "Advanced: configuration"
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
## Project configuration
|
## Project configuration
|
||||||
|
|
||||||
- `metadata: any` - Any JSON-serializable metadata that will be put directly to the test report.
|
- `metadata: any` - Any JSON-serializable metadata that will be put directly to the test report.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
id: test-cli
|
id: test-cli
|
||||||
title: "Advanced: Command Line"
|
title: "Advanced: command line"
|
||||||
---
|
---
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
|
|
|
||||||
|
|
@ -305,3 +305,127 @@ There are many more things you can do with projects:
|
||||||
:::note
|
:::note
|
||||||
`--browser` command line option is not compatible with projects. Specify `browserName` in each project instead.
|
`--browser` command line option is not compatible with projects. Specify `browserName` in each project instead.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
## Mobile emulation
|
||||||
|
|
||||||
|
You can use configuration file to make default `context` emulate a mobile device.
|
||||||
|
|
||||||
|
Here is an example configuration that runs tests in "Pixel 4" and "iPhone 11" emulation modes. Note that it uses the [projects](./test-advanced.md#projects) feature to run the same set of tests in multiple configurations.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// playwright.config.js
|
||||||
|
const { devices } = require('playwright');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
projects: [
|
||||||
|
// "Pixel 4" tests use Chromium browser.
|
||||||
|
{
|
||||||
|
name: 'Pixel 4',
|
||||||
|
use: {
|
||||||
|
browserName: 'chromium',
|
||||||
|
...devices['Pixel 4'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// "iPhone 11" tests use WebKit browser.
|
||||||
|
{
|
||||||
|
name: 'iPhone 11',
|
||||||
|
use: {
|
||||||
|
browserName: 'webkit',
|
||||||
|
...devices['iPhone 11'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// playwright.config.ts
|
||||||
|
import { PlaywrightTestConfig } from 'playwright/test';
|
||||||
|
import { devices } from 'playwright';
|
||||||
|
|
||||||
|
const config: PlaywrightTestConfig = {
|
||||||
|
projects: [
|
||||||
|
// "Pixel 4" tests use Chromium browser.
|
||||||
|
{
|
||||||
|
name: 'Pixel 4',
|
||||||
|
use: {
|
||||||
|
browserName: 'chromium',
|
||||||
|
...devices['Pixel 4'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// "iPhone 11" tests use WebKit browser.
|
||||||
|
{
|
||||||
|
name: 'iPhone 11',
|
||||||
|
use: {
|
||||||
|
browserName: 'webkit',
|
||||||
|
...devices['iPhone 11'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Network mocking
|
||||||
|
|
||||||
|
You don't have to configure anything to mock network requests. Just define a custom [Route] that mocks network for a browser context.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// example.spec.js
|
||||||
|
const { test, expect } = require('playwright/test');
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
// Block any css requests for each test in this file.
|
||||||
|
await context.route(/.css/, route => route.abort());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('loads page without css', async ({ page }) => {
|
||||||
|
await page.goto('https://playwright.dev');
|
||||||
|
// ... test goes here
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// example.spec.ts
|
||||||
|
import { test, expect } from 'playwright/test';
|
||||||
|
|
||||||
|
test.beforeEach(async ({ context }) => {
|
||||||
|
// Block any css requests for each test in this file.
|
||||||
|
await context.route(/.css/, route => route.abort());
|
||||||
|
});
|
||||||
|
|
||||||
|
test('loads page without css', async ({ page }) => {
|
||||||
|
await page.goto('https://playwright.dev');
|
||||||
|
// ... test goes here
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Alternatively, you can use [`method: Page.route`] to mock network in a single test.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// example.spec.js
|
||||||
|
const { test, expect } = require('playwright/test');
|
||||||
|
|
||||||
|
test('loads page without images', async ({ page }) => {
|
||||||
|
// Block png and jpeg images.
|
||||||
|
await page.route(/(png|jpeg)$/, route => route.abort());
|
||||||
|
|
||||||
|
await page.goto('https://playwright.dev');
|
||||||
|
// ... test goes here
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// example.spec.ts
|
||||||
|
import { test, expect } from 'playwright/test';
|
||||||
|
|
||||||
|
test('loads page without images', async ({ page }) => {
|
||||||
|
// Block png and jpeg images.
|
||||||
|
await page.route(/(png|jpeg)$/, route => route.abort());
|
||||||
|
|
||||||
|
await page.goto('https://playwright.dev');
|
||||||
|
// ... test goes here
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -1,145 +0,0 @@
|
||||||
---
|
|
||||||
id: test-examples
|
|
||||||
title: "Examples"
|
|
||||||
---
|
|
||||||
|
|
||||||
<!-- TOC -->
|
|
||||||
|
|
||||||
## Multiple pages
|
|
||||||
|
|
||||||
The default `context` argument is a [BrowserContext]. Browser contexts are isolated execution environments that can host multiple pages. See [multi-page scenarios](./multi-pages.md) for more examples.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// example.spec.js
|
|
||||||
const { test } = require('playwright/test');
|
|
||||||
|
|
||||||
test('tests on multiple web pages', async ({ context }) => {
|
|
||||||
const pageFoo = await context.newPage();
|
|
||||||
const pageBar = await context.newPage();
|
|
||||||
// Test function
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// example.spec.ts
|
|
||||||
import { test } from 'playwright/test';
|
|
||||||
|
|
||||||
test('tests on multiple web pages', async ({ context }) => {
|
|
||||||
const pageFoo = await context.newPage();
|
|
||||||
const pageBar = await context.newPage();
|
|
||||||
// Test function
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Mobile emulation
|
|
||||||
|
|
||||||
`use` section in the configuration file can be used to configure mobile emulation in the default `context`.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// config.ts
|
|
||||||
import { PlaywrightTestConfig } from "playwright/test";
|
|
||||||
import { devices } from "playwright";
|
|
||||||
|
|
||||||
const config: PlaywrightTestConfig = {
|
|
||||||
timeout: 30000,
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'chromium',
|
|
||||||
use: {
|
|
||||||
browserName: 'chromium',
|
|
||||||
headless: true,
|
|
||||||
...devices["Pixel 2"],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
export default config;
|
|
||||||
```
|
|
||||||
|
|
||||||
## Network mocking
|
|
||||||
|
|
||||||
Define a custom route that mocks network calls for a browser context.
|
|
||||||
|
|
||||||
```js
|
|
||||||
// In foo.spec.ts
|
|
||||||
import { test, expect } from "playwright/test";
|
|
||||||
|
|
||||||
test.beforeEach(async ({ context }) => {
|
|
||||||
// Block any css requests for each test in this file.
|
|
||||||
await context.route(/.css/, route => route.abort());
|
|
||||||
});
|
|
||||||
|
|
||||||
test("loads page without css", async ({ page }) => {
|
|
||||||
// Alternatively, block any png requests just for this test.
|
|
||||||
await page.route(/.png/, route => route.abort());
|
|
||||||
|
|
||||||
// Test function code.
|
|
||||||
await page.goto("https://stackoverflow.com");
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Visual comparisons
|
|
||||||
|
|
||||||
The `expect` API supports visual comparisons with `toMatchSnapshot`. This uses the [pixelmatch](https://github.com/mapbox/pixelmatch) library, and you can pass `threshold` as an option.
|
|
||||||
|
|
||||||
```js
|
|
||||||
import { test, expect } from "playwright/test";
|
|
||||||
|
|
||||||
test("compares page screenshot", async ({ page }) => {
|
|
||||||
await page.goto("https://stackoverflow.com");
|
|
||||||
const screenshot = await page.screenshot();
|
|
||||||
expect(screenshot).toMatchSnapshot(`test.png`, { threshold: 0.2 });
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
On first execution, this will generate golden snapshots. Subsequent runs will compare against the golden snapshots. To update golden snapshots with new actual values, run with the `--update-snapshots` flag.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Update golden snapshots when they differ from actual
|
|
||||||
npx playwright test --update-snapshots
|
|
||||||
```
|
|
||||||
|
|
||||||
### Page object model
|
|
||||||
|
|
||||||
To introduce a Page Object for a particular page, create a class that will use the `page` object.
|
|
||||||
|
|
||||||
Create a `LoginPage` helper class to encapsulate common operations on the login page.
|
|
||||||
```js
|
|
||||||
// login-page.ts
|
|
||||||
import type { Page } from "playwright";
|
|
||||||
|
|
||||||
export class LoginPage {
|
|
||||||
page: Page;
|
|
||||||
|
|
||||||
constructor(page: Page) {
|
|
||||||
this.page = page;
|
|
||||||
}
|
|
||||||
|
|
||||||
async goto() {
|
|
||||||
await this.page.goto("https://example.com/login");
|
|
||||||
}
|
|
||||||
|
|
||||||
async login() {
|
|
||||||
await this.page.fill("#username", TEST_USERNAME);
|
|
||||||
await this.page.fill("#password", TEST_PASSWORD);
|
|
||||||
await this.page.click("text=Login");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Use the `LoginPage` class in the tests.
|
|
||||||
```js
|
|
||||||
// my.spec.ts
|
|
||||||
import { test, expect } from "playwright/test";
|
|
||||||
import { LoginPage } from "./login-page";
|
|
||||||
|
|
||||||
test('login works', async ({ page }) => {
|
|
||||||
// Create the login page and perform operations.
|
|
||||||
const loginPage = new LoginPage(page);
|
|
||||||
await loginPage.goto();
|
|
||||||
await loginPage.login();
|
|
||||||
|
|
||||||
// Verify it worked.
|
|
||||||
expect(await page.textContent("#user-info")).toBe("Welcome, Test User!");
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
id: test-fixtures
|
id: test-fixtures
|
||||||
title: "Advanced: Fixtures"
|
title: "Advanced: fixtures"
|
||||||
---
|
---
|
||||||
|
|
||||||
<!-- TOC -->
|
<!-- TOC -->
|
||||||
|
|
|
||||||
144
docs/src/test-pom.md
Normal file
144
docs/src/test-pom.md
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
---
|
||||||
|
id: test-pom
|
||||||
|
title: "Page Object Model"
|
||||||
|
---
|
||||||
|
|
||||||
|
Page Object Model is a common pattern that introduces abstractions over web app pages to simplify interactions with them in multiple tests. It is best explained by an example.
|
||||||
|
|
||||||
|
We will create a `PlaywrightDevPage` helper class to encapsulate common operations on the `playwright.dev` page. Internally, it will use the `page` object.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// playwright-dev-page.js
|
||||||
|
exports.PlaywrightDevPage = class PlaywrightDevPage {
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
async goto() {
|
||||||
|
await this.page.goto('https://playwright.dev');
|
||||||
|
}
|
||||||
|
|
||||||
|
async toc() {
|
||||||
|
const text = await this.page.innerText('article ul');
|
||||||
|
return text.split('\n').filter(line => !!line);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStarted() {
|
||||||
|
await this.page.click('text=Get started');
|
||||||
|
await this.page.waitForSelector(`text=Core concepts`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async coreConcepts() {
|
||||||
|
await this.getStarted();
|
||||||
|
await this.page.click('text=Core concepts');
|
||||||
|
await this.page.waitForSelector(`h1:has-text("Core concepts")`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// playwright-dev-page.ts
|
||||||
|
import type { Page } from 'playwright';
|
||||||
|
|
||||||
|
export class PlaywrightDevPage {
|
||||||
|
readonly page: Page;
|
||||||
|
|
||||||
|
constructor(page: Page) {
|
||||||
|
this.page = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
async goto() {
|
||||||
|
await this.page.goto('https://playwright.dev');
|
||||||
|
}
|
||||||
|
|
||||||
|
async toc() {
|
||||||
|
const text = await this.page.innerText('article ul');
|
||||||
|
return text.split('\n').filter(line => !!line);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getStarted() {
|
||||||
|
await this.page.click('text=Get started');
|
||||||
|
await this.page.waitForSelector(`text=Core concepts`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async coreConcepts() {
|
||||||
|
await this.getStarted();
|
||||||
|
await this.page.click('text=Core concepts');
|
||||||
|
await this.page.waitForSelector(`h1:has-text("Core concepts")`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now we can use the `PlaywrightDevPage` class in our tests.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// example.spec.js
|
||||||
|
const { test, expect } = require('playwright/test');
|
||||||
|
const { PlaywrightDevPage } = require('./playwright-dev-page');
|
||||||
|
|
||||||
|
test('Get Started table of contents', async ({ page }) => {
|
||||||
|
const playwrightDev = new PlaywrightDevPage(page);
|
||||||
|
await playwrightDev.goto();
|
||||||
|
await playwrightDev.getStarted();
|
||||||
|
expect(await playwrightDev.toc()).toEqual([
|
||||||
|
'Installation',
|
||||||
|
'Usage',
|
||||||
|
'First script',
|
||||||
|
'Record scripts',
|
||||||
|
'TypeScript support',
|
||||||
|
'System requirements',
|
||||||
|
'Release notes'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Core Concepts table of contents', async ({ page }) => {
|
||||||
|
const playwrightDev = new PlaywrightDevPage(page);
|
||||||
|
await playwrightDev.goto();
|
||||||
|
await playwrightDev.coreConcepts();
|
||||||
|
expect(await playwrightDev.toc()).toEqual([
|
||||||
|
'Browser',
|
||||||
|
'Browser contexts',
|
||||||
|
'Pages and frames',
|
||||||
|
'Selectors',
|
||||||
|
'Auto-waiting',
|
||||||
|
'Execution contexts: Playwright and Browser',
|
||||||
|
'Evaluation Argument'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// example.spec.ts
|
||||||
|
import { test, expect } from 'playwright/test';
|
||||||
|
import { PlaywrightDevPage } from './playwright-dev-page';
|
||||||
|
|
||||||
|
test('Get Started table of contents', async ({ page }) => {
|
||||||
|
const playwrightDev = new PlaywrightDevPage(page);
|
||||||
|
await playwrightDev.goto();
|
||||||
|
await playwrightDev.getStarted();
|
||||||
|
expect(await playwrightDev.toc()).toEqual([
|
||||||
|
'Installation',
|
||||||
|
'Usage',
|
||||||
|
'First script',
|
||||||
|
'Record scripts',
|
||||||
|
'TypeScript support',
|
||||||
|
'System requirements',
|
||||||
|
'Release notes'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Core Concepts table of contents', async ({ page }) => {
|
||||||
|
const playwrightDev = new PlaywrightDevPage(page);
|
||||||
|
await playwrightDev.goto();
|
||||||
|
await playwrightDev.coreConcepts();
|
||||||
|
expect(await playwrightDev.toc()).toEqual([
|
||||||
|
'Browser',
|
||||||
|
'Browser contexts',
|
||||||
|
'Pages and frames',
|
||||||
|
'Selectors',
|
||||||
|
'Auto-waiting',
|
||||||
|
'Execution contexts: Playwright and Browser',
|
||||||
|
'Evaluation Argument'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
@ -194,7 +194,7 @@ module.exports = {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```ts
|
||||||
// playwright.config.ts
|
// playwright.config.ts
|
||||||
import { PlaywrightTestConfig } from 'playwright/test';
|
import { PlaywrightTestConfig } from 'playwright/test';
|
||||||
|
|
||||||
|
|
@ -221,7 +221,7 @@ module.exports = {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
```js
|
```ts
|
||||||
// playwright.config.ts
|
// playwright.config.ts
|
||||||
import { PlaywrightTestConfig } from 'playwright/test';
|
import { PlaywrightTestConfig } from 'playwright/test';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
---
|
---
|
||||||
id: test-snapshots
|
id: test-snapshots
|
||||||
title: "Snapshots"
|
title: "Visual comparisons"
|
||||||
---
|
---
|
||||||
|
|
||||||
Playwright Test includes the ability to produce and compare snapshots. For that, use `expect(value).toMatchSnapshot()`. Test runner auto-detects the content type, and includes built-in matchers for text, png and jpeg images, and arbitrary binary data.
|
Playwright Test includes the ability to produce and visually compare screenshots using `expect(value).toMatchSnapshot()`. On first execution, Playwright test will generate reference screenshots. Subsequent runs will compare against the reference.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
// example.spec.js
|
// example.spec.js
|
||||||
|
|
@ -25,4 +25,56 @@ test('example test', async ({ page }) => {
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Sometimes you need to update the reference screenshot, for example when the page has changed. Do this with the `--update-snapshots` flag.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
npx playwright test --update-snapshots
|
||||||
|
```
|
||||||
|
|
||||||
|
Playwright Test uses the [pixelmatch](https://github.com/mapbox/pixelmatch) library. You can pass comparison `threshold` as an option.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// example.spec.js
|
||||||
|
const { test, expect } = require('playwright/test');
|
||||||
|
|
||||||
|
test('example test', async ({ page }) => {
|
||||||
|
await page.goto('https://playwright.dev');
|
||||||
|
expect(await page.screenshot()).toMatchSnapshot({ threshold: 0.2 });
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// example.spec.ts
|
||||||
|
import { test, expect } from 'playwright/test';
|
||||||
|
|
||||||
|
test('example test', async ({ page }) => {
|
||||||
|
await page.goto('https://playwright.dev');
|
||||||
|
expect(await page.screenshot()).toMatchSnapshot({ threshold: 0.2 });
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
Apart from screenshots, `expect(value).toMatchSnapshot()` can also be used to compare text, png and jpeg images, or arbitrary binary data. Playwright Test auto-detects the content type and uses the appropriate comparison algorithm.
|
||||||
|
|
||||||
|
Here we compare text content against the reference.
|
||||||
|
|
||||||
|
```js
|
||||||
|
// example.spec.js
|
||||||
|
const { test, expect } = require('playwright/test');
|
||||||
|
|
||||||
|
test('example test', async ({ page }) => {
|
||||||
|
await page.goto('https://playwright.dev');
|
||||||
|
expect(await page.textContent('.hero__title')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// example.spec.ts
|
||||||
|
import { test, expect } from 'playwright/test';
|
||||||
|
|
||||||
|
test('example test', async ({ page }) => {
|
||||||
|
await page.goto('https://playwright.dev');
|
||||||
|
expect(await page.textContent('.hero__title')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
Snapshots are stored next to the test file, in a separate directory. For example, `my.spec.js` file will produce and store snapshots in the `my.spec.js-snapshots` directory. You should commit this directory to your version control (e.g. `git`), and review any changes to it.
|
Snapshots are stored next to the test file, in a separate directory. For example, `my.spec.js` file will produce and store snapshots in the `my.spec.js-snapshots` directory. You should commit this directory to your version control (e.g. `git`), and review any changes to it.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue