docs(fetch): merge UI test examples into API testing guide (#9684)

This commit is contained in:
Yury Semikhatsky 2021-10-21 11:44:06 -07:00 committed by GitHub
parent c21d6b791c
commit 2ad2f53482
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 157 additions and 230 deletions

View file

@ -14,7 +14,7 @@ A few examples where it may come in handy:
All of that could be achieved via [APIRequestContext] methods. All of that could be achieved via [APIRequestContext] methods.
<!-- TOC --> <!-- TOC3 -->
## Writing API Test ## Writing API Test
@ -25,7 +25,7 @@ The following example demonstrates how to use Playwright to test issues creation
- Create a few issues and validate server state. - Create a few issues and validate server state.
- Delete the repository after running tests. - Delete the repository after running tests.
### Configure ### Configuration
GitHub API requires authorization, so we'll configure the token once for all tests. While at it, we'll also set the `baseURL` to simplify the tests. You can either put them in the configuration file, or in the test file with `test.use()`. GitHub API requires authorization, so we'll configure the token once for all tests. While at it, we'll also set the `baseURL` to simplify the tests. You can either put them in the configuration file, or in the test file with `test.use()`.
@ -69,7 +69,7 @@ const config = {
module.exports = config; module.exports = config;
``` ```
### Write tests ### Writing tests
Playwright Test comes with the built-in `request` fixture that respects configuration options like `baseURL` or `extraHTTPHeaders` we specified and is ready to send some requests. Playwright Test comes with the built-in `request` fixture that respects configuration options like `baseURL` or `extraHTTPHeaders` we specified and is ready to send some requests.
@ -135,9 +135,9 @@ test.afterAll(async ({ request }) => {
}); });
``` ```
### Using request context ## Using request context
Behind the scenes, `request` fixture will actually call [`method: APIRequest.newContext`]. You can always do that manually if you'd like more control. Below is a standalone script that does the same as `beforeAll` and `afterAll` from above. Behind the scenes, [`request` fixture](./api/class-fixtures#fixtures-request) will actually call [`method: APIRequest.newContext`]. You can always do that manually if you'd like more control. Below is a standalone script that does the same as `beforeAll` and `afterAll` from above.
```js ```js
const { request } = require('@playwright/test'); const { request } = require('@playwright/test');
@ -173,14 +173,45 @@ const USER = 'github-username';
})() })()
``` ```
## Prepare server state via API calls ## Sending API requests from UI tests
While running tests inside browsers you may want to make calls to the HTTP API of your application. It may be helpful if you need to prepare server state before running a test or to check some postconditions on the server after performing some actions in the browser. All of that could be achieved via [APIRequestContext] methods.
### Establishing preconditions
The following test creates a new issue via API and then navigates to the list of all issues in the The following test creates a new issue via API and then navigates to the list of all issues in the
project to check that it appears at the top of the list. project to check that it appears at the top of the list.
```js ```js js-flavor=ts
test('last created issue should be first in the list', async ({ page, request }) => { import { test, expect } from '@playwright/test';
const newIssue = await request.post(`/repos/${USER}/${REPO}/issues`, {
const REPO = 'test-repo-1';
const USER = 'github-username';
// Request context is reused by all tests in the file.
let apiContext;
test.beforeAll(async ({ playwright }) => {
apiContext = await playwright.request.newContext({
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
});
})
test.afterAll(async ({ }) => {
// Dispose all responses.
await apiContext.dispose();
});
test('last created issue should be first in the list', async ({ page }) => {
const newIssue = await apiContext.post(`/repos/${USER}/${REPO}/issues`, {
data: { data: {
title: '[Feature] request 1', title: '[Feature] request 1',
} }
@ -193,12 +224,82 @@ test('last created issue should be first in the list', async ({ page, request })
}); });
``` ```
## Check the server state after running user actions ```js js-flavor=js
// @ts-check
const { test, expect } = require('@playwright/test');
const REPO = 'test-repo-1';
const USER = 'github-username';
// Request context is reused by all tests in the file.
let apiContext;
test.beforeAll(async ({ playwright }) => {
apiContext = await playwright.request.newContext({
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
});
})
test.afterAll(async ({ }) => {
// Dispose all responses.
await apiContext.dispose();
});
test('last created issue should be first in the list', async ({ page }) => {
const newIssue = await apiContext.post(`/repos/${USER}/${REPO}/issues`, {
data: {
title: '[Feature] request 1',
}
});
expect(newIssue.ok()).toBeTruthy();
await page.goto(`https://github.com/${USER}/${REPO}/issues`);
const firstIssue = page.locator(`a[data-hovercard-type='issue']`).first();
await expect(firstIssue).toHaveText('[Feature] request 1');
});
```
### Validating postconditions
The following test creates a new issue via user interface in the browser and then uses checks if The following test creates a new issue via user interface in the browser and then uses checks if
it was created via API: it was created via API:
```js ```js js-flavor=ts
import { test, expect } from '@playwright/test';
const REPO = 'test-repo-1';
const USER = 'github-username';
// Request context is reused by all tests in the file.
let apiContext;
test.beforeAll(async ({ playwright }) => {
apiContext = await playwright.request.newContext({
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
});
})
test.afterAll(async ({ }) => {
// Dispose all responses.
await apiContext.dispose();
});
test('last created issue should be on the server', async ({ page, request }) => { test('last created issue should be on the server', async ({ page, request }) => {
await page.goto(`https://github.com/${USER}/${REPO}/issues`); await page.goto(`https://github.com/${USER}/${REPO}/issues`);
await page.click('text=New Issue'); await page.click('text=New Issue');
@ -215,17 +316,52 @@ test('last created issue should be on the server', async ({ page, request }) =>
}); });
``` ```
### API reference ```js js-flavor=js
- [`property: Playwright.request`] // @ts-check
- [`property: BrowserContext.request`] const { test, expect } = require('@playwright/test');
- [`property: Page.request`]
- [`method: APIRequest.newContext`]
- [`method: APIRequestContext.delete`]
- [`method: APIRequestContext.fetch`]
- [`method: APIRequestContext.get`]
- [`method: APIRequestContext.post`]
## Reuse authentication state const REPO = 'test-repo-1';
const USER = 'github-username';
// Request context is reused by all tests in the file.
let apiContext;
test.beforeAll(async ({ playwright }) => {
apiContext = await playwright.request.newContext({
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
});
})
test.afterAll(async ({ }) => {
// Dispose all responses.
await apiContext.dispose();
});
test('last created issue should be on the server', async ({ page, request }) => {
await page.goto(`https://github.com/${USER}/${REPO}/issues`);
await page.click('text=New Issue');
await page.fill('[aria-label="Title"]', 'Bug report 1');
await page.fill('[aria-label="Comment body"]', 'Bug description');
await page.click('text=Submit new issue');
const issueId = page.url().substr(page.url().lastIndexOf('/'));
const newIssue = await request.get(`https://api.github.com/repos/${USER}/${REPO}/issues/${issueId}`);
expect(newIssue.ok()).toBeTruthy();
expect(newIssue).toEqual(expect.objectContaining({
title: 'Bug report 1'
}));
});
```
## Reusing authentication state
Web apps use cookie-based or token-based authentication, where authenticated Web apps use cookie-based or token-based authentication, where authenticated
state is stored as [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies). state is stored as [cookies](https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies).
@ -251,9 +387,3 @@ await requestContext.storageState({ path: 'state.json' });
// Create a new context with the saved storage state. // Create a new context with the saved storage state.
const context = await browser.newContext({ storageState: 'state.json' }); const context = await browser.newContext({ storageState: 'state.json' });
``` ```
### API reference
- [`method: Browser.newContext`]
- [`method: APIRequestContext.storageState`]
- [`method: APIRequest.newContext`]

View file

@ -1,203 +0,0 @@
---
id: test-send-api-requests
title: "Send API requests"
---
While running tests inside browsers you may want to make calls to the HTTP API of your application. It may be helpful if you need to prepare server state before running a test or to check some postconditions on the server after performing some actions in the browser. All of that could be achieved via [APIRequestContext] methods.
<!-- TOC -->
## Establishing preconditions
The following test creates a new issue via API and then navigates to the list of all issues in the
project to check that it appears at the top of the list.
```js js-flavor=ts
import { test, expect } from '@playwright/test';
const REPO = 'test-repo-1';
const USER = 'github-username';
// Request context is reused by all tests in the file.
let apiContext;
test.beforeAll(async ({ playwright }) => {
apiContext = await playwright.request.newContext({
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
});
})
test.afterAll(async ({ }) => {
// Dispose all responses.
await apiContext.dispose();
});
test('last created issue should be first in the list', async ({ page }) => {
const newIssue = await apiContext.post(`/repos/${USER}/${REPO}/issues`, {
data: {
title: '[Feature] request 1',
}
});
expect(newIssue.ok()).toBeTruthy();
await page.goto(`https://github.com/${USER}/${REPO}/issues`);
const firstIssue = page.locator(`a[data-hovercard-type='issue']`).first();
await expect(firstIssue).toHaveText('[Feature] request 1');
});
```
```js js-flavor=js
// @ts-check
const { test, expect } = require('@playwright/test');
const REPO = 'test-repo-1';
const USER = 'github-username';
// Request context is reused by all tests in the file.
let apiContext;
test.beforeAll(async ({ playwright }) => {
apiContext = await playwright.request.newContext({
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
});
})
test.afterAll(async ({ }) => {
// Dispose all responses.
await apiContext.dispose();
});
test('last created issue should be first in the list', async ({ page }) => {
const newIssue = await apiContext.post(`/repos/${USER}/${REPO}/issues`, {
data: {
title: '[Feature] request 1',
}
});
expect(newIssue.ok()).toBeTruthy();
await page.goto(`https://github.com/${USER}/${REPO}/issues`);
const firstIssue = page.locator(`a[data-hovercard-type='issue']`).first();
await expect(firstIssue).toHaveText('[Feature] request 1');
});
```
## Validating postconditions
The following test creates a new issue via user interface in the browser and then uses checks if
it was created via API:
```js js-flavor=ts
import { test, expect } from '@playwright/test';
const REPO = 'test-repo-1';
const USER = 'github-username';
// Request context is reused by all tests in the file.
let apiContext;
test.beforeAll(async ({ playwright }) => {
apiContext = await playwright.request.newContext({
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
});
})
test.afterAll(async ({ }) => {
// Dispose all responses.
await apiContext.dispose();
});
test('last created issue should be on the server', async ({ page, request }) => {
await page.goto(`https://github.com/${USER}/${REPO}/issues`);
await page.click('text=New Issue');
await page.fill('[aria-label="Title"]', 'Bug report 1');
await page.fill('[aria-label="Comment body"]', 'Bug description');
await page.click('text=Submit new issue');
const issueId = page.url().substr(page.url().lastIndexOf('/'));
const newIssue = await request.get(`https://api.github.com/repos/${USER}/${REPO}/issues/${issueId}`);
expect(newIssue.ok()).toBeTruthy();
expect(newIssue).toEqual(expect.objectContaining({
title: 'Bug report 1'
}));
});
```
```js js-flavor=js
// @ts-check
const { test, expect } = require('@playwright/test');
const REPO = 'test-repo-1';
const USER = 'github-username';
// Request context is reused by all tests in the file.
let apiContext;
test.beforeAll(async ({ playwright }) => {
apiContext = await playwright.request.newContext({
// All requests we send go to this API endpoint.
baseURL: 'https://api.github.com',
extraHTTPHeaders: {
// We set this header per GitHub guidelines.
'Accept': 'application/vnd.github.v3+json',
// Add authorization token to all requests.
// Assuming personal access token available in the environment.
'Authorization': `token ${process.env.API_TOKEN}`,
},
});
})
test.afterAll(async ({ }) => {
// Dispose all responses.
await apiContext.dispose();
});
test('last created issue should be on the server', async ({ page, request }) => {
await page.goto(`https://github.com/${USER}/${REPO}/issues`);
await page.click('text=New Issue');
await page.fill('[aria-label="Title"]', 'Bug report 1');
await page.fill('[aria-label="Comment body"]', 'Bug description');
await page.click('text=Submit new issue');
const issueId = page.url().substr(page.url().lastIndexOf('/'));
const newIssue = await request.get(`https://api.github.com/repos/${USER}/${REPO}/issues/${issueId}`);
expect(newIssue.ok()).toBeTruthy();
expect(newIssue).toEqual(expect.objectContaining({
title: 'Bug report 1'
}));
});
```
### API reference
- [`request` fixture](./api/class-fixtures#fixtures-request)
- [`property: Playwright.request`]
- [`property: BrowserContext.request`]
- [`property: Page.request`]
- [`method: APIRequest.newContext`]
- [`method: APIRequestContext.delete`]
- [`method: APIRequestContext.fetch`]
- [`method: APIRequestContext.get`]
- [`method: APIRequestContext.post`]