diff --git a/docs/src/test-api-testing-js.md b/docs/src/test-api-testing-js.md index a250ec2bb0..8512118fcf 100644 --- a/docs/src/test-api-testing-js.md +++ b/docs/src/test-api-testing-js.md @@ -14,7 +14,7 @@ A few examples where it may come in handy: All of that could be achieved via [APIRequestContext] methods. - + ## 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. - 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()`. @@ -69,7 +69,7 @@ const 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. @@ -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 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 project to check that it appears at the top of the list. -```js -test('last created issue should be first in the list', async ({ page, request }) => { - const newIssue = await request.post(`/repos/${USER}/${REPO}/issues`, { +```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', } @@ -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 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 }) => { await page.goto(`https://github.com/${USER}/${REPO}/issues`); await page.click('text=New Issue'); @@ -215,17 +316,52 @@ test('last created issue should be on the server', async ({ page, request }) => }); ``` -### API reference -- [`property: Playwright.request`] -- [`property: BrowserContext.request`] -- [`property: Page.request`] -- [`method: APIRequest.newContext`] -- [`method: APIRequestContext.delete`] -- [`method: APIRequestContext.fetch`] -- [`method: APIRequestContext.get`] -- [`method: APIRequestContext.post`] +```js js-flavor=js +// @ts-check +const { test, expect } = require('@playwright/test'); -## 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 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. const context = await browser.newContext({ storageState: 'state.json' }); ``` - -### API reference -- [`method: Browser.newContext`] -- [`method: APIRequestContext.storageState`] -- [`method: APIRequest.newContext`] - diff --git a/docs/src/test-send-api-requests-js.md b/docs/src/test-send-api-requests-js.md deleted file mode 100644 index 3d972adeaa..0000000000 --- a/docs/src/test-send-api-requests-js.md +++ /dev/null @@ -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. - - - -## 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`]