expect-webdriverio 6.1.0 → 8.0.0-alpha.0
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 +22 -7
- package/jasmine-wdio-expect-async.d.ts +1 -1
- package/jest.d.ts +4 -2
- package/lib/api/index.js +2 -0
- package/lib/constants.js +0 -6
- package/lib/index.js +3 -13
- package/lib/jasmineUtils.js +2 -2
- package/lib/matchers/asymmetrics/asymmetricsUtils.js +10 -0
- package/lib/matchers/asymmetrics/multiRemote.js +36 -0
- package/lib/matchers/asymmetrics/oneOf.js +2 -2
- package/lib/matchers/browser/toHaveClipboardText.js +24 -10
- package/lib/matchers/browser/toHaveLocalStorageItem.js +23 -22
- package/lib/matchers/browser/toHaveTitle.js +16 -6
- package/lib/matchers/browser/toHaveUrl.js +16 -6
- package/lib/matchers/element/toBeClickable.js +3 -2
- package/lib/matchers/element/toBeDisabled.js +3 -2
- package/lib/matchers/element/toBeDisplayed.js +3 -2
- package/lib/matchers/element/toBeDisplayedInViewport.js +3 -2
- package/lib/matchers/element/toBeEnabled.js +3 -2
- package/lib/matchers/element/toBeExisting.js +5 -2
- package/lib/matchers/element/toBeFocused.js +3 -2
- package/lib/matchers/element/toBeSelected.js +4 -4
- package/lib/matchers/element/toHaveAttribute.js +4 -16
- package/lib/matchers/element/toHaveChildren.js +9 -14
- package/lib/matchers/element/toHaveComputedLabel.js +4 -6
- package/lib/matchers/element/toHaveComputedRole.js +4 -6
- package/lib/matchers/element/toHaveElementClass.js +5 -10
- package/lib/matchers/element/toHaveElementProperty.js +4 -9
- package/lib/matchers/element/toHaveHTML.js +6 -8
- package/lib/matchers/element/toHaveHeight.js +6 -7
- package/lib/matchers/element/toHaveHref.js +6 -3
- package/lib/matchers/element/toHaveId.js +3 -2
- package/lib/matchers/element/toHaveSize.js +3 -4
- package/lib/matchers/element/toHaveStyle.js +3 -4
- package/lib/matchers/element/toHaveText.js +5 -8
- package/lib/matchers/element/toHaveValue.js +1 -1
- package/lib/matchers/element/toHaveWidth.js +6 -7
- package/lib/matchers/elements/toBeElementsArrayOfSize.js +71 -6
- package/lib/matchers/mock/toBeRequested.js +1 -1
- package/lib/matchers/mock/toBeRequestedTimes.js +30 -7
- package/lib/matchers/mock/toBeRequestedWith.js +40 -15
- package/lib/matchers/snapshot.js +38 -7
- package/lib/matchers.js +32 -32
- package/lib/snapshot.js +1 -8
- package/lib/tsconfig.build.tsbuildinfo +1 -1
- package/lib/util/elementsUtil.js +35 -6
- package/lib/util/executeBrowserCommand.js +37 -0
- package/lib/util/executeCommand.js +101 -52
- package/lib/util/formatMessage.js +81 -7
- package/lib/util/multiRemoteUtils.js +93 -0
- package/lib/util/numberOptionsUtil.js +19 -24
- package/lib/util/refetchElements.js +24 -2
- package/lib/utils.js +16 -111
- package/package.json +37 -40
- package/types/expect-global.d.ts +1 -2
- package/types/expect-webdriverio.d.ts +386 -232
package/README.md
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# expect-webdriverio [](https://github.com/webdriverio/expect-webdriverio/actions/workflows/test.yml)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> [!IMPORTANT]
|
|
4
|
+
> This branch is v8, in development for WebdriverIO v10. Its docs are not released yet.
|
|
5
|
+
> For the latest release (v7, WebdriverIO v9), see the [v7 README](https://github.com/webdriverio/expect-webdriverio/blob/v7/README.md).
|
|
6
|
+
|
|
7
|
+
###### [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
8
|
|
|
5
9
|
> [WebdriverIO](https://webdriver.io/) assertion library inspired by [expect](https://www.npmjs.com/package/expect)
|
|
6
10
|
|
|
7
11
|
## Key Features
|
|
8
12
|
|
|
9
|
-
- [Waits](#default-options) for expectations to succeed
|
|
10
|
-
- Supports single element `$()` & multiple elements `$$()`
|
|
13
|
+
- [Waits](docs/API.md#default-options) for expectations to succeed
|
|
14
|
+
- Supports single element `$()` & [multiple elements](docs/MultipleElements.md) `$$()`
|
|
15
|
+
- Supports [multi-remote](docs/MultiRemote.md) browsers & elements, with one expected value per browser instance
|
|
11
16
|
- Detailed [error messages](#error-messages)
|
|
12
17
|
- Works in Mocha, Cucumber, Jest, and Jasmine
|
|
13
18
|
- Built-in [types](docs/Types.md) for TypeScript and JS autocompletion
|
|
@@ -21,7 +26,7 @@
|
|
|
21
26
|
- **Cucumber**: `npm install @wdio/cucumber-framework`
|
|
22
27
|
- **Jest**: No adapter needed — see [Jest Framework section](docs/Framework.md#jest)
|
|
23
28
|
|
|
24
|
-
**Note:** [WebdriverIO](https://github.com/webdriverio/webdriverio) `v9.
|
|
29
|
+
**Note:** [WebdriverIO](https://github.com/webdriverio/webdriverio) `v9.31.5` or higher is required!
|
|
25
30
|
|
|
26
31
|
## Usage
|
|
27
32
|
|
|
@@ -30,12 +35,23 @@
|
|
|
30
35
|
If you run your tests through the [WDIO testrunner](https://webdriver.io/docs/clioptions), no additional setup is needed. WebdriverIO initializes `expect-webdriverio` and makes `expect` available in the global scope so you can use it directly in your tests:
|
|
31
36
|
|
|
32
37
|
```js
|
|
33
|
-
await expect($('
|
|
38
|
+
await expect($('#submit')).toBeDisplayed()
|
|
34
39
|
await expect($$('buttons')).toBeDisplayed()
|
|
35
40
|
```
|
|
36
41
|
|
|
37
42
|
See more [Examples](docs/Examples.md).
|
|
38
43
|
|
|
44
|
+
#### Multi-remote
|
|
45
|
+
|
|
46
|
+
With [multi-remote](https://webdriver.io/docs/multiremote), assertions check every browser instance, with a single expected value or one per instance:
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
await expect(multiRemoteBrowser).toHaveTitle('WebdriverIO')
|
|
50
|
+
await expect(multiRemoteBrowser.$('h1')).toHaveText(expect.multiRemote({ chrome: 'Welcome', firefox: 'Bienvenue' }))
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Some WebdriverIO environment variables are recommended, see [Multi-remote Support](docs/MultiRemote.md#requirements--configuration).
|
|
54
|
+
|
|
39
55
|
#### Local & Browser Runners
|
|
40
56
|
- **Local Runner**: Fully compatible. You can leverage your test framework adapter as mentioned above.
|
|
41
57
|
- **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).
|
|
@@ -57,7 +73,7 @@ import { expect } from 'expect-webdriverio'
|
|
|
57
73
|
|
|
58
74
|
await browser.url('https://webdriver.io')
|
|
59
75
|
|
|
60
|
-
const $button = await browser.$('
|
|
76
|
+
const $button = await browser.$('.DocSearch-Button')
|
|
61
77
|
await expect($button).toBeDisplayed()
|
|
62
78
|
|
|
63
79
|
await browser.deleteSession()
|
|
@@ -86,4 +102,3 @@ First of all, **feel free to raise an issue with your suggestions or help with P
|
|
|
86
102
|
### Planned
|
|
87
103
|
|
|
88
104
|
- Cookie matchers
|
|
89
|
-
- Multiremote support (in progress)
|
|
@@ -80,7 +80,7 @@ declare namespace ExpectWebdriverIO {
|
|
|
80
80
|
* Under `@wdio/jasmine-framework`, the global `expect` is overridden to use Jasmine's `expectAsync`.
|
|
81
81
|
* It contains custom WebdriverIO matchers as well as Jasmine's built-in async & sync matchers but not the basic Jest's expect library matchers.
|
|
82
82
|
*/
|
|
83
|
-
// @ts-expect-error:
|
|
83
|
+
// @ts-expect-error: the Jasmine `expect` is always loaded with this entry, so the two declarations conflict
|
|
84
84
|
declare const expect: ExpectWebdriverIO.JasmineExpect
|
|
85
85
|
declare namespace NodeJS {
|
|
86
86
|
interface Global {
|
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/constants.js
CHANGED
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import { getGlobalSingleton } from './util/globalSingleton.js';
|
|
2
2
|
function createSharedConstants() {
|
|
3
|
-
const DEFAULT_FEATURE_FLAGS = {
|
|
4
|
-
useToHaveTextStrictMultiElementsCompareStrategy: false
|
|
5
|
-
};
|
|
6
3
|
const DEFAULT_OPTIONS = {
|
|
7
4
|
wait: 2000,
|
|
8
5
|
interval: 100,
|
|
9
6
|
beforeAssertion: async () => { },
|
|
10
7
|
afterAssertion: async () => { },
|
|
11
|
-
featureFlags: DEFAULT_FEATURE_FLAGS
|
|
12
8
|
};
|
|
13
9
|
const DEFAULT_OPTIONS_TO_BE_DISPLAYED = {
|
|
14
10
|
...DEFAULT_OPTIONS,
|
|
@@ -18,14 +14,12 @@ function createSharedConstants() {
|
|
|
18
14
|
visibilityProperty: true
|
|
19
15
|
};
|
|
20
16
|
return {
|
|
21
|
-
DEFAULT_FEATURE_FLAGS,
|
|
22
17
|
DEFAULT_OPTIONS,
|
|
23
18
|
DEFAULT_OPTIONS_TO_BE_DISPLAYED,
|
|
24
19
|
defaultOptionsList: [DEFAULT_OPTIONS, DEFAULT_OPTIONS_TO_BE_DISPLAYED]
|
|
25
20
|
};
|
|
26
21
|
}
|
|
27
22
|
const shared = getGlobalSingleton('constants', createSharedConstants);
|
|
28
|
-
export const DEFAULT_FEATURE_FLAGS = shared.DEFAULT_FEATURE_FLAGS;
|
|
29
23
|
export const DEFAULT_OPTIONS = shared.DEFAULT_OPTIONS;
|
|
30
24
|
export const DEFAULT_OPTIONS_TO_BE_DISPLAYED = shared.DEFAULT_OPTIONS_TO_BE_DISPLAYED;
|
|
31
25
|
export const defaultOptionsList = shared.defaultOptionsList;
|
package/lib/index.js
CHANGED
|
@@ -4,10 +4,10 @@ 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 = {};
|
|
10
|
-
const matchers = new Map();
|
|
11
11
|
const extend = expectLib.extend;
|
|
12
12
|
expectLib.extend = (extendedMatchers) => {
|
|
13
13
|
if (!extendedMatchers || typeof extendedMatchers !== 'object') {
|
|
@@ -15,7 +15,6 @@ function createSharedExpectSetup() {
|
|
|
15
15
|
}
|
|
16
16
|
Object.entries(extendedMatchers).forEach(([name, matcher]) => {
|
|
17
17
|
wdioCustomMatchers[name] = matcher;
|
|
18
|
-
matchers.set(name, matcher);
|
|
19
18
|
});
|
|
20
19
|
return extend(extendedMatchers);
|
|
21
20
|
};
|
|
@@ -28,6 +27,7 @@ function createSharedExpectSetup() {
|
|
|
28
27
|
const wdioExpect = expectLib;
|
|
29
28
|
wdioExpect.extend(filteredWdioMatchers);
|
|
30
29
|
wdioExpect.oneOf = oneOf;
|
|
30
|
+
wdioExpect.multiRemote = multiRemote;
|
|
31
31
|
Object.defineProperty(wdioExpect, 'soft', {
|
|
32
32
|
configurable: true,
|
|
33
33
|
value: (actual) => createSoftExpect(actual)
|
|
@@ -44,11 +44,10 @@ function createSharedExpectSetup() {
|
|
|
44
44
|
configurable: true,
|
|
45
45
|
value: (testId) => SoftAssertService.getInstance().clearFailures(testId)
|
|
46
46
|
});
|
|
47
|
-
return { wdioExpect, wdioCustomMatchers
|
|
47
|
+
return { wdioExpect, wdioCustomMatchers };
|
|
48
48
|
}
|
|
49
49
|
const sharedExpectSetup = getGlobalSingleton('expect', createSharedExpectSetup);
|
|
50
50
|
export const wdioCustomMatchers = sharedExpectSetup.wdioCustomMatchers;
|
|
51
|
-
export const matchers = sharedExpectSetup.matchers;
|
|
52
51
|
export const expect = sharedExpectSetup.wdioExpect;
|
|
53
52
|
export const getDefaultOptions = () => DEFAULT_OPTIONS;
|
|
54
53
|
export const setDefaultOptions = (options) => {
|
|
@@ -60,15 +59,6 @@ export const setDefaultOptions = (options) => {
|
|
|
60
59
|
});
|
|
61
60
|
});
|
|
62
61
|
};
|
|
63
|
-
export const setFeatureFlags = (featureFlags) => {
|
|
64
|
-
Object.entries(featureFlags).forEach(([ffName, ffValue]) => {
|
|
65
|
-
defaultOptionsList.forEach((option) => {
|
|
66
|
-
option.featureFlags[ffName] = ffValue;
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
};
|
|
70
|
-
export const setOptions = setDefaultOptions;
|
|
71
|
-
export const getConfig = getDefaultOptions;
|
|
72
62
|
export { SnapshotService } from './snapshot.js';
|
|
73
63
|
export { SoftAssertService } from './softAssert.js';
|
|
74
64
|
export { SoftAssertionService } from './softAssertService.js';
|
package/lib/jasmineUtils.js
CHANGED
|
@@ -11,10 +11,10 @@ function asymmetricMatch(a, b) {
|
|
|
11
11
|
if (asymmetricA && asymmetricB) {
|
|
12
12
|
return undefined;
|
|
13
13
|
}
|
|
14
|
-
const matchersUtil = {
|
|
14
|
+
const matchersUtil = Object.assign([], {
|
|
15
15
|
equals,
|
|
16
16
|
contains: (actual, expected) => actual.some(value => equals(value, expected)),
|
|
17
|
-
};
|
|
17
|
+
});
|
|
18
18
|
if (asymmetricA) {
|
|
19
19
|
return a.asymmetricMatch(b, matchersUtil);
|
|
20
20
|
}
|
|
@@ -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,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { compareText } from '../../utils.js';
|
|
2
2
|
import { WdioAsymmetricMatchers } from './asymmetricsUtils.js';
|
|
3
3
|
const ONE_OF_TAG = 'expect-webdriverio.oneOf';
|
|
4
4
|
const ONE_OF_SYMBOL = Symbol.for(ONE_OF_TAG);
|
|
@@ -19,7 +19,7 @@ export class OneOfMatcher extends WdioAsymmetricMatchers {
|
|
|
19
19
|
else if (typeof actual !== 'string') {
|
|
20
20
|
return false;
|
|
21
21
|
}
|
|
22
|
-
return
|
|
22
|
+
return this.sample.some((expected) => expected !== null && compareText(actual, expected, this.options).success);
|
|
23
23
|
}
|
|
24
24
|
withOptions(options) {
|
|
25
25
|
return new OneOfMatcher(...this.sample).setOptions(options);
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { waitUntil, enhanceError, compareText } from '../../utils.js';
|
|
1
|
+
import { waitUntil, enhanceError, compareTextOrOneOf } from '../../utils.js';
|
|
3
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
4
|
-
|
|
3
|
+
import { executeBrowserCommand } from '../../util/executeBrowserCommand.js';
|
|
4
|
+
import { buildWdioAsymmetricMatchersWithOptions } from '../asymmetrics/asymmetricsUtils.js';
|
|
5
|
+
let permissionWarningLogged = false;
|
|
5
6
|
export async function toHaveClipboardText(browser, expectedValue, options = DEFAULT_OPTIONS) {
|
|
6
7
|
const { expectation = 'clipboard text', verb = 'have', isNot, matcherName = 'toHaveClipboardText' } = this;
|
|
7
8
|
await options.beforeAssertion?.({
|
|
@@ -9,14 +10,16 @@ export async function toHaveClipboardText(browser, expectedValue, options = DEFA
|
|
|
9
10
|
expectedValue,
|
|
10
11
|
options,
|
|
11
12
|
});
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
const expectedWithOptions = buildWdioAsymmetricMatchersWithOptions(expectedValue, options);
|
|
14
|
+
const { actual, success: pass, subject, expected } = await waitUntil(async () => {
|
|
15
|
+
return await executeBrowserCommand({
|
|
16
|
+
browser,
|
|
17
|
+
isNot,
|
|
18
|
+
expectedValue: expectedWithOptions,
|
|
19
|
+
compare: (browser, expectedValue) => compareClipboardText(browser, expectedValue, options),
|
|
20
|
+
});
|
|
18
21
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
19
|
-
const message = enhanceError(
|
|
22
|
+
const message = enhanceError(subject, expected, actual, this, verb, expectation, '', options);
|
|
20
23
|
const result = {
|
|
21
24
|
pass,
|
|
22
25
|
message: () => message
|
|
@@ -29,3 +32,14 @@ export async function toHaveClipboardText(browser, expectedValue, options = DEFA
|
|
|
29
32
|
});
|
|
30
33
|
return result;
|
|
31
34
|
}
|
|
35
|
+
const compareClipboardText = async (browser, expectedValue, options) => {
|
|
36
|
+
await browser.setPermissions({ name: 'clipboard-read' }, 'granted')
|
|
37
|
+
.catch((err) => {
|
|
38
|
+
if (!permissionWarningLogged) {
|
|
39
|
+
permissionWarningLogged = true;
|
|
40
|
+
console.warn(`expect-webdriverio: Couldn't set clipboard permissions: ${err}`);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const actual = await browser.execute(() => window.navigator.clipboard.readText());
|
|
44
|
+
return compareTextOrOneOf(actual, expectedValue, options);
|
|
45
|
+
};
|
|
@@ -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?.({
|
|
@@ -8,28 +10,17 @@ export async function toHaveLocalStorageItem(browser, key, expectedValue, option
|
|
|
8
10
|
expectedValue: expectedValue ? [key, expectedValue] : key,
|
|
9
11
|
options,
|
|
10
12
|
});
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
expected = expectedValue;
|
|
21
|
-
}
|
|
22
|
-
const { actual, success: pass } = await waitUntil(async () => {
|
|
23
|
-
const actual = await browser.execute((storageKey) => {
|
|
24
|
-
return localStorage.getItem(storageKey);
|
|
25
|
-
}, key);
|
|
26
|
-
if (actual === null) {
|
|
27
|
-
return { actual, success: false, subject: browser };
|
|
28
|
-
}
|
|
29
|
-
const compareResult = compareText(actual, expected, options);
|
|
30
|
-
return { actual, success: compareResult.success, subject: browser };
|
|
13
|
+
const expected = expectedValue ?? expect.anything();
|
|
14
|
+
const expectedWithOptions = buildWdioAsymmetricMatchersWithOptions(expected, options);
|
|
15
|
+
const { actual, success: pass, subject, expected: expectedValues } = await waitUntil(async () => {
|
|
16
|
+
return await executeBrowserCommand({
|
|
17
|
+
browser,
|
|
18
|
+
isNot,
|
|
19
|
+
expectedValue: expectedWithOptions,
|
|
20
|
+
compare: (browser, expectedValue) => compareStorageItem(browser, key, expectedValue, options),
|
|
21
|
+
});
|
|
31
22
|
}, isNot, { wait: options.wait, interval: options.interval });
|
|
32
|
-
const message = enhanceError(
|
|
23
|
+
const message = enhanceError(subject, expectedValues, actual, this, verb, expectation, key, options);
|
|
33
24
|
const result = {
|
|
34
25
|
pass,
|
|
35
26
|
message: () => message
|
|
@@ -42,3 +33,13 @@ export async function toHaveLocalStorageItem(browser, key, expectedValue, option
|
|
|
42
33
|
});
|
|
43
34
|
return result;
|
|
44
35
|
}
|
|
36
|
+
const compareStorageItem = async (browser, key, expected, options) => {
|
|
37
|
+
const actual = await browser.execute((storageKey) => {
|
|
38
|
+
return localStorage.getItem(storageKey);
|
|
39
|
+
}, key);
|
|
40
|
+
if (actual === null) {
|
|
41
|
+
return { actual, success: false };
|
|
42
|
+
}
|
|
43
|
+
const compareResult = compareTextOrOneOf(actual, expected, options);
|
|
44
|
+
return { actual, success: compareResult.success };
|
|
45
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
|
|
|
2
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
3
3
|
export async function toBeClickable(received, options = DEFAULT_OPTIONS) {
|
|
4
4
|
this.expectation = this.expectation || 'clickable';
|
|
5
|
+
const { matcherName = 'toBeClickable' } = this;
|
|
5
6
|
await options.beforeAssertion?.({
|
|
6
|
-
matcherName
|
|
7
|
+
matcherName,
|
|
7
8
|
options,
|
|
8
9
|
});
|
|
9
10
|
const result = await executeCommandBe.call(this, received, el => el?.isClickable(), options);
|
|
10
11
|
await options.afterAssertion?.({
|
|
11
|
-
matcherName
|
|
12
|
+
matcherName,
|
|
12
13
|
options,
|
|
13
14
|
result
|
|
14
15
|
});
|
|
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
|
|
|
2
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
3
3
|
export async function toBeDisabled(received, options = DEFAULT_OPTIONS) {
|
|
4
4
|
this.expectation = this.expectation || 'disabled';
|
|
5
|
+
const { matcherName = 'toBeDisabled' } = this;
|
|
5
6
|
await options.beforeAssertion?.({
|
|
6
|
-
matcherName
|
|
7
|
+
matcherName,
|
|
7
8
|
options,
|
|
8
9
|
});
|
|
9
10
|
const result = await executeCommandBe.call(this, received, async (el) => !await el.isEnabled(), options);
|
|
10
11
|
await options.afterAssertion?.({
|
|
11
|
-
matcherName
|
|
12
|
+
matcherName,
|
|
12
13
|
options,
|
|
13
14
|
result
|
|
14
15
|
});
|
|
@@ -2,8 +2,9 @@ import { executeCommandBe } from '../../utils.js';
|
|
|
2
2
|
import { DEFAULT_OPTIONS_TO_BE_DISPLAYED } from '../../constants.js';
|
|
3
3
|
export async function toBeDisplayed(received, options = DEFAULT_OPTIONS_TO_BE_DISPLAYED) {
|
|
4
4
|
this.expectation = this.expectation || 'displayed';
|
|
5
|
+
const { matcherName = 'toBeDisplayed' } = this;
|
|
5
6
|
await options.beforeAssertion?.({
|
|
6
|
-
matcherName
|
|
7
|
+
matcherName,
|
|
7
8
|
options,
|
|
8
9
|
});
|
|
9
10
|
const { withinViewport, contentVisibilityAuto, opacityProperty, visibilityProperty, ...commandOptions } = { ...DEFAULT_OPTIONS_TO_BE_DISPLAYED, ...options };
|
|
@@ -14,7 +15,7 @@ export async function toBeDisplayed(received, options = DEFAULT_OPTIONS_TO_BE_DI
|
|
|
14
15
|
visibilityProperty
|
|
15
16
|
}), commandOptions);
|
|
16
17
|
await options.afterAssertion?.({
|
|
17
|
-
matcherName
|
|
18
|
+
matcherName,
|
|
18
19
|
options,
|
|
19
20
|
result
|
|
20
21
|
});
|
|
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
|
|
|
2
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
3
3
|
export async function toBeDisplayedInViewport(received, options = DEFAULT_OPTIONS) {
|
|
4
4
|
this.expectation = this.expectation || 'displayed in viewport';
|
|
5
|
+
const { matcherName = 'toBeDisplayedInViewport' } = this;
|
|
5
6
|
await options.beforeAssertion?.({
|
|
6
|
-
matcherName
|
|
7
|
+
matcherName,
|
|
7
8
|
options,
|
|
8
9
|
});
|
|
9
10
|
const result = await executeCommandBe.call(this, received, el => el?.isDisplayed({ withinViewport: true }), options);
|
|
10
11
|
await options.afterAssertion?.({
|
|
11
|
-
matcherName
|
|
12
|
+
matcherName,
|
|
12
13
|
options,
|
|
13
14
|
result
|
|
14
15
|
});
|
|
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
|
|
|
2
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
3
3
|
export async function toBeEnabled(received, options = DEFAULT_OPTIONS) {
|
|
4
4
|
this.expectation = this.expectation || 'enabled';
|
|
5
|
+
const { matcherName = 'toBeEnabled' } = this;
|
|
5
6
|
await options.beforeAssertion?.({
|
|
6
|
-
matcherName
|
|
7
|
+
matcherName,
|
|
7
8
|
options,
|
|
8
9
|
});
|
|
9
10
|
const result = await executeCommandBe.call(this, received, el => el?.isEnabled(), options);
|
|
10
11
|
await options.afterAssertion?.({
|
|
11
|
-
matcherName
|
|
12
|
+
matcherName,
|
|
12
13
|
options,
|
|
13
14
|
result
|
|
14
15
|
});
|
|
@@ -4,13 +4,14 @@ export async function toExist(received, options = DEFAULT_OPTIONS) {
|
|
|
4
4
|
this.expectation = this.expectation || 'exist';
|
|
5
5
|
this.verb = this.verb || '';
|
|
6
6
|
this.allowEmptyElements = true;
|
|
7
|
+
const { matcherName = 'toExist' } = this;
|
|
7
8
|
await options.beforeAssertion?.({
|
|
8
|
-
matcherName
|
|
9
|
+
matcherName,
|
|
9
10
|
options,
|
|
10
11
|
});
|
|
11
12
|
const result = await executeCommandBe.call(this, received, el => el?.isExisting(), options);
|
|
12
13
|
await options.afterAssertion?.({
|
|
13
|
-
matcherName
|
|
14
|
+
matcherName,
|
|
14
15
|
options,
|
|
15
16
|
result
|
|
16
17
|
});
|
|
@@ -19,10 +20,12 @@ export async function toExist(received, options = DEFAULT_OPTIONS) {
|
|
|
19
20
|
export function toBeExisting(el, options) {
|
|
20
21
|
this.expectation = 'existing';
|
|
21
22
|
this.verb = 'be';
|
|
23
|
+
this.matcherName ??= 'toBeExisting';
|
|
22
24
|
return toExist.call(this, el, options);
|
|
23
25
|
}
|
|
24
26
|
export function toBePresent(el, options) {
|
|
25
27
|
this.expectation = 'present';
|
|
26
28
|
this.verb = 'be';
|
|
29
|
+
this.matcherName ??= 'toBePresent';
|
|
27
30
|
return toExist.call(this, el, options);
|
|
28
31
|
}
|
|
@@ -2,13 +2,14 @@ import { executeCommandBe } from '../../utils.js';
|
|
|
2
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
3
3
|
export async function toBeFocused(received, options = DEFAULT_OPTIONS) {
|
|
4
4
|
this.expectation = this.expectation || 'focused';
|
|
5
|
+
const { matcherName = 'toBeFocused' } = this;
|
|
5
6
|
await options.beforeAssertion?.({
|
|
6
|
-
matcherName
|
|
7
|
+
matcherName,
|
|
7
8
|
options,
|
|
8
9
|
});
|
|
9
10
|
const result = await executeCommandBe.call(this, received, el => el?.isFocused(), options);
|
|
10
11
|
await options.afterAssertion?.({
|
|
11
|
-
matcherName
|
|
12
|
+
matcherName,
|
|
12
13
|
options,
|
|
13
14
|
result
|
|
14
15
|
});
|
|
@@ -2,14 +2,14 @@ import { executeCommandBe } from '../../utils.js';
|
|
|
2
2
|
import { DEFAULT_OPTIONS } from '../../constants.js';
|
|
3
3
|
export async function toBeSelected(received, options = DEFAULT_OPTIONS) {
|
|
4
4
|
this.expectation = this.expectation || 'selected';
|
|
5
|
-
|
|
5
|
+
const { matcherName = 'toBeSelected' } = this;
|
|
6
6
|
await options.beforeAssertion?.({
|
|
7
|
-
matcherName
|
|
7
|
+
matcherName,
|
|
8
8
|
options,
|
|
9
9
|
});
|
|
10
10
|
const result = await executeCommandBe.call(this, received, el => el?.isSelected(), options);
|
|
11
11
|
await options.afterAssertion?.({
|
|
12
|
-
matcherName
|
|
12
|
+
matcherName,
|
|
13
13
|
options,
|
|
14
14
|
result
|
|
15
15
|
});
|
|
@@ -17,7 +17,7 @@ export async function toBeSelected(received, options = DEFAULT_OPTIONS) {
|
|
|
17
17
|
}
|
|
18
18
|
export async function toBeChecked(received, options = DEFAULT_OPTIONS) {
|
|
19
19
|
this.expectation = 'checked';
|
|
20
|
-
this.matcherName
|
|
20
|
+
this.matcherName ??= 'toBeChecked';
|
|
21
21
|
const result = await toBeSelected.call(this, received, options);
|
|
22
22
|
return result;
|
|
23
23
|
}
|