Skip to content

Commit

Permalink
feat(create-playwright): add option to add examples (#8963)
Browse files Browse the repository at this point in the history
  • Loading branch information
mxschmitt committed Oct 21, 2021
1 parent df188d5 commit dd5364e
Show file tree
Hide file tree
Showing 23 changed files with 649 additions and 61 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Expand Up @@ -12,4 +12,4 @@ browser_patches/*/checkout/
browser_patches/chromium/output/
**/*.d.ts
output/
/test-results/
test-results/
2 changes: 1 addition & 1 deletion docs/src/selectors.md
Expand Up @@ -75,7 +75,7 @@ methods accept [`param: selector`] as their first argument.
- Combine css and text selectors
```js
await page.click('article:has-text("Playwright")');
await page.click('#nav-bar :text("Contact us")');
await page.click('#nav-bar >> text=Contact Us');
```
```java
page.click("article:has-text(\"Playwright\")");
Expand Down
@@ -0,0 +1,17 @@
import { test, expect } from '@playwright/test';

/**
* Inside every test you get a new isolated page instance.
* @see https://playwright.dev/docs/intro
* @see https://playwright.dev/docs/api/class-page
*/
test('basic test', async ({ page }) => {
await page.goto('https://todomvc.com/examples/vanilla-es6/');

const inputBox = page.locator('input.new-todo');
const todoList = page.locator('.todo-list');

await inputBox.fill('Learn Playwright');
await inputBox.press('Enter');
await expect(todoList).toHaveText('Learn Playwright');
});
54 changes: 54 additions & 0 deletions packages/create-playwright/assets/examples/2-actions.spec.ts
@@ -0,0 +1,54 @@
import { test, expect } from '@playwright/test';

test.beforeEach(async ({ page }) => {
await page.goto('https://todomvc.com/examples/vanilla-es6/');
});

/**
* Locators are used to represent a selector on a page and re-use them. They have
* strictMode enabled by default. This option will throw an error if the selector
* will resolve to multiple elements.
* In this example we create a todo item, assert that it exists and then filter
* by the completed items to ensure that the item is not visible anymore.
* @see https://playwright.dev/docs/api/class-locator
*/
test('basic interaction', async ({ page }) => {
const inputBox = page.locator('input.new-todo');
const todoList = page.locator('.todo-list');

await inputBox.fill('Learn Playwright');
await inputBox.press('Enter');
await expect(todoList).toHaveText('Learn Playwright');
await page.locator('.filters >> text=Completed').click();
await expect(todoList).not.toHaveText('Learn Playwright');
});

/**
* Playwright supports different selector engines which you can combine with '>>'.
* @see https://playwright.dev/docs/selectors
*/
test('element selectors', async ({ page }) => {
// When no selector engine is specified, Playwright will use the css selector engine.
await page.type('.header input', 'Learn Playwright');
// So the selector above is the same as the following:
await page.press('css=.header input', 'Enter');

// select by text with the text selector engine:
await page.click('text=All');

// css allows you to select by attribute:
await page.click('[id="toggle-all"]');

// Combine css and text selectors (https://playwright.dev/docs/selectors/#text-selector)
await page.click('.todo-list > li:has-text("Playwright")');
await page.click('.todoapp .footer >> text=Completed');

// Selecting based on layout, with css selector
expect(await page.innerText('a:right-of(:text("Active"))')).toBe('Completed');

// Only visible elements, with css selector
await page.click('text=Completed >> visible=true');

// XPath selector
await page.click('xpath=//html/body/section/section/label');
});
51 changes: 51 additions & 0 deletions packages/create-playwright/assets/examples/3-assertions.spec.ts
@@ -0,0 +1,51 @@
import { test, expect } from '@playwright/test';

test.beforeEach(async ({ page }) => {
await page.goto('https://todomvc.com/examples/vanilla-es6/');
});

/**
* All available test assertions are listed here:
* @see https://playwright.dev/docs/test-assertions/
*/
test('should be able to use assertions', async ({ page }) => {
await test.step('toHaveTitle/toHaveURL', async () => {
await expect(page).toHaveTitle('Vanilla ES6 • TodoMVC');
await expect(page).toHaveURL('https://todomvc.com/examples/vanilla-es6/');
});

await test.step('toBeEmpty/toHaveValue', async () => {
const input = page.locator('input.new-todo');
await expect(input).toBeEmpty();
await input.fill('Buy milk');
await expect(input).toHaveValue('Buy milk');
await input.press('Enter');
});

await test.step('toHaveCount/toHaveText/toContainText', async () => {
const items = page.locator('.todo-list li');
await expect(items).toHaveCount(1);
await expect(items.first()).toHaveText('Buy milk');
await expect(items).toHaveText(['Buy milk']);
await expect(items.first()).toContainText('milk');
});

await test.step('toBeChecked', async () => {
const firstItemCheckbox = page.locator('input[type=checkbox]:left-of(:text("Buy milk"))');
await expect(firstItemCheckbox).not.toBeChecked();
await firstItemCheckbox.check();
await expect(firstItemCheckbox).toBeChecked();
});

await test.step('toBeVisible/toBeHidden', async () => {
await expect(page.locator('text=Buy milk')).toBeVisible();
await page.click('text=Active');
await expect(page.locator('text=Buy milk')).toBeHidden();
});

await test.step('toHaveClass/toHaveCSS', async () => {
await expect(page.locator('[placeholder="What needs to be done?"]')).toHaveClass('new-todo');
await page.click('text=Clear completed');
await expect(page.locator('.main')).toHaveCSS('display', 'none');
});
});
19 changes: 19 additions & 0 deletions packages/create-playwright/assets/examples/4-file-uploads.spec.ts
@@ -0,0 +1,19 @@
import { test, expect } from '@playwright/test';

const fileToUpload = __filename; // '__filename' is the current test file.

/**
* In this test we wait for an file chooser to appear while we click on an
* input. Once the event was emitted we set the file and submit the form.
* @see https://playwright.dev/docs/api/class-filechooser
*/
test('should be able to upload files', async ({ page, context }) => {
await page.goto('/file-uploads.html');
const [fileChooser] = await Promise.all([
page.waitForEvent('filechooser'),
page.click('input')
]);
await fileChooser.setFiles(fileToUpload);
await page.click('input[type=submit]');
await expect(page.locator('text=4-file-uploads.spec.ts')).toBeVisible();
});
41 changes: 41 additions & 0 deletions packages/create-playwright/assets/examples/5-networking.spec.ts
@@ -0,0 +1,41 @@
import { test, expect } from '@playwright/test';

/**
* This test clicks on an element with the text 'Load user' and waits for a
* specific HTTP response. This response contains a JSON body where we assert
* some properties.
*/
test('should be able to read a response body', async ({ page }) => {
await page.goto('/network.html');
const [response] = await Promise.all([
page.waitForResponse('/api/v1/users.json'),
page.click('text=Load user')
]);
await expect(page.locator('#user-full-name')).toContainText('John Doe');
const responseBody = await response.json();
expect(responseBody.id).toBe(1);
expect(responseBody.fullName).toBe('John Doe');
});

test.describe('mocked responses', () => {
/**
* Before every test set the request interception handler and fulfill the
* requests with a mocked response. See here:
* @see https://playwright.dev/docs/network#handle-requests
*/
test.beforeEach(async ({ context }) => {
await context.route('/api/v1/users.json', route => route.fulfill({
body: JSON.stringify({
'id': 2,
'fullName': 'James Bond'
}),
contentType: 'application/json'
}));
});

test('be able to mock responses', async ({ page }) => {
await page.goto('/network.html');
await page.click('text=Load user');
await expect(page.locator('p')).toHaveText('User: James Bond');
});
});
8 changes: 8 additions & 0 deletions packages/create-playwright/assets/examples/README.md
@@ -0,0 +1,8 @@
# Playwright examples

This directory contains examples for Playwright. Run them with the following command:

```sh
npm run test:e2e-examples
yarn test:e2e-examples
```
13 changes: 13 additions & 0 deletions packages/create-playwright/assets/examples/playwright.config.ts
@@ -0,0 +1,13 @@
import { PlaywrightTestConfig } from '@playwright/test';

// Reference: https://playwright.dev/docs/test-configuration
const config: PlaywrightTestConfig = {
// Run your local dev server before starting the tests:
// https://playwright.dev/docs/test-advanced#launching-a-development-web-server-during-the-tests
webServer: {
command: 'node ./server',
port: 4345,
cwd: __dirname,
},
};
export default config;
14 changes: 14 additions & 0 deletions packages/create-playwright/assets/examples/pom/fixtures.ts
@@ -0,0 +1,14 @@
import { test as base } from '@playwright/test';
import { TodoPage } from './todoPage.pom';

/**
* This adds a todoPage fixture which has access to the page instance
* @see https://playwright.dev/docs/test-fixtures
*/
export const test = base.extend<{ todoPage: TodoPage }>({
todoPage: async ({ page }, use) => {
await use(new TodoPage(page));
},
});

export const expect = test.expect;
@@ -0,0 +1,63 @@
import { test, expect } from './fixtures';

/**
* Fixtures are used here to create a TodoApp instance for every test. These are
* defined inside the 'fixtures.ts' file. This will reduce the amount of
* boilerplate created for each test and makes it more reusable.
* @see https://playwright.dev/docs/test-fixtures
*/
test.beforeEach(async ({ todoPage }) => {
await todoPage.goto();
});

test('should display zero initial items', async ({ todoPage }) => {
await expect(todoPage.listItems).toHaveCount(0);
});

test('should be able to add new items', async ({ todoPage }) => {
await todoPage.addItem('Example #1');
await todoPage.addItem('Example #2');
await expect(todoPage.listItems).toHaveText(['Example #1', 'Example #2']);
});

test('should be able to mark items as completed', async ({ todoPage }) => {
await todoPage.addItem('Example #1');
const firstListItem = todoPage.listItems.first();
await expect(firstListItem).not.toHaveClass('completed');
await firstListItem.locator('.toggle').check();
await expect(firstListItem).toHaveClass('completed');
});

test('should still show the items after a page reload', async ({ page, todoPage }) => {
await todoPage.addItem('Example #1');
await expect(todoPage.listItems).toHaveText(['Example #1']);
await page.reload();
await expect(todoPage.listItems).toHaveText(['Example #1']);
});

test('should be able to filter by uncompleted items', async ({ todoPage }) => {
await todoPage.addItem('Example #1');
await todoPage.addItem('Example #2');
await todoPage.addItem('Example #3');
await todoPage.listItems.last().locator('.toggle').check();
await todoPage.filterByActiveItemsButton.click();
await expect(todoPage.listItems).toHaveCount(2);
await expect(todoPage.listItems).toHaveText(['Example #1', 'Example #2']);
});

test('should be able to filter by completed items', async ({ todoPage }) => {
await todoPage.addItem('Example #1');
await todoPage.addItem('Example #2');
await todoPage.addItem('Example #3');
await todoPage.listItems.last().locator('.toggle').check();
await todoPage.filterByCompletedItemsButton.click();
await expect(todoPage.listItems).toHaveText(['Example #3']);
});

test('should be able to delete completed items', async ({ todoPage }) => {
await todoPage.addItem('Example #1');
await todoPage.listItems.last().locator('.toggle').check();
await expect(todoPage.listItems).toHaveText(['Example #1']);
await todoPage.listItems.first().locator('button.destroy').click();
await expect(todoPage.listItems).toHaveText([]);
});
69 changes: 69 additions & 0 deletions packages/create-playwright/assets/examples/pom/pom.spec.ts
@@ -0,0 +1,69 @@
import { test, expect } from '@playwright/test';
import { TodoPage } from './todoPage.pom';

test.describe('ToDo App', () => {
test('should display zero initial items', async ({ page }) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await expect(todoPage.listItems).toHaveCount(0);
});

test('should be able to add new items', async ({ page }) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addItem('Example #1');
await todoPage.addItem('Example #2');
await expect(todoPage.listItems).toHaveText(['Example #1', 'Example #2']);
});

test('should be able to mark items as completed', async ({ page }) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addItem('Example #1');
const firstListItem = todoPage.listItems.first();
await expect(firstListItem).not.toHaveClass('completed');
await firstListItem.locator('.toggle').check();
await expect(firstListItem).toHaveClass('completed');
});

test('should still show the items after a page reload', async ({ page }) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addItem('Example #1');
await expect(todoPage.listItems).toHaveText(['Example #1']);
await page.reload();
await expect(todoPage.listItems).toHaveText(['Example #1']);
});

test('should be able to filter by uncompleted items', async ({ page }) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addItem('Example #1');
await todoPage.addItem('Example #2');
await todoPage.addItem('Example #3');
await todoPage.listItems.last().locator('.toggle').check();
await todoPage.filterByActiveItemsButton.click();
await expect(todoPage.listItems).toHaveText(['Example #1', 'Example #2']);
});

test('should be able to filter by completed items', async ({ page }) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addItem('Example #1');
await todoPage.addItem('Example #2');
await todoPage.addItem('Example #3');
await todoPage.listItems.last().locator('.toggle').check();
await todoPage.filterByCompletedItemsButton.click();
await expect(todoPage.listItems).toHaveText(['Example #3']);
});

test('should be able to delete completed items', async ({ page }) => {
const todoPage = new TodoPage(page);
await todoPage.goto();
await todoPage.addItem('Example #1');
await todoPage.listItems.last().locator('.toggle').check();
await expect(todoPage.listItems).toHaveText(['Example #1']);
await todoPage.listItems.first().locator('button.destroy').click();
await expect(todoPage.listItems).toHaveText([]);
});
});

0 comments on commit dd5364e

Please sign in to comment.