Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 14 additions & 85 deletions docs/src/api/class-locatorassertions.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,46 +213,29 @@ Whether to use `element.innerText` instead of `element.textContent` when retriev
### option: LocatorAssertions.NotToContainText.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18

## async method: LocatorAssertions.NotToHaveAttribute#1
* since: v1.18
Comment thread
rwoll marked this conversation as resolved.
## async method: LocatorAssertions.NotToHaveAttribute
* since: v1.20
* langs: python

The opposite of [`method: LocatorAssertions.toHaveAttribute#1`].
The opposite of [`method: LocatorAssertions.toHaveAttribute`].

### param: LocatorAssertions.NotToHaveAttribute#1.name
### param: LocatorAssertions.NotToHaveAttribute.name
* since: v1.18
- `name` <[string]>

Attribute name.

### param: LocatorAssertions.NotToHaveAttribute#1.value
### param: LocatorAssertions.NotToHaveAttribute.value
* since: v1.18
- `value` <[string]|[RegExp]>

Expected attribute value.

### option: LocatorAssertions.NotToHaveAttribute#1.timeout = %%-js-assertions-timeout-%%
### option: LocatorAssertions.NotToHaveAttribute.timeout = %%-js-assertions-timeout-%%
* since: v1.18
### option: LocatorAssertions.NotToHaveAttribute#1.timeout = %%-csharp-java-python-assertions-timeout-%%
### option: LocatorAssertions.NotToHaveAttribute.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.18

## async method: LocatorAssertions.NotToHaveAttribute#2
* since: v1.26
* langs: python

The opposite of [`method: LocatorAssertions.toHaveAttribute#2`].

### param: LocatorAssertions.NotToHaveAttribute#2.name
* since: v1.26
- `name` <[string]>

Attribute name.

### option: LocatorAssertions.NotToHaveAttribute#2.timeout = %%-js-assertions-timeout-%%
* since: v1.26
### option: LocatorAssertions.NotToHaveAttribute#2.timeout = %%-csharp-java-python-assertions-timeout-%%
* since: v1.26

## async method: LocatorAssertions.NotToHaveClass
* since: v1.20
* langs: python
Expand Down Expand Up @@ -921,16 +904,15 @@ Whether to use `element.innerText` instead of `element.textContent` when retriev
* since: v1.18


## async method: LocatorAssertions.toHaveAttribute#1
* since: v1.18
## async method: LocatorAssertions.toHaveAttribute
* since: v1.20
* langs:
- alias-java: hasAttribute

Ensures the [Locator] points to an element with given attribute value.
Ensures the [Locator] points to an element with given attribute.

```js
const locator = page.locator('input');
// Assert attribute with given value.
await expect(locator).toHaveAttribute('type', 'text');
```

Expand All @@ -957,76 +939,23 @@ var locator = Page.Locator("input");
await Expect(locator).ToHaveAttributeAsync("type", "text");
```

### param: LocatorAssertions.toHaveAttribute#1.name
### param: LocatorAssertions.toHaveAttribute.name
* since: v1.18
- `name` <[string]>

Attribute name.

### param: LocatorAssertions.toHaveAttribute#1.value
### param: LocatorAssertions.toHaveAttribute.value
* since: v1.18
- `value` <[string]|[RegExp]>

Expected attribute value.

### option: LocatorAssertions.toHaveAttribute#1.timeout = %%-js-assertions-timeout-%%
### option: LocatorAssertions.toHaveAttribute.timeout = %%-js-assertions-timeout-%%
* since: v1.18
### option: LocatorAssertions.toHaveAttribute#1.timeout = %%-csharp-java-python-assertions-timeout-%%
### option: LocatorAssertions.toHaveAttribute.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 existence.
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:
Expand Down
32 changes: 0 additions & 32 deletions docs/src/release-notes-csharp.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,6 @@ title: "Release notes"
toc_max_heading_level: 2
---

## Version 1.26

### Assertions

- [`method: LocatorAssertions.toHaveAttribute#2`] can now be used for asserting attribute existence.
- New option `Enabled` for [`method: LocatorAssertions.toBeEnabled`].
- [`method: LocatorAssertions.toHaveText`] now pierces open shadow roots.
- New option `Editable` for [`method: LocatorAssertions.toBeEditable`].
- New option `Visible` for [`method: LocatorAssertions.toBeVisible`].
- [`method: APIResponseAssertions.toBeOK`] is now available.

