fix: support toHaveAttribute(name, options) (#16941)

This commit is contained in:
Yury Semikhatsky 2022-08-30 17:53:00 -07:00 committed by GitHub
parent 990167124f
commit f30ac1d678
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 124 additions and 23 deletions

View file

@ -204,10 +204,10 @@ Whether to use `element.innerText` instead of `element.textContent` when retriev
* since: v1.18 * since: v1.18
## async method: LocatorAssertions.NotToHaveAttribute ## async method: LocatorAssertions.NotToHaveAttribute
* since: v1.20 * since: v1.18
* langs: python * langs: python
The opposite of [`method: LocatorAssertions.toHaveAttribute`]. The opposite of [`method: LocatorAssertions.toHaveAttribute#1`].
### param: LocatorAssertions.NotToHaveAttribute.name ### param: LocatorAssertions.NotToHaveAttribute.name
* since: v1.18 * since: v1.18
@ -885,21 +885,17 @@ Whether to use `element.innerText` instead of `element.textContent` when retriev
* since: v1.18 * since: v1.18
## async method: LocatorAssertions.toHaveAttribute ## async method: LocatorAssertions.toHaveAttribute#1
* since: v1.20 * since: v1.18
* langs: * langs:
- alias-java: hasAttribute - alias-java: hasAttribute
Ensures the [Locator] points to an element with given attribute. If the method Ensures the [Locator] points to an element with given attribute value.
is used without `'value'` argument, then the method will assert attribute existance.
```js ```js
const locator = page.locator('input'); const locator = page.locator('input');
// Assert attribute with given value. // Assert attribute with given value.
await expect(locator).toHaveAttribute('type', 'text'); await expect(locator).toHaveAttribute('type', 'text');
// Assert attribute existance.
await expect(locator).toHaveAttribute('disabled');
await expect(locator).not.toHaveAttribute('open');
``` ```
```java ```java
@ -925,23 +921,76 @@ var locator = Page.Locator("input");
await Expect(locator).ToHaveAttributeAsync("type", "text"); await Expect(locator).ToHaveAttributeAsync("type", "text");
``` ```
### param: LocatorAssertions.toHaveAttribute.name ### param: LocatorAssertions.toHaveAttribute#1.name
* since: v1.18 * since: v1.18
- `name` <[string]> - `name` <[string]>
Attribute name. Attribute name.
### param: LocatorAssertions.toHaveAttribute.value ### param: LocatorAssertions.toHaveAttribute#1.value
* since: v1.18 * since: v1.18
- `value` ?<[string]|[RegExp]> - `value` <[string]|[RegExp]>
Optional expected attribute value. If missing, method will assert attribute presence. Expected attribute value.
### option: LocatorAssertions.toHaveAttribute.timeout = %%-js-assertions-timeout-%% ### option: LocatorAssertions.toHaveAttribute#1.timeout = %%-js-assertions-timeout-%%
* since: v1.18 * since: v1.18
### option: LocatorAssertions.toHaveAttribute.timeout = %%-csharp-java-python-assertions-timeout-%% ### option: LocatorAssertions.toHaveAttribute#1.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18 * since: v1.18
## async method: LocatorAssertions.toHaveAttribute#2
* since: v1.26
* langs:
- alias-java: hasAttribute
Ensures the [Locator] points to an element with given attribute. The method will assert attribute
presence.
```js
const locator = page.locator('input');
// Assert attribute existance.
await expect(locator).toHaveAttribute('disabled');
await expect(locator).not.toHaveAttribute('open');
```
```java
assertThat(page.locator("input")).hasAttribute("disabled");
assertThat(page.locator("input")).not().hasAttribute("open");
```
```python async
from playwright.async_api import expect
locator = page.locator("input")
await expect(locator).to_have_attribute("disabled")
await expect(locator).not_to_have_attribute("open")
```
```python sync
from playwright.sync_api import expect
locator = page.locator("input")
expect(locator).to_have_attribute("disabled")
expect(locator).not_to_have_attribute("open")
```
```csharp
var locator = Page.Locator("input");
await Expect(locator).ToHaveAttributeAsync("disabled");
await Expect(locator).Not.ToHaveAttributeAsync("open");
```
### param: LocatorAssertions.toHaveAttribute#2.name
* since: v1.26
- `name` <[string]>
Attribute name.
### option: LocatorAssertions.toHaveAttribute#2.timeout = %%-js-assertions-timeout-%%
* since: v1.26
### option: LocatorAssertions.toHaveAttribute#2.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.26
## async method: LocatorAssertions.toHaveClass ## async method: LocatorAssertions.toHaveClass
* since: v1.20 * since: v1.20
* langs: * langs:

View file

@ -17,7 +17,7 @@
import type { Locator, Page, APIResponse } from 'playwright-core'; import type { Locator, Page, APIResponse } from 'playwright-core';
import type { FrameExpectOptions } from 'playwright-core/lib/client/types'; import type { FrameExpectOptions } from 'playwright-core/lib/client/types';
import { colors } from 'playwright-core/lib/utilsBundle'; import { colors } from 'playwright-core/lib/utilsBundle';
import { constructURLBasedOnBaseURL } from 'playwright-core/lib/utils'; import { constructURLBasedOnBaseURL, isRegExp } from 'playwright-core/lib/utils';
import type { Expect } from '../types'; import type { Expect } from '../types';
import { expectTypes, callLogText } from '../util'; import { expectTypes, callLogText } from '../util';
import { toBeTruthy } from './toBeTruthy'; import { toBeTruthy } from './toBeTruthy';
@ -138,18 +138,25 @@ export function toHaveAttribute(
this: ReturnType<Expect['getState']>, this: ReturnType<Expect['getState']>,
locator: LocatorEx, locator: LocatorEx,
name: string, name: string,
expected: string | RegExp | undefined, expected: string | RegExp | undefined | { timeout?: number},
options?: { timeout?: number }, options?: { timeout?: number },
) { ) {
if (!options) {
// Update params for the case toHaveAttribute(name, options);
if (typeof expected === 'object' && !isRegExp(expected)) {
options = expected;
expected = undefined;
}
}
if (expected === undefined) { if (expected === undefined) {
return toBeTruthy.call(this, 'toHaveAttribute', locator, 'Locator', async (isNot, timeout, customStackTrace) => { return toBeTruthy.call(this, 'toHaveAttribute', locator, 'Locator', async (isNot, timeout, customStackTrace) => {
return await locator._expect(customStackTrace, 'to.have.attribute', { expressionArg: name, isNot, timeout }); return await locator._expect(customStackTrace, 'to.have.attribute', { expressionArg: name, isNot, timeout });
}, options); }, options);
} }
return toMatchText.call(this, 'toHaveAttribute', locator, 'Locator', async (isNot, timeout, customStackTrace) => { return toMatchText.call(this, 'toHaveAttribute', locator, 'Locator', async (isNot, timeout, customStackTrace) => {
const expectedText = toExpectedTextValues([expected]); const expectedText = toExpectedTextValues([expected as (string | RegExp)]);
return await locator._expect(customStackTrace, 'to.have.attribute.value', { expressionArg: name, expectedText, isNot, timeout }); return await locator._expect(customStackTrace, 'to.have.attribute.value', { expressionArg: name, expectedText, isNot, timeout });
}, expected, options); }, expected as (string | RegExp), options);
} }
export function toHaveClass( export function toHaveClass(

View file

@ -3423,23 +3423,39 @@ interface LocatorAssertions {
}): Promise<void>; }): Promise<void>;
/** /**
* Ensures the [Locator] points to an element with given attribute. If the method is used without `'value'` argument, then * Ensures the [Locator] points to an element with given attribute value.
* the method will assert attribute existance.
* *
* ```js * ```js
* const locator = page.locator('input'); * const locator = page.locator('input');
* // Assert attribute with given value. * // Assert attribute with given value.
* await expect(locator).toHaveAttribute('type', 'text'); * await expect(locator).toHaveAttribute('type', 'text');
* ```
*
* @param name Attribute name.
* @param value Expected attribute value.
* @param options
*/
toHaveAttribute(name: string, value: string|RegExp, options?: {
/**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/
timeout?: number;
}): Promise<void>;
/**
* Ensures the [Locator] points to an element with given attribute. The method will assert attribute presence.
*
* ```js
* const locator = page.locator('input');
* // Assert attribute existance. * // Assert attribute existance.
* await expect(locator).toHaveAttribute('disabled'); * await expect(locator).toHaveAttribute('disabled');
* await expect(locator).not.toHaveAttribute('open'); * await expect(locator).not.toHaveAttribute('open');
* ``` * ```
* *
* @param name Attribute name. * @param name Attribute name.
* @param value Optional expected attribute value. If missing, method will assert attribute presence.
* @param options * @param options
*/ */
toHaveAttribute(name: string, value?: string|RegExp, options?: { toHaveAttribute(name: string, options?: {
/** /**
* Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`. * Time to retry the assertion for. Defaults to `timeout` in `TestConfig.expect`.
*/ */

View file

@ -235,6 +235,15 @@ test.describe('toHaveAttribute', () => {
await expect(locator).not.toHaveAttribute('open'); await expect(locator).not.toHaveAttribute('open');
await expect(locator).toHaveAttribute('id', 'node'); await expect(locator).toHaveAttribute('id', 'node');
}); });
test('should support boolean attribute with options', async ({ page }) => {
await page.setContent('<div checked id=node>Text content</div>');
const locator = page.locator('#node');
await expect(locator).toHaveAttribute('id', { timeout: 100 });
await expect(locator).toHaveAttribute('checked', { timeout: 100 });
await expect(locator).not.toHaveAttribute('open', { timeout: 100 });
await expect(locator).toHaveAttribute('id', 'node', { timeout: 100 });
});
}); });
test.describe('toHaveCSS', () => { test.describe('toHaveCSS', () => {

View file

@ -283,6 +283,26 @@ test('should return void/Promise when appropriate', async ({ runTSC }) => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });
test('should suppport toHaveAttribute withou optional value', async ({ runTSC }) => {
const result = await runTSC({
'a.spec.ts': `
const { test } = pwt;
test('custom matchers', async ({ page }) => {
const locator = page.locator('#node');
await test.expect(locator).toHaveAttribute('name', 'value');
await test.expect(locator).toHaveAttribute('name', 'value', { timeout: 10 });
await test.expect(locator).toHaveAttribute('disabled');
await test.expect(locator).toHaveAttribute('disabled', { timeout: 10 });
// @ts-expect-error
await test.expect(locator).toHaveAttribute('disabled', { foo: 1 });
// @ts-expect-error
await test.expect(locator).toHaveAttribute('name', 'value', 'opt');
});
`
});
expect(result.exitCode).toBe(0);
});
test.describe('helpful expect errors', () => { test.describe('helpful expect errors', () => {
test('top-level', async ({ runInlineTest }) => { test('top-level', async ({ runInlineTest }) => {
const result = await runInlineTest({ const result = await runInlineTest({