expect-webdriverio 5.3.2 → 5.3.3
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/test/__fixtures__/utils.js +19 -0
- package/lib/test/index.test.js +8 -0
- package/lib/test/matchers/beMatchers.test.js +109 -0
- package/lib/test/matchers/browser/toHaveClipboardText.test.js +21 -0
- package/lib/test/matchers/browserMatchers.test.js +102 -0
- package/lib/test/matchers/element/toBeDisabled.test.js +121 -0
- package/lib/test/matchers/element/toHaveAttribute.test.js +131 -0
- package/lib/test/matchers/element/toHaveChildren.test.js +62 -0
- package/lib/test/matchers/element/toHaveComputedLabel.test.js +284 -0
- package/lib/test/matchers/element/toHaveComputedRole.test.js +284 -0
- package/lib/test/matchers/element/toHaveElementClass.test.js +125 -0
- package/lib/test/matchers/element/toHaveElementProperty.test.js +111 -0
- package/lib/test/matchers/element/toHaveHTML.test.js +291 -0
- package/lib/test/matchers/element/toHaveHeight.test.js +171 -0
- package/lib/test/matchers/element/toHaveHref.test.js +54 -0
- package/lib/test/matchers/element/toHaveId.test.js +54 -0
- package/lib/test/matchers/element/toHaveSize.test.js +109 -0
- package/lib/test/matchers/element/toHaveStyle.test.js +199 -0
- package/lib/test/matchers/element/toHaveText.test.js +333 -0
- package/lib/test/matchers/element/toHaveValue.test.js +81 -0
- package/lib/test/matchers/element/toHaveWidth.test.js +171 -0
- package/lib/test/matchers/elements/toBeElementsArrayOfSize.test.js +141 -0
- package/lib/test/matchers/mock/toBeRequested.test.js +73 -0
- package/lib/test/matchers/mock/toBeRequestedTimes.test.js +108 -0
- package/lib/test/matchers/mock/toBeRequestedWith.test.js +343 -0
- package/lib/test/matchers.test.js +54 -0
- package/lib/test/snapshot.test.js +53 -0
- package/lib/test/softAssertions.test.js +332 -0
- package/lib/test/util/elementsUtil.test.js +18 -0
- package/lib/test/util/formatMessage.test.js +96 -0
- package/lib/test/util/refetchElements.test.js +19 -0
- package/lib/test/utils.test.js +117 -0
- package/lib/{tsconfig.build.tsbuildinfo → tsconfig.tsbuildinfo} +1 -1
- package/package.json +2 -2
- package/types/expect-webdriverio.d.ts +39 -28
- package/types/standalone.d.ts +2 -0
- /package/lib/{constants.js → src/constants.js} +0 -0
- /package/lib/{index.js → src/index.js} +0 -0
- /package/lib/{jasmineUtils.js → src/jasmineUtils.js} +0 -0
- /package/lib/{matchers → src/matchers}/browser/toHaveClipboardText.js +0 -0
- /package/lib/{matchers → src/matchers}/browser/toHaveTitle.js +0 -0
- /package/lib/{matchers → src/matchers}/browser/toHaveUrl.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toBeClickable.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toBeDisabled.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toBeDisplayed.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toBeDisplayedInViewport.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toBeEnabled.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toBeExisting.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toBeFocused.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toBeSelected.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveAttribute.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveChildren.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveClass.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveComputedLabel.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveComputedRole.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveElementProperty.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveHTML.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveHeight.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveHref.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveId.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveSize.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveStyle.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveText.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveValue.js +0 -0
- /package/lib/{matchers → src/matchers}/element/toHaveWidth.js +0 -0
- /package/lib/{matchers → src/matchers}/elements/toBeElementsArrayOfSize.js +0 -0
- /package/lib/{matchers → src/matchers}/mock/toBeRequested.js +0 -0
- /package/lib/{matchers → src/matchers}/mock/toBeRequestedTimes.js +0 -0
- /package/lib/{matchers → src/matchers}/mock/toBeRequestedWith.js +0 -0
- /package/lib/{matchers → src/matchers}/snapshot.js +0 -0
- /package/lib/{matchers.js → src/matchers.js} +0 -0
- /package/lib/{snapshot.js → src/snapshot.js} +0 -0
- /package/lib/{softAssert.js → src/softAssert.js} +0 -0
- /package/lib/{softAssertService.js → src/softAssertService.js} +0 -0
- /package/lib/{softExpect.js → src/softExpect.js} +0 -0
- /package/lib/{types.js → src/types.js} +0 -0
- /package/lib/{util → src/util}/elementsUtil.js +0 -0
- /package/lib/{util → src/util}/executeCommand.js +0 -0
- /package/lib/{util → src/util}/formatMessage.js +0 -0
- /package/lib/{util → src/util}/refetchElements.js +0 -0
- /package/lib/{utils.js → src/utils.js} +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { test, expect, vi } from 'vitest';
|
|
2
|
+
import { matchers, expect as expectLib } from '../src/index.js';
|
|
3
|
+
const ALL_MATCHERS = [
|
|
4
|
+
'toHaveClipboardText',
|
|
5
|
+
'toHaveTitle',
|
|
6
|
+
'toHaveUrl',
|
|
7
|
+
'toBeClickable',
|
|
8
|
+
'toBeDisabled',
|
|
9
|
+
'toBeDisplayed',
|
|
10
|
+
'toBeDisplayedInViewport',
|
|
11
|
+
'toBeEnabled',
|
|
12
|
+
'toExist',
|
|
13
|
+
'toBeExisting',
|
|
14
|
+
'toBePresent',
|
|
15
|
+
'toBeFocused',
|
|
16
|
+
'toBeSelected',
|
|
17
|
+
'toBeChecked',
|
|
18
|
+
'toHaveAttributeAndValue',
|
|
19
|
+
'toHaveAttribute',
|
|
20
|
+
'toHaveAttr',
|
|
21
|
+
'toHaveChildren',
|
|
22
|
+
'toHaveClass',
|
|
23
|
+
'toHaveElementClass',
|
|
24
|
+
'toHaveClassContaining',
|
|
25
|
+
'toHaveComputedLabel',
|
|
26
|
+
'toHaveComputedRole',
|
|
27
|
+
'toHaveElementProperty',
|
|
28
|
+
'toHaveHeight',
|
|
29
|
+
'toHaveHref',
|
|
30
|
+
'toHaveLink',
|
|
31
|
+
'toHaveHTML',
|
|
32
|
+
'toHaveId',
|
|
33
|
+
'toHaveSize',
|
|
34
|
+
'toHaveStyle',
|
|
35
|
+
'toHaveText',
|
|
36
|
+
'toHaveValue',
|
|
37
|
+
'toHaveWidth',
|
|
38
|
+
'toBeElementsArrayOfSize',
|
|
39
|
+
'toBeRequested',
|
|
40
|
+
'toBeRequestedTimes',
|
|
41
|
+
'toBeRequestedWith',
|
|
42
|
+
'toBeRequestedWithResponse',
|
|
43
|
+
'toMatchSnapshot',
|
|
44
|
+
'toMatchInlineSnapshot'
|
|
45
|
+
];
|
|
46
|
+
test('matchers', () => {
|
|
47
|
+
expect([...matchers.keys()]).toEqual(ALL_MATCHERS);
|
|
48
|
+
});
|
|
49
|
+
test('allows to add matcher', () => {
|
|
50
|
+
const matcher = vi.fn((actual, expected) => ({ pass: actual === expected }));
|
|
51
|
+
expectLib.extend({ toBeCustom: matcher });
|
|
52
|
+
expectLib('foo').toBeCustom('foo');
|
|
53
|
+
expect(matchers.keys()).toContain('toBeCustom');
|
|
54
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { test, expect } from 'vitest';
|
|
5
|
+
import { expect as expectExport, SnapshotService } from '../src/index.js';
|
|
6
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const __filename = path.basename(fileURLToPath(import.meta.url));
|
|
8
|
+
const service = SnapshotService.initiate({
|
|
9
|
+
resolveSnapshotPath: (path, extension) => path + extension
|
|
10
|
+
});
|
|
11
|
+
test('supports snapshot testing', async () => {
|
|
12
|
+
await service.beforeTest({
|
|
13
|
+
title: 'test',
|
|
14
|
+
parent: 'parent',
|
|
15
|
+
file: path.join(__dirname, __filename),
|
|
16
|
+
});
|
|
17
|
+
process.env.WDIO_INTERNAL_TEST = 'true';
|
|
18
|
+
const exp = expectExport;
|
|
19
|
+
expect(exp).toBeDefined();
|
|
20
|
+
expect(exp({}).toMatchSnapshot).toBeDefined();
|
|
21
|
+
expect(exp({}).toMatchInlineSnapshot).toBeDefined();
|
|
22
|
+
await exp({ a: 'a' }).toMatchSnapshot();
|
|
23
|
+
await exp({ deep: { nested: { object: 'value' } } }).toMatchInlineSnapshot(`
|
|
24
|
+
{
|
|
25
|
+
"deep": {
|
|
26
|
+
"nested": {
|
|
27
|
+
"object": "value",
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
}
|
|
31
|
+
`);
|
|
32
|
+
await service.after();
|
|
33
|
+
const expectedSnapfileExist = await fs.access(path.resolve(__dirname, 'snapshot.test.ts.snap'))
|
|
34
|
+
.then(() => true, () => false);
|
|
35
|
+
expect(expectedSnapfileExist).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
test('supports cucumber snapshot testing', async () => {
|
|
38
|
+
await service.beforeStep({
|
|
39
|
+
text: 'Fake step',
|
|
40
|
+
}, {
|
|
41
|
+
name: 'Fake scenario',
|
|
42
|
+
uri: `${__dirname}/file.feature`,
|
|
43
|
+
});
|
|
44
|
+
const exp = expectExport;
|
|
45
|
+
expect(exp).toBeDefined();
|
|
46
|
+
expect(exp({}).toMatchSnapshot).toBeDefined();
|
|
47
|
+
expect(exp({}).toMatchInlineSnapshot).toBeDefined();
|
|
48
|
+
await exp({ cucum: 'ber' }).toMatchSnapshot();
|
|
49
|
+
await service.after();
|
|
50
|
+
const expectedSnapfileExist = await fs.access(path.resolve(__dirname, 'file.feature.snap'))
|
|
51
|
+
.then(() => true, () => false);
|
|
52
|
+
expect(expectedSnapfileExist).toBe(true);
|
|
53
|
+
});
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { $ } from '@wdio/globals';
|
|
3
|
+
import { expect as expectWdio, SoftAssertionService, SoftAssertService } from '../src/index.js';
|
|
4
|
+
vi.mock('@wdio/globals');
|
|
5
|
+
describe('Soft Assertions', () => {
|
|
6
|
+
let el;
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
el = $('sel');
|
|
9
|
+
el.getText = vi.fn().mockImplementation(() => 'Actual Text');
|
|
10
|
+
expectWdio.clearSoftFailures();
|
|
11
|
+
});
|
|
12
|
+
describe('expect.soft', () => {
|
|
13
|
+
it('should not throw immediately on failure', async () => {
|
|
14
|
+
const softService = SoftAssertService.getInstance();
|
|
15
|
+
softService.setCurrentTest('test-1', 'test name', 'test file');
|
|
16
|
+
await expectWdio.soft(el).toHaveText('Expected Text');
|
|
17
|
+
const failures = expectWdio.getSoftFailures();
|
|
18
|
+
expect(failures.length).toBe(1);
|
|
19
|
+
expect(failures[0].matcherName).toBe('toHaveText');
|
|
20
|
+
expect(failures[0].error.message).toContain('text');
|
|
21
|
+
});
|
|
22
|
+
it('should support chained assertions with .not', async () => {
|
|
23
|
+
const softService = SoftAssertService.getInstance();
|
|
24
|
+
softService.setCurrentTest('test-2', 'test name', 'test file');
|
|
25
|
+
await expectWdio.soft(el).not.toHaveText('Actual Text');
|
|
26
|
+
const failures = expectWdio.getSoftFailures();
|
|
27
|
+
expect(failures.length).toBe(1);
|
|
28
|
+
expect(failures[0].matcherName).toBe('not.toHaveText');
|
|
29
|
+
});
|
|
30
|
+
it('should support multiple soft failures in the same test', async () => {
|
|
31
|
+
const softService = SoftAssertService.getInstance();
|
|
32
|
+
softService.setCurrentTest('test-3', 'test name', 'test file');
|
|
33
|
+
await expectWdio.soft(el).toHaveText('First Expected');
|
|
34
|
+
await expectWdio.soft(el).toHaveText('Second Expected');
|
|
35
|
+
await expectWdio.soft(el).toHaveText('Third Expected');
|
|
36
|
+
const failures = expectWdio.getSoftFailures();
|
|
37
|
+
expect(failures.length).toBe(3);
|
|
38
|
+
expect(failures[0].matcherName).toBe('toHaveText');
|
|
39
|
+
expect(failures[1].matcherName).toBe('toHaveText');
|
|
40
|
+
expect(failures[2].matcherName).toBe('toHaveText');
|
|
41
|
+
});
|
|
42
|
+
it('should allow passing assertions', async () => {
|
|
43
|
+
const softService = SoftAssertService.getInstance();
|
|
44
|
+
softService.setCurrentTest('test-4', 'test name', 'test file');
|
|
45
|
+
await expectWdio.soft(el).toHaveText('Actual Text');
|
|
46
|
+
const failures = expectWdio.getSoftFailures();
|
|
47
|
+
expect(failures.length).toBe(0);
|
|
48
|
+
});
|
|
49
|
+
it('assertSoftFailures should throw if failures exist', async () => {
|
|
50
|
+
const softService = SoftAssertService.getInstance();
|
|
51
|
+
softService.setCurrentTest('test-5', 'test name', 'test file');
|
|
52
|
+
await expectWdio.soft(el).toHaveText('Expected Text');
|
|
53
|
+
await expect(() => expectWdio.assertSoftFailures()).toThrow(/1 soft assertion failure/);
|
|
54
|
+
});
|
|
55
|
+
it('clearSoftFailures should remove all failures', async () => {
|
|
56
|
+
const softService = SoftAssertService.getInstance();
|
|
57
|
+
softService.setCurrentTest('test-6', 'test name', 'test file');
|
|
58
|
+
await expectWdio.soft(el).toHaveText('First Expected');
|
|
59
|
+
await expectWdio.soft(el).toHaveText('Second Expected');
|
|
60
|
+
expect(expectWdio.getSoftFailures().length).toBe(2);
|
|
61
|
+
expectWdio.clearSoftFailures();
|
|
62
|
+
expect(expectWdio.getSoftFailures().length).toBe(0);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
describe('SoftAssertService hooks', () => {
|
|
66
|
+
it('afterTest should throw if soft failures exist', () => {
|
|
67
|
+
const softService = SoftAssertService.getInstance();
|
|
68
|
+
softService.setCurrentTest('test-hooks-1', 'test hooks', 'test file');
|
|
69
|
+
const error = new Error('Test failure');
|
|
70
|
+
softService.addFailure(error, 'toBeDisplayed');
|
|
71
|
+
const testResult = { passed: true, error: 'undefined' };
|
|
72
|
+
const service = new SoftAssertionService();
|
|
73
|
+
service.afterTest({
|
|
74
|
+
file: 'test file', parent: '', title: 'test hooks',
|
|
75
|
+
fullName: '',
|
|
76
|
+
ctx: undefined,
|
|
77
|
+
type: '',
|
|
78
|
+
fullTitle: '',
|
|
79
|
+
pending: false
|
|
80
|
+
}, null, testResult);
|
|
81
|
+
expect(testResult.passed).toBe(true);
|
|
82
|
+
expect(testResult.error).toBeDefined();
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe('Different Matcher Types', () => {
|
|
86
|
+
beforeEach(async () => {
|
|
87
|
+
el = $('sel');
|
|
88
|
+
el.getText = vi.fn().mockImplementation(() => 'Actual Text');
|
|
89
|
+
el.isDisplayed = vi.fn().mockImplementation(() => false);
|
|
90
|
+
el.getAttribute = vi.fn().mockImplementation(() => 'actual-class');
|
|
91
|
+
el.isClickable = vi.fn().mockImplementation(() => false);
|
|
92
|
+
expectWdio.clearSoftFailures();
|
|
93
|
+
});
|
|
94
|
+
it('should handle boolean matchers', async () => {
|
|
95
|
+
const softService = SoftAssertService.getInstance();
|
|
96
|
+
softService.setCurrentTest('boolean-test', 'boolean test', 'test file');
|
|
97
|
+
await expectWdio.soft(el).toBeDisplayed();
|
|
98
|
+
await expectWdio.soft(el).toBeClickable();
|
|
99
|
+
const failures = expectWdio.getSoftFailures();
|
|
100
|
+
expect(failures.length).toBe(2);
|
|
101
|
+
expect(failures[0].matcherName).toBe('toBeDisplayed');
|
|
102
|
+
expect(failures[1].matcherName).toBe('toBeClickable');
|
|
103
|
+
});
|
|
104
|
+
it('should handle attribute matchers with multiple parameters', async () => {
|
|
105
|
+
const softService = SoftAssertService.getInstance();
|
|
106
|
+
softService.setCurrentTest('attribute-test', 'attribute test', 'test file');
|
|
107
|
+
await expectWdio.soft(el).toHaveAttribute('class', 'expected-class');
|
|
108
|
+
const failures = expectWdio.getSoftFailures();
|
|
109
|
+
expect(failures.length).toBe(1);
|
|
110
|
+
expect(failures[0].matcherName).toBe('toHaveAttribute');
|
|
111
|
+
});
|
|
112
|
+
it('should handle matchers with options', async () => {
|
|
113
|
+
const softService = SoftAssertService.getInstance();
|
|
114
|
+
softService.setCurrentTest('options-test', 'options test', 'test file');
|
|
115
|
+
await expectWdio.soft(el).toHaveText('Expected', { ignoreCase: true, wait: 1000 });
|
|
116
|
+
const failures = expectWdio.getSoftFailures();
|
|
117
|
+
expect(failures.length).toBe(1);
|
|
118
|
+
expect(failures[0].matcherName).toBe('toHaveText');
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
describe('Test Isolation', () => {
|
|
122
|
+
it('should isolate failures between different test contexts', async () => {
|
|
123
|
+
const softService = SoftAssertService.getInstance();
|
|
124
|
+
softService.setCurrentTest('isolation-test-1', 'test 1', 'file1');
|
|
125
|
+
await expectWdio.soft(el).toHaveText('Expected Text 1');
|
|
126
|
+
expect(expectWdio.getSoftFailures().length).toBe(1);
|
|
127
|
+
softService.setCurrentTest('isolation-test-2', 'test 2', 'file2');
|
|
128
|
+
await expectWdio.soft(el).toHaveText('Expected Text 2');
|
|
129
|
+
expect(expectWdio.getSoftFailures('isolation-test-2').length).toBe(1);
|
|
130
|
+
expect(expectWdio.getSoftFailures('isolation-test-1').length).toBe(1);
|
|
131
|
+
expectWdio.clearSoftFailures('isolation-test-1');
|
|
132
|
+
expect(expectWdio.getSoftFailures('isolation-test-1').length).toBe(0);
|
|
133
|
+
expect(expectWdio.getSoftFailures('isolation-test-2').length).toBe(1);
|
|
134
|
+
});
|
|
135
|
+
it('should handle calls without test context gracefully', async () => {
|
|
136
|
+
const softService = SoftAssertService.getInstance();
|
|
137
|
+
softService.clearCurrentTest();
|
|
138
|
+
await expect(async () => {
|
|
139
|
+
await expectWdio.soft(el).toHaveText('Expected Text');
|
|
140
|
+
}).rejects.toThrow();
|
|
141
|
+
});
|
|
142
|
+
it('should handle rapid concurrent soft assertions', async () => {
|
|
143
|
+
const softService = SoftAssertService.getInstance();
|
|
144
|
+
softService.setCurrentTest('concurrent-test', 'concurrent', 'test file');
|
|
145
|
+
el.getText = vi.fn().mockImplementation(() => 'Actual Text');
|
|
146
|
+
el.isDisplayed = vi.fn().mockImplementation(() => false);
|
|
147
|
+
el.isClickable = vi.fn().mockImplementation(() => false);
|
|
148
|
+
const promises = [
|
|
149
|
+
expectWdio.soft(el).toHaveText('Expected 1'),
|
|
150
|
+
expectWdio.soft(el).toBeDisplayed(),
|
|
151
|
+
expectWdio.soft(el).toBeClickable()
|
|
152
|
+
];
|
|
153
|
+
await Promise.all(promises);
|
|
154
|
+
const failures = expectWdio.getSoftFailures();
|
|
155
|
+
expect(failures.length).toBe(3);
|
|
156
|
+
const matcherNames = failures.map(f => f.matcherName);
|
|
157
|
+
expect(matcherNames).toContain('toHaveText');
|
|
158
|
+
expect(matcherNames).toContain('toBeDisplayed');
|
|
159
|
+
expect(matcherNames).toContain('toBeClickable');
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
describe('Edge Cases & Error Handling', () => {
|
|
163
|
+
it('should handle matcher that throws non-standard errors', async () => {
|
|
164
|
+
const softService = SoftAssertService.getInstance();
|
|
165
|
+
softService.setCurrentTest('error-test', 'error test', 'test file');
|
|
166
|
+
const originalMethod = el.getText;
|
|
167
|
+
el.getText = vi.fn().mockImplementation(() => {
|
|
168
|
+
throw new TypeError('Weird browser error');
|
|
169
|
+
});
|
|
170
|
+
await expectWdio.soft(el).toHaveText('Expected Text');
|
|
171
|
+
const failures = expectWdio.getSoftFailures();
|
|
172
|
+
expect(failures.length).toBe(1);
|
|
173
|
+
expect(failures[0].error).toBeInstanceOf(Error);
|
|
174
|
+
expect(failures[0].error.message).toContain('Weird browser error');
|
|
175
|
+
el.getText = originalMethod;
|
|
176
|
+
});
|
|
177
|
+
it('should handle very long error messages', async () => {
|
|
178
|
+
const softService = SoftAssertService.getInstance();
|
|
179
|
+
softService.setCurrentTest('long-error-test', 'long error', 'test file');
|
|
180
|
+
const veryLongText = 'A'.repeat(10000);
|
|
181
|
+
await expectWdio.soft(el).toHaveText(veryLongText);
|
|
182
|
+
const failures = expectWdio.getSoftFailures();
|
|
183
|
+
expect(failures.length).toBe(1);
|
|
184
|
+
expect(failures[0].error.message.length).toBeGreaterThan(0);
|
|
185
|
+
});
|
|
186
|
+
it('should handle null/undefined values gracefully', async () => {
|
|
187
|
+
const softService = SoftAssertService.getInstance();
|
|
188
|
+
softService.setCurrentTest('null-test', 'null test', 'test file');
|
|
189
|
+
await expectWdio.soft(el).toHaveText(null);
|
|
190
|
+
await expectWdio.soft(el).toHaveAttribute('class', undefined);
|
|
191
|
+
const failures = expectWdio.getSoftFailures();
|
|
192
|
+
expect(failures.length).toBe(2);
|
|
193
|
+
});
|
|
194
|
+
it('should capture error location information', async () => {
|
|
195
|
+
const softService = SoftAssertService.getInstance();
|
|
196
|
+
softService.setCurrentTest('location-test', 'location test', 'test file');
|
|
197
|
+
await expectWdio.soft(el).toHaveText('Expected Text');
|
|
198
|
+
const failures = expectWdio.getSoftFailures();
|
|
199
|
+
expect(failures.length).toBe(1);
|
|
200
|
+
if (failures[0].location) {
|
|
201
|
+
expect(failures[0].location).toBeTruthy();
|
|
202
|
+
expect(typeof failures[0].location).toBe('string');
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
it('should handle maximum failure limits', async () => {
|
|
206
|
+
const softService = SoftAssertService.getInstance();
|
|
207
|
+
softService.setCurrentTest('limit-test', 'limit test', 'test file');
|
|
208
|
+
const promises = [];
|
|
209
|
+
for (let i = 0; i < 150; i++) {
|
|
210
|
+
promises.push(expectWdio.soft(el).toHaveText(`Expected ${i}`));
|
|
211
|
+
}
|
|
212
|
+
await Promise.all(promises);
|
|
213
|
+
const failures = expectWdio.getSoftFailures();
|
|
214
|
+
expect(failures.length).toBeGreaterThan(0);
|
|
215
|
+
expect(failures.length).toBeLessThanOrEqual(150);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
describe('SoftAssertionService Configuration', () => {
|
|
219
|
+
beforeEach(() => {
|
|
220
|
+
expectWdio.clearSoftFailures();
|
|
221
|
+
});
|
|
222
|
+
it('should auto-assert failures by default', () => {
|
|
223
|
+
const softService = SoftAssertService.getInstance();
|
|
224
|
+
const testId = 'test file::config default';
|
|
225
|
+
softService.setCurrentTest(testId, 'config default', 'test file');
|
|
226
|
+
const error = new Error('Test failure');
|
|
227
|
+
softService.addFailure(error, 'toBeDisplayed');
|
|
228
|
+
const service = new SoftAssertionService();
|
|
229
|
+
const testResult = { passed: true, error: undefined };
|
|
230
|
+
service.afterTest({
|
|
231
|
+
file: 'test file',
|
|
232
|
+
parent: '',
|
|
233
|
+
title: 'config default',
|
|
234
|
+
fullName: '',
|
|
235
|
+
ctx: undefined,
|
|
236
|
+
type: '',
|
|
237
|
+
fullTitle: '',
|
|
238
|
+
pending: false
|
|
239
|
+
}, null, testResult);
|
|
240
|
+
expect(testResult.passed).toBe(false);
|
|
241
|
+
expect(testResult.error).toBeDefined();
|
|
242
|
+
});
|
|
243
|
+
it('should not auto-assert when autoAssertOnTestEnd is false', () => {
|
|
244
|
+
const softService = SoftAssertService.getInstance();
|
|
245
|
+
const testId = 'test file::config disabled';
|
|
246
|
+
softService.setCurrentTest(testId, 'config disabled', 'test file');
|
|
247
|
+
const error = new Error('Test failure');
|
|
248
|
+
softService.addFailure(error, 'toBeDisplayed');
|
|
249
|
+
const service = new SoftAssertionService({ autoAssertOnTestEnd: false });
|
|
250
|
+
const testResult = { passed: true, error: undefined };
|
|
251
|
+
service.afterTest({
|
|
252
|
+
file: 'test file',
|
|
253
|
+
parent: '',
|
|
254
|
+
title: 'config disabled',
|
|
255
|
+
fullName: '',
|
|
256
|
+
ctx: undefined,
|
|
257
|
+
type: '',
|
|
258
|
+
fullTitle: '',
|
|
259
|
+
pending: false
|
|
260
|
+
}, null, testResult);
|
|
261
|
+
expect(testResult.passed).toBe(true);
|
|
262
|
+
expect(testResult.error).toBeUndefined();
|
|
263
|
+
const failures = expectWdio.getSoftFailures(testId);
|
|
264
|
+
expect(failures.length).toBe(1);
|
|
265
|
+
});
|
|
266
|
+
it('should still auto-assert with explicit autoAssertOnTestEnd: true', () => {
|
|
267
|
+
const softService = SoftAssertService.getInstance();
|
|
268
|
+
const testId = 'test file::config explicit';
|
|
269
|
+
softService.setCurrentTest(testId, 'config explicit', 'test file');
|
|
270
|
+
const error = new Error('Test failure');
|
|
271
|
+
softService.addFailure(error, 'toBeDisplayed');
|
|
272
|
+
const service = new SoftAssertionService({ autoAssertOnTestEnd: true });
|
|
273
|
+
const testResult = { passed: true, error: undefined };
|
|
274
|
+
service.afterTest({
|
|
275
|
+
file: 'test file',
|
|
276
|
+
parent: '',
|
|
277
|
+
title: 'config explicit',
|
|
278
|
+
fullName: '',
|
|
279
|
+
ctx: undefined,
|
|
280
|
+
type: '',
|
|
281
|
+
fullTitle: '',
|
|
282
|
+
pending: false
|
|
283
|
+
}, null, testResult);
|
|
284
|
+
expect(testResult.passed).toBe(false);
|
|
285
|
+
expect(testResult.error).toBeDefined();
|
|
286
|
+
});
|
|
287
|
+
it('should skip auto-assert if test already has an error', () => {
|
|
288
|
+
const softService = SoftAssertService.getInstance();
|
|
289
|
+
softService.setCurrentTest('config-existing-error-test', 'config existing error', 'test file');
|
|
290
|
+
const error = new Error('Soft assertion failure');
|
|
291
|
+
softService.addFailure(error, 'toBeDisplayed');
|
|
292
|
+
const service = new SoftAssertionService();
|
|
293
|
+
const existingError = new Error('Pre-existing test error');
|
|
294
|
+
const testResult = { passed: false, error: existingError };
|
|
295
|
+
service.afterTest({
|
|
296
|
+
file: 'test file',
|
|
297
|
+
parent: '',
|
|
298
|
+
title: 'config existing error',
|
|
299
|
+
fullName: '',
|
|
300
|
+
ctx: undefined,
|
|
301
|
+
type: '',
|
|
302
|
+
fullTitle: '',
|
|
303
|
+
pending: false
|
|
304
|
+
}, null, testResult);
|
|
305
|
+
expect(testResult.passed).toBe(false);
|
|
306
|
+
expect(testResult.error).toBe(existingError);
|
|
307
|
+
expect(testResult.error?.message).toBe('Pre-existing test error');
|
|
308
|
+
});
|
|
309
|
+
it('should accept undefined options and use defaults', () => {
|
|
310
|
+
const service = new SoftAssertionService(undefined);
|
|
311
|
+
expect(service).toBeDefined();
|
|
312
|
+
const softService = SoftAssertService.getInstance();
|
|
313
|
+
const testId = 'test file::config undefined';
|
|
314
|
+
softService.setCurrentTest(testId, 'config undefined', 'test file');
|
|
315
|
+
const error = new Error('Test failure');
|
|
316
|
+
softService.addFailure(error, 'toBeDisplayed');
|
|
317
|
+
const testResult = { passed: true, error: undefined };
|
|
318
|
+
service.afterTest({
|
|
319
|
+
file: 'test file',
|
|
320
|
+
parent: '',
|
|
321
|
+
title: 'config undefined',
|
|
322
|
+
fullName: '',
|
|
323
|
+
ctx: undefined,
|
|
324
|
+
type: '',
|
|
325
|
+
fullTitle: '',
|
|
326
|
+
pending: false
|
|
327
|
+
}, null, testResult);
|
|
328
|
+
expect(testResult.passed).toBe(false);
|
|
329
|
+
expect(testResult.error).toBeDefined();
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { vi, test, describe, expect } from 'vitest';
|
|
2
|
+
import { $, $$ } from '@wdio/globals';
|
|
3
|
+
import { wrapExpectedWithArray } from '../../src/util/elementsUtil.js';
|
|
4
|
+
vi.mock('@wdio/globals');
|
|
5
|
+
describe('elementsUtil', () => {
|
|
6
|
+
describe('wrapExpectedWithArray', () => {
|
|
7
|
+
test('is not array ', async () => {
|
|
8
|
+
const el = (await $('sel'));
|
|
9
|
+
const actual = wrapExpectedWithArray(el, 'Test Actual', 'Test Expected');
|
|
10
|
+
expect(actual).toEqual('Test Expected');
|
|
11
|
+
});
|
|
12
|
+
test('is array ', async () => {
|
|
13
|
+
const els = (await $$('sel'));
|
|
14
|
+
const actual = wrapExpectedWithArray(els, ['Test Actual', 'Test Actual'], 'Test Expected');
|
|
15
|
+
expect(actual).toEqual(['Test Expected']);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { test, describe, beforeEach, expect } from 'vitest';
|
|
2
|
+
import { printDiffOrStringify, printExpected, printReceived } from 'jest-matcher-utils';
|
|
3
|
+
import { enhanceError, numberError } from '../../src/util/formatMessage.js';
|
|
4
|
+
describe('formatMessage', () => {
|
|
5
|
+
describe('enhanceError', () => {
|
|
6
|
+
describe('default', () => {
|
|
7
|
+
let actual;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
actual = enhanceError('Test Subject', 'Test Expected', 'Test Actual', { isNot: false }, 'Test Verb', 'Test Expectation', '', { message: '', containing: false });
|
|
10
|
+
});
|
|
11
|
+
test('starting message', () => {
|
|
12
|
+
expect(actual).toMatch('Expect Test Subject to Test Verb Test Expectation');
|
|
13
|
+
});
|
|
14
|
+
test('diff string', () => {
|
|
15
|
+
const diffString = printDiffOrStringify('Test Expected', 'Test Actual', 'Expected', 'Received', true);
|
|
16
|
+
expect(actual).toMatch(diffString);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe('isNot', () => {
|
|
20
|
+
let actual;
|
|
21
|
+
describe('different', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
actual = enhanceError('Test Subject', 'Test Expected', 'Test Actual', { isNot: true }, 'Test Verb', 'Test Expectation', '', { message: '', containing: false });
|
|
24
|
+
});
|
|
25
|
+
test('starting message', () => {
|
|
26
|
+
expect(actual).toMatch('Expect Test Subject not to Test Verb Test Expectation');
|
|
27
|
+
});
|
|
28
|
+
test('diff string', () => {
|
|
29
|
+
const diffString = printDiffOrStringify('Test Expected', 'Test Actual', 'Expected [not]', 'Received ', true);
|
|
30
|
+
expect(actual).toMatch(diffString);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('same', () => {
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
actual = enhanceError('Test Subject', 'Test Same', 'Test Same', { isNot: true }, 'Test Verb', 'Test Expectation', '', { message: '', containing: false });
|
|
36
|
+
});
|
|
37
|
+
test('starting message', () => {
|
|
38
|
+
expect(actual).toMatch('Expect Test Subject not to Test Verb Test Expectation');
|
|
39
|
+
});
|
|
40
|
+
test('diff string', () => {
|
|
41
|
+
const diffString = `Expected [not]: ${printExpected('Test Same')}\n` +
|
|
42
|
+
`Received : ${printReceived('Test Same')}`;
|
|
43
|
+
expect(actual).toMatch(diffString);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('containing', () => {
|
|
48
|
+
let actual;
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
actual = enhanceError('Test Subject', 'Test Expected', 'Test Actual', { isNot: false }, 'Test Verb', 'Test Expectation', '', { message: '', containing: true });
|
|
51
|
+
});
|
|
52
|
+
test('starting message', () => {
|
|
53
|
+
expect(actual).toMatch('Expect Test Subject to Test Verb Test Expectation containing');
|
|
54
|
+
});
|
|
55
|
+
test('diff string', () => {
|
|
56
|
+
const diffString = printDiffOrStringify('Test Expected', 'Test Actual', 'Expected', 'Received', true);
|
|
57
|
+
expect(actual).toMatch(diffString);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
describe('message', () => {
|
|
61
|
+
let actual;
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
actual = enhanceError('Test Subject', 'Test Expected', 'Test Actual', { isNot: false }, 'Test Verb', 'Test Expectation', '', { message: 'Test Message', containing: false });
|
|
64
|
+
});
|
|
65
|
+
test('starting message', () => {
|
|
66
|
+
expect(actual).toMatch('Test Message\nExpect Test Subject to Test Verb Test Expectation');
|
|
67
|
+
});
|
|
68
|
+
test('diff string', () => {
|
|
69
|
+
const diffString = printDiffOrStringify('Test Expected', 'Test Actual', 'Expected', 'Received', true);
|
|
70
|
+
expect(actual).toMatch(diffString);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('arg2', () => {
|
|
74
|
+
let actual;
|
|
75
|
+
beforeEach(() => {
|
|
76
|
+
actual = enhanceError('Test Subject', 'Test Expected', 'Test Actual', { isNot: false }, 'Test Verb', 'Test Expectation', 'Test Arg2', { message: 'Test Message', containing: false });
|
|
77
|
+
});
|
|
78
|
+
test('starting message', () => {
|
|
79
|
+
expect(actual).toMatch('Expect Test Subject to Test Verb Test Expectation Test Arg2');
|
|
80
|
+
});
|
|
81
|
+
test('diff string', () => {
|
|
82
|
+
const diffString = printDiffOrStringify('Test Expected', 'Test Actual', 'Expected', 'Received', true);
|
|
83
|
+
expect(actual).toMatch(diffString);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('numberError', () => {
|
|
88
|
+
test('should return correct message', () => {
|
|
89
|
+
expect(numberError()).toBe('no params');
|
|
90
|
+
expect(numberError({ eq: 0 })).toBe(0);
|
|
91
|
+
expect(numberError({ gte: 1 })).toBe('>= 1');
|
|
92
|
+
expect(numberError({ lte: 1 })).toBe(' <= 1');
|
|
93
|
+
expect(numberError({ gte: 2, lte: 1 })).toBe('>= 2 && <= 1');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { vi, test, describe, beforeEach, expect } from 'vitest';
|
|
2
|
+
import { $$ } from '@wdio/globals';
|
|
3
|
+
import { refetchElements } from '../../src/util/refetchElements.js';
|
|
4
|
+
vi.mock('@wdio/globals');
|
|
5
|
+
describe('refetchElements', () => {
|
|
6
|
+
let els;
|
|
7
|
+
beforeEach(async () => {
|
|
8
|
+
els = (await $$('parent'));
|
|
9
|
+
els.parent._length = 5;
|
|
10
|
+
});
|
|
11
|
+
test('default', async () => {
|
|
12
|
+
const actual = await refetchElements(els, 5, true);
|
|
13
|
+
expect(actual.length).toBe(5);
|
|
14
|
+
});
|
|
15
|
+
test('wait is 0', async () => {
|
|
16
|
+
const actual = await refetchElements(els, 0, true);
|
|
17
|
+
expect(actual).toEqual(els);
|
|
18
|
+
});
|
|
19
|
+
});
|