### Other highlights

- New option `MaxRedirects` for [`method: APIRequestContext.get`] and others to limit redirect count.
- Codegen now supports NUnit and MSTest frameworks.
- ASP .NET is now supported.

### Behavior Change

A bunch of Playwright APIs already support the `WaitUntil: WaitUntilState.DOMContentLoaded` option.
For example:

```csharp
await Page.GotoAsync("https://playwright.dev", new() { WaitUntil = WaitUntilState.DOMContentLoaded });
```

Prior to 1.26, this would wait for all iframes to fire the `DOMContentLoaded`
event.

To align with web specification, the `WaitUntilState.DOMContentLoaded` value only waits for
the target frame to fire the `'DOMContentLoaded'` event. Use `WaitUntil: WaitUntilState.Load` to wait for all iframes.

## Version 1.25

### New .runsettings file support
Expand Down
30 changes: 0 additions & 30 deletions docs/src/release-notes-java.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,6 @@ title: "Release notes"
toc_max_heading_level: 2
---

## Version 1.26

### Assertions

- [`method: LocatorAssertions.toHaveAttribute#2`] can now be used for asserting attribute existence.
- New option `enabled` for [`method: LocatorAssertions.toBeEnabled`].
- [`method: LocatorAssertions.toHaveText`] now pierces open shadow roots.
- New option `editable` for [`method: LocatorAssertions.toBeEditable`].
- New option `visible` for [`method: LocatorAssertions.toBeVisible`].

### Other highlights

- New option `setMaxRedirects` for [`method: APIRequestContext.get`] and others to limit redirect count.
- Docker images are now using OpenJDK 17.

### Behavior Change

A bunch of Playwright APIs already support the `setWaitUntil(WaitUntilState.DOMCONTENTLOADED)` option.
For example:

```js
page.navigate("https://playwright.dev", new Page.NavigateOptions().setWaitUntil(WaitUntilState.DOMCONTENTLOADED));
```

Prior to 1.26, this would wait for all iframes to fire the `DOMContentLoaded`
event.

To align with web specification, the `WaitUntilState.DOMCONTENTLOADED` value only waits for
the target frame to fire the `'DOMContentLoaded'` event. Use `setWaitUntil(WaitUntilState.LOAD)` to wait for all iframes.

## Version 1.25

