diff --git a/docs/README.md b/docs/README.md index ce2625487c..831478bb26 100644 --- a/docs/README.md +++ b/docs/README.md @@ -16,7 +16,7 @@ - [Pages and frames](./core-concepts.md#pages-and-frames) - [Selectors](./core-concepts.md#selectors) - [Auto-waiting](./core-concepts.md#auto-waiting) - - [Node.js and browser execution contexts](./core-concepts.md#nodejs-and-browser-execution-contexts) + - [Execution contexts: Node.js and Browser](./core-concepts.md#execution-contexts-nodejs-and-browser) - [Object & element handles](./core-concepts.md#object--element-handles) 1. [Input](./input.md) - [Text input](./input.md#text-input) @@ -43,18 +43,25 @@ - [Handle requests](./network.md#handle-requests) - [Modify requests](./network.md#modify-requests) - [Abort requests](./network.md#abort-requests) -1. [Scraping and Verification](./verification.md) - - [Evaluating JavaScript](./verification.md#evaluating-javascript) - - [Capturing screenshot](./verification.md#capturing-screenshot) +1. [Assertions](./assertions.md) + - [Common patterns](./assertions.md#common-patterns) + - [Element Handles](./assertions.md#element-handles) + - [Custom assertions](./assertions.md#custom-assertions) +1. [Verification](./verification.md) + - [Screenshots](./verification.md#screenshots) + - [Console logs](./verification.md#console-logs) + - [Page errors](./verification.md#page-errors) - [Page events](./verification.md#page-events) - - [Handling exceptions](./verification.md#handling-exceptions) 1. [Navigation and Loading](./loading.md) - [Overview](./loading.md#) - [Common scenarios](./loading.md#common-scenarios) - [Loading a popup](./loading.md#loading-a-popup) - [Client-side redirects](./loading.md#unusual-client-side-redirects) - [Navigation after a timeout](./loading.md#click-triggers-navigation-after-a-timeout) -1. [Actionability](./actionability.md) +1. [Test runners](./test-runners.md) + - [Jest / Jasmine](./test-runners.md#jest--jasmine) + - [AVA](./test-runners.md#ava) + - [Mocha](./test-runners.md#mocha) 1. [Continuous Integration](./ci.md) - [Docker](./ci.md#docker) - [GitHub Actions](./ci.md#github-actions) @@ -63,11 +70,8 @@ - [CircleCI](./ci.md#circleci) - [AppVeyor](./ci.md#appveyor) - [Troubleshooting](./troubleshooting.md) -1. [Test runners](./test-runners.md) - - [Jest / Jasmine](./test-runners.md#jest--jasmine) - - [AVA](./test-runners.md#ava) - - [Mocha](./test-runners.md#mocha) 1. [Selector engines](./selectors.md) +1. [Actionability](./actionability.md) 1. [Extensibility](./extensibility.md) - [Custom selector engines](./extensibility.md#custom-selector-engines) 1. [API Reference](./api.md) \ No newline at end of file diff --git a/docs/assertions.md b/docs/assertions.md new file mode 100644 index 0000000000..5ac5e6ee57 --- /dev/null +++ b/docs/assertions.md @@ -0,0 +1,124 @@ +# Assertions + +The Playwright API can be used to read element contents and properties for test assertions. These values are fetched from the browser page and asserted in +Node.js. + +The examples in this guide use the built-in [`assert` module](https://nodejs.org/api/assert.html), but they can be used with any assertion library (like [Expect](https://www.npmjs.com/package/expect) or [Chai](https://www.npmjs.com/package/chai)). See [Test runners](test-runners.md) for more info. + + +- [Common patterns](#common-patterns) +- [Element Handles](#element-handles) +- [Custom assertions](#custom-assertions) + + +
+ +## Common patterns + +Playwright provides convenience APIs for common assertion tasks, like finding the +text content of an element. These APIs require a [selector](selectors.md) to locate +the element. + +```js +// Assert text content +const content = await page.textContent('nav:first-child'); +assert(content === 'home'); + +// Assert inner text +const text = await page.innerText('.selected'); +assert(text === 'value'); + +// Assert inner HTML +const html = await page.innerHTML('div.result'); +assert(html === '

Result

') + +// Assert `checked` attribute +const checked = await page.getAttribute('input', 'checked'); +assert(checked); +``` + +#### API reference + +- [page.textContent(selector[, options])](api.md#pagetextcontentselector-options) +- [page.innerText(selector[, options])](api.md#pageinnertextselector-options) +- [page.innerHTML(selector[, options])](api.md#pageinnerhtmlselector-options) +- [page.getAttribute(selector, name[, options])](api.md#pagegetattributeselector-name-options) +- [frame.textContent(selector[, options])](api.md#frametextcontentselector-options) +- [frame.innerText(selector[, options])](api.md#frameinnertextselector-options) +- [frame.innerHTML(selector[, options])](api.md#frameinnerhtmlselector-options) +- [frame.getAttribute(selector, name[, options])](api.md#framegetattributeselector-name-options) + +
+ +## Element Handles + +[ElementHandle](api.md#class-elementhandle) objects represent in-page DOM +elements. They can be used to assert for multiple properties of the element. + +It is recommended to fetch the `ElementHandle` object with +[`page.waitForSelector`](api.md#pagewaitforselectorselector-options) or +[`frame.waitForSelector`](api.md#framewaitforselectorselector-options). These +APIs wait for the element to be visible and then return an `ElementHandle`. + +```js +// Get the element handle +const elementHandle = page.waitForSelector('#box'); + +// Assert bounding box for the element +const boundingBox = await elementHandle.boundingBox(); +assert(boundingBox.width === 100); + +// Assert attribute for the element +const classNames = await elementHandle.getAttribute('class'); +assert(classNames.includes('highlighted')); +``` + +#### API reference + +- [elementHandle.textContent()](api.md#elementhandletextcontent) +- [elementHandle.innerText()](api.md#elementhandleinnertext) +- [elementHandle.innerHTML()](api.md#elementhandleinnerhtml) +- [elementHandle.getAttribute(name)](api.md#elementhandlegetattributename) +- [elementHandle.boundingBox()](api.md#elementhandleboundingbox) + +
+ +## Custom assertions + +With Playwright, you can also write custom JavaScript to run in the context of +the browser. This is useful in situations where you want to assert for values +that are not covered by the convenience APIs above. + +The following APIs do not auto-wait for the element. It is recommended to use +[`page.waitForSelector`](api.md#pagewaitforselectorselector-options) or +[`frame.waitForSelector`](api.md#framewaitforselectorselector-options). + +```js +// Assert local storage value +const userId = page.evaluate(() => window.localStorage.getItem('userId')); +assert(userId); + +// Assert value for input element +await page.waitForSelector('#search'); +const value = await page.$eval('#search', el => el.value); +assert(value === 'query'); + +// Assert computed style +const fontSize = await page.$eval('div', el => window.getComputedStyle(el).fontSize); +assert(fontSize === '16px'); + +// Assert list length +const length = await page.$$eval('li.selected', (items) => items.length); +assert(length === 3); +``` + +#### API reference + +- [page.evaluate(pageFunction[, arg])](api.md#pageevaluatepagefunction-arg) +- [page.$eval(selector, pageFunction[, arg])](api.md#pageevalselector-pagefunction-arg) +- [page.$$eval(selector, pageFunction[, arg])](api.md#pageevalselector-pagefunction-arg-1) +- [frame.evaluate(pageFunction[, arg])](api.md#frameevaluatepagefunction-arg) +- [frame.$eval(selector, pageFunction[, arg])](api.md#frameevalselector-pagefunction-arg) +- [frame.$$eval(selector, pageFunction[, arg])](api.md#frameevalselector-pagefunction-arg-1) +- [elementHandle.$eval(selector, pageFunction[, arg])](api.md#elementhandleevalselector-pagefunction-arg) +- [elementHandle.$$eval(selector, pageFunction[, arg])](api.md#elementhandleevalselector-pagefunction-arg-1) diff --git a/docs/core-concepts.md b/docs/core-concepts.md index 5406bb8392..cde7398aae 100644 --- a/docs/core-concepts.md +++ b/docs/core-concepts.md @@ -14,8 +14,8 @@ the following primitives. - [Pages and frames](#pages-and-frames) - [Selectors](#selectors) - [Auto-waiting](#auto-waiting) -- [Node.js and browser execution contexts](#nodejs-and-browser-execution-contexts) -- [Object & element handles](#object--element-handles) +- [Execution contexts: Node.js and Browser](#execution-contexts-nodejs-and-browser) +- [Object & Element handles](#object--element-handles)
@@ -227,15 +227,30 @@ await page.waitForSelector('#promo', { state: 'detached' });
-## Node.js and browser execution contexts +## Execution contexts: Node.js and Browser -Playwright scripts run in your Node.js environment. You page scripts run in the page environment. Those environments don't intersect, they are running in different virtual machines in different processes and potentially on different computers. - -IMAGE PLACEHOLDER +Playwright scripts run in your Node.js environment. You page scripts run in the browser page environment. Those environments don't intersect, they are running in different virtual machines in different processes and even potentially on different computers. The [`page.evaluate`](https://github.com/microsoft/playwright/blob/master/docs/api.md#pageevaluatepagefunction-arg) API can run a JavaScript function in the context -of the web page and bring results back to the Node.js environment. Globals like -`window` and `document` along with the web page runtime can be used in `evaluate`. +of the web page and bring results back to the Node.js environment. Browser globals like +`window` and `document` can be used in `evaluate`. + +```js +const href = await page.evaluate(() => document.location.href); +``` + +If the result is a Promise or if the function is asynchronous evaluate will automatically wait until it's resolved: +```js +const status = await page.evaluate(async () => { + const response = await fetch(location.href); + return response.status; +}); +``` + +### Evaluation + +Functions passed inside `page.evaluate` can accept parameters. These parameters are +serialized and sent into your web page over the wire. You can pass primitive types, JSON-alike objects and remote object handles received from the page. Right: @@ -257,34 +272,33 @@ const result = await page.evaluate(() => { }); ``` -Evaluation parameters are serialized and sent into your web page over the wire. -You can pass primitive types, JSON-alike objects and remote object handles received from the page. +#### API reference + +- [`page.evaluate(pageFunction[, arg])`](api.md#pageevaluatepagefunction-arg) +- [`frame.evaluate(pageFunction[, arg])`](api.md#frameevaluatepagefunction-arg)
-## Object & element handles +## Object & Element handles -Playwright has an API to create Node-side handles to the page DOM elements or any other objects inside the page. -These handles live in the Node.js process, whereas the actual objects reside in browser. - -IMAGE PLACEHOLDER +Playwright can create Node-side handles to the page DOM elements or any other objects inside the page. These handles live in the Node.js process, whereas the actual objects reside in browser. There are two types of handles: -- [`JSHandle`](./api.md#class-jshandle) to reference any javascript objects in the page +- [`JSHandle`](./api.md#class-jshandle) to reference any JavaScript objects in the page - [`ElementHandle`](./api.md#class-elementhandle) to reference DOM elements in the page -Note that since any DOM element in the page is also a javascript object, +Note that since any DOM element in the page is also a JavaScript object, Playwright's [`ElementHandle`](./api.md#class-elementhandle) extends [`JSHandle`](./api.md#class-jshandle). -Handles Lifetime: +### Handles Lifecycle - Handles can be acquired using the page methods [`page.evaluateHandle`](./api.md#pageevaluatehandlepagefunction-arg), [`page.$`](./api.md#pageselector) or [`page.$$`](./api.md#pageselector-1) or their frame counterparts [`frame.evaluateHandle`](./api.md#frameevaluatehandlepagefunction-arg), [`frame.$`](./api.md#frameselector) or [`frame.$$`](./api.md#frameselector-1). -- Once created, handles will retain object from [garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management) +- Once created, handles will retain object from [garbage collection](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Memory_Management). - Handles will be **automatically disposed** once the page or frame they belong to navigates or closes. - Handles can be **manually disposed** using [`jsHandle.dispose`](./api.md#jshandledispose) method. -Here is how you can use these handles: +### Example: ElementHandle ```js // The first parameter of the elementHandle.evaluate callback is the element handle points to. @@ -292,7 +306,7 @@ const ulElementHandle = await page.$('ul'); await ulElementHandle.evaluate(ulElement => getComputedStyle(ulElement).getPropertyValue('display')); ``` -Alternatively, handles can be passed as arguments to [`page.evaluate`](./api.md#pageevaluatepagefunction-arg) function: +Handles can also be passed as arguments to [`page.evaluate`](./api.md#pageevaluatepagefunction-arg) function: ```js // In the page API, you can pass handle as a parameter. @@ -300,6 +314,35 @@ const ulElementHandle = await page.$('ul'); await page.evaluate(uiElement => getComputedStyle(uiElement).getPropertyValue('display'), uiElement); ``` +### Example: JSHandle + +```js +// Create a new array in the page, write a reference to it in +// window.myArray and get a handle to it. +const myArrayHandle = await page.evaluateHandle(() => { + window.myArray = [1]; + return myArray; +}); + +// Get current length of the array using the handle. +const length = await page.evaluate( + (arg) => arg.myArray.length, + { myArray: myArrayHandle } +); + +// Add one more element to the array using the handle +await page.evaluate((arg) => arg.myArray.push(arg.newElement), { + myArray: myArrayHandle, + newElement: 2 +}); + +// Get current length of the array using window.myArray reference. +const newLength = await page.evaluate(() => window.myArray.length); + +// Release the object when it's no longer needed. +await myArrayHandle.dispose(); +``` + #### API reference - [class `JSHandle`](./api.md#class-jshandle) - [class `ElementHandle`](./api.md#class-elementhandle) @@ -307,5 +350,3 @@ await page.evaluate(uiElement => getComputedStyle(uiElement).getPropertyValue('d - [`page.$`](./api.md#pageselector) - [`page.$$`](./api.md#pageselector-1) - [`jsHandle.evaluate`](./api.md#jshandleevaluatepagefunction-arg) - -
diff --git a/docs/verification.md b/docs/verification.md index 7ce9abbc85..f03b2de1ee 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -1,69 +1,15 @@ -# Scraping and verification +# Verification -- [Evaluating JavaScript](#evaluating-javascript) -- [Capturing screenshot](#capturing-screenshot) +- [Screenshots](#screenshots) +- [Console logs](#console-logs) +- [Page errors](#page-errors) - [Page events](#page-events) -- [Handling exceptions](#handling-exceptions)
-## Evaluating JavaScript - -Execute JavaScript function in the page: -```js -const href = await page.evaluate(() => document.location.href); -``` - -If the result is a Promise or if the function is asynchronous evaluate will automatically wait until it's resolved: -```js -const status = await page.evaluate(async () => { - const response = await fetch(location.href); - return response.status; -}); -``` - -Get object handle and use it in multiple evaluations: -```js -// Create a new array in the page, write a reference to it in -// window.myArray and get a handle to it. -const myArrayHandle = await page.evaluateHandle(() => { - window.myArray = [1]; - return myArray; -}); - -// Get current length of the array using the handle. -const length = await page.evaluate( - (arg) => arg.myArray.length, - { myArray: myArrayHandle } -); - -// Add one more element to the array using the handle -await page.evaluate((arg) => arg.myArray.push(arg.newElement), { - myArray: myArrayHandle, - newElement: 2 -}); - -// Get current length of the array using window.myArray reference. -const newLength = await page.evaluate(() => window.myArray.length); - -// Release the object when it's no longer needed. -await myArrayHandle.dispose(); -``` - -#### API reference - -- [page.$(selector)](./api.md#pageselector) -- [page.$$(selector)](./api.md#pageselector-1) -- [page.$eval(selector, pageFunction[, arg])](./api.md#pageevalselector-pagefunction-arg) -- [page.$$eval(selector, pageFunction[, arg])](./api.md#pageevalselector-pagefunction-arg-1) -- [page.evaluate(pageFunction[, arg])](./api.md#pageevaluatepagefunction-arg) -- [page.evaluateHandle(pageFunction[, arg])](./api.md#pageevaluatehandlepagefunction-arg) - -
- -## Capturing screenshot +## Screenshots ```js // Save to file @@ -88,7 +34,7 @@ await elementHandle.screenshot({ path: 'screenshot.png' });
-## Page events +## Console logs You can listen for various events on the `page` object. Following are just some of the examples of the events you can assert and handle: @@ -103,6 +49,45 @@ page.on('console', msg => { }); ``` +#### API reference + +- [class: ConsoleMessage](./api.md#class-consolemessage) +- [class: Page](./api.md#class-page) +- [event: 'console'](./api.md#event-console) + +
+ +## Page errors + +Listen for uncaught exceptions in the page with the `pagerror` event. + +```js +// Log all uncaught errors to the terminal +page.on('pageerror', exception => { + console.log(`Uncaught exception: "${exception}"`); +}); + +// Navigate to a page with an exception. +await page.goto('data:text/html,'); +``` + +#### API reference + +- [class: Page](./api.md#class-page) +- [event: 'pageerror'](./api.md#event-pageerror) + +
+ +## Page events + +#### `"requestfailed"` + +```js +page.on('requestfailed', request => { + console.log(request.url() + ' ' + request.failure().errorText); +}); +``` + #### `"dialog"` - handle alert, confirm, prompt ```js @@ -122,30 +107,7 @@ const [popup] = await Promise.all([ #### API reference -- [class: ConsoleMessage](./api.md#class-consolemessage) - [class: Page](./api.md#class-page) -- [event: 'console'](./api.md#event-console) +- [event: 'requestfailed'](./api.md#event-requestfailed) - [event: 'dialog'](./api.md#event-dialog) - [event: 'popup'](./api.md#event-popup) - -
- - -## Handling exceptions - -Listen uncaught exceptions in the page: -```js -// Log all uncaught errors to the terminal -page.on('pageerror', exception => { - console.log(`Uncaught exception: "${exception}"`); -}); - -// Navigate to a page with an exception. -await page.goto('data:text/html,'); -``` - -#### API reference - -- [event: 'pageerror'](./api.md#event-pageerror) - -