@wdio/utils 9.0.0-alpha.9 → 9.0.4
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/build/envDetector.d.ts +3 -3
- package/build/envDetector.d.ts.map +1 -1
- package/build/index.d.ts +2 -2
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1902 -24
- package/build/initializeServices.d.ts +3 -3
- package/build/initializeServices.d.ts.map +1 -1
- package/build/monad.d.ts.map +1 -1
- package/build/node/manager.d.ts +2 -2
- package/build/node/manager.d.ts.map +1 -1
- package/build/node/startWebDriver.d.ts +2 -3
- package/build/node/startWebDriver.d.ts.map +1 -1
- package/build/node/utils.d.ts.map +1 -1
- package/build/node.js +473 -0
- package/build/pIteration.d.ts.map +1 -1
- package/build/shim.d.ts.map +1 -1
- package/build/startWebDriver.d.ts +2 -3
- package/build/startWebDriver.d.ts.map +1 -1
- package/build/test-framework/errorHandler.d.ts.map +1 -1
- package/build/test-framework/testFnWrapper.d.ts.map +1 -1
- package/build/test-framework/testInterfaceWrapper.d.ts.map +1 -1
- package/build/utils.d.ts +11 -2
- package/build/utils.d.ts.map +1 -1
- package/package.json +10 -11
- package/build/constants.js +0 -114
- package/build/envDetector.js +0 -251
- package/build/initializePlugin.js +0 -38
- package/build/initializeServices.js +0 -159
- package/build/monad.js +0 -196
- package/build/node/index.js +0 -3
- package/build/node/manager.js +0 -106
- package/build/node/startWebDriver.js +0 -140
- package/build/node/utils.js +0 -285
- package/build/pIteration.js +0 -347
- package/build/shim.js +0 -293
- package/build/startWebDriver.js +0 -20
- package/build/test-framework/errorHandler.js +0 -33
- package/build/test-framework/index.js +0 -4
- package/build/test-framework/testFnWrapper.js +0 -97
- package/build/test-framework/testInterfaceWrapper.js +0 -162
- package/build/test-framework/types.js +0 -1
- package/build/utils.js +0 -320
- /package/{LICENSE-MIT → LICENSE} +0 -0
package/build/shim.js
DELETED
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
import logger from '@wdio/logger';
|
|
2
|
-
import * as iterators from './pIteration.js';
|
|
3
|
-
const log = logger('@wdio/utils:shim');
|
|
4
|
-
let inCommandHook = false;
|
|
5
|
-
const ELEMENT_QUERY_COMMANDS = [
|
|
6
|
-
'$', '$$', 'custom$', 'custom$$', 'shadow$', 'shadow$$', 'react$',
|
|
7
|
-
'react$$', 'nextElement', 'previousElement', 'parentElement'
|
|
8
|
-
];
|
|
9
|
-
const ELEMENT_PROPS = [
|
|
10
|
-
'elementId', 'error', 'selector', 'parent', 'index', 'isReactElement',
|
|
11
|
-
'length'
|
|
12
|
-
];
|
|
13
|
-
const ACTION_COMMANDS = ['action', 'actions'];
|
|
14
|
-
const PROMISE_METHODS = ['then', 'catch', 'finally'];
|
|
15
|
-
const TIME_BUFFER = 3;
|
|
16
|
-
export async function executeHooksWithArgs(hookName, hooks = [], args = []) {
|
|
17
|
-
/**
|
|
18
|
-
* make sure hooks are an array of functions
|
|
19
|
-
*/
|
|
20
|
-
if (!Array.isArray(hooks)) {
|
|
21
|
-
hooks = [hooks];
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* make sure args is an array since we are calling apply
|
|
25
|
-
*/
|
|
26
|
-
if (!Array.isArray(args)) {
|
|
27
|
-
args = [args];
|
|
28
|
-
}
|
|
29
|
-
const hooksPromises = hooks.map((hook) => new Promise((resolve, reject) => {
|
|
30
|
-
let result;
|
|
31
|
-
try {
|
|
32
|
-
result = hook.apply(this, args);
|
|
33
|
-
}
|
|
34
|
-
catch (e) {
|
|
35
|
-
/**
|
|
36
|
-
* When we use `this.skip()` inside a test or a hook, it's a signal that we want to stop that particular test.
|
|
37
|
-
* Mocha, the testing framework, knows how to handle this for its own built-in hooks and test steps.
|
|
38
|
-
* However, for our custom hooks, we need to reject the promise, which effectively skips the test case.
|
|
39
|
-
* For more details, refer to: https://github.com/mochajs/mocha/pull/3859#issuecomment-534116333
|
|
40
|
-
*/
|
|
41
|
-
if (/^(sync|async) skip; aborting execution$/.test(e.message)) {
|
|
42
|
-
return reject();
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* in case of jasmine, when rejecting, we need to pass the message of rejection as well
|
|
46
|
-
*/
|
|
47
|
-
if (/^=> marked Pending/.test(e)) {
|
|
48
|
-
return reject(e);
|
|
49
|
-
}
|
|
50
|
-
log.error(e.stack);
|
|
51
|
-
return resolve(e);
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* if a promise is returned make sure we don't have a catch handler
|
|
55
|
-
* so in case of a rejection it won't cause the hook to fail
|
|
56
|
-
*/
|
|
57
|
-
if (result && typeof result.then === 'function') {
|
|
58
|
-
return result.then(resolve, (e) => {
|
|
59
|
-
log.error(e.stack || e.message);
|
|
60
|
-
resolve(e);
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
resolve(result);
|
|
64
|
-
}));
|
|
65
|
-
const start = Date.now();
|
|
66
|
-
const result = await Promise.all(hooksPromises);
|
|
67
|
-
if (hooksPromises.length) {
|
|
68
|
-
log.debug(`Finished to run "${hookName}" hook in ${Date.now() - start}ms`);
|
|
69
|
-
}
|
|
70
|
-
return result;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* wrap command to enable before and after command to be executed
|
|
74
|
-
* @param commandName name of the command (e.g. getTitle)
|
|
75
|
-
* @param fn command function
|
|
76
|
-
*/
|
|
77
|
-
export function wrapCommand(commandName, fn) {
|
|
78
|
-
async function wrapCommandFn(...args) {
|
|
79
|
-
const beforeHookArgs = [commandName, args];
|
|
80
|
-
if (!inCommandHook && this.options.beforeCommand) {
|
|
81
|
-
inCommandHook = true;
|
|
82
|
-
await executeHooksWithArgs.call(this, 'beforeCommand', this.options.beforeCommand, beforeHookArgs);
|
|
83
|
-
inCommandHook = false;
|
|
84
|
-
}
|
|
85
|
-
let commandResult;
|
|
86
|
-
let commandError;
|
|
87
|
-
try {
|
|
88
|
-
commandResult = await fn.apply(this, args);
|
|
89
|
-
}
|
|
90
|
-
catch (err) {
|
|
91
|
-
commandError = err;
|
|
92
|
-
}
|
|
93
|
-
if (!inCommandHook && this.options.afterCommand) {
|
|
94
|
-
inCommandHook = true;
|
|
95
|
-
const afterHookArgs = [...beforeHookArgs, commandResult, commandError];
|
|
96
|
-
await executeHooksWithArgs.call(this, 'afterCommand', this.options.afterCommand, afterHookArgs);
|
|
97
|
-
inCommandHook = false;
|
|
98
|
-
}
|
|
99
|
-
if (commandError) {
|
|
100
|
-
throw commandError;
|
|
101
|
-
}
|
|
102
|
-
return commandResult;
|
|
103
|
-
}
|
|
104
|
-
function wrapElementFn(promise, cmd, args, prevInnerArgs) {
|
|
105
|
-
return new Proxy(Promise.resolve(promise).then((ctx) => cmd.call(ctx, ...args)), {
|
|
106
|
-
get: (target, prop) => {
|
|
107
|
-
/**
|
|
108
|
-
* handle symbols, e.g. async iterators
|
|
109
|
-
*/
|
|
110
|
-
if (typeof prop === 'symbol') {
|
|
111
|
-
return () => ({
|
|
112
|
-
i: 0,
|
|
113
|
-
target,
|
|
114
|
-
async next() {
|
|
115
|
-
const elems = await this.target;
|
|
116
|
-
if (!Array.isArray(elems)) {
|
|
117
|
-
throw new Error('Can not iterate over non array');
|
|
118
|
-
}
|
|
119
|
-
if (this.i < elems.length) {
|
|
120
|
-
return { value: elems[this.i++], done: false };
|
|
121
|
-
}
|
|
122
|
-
return { done: true };
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
/**
|
|
127
|
-
* if we access an index on an element array promise, e.g.:
|
|
128
|
-
* ```js
|
|
129
|
-
* const elems = await $$('foo')[2]
|
|
130
|
-
* ```
|
|
131
|
-
*/
|
|
132
|
-
const numValue = parseInt(prop, 10);
|
|
133
|
-
if (!isNaN(numValue)) {
|
|
134
|
-
return wrapElementFn(target,
|
|
135
|
-
/**
|
|
136
|
-
* `this` is an array of WebdriverIO elements
|
|
137
|
-
*
|
|
138
|
-
* Note(Christian): types for elements are defined in the
|
|
139
|
-
* webdriverio package and not accessible here
|
|
140
|
-
*/
|
|
141
|
-
function (index) {
|
|
142
|
-
return this[index];
|
|
143
|
-
}, [prop], { prop, args });
|
|
144
|
-
}
|
|
145
|
-
/**
|
|
146
|
-
* if we call a query method on a resolve promise, e.g.:
|
|
147
|
-
* ```js
|
|
148
|
-
* await $('foo').$('bar')
|
|
149
|
-
* ```
|
|
150
|
-
*/
|
|
151
|
-
if (ELEMENT_QUERY_COMMANDS.includes(prop) || prop.endsWith('$')) {
|
|
152
|
-
// this: WebdriverIO.Element
|
|
153
|
-
return wrapCommand(prop, function (...args) {
|
|
154
|
-
return this[prop].apply(this, args);
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* if we call an array iterator function like map or forEach on an
|
|
159
|
-
* set of elements, e.g.:
|
|
160
|
-
* ```js
|
|
161
|
-
* await $('body').$('header').$$('div').map((elem) => elem.getLocation())
|
|
162
|
-
* ```
|
|
163
|
-
*/
|
|
164
|
-
if (commandName.endsWith('$$') && typeof iterators[prop] === 'function') {
|
|
165
|
-
return (mapIterator) => wrapElementFn(target, function (mapIterator) {
|
|
166
|
-
// @ts-ignore
|
|
167
|
-
return iterators[prop](this, mapIterator);
|
|
168
|
-
}, [mapIterator]);
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* allow to grab the length or other properties of fetched element set, e.g.:
|
|
172
|
-
* ```js
|
|
173
|
-
* const elemAmount = await $$('foo').length
|
|
174
|
-
* ```
|
|
175
|
-
*/
|
|
176
|
-
if (ELEMENT_PROPS.includes(prop)) {
|
|
177
|
-
return target.then((res) => res[prop]);
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* allow to resolve an chained element query, e.g.:
|
|
181
|
-
* ```js
|
|
182
|
-
* const elem = await $('foo').$('bar')
|
|
183
|
-
* console.log(elem.selector) // "bar"
|
|
184
|
-
* ```
|
|
185
|
-
*/
|
|
186
|
-
if (PROMISE_METHODS.includes(prop)) {
|
|
187
|
-
return target[prop].bind(target);
|
|
188
|
-
}
|
|
189
|
-
/**
|
|
190
|
-
* call a command on an element query, e.g.:
|
|
191
|
-
* ```js
|
|
192
|
-
* const tagName = await $('foo').$('bar').getTagName()
|
|
193
|
-
* ```
|
|
194
|
-
*/
|
|
195
|
-
return (...args) => target.then(async (elem) => {
|
|
196
|
-
if (!elem) {
|
|
197
|
-
let errMsg = 'Element could not be found';
|
|
198
|
-
const prevElem = await promise;
|
|
199
|
-
if (Array.isArray(prevElem) && prevInnerArgs && prevInnerArgs.prop === 'get') {
|
|
200
|
-
errMsg = `Index out of bounds! $$(${prevInnerArgs.args[0]}) returned only ${prevElem.length} elements.`;
|
|
201
|
-
}
|
|
202
|
-
throw new Error(errMsg);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Jasmine uses `toJSON` to parse the target object for information.
|
|
206
|
-
* Since WebdriverIo doesn't have this method on the Element object
|
|
207
|
-
* we need to mimic it here
|
|
208
|
-
*/
|
|
209
|
-
if (prop === 'toJSON') {
|
|
210
|
-
return { ELEMENT: elem.elementId };
|
|
211
|
-
}
|
|
212
|
-
/**
|
|
213
|
-
* provide a better error message than "TypeError: elem[prop] is not a function"
|
|
214
|
-
*/
|
|
215
|
-
if (typeof elem[prop] !== 'function') {
|
|
216
|
-
throw new Error(`Can't call "${prop}" on element with selector "${elem.selector}", it is not a function`);
|
|
217
|
-
}
|
|
218
|
-
return elem[prop](...args);
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
function chainElementQuery(...args) {
|
|
224
|
-
return wrapElementFn(this, wrapCommandFn, args);
|
|
225
|
-
}
|
|
226
|
-
return function (...args) {
|
|
227
|
-
/**
|
|
228
|
-
* if the command suppose to return an element, we apply `chainElementQuery` to allow
|
|
229
|
-
* chaining of these promises.
|
|
230
|
-
*/
|
|
231
|
-
const command = ELEMENT_QUERY_COMMANDS.includes(commandName) || commandName.endsWith('$')
|
|
232
|
-
? chainElementQuery
|
|
233
|
-
: ACTION_COMMANDS.includes(commandName)
|
|
234
|
-
/**
|
|
235
|
-
* actions commands are a bit special as they return their own
|
|
236
|
-
* sync interface
|
|
237
|
-
*/
|
|
238
|
-
? fn
|
|
239
|
-
: wrapCommandFn;
|
|
240
|
-
return command.apply(this, args);
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* execute test or hook asynchronously
|
|
245
|
-
*
|
|
246
|
-
* @param {Function} fn spec or hook method
|
|
247
|
-
* @param {object} retries { limit: number, attempts: number }
|
|
248
|
-
* @param {Array} args arguments passed to hook
|
|
249
|
-
* @param {number} timeout The maximum time (in milliseconds) to wait for the function to complete
|
|
250
|
-
* @return {Promise} that gets resolved once test/hook is done or was retried enough
|
|
251
|
-
*/
|
|
252
|
-
export async function executeAsync(fn, retries, args = [], timeout = 20000) {
|
|
253
|
-
this.wdioRetries = retries.attempts;
|
|
254
|
-
try {
|
|
255
|
-
/**
|
|
256
|
-
* To prevent test failures due to timeout exceptions in Jasmine from overwriting test objects with subsequent values,
|
|
257
|
-
* we reduce the overall timeout by a constant known as TIME_BUFFER. TIME_BUFFER acts as a safety margin, allowing a small
|
|
258
|
-
* window of time for an operation to complete before triggering a timeout. This approach ensures that test results are handled
|
|
259
|
-
* properly without affecting the overall test execution timing.
|
|
260
|
-
*/
|
|
261
|
-
// @ts-expect-error
|
|
262
|
-
const _timeout = (this?._runnable?._timeout || globalThis.jasmine?.DEFAULT_TIMEOUT_INTERVAL || timeout) - TIME_BUFFER;
|
|
263
|
-
/**
|
|
264
|
-
* Executes the function with specified timeout and returns the result, or throws an error if the timeout is exceeded.
|
|
265
|
-
*/
|
|
266
|
-
let done = false;
|
|
267
|
-
const result = await Promise.race([
|
|
268
|
-
fn.apply(this, args),
|
|
269
|
-
new Promise((resolve, reject) => {
|
|
270
|
-
setTimeout(() => {
|
|
271
|
-
if (done) {
|
|
272
|
-
resolve();
|
|
273
|
-
}
|
|
274
|
-
else {
|
|
275
|
-
reject(new Error('Timeout'));
|
|
276
|
-
}
|
|
277
|
-
}, _timeout);
|
|
278
|
-
})
|
|
279
|
-
]);
|
|
280
|
-
done = true;
|
|
281
|
-
if (result !== null && typeof result === 'object' && 'finally' in result && typeof result.finally === 'function') {
|
|
282
|
-
result.catch((err) => err);
|
|
283
|
-
}
|
|
284
|
-
return await result;
|
|
285
|
-
}
|
|
286
|
-
catch (err) {
|
|
287
|
-
if (retries.limit > retries.attempts) {
|
|
288
|
-
retries.attempts++;
|
|
289
|
-
return await executeAsync.call(this, fn, retries, args);
|
|
290
|
-
}
|
|
291
|
-
throw err;
|
|
292
|
-
}
|
|
293
|
-
}
|
package/build/startWebDriver.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import logger from '@wdio/logger';
|
|
2
|
-
import { definesRemoteDriver } from './utils.js';
|
|
3
|
-
const log = logger('@wdio/utils');
|
|
4
|
-
export async function startWebDriver(options) {
|
|
5
|
-
/**
|
|
6
|
-
* if any of the connection parameter are set, don't start any driver
|
|
7
|
-
*/
|
|
8
|
-
if (definesRemoteDriver(options)) {
|
|
9
|
-
log.info(`Connecting to existing driver at ${options.protocol}://${options.hostname}:${options.port}${options.path}`);
|
|
10
|
-
return;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* only import `startWebDriver` when run in Node.js
|
|
14
|
-
*/
|
|
15
|
-
if (globalThis.process) {
|
|
16
|
-
const { startWebDriver } = await import('./node/index.js');
|
|
17
|
-
return startWebDriver(options);
|
|
18
|
-
}
|
|
19
|
-
throw new Error('Please provide a valid `hostname` and `port` to start WebDriver sessions in the browser!');
|
|
20
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* notify `WDIOCLInterface` about failure in hook
|
|
3
|
-
* we need to do it this way because `beforeFn` and `afterFn` are not real hooks.
|
|
4
|
-
* Otherwise hooks failures are lost.
|
|
5
|
-
*
|
|
6
|
-
* @param {string} hookName name of the hook
|
|
7
|
-
* @param {Array} hookResults hook functions results array
|
|
8
|
-
* @param {string} cid cid
|
|
9
|
-
*/
|
|
10
|
-
export const logHookError = (hookName, hookResults = [], cid) => {
|
|
11
|
-
const result = hookResults.find(result => result instanceof Error);
|
|
12
|
-
if (typeof result === 'undefined') {
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* need to convert Error to plain object, otherwise it is lost on process.send
|
|
17
|
-
*/
|
|
18
|
-
const error = { message: result.message };
|
|
19
|
-
const content = {
|
|
20
|
-
cid: cid,
|
|
21
|
-
error: error,
|
|
22
|
-
fullTitle: `${hookName} Hook`,
|
|
23
|
-
type: 'hook',
|
|
24
|
-
state: 'fail'
|
|
25
|
-
};
|
|
26
|
-
if (globalThis.process && typeof globalThis.process.send === 'function') {
|
|
27
|
-
globalThis.process.send({
|
|
28
|
-
origin: 'reporter',
|
|
29
|
-
name: 'printFailureMessage',
|
|
30
|
-
content
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
};
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { logHookError } from './errorHandler.js';
|
|
2
|
-
import { executeHooksWithArgs, executeAsync } from '../shim.js';
|
|
3
|
-
const STACKTRACE_FILTER = [
|
|
4
|
-
'node_modules/webdriver/',
|
|
5
|
-
'node_modules/webdriverio/',
|
|
6
|
-
'node_modules/@wdio/',
|
|
7
|
-
'(internal/process/task',
|
|
8
|
-
'(node:internal/process/task'
|
|
9
|
-
];
|
|
10
|
-
/**
|
|
11
|
-
* wraps test framework spec/hook function with WebdriverIO before/after hooks
|
|
12
|
-
*
|
|
13
|
-
* @param {string} type Test/Step or Hook
|
|
14
|
-
* @param {object} spec specFn and specFnArgs
|
|
15
|
-
* @param {object} before beforeFn and beforeFnArgs
|
|
16
|
-
* @param {object} after afterFn and afterFnArgs
|
|
17
|
-
* @param {string} cid cid
|
|
18
|
-
* @param {number} repeatTest number of retries if test fails
|
|
19
|
-
* @param {string} hookName the hook name
|
|
20
|
-
* @param {number} timeout the maximum time (in milliseconds) to wait for
|
|
21
|
-
* @return {*} specFn result
|
|
22
|
-
*/
|
|
23
|
-
export const testFnWrapper = function (...args) {
|
|
24
|
-
return testFrameworkFnWrapper.call(this, { executeHooksWithArgs, executeAsync }, ...args);
|
|
25
|
-
};
|
|
26
|
-
/**
|
|
27
|
-
* wraps test framework spec/hook function with WebdriverIO before/after hooks
|
|
28
|
-
*
|
|
29
|
-
* @param {object} wrapFunctions executeHooksWithArgs, executeAsync, runSync
|
|
30
|
-
* @param {string} type Test/Step or Hook
|
|
31
|
-
* @param {object} spec specFn and specFnArgs array
|
|
32
|
-
* @param {object} before beforeFn and beforeFnArgs function
|
|
33
|
-
* @param {object} after afterFn and afterFnArgs function
|
|
34
|
-
* @param {string} cid cid
|
|
35
|
-
* @param {number} repeatTest number of retries if test fails
|
|
36
|
-
* @param {string} hookName the hook name
|
|
37
|
-
* @param {number} timeout the maximum time (in milliseconds) to wait for
|
|
38
|
-
* @return {*} specFn result
|
|
39
|
-
*/
|
|
40
|
-
export const testFrameworkFnWrapper = async function ({ executeHooksWithArgs, executeAsync }, type, { specFn, specFnArgs }, { beforeFn, beforeFnArgs }, { afterFn, afterFnArgs }, cid, repeatTest = 0, hookName, timeout) {
|
|
41
|
-
const retries = { attempts: 0, limit: repeatTest };
|
|
42
|
-
const beforeArgs = beforeFnArgs(this);
|
|
43
|
-
if (type === 'Hook' && hookName) {
|
|
44
|
-
beforeArgs.push(hookName);
|
|
45
|
-
}
|
|
46
|
-
await logHookError(`Before${type}`, await executeHooksWithArgs(`before${type}`, beforeFn, beforeArgs), cid);
|
|
47
|
-
let result;
|
|
48
|
-
let error;
|
|
49
|
-
const testStart = Date.now();
|
|
50
|
-
try {
|
|
51
|
-
result = await executeAsync.call(this, specFn, retries, specFnArgs, timeout);
|
|
52
|
-
if (globalThis._jasmineTestResult !== undefined) {
|
|
53
|
-
result = globalThis._jasmineTestResult;
|
|
54
|
-
globalThis._jasmineTestResult = undefined;
|
|
55
|
-
}
|
|
56
|
-
if (globalThis._wdioDynamicJasmineResultErrorList?.length > 0) {
|
|
57
|
-
globalThis._wdioDynamicJasmineResultErrorList[0].stack = filterStackTrace(globalThis._wdioDynamicJasmineResultErrorList[0].stack);
|
|
58
|
-
error = globalThis._wdioDynamicJasmineResultErrorList[0];
|
|
59
|
-
globalThis._wdioDynamicJasmineResultErrorList = undefined;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
catch (err) {
|
|
63
|
-
if (err.stack) {
|
|
64
|
-
err.stack = filterStackTrace(err.stack);
|
|
65
|
-
}
|
|
66
|
-
error = err;
|
|
67
|
-
}
|
|
68
|
-
const duration = Date.now() - testStart;
|
|
69
|
-
const afterArgs = afterFnArgs(this);
|
|
70
|
-
afterArgs.push({
|
|
71
|
-
retries,
|
|
72
|
-
error,
|
|
73
|
-
result,
|
|
74
|
-
duration,
|
|
75
|
-
passed: !error
|
|
76
|
-
});
|
|
77
|
-
if (type === 'Hook' && hookName) {
|
|
78
|
-
afterArgs.push(hookName);
|
|
79
|
-
}
|
|
80
|
-
await logHookError(`After${type}`, await executeHooksWithArgs(`after${type}`, afterFn, [...afterArgs]), cid);
|
|
81
|
-
if (error && !error.matcherName) {
|
|
82
|
-
throw error;
|
|
83
|
-
}
|
|
84
|
-
return result;
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* Filter out internal stacktraces. exporting to allow testing of the function
|
|
88
|
-
* @param {string} stack Stacktrace
|
|
89
|
-
* @returns {string}
|
|
90
|
-
*/
|
|
91
|
-
export const filterStackTrace = (stack) => {
|
|
92
|
-
return stack
|
|
93
|
-
.split('\n')
|
|
94
|
-
.filter(line => !STACKTRACE_FILTER.some(l => line.includes(l)))
|
|
95
|
-
.map(line => line.replace(/\?invalidateCache=(\d\.\d+|\d)/g, ''))
|
|
96
|
-
.join('\n');
|
|
97
|
-
};
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* used to wrap mocha, jasmine test frameworks functions (`it`, `beforeEach` and other)
|
|
3
|
-
* with WebdriverIO before/after Test/Hook hooks.
|
|
4
|
-
* Entrypoint is `wrapGlobalTestMethod`, other functions are exported for testing purposes.
|
|
5
|
-
*
|
|
6
|
-
* NOTE: not used by cucumber test framework. `testFnWrapper` is called directly there
|
|
7
|
-
*/
|
|
8
|
-
import { filterSpecArgs } from '../utils.js';
|
|
9
|
-
import { testFnWrapper } from './testFnWrapper.js';
|
|
10
|
-
const MOCHA_COMMANDS = ['skip', 'only'];
|
|
11
|
-
/**
|
|
12
|
-
* runs a hook and execute before/after hook
|
|
13
|
-
*
|
|
14
|
-
* @param {Function} hookFn function that was passed to the framework hook
|
|
15
|
-
* @param {Function} origFn original framework hook function
|
|
16
|
-
* @param {Function} beforeFn before hook
|
|
17
|
-
* @param {Function} beforeFnArgs function that returns args for `beforeFn`
|
|
18
|
-
* @param {Function} afterFn after hook
|
|
19
|
-
* @param {Function} afterArgsFn function that returns args for `afterFn`
|
|
20
|
-
* @param {string} cid cid
|
|
21
|
-
* @param {number} repeatTest number of retries if hook fails
|
|
22
|
-
* @return {Function} wrapped framework hook function
|
|
23
|
-
*/
|
|
24
|
-
export const runHook = function (hookFn, origFn, beforeFn, beforeFnArgs, afterFn, afterFnArgs, cid, repeatTest, timeout) {
|
|
25
|
-
const wrappedHook = function (...hookFnArgs) {
|
|
26
|
-
return testFnWrapper.call(this, 'Hook', {
|
|
27
|
-
specFn: hookFn,
|
|
28
|
-
specFnArgs: filterSpecArgs(hookFnArgs)
|
|
29
|
-
}, {
|
|
30
|
-
beforeFn,
|
|
31
|
-
beforeFnArgs
|
|
32
|
-
}, {
|
|
33
|
-
afterFn,
|
|
34
|
-
afterFnArgs
|
|
35
|
-
}, cid, repeatTest, origFn.name);
|
|
36
|
-
};
|
|
37
|
-
/**
|
|
38
|
-
* make sure Mocha grabs the correct hook function body
|
|
39
|
-
*/
|
|
40
|
-
wrappedHook.toString = () => hookFn.toString();
|
|
41
|
-
return origFn(wrappedHook, timeout);
|
|
42
|
-
};
|
|
43
|
-
/**
|
|
44
|
-
* runs a spec function (test function)
|
|
45
|
-
*
|
|
46
|
-
* @param {string} specTitle test description
|
|
47
|
-
* @param {Function} specFn test function that got passed in from the user
|
|
48
|
-
* @param {Function} origFn original framework test function
|
|
49
|
-
* @param {Function} beforeFn before hook
|
|
50
|
-
* @param {Function} beforeFnArgs function that returns args for `beforeFn`
|
|
51
|
-
* @param {Function} afterFn after hook
|
|
52
|
-
* @param {Function} afterFnArgs function that returns args for `afterFn`
|
|
53
|
-
* @param {string} cid cid
|
|
54
|
-
* @param {number} repeatTest number of retries if test fails
|
|
55
|
-
* @return {Function} wrapped test function
|
|
56
|
-
*/
|
|
57
|
-
export const runSpec = function (specTitle, specFn, origFn, beforeFn, beforeFnArgs, afterFn, afterFnArgs, cid, repeatTest, timeout) {
|
|
58
|
-
const wrappedFn = function (...specFnArgs) {
|
|
59
|
-
return testFnWrapper.call(this, 'Test', {
|
|
60
|
-
specFn,
|
|
61
|
-
specFnArgs: filterSpecArgs(specFnArgs)
|
|
62
|
-
}, {
|
|
63
|
-
beforeFn,
|
|
64
|
-
beforeFnArgs
|
|
65
|
-
}, {
|
|
66
|
-
afterFn,
|
|
67
|
-
afterFnArgs
|
|
68
|
-
}, cid, repeatTest);
|
|
69
|
-
};
|
|
70
|
-
/**
|
|
71
|
-
* make sure Mocha grabs the correct test function body
|
|
72
|
-
*/
|
|
73
|
-
wrappedFn.toString = () => specFn.toString();
|
|
74
|
-
return origFn(specTitle, wrappedFn, timeout);
|
|
75
|
-
};
|
|
76
|
-
/**
|
|
77
|
-
* wraps hooks and test function of a framework within a fiber context
|
|
78
|
-
*
|
|
79
|
-
* @param {Function} origFn original framework function
|
|
80
|
-
* @param {Boolean} isSpec whether or not origFn is a spec
|
|
81
|
-
* @param {string[]} testInterfaceFnNames command that runs specs, e.g. `it`, `it.only` or `fit`
|
|
82
|
-
* @param {Function} beforeFn before hook
|
|
83
|
-
* @param {Function} beforeFnArgs function that returns args for `beforeFn`
|
|
84
|
-
* @param {Function} afterFn after hook
|
|
85
|
-
* @param {Function} afterArgsFn function that returns args for `afterFn`
|
|
86
|
-
* @param {string} cid cid
|
|
87
|
-
* @return {Function} wrapped test/hook function
|
|
88
|
-
*/
|
|
89
|
-
export const wrapTestFunction = function (origFn, isSpec, beforeFn, beforeArgsFn, afterFn, afterArgsFn, cid) {
|
|
90
|
-
return function (...specArguments) {
|
|
91
|
-
/**
|
|
92
|
-
* Variadic arguments:
|
|
93
|
-
* [title, fn], [title], [fn]
|
|
94
|
-
* [title, fn, retryCnt], [title, retryCnt], [fn, retryCnt]
|
|
95
|
-
*/
|
|
96
|
-
let retryCnt = typeof specArguments[specArguments.length - 1] === 'number'
|
|
97
|
-
? specArguments.pop() :
|
|
98
|
-
0;
|
|
99
|
-
/**
|
|
100
|
-
* Jasmine uses a timeout value as last parameter, in this case the arguments
|
|
101
|
-
* should be [title, fn, timeout, retryCnt]
|
|
102
|
-
*/
|
|
103
|
-
// @ts-expect-error
|
|
104
|
-
let timeout = globalThis.jasmine?.DEFAULT_TIMEOUT_INTERVAL;
|
|
105
|
-
// @ts-expect-error
|
|
106
|
-
if (globalThis.jasmine) {
|
|
107
|
-
// if we have [title, fn, timeout, retryCnt]
|
|
108
|
-
if (typeof specArguments[specArguments.length - 1] === 'number') {
|
|
109
|
-
timeout = specArguments.pop();
|
|
110
|
-
// if we have [title, fn, timeout]
|
|
111
|
-
}
|
|
112
|
-
else {
|
|
113
|
-
timeout = retryCnt;
|
|
114
|
-
retryCnt = 0;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const specFn = typeof specArguments[0] === 'function' ? specArguments.shift()
|
|
118
|
-
: (typeof specArguments[1] === 'function' ? specArguments[1] : undefined);
|
|
119
|
-
const specTitle = specArguments[0];
|
|
120
|
-
if (isSpec) {
|
|
121
|
-
if (specFn) {
|
|
122
|
-
return runSpec(specTitle, specFn, origFn, beforeFn, beforeArgsFn, afterFn, afterArgsFn, cid, retryCnt, timeout);
|
|
123
|
-
}
|
|
124
|
-
/**
|
|
125
|
-
* if specFn is undefined we are dealing with a pending function
|
|
126
|
-
*/
|
|
127
|
-
return origFn(specTitle);
|
|
128
|
-
}
|
|
129
|
-
return runHook(specFn, origFn, beforeFn, beforeArgsFn, afterFn, afterArgsFn, cid, retryCnt, timeout);
|
|
130
|
-
};
|
|
131
|
-
};
|
|
132
|
-
/**
|
|
133
|
-
* Wraps global test function like `it`.
|
|
134
|
-
*
|
|
135
|
-
* The scope parameter is used in the qunit framework since all functions are bound to global.QUnit instead of global
|
|
136
|
-
*
|
|
137
|
-
* @param {boolean} isTest is `origFn` test function, otherwise hook
|
|
138
|
-
* @param {Function} beforeFn before hook
|
|
139
|
-
* @param {Function} beforeFnArgs function that returns args for `beforeFn`
|
|
140
|
-
* @param {Function} afterFn after hook
|
|
141
|
-
* @param {Function} afterArgsFn function that returns args for `afterFn`
|
|
142
|
-
* @param {string} fnName test interface command to wrap, e.g. `beforeEach`
|
|
143
|
-
* @param {string} cid cid
|
|
144
|
-
* @param {Object} scope the scope to run command from, defaults to global
|
|
145
|
-
*/
|
|
146
|
-
export const wrapGlobalTestMethod = function (isSpec, beforeFn, beforeArgsFn, afterFn, afterArgsFn, fnName, cid, scope = globalThis) {
|
|
147
|
-
const origFn = scope[fnName];
|
|
148
|
-
scope[fnName] = wrapTestFunction(origFn, isSpec, beforeFn, beforeArgsFn, afterFn, afterArgsFn, cid);
|
|
149
|
-
addMochaCommands(origFn, scope[fnName]);
|
|
150
|
-
};
|
|
151
|
-
/**
|
|
152
|
-
* support `it.skip` and `it.only` for the Mocha framework
|
|
153
|
-
* @param {Function} origFn original function
|
|
154
|
-
* @param {function} newFn wrapped function
|
|
155
|
-
*/
|
|
156
|
-
function addMochaCommands(origFn, newFn) {
|
|
157
|
-
MOCHA_COMMANDS.forEach((commandName) => {
|
|
158
|
-
if (typeof origFn[commandName] === 'function') {
|
|
159
|
-
newFn[commandName] = origFn[commandName];
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|