expect-webdriverio 5.1.0 → 5.2.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/lib/index.js CHANGED
@@ -1,8 +1,16 @@
1
1
  import { expect as expectLib } from 'expect';
2
- import wdioMatchers from './matchers.js';
2
+ import * as wdioMatchers from './matchers.js';
3
3
  import { DEFAULT_OPTIONS } from './constants.js';
4
+ import createSoftExpect from './softExpect.js';
5
+ import { SoftAssertService } from './softAssert.js';
4
6
  export const matchers = new Map();
7
+ const filteredMatchers = {};
5
8
  const extend = expectLib.extend;
9
+ Object.keys(wdioMatchers).forEach(matcher => {
10
+ if (typeof wdioMatchers[matcher] === 'function') {
11
+ filteredMatchers[matcher] = wdioMatchers[matcher];
12
+ }
13
+ });
6
14
  expectLib.extend = (m) => {
7
15
  if (!m || typeof m !== 'object') {
8
16
  return;
@@ -10,8 +18,21 @@ expectLib.extend = (m) => {
10
18
  Object.entries(m).forEach(([name, matcher]) => matchers.set(name, matcher));
11
19
  return extend(m);
12
20
  };
13
- expectLib.extend(wdioMatchers);
14
- export const expect = expectLib;
21
+ expectLib.extend(filteredMatchers);
22
+ const expectWithSoft = expectLib;
23
+ Object.defineProperty(expectWithSoft, 'soft', {
24
+ value: (actual) => createSoftExpect(actual)
25
+ });
26
+ Object.defineProperty(expectWithSoft, 'getSoftFailures', {
27
+ value: (testId) => SoftAssertService.getInstance().getFailures(testId)
28
+ });
29
+ Object.defineProperty(expectWithSoft, 'assertSoftFailures', {
30
+ value: (testId) => SoftAssertService.getInstance().assertNoFailures(testId)
31
+ });
32
+ Object.defineProperty(expectWithSoft, 'clearSoftFailures', {
33
+ value: (testId) => SoftAssertService.getInstance().clearFailures(testId)
34
+ });
35
+ export const expect = expectWithSoft;
15
36
  export const getConfig = () => DEFAULT_OPTIONS;
16
37
  export const setDefaultOptions = (options = {}) => {
17
38
  Object.entries(options).forEach(([key, value]) => {
@@ -22,4 +43,6 @@ export const setDefaultOptions = (options = {}) => {
22
43
  };
23
44
  export const setOptions = setDefaultOptions;
24
45
  export { SnapshotService } from './snapshot.js';
46
+ export { SoftAssertService } from './softAssert.js';
47
+ export { SoftAssertionService } from './softAssertService.js';
25
48
  export * as utils from './utils.js';
@@ -1,5 +1,5 @@
1
1
  import path from 'node:path';
2
- import { expect } from '../index.js';
2
+ import { expect } from 'expect';
3
3
  import { SnapshotService } from '../snapshot.js';
4
4
  function returnSnapshotError(snapshotError) {
5
5
  try {
package/lib/matchers.js CHANGED
@@ -1,62 +1,31 @@
1
- import * as toHaveClipboardText from './matchers/browser/toHaveClipboardText.js';
2
- import * as toHaveTitle from './matchers/browser/toHaveTitle.js';
3
- import * as toHaveUrl from './matchers/browser/toHaveUrl.js';
4
- import * as toBeElementsArrayOfSize from './matchers/elements/toBeElementsArrayOfSize.js';
5
- import * as toBeClickable from './matchers/element/toBeClickable.js';
6
- import * as toBeDisabled from './matchers/element/toBeDisabled.js';
7
- import * as toBeDisplayed from './matchers/element/toBeDisplayed.js';
8
- import * as toBeDisplayedInViewport from './matchers/element/toBeDisplayedInViewport.js';
9
- import * as toBeEnabled from './matchers/element/toBeEnabled.js';
10
- import * as toBeExisting from './matchers/element/toBeExisting.js';
11
- import * as toBeFocused from './matchers/element/toBeFocused.js';
12
- import * as toBeSelected from './matchers/element/toBeSelected.js';
13
- import * as toHaveAttribute from './matchers/element/toHaveAttribute.js';
14
- import * as toHaveChildren from './matchers/element/toHaveChildren.js';
15
- import * as toHaveClass from './matchers/element/toHaveClass.js';
16
- import * as toHaveHref from './matchers/element/toHaveHref.js';
17
- import * as toHaveHTML from './matchers/element/toHaveHTML.js';
18
- import * as toHaveId from './matchers/element/toHaveId.js';
19
- import * as toHaveSize from './matchers/element/toHaveSize.js';
20
- import * as toHaveElementProperty from './matchers/element/toHaveElementProperty.js';
21
- import * as toHaveText from './matchers/element/toHaveText.js';
22
- import * as toHaveValue from './matchers/element/toHaveValue.js';
23
- import * as toHaveStyle from './matchers/element/toHaveStyle.js';
24
- import * as toBeRequested from './matchers/mock/toBeRequested.js';
25
- import * as toBeRequestedTimes from './matchers/mock/toBeRequestedTimes.js';
26
- import * as toBeRequestedWith from './matchers/mock/toBeRequestedWith.js';
27
- import * as snapshotMatchers from './matchers/snapshot.js';
28
- const matchers = {
29
- ...toHaveClipboardText,
30
- ...toHaveTitle,
31
- ...toHaveUrl,
32
- ...toBeElementsArrayOfSize,
33
- ...toBeClickable,
34
- ...toBeDisabled,
35
- ...toBeDisplayed,
36
- ...toBeDisplayedInViewport,
37
- ...toBeEnabled,
38
- ...toBeExisting,
39
- ...toBeFocused,
40
- ...toBeSelected,
41
- ...toHaveAttribute,
42
- ...toHaveChildren,
43
- ...toHaveClass,
44
- ...toHaveHref,
45
- ...toHaveHTML,
46
- ...toHaveId,
47
- ...toHaveSize,
48
- ...toHaveElementProperty,
49
- ...toHaveText,
50
- ...toHaveValue,
51
- ...toHaveStyle,
52
- ...toBeRequested,
53
- ...toBeRequestedTimes,
54
- ...toBeRequestedWith,
55
- ...snapshotMatchers
56
- };
57
- Object.keys(matchers).forEach((key) => {
58
- if (key.endsWith('Fn')) {
59
- delete matchers[key];
60
- }
61
- });
62
- export default matchers;
1
+ export * from './matchers/browser/toHaveClipboardText.js';
2
+ export * from './matchers/browser/toHaveTitle.js';
3
+ export * from './matchers/browser/toHaveUrl.js';
4
+ export * from './matchers/element/toBeClickable.js';
5
+ export * from './matchers/element/toBeDisabled.js';
6
+ export * from './matchers/element/toBeDisplayed.js';
7
+ export * from './matchers/element/toBeDisplayedInViewport.js';
8
+ export * from './matchers/element/toBeEnabled.js';
9
+ export * from './matchers/element/toBeExisting.js';
10
+ export * from './matchers/element/toBeFocused.js';
11
+ export * from './matchers/element/toBeSelected.js';
12
+ export * from './matchers/element/toHaveAttribute.js';
13
+ export * from './matchers/element/toHaveChildren.js';
14
+ export * from './matchers/element/toHaveClass.js';
15
+ export * from './matchers/element/toHaveComputedLabel.js';
16
+ export * from './matchers/element/toHaveComputedRole.js';
17
+ export * from './matchers/element/toHaveElementProperty.js';
18
+ export * from './matchers/element/toHaveHeight.js';
19
+ export * from './matchers/element/toHaveHref.js';
20
+ export * from './matchers/element/toHaveHTML.js';
21
+ export * from './matchers/element/toHaveId.js';
22
+ export * from './matchers/element/toHaveSize.js';
23
+ export * from './matchers/element/toHaveStyle.js';
24
+ export * from './matchers/element/toHaveText.js';
25
+ export * from './matchers/element/toHaveValue.js';
26
+ export * from './matchers/element/toHaveWidth.js';
27
+ export * from './matchers/elements/toBeElementsArrayOfSize.js';
28
+ export * from './matchers/mock/toBeRequested.js';
29
+ export * from './matchers/mock/toBeRequestedTimes.js';
30
+ export * from './matchers/mock/toBeRequestedWith.js';
31
+ export * from './matchers/snapshot.js';
package/lib/snapshot.js CHANGED
@@ -63,17 +63,20 @@ export class SnapshotService {
63
63
  async beforeTest(test) {
64
64
  __classPrivateFieldSet(this, _SnapshotService_currentFilePath, test.file, "f");
65
65
  __classPrivateFieldSet(this, _SnapshotService_currentTestName, `${test.parent} > ${test.title}`, "f");
66
- await __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f").startCurrentRun(test.file, test.fullTitle, __classPrivateFieldGet(this, _SnapshotService_options, "f"));
66
+ await __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f").setup(test.file, __classPrivateFieldGet(this, _SnapshotService_options, "f"));
67
67
  }
68
68
  async beforeStep(step, scenario) {
69
69
  const file = scenario.uri;
70
70
  const testName = `${scenario.name} > ${step.text}`;
71
71
  __classPrivateFieldSet(this, _SnapshotService_currentFilePath, file, "f");
72
72
  __classPrivateFieldSet(this, _SnapshotService_currentTestName, testName, "f");
73
- await __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f").startCurrentRun(file, testName, __classPrivateFieldGet(this, _SnapshotService_options, "f"));
73
+ await __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f").setup(file, __classPrivateFieldGet(this, _SnapshotService_options, "f"));
74
74
  }
75
75
  async after() {
76
- const result = await __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f").finishCurrentRun();
76
+ if (!__classPrivateFieldGet(this, _SnapshotService_currentFilePath, "f")) {
77
+ return;
78
+ }
79
+ const result = await __classPrivateFieldGet(this, _SnapshotService_snapshotClient, "f").finish(__classPrivateFieldGet(this, _SnapshotService_currentFilePath, "f"));
77
80
  if (!result) {
78
81
  return;
79
82
  }
@@ -0,0 +1,76 @@
1
+ export class SoftAssertService {
2
+ constructor() {
3
+ this.failureMap = new Map();
4
+ this.currentTest = null;
5
+ }
6
+ static getInstance() {
7
+ if (!SoftAssertService.instance) {
8
+ SoftAssertService.instance = new SoftAssertService();
9
+ }
10
+ return SoftAssertService.instance;
11
+ }
12
+ setCurrentTest(testId, testName, testFile) {
13
+ this.currentTest = { id: testId, name: testName, file: testFile };
14
+ if (!this.failureMap.has(testId)) {
15
+ this.failureMap.set(testId, []);
16
+ }
17
+ }
18
+ clearCurrentTest() {
19
+ this.currentTest = null;
20
+ }
21
+ getCurrentTestId() {
22
+ return this.currentTest?.id || null;
23
+ }
24
+ addFailure(error, matcherName) {
25
+ const testId = this.getCurrentTestId();
26
+ if (!testId) {
27
+ throw error;
28
+ }
29
+ const stackLines = error.stack?.split('\n') || [];
30
+ let location = '';
31
+ for (const line of stackLines) {
32
+ if (line && !line.includes('expect-webdriverio') && !line.includes('node_modules')) {
33
+ location = line.trim();
34
+ break;
35
+ }
36
+ }
37
+ const failures = this.failureMap.get(testId) || [];
38
+ failures.push({ error, matcherName, location });
39
+ this.failureMap.set(testId, failures);
40
+ }
41
+ getFailures(testId) {
42
+ const id = testId || this.getCurrentTestId();
43
+ if (!id) {
44
+ return [];
45
+ }
46
+ return this.failureMap.get(id) || [];
47
+ }
48
+ clearFailures(testId) {
49
+ const id = testId || this.getCurrentTestId();
50
+ if (id) {
51
+ this.failureMap.delete(id);
52
+ }
53
+ }
54
+ assertNoFailures(testId) {
55
+ const id = testId || this.getCurrentTestId();
56
+ if (!id) {
57
+ return;
58
+ }
59
+ const failures = this.getFailures(id);
60
+ if (failures.length === 0) {
61
+ return;
62
+ }
63
+ let message = `${failures.length} soft assertion failure${failures.length > 1 ? 's' : ''}:\n\n`;
64
+ failures.forEach((failure, index) => {
65
+ message += `${index + 1}) ${failure.matcherName}: ${failure.error.message}\n`;
66
+ if (failure.location) {
67
+ message += ` at ${failure.location}\n`;
68
+ }
69
+ message += '\n';
70
+ });
71
+ this.clearFailures(id);
72
+ const error = new Error(message);
73
+ error.name = 'SoftAssertionsError';
74
+ throw error;
75
+ }
76
+ }
@@ -0,0 +1,47 @@
1
+ import { SoftAssertService } from './softAssert.js';
2
+ export class SoftAssertionService {
3
+ constructor(serviceOptions) {
4
+ this.softAssertService = SoftAssertService.getInstance();
5
+ this.options = {
6
+ autoAssertOnTestEnd: true,
7
+ ...serviceOptions
8
+ };
9
+ }
10
+ beforeTest(test) {
11
+ const testId = this.getTestId(test);
12
+ this.softAssertService.setCurrentTest(testId, test.title, test.file);
13
+ }
14
+ beforeStep(step, scenario) {
15
+ const stepId = `${scenario.uri || ''}:${scenario.name || ''}:${step.text || ''}`;
16
+ this.softAssertService.setCurrentTest(stepId, step.text, scenario.uri);
17
+ }
18
+ afterTest(test, _, result) {
19
+ if (!result.error && this.options.autoAssertOnTestEnd) {
20
+ try {
21
+ const testId = this.getTestId(test);
22
+ this.softAssertService.assertNoFailures(testId);
23
+ }
24
+ catch (error) {
25
+ result.error = error;
26
+ result.passed = false;
27
+ }
28
+ }
29
+ this.softAssertService.clearCurrentTest();
30
+ }
31
+ afterStep(step, scenario, result) {
32
+ if (result.passed) {
33
+ try {
34
+ const stepId = `${scenario.uri || ''}:${scenario.name || ''}:${step.text || ''}`;
35
+ this.softAssertService.assertNoFailures(stepId);
36
+ }
37
+ catch (error) {
38
+ result.error = error;
39
+ result.passed = false;
40
+ }
41
+ }
42
+ this.softAssertService.clearCurrentTest();
43
+ }
44
+ getTestId(test) {
45
+ return `${test.file || ''}:${test.parent || ''}:${test.title || ''}`;
46
+ }
47
+ }
@@ -0,0 +1,68 @@
1
+ import { expect, matchers } from './index.js';
2
+ import { SoftAssertService } from './softAssert.js';
3
+ const createSoftExpect = (actual) => {
4
+ const softService = SoftAssertService.getInstance();
5
+ return new Proxy({}, {
6
+ get(target, prop) {
7
+ const propName = String(prop);
8
+ if (propName === 'not') {
9
+ return createSoftNotProxy(actual, softService);
10
+ }
11
+ if (propName === 'resolves' || propName === 'rejects') {
12
+ return createSoftChainProxy(actual, propName, softService);
13
+ }
14
+ if (matchers.has(propName)) {
15
+ return createSoftMatcher(actual, propName, softService);
16
+ }
17
+ return undefined;
18
+ }
19
+ });
20
+ };
21
+ const createSoftNotProxy = (actual, softService) => {
22
+ return new Proxy({}, {
23
+ get(target, prop) {
24
+ const propName = String(prop);
25
+ if (matchers.has(propName)) {
26
+ return createSoftMatcher(actual, propName, softService, 'not');
27
+ }
28
+ return undefined;
29
+ }
30
+ });
31
+ };
32
+ const createSoftChainProxy = (actual, chainType, softService) => {
33
+ return new Proxy({}, {
34
+ get(target, prop) {
35
+ const propName = String(prop);
36
+ if (matchers.has(propName)) {
37
+ return createSoftMatcher(actual, propName, softService, chainType);
38
+ }
39
+ return undefined;
40
+ }
41
+ });
42
+ };
43
+ const createSoftMatcher = (actual, matcherName, softService, prefix) => {
44
+ return async (...args) => {
45
+ try {
46
+ let expectChain = expect(actual);
47
+ if (prefix === 'not') {
48
+ expectChain = expectChain.not;
49
+ }
50
+ else if (prefix === 'resolves') {
51
+ expectChain = expectChain.resolves;
52
+ }
53
+ else if (prefix === 'rejects') {
54
+ expectChain = expectChain.rejects;
55
+ }
56
+ return await expectChain[matcherName](...args);
57
+ }
58
+ catch (error) {
59
+ const fullMatcherName = prefix ? `${prefix}.${matcherName}` : matcherName;
60
+ softService.addFailure(error, fullMatcherName);
61
+ return {
62
+ pass: true,
63
+ message: () => `Soft assertion failed: ${fullMatcherName}`
64
+ };
65
+ }
66
+ };
67
+ };
68
+ export default createSoftExpect;
@@ -1 +1 @@
1
- {"root":["../src/constants.ts","../src/index.ts","../src/jasmineUtils.ts","../src/matchers.ts","../src/snapshot.ts","../src/types.ts","../src/utils.ts","../src/matchers/snapshot.ts","../src/matchers/browser/toHaveClipboardText.ts","../src/matchers/browser/toHaveTitle.ts","../src/matchers/browser/toHaveUrl.ts","../src/matchers/element/toBeClickable.ts","../src/matchers/element/toBeDisabled.ts","../src/matchers/element/toBeDisplayed.ts","../src/matchers/element/toBeDisplayedInViewport.ts","../src/matchers/element/toBeEnabled.ts","../src/matchers/element/toBeExisting.ts","../src/matchers/element/toBeFocused.ts","../src/matchers/element/toBeSelected.ts","../src/matchers/element/toHaveAttribute.ts","../src/matchers/element/toHaveChildren.ts","../src/matchers/element/toHaveClass.ts","../src/matchers/element/toHaveComputedLabel.ts","../src/matchers/element/toHaveComputedRole.ts","../src/matchers/element/toHaveElementProperty.ts","../src/matchers/element/toHaveHTML.ts","../src/matchers/element/toHaveHeight.ts","../src/matchers/element/toHaveHref.ts","../src/matchers/element/toHaveId.ts","../src/matchers/element/toHaveSize.ts","../src/matchers/element/toHaveStyle.ts","../src/matchers/element/toHaveText.ts","../src/matchers/element/toHaveValue.ts","../src/matchers/element/toHaveWidth.ts","../src/matchers/elements/toBeElementsArrayOfSize.ts","../src/matchers/mock/toBeRequested.ts","../src/matchers/mock/toBeRequestedTimes.ts","../src/matchers/mock/toBeRequestedWith.ts","../src/util/elementsUtil.ts","../src/util/executeCommand.ts","../src/util/formatMessage.ts","../src/util/refetchElements.ts"],"version":"5.7.3"}
1
+ {"root":["../src/constants.ts","../src/index.ts","../src/jasmineUtils.ts","../src/matchers.ts","../src/snapshot.ts","../src/softAssert.ts","../src/softAssertService.ts","../src/softExpect.ts","../src/types.ts","../src/utils.ts","../src/matchers/snapshot.ts","../src/matchers/browser/toHaveClipboardText.ts","../src/matchers/browser/toHaveTitle.ts","../src/matchers/browser/toHaveUrl.ts","../src/matchers/element/toBeClickable.ts","../src/matchers/element/toBeDisabled.ts","../src/matchers/element/toBeDisplayed.ts","../src/matchers/element/toBeDisplayedInViewport.ts","../src/matchers/element/toBeEnabled.ts","../src/matchers/element/toBeExisting.ts","../src/matchers/element/toBeFocused.ts","../src/matchers/element/toBeSelected.ts","../src/matchers/element/toHaveAttribute.ts","../src/matchers/element/toHaveChildren.ts","../src/matchers/element/toHaveClass.ts","../src/matchers/element/toHaveComputedLabel.ts","../src/matchers/element/toHaveComputedRole.ts","../src/matchers/element/toHaveElementProperty.ts","../src/matchers/element/toHaveHTML.ts","../src/matchers/element/toHaveHeight.ts","../src/matchers/element/toHaveHref.ts","../src/matchers/element/toHaveId.ts","../src/matchers/element/toHaveSize.ts","../src/matchers/element/toHaveStyle.ts","../src/matchers/element/toHaveText.ts","../src/matchers/element/toHaveValue.ts","../src/matchers/element/toHaveWidth.ts","../src/matchers/elements/toBeElementsArrayOfSize.ts","../src/matchers/mock/toBeRequested.ts","../src/matchers/mock/toBeRequestedTimes.ts","../src/matchers/mock/toBeRequestedWith.ts","../src/util/elementsUtil.ts","../src/util/executeCommand.ts","../src/util/formatMessage.ts","../src/util/refetchElements.ts"],"version":"5.8.3"}
package/lib/utils.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import isEqual from 'lodash.isequal';
2
- import { expect } from './index.js';
2
+ import { expect } from 'expect';
3
3
  import { DEFAULT_OPTIONS } from './constants.js';
4
4
  import { wrapExpectedWithArray } from './util/elementsUtil.js';
5
5
  import { executeCommand } from './util/executeCommand.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expect-webdriverio",
3
- "version": "5.1.0",
3
+ "version": "5.2.1",
4
4
  "author": "Mykola Grybyk <mykola.grybyk@gmail.com>",
5
5
  "description": "WebdriverIO Assertion Library",
6
6
  "license": "MIT",
@@ -58,7 +58,7 @@
58
58
  "prepare": "husky install"
59
59
  },
60
60
  "dependencies": {
61
- "@vitest/snapshot": "^2.0.5",
61
+ "@vitest/snapshot": "^3.2.0",
62
62
  "expect": "^29.7.0",
63
63
  "jest-matcher-utils": "^29.7.0",
64
64
  "lodash.isequal": "^4.5.0"
@@ -68,16 +68,16 @@
68
68
  "@types/jest": "^29.5.12",
69
69
  "@types/lodash.isequal": "^4.5.8",
70
70
  "@types/node": "^22.9.0",
71
- "@wdio/types": "^9.9.0",
72
71
  "@vitest/coverage-v8": "^3.0.5",
73
- "@wdio/eslint": "^0.0.5",
72
+ "@wdio/eslint": "^0.1.0",
73
+ "@wdio/types": "^9.9.0",
74
74
  "c8": "^10.1.2",
75
75
  "eslint": "^9.15.0",
76
76
  "husky": "^9.1.5",
77
77
  "npm-run-all2": "^7.0.1",
78
- "release-it": "^18.1.1",
78
+ "release-it": "^19.0.1",
79
79
  "rimraf": "^6.0.1",
80
- "shelljs": "^0.8.5",
80
+ "shelljs": "^0.9.1",
81
81
  "typescript": "^5.5.4",
82
82
  "vitest": "^3.0.4",
83
83
  "webdriverio": "^9.2.11"
@@ -14,6 +14,35 @@ declare namespace ExpectWebdriverIO {
14
14
  }
15
15
  }
16
16
 
17
+ interface SoftFailure {
18
+ error: Error;
19
+ matcherName: string;
20
+ location?: string;
21
+ }
22
+
23
+ class SoftAssertService {
24
+ static getInstance(): SoftAssertService;
25
+ setCurrentTest(testId: string, testName?: string, testFile?: string): void;
26
+ clearCurrentTest(): void;
27
+ getCurrentTestId(): string | null;
28
+ addFailure(error: Error, matcherName: string): void;
29
+ getFailures(testId?: string): SoftFailure[];
30
+ clearFailures(testId?: string): void;
31
+ assertNoFailures(testId?: string): void;
32
+ }
33
+
34
+ interface SoftAssertionServiceOptions {
35
+ autoAssertOnTestEnd?: boolean;
36
+ }
37
+
38
+ class SoftAssertionService implements import('@wdio/types').Services.ServiceInstance {
39
+ constructor(serviceOptions?: SoftAssertionServiceOptions, capabilities?: any, config?: any);
40
+ beforeTest(test: import('@wdio/types').Frameworks.Test): void;
41
+ beforeStep(step: import('@wdio/types').Frameworks.PickleStep, scenario: import('@wdio/types').Frameworks.Scenario): void;
42
+ afterTest(test: import('@wdio/types').Frameworks.Test, context: any, result: import('@wdio/types').Frameworks.TestResult): void;
43
+ afterStep(step: import('@wdio/types').Frameworks.PickleStep, scenario: import('@wdio/types').Frameworks.Scenario, result: { passed: boolean, error?: Error }): void;
44
+ }
45
+
17
46
  interface AssertionResult {
18
47
  pass: boolean
19
48
  message(): string
@@ -447,6 +476,57 @@ declare namespace ExpectWebdriverIO {
447
476
  asymmetricMatch(...args: any[]): boolean
448
477
  toString(): string
449
478
  }
479
+
480
+ /**
481
+ * expect function declaration, containing two generics:
482
+ * - T: the type of the actual value, e.g. WebdriverIO.Browser or WebdriverIO.Element
483
+ * - R: the type of the return value, e.g. Promise<void> or void
484
+ */
485
+ interface Expect {
486
+ <T = unknown, R extends void | Promise<void> = void | Promise<void>>(actual: T): Matchers<R, T>
487
+
488
+ /**
489
+ * Creates a soft assertion wrapper around standard expect
490
+ * Soft assertions record failures but don't throw errors immediately
491
+ * All failures are collected and reported at the end of the test
492
+ */
493
+ soft<T = unknown>(actual: T): Matchers<Promise<void>, T>
494
+
495
+ /**
496
+ * Get all current soft assertion failures
497
+ */
498
+ getSoftFailures(testId?: string): SoftFailure[]
499
+
500
+ /**
501
+ * Manually assert all soft failures (throws an error if any failures exist)
502
+ */
503
+ assertSoftFailures(testId?: string): void
504
+
505
+ /**
506
+ * Clear all current soft assertion failures
507
+ */
508
+ clearSoftFailures(testId?: string): void
509
+
510
+ // Standard asymmetric matchers from Jest
511
+ extend(map: Record<string, Function>): void
512
+ anything(): PartialMatcher
513
+ any(sample: unknown): PartialMatcher
514
+ stringContaining(expected: string): PartialMatcher
515
+ objectContaining(sample: Record<string, unknown>): PartialMatcher
516
+ arrayContaining(sample: Array<unknown>): PartialMatcher
517
+ stringMatching(expected: string | RegExp): PartialMatcher
518
+ not: AsymmetricMatchers
519
+ }
520
+
521
+ interface AsymmetricMatchers {
522
+ any(expectedObject: any): PartialMatcher
523
+ anything(): PartialMatcher
524
+ arrayContaining(sample: Array<unknown>): PartialMatcher
525
+ objectContaining(sample: Record<string, unknown>): PartialMatcher
526
+ stringContaining(expected: string): PartialMatcher
527
+ stringMatching(expected: string | RegExp): PartialMatcher
528
+ not: AsymmetricMatchers
529
+ }
450
530
  }
451
531
 
452
532
  declare module 'expect-webdriverio' {