expect-webdriverio 6.1.0 → 7.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -5
- package/jest.d.ts +4 -2
- package/lib/api/index.js +2 -0
- package/lib/index.js +2 -0
- package/lib/matchers/asymmetrics/asymmetricsUtils.js +10 -0
- package/lib/matchers/asymmetrics/multiRemote.js +36 -0
- package/lib/matchers/browser/toHaveClipboardText.js +18 -8
- package/lib/matchers/browser/toHaveLocalStorageItem.js +22 -11
- package/lib/matchers/browser/toHaveTitle.js +16 -6
- package/lib/matchers/browser/toHaveUrl.js +16 -6
- package/lib/matchers/element/toHaveAttribute.js +2 -2
- package/lib/matchers/element/toHaveChildren.js +4 -3
- package/lib/matchers/element/toHaveComputedLabel.js +4 -4
- package/lib/matchers/element/toHaveComputedRole.js +4 -4
- package/lib/matchers/element/toHaveElementClass.js +4 -4
- package/lib/matchers/element/toHaveElementProperty.js +4 -4
- package/lib/matchers/element/toHaveHTML.js +4 -4
- package/lib/matchers/element/toHaveHeight.js +2 -2
- package/lib/matchers/element/toHaveSize.js +3 -3
- package/lib/matchers/element/toHaveStyle.js +3 -3
- package/lib/matchers/element/toHaveText.js +5 -5
- package/lib/matchers/element/toHaveValue.js +1 -1
- package/lib/matchers/element/toHaveWidth.js +2 -2
- package/lib/matchers/elements/toBeElementsArrayOfSize.js +70 -3
- package/lib/matchers/mock/toBeRequestedTimes.js +28 -5
- package/lib/matchers/mock/toBeRequestedWith.js +40 -12
- package/lib/matchers/snapshot.js +40 -11
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/lib/util/elementsUtil.js +34 -6
- package/lib/util/executeBrowserCommand.js +37 -0
- package/lib/util/executeCommand.js +107 -19
- package/lib/util/formatMessage.js +80 -6
- package/lib/util/multiRemoteUtils.js +85 -0
- package/lib/util/numberOptionsUtil.js +6 -0
- package/lib/util/refetchElements.js +24 -2
- package/lib/util/stackUtil.js +7 -0
- package/lib/utils.js +7 -2
- package/package.json +3 -3
- package/types/expect-webdriverio.d.ts +384 -39
package/README.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
# expect-webdriverio [](https://github.com/webdriverio/expect-webdriverio/actions/workflows/test.yml)
|
|
2
2
|
|
|
3
|
-
###### [API](docs/API.md) | [TypeScript / JS Autocomplete](docs/Types.md) | [Examples](docs/Examples.md) | [Extending Matchers](docs/CustomMatchers.md)
|
|
3
|
+
###### [API](docs/API.md) | [Multiple Elements](docs/MultipleElements.md) | [Multi-remote](docs/MultiRemote.md) | [TypeScript / JS Autocomplete](docs/Types.md) | [Examples](docs/Examples.md) | [Extending Matchers](docs/CustomMatchers.md)
|
|
4
4
|
|
|
5
5
|
> [WebdriverIO](https://webdriver.io/) assertion library inspired by [expect](https://www.npmjs.com/package/expect)
|
|
6
6
|
|
|
7
7
|
## Key Features
|
|
8
8
|
|
|
9
|
-
- [Waits](#default-options) for expectations to succeed
|
|
10
|
-
- Supports single element `$()` & multiple elements `$$()`
|
|
9
|
+
- [Waits](docs/API.md#default-options) for expectations to succeed
|
|
10
|
+
- Supports single element `$()` & [multiple elements](docs/MultipleElements.md) `$$()`
|
|
11
|
+
- Supports [multi-remote](docs/MultiRemote.md) browsers & elements, with one expected value per browser instance
|
|
11
12
|
- Detailed [error messages](#error-messages)
|
|
12
13
|
- Works in Mocha, Cucumber, Jest, and Jasmine
|
|
13
14
|
- Built-in [types](docs/Types.md) for TypeScript and JS autocompletion
|
|
@@ -21,7 +22,7 @@
|
|
|
21
22
|
- **Cucumber**: `npm install @wdio/cucumber-framework`
|
|
22
23
|
- **Jest**: No adapter needed — see [Jest Framework section](docs/Framework.md#jest)
|
|
23
24
|
|
|
24
|
-
**Note:** [WebdriverIO](https://github.com/webdriverio/webdriverio) `v9.
|
|
25
|
+
**Note:** [WebdriverIO](https://github.com/webdriverio/webdriverio) `v9.31.5` or higher is required!
|
|
25
26
|
|
|
26
27
|
## Usage
|
|
27
28
|
|
|
@@ -36,6 +37,17 @@ await expect($$('buttons')).toBeDisplayed()
|
|
|
36
37
|
|
|
37
38
|
See more [Examples](docs/Examples.md).
|
|
38
39
|
|
|
40
|
+
#### Multi-remote
|
|
41
|
+
|
|
42
|
+
With [multi-remote](https://webdriver.io/docs/multiremote), assertions check every browser instance, with a single expected value or one per instance:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
await expect(multiRemoteBrowser).toHaveTitle('WebdriverIO')
|
|
46
|
+
await expect(multiRemoteBrowser.$('h1')).toHaveText(expect.multiRemote({ chrome: 'Welcome', firefox: 'Bienvenue' }))
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Some feature flags and WebdriverIO environment variables are required or recommended, see [Multi-remote Support](docs/MultiRemote.md#requirements--configuration).
|
|
50
|
+
|
|
39
51
|
#### Local & Browser Runners
|
|
40
52
|
- **Local Runner**: Fully compatible. You can leverage your test framework adapter as mentioned above.
|
|
41
53
|
- **Browser Runner**: Designed for component frameworks like React, Preact, Vue.js, Svelte, and SolidJS, with a few known limitations. For details, see the [Browser Runner Framework section](docs/Framework.md#browser-runner).
|
|
@@ -86,4 +98,3 @@ First of all, **feel free to raise an issue with your suggestions or help with P
|
|
|
86
98
|
### Planned
|
|
87
99
|
|
|
88
100
|
- Cookie matchers
|
|
89
|
-
- Multiremote support (in progress)
|
package/jest.d.ts
CHANGED
|
@@ -19,16 +19,18 @@ declare namespace jest {
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* snapshot matcher
|
|
22
|
+
* A plain `Element[]` or `MultiRemoteElement[]` is only recognized from its elements, so an empty one is snapshotted synchronously
|
|
22
23
|
* @param label optional snapshot label
|
|
23
24
|
*/
|
|
24
|
-
toMatchSnapshot(label?: string): T extends WdioElementOrPromiseLike ? Promise<void> : R;
|
|
25
|
+
toMatchSnapshot(label?: string): T extends WdioElementOrPromiseLike | WebdriverIO.MultiRemoteElement | WebdriverIO.ElementArray ? Promise<void> : T extends WebdriverIO.Element[] | WebdriverIO.MultiRemoteElement[] ? Promise<void> | R : R;
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* inline snapshot matcher
|
|
29
|
+
* A plain `Element[]` or `MultiRemoteElement[]` is only recognized from its elements, so an empty one is snapshotted synchronously
|
|
28
30
|
* @param snapshot snapshot string (autogenerated if not specified)
|
|
29
31
|
* @param label optional snapshot label
|
|
30
32
|
*/
|
|
31
|
-
toMatchInlineSnapshot(snapshot?: string, label?: string): T extends WdioElementOrPromiseLike ? Promise<void> : R;
|
|
33
|
+
toMatchInlineSnapshot(snapshot?: string, label?: string): T extends WdioElementOrPromiseLike | WebdriverIO.MultiRemoteElement | WebdriverIO.ElementArray ? Promise<void> : T extends WebdriverIO.Element[] | WebdriverIO.MultiRemoteElement[] ? Promise<void> | R : R;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
interface Expect extends ExpectWebdriverIO.Expect {}
|
package/lib/api/index.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { DEFAULT_OPTIONS, defaultOptionsList } from './constants.js';
|
|
|
4
4
|
import createSoftExpect from './softExpect.js';
|
|
5
5
|
import { SoftAssertService } from './softAssert.js';
|
|
6
6
|
import { oneOf } from './matchers/asymmetrics/oneOf.js';
|
|
7
|
+
import { multiRemote } from './matchers/asymmetrics/multiRemote.js';
|
|
7
8
|
import { getGlobalSingleton } from './util/globalSingleton.js';
|
|
8
9
|
function createSharedExpectSetup() {
|
|
9
10
|
const wdioCustomMatchers = {};
|
|
@@ -28,6 +29,7 @@ function createSharedExpectSetup() {
|
|
|
28
29
|
const wdioExpect = expectLib;
|
|
29
30
|
wdioExpect.extend(filteredWdioMatchers);
|
|
30
31
|
wdioExpect.oneOf = oneOf;
|
|
32
|
+
wdioExpect.multiRemote = multiRemote;
|
|
31
33
|
Object.defineProperty(wdioExpect, 'soft', {
|
|
32
34
|
configurable: true,
|
|
33
35
|
value: (actual) => createSoftExpect(actual)
|
|
@@ -5,10 +5,20 @@ export const buildWdioAsymmetricMatchersWithOptions = (expectedValue, options) =
|
|
|
5
5
|
if (Array.isArray(expectedValue)) {
|
|
6
6
|
return expectedValue.map((value) => buildOneAsymmetricMatcherWithOptions(value, options));
|
|
7
7
|
}
|
|
8
|
+
if (isPlainObject(expectedValue)) {
|
|
9
|
+
return Object.fromEntries(Object.entries(expectedValue).map(([key, value]) => [key, buildWdioAsymmetricMatchersWithOptions(value, options)]));
|
|
10
|
+
}
|
|
8
11
|
return buildOneAsymmetricMatcherWithOptions(expectedValue, options);
|
|
9
12
|
}
|
|
10
13
|
return expectedValue;
|
|
11
14
|
};
|
|
15
|
+
const isPlainObject = (value) => {
|
|
16
|
+
if (typeof value !== 'object' || value === null || isAsymmetricMatcher(value)) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const prototype = Object.getPrototypeOf(value);
|
|
20
|
+
return prototype === Object.prototype || prototype === null;
|
|
21
|
+
};
|
|
12
22
|
const buildOneAsymmetricMatcherWithOptions = (expectedValue, options) => {
|
|
13
23
|
if (options) {
|
|
14
24
|
if (isAsymmetricMatcher(expectedValue) && 'withOptions' in expectedValue && typeof expectedValue.withOptions === 'function') {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { equals } from '../../jasmineUtils.js';
|
|
2
|
+
import { hasSameInstanceNames, isMultiRemoteValues, MULTI_REMOTE_MATCHER_SYMBOL } from '../../util/multiRemoteUtils.js';
|
|
3
|
+
import { buildWdioAsymmetricMatchersWithOptions, WdioAsymmetricMatchers } from './asymmetricsUtils.js';
|
|
4
|
+
export class MultiRemoteMatcher extends WdioAsymmetricMatchers {
|
|
5
|
+
[MULTI_REMOTE_MATCHER_SYMBOL] = true;
|
|
6
|
+
constructor(sample) {
|
|
7
|
+
if (!isMultiRemoteValues(sample)) {
|
|
8
|
+
throw new TypeError(`expect.multiRemote() expects an object with one expected value per multi-remote instance, e.g. { chrome: 'A', firefox: 'B' }. Received: ${String(sample)}`);
|
|
9
|
+
}
|
|
10
|
+
super(sample);
|
|
11
|
+
}
|
|
12
|
+
get values() {
|
|
13
|
+
return this.sample;
|
|
14
|
+
}
|
|
15
|
+
asymmetricMatch(actual) {
|
|
16
|
+
return isMultiRemoteValues(actual)
|
|
17
|
+
&& hasSameInstanceNames(actual, Object.keys(this.sample))
|
|
18
|
+
&& Object.entries(this.sample).every(([instance, expected]) => equals(actual[instance], expected));
|
|
19
|
+
}
|
|
20
|
+
withOptions(options) {
|
|
21
|
+
return new MultiRemoteMatcher(buildWdioAsymmetricMatchersWithOptions({ ...this.sample }, options));
|
|
22
|
+
}
|
|
23
|
+
toAsymmetricMatcher() {
|
|
24
|
+
const values = Object.entries(this.sample).map(([instance, expected]) => `${instance}: ${formatValue(expected)}`);
|
|
25
|
+
return `multiRemote<${values.join(', ')}>`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const formatValue = (value) => {
|
|
29
|
+
if (value && typeof value === 'object' && 'toAsymmetricMatcher' in value && typeof value.toAsymmetricMatcher === 'function') {
|
|
30
|
+
return value.toAsymmetricMatcher();
|
|
31
|
+
}
|
|
32
|
+
return value instanceof RegExp ? value.toString() : JSON.stringify(value) ?? String(value);
|
|
33
|
+
};
|
|
34
|
+
export function multiRemote(values) {
|
|
35
|
+
return new MultiRemoteMatcher(values);
|
|
36
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import logger from '@wdio/logger';
|
|
2
|
-
import { waitUntil, enhanceError,
|
|
2
|
+
import { waitUntil, enhanceError, compareTextOrOneOf } from '../../utils.js';
|
|
3
3
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
4
|
+
import { executeBrowserCommand } from '../../util/executeBrowserCommand.js';
|
|
5
|
+
import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
|
|
4
6
|
const log = logger('expect-webdriverio');
|
|
5
7
|
export async function toHaveClipboardText(browser, expectedValue, options = DEFAULT_OPTIONS) {
|
|
6
8
|
const { expectation = 'clipboard text', verb = 'have', isNot, matcherName = 'toHaveClipboardText' } = this;
|
|
@@ -9,14 +11,16 @@ export async function toHaveClipboardText(browser, expectedValue, options = DEFA
|
|
|
9
11
|
expectedValue,
|
|
10
12
|
options,
|
|
11
13
|
});
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
const expectedWithOptions = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
|
|
15
|
+
const { actual, success: pass, subject, expected } = await waitUntil(async () => {
|
|
16
|
+
return await executeBrowserCommand({
|
|
17
|
+
browser,
|
|
18
|
+
isNot,
|
|
19
|
+
expectedValue: expectedWithOptions,
|
|
20
|
+
compare: (browser, expectedValue) => compareClipboardText(browser, expectedValue, options),
|
|
21
|
+
});
|
|
18
22
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
19
|
-
const message = enhanceError(
|
|
23
|
+
const message = enhanceError(subject, expected, actual, this, verb, expectation, '', options);
|
|
20
24
|
const result = {
|
|
21
25
|
pass,
|
|
22
26
|
message: () => message
|
|
@@ -29,3 +33,9 @@ export async function toHaveClipboardText(browser, expectedValue, options = DEFA
|
|
|
29
33
|
});
|
|
30
34
|
return result;
|
|
31
35
|
}
|
|
36
|
+
const compareClipboardText = async (browser, expectedValue, options) => {
|
|
37
|
+
await browser.setPermissions({ name: 'clipboard-read' }, 'granted')
|
|
38
|
+
.catch((err) => log.warn(`Couldn't set clipboard permissions: ${err}`));
|
|
39
|
+
const actual = await browser.execute(() => window.navigator.clipboard.readText());
|
|
40
|
+
return compareTextOrOneOf(actual, expectedValue, options);
|
|
41
|
+
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { waitUntil, enhanceError,
|
|
1
|
+
import { waitUntil, enhanceError, compareTextOrOneOf } from '../../utils.js';
|
|
2
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
3
3
|
import { expect } from 'expect';
|
|
4
|
+
import { executeBrowserCommand } from '../../util/executeBrowserCommand.js';
|
|
5
|
+
import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
|
|
4
6
|
export async function toHaveLocalStorageItem(browser, key, expectedValue, options = DEFAULT_OPTIONS) {
|
|
5
7
|
const { expectation = 'localStorage item', verb = 'have', isNot, matcherName = 'toHaveLocalStorageItem' } = this;
|
|
6
8
|
await options.beforeAssertion?.({
|
|
@@ -19,17 +21,16 @@ export async function toHaveLocalStorageItem(browser, key, expectedValue, option
|
|
|
19
21
|
else {
|
|
20
22
|
expected = expectedValue;
|
|
21
23
|
}
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return { actual, success: compareResult.success, subject: browser };
|
|
24
|
+
const expectedWithOptions = buildWdioAsymmetricMatchersWithOptions(expected, options);
|
|
25
|
+
const { actual, success: pass, subject, expected: expectedValues } = await waitUntil(async () => {
|
|
26
|
+
return await executeBrowserCommand({
|
|
27
|
+
browser,
|
|
28
|
+
isNot,
|
|
29
|
+
expectedValue: expectedWithOptions,
|
|
30
|
+
compare: (browser, expectedValue) => compareStorageItem(browser, key, expectedValue, options),
|
|
31
|
+
});
|
|
31
32
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
32
|
-
const message = enhanceError(
|
|
33
|
+
const message = enhanceError(subject, expectedValues, actual, this, verb, expectation, key, options);
|
|
33
34
|
const result = {
|
|
34
35
|
pass,
|
|
35
36
|
message: () => message
|
|
@@ -42,3 +43,13 @@ export async function toHaveLocalStorageItem(browser, key, expectedValue, option
|
|
|
42
43
|
});
|
|
43
44
|
return result;
|
|
44
45
|
}
|
|
46
|
+
const compareStorageItem = async (browser, key, expected, options) => {
|
|
47
|
+
const actual = await browser.execute((storageKey) => {
|
|
48
|
+
return localStorage.getItem(storageKey);
|
|
49
|
+
}, key);
|
|
50
|
+
if (actual === null) {
|
|
51
|
+
return { actual, success: false };
|
|
52
|
+
}
|
|
53
|
+
const compareResult = compareTextOrOneOf(actual, expected, options);
|
|
54
|
+
return { actual, success: compareResult.success };
|
|
55
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { waitUntil, enhanceError,
|
|
1
|
+
import { waitUntil, enhanceError, compareTextOrOneOf } from '../../utils.js';
|
|
2
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
3
|
+
import { executeBrowserCommand } from '../../util/executeBrowserCommand.js';
|
|
4
|
+
import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
|
|
3
5
|
export async function toHaveTitle(browser, expectedValue, options = DEFAULT_OPTIONS) {
|
|
4
6
|
const { expectation = 'title', verb = 'have', isNot, matcherName = 'toHaveTitle' } = this;
|
|
5
7
|
await options.beforeAssertion?.({
|
|
@@ -7,12 +9,16 @@ export async function toHaveTitle(browser, expectedValue, options = DEFAULT_OPTI
|
|
|
7
9
|
expectedValue,
|
|
8
10
|
options,
|
|
9
11
|
});
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const expectedWithOptions = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
|
|
13
|
+
const { actual, success, subject, expected } = await waitUntil(async () => {
|
|
14
|
+
return await executeBrowserCommand({
|
|
15
|
+
browser,
|
|
16
|
+
isNot,
|
|
17
|
+
expectedValue: expectedWithOptions,
|
|
18
|
+
compare: (browser, expectedValue) => compareTitle(browser, expectedValue, options),
|
|
19
|
+
});
|
|
14
20
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
15
|
-
const message = enhanceError(
|
|
21
|
+
const message = enhanceError(subject, expected, actual, { isNot, browserTargetType: 'window' }, verb, expectation, '', options);
|
|
16
22
|
const result = {
|
|
17
23
|
pass: success,
|
|
18
24
|
message: () => message
|
|
@@ -25,3 +31,7 @@ export async function toHaveTitle(browser, expectedValue, options = DEFAULT_OPTI
|
|
|
25
31
|
});
|
|
26
32
|
return result;
|
|
27
33
|
}
|
|
34
|
+
const compareTitle = async (browser, expectedValue, options) => {
|
|
35
|
+
const actual = await browser.getTitle();
|
|
36
|
+
return compareTextOrOneOf(actual, expectedValue, options);
|
|
37
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { waitUntil, enhanceError,
|
|
1
|
+
import { waitUntil, enhanceError, compareTextOrOneOf } from '../../utils.js';
|
|
2
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
3
|
+
import { executeBrowserCommand } from '../../util/executeBrowserCommand.js';
|
|
4
|
+
import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
|
|
3
5
|
export async function toHaveUrl(browser, expectedValue, options = DEFAULT_OPTIONS) {
|
|
4
6
|
const { expectation = 'url', verb = 'have', isNot, matcherName = 'toHaveUrl' } = this;
|
|
5
7
|
await options.beforeAssertion?.({
|
|
@@ -7,12 +9,16 @@ export async function toHaveUrl(browser, expectedValue, options = DEFAULT_OPTION
|
|
|
7
9
|
expectedValue,
|
|
8
10
|
options,
|
|
9
11
|
});
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const expectedWithOptions = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
|
|
13
|
+
const { success: pass, actual, subject, expected } = await waitUntil(async () => {
|
|
14
|
+
return await executeBrowserCommand({
|
|
15
|
+
browser,
|
|
16
|
+
isNot,
|
|
17
|
+
expectedValue: expectedWithOptions,
|
|
18
|
+
compare: (browser, expectedValue) => compareUrl(browser, expectedValue, options),
|
|
19
|
+
});
|
|
14
20
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
15
|
-
const message = enhanceError(
|
|
21
|
+
const message = enhanceError(subject, expected, actual, { isNot, browserTargetType: 'window' }, verb, expectation, '', options);
|
|
16
22
|
const result = {
|
|
17
23
|
pass,
|
|
18
24
|
message: () => message
|
|
@@ -25,3 +31,7 @@ export async function toHaveUrl(browser, expectedValue, options = DEFAULT_OPTION
|
|
|
25
31
|
});
|
|
26
32
|
return result;
|
|
27
33
|
}
|
|
34
|
+
const compareUrl = async (browser, expectedValue, options) => {
|
|
35
|
+
const actual = await browser.getUrl();
|
|
36
|
+
return compareTextOrOneOf(actual, expectedValue, options);
|
|
37
|
+
};
|
|
@@ -20,7 +20,7 @@ async function conditionAttributeValueMatchWithExpected(el, attribute, expectedV
|
|
|
20
20
|
export async function toHaveAttributeAndValue(received, attribute, expectedValue, options = DEFAULT_OPTIONS) {
|
|
21
21
|
const { expectation = 'attribute', verb = 'have', isNot } = this;
|
|
22
22
|
expectedValue = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
|
|
23
|
-
const { success: pass, actual: attr, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
23
|
+
const { success: pass, actual: attr, subject: el, context: { isSome } = {}, expected: expectedValues } = await waitUntil(async (iteration) => {
|
|
24
24
|
return await executeCommandWithStrategy({
|
|
25
25
|
unresolvedElements: received,
|
|
26
26
|
supportsArrayContaining: true,
|
|
@@ -32,7 +32,7 @@ export async function toHaveAttributeAndValue(received, attribute, expectedValue
|
|
|
32
32
|
strategy: 'NewStrictMultipleElements',
|
|
33
33
|
});
|
|
34
34
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
35
|
-
const expected = wrapExpectedWithArray(el, attr, expectedValue);
|
|
35
|
+
const expected = expectedValues ?? wrapExpectedWithArray(el, attr, expectedValue);
|
|
36
36
|
const message = enhanceError(el, expected, attr, { isNot, isSome }, verb, expectation, attribute, options);
|
|
37
37
|
return {
|
|
38
38
|
pass,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
2
2
|
import { executeCommandWithStrategy } from '../../util/executeCommand.js';
|
|
3
3
|
import { isEmptyOrLegacyNumberOptions, validateNumberArrayAndExtractOptions } from '../../util/numberOptionsUtil.js';
|
|
4
|
+
import { isMultiRemoteMatcher } from '../../util/multiRemoteUtils.js';
|
|
4
5
|
import { enhanceError, waitUntil, wrapExpectedWithArray } from '../../utils.js';
|
|
5
6
|
async function condition(el, expectedValue) {
|
|
6
7
|
const children = await el.$$('./*').getElements();
|
|
@@ -15,7 +16,7 @@ async function condition(el, expectedValue) {
|
|
|
15
16
|
export async function toHaveChildren(received, expectedValueOrOptions, options = DEFAULT_OPTIONS) {
|
|
16
17
|
const { expectation = 'children', verb = 'have', isNot, matcherName = 'toHaveChildren' } = this;
|
|
17
18
|
const paramsCount = arguments.length;
|
|
18
|
-
if (paramsCount > 1 && (expectedValueOrOptions === undefined || isEmptyOrLegacyNumberOptions(expectedValueOrOptions))) {
|
|
19
|
+
if (paramsCount > 1 && (expectedValueOrOptions === undefined || (isEmptyOrLegacyNumberOptions(expectedValueOrOptions) && !isMultiRemoteMatcher(expectedValueOrOptions)))) {
|
|
19
20
|
console.warn('Passing undefined or NumberOptions as the second argument to toHaveChildren is deprecated. Use a NumberMatcher instead. For example, `expect(el).toHaveChildren({ gte: 1 }, options)`');
|
|
20
21
|
}
|
|
21
22
|
await options.beforeAssertion?.({
|
|
@@ -24,7 +25,7 @@ export async function toHaveChildren(received, expectedValueOrOptions, options =
|
|
|
24
25
|
options,
|
|
25
26
|
});
|
|
26
27
|
const { numberMatcher: expectedNumber, commandOptions } = validateNumberArrayAndExtractOptions(expectedValueOrOptions, options, { supportDefaultAsGteThen1: true });
|
|
27
|
-
const { success: pass, actual: children, subject, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
28
|
+
const { success: pass, actual: children, subject, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
|
|
28
29
|
return await executeCommandWithStrategy({
|
|
29
30
|
unresolvedElements: received,
|
|
30
31
|
expectedValues: expectedNumber,
|
|
@@ -33,7 +34,7 @@ export async function toHaveChildren(received, expectedValueOrOptions, options =
|
|
|
33
34
|
strategy: 'NewStrictMultipleElements',
|
|
34
35
|
});
|
|
35
36
|
}, isNot, { wait: commandOptions.wait, interval: commandOptions.interval });
|
|
36
|
-
const expectedArray = wrapExpectedWithArray(subject, children, expectedNumber);
|
|
37
|
+
const expectedArray = expected ?? wrapExpectedWithArray(subject, children, expectedNumber);
|
|
37
38
|
const message = enhanceError(subject, expectedArray, children, { isNot, isSome }, verb, expectation, '', commandOptions);
|
|
38
39
|
const result = {
|
|
39
40
|
pass,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
2
2
|
import { executeCommandWithStrategy } from '../../util/executeCommand.js';
|
|
3
|
-
import {
|
|
3
|
+
import { compareTextOrOneOf, enhanceError, waitUntil, wrapExpectedWithArray } from '../../utils.js';
|
|
4
4
|
import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
|
|
5
5
|
async function singleElementCompare(element, label, options) {
|
|
6
6
|
const actualLabel = await element.getComputedLabel();
|
|
7
|
-
return
|
|
7
|
+
return compareTextOrOneOf(actualLabel, label, options);
|
|
8
8
|
}
|
|
9
9
|
export async function toHaveComputedLabel(received, expectedValue, options = DEFAULT_OPTIONS) {
|
|
10
10
|
const { expectation = 'computed label', verb = 'have', isNot, matcherName = 'toHaveComputedLabel' } = this;
|
|
@@ -14,7 +14,7 @@ export async function toHaveComputedLabel(received, expectedValue, options = DEF
|
|
|
14
14
|
options,
|
|
15
15
|
});
|
|
16
16
|
expectedValue = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
|
|
17
|
-
const { success: pass, actual: actualLabel, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
17
|
+
const { success: pass, actual: actualLabel, subject: el, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
|
|
18
18
|
return await executeCommandWithStrategy({
|
|
19
19
|
unresolvedElements: received,
|
|
20
20
|
supportsArrayContaining: true,
|
|
@@ -25,7 +25,7 @@ export async function toHaveComputedLabel(received, expectedValue, options = DEF
|
|
|
25
25
|
strictConfiguration: { allowArrayWithSingleElement: true }
|
|
26
26
|
});
|
|
27
27
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
28
|
-
const message = enhanceError(el, wrapExpectedWithArray(el, actualLabel, expectedValue), actualLabel, { isNot, isSome }, verb, expectation, '', options);
|
|
28
|
+
const message = enhanceError(el, expected ?? wrapExpectedWithArray(el, actualLabel, expectedValue), actualLabel, { isNot, isSome }, verb, expectation, '', options);
|
|
29
29
|
const result = {
|
|
30
30
|
pass,
|
|
31
31
|
message: () => message
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
2
2
|
import { executeCommandWithStrategy } from '../../util/executeCommand.js';
|
|
3
|
-
import {
|
|
3
|
+
import { compareTextOrOneOf, enhanceError, waitUntil, wrapExpectedWithArray } from '../../utils.js';
|
|
4
4
|
import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
|
|
5
5
|
async function singleElementCompare(element, role, options) {
|
|
6
6
|
const actualRole = await element.getComputedRole();
|
|
7
|
-
return
|
|
7
|
+
return compareTextOrOneOf(actualRole, role, options);
|
|
8
8
|
}
|
|
9
9
|
export async function toHaveComputedRole(received, expectedValue, options = DEFAULT_OPTIONS) {
|
|
10
10
|
const { expectation = 'computed role', verb = 'have', isNot, matcherName = 'toHaveComputedRole' } = this;
|
|
@@ -14,7 +14,7 @@ export async function toHaveComputedRole(received, expectedValue, options = DEFA
|
|
|
14
14
|
options,
|
|
15
15
|
});
|
|
16
16
|
expectedValue = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
|
|
17
|
-
const { success: pass, actual: actualRole, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
17
|
+
const { success: pass, actual: actualRole, subject: el, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
|
|
18
18
|
return await executeCommandWithStrategy({
|
|
19
19
|
unresolvedElements: received,
|
|
20
20
|
supportsArrayContaining: true,
|
|
@@ -25,7 +25,7 @@ export async function toHaveComputedRole(received, expectedValue, options = DEFA
|
|
|
25
25
|
strictConfiguration: { allowArrayWithSingleElement: true }
|
|
26
26
|
});
|
|
27
27
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
28
|
-
const message = enhanceError(el, wrapExpectedWithArray(el, actualRole, expectedValue), actualRole, { isNot, isSome }, verb, expectation, '', options);
|
|
28
|
+
const message = enhanceError(el, expected ?? wrapExpectedWithArray(el, actualRole, expectedValue), actualRole, { isNot, isSome }, verb, expectation, '', options);
|
|
29
29
|
const result = {
|
|
30
30
|
pass,
|
|
31
31
|
message: () => message
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
2
2
|
import { executeCommandWithStrategy } from '../../util/executeCommand.js';
|
|
3
|
-
import { compareText,
|
|
3
|
+
import { compareText, compareTextOrOneOf, enhanceError, isAsymmetricMatcher, waitUntil, wrapExpectedWithArray } from '../../utils.js';
|
|
4
4
|
async function singleElementCompare(el, attribute, value, options) {
|
|
5
5
|
const actualClass = await el.getAttribute(attribute);
|
|
6
6
|
if (value === undefined) {
|
|
@@ -14,7 +14,7 @@ async function singleElementCompare(el, attribute, value, options) {
|
|
|
14
14
|
}
|
|
15
15
|
const classes = actualClass.split(' ');
|
|
16
16
|
const isValueInClasses = classes.some((clazz) => {
|
|
17
|
-
return
|
|
17
|
+
return compareTextOrOneOf(clazz, value, options).success;
|
|
18
18
|
});
|
|
19
19
|
return {
|
|
20
20
|
success: isValueInClasses,
|
|
@@ -32,7 +32,7 @@ export async function toHaveElementClass(received, expectedValue, options = DEFA
|
|
|
32
32
|
options,
|
|
33
33
|
});
|
|
34
34
|
const attribute = 'class';
|
|
35
|
-
const { success: pass, actual: attr, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
35
|
+
const { success: pass, actual: attr, subject: el, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
|
|
36
36
|
return await executeCommandWithStrategy({
|
|
37
37
|
unresolvedElements: received,
|
|
38
38
|
supportsArrayContaining: true,
|
|
@@ -43,7 +43,7 @@ export async function toHaveElementClass(received, expectedValue, options = DEFA
|
|
|
43
43
|
strictConfiguration: { allowArrayWithSingleElement: true }
|
|
44
44
|
});
|
|
45
45
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
46
|
-
const message = enhanceError(el, wrapExpectedWithArray(el, attr, expectedValue), attr, { isNot, isSome }, verb, expectation, '', options);
|
|
46
|
+
const message = enhanceError(el, expected ?? wrapExpectedWithArray(el, attr, expectedValue), attr, { isNot, isSome }, verb, expectation, '', options);
|
|
47
47
|
const result = {
|
|
48
48
|
pass,
|
|
49
49
|
message: () => message
|
|
@@ -20,7 +20,7 @@ async function condition(el, property, expectedValue, options = DEFAULT_OPTIONS)
|
|
|
20
20
|
return compareText(propertyValue.toString(), expectedValue, options);
|
|
21
21
|
}
|
|
22
22
|
export async function toHaveElementProperty(received, property, value, options = DEFAULT_OPTIONS) {
|
|
23
|
-
const { expectation = 'property', verb = 'have', isNot, matcherName = 'toHaveElementProperty' } = this;
|
|
23
|
+
const { expectation = 'property', verb = 'have', isNot, matcherName = 'toHaveElementProperty', allowObjectExpectedValue = true } = this;
|
|
24
24
|
const paramsCount = arguments.length;
|
|
25
25
|
if (value === undefined || value === null) {
|
|
26
26
|
if (paramsCount > 2) {
|
|
@@ -34,7 +34,7 @@ export async function toHaveElementProperty(received, property, value, options =
|
|
|
34
34
|
options,
|
|
35
35
|
});
|
|
36
36
|
value = buildWdioAsymmetricMatchersWithOptions(value, options);
|
|
37
|
-
const { success: pass, actual: actualProppertyValue, subject: elements, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
37
|
+
const { success: pass, actual: actualProppertyValue, subject: elements, context: { isSome } = {}, expected: expectedValues } = await waitUntil(async (iteration) => {
|
|
38
38
|
return await executeCommandWithStrategy({
|
|
39
39
|
unresolvedElements: received,
|
|
40
40
|
supportsArrayContaining: true,
|
|
@@ -44,10 +44,10 @@ export async function toHaveElementProperty(received, property, value, options =
|
|
|
44
44
|
},
|
|
45
45
|
context: { isNot, iteration },
|
|
46
46
|
strategy: 'NewStrictMultipleElements',
|
|
47
|
-
strictConfiguration: { allowArrayWithSingleElement: false }
|
|
47
|
+
strictConfiguration: { allowArrayWithSingleElement: false, allowObjectExpectedValue }
|
|
48
48
|
});
|
|
49
49
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
50
|
-
const expected = wrapExpectedWithArray(elements, actualProppertyValue, value);
|
|
50
|
+
const expected = expectedValues ?? wrapExpectedWithArray(elements, actualProppertyValue, value);
|
|
51
51
|
const message = enhanceError(elements, expected, actualProppertyValue, { isNot, isSome }, verb, expectation, property, options);
|
|
52
52
|
const result = {
|
|
53
53
|
pass,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
2
|
-
import {
|
|
2
|
+
import { compareTextOrOneOf, enhanceError, waitUntil, wrapExpectedWithArray } from '../../utils.js';
|
|
3
3
|
import { executeCommandWithStrategy } from '../../util/executeCommand.js';
|
|
4
4
|
import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
|
|
5
5
|
async function singleElementCompare(el, html, options) {
|
|
6
6
|
const actualHTML = await el.getHTML(options);
|
|
7
|
-
return
|
|
7
|
+
return compareTextOrOneOf(actualHTML, html, options);
|
|
8
8
|
}
|
|
9
9
|
export async function toHaveHTML(received, expectedValue, options = DEFAULT_OPTIONS) {
|
|
10
10
|
const { expectation = 'HTML', verb = 'have', isNot, matcherName = 'toHaveHTML' } = this;
|
|
@@ -14,7 +14,7 @@ export async function toHaveHTML(received, expectedValue, options = DEFAULT_OPTI
|
|
|
14
14
|
expectedValue,
|
|
15
15
|
options,
|
|
16
16
|
});
|
|
17
|
-
const { success: pass, actual: actualHTML, subject: elements, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
17
|
+
const { success: pass, actual: actualHTML, subject: elements, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
|
|
18
18
|
const result = await executeCommandWithStrategy({
|
|
19
19
|
unresolvedElements: received,
|
|
20
20
|
supportsArrayContaining: true,
|
|
@@ -26,7 +26,7 @@ export async function toHaveHTML(received, expectedValue, options = DEFAULT_OPTI
|
|
|
26
26
|
});
|
|
27
27
|
return result;
|
|
28
28
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
29
|
-
const expectedValues = wrapExpectedWithArray(elements, actualHTML, expectedValue);
|
|
29
|
+
const expectedValues = expected ?? wrapExpectedWithArray(elements, actualHTML, expectedValue);
|
|
30
30
|
const message = enhanceError(elements, expectedValues, actualHTML, { isNot, isSome }, verb, expectation, '', options);
|
|
31
31
|
const result = {
|
|
32
32
|
pass,
|
|
@@ -18,7 +18,7 @@ export async function toHaveHeight(received, expectedValue, options = DEFAULT_OP
|
|
|
18
18
|
options,
|
|
19
19
|
});
|
|
20
20
|
const { numberMatcher: expectedNumber, commandOptions } = validateNumberArrayAndExtractOptions(expectedValue, options);
|
|
21
|
-
const { success: pass, actual: actualHeight, subject: elements, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
21
|
+
const { success: pass, actual: actualHeight, subject: elements, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
|
|
22
22
|
return await executeCommandWithStrategy({
|
|
23
23
|
unresolvedElements: received,
|
|
24
24
|
expectedValues: expectedNumber,
|
|
@@ -28,7 +28,7 @@ export async function toHaveHeight(received, expectedValue, options = DEFAULT_OP
|
|
|
28
28
|
strictConfiguration: { allowArrayWithSingleElement: false }
|
|
29
29
|
});
|
|
30
30
|
}, isNot, { wait: commandOptions.wait, interval: commandOptions.interval });
|
|
31
|
-
const expectedValues = wrapExpectedWithArray(elements, actualHeight, expectedNumber);
|
|
31
|
+
const expectedValues = expected ?? wrapExpectedWithArray(elements, actualHeight, expectedNumber);
|
|
32
32
|
const message = enhanceError(elements, expectedValues, actualHeight, { isNot, isSome }, verb, expectation, '', commandOptions);
|
|
33
33
|
const result = {
|
|
34
34
|
pass,
|
|
@@ -12,17 +12,17 @@ export async function toHaveSize(received, expectedValue, options = DEFAULT_OPTI
|
|
|
12
12
|
expectedValue,
|
|
13
13
|
options,
|
|
14
14
|
});
|
|
15
|
-
const { success: pass, actual: actualSize, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
15
|
+
const { success: pass, actual: actualSize, subject: el, context: { isSome } = {}, expected } = await waitUntil(async (iteration) => {
|
|
16
16
|
return await executeCommandWithStrategy({
|
|
17
17
|
unresolvedElements: received,
|
|
18
18
|
expectedValues: expectedValue,
|
|
19
19
|
singleElementCompare: (element, expectedSize) => condition(element, expectedSize),
|
|
20
20
|
context: { isNot, iteration },
|
|
21
21
|
strategy: 'NewStrictMultipleElements',
|
|
22
|
-
strictConfiguration: { allowArrayWithSingleElement: false }
|
|
22
|
+
strictConfiguration: { allowArrayWithSingleElement: false, allowObjectExpectedValue: true }
|
|
23
23
|
});
|
|
24
24
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
25
|
-
const message = enhanceError(el, wrapExpectedWithArray(el, actualSize, expectedValue), actualSize, { isNot, isSome }, verb, expectation, '', options);
|
|
25
|
+
const message = enhanceError(el, expected ?? wrapExpectedWithArray(el, actualSize, expectedValue), actualSize, { isNot, isSome }, verb, expectation, '', options);
|
|
26
26
|
const result = {
|
|
27
27
|
pass,
|
|
28
28
|
message: () => message
|
|
@@ -14,17 +14,17 @@ export async function toHaveStyle(received, expectedValue, options = DEFAULT_OPT
|
|
|
14
14
|
expectedValue,
|
|
15
15
|
options,
|
|
16
16
|
});
|
|
17
|
-
const { success: pass, actual: actualStyle, subject: el, context: { isSome } = {} } = await waitUntil(async (iteration) => {
|
|
17
|
+
const { success: pass, actual: actualStyle, subject: el, context: { isSome } = {}, expected: expectedValues } = await waitUntil(async (iteration) => {
|
|
18
18
|
return await executeCommandWithStrategy({
|
|
19
19
|
unresolvedElements: received,
|
|
20
20
|
expectedValues: expectedValue,
|
|
21
21
|
singleElementCompare: (element, expectedValues) => condition(element, expectedValues, options),
|
|
22
22
|
context: { isNot, iteration },
|
|
23
23
|
strategy: 'NewStrictMultipleElements',
|
|
24
|
-
strictConfiguration: { allowArrayWithSingleElement: false }
|
|
24
|
+
strictConfiguration: { allowArrayWithSingleElement: false, allowObjectExpectedValue: true }
|
|
25
25
|
});
|
|
26
26
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
27
|
-
const expected = wrapExpectedWithArray(el, actualStyle, expectedValue);
|
|
27
|
+
const expected = expectedValues ?? wrapExpectedWithArray(el, actualStyle, expectedValue);
|
|
28
28
|
const message = enhanceError(el, expected, actualStyle, { isNot, isSome }, verb, expectation, '', options);
|
|
29
29
|
const result = {
|
|
30
30
|
pass,
|