chore(expect): polish matcher names, remote arguable ones (#8060)

This commit is contained in:
Pavel Feldman 2021-08-06 16:58:42 -07:00 committed by GitHub
parent efb4af8489
commit 3424f59e67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 28 additions and 141 deletions

View File

@ -85,32 +85,12 @@ Refer to [configuration](./test-configuration.md) section for configuring test r
## Writing assertions ## Writing assertions
Playwright Test uses [expect](https://jestjs.io/docs/expect) library for test assertions. It provides a lot of matchers like `toEqual`, `toContain`, `toMatch`, `toMatchSnapshot` and many more. Playwright also extends this set with the following matchers: Playwright Test uses [expect](https://jestjs.io/docs/expect) library for test assertions. It extends it with the Playwright-specific matchers to achieve greater testing ergonomics.
- `toBeChecked` Learn more about [test assertions here](./test-assertions.md).
- `toBeDisabled`
- `toBeEditable` Here is a quick example of using them:
- `toBeEmpty`
- `toBeEnabled`
- `toBeFocused`
- `toBeHidden`
- `toBeSelected`
- `toBeVisible`
- `toContainText`
- `toHaveAttr`
- `toHaveClass`
- `toHaveCount`
- `toHaveCSS`
- `toHaveData`
- `toHaveId`
- `toHaveProp`
- `toHaveText`
- `toHaveTitle`
- `toHaveURL`
- `toHaveValue`
- `toMatchSnapshot`
- Find out more in the [assertions](./assertions.md) guide
```js js-flavor=js ```js js-flavor=js
// example.spec.js // example.spec.js
@ -123,7 +103,7 @@ test('my test', async ({ page }) => {
await expect(page).toHaveTitle('Playwright'); await expect(page).toHaveTitle('Playwright');
// Expect an attribute "to be strictly equal" to the value. // Expect an attribute "to be strictly equal" to the value.
await expect(page.locator('text=Get Started').toHaveAttr('href', '/docs/intro'); await expect(page.locator('text=Get Started').toHaveAttribute('href', '/docs/intro');
// Expect an element "to be visible". // Expect an element "to be visible".
await expect(page.locator('text=Learn more')).toBeVisible(); await expect(page.locator('text=Learn more')).toBeVisible();
@ -148,7 +128,7 @@ test('my test', async ({ page }) => {
await expect(page).toHaveTitle('Playwright'); await expect(page).toHaveTitle('Playwright');
// Expect an attribute "to be strictly equal" to the value. // Expect an attribute "to be strictly equal" to the value.
await expect(page.locator('text=Get Started').toHaveAttr('href', '/docs/intro'); await expect(page.locator('text=Get Started').toHaveAttribute('href', '/docs/intro');
// Expect an element "to be visible". // Expect an element "to be visible".
await expect(page.locator('text=Learn more')).toBeVisible(); await expect(page.locator('text=Learn more')).toBeVisible();

View File

@ -107,17 +107,6 @@ const locator = await page.locator('.my-element');
await expect(locator).toBeHidden(); await expect(locator).toBeHidden();
``` ```
## expect(locator).toBeSelected
- `options`
- `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`].
Ensures [Locator] points to a selected option.
```js
const locator = await page.locator('option[value=Three]');
await expect(locator).toBeSelected();
```
## expect(locator).toBeVisible ## expect(locator).toBeVisible
- `options` - `options`
- `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`]. - `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`].
@ -142,7 +131,7 @@ const locator = await page.locator('.title');
await expect(locator).toContainText('substring'); await expect(locator).toContainText('substring');
``` ```
## expect(locator).toHaveAttr(name, value) ## expect(locator).toHaveAttribute(name, value)
- `name`: <[string]> Attribute name - `name`: <[string]> Attribute name
- `value`: <[string]|[RegExp]> Attribute value - `value`: <[string]|[RegExp]> Attribute value
- `options` - `options`
@ -152,7 +141,7 @@ Ensures [Locator] points to an element with given attribute.
```js ```js
const locator = await page.locator('input'); const locator = await page.locator('input');
await expect(locator).toHaveAttr('type', 'text'); await expect(locator).toHaveAttribute('type', 'text');
``` ```
## expect(locator).toHaveClass(expected) ## expect(locator).toHaveClass(expected)
@ -199,19 +188,6 @@ const locator = await page.locator('button');
await expect(locator).toHaveCSS('display', 'flex'); await expect(locator).toHaveCSS('display', 'flex');
``` ```
## expect(locator).toHaveData(name, value)
- `name`: <[string]> Data attribute name
- `value`: <[string]|[RegExp]> Data value
- `options`
- `timeout`: <[number]> Time to retry assertion for, defaults to [`property: Fixtures.actionTimeout`].
Ensures [Locator] points to an element with given data binding.
```js
const locator = await page.locator('input');
await expect(locator).toHaveData('mydata', 'myvalue');
```
## expect(locator).toHaveId(id) ## expect(locator).toHaveId(id)
- `id`: <[string]> Element id - `id`: <[string]> Element id
- `options` - `options`
@ -224,7 +200,7 @@ const locator = await page.locator('input');
await expect(locator).toHaveId('lastname'); await expect(locator).toHaveId('lastname');
``` ```
## expect(locator).toHaveProp(name, value) ## expect(locator).toHaveJSProperty(name, value)
- `name`: <[string]> Property name - `name`: <[string]> Property name
- `value`: <[any]> Property value - `value`: <[any]> Property value
- `options` - `options`
@ -235,7 +211,7 @@ of a primitive type as well as a plain serializable JavaScript object.
```js ```js
const locator = await page.locator('.component'); const locator = await page.locator('.component');
await expect(locator).toHaveProp('loaded', true); await expect(locator).toHaveJSProperty('loaded', true);
``` ```
## expect(locator).toHaveText(expected, options) ## expect(locator).toHaveText(expected, options)

View File

@ -23,16 +23,14 @@ import {
toBeEnabled, toBeEnabled,
toBeFocused, toBeFocused,
toBeHidden, toBeHidden,
toBeSelected,
toBeVisible, toBeVisible,
toContainText, toContainText,
toHaveAttr, toHaveAttribute,
toHaveClass, toHaveClass,
toHaveCount, toHaveCount,
toHaveCSS, toHaveCSS,
toHaveData,
toHaveId, toHaveId,
toHaveProp, toHaveJSProperty,
toHaveText, toHaveText,
toHaveTitle, toHaveTitle,
toHaveURL, toHaveURL,
@ -54,16 +52,14 @@ const customMatchers = {
toBeEnabled, toBeEnabled,
toBeFocused, toBeFocused,
toBeHidden, toBeHidden,
toBeSelected,
toBeVisible, toBeVisible,
toContainText, toContainText,
toHaveAttr, toHaveAttribute,
toHaveClass, toHaveClass,
toHaveCount, toHaveCount,
toHaveCSS, toHaveCSS,
toHaveData,
toHaveId, toHaveId,
toHaveProp, toHaveJSProperty,
toHaveText, toHaveText,
toHaveTitle, toHaveTitle,
toHaveURL, toHaveURL,

View File

@ -96,18 +96,6 @@ export function toBeHidden(
}, options); }, options);
} }
export function toBeSelected(
this: ReturnType<Expect['getState']>,
locator: Locator,
options?: { timeout?: number },
) {
return toBeTruthy.call(this, 'toBeSelected', locator, 'Locator', async timeout => {
return await locator.evaluate(element => {
return (element as HTMLOptionElement).selected;
}, { timeout });
}, options);
}
export function toBeVisible( export function toBeVisible(
this: ReturnType<Expect['getState']>, this: ReturnType<Expect['getState']>,
locator: Locator, locator: Locator,
@ -131,14 +119,14 @@ export function toContainText(
}, expected, { ...options, matchSubstring: true }); }, expected, { ...options, matchSubstring: true });
} }
export function toHaveAttr( export function toHaveAttribute(
this: ReturnType<Expect['getState']>, this: ReturnType<Expect['getState']>,
locator: Locator, locator: Locator,
name: string, name: string,
expected: string | RegExp, expected: string | RegExp,
options?: { timeout?: number }, options?: { timeout?: number },
) { ) {
return toMatchText.call(this, 'toHaveAttr', locator, 'Locator', async timeout => { return toMatchText.call(this, 'toHaveAttribute', locator, 'Locator', async timeout => {
return await locator.getAttribute(name, { timeout }) || ''; return await locator.getAttribute(name, { timeout }) || '';
}, expected, options); }, expected, options);
} }
@ -185,18 +173,6 @@ export function toHaveCSS(
}, expected, options); }, expected, options);
} }
export function toHaveData(
this: ReturnType<Expect['getState']>,
locator: Locator,
name: string,
expected: string | RegExp,
options?: { timeout?: number },
) {
return toMatchText.call(this, 'toHaveData', locator, 'Locator', async timeout => {
return await locator.getAttribute('data-' + name, { timeout }) || '';
}, expected, options);
}
export function toHaveId( export function toHaveId(
this: ReturnType<Expect['getState']>, this: ReturnType<Expect['getState']>,
locator: Locator, locator: Locator,
@ -208,14 +184,14 @@ export function toHaveId(
}, expected, options); }, expected, options);
} }
export function toHaveProp( export function toHaveJSProperty(
this: ReturnType<Expect['getState']>, this: ReturnType<Expect['getState']>,
locator: Locator, locator: Locator,
name: string, name: string,
expected: any, expected: any,
options?: { timeout?: number }, options?: { timeout?: number },
) { ) {
return toEqual.call(this, 'toHaveProp', locator, 'Locator', async timeout => { return toEqual.call(this, 'toHaveJSProperty', locator, 'Locator', async timeout => {
return await locator.evaluate((element, name) => (element as any)[name], name, { timeout }); return await locator.evaluate((element, name) => (element as any)[name], name, { timeout });
}, expected, options); }, expected, options);
} }

