feat: expect(locator).toHaveAttribute to assert attribute presence (#16767)
This patch changes `expect(locator).toHaveAttribute()` so that the `value` argument can be omitted. When done so, the method will assert attribute existance. Fixes #16517
This commit is contained in:
Родитель
6087c9abfd
Коммит
622c73cc1e
|
@ -890,11 +890,16 @@ Whether to use `element.innerText` instead of `element.textContent` when retriev
|
||||||
* langs:
|
* langs:
|
||||||
- alias-java: hasAttribute
|
- alias-java: hasAttribute
|
||||||
|
|
||||||
Ensures the [Locator] points to an element with given attribute.
|
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.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
const locator = page.locator('input');
|
const locator = page.locator('input');
|
||||||
|
// 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
|
||||||
|
@ -928,9 +933,9 @@ Attribute name.
|
||||||
|
|
||||||
### param: LocatorAssertions.toHaveAttribute.value
|
### param: LocatorAssertions.toHaveAttribute.value
|
||||||
* since: v1.18
|
* since: v1.18
|
||||||
- `value` <[string]|[RegExp]>
|
- `value` ?<[string]|[RegExp]>
|
||||||
|
|
||||||
Expected attribute value.
|
Optional expected attribute value. If missing, method will assert attribute presence.
|
||||||
|
|
||||||
### option: LocatorAssertions.toHaveAttribute.timeout = %%-js-assertions-timeout-%%
|
### option: LocatorAssertions.toHaveAttribute.timeout = %%-js-assertions-timeout-%%
|
||||||
* since: v1.18
|
* since: v1.18
|
||||||
|
|
|
@ -1024,7 +1024,9 @@ export class InjectedScript {
|
||||||
{
|
{
|
||||||
// Element state / boolean values.
|
// Element state / boolean values.
|
||||||
let elementState: boolean | 'error:notconnected' | 'error:notcheckbox' | undefined;
|
let elementState: boolean | 'error:notconnected' | 'error:notcheckbox' | undefined;
|
||||||
if (expression === 'to.be.checked') {
|
if (expression === 'to.have.attribute') {
|
||||||
|
elementState = element.hasAttribute(options.expressionArg);
|
||||||
|
} else if (expression === 'to.be.checked') {
|
||||||
elementState = progress.injectedScript.elementState(element, 'checked');
|
elementState = progress.injectedScript.elementState(element, 'checked');
|
||||||
} else if (expression === 'to.be.unchecked') {
|
} else if (expression === 'to.be.unchecked') {
|
||||||
elementState = progress.injectedScript.elementState(element, 'unchecked');
|
elementState = progress.injectedScript.elementState(element, 'unchecked');
|
||||||
|
@ -1082,7 +1084,7 @@ export class InjectedScript {
|
||||||
{
|
{
|
||||||
// Single text value.
|
// Single text value.
|
||||||
let received: string | undefined;
|
let received: string | undefined;
|
||||||
if (expression === 'to.have.attribute') {
|
if (expression === 'to.have.attribute.value') {
|
||||||
received = element.getAttribute(options.expressionArg) || '';
|
received = element.getAttribute(options.expressionArg) || '';
|
||||||
} else if (expression === 'to.have.class') {
|
} else if (expression === 'to.have.class') {
|
||||||
received = element.classList.toString();
|
received = element.classList.toString();
|
||||||
|
|
|
@ -138,12 +138,17 @@ export function toHaveAttribute(
|
||||||
this: ReturnType<Expect['getState']>,
|
this: ReturnType<Expect['getState']>,
|
||||||
locator: LocatorEx,
|
locator: LocatorEx,
|
||||||
name: string,
|
name: string,
|
||||||
expected: string | RegExp,
|
expected: string | RegExp | undefined,
|
||||||
options?: { timeout?: number },
|
options?: { timeout?: number },
|
||||||
) {
|
) {
|
||||||
|
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) => {
|
return toMatchText.call(this, 'toHaveAttribute', locator, 'Locator', async (isNot, timeout, customStackTrace) => {
|
||||||
const expectedText = toExpectedTextValues([expected]);
|
const expectedText = toExpectedTextValues([expected]);
|
||||||
return await locator._expect(customStackTrace, 'to.have.attribute', { expressionArg: name, expectedText, isNot, timeout });
|
return await locator._expect(customStackTrace, 'to.have.attribute.value', { expressionArg: name, expectedText, isNot, timeout });
|
||||||
}, expected, options);
|
}, expected, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3423,18 +3423,23 @@ interface LocatorAssertions {
|
||||||
}): Promise<void>;
|
}): Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensures the [Locator] points to an element with given attribute.
|
* 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.
|
||||||
*
|
*
|
||||||
* ```js
|
* ```js
|
||||||
* const locator = page.locator('input');
|
* const locator = page.locator('input');
|
||||||
|
* // 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');
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @param name Attribute name.
|
* @param name Attribute name.
|
||||||
* @param value Expected attribute value.
|
* @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, value?: string|RegExp, 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`.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -228,8 +228,11 @@ test.describe('toHaveURL', () => {
|
||||||
|
|
||||||
test.describe('toHaveAttribute', () => {
|
test.describe('toHaveAttribute', () => {
|
||||||
test('pass', async ({ page }) => {
|
test('pass', async ({ page }) => {
|
||||||
await page.setContent('<div id=node>Text content</div>');
|
await page.setContent('<div checked id=node>Text content</div>');
|
||||||
const locator = page.locator('#node');
|
const locator = page.locator('#node');
|
||||||
|
await expect(locator).toHaveAttribute('id');
|
||||||
|
await expect(locator).toHaveAttribute('checked');
|
||||||
|
await expect(locator).not.toHaveAttribute('open');
|
||||||
await expect(locator).toHaveAttribute('id', 'node');
|
await expect(locator).toHaveAttribute('id', 'node');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Загрузка…
Ссылка в новой задаче