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
## async method: LocatorAssertions.NotToHaveAttribute
* since: v1.20
* since: v1.18
* langs: python
The opposite of [`method: LocatorAssertions.toHaveAttribute`].
The opposite of [`method: LocatorAssertions.toHaveAttribute#1`].
### param: LocatorAssertions.NotToHaveAttribute.name
* since: v1.18
@ -885,21 +885,17 @@ Whether to use `element.innerText` instead of `element.textContent` when retriev
* since: v1.18
## async method: LocatorAssertions.toHaveAttribute
* since: v1.20
## async method: LocatorAssertions.toHaveAttribute#1
* since: v1.18
* langs:
- alias-java: hasAttribute
Ensures the [Locator] points to an element with given attribute. If the method
is used without `'value'` argument, then the method will assert attribute existance.
Ensures the [Locator] points to an element with given attribute value.
```js
const locator = page.locator('input');
// Assert attribute with given value.
await expect(locator).toHaveAttribute('type', 'text');
// Assert attribute existance.
await expect(locator).toHaveAttribute('disabled');
await expect(locator).not.toHaveAttribute('open');
```
```java
@ -925,23 +921,76 @@ var locator = Page.Locator("input");
await Expect(locator).ToHaveAttributeAsync("type", "text");
```
### param: LocatorAssertions.toHaveAttribute.name
### param: LocatorAssertions.toHaveAttribute#1.name
* since: v1.18
- `name` <[string]>
Attribute name.
### param: LocatorAssertions.toHaveAttribute.value
### param: LocatorAssertions.toHaveAttribute#1.value
* 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
### option: LocatorAssertions.toHaveAttribute.timeout = %%-csharp-java-python-assertions-timeout-%%
### option: LocatorAssertions.toHaveAttribute#1.timeout = %%-csharp-java-python-assertions-timeout-%%
* 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
* since: v1.20
* langs:

View file

@ -17,7 +17,7 @@
import type { Locator, Page, APIResponse } from 'playwright-core';
import type { FrameExpectOptions } from 'playwright-core/lib/client/types';
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 { expectTypes, callLogText } from '../util';
import { toBeTruthy } from './toBeTruthy';
@ -138,18 +138,25 @@ export function toHaveAttribute(
this: ReturnType<Expect['getState']>,
locator: LocatorEx,
name: string,
expected: string | RegExp | undefined,
expected: string | RegExp | undefined | { 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) {
return toBeTruthy.call(this, 'toHaveAttribute', locator, 'Locator', async (isNot, timeout, customStackTrace) => {
return await locator._expect(customStackTrace, 'to.have.attribute', { expressionArg: name, isNot, timeout });
}, options);
}
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 });
}, expected, options);
}, expected as (string | RegExp), options);
}
export function toHaveClass(

View file

@ -3423,23 +3423,39 @@ interface LocatorAssertions {
}): Promise<void>;
/**
* Ensures the [Locator] points to an element with given attribute. If the method is used without `'value'` argument, then
* the method will assert attribute existance.
* Ensures the [Locator] points to an element with given attribute value.
*
* ```js
* const locator = page.locator('input');
* // Assert attribute with given value.
* 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.
* await expect(locator).toHaveAttribute('disabled');
* await expect(locator).not.toHaveAttribute('open');
* ```
*
* @param name Attribute name.
* @param value Optional expected attribute value. If missing, method will assert attribute presence.
* @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`.
*/

View file

@ -235,6 +235,15 @@ test.describe('toHaveAttribute', () => {
await expect(locator).not.toHaveAttribute('open');
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', () => {

View file

@ -283,6 +283,26 @@ test('should return void/Promise when appropriate', async ({ runTSC }) => {
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('top-level', async ({ runInlineTest }) => {
const result = await runInlineTest({