View File

@ -32,7 +32,7 @@ test('should support toHaveCount', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });
test('should support toHaveProp', async ({ runInlineTest }) => { test('should support toHaveJSProperty', async ({ runInlineTest }) => {
const result = await runInlineTest({ const result = await runInlineTest({
'a.test.ts': ` 'a.test.ts': `
const { test } = pwt; const { test } = pwt;
@ -41,14 +41,14 @@ test('should support toHaveProp', async ({ runInlineTest }) => {
await page.setContent('<div></div>'); await page.setContent('<div></div>');
await page.$eval('div', e => e.foo = { a: 1, b: 'string', c: new Date(1627503992000) }); await page.$eval('div', e => e.foo = { a: 1, b: 'string', c: new Date(1627503992000) });
const locator = page.locator('div'); const locator = page.locator('div');
await expect(locator).toHaveProp('foo', { a: 1, b: 'string', c: new Date(1627503992000) }); await expect(locator).toHaveJSProperty('foo', { a: 1, b: 'string', c: new Date(1627503992000) });
}); });
test('fail', async ({ page }) => { test('fail', async ({ page }) => {
await page.setContent('<div></div>'); await page.setContent('<div></div>');
await page.$eval('div', e => e.foo = { a: 1, b: 'string', c: new Date(1627503992000) }); await page.$eval('div', e => e.foo = { a: 1, b: 'string', c: new Date(1627503992000) });
const locator = page.locator('div'); const locator = page.locator('div');
await expect(locator).toHaveProp('foo', { a: 1, b: 'string', c: new Date(1627503992001) }, { timeout: 1000 }); await expect(locator).toHaveJSProperty('foo', { a: 1, b: 'string', c: new Date(1627503992001) }, { timeout: 1000 });
}); });
`, `,
}, { workers: 1 }); }, { workers: 1 });

View File

@ -141,7 +141,7 @@ test('should support toHaveText with innerText', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });
test('should support toHaveAttr', async ({ runInlineTest }) => { test('should support toHaveAttribute', async ({ runInlineTest }) => {
const result = await runInlineTest({ const result = await runInlineTest({
'a.test.ts': ` 'a.test.ts': `
const { test } = pwt; const { test } = pwt;
@ -149,23 +149,7 @@ test('should support toHaveAttr', async ({ runInlineTest }) => {
test('pass', async ({ page }) => { test('pass', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>'); await page.setContent('<div id=node>Text content</div>');
const locator = page.locator('#node'); const locator = page.locator('#node');
await expect(locator).toHaveAttr('id', 'node'); await expect(locator).toHaveAttribute('id', 'node');
});
`,
}, { workers: 1 });
expect(result.passed).toBe(1);
expect(result.exitCode).toBe(0);
});
test('should support toHaveData', async ({ runInlineTest }) => {
const result = await runInlineTest({
'a.test.ts': `
const { test } = pwt;
test('pass', async ({ page }) => {
await page.setContent('<div id=node>Text content</div>');
const locator = page.locator('#node');
await expect(locator).toHaveAttr('id', 'node');
}); });
`, `,
}, { workers: 1 }); }, { workers: 1 });

View File

@ -128,7 +128,7 @@ test('should support toBeVisible, toBeHidden', async ({ runInlineTest }) => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });
test('should support toBeFocused, toBeSelected', async ({ runInlineTest }) => { test('should support toBeFocused', async ({ runInlineTest }) => {
const result = await runInlineTest({ const result = await runInlineTest({
'a.test.ts': ` 'a.test.ts': `
const { test } = pwt; const { test } = pwt;
@ -139,23 +139,8 @@ test('should support toBeFocused, toBeSelected', async ({ runInlineTest }) => {
await locator.focus(); await locator.focus();
await expect(locator).toBeFocused({ timeout: 1000 }); await expect(locator).toBeFocused({ timeout: 1000 });
}); });
test('selected', async ({ page }) => {
await page.setContent('<select><option>One</option></select>');
const locator = page.locator('option');
await expect(locator).toBeSelected();
});
test('fail on strict option', async ({ page }) => {
await page.setContent('<select><option>One</option><option>Two</option></select>');
const locator = page.locator('option');
await expect(locator).toBeSelected();
});
`, `,
}, { workers: 1 }); }, { workers: 1 });
const output = stripAscii(result.output); expect(result.passed).toBe(1);
expect(output).toContain('strict mode violation'); expect(result.exitCode).toBe(0);
expect(result.passed).toBe(2);
expect(result.failed).toBe(1);
expect(result.exitCode).toBe(1);
}); });

14
types/testExpect.d.ts vendored
View File

@ -109,11 +109,6 @@ declare global {
*/ */
toBeVisible(options?: { timeout?: number }): Promise<R>; toBeVisible(options?: { timeout?: number }): Promise<R>;
/**
* Asserts given select option is selected
*/
toBeSelected(options?: { timeout?: number }): Promise<R>;
/** /**
* Asserts element's text content matches given pattern or contains given substring. * Asserts element's text content matches given pattern or contains given substring.
*/ */
@ -122,7 +117,7 @@ declare global {
/** /**
* Asserts element's attributes `name` matches expected value. * Asserts element's attributes `name` matches expected value.
*/ */
toHaveAttr(expected: string | RegExp, name: string, options?: { timeout?: number }): Promise<R>; toHaveAttribute(expected: string | RegExp, name: string, options?: { timeout?: number }): Promise<R>;
/** /**
* Asserts that DOM node has a given CSS class. * Asserts that DOM node has a given CSS class.
@ -139,11 +134,6 @@ declare global {
*/ */
toHaveCSS(expected: string | RegExp, name: string, options?: { timeout?: number }): Promise<R>; toHaveCSS(expected: string | RegExp, name: string, options?: { timeout?: number }): Promise<R>;
/**
* Asserts element's data attribute data-`name` matches expected value.
*/
toHaveData(expected: string | RegExp, name: string, options?: { timeout?: number }): Promise<R>;
/** /**
* Asserts element's `id` attribute matches expected value. * Asserts element's `id` attribute matches expected value.
*/ */
@ -152,7 +142,7 @@ declare global {
/** /**
* Asserts JavaScript object that corresponds to the Node has a property with given value. * Asserts JavaScript object that corresponds to the Node has a property with given value.
*/ */
toHaveProp(name: string, value: any, options?: { timeout?: number }): Promise<R>; toHaveJSProperty(name: string, value: any, options?: { timeout?: number }): Promise<R>;
/** /**
* Asserts element's text content. * Asserts element's text content.