### New APIs & changes
Expand Down
1 change: 0 additions & 1 deletion docs/src/release-notes-js.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ Read more in [our documentation](./docker#experimental-playwright-test-docker-in

### Assertions

- [`method: LocatorAssertions.toHaveAttribute#2`] can now be used for asserting attribute existence.
- New option `enabled` for [`method: LocatorAssertions.toBeEnabled`].
- [`method: LocatorAssertions.toHaveText`] now pierces open shadow roots.
- New option `editable` for [`method: LocatorAssertions.toBeEditable`].
Expand Down
30 changes: 0 additions & 30 deletions docs/src/release-notes-python.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,6 @@ title: "Release notes"
toc_max_heading_level: 2
---

## Version 1.26

### Assertions

- [`method: LocatorAssertions.toHaveAttribute#2`] can now be used for asserting attribute existence.
- New option `enabled` for [`method: LocatorAssertions.toBeEnabled`].
- [`method: LocatorAssertions.toHaveText`] now pierces open shadow roots.
- New option `editable` for [`method: LocatorAssertions.toBeEditable`].
- New option `visible` for [`method: LocatorAssertions.toBeVisible`].

### Other highlights

- New option `max_redirects` for [`method: APIRequestContext.get`] and others to limit redirect count.
- Python 3.11 is now supported.
Comment on lines -7 to -20
Copy link
Copy Markdown
Member Author

@rwoll rwoll Sep 16, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll bring back the release notes cleanly post cherry-pick closer to actual release. Some release notes got reverted since they were changed in 4b8a85e which also coupled a change to code. To get a clean, conflict free revert just included the whole commit.


### Behavior Change

A bunch of Playwright APIs already support the `wait_until: "domcontentloaded"` option.
For example:

```python
page.goto("https://playwright.dev", wait_until="domcontentloaded")
```

Prior to 1.26, this would wait for all iframes to fire the `DOMContentLoaded`
event.

To align with web specification, the `'domcontentloaded'` value only waits for
the target frame to fire the `'DOMContentLoaded'` event. Use `wait_until="load"` to wait for all iframes.

## Version 1.25

### Announcements
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1038,9 +1038,7 @@ export class InjectedScript {
{
// Element state / boolean values.
let elementState: boolean | 'error:notconnected' | 'error:notcheckbox' | undefined;
if (expression === 'to.have.attribute') {
elementState = element.hasAttribute(options.expressionArg);
} else if (expression === 'to.be.checked') {
if (expression === 'to.be.checked') {
elementState = progress.injectedScript.elementState(element, 'checked');
} else if (expression === 'to.be.unchecked') {
elementState = progress.injectedScript.elementState(element, 'unchecked');
Expand Down Expand Up @@ -1100,7 +1098,7 @@ export class InjectedScript {
{
// Single text value.
let received: string | undefined;
if (expression === 'to.have.attribute.value') {
if (expression === 'to.have.attribute') {
received = element.getAttribute(options.expressionArg) || '';
} else if (expression === 'to.have.class') {
received = element.classList.toString();
Expand Down
22 changes: 5 additions & 17 deletions packages/playwright-test/src/matchers/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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, isRegExp } from 'playwright-core/lib/utils';
import { constructURLBasedOnBaseURL } from 'playwright-core/lib/utils';
import type { Expect } from '../types';
import { expectTypes, callLogText } from '../util';
import { toBeTruthy } from './toBeTruthy';
Expand Down Expand Up @@ -141,25 +141,13 @@ export function toHaveAttribute(
this: ReturnType<Expect['getState']>,
locator: LocatorEx,
name: string,
expected: string | RegExp | undefined | { timeout?: number},
expected: string | RegExp,
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 as (string | RegExp)]);
return await locator._expect(customStackTrace, 'to.have.attribute.value', { expressionArg: name, expectedText, isNot, timeout });
}, expected as (string | RegExp), options);
const expectedText = toExpectedTextValues([expected]);
return await locator._expect(customStackTrace, 'to.have.attribute', { expressionArg: name, expectedText, isNot, timeout });
}, expected, options);
}

export function toHaveClass(
Expand Down
23 changes: 1 addition & 22 deletions packages/playwright-test/types/test.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3450,11 +3450,10 @@ interface LocatorAssertions {
}): Promise<void>;

/**
* Ensures the [Locator] points to an element with given attribute value.
* Ensures the [Locator] points to an element with given attribute.
*
* ```js
* const locator = page.locator('input');
* // Assert attribute with given value.
* await expect(locator).toHaveAttribute('type', 'text');
* ```
*
Expand All @@ -3469,26 +3468,6 @@ interface LocatorAssertions {
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 existence.
* await expect(locator).toHaveAttribute('disabled');
* await expect(locator).not.toHaveAttribute('open');
* ```
*
* @param name Attribute name.
* @param options
*/
toHaveAttribute(name: string, 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 CSS classes. This needs to be a full match or using a relaxed
* regular expression.
Expand Down
14 changes: 1 addition & 13 deletions tests/page/expect-misc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,22 +228,10 @@ test.describe('toHaveURL', () => {

test.describe('toHaveAttribute', () => {
test('pass', async ({ page }) => {
await page.setContent('<div checked id=node>Text content</div>');
await page.setContent('<div id=node>Text content</div>');
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');
});

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: 5000 });
await expect(locator).toHaveAttribute('checked', { timeout: 5000 });
await expect(locator).not.toHaveAttribute('open', { timeout: 5000 });
await expect(locator).toHaveAttribute('id', 'node', { timeout: 5000 });
});
});

test.describe('toHaveCSS', () => {
Expand Down
Loading