Add coexistence checks to all enqueue methods to prevent loading both React and Grid.js assets simultaneously. Changes: - ReactAdmin.php: Only enqueue React assets when ?react=1 - Init.php: Skip Grid.js when React active on admin pages - Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0 - Customer.php, Product.php, License.php: Add coexistence checks Now the toggle between Classic and React versions works correctly. Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
241 lines
9.8 KiB
Markdown
241 lines
9.8 KiB
Markdown
# expect-puppeteer
|
|
|
|
[![Build Status][build-badge]][build]
|
|
[![version][version-badge]][package]
|
|
[![MIT License][license-badge]][license]
|
|
|
|
Assertion library for Puppeteer.
|
|
|
|
```
|
|
npm install expect-puppeteer
|
|
```
|
|
|
|
## Usage
|
|
|
|
Without Jest:
|
|
|
|
```js
|
|
import expect from 'expect-puppeteer'
|
|
;(async () => {
|
|
const browser = await puppeteer.launch()
|
|
const page = await browser.newPage()
|
|
await page.goto('https://google.com')
|
|
await expect(page).toMatch('google')
|
|
await browser.close()
|
|
})()
|
|
```
|
|
|
|
## Use with Jest
|
|
|
|
To use with Jest, just modify your configuration:
|
|
|
|
```json
|
|
{
|
|
"setupFilesAfterEnv": ["expect-puppeteer"]
|
|
}
|
|
```
|
|
|
|
## Why do I need it
|
|
|
|
Writing integration test is very hard, especially when you are testing a Single Page Applications. Data are loaded asynchronously and it is difficult to know exactly when an element will be displayed in the page.
|
|
|
|
[Puppeteer API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md) is great, but it is low level and not designed for integration testing.
|
|
|
|
This API is designed for integration testing:
|
|
|
|
- It will wait for element before running an action
|
|
- It adds additional feature like matching an element using text
|
|
|
|
**Example**
|
|
|
|
```js
|
|
// Does not work if button is not in page
|
|
await page.click('button')
|
|
|
|
// Will try while 500ms to click on "button"
|
|
await page.toClick('button')
|
|
|
|
// Will match a button with a "My button" text inside
|
|
await page.toClick('button', { text: 'My button' })
|
|
```
|
|
|
|
## API
|
|
|
|
##### Table of Contents
|
|
|
|
<!-- toc -->
|
|
|
|
- [toClick](#toClick)
|
|
- [toDisplayDialog](#toDisplayDialog)
|
|
- [toFill](#toFill)
|
|
- [toFillForm](#toFillForm)
|
|
- [toMatch](#toMatch)
|
|
- [toMatchElement](#toMatchElement)
|
|
- [toSelect](#toSelect)
|
|
- [toUploadFile](#toUploadFile)
|
|
|
|
### <a name="toClick"></a>expect(instance).toClick(selector[, options])
|
|
|
|
Expect an element to be in the page or element, then click on it.
|
|
|
|
- `instance` <[Page]|[ElementHandle]> Context
|
|
- `selector` <[string]> A [selector] to click on
|
|
- `options` <[Object]> Optional parameters
|
|
- `button` <"left"|"right"|"middle"> Defaults to `left`.
|
|
- `clickCount` <[number]> defaults to 1. See [UIEvent.detail].
|
|
- `delay` <[number]> Time to wait between `mousedown` and `mouseup` in milliseconds. Defaults to 0.
|
|
- `text` <[string]|[RegExp]> A text or a RegExp to match in element `textContent`.
|
|
|
|
```js
|
|
await expect(page).toClick('button', { text: 'Home' })
|
|
```
|
|
|
|
### <a name="toDisplayDialog"></a>expect(page).toDisplayDialog(block)
|
|
|
|
Expect block function to trigger a dialog and returns it.
|
|
|
|
- `page` <[Page]> Context
|
|
- `block` <[function]> A [function] that should trigger a dialog
|
|
|
|
```js
|
|
const dialog = await expect(page).toDisplayDialog(async () => {
|
|
await expect(page).toClick('button', { text: 'Show dialog' })
|
|
})
|
|
```
|
|
|
|
### <a name="toFill"></a>expect(instance).toFill(selector, value[, options])
|
|
|
|
Expect a control to be in the page or element, then fill it with text.
|
|
|
|
- `instance` <[Page]|[ElementHandle]> Context
|
|
- `selector` <[string]> A [selector] to match field
|
|
- `value` <[string]> Value to fill
|
|
- `options` <[Object]> Optional parameters
|
|
- `delay` <[number]> delay to pass to [the puppeteer `element.type` API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#elementhandletypetext-options)
|
|
|
|
```js
|
|
await expect(page).toFill('input[name="firstName"]', 'James')
|
|
```
|
|
|
|
### <a name="toFillForm"></a>expect(instance).toFillForm(selector, values[, options])
|
|
|
|
Expect a form to be in the page or element, then fill its controls.
|
|
|
|
- `instance` <[Page]|[ElementHandle]> Context
|
|
- `selector` <[string]> A [selector] to match form
|
|
- `values` <[Object]> Values to fill
|
|
- `options` <[Object]> Optional parameters
|
|
- `delay` <[number]> delay to pass to [the puppeteer `element.type` API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#elementhandletypetext-options)
|
|
|
|
```js
|
|
await expect(page).toFillForm('form[name="myForm"]', {
|
|
firstName: 'James',
|
|
lastName: 'Bond',
|
|
})
|
|
```
|
|
|
|
### <a name="toMatch"></a>expect(instance).toMatch(matcher[, options])
|
|
|
|
Expect a text or a string RegExp to be present in the page or element.
|
|
|
|
- `instance` <[Page]|[ElementHandle]> Context
|
|
- `matcher` <[string]|[RegExp]> A text or a RegExp to match in page
|
|
- `options` <[Object]> Optional parameters
|
|
- `polling` <[string]|[number]> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values:
|
|
- `raf` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
|
|
- `mutation` - to execute `pageFunction` on every DOM mutation.
|
|
- `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
|
|
|
|
```js
|
|
// Matching using text
|
|
await expect(page).toMatch('Lorem ipsum')
|
|
// Matching using RegExp
|
|
await expect(page).toMatch(/lo.*/)
|
|
```
|
|
|
|
### <a name="toMatchElement"></a>expect(instance).toMatchElement(selector[, options])
|
|
|
|
Expect an element be present in the page or element.
|
|
|
|
- `instance` <[Page]|[ElementHandle]> Context
|
|
- `selector` <[string]> A [selector] to match element
|
|
- `options` <[Object]> Optional parameters
|
|
- `polling` <[string]|[number]> An interval at which the `pageFunction` is executed, defaults to `raf`. If `polling` is a number, then it is treated as an interval in milliseconds at which the function would be executed. If `polling` is a string, then it can be one of the following values:
|
|
- `raf` - to constantly execute `pageFunction` in `requestAnimationFrame` callback. This is the tightest polling mode which is suitable to observe styling changes.
|
|
- `mutation` - to execute `pageFunction` on every DOM mutation.
|
|
- `timeout` <[number]> maximum time to wait for in milliseconds. Defaults to `30000` (30 seconds). Pass `0` to disable timeout. The default value can be changed by using the [page.setDefaultTimeout(timeout)](#pagesetdefaulttimeouttimeout) method.
|
|
- `text` <[string]|[RegExp]> A text or a RegExp to match in element `textContent`.
|
|
- `visible` <[boolean]> wait for element to be present in DOM and to be visible, i.e. to not have `display: none` or `visibility: hidden` CSS properties. Defaults to `false`.
|
|
|
|
```js
|
|
// Select a row containing a text
|
|
const row = await expect(page).toMatchElement('tr', { text: 'My row' })
|
|
// Click on the third column link
|
|
await expect(row).toClick('td:nth-child(2) a')
|
|
```
|
|
|
|
### <a name="toSelect"></a>expect(instance).toSelect(selector, valueOrText)
|
|
|
|
Expect a select control to be present in the page or element, then select the specified option.
|
|
|
|
- `instance` <[Page]|[ElementHandle]> Context
|
|
- `selector` <[string]> A [selector] to match select [element]
|
|
- `valueOrText` <[string]> Value or text matching option
|
|
|
|
```js
|
|
await expect(page).toSelect('select[name="choices"]', 'Choice 1')
|
|
```
|
|
|
|
### <a name="toUploadFile"></a>expect(instance).toUploadFile(selector, filePath)
|
|
|
|
Expect a input file control to be present in the page or element, then fill it with a local file.
|
|
|
|
- `instance` <[Page]|[ElementHandle]> Context
|
|
- `selector` <[string]> A [selector] to match input [element]
|
|
- `filePath` <[string]> A file path
|
|
|
|
```js
|
|
import path from 'path'
|
|
|
|
await expect(page).toUploadFile(
|
|
'input[type="file"]',
|
|
path.join(__dirname, 'file.txt'),
|
|
)
|
|
```
|
|
|
|
## Configure default options
|
|
|
|
To configure default options like `timeout`, `expect-puppeteer` exposes two methods: `getDefaultOptions` and `setDefaultOptions`. You can find available options in [Puppeteer `page.waitForFunction` documentation](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitforfunctionpagefunction-options-args). Default options are set to: `{ timeout: 500 }`.
|
|
|
|
```js
|
|
import { setDefaultOptions } from 'expect-puppeteer'
|
|
|
|
setDefaultOptions({ timeout: 1000 })
|
|
```
|
|
|
|
## License
|
|
|
|
MIT
|
|
|
|
[build-badge]: https://img.shields.io/travis/smooth-code/jest-puppeteer.svg?style=flat-square
|
|
[build]: https://travis-ci.org/smooth-code/jest-puppeteer
|
|
[version-badge]: https://img.shields.io/npm/v/expect-puppeteer.svg?style=flat-square
|
|
[package]: https://www.npmjs.com/package/expect-puppeteer
|
|
[license-badge]: https://img.shields.io/npm/l/expect-puppeteer.svg?style=flat-square
|
|
[license]: https://github.com/smooth-code/jest-puppeteer/blob/master/LICENSE
|
|
[array]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array 'Array'
|
|
[boolean]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type 'Boolean'
|
|
[function]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function 'Function'
|
|
[number]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Number_type 'Number'
|
|
[object]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object 'Object'
|
|
[promise]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise 'Promise'
|
|
[regexp]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp 'RegExp'
|
|
[string]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type 'String'
|
|
[error]: https://nodejs.org/api/errors.html#errors_class_error 'Error'
|
|
[element]: https://developer.mozilla.org/en-US/docs/Web/API/element 'Element'
|
|
[map]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map 'Map'
|
|
[selector]: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors 'selector'
|
|
[page]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-page 'Page'
|
|
[elementhandle]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#class-elementhandle 'ElementHandle'
|
|
[uievent.detail]: https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
|