@wdio/jasmine-framework 7.17.0 → 7.19.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.
@@ -0,0 +1,118 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="expect-webdriverio/types/expect-webdriverio" />
3
+ import Jasmine from 'jasmine';
4
+ import { EventEmitter } from 'events';
5
+ import type { Options, Services, Capabilities } from '@wdio/types';
6
+ import type { JasmineOpts as jasmineNodeOpts, ResultHandlerPayload, FrameworkMessage, FormattedMessage } from './types';
7
+ declare type HooksArray = {
8
+ [K in keyof Required<Services.HookFunctions>]: Required<Services.HookFunctions>[K][];
9
+ };
10
+ interface WebdriverIOJasmineConfig extends Omit<Options.Testrunner, keyof HooksArray>, HooksArray {
11
+ jasmineOpts: Omit<jasmineNodeOpts, 'cleanStack'>;
12
+ }
13
+ /**
14
+ * Jasmine runner
15
+ */
16
+ declare class JasmineAdapter {
17
+ private _cid;
18
+ private _config;
19
+ private _specs;
20
+ private _capabilities;
21
+ private _jasmineOpts;
22
+ private _reporter;
23
+ private _totalTests;
24
+ private _hasTests;
25
+ private _lastTest?;
26
+ private _lastSpec?;
27
+ private _jrunner?;
28
+ constructor(_cid: string, _config: WebdriverIOJasmineConfig, _specs: string[], _capabilities: Capabilities.RemoteCapabilities, reporter: EventEmitter);
29
+ init(): Promise<this>;
30
+ _loadFiles(): void;
31
+ _grep(suite: jasmine.Suite): void;
32
+ hasTests(): boolean;
33
+ run(): Promise<unknown>;
34
+ customSpecFilter(spec: jasmine.Spec): boolean;
35
+ /**
36
+ * Hooks which are added as true Jasmine hooks need to call done() to notify async
37
+ */
38
+ wrapHook(hookName: keyof Services.HookFunctions): () => Promise<void | unknown[]>;
39
+ prepareMessage(hookName: keyof Services.HookFunctions): FormattedMessage;
40
+ formatMessage(params: FrameworkMessage): FormattedMessage;
41
+ getExpectationResultHandler(jasmine: jasmine.Jasmine): any;
42
+ expectationResultHandler(origHandler: Function): (this: jasmine.Spec, passed: boolean, data: ResultHandlerPayload) => any;
43
+ }
44
+ declare const adapterFactory: {
45
+ init?: Function;
46
+ };
47
+ export default adapterFactory;
48
+ export { JasmineAdapter, adapterFactory };
49
+ export * from './types';
50
+ declare type jasmine = typeof Jasmine;
51
+ declare global {
52
+ /**
53
+ * Define a single spec. A spec should contain one or more expectations that test the state of the code.
54
+ * A spec whose expectations all succeed will be passing and a spec with any failures will fail.
55
+ * @param expectation Textual description of what this spec is checking
56
+ * @param assertion Function that contains the code of your test. If not provided the test will be pending.
57
+ * @param timeout Custom timeout for an async spec.
58
+ * @param retries Custom retry count for this single spec (WebdriverIO specific)
59
+ */
60
+ function it(expectation: string, assertion?: jasmine.ImplementationCallback, timeout?: number, retries?: number): void;
61
+ /**
62
+ * A focused `it`. If suites or specs are focused, only those that are focused will be executed.
63
+ * @param expectation Textual description of what this spec is checking
64
+ * @param assertion Function that contains the code of your test. If not provided the test will be pending.
65
+ * @param timeout Custom timeout for an async spec.
66
+ * @param retries Custom retry count for this single spec (WebdriverIO specific)
67
+ */
68
+ function fit(expectation: string, assertion?: jasmine.ImplementationCallback, timeout?: number, retries?: number): void;
69
+ /**
70
+ * A temporarily disabled `it`. The spec will report as pending and will not be executed.
71
+ * @param expectation Textual description of what this spec is checking
72
+ * @param assertion Function that contains the code of your test. If not provided the test will be pending.
73
+ * @param timeout Custom timeout for an async spec.
74
+ * @param retries Custom retry count for this single spec (WebdriverIO specific)
75
+ */
76
+ function xit(expectation: string, assertion?: jasmine.ImplementationCallback, timeout?: number, retries?: number): void;
77
+ /**
78
+ * Run some shared setup before each of the specs in the describe in which it is called.
79
+ * @param action Function that contains the code to setup your specs.
80
+ * @param timeout Custom timeout for an async beforeEach.
81
+ * @param retries Custom retry count for this single hook (WebdriverIO specific)
82
+ */
83
+ function beforeEach(action: jasmine.ImplementationCallback, timeout?: number, retries?: number): void;
84
+ /**
85
+ * Run some shared teardown after each of the specs in the describe in which it is called.
86
+ * @param action Function that contains the code to teardown your specs.
87
+ * @param timeout Custom timeout for an async afterEach.
88
+ * @param retries Custom retry count for this single hook (WebdriverIO specific)
89
+ */
90
+ function afterEach(action: jasmine.ImplementationCallback, timeout?: number, retries?: number): void;
91
+ /**
92
+ * Run some shared setup once before all of the specs in the describe are run.
93
+ * Note: Be careful, sharing the setup from a beforeAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
94
+ * @param action Function that contains the code to setup your specs.
95
+ * @param timeout Custom timeout for an async beforeAll.
96
+ * @param retries Custom retry count for this single hook (WebdriverIO specific)
97
+ */
98
+ function beforeAll(action: jasmine.ImplementationCallback, timeout?: number, retries?: number): void;
99
+ /**
100
+ * Run some shared teardown once before all of the specs in the describe are run.
101
+ * Note: Be careful, sharing the teardown from a afterAll makes it easy to accidentally leak state between your specs so that they erroneously pass or fail.
102
+ * @param action Function that contains the code to teardown your specs.
103
+ * @param timeout Custom timeout for an async afterAll
104
+ * @param retries Custom retry count for this single hook (WebdriverIO specific)
105
+ */
106
+ function afterAll(action: jasmine.ImplementationCallback, timeout?: number, retries?: number): void;
107
+ namespace WebdriverIO {
108
+ interface JasmineOpts extends jasmineNodeOpts {
109
+ }
110
+ }
111
+ namespace jasmine {
112
+ interface Matchers<T> extends ExpectWebdriverIO.Matchers<any, T> {
113
+ }
114
+ interface AsyncMatchers<T, U> extends ExpectWebdriverIO.Matchers<Promise<void>, T> {
115
+ }
116
+ }
117
+ }
118
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/build/index.js ADDED
@@ -0,0 +1,320 @@
1
+ "use strict";
2
+ /// <reference types="expect-webdriverio/jasmine" />
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
16
+ };
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports.adapterFactory = exports.JasmineAdapter = void 0;
22
+ const jasmine_1 = __importDefault(require("jasmine"));
23
+ const utils_1 = require("@wdio/utils");
24
+ const logger_1 = __importDefault(require("@wdio/logger"));
25
+ const reporter_1 = __importDefault(require("./reporter"));
26
+ const INTERFACES = {
27
+ bdd: ['beforeAll', 'beforeEach', 'it', 'xit', 'fit', 'afterEach', 'afterAll']
28
+ };
29
+ const TEST_INTERFACES = ['it', 'fit', 'xit'];
30
+ const NOOP = function noop() { };
31
+ const DEFAULT_TIMEOUT_INTERVAL = 60000;
32
+ const log = (0, logger_1.default)('@wdio/jasmine-framework');
33
+ /**
34
+ * Jasmine runner
35
+ */
36
+ class JasmineAdapter {
37
+ constructor(_cid, _config, _specs, _capabilities, reporter) {
38
+ this._cid = _cid;
39
+ this._config = _config;
40
+ this._specs = _specs;
41
+ this._capabilities = _capabilities;
42
+ this._totalTests = 0;
43
+ this._hasTests = true;
44
+ this._jasmineOpts = Object.assign({
45
+ cleanStack: true
46
+ }, (this._config.jasmineOpts ||
47
+ // @ts-expect-error legacy option
48
+ this._config.jasmineNodeOpts));
49
+ this._reporter = new reporter_1.default(reporter, {
50
+ cid: this._cid,
51
+ specs: this._specs,
52
+ cleanStack: this._jasmineOpts.cleanStack
53
+ });
54
+ this._hasTests = true;
55
+ }
56
+ async init() {
57
+ const self = this;
58
+ this._jrunner = new jasmine_1.default({});
59
+ const { jasmine } = this._jrunner;
60
+ // @ts-ignore outdated
61
+ const jasmineEnv = jasmine.getEnv();
62
+ this._jrunner.projectBaseDir = '';
63
+ // @ts-ignore outdated
64
+ this._jrunner.specDir = '';
65
+ this._jrunner.addSpecFiles(this._specs);
66
+ // @ts-ignore only way to hack timeout into jasmine
67
+ jasmine.DEFAULT_TIMEOUT_INTERVAL = this._jasmineOpts.defaultTimeoutInterval || DEFAULT_TIMEOUT_INTERVAL;
68
+ jasmineEnv.addReporter(this._reporter);
69
+ /**
70
+ * Filter specs to run based on jasmineOpts.grep and jasmineOpts.invert
71
+ */
72
+ jasmineEnv.configure({
73
+ specFilter: this._jasmineOpts.specFilter || this.customSpecFilter.bind(this),
74
+ stopOnSpecFailure: Boolean(this._jasmineOpts.stopOnSpecFailure),
75
+ failSpecWithNoExpectations: Boolean(this._jasmineOpts.failSpecWithNoExpectations),
76
+ failFast: this._jasmineOpts.failFast,
77
+ random: Boolean(this._jasmineOpts.random),
78
+ seed: Boolean(this._jasmineOpts.seed),
79
+ oneFailurePerSpec: Boolean(
80
+ // depcrecated old property
81
+ this._jasmineOpts.stopSpecOnExpectationFailure ||
82
+ this._jasmineOpts.oneFailurePerSpec)
83
+ });
84
+ /**
85
+ * enable expectHandler
86
+ */
87
+ jasmine.Spec.prototype.addExpectationResult = this.getExpectationResultHandler(jasmine);
88
+ const hookArgsFn = (context) => [{ ...(self._lastTest || {}) }, context];
89
+ const emitHookEvent = (fnName, eventType) => (_test, _context, { error } = {}) => {
90
+ const title = `"${fnName === 'beforeAll' ? 'before' : 'after'} all" hook`;
91
+ const hook = {
92
+ id: '',
93
+ start: new Date(),
94
+ type: 'hook',
95
+ description: title,
96
+ fullName: title,
97
+ duration: null,
98
+ properties: {},
99
+ passedExpectations: [],
100
+ pendingReason: '',
101
+ failedExpectations: [],
102
+ deprecationWarnings: [],
103
+ status: '',
104
+ ...(error ? { error } : {})
105
+ };
106
+ this._reporter.emit('hook:' + eventType, hook);
107
+ };
108
+ /**
109
+ * wrap commands
110
+ */
111
+ INTERFACES['bdd'].forEach((fnName) => {
112
+ const isTest = TEST_INTERFACES.includes(fnName);
113
+ const beforeHook = [...this._config.beforeHook];
114
+ const afterHook = [...this._config.afterHook];
115
+ /**
116
+ * add beforeAll and afterAll hooks to reporter
117
+ */
118
+ if (fnName.includes('All')) {
119
+ beforeHook.push(emitHookEvent(fnName, 'start'));
120
+ afterHook.push(emitHookEvent(fnName, 'end'));
121
+ }
122
+ (0, utils_1.runTestInFiberContext)(isTest, isTest ? this._config.beforeTest : beforeHook, hookArgsFn, isTest ? this._config.afterTest : afterHook, hookArgsFn, fnName, this._cid);
123
+ });
124
+ /**
125
+ * for a clean stdout we need to avoid that Jasmine initialises the
126
+ * default reporter
127
+ */
128
+ jasmine_1.default.prototype.configureDefaultReporter = NOOP;
129
+ /**
130
+ * wrap Suite and Spec prototypes to get access to their data
131
+ */
132
+ // @ts-ignore
133
+ let beforeAllMock = jasmine.Suite.prototype.beforeAll;
134
+ // @ts-ignore
135
+ jasmine.Suite.prototype.beforeAll = function (...args) {
136
+ self._lastSpec = this.result;
137
+ beforeAllMock.apply(this, args);
138
+ };
139
+ let executeMock = jasmine.Spec.prototype.execute;
140
+ jasmine.Spec.prototype.execute = function (...args) {
141
+ self._lastTest = this.result;
142
+ // @ts-ignore overwrite existing type
143
+ self._lastTest.start = new Date().getTime();
144
+ executeMock.apply(this, args);
145
+ };
146
+ this._loadFiles();
147
+ /**
148
+ * import and set options for `expect-webdriverio` assertion lib once
149
+ * the framework was initiated so that it can detect the environment
150
+ */
151
+ const { setOptions } = require('expect-webdriverio');
152
+ setOptions({
153
+ wait: this._config.waitforTimeout,
154
+ interval: this._config.waitforInterval, // interval between attempts
155
+ });
156
+ return this;
157
+ }
158
+ _loadFiles() {
159
+ if (!this._jrunner) {
160
+ throw new Error('Jasmine not initiate yet');
161
+ }
162
+ try {
163
+ if (Array.isArray(this._jasmineOpts.requires)) {
164
+ // @ts-ignore outdated types
165
+ this._jrunner.addRequires(this._jasmineOpts.requires);
166
+ }
167
+ if (Array.isArray(this._jasmineOpts.helpers)) {
168
+ // @ts-ignore outdated types
169
+ this._jrunner.addHelperFiles(this._jasmineOpts.helpers);
170
+ }
171
+ // @ts-ignore outdated types
172
+ this._jrunner.loadRequires();
173
+ this._jrunner.loadHelpers();
174
+ this._jrunner.loadSpecs();
175
+ // @ts-ignore outdated types
176
+ this._grep(this._jrunner.env.topSuite());
177
+ this._hasTests = this._totalTests > 0;
178
+ }
179
+ catch (err) {
180
+ log.warn('Unable to load spec files quite likely because they rely on `browser` object that is not fully initialised.\n' +
181
+ '`browser` object has only `capabilities` and some flags like `isMobile`.\n' +
182
+ 'Helper files that use other `browser` commands have to be moved to `before` hook.\n' +
183
+ `Spec file(s): ${this._specs.join(',')}\n`, 'Error: ', err);
184
+ }
185
+ }
186
+ _grep(suite) {
187
+ // @ts-ignore outdated types
188
+ suite.children.forEach((child) => {
189
+ if (Array.isArray(child.children)) {
190
+ return this._grep(child);
191
+ }
192
+ if (this.customSpecFilter(child)) {
193
+ this._totalTests++;
194
+ }
195
+ });
196
+ }
197
+ hasTests() {
198
+ return this._hasTests;
199
+ }
200
+ async run() {
201
+ const result = await new Promise((resolve, reject) => {
202
+ if (!this._jrunner) {
203
+ return reject(new Error('Jasmine not initiate yet'));
204
+ }
205
+ // @ts-expect-error
206
+ this._jrunner.env.beforeAll(this.wrapHook('beforeSuite'));
207
+ // @ts-expect-error
208
+ this._jrunner.env.afterAll(this.wrapHook('afterSuite'));
209
+ this._jrunner.onComplete(() => resolve(this._reporter.getFailedCount()));
210
+ this._jrunner.execute();
211
+ });
212
+ await (0, utils_1.executeHooksWithArgs)('after', this._config.after, [result, this._capabilities, this._specs]);
213
+ return result;
214
+ }
215
+ customSpecFilter(spec) {
216
+ const { grep, invertGrep } = this._jasmineOpts;
217
+ const grepMatch = !grep || spec.getFullName().match(new RegExp(grep)) !== null;
218
+ if (grepMatch === Boolean(invertGrep)) {
219
+ // @ts-ignore outdated types
220
+ spec.pend('grep');
221
+ return false;
222
+ }
223
+ return true;
224
+ }
225
+ /**
226
+ * Hooks which are added as true Jasmine hooks need to call done() to notify async
227
+ */
228
+ wrapHook(hookName) {
229
+ return () => (0, utils_1.executeHooksWithArgs)(hookName, this._config[hookName], [this.prepareMessage(hookName)]).catch((e) => {
230
+ log.info(`Error in ${hookName} hook: ${e.stack.slice(7)}`);
231
+ });
232
+ }
233
+ prepareMessage(hookName) {
234
+ var _a, _b;
235
+ const params = { type: hookName };
236
+ switch (hookName) {
237
+ case 'beforeSuite':
238
+ case 'afterSuite':
239
+ params.payload = Object.assign({
240
+ file: (_a = this._jrunner) === null || _a === void 0 ? void 0 : _a.specFiles[0]
241
+ }, this._lastSpec);
242
+ break;
243
+ case 'beforeTest':
244
+ case 'afterTest':
245
+ params.payload = Object.assign({
246
+ file: (_b = this._jrunner) === null || _b === void 0 ? void 0 : _b.specFiles[0]
247
+ }, this._lastTest);
248
+ break;
249
+ }
250
+ return this.formatMessage(params);
251
+ }
252
+ formatMessage(params) {
253
+ var _a;
254
+ let message = {
255
+ type: params.type
256
+ };
257
+ if (params.payload) {
258
+ message.title = params.payload.description;
259
+ message.fullName = params.payload.fullName || null;
260
+ message.file = params.payload.file;
261
+ if (params.payload.failedExpectations && params.payload.failedExpectations.length) {
262
+ message.errors = params.payload.failedExpectations;
263
+ message.error = params.payload.failedExpectations[0];
264
+ }
265
+ if (params.payload.id && params.payload.id.startsWith('spec')) {
266
+ message.parent = (_a = this._lastSpec) === null || _a === void 0 ? void 0 : _a.description;
267
+ message.passed = params.payload.failedExpectations.length === 0;
268
+ }
269
+ if (params.type === 'afterTest') {
270
+ message.duration = new Date().getTime() - params.payload.start;
271
+ }
272
+ if (typeof params.payload.duration === 'number') {
273
+ message.duration = params.payload.duration;
274
+ }
275
+ }
276
+ return message;
277
+ }
278
+ getExpectationResultHandler(jasmine) {
279
+ let { expectationResultHandler } = this._jasmineOpts;
280
+ const origHandler = jasmine.Spec.prototype.addExpectationResult;
281
+ if (typeof expectationResultHandler !== 'function') {
282
+ return origHandler;
283
+ }
284
+ return this.expectationResultHandler(origHandler);
285
+ }
286
+ expectationResultHandler(origHandler) {
287
+ const { expectationResultHandler } = this._jasmineOpts;
288
+ return function (passed, data) {
289
+ try {
290
+ expectationResultHandler.call(this, passed, data);
291
+ }
292
+ catch (e) {
293
+ /**
294
+ * propagate expectationResultHandler error if actual assertion passed
295
+ * but the custom handler decides to throw
296
+ */
297
+ if (passed) {
298
+ passed = false;
299
+ data = {
300
+ passed,
301
+ message: 'expectationResultHandlerError: ' + e.message,
302
+ error: e
303
+ };
304
+ }
305
+ }
306
+ return origHandler.call(this, passed, data);
307
+ };
308
+ }
309
+ }
310
+ exports.JasmineAdapter = JasmineAdapter;
311
+ const adapterFactory = {};
312
+ exports.adapterFactory = adapterFactory;
313
+ adapterFactory.init = async function (...args) {
314
+ // @ts-ignore pass along parameters
315
+ const adapter = new JasmineAdapter(...args);
316
+ const instance = await adapter.init();
317
+ return instance;
318
+ };
319
+ exports.default = adapterFactory;
320
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,24 @@
1
+ /// <reference types="node" />
2
+ import type { EventEmitter } from 'events';
3
+ import type { ReporterOptions, TestEvent, SuiteEvent } from './types';
4
+ export default class JasmineReporter {
5
+ private _reporter;
6
+ startedSuite?: SuiteEvent;
7
+ private _cid;
8
+ private _specs;
9
+ private _shouldCleanStack;
10
+ private _parent;
11
+ private _failedCount;
12
+ private _suiteStart;
13
+ private _testStart;
14
+ constructor(_reporter: EventEmitter, params: ReporterOptions);
15
+ suiteStarted(suite: jasmine.SuiteResult): void;
16
+ specStarted(test: jasmine.SpecResult): void;
17
+ specDone(test: jasmine.SpecResult): void;
18
+ suiteDone(suite: jasmine.SuiteResult): void;
19
+ emit(event: string, payload: SuiteEvent | TestEvent): void;
20
+ getFailedCount(): number;
21
+ getUniqueIdentifier(target: Pick<TestEvent, 'description' | 'id'>): string;
22
+ cleanStack(error: jasmine.FailedExpectation): jasmine.FailedExpectation;
23
+ }
24
+ //# sourceMappingURL=reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAA;AAE1C,OAAO,KAAK,EAAE,eAAe,EAAe,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAMlF,MAAM,CAAC,OAAO,OAAO,eAAe;IAY5B,OAAO,CAAC,SAAS;IAXd,YAAY,CAAC,EAAE,UAAU,CAAA;IAEhC,OAAO,CAAC,IAAI,CAAQ;IACpB,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,OAAO,CAAoB;IACnC,OAAO,CAAC,YAAY,CAAI;IACxB,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,UAAU,CAAa;gBAGnB,SAAS,EAAE,YAAY,EAC/B,MAAM,EAAE,eAAe;IAO3B,YAAY,CAAE,KAAK,EAAE,OAAO,CAAC,WAAW;IAwBxC,WAAW,CAAE,IAAI,EAAE,OAAO,CAAC,UAAU;IAyBrC,QAAQ,CAAE,IAAI,EAAE,OAAO,CAAC,UAAU;IAiClC,SAAS,CAAE,KAAK,EAAE,OAAO,CAAC,WAAW;IA8CrC,IAAI,CAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,SAAS;IAwBpD,cAAc;IAId,mBAAmB,CAAE,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAIlE,UAAU,CAAE,KAAK,EAAE,OAAO,CAAC,iBAAiB;CAU/C"}
@@ -0,0 +1,170 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const logger_1 = __importDefault(require("@wdio/logger"));
7
+ const log = (0, logger_1.default)('@wdio/jasmine-framework');
8
+ const STACKTRACE_FILTER = /(node_modules(\/|\\)(\w+)*|@wdio\/sync\/(build|src)|- - - - -)/g;
9
+ class JasmineReporter {
10
+ constructor(_reporter, params) {
11
+ this._reporter = _reporter;
12
+ this._parent = [];
13
+ this._failedCount = 0;
14
+ this._suiteStart = new Date();
15
+ this._testStart = new Date();
16
+ this._cid = params.cid;
17
+ this._specs = params.specs;
18
+ this._shouldCleanStack = typeof params.cleanStack === 'boolean' ? params.cleanStack : true;
19
+ }
20
+ suiteStarted(suite) {
21
+ this._suiteStart = new Date();
22
+ const newSuite = {
23
+ type: 'suite',
24
+ start: this._suiteStart,
25
+ ...suite
26
+ };
27
+ this.startedSuite = newSuite;
28
+ let fullName = suite.description;
29
+ for (const parent of this._parent.reverse()) {
30
+ fullName = parent.description + '.' + fullName;
31
+ }
32
+ newSuite.fullName = fullName;
33
+ this.emit('suite:start', newSuite);
34
+ this._parent.push({
35
+ description: suite.description,
36
+ id: suite.id,
37
+ tests: 0
38
+ });
39
+ }
40
+ specStarted(test) {
41
+ this._testStart = new Date();
42
+ const newTest = {
43
+ type: 'test',
44
+ start: this._testStart,
45
+ ...test
46
+ };
47
+ const parentSuite = this._parent[this._parent.length - 1];
48
+ /**
49
+ * if jasmine test has no root describe block, create one
50
+ */
51
+ if (!parentSuite) {
52
+ log.warn('No root suite was defined! This can cause reporters to malfunction. ' +
53
+ 'Please always start a spec file with describe("...", () => { ... }).');
54
+ }
55
+ else {
56
+ parentSuite.tests++;
57
+ }
58
+ this.emit('test:start', newTest);
59
+ }
60
+ specDone(test) {
61
+ const newTest = {
62
+ ...test,
63
+ start: this._testStart,
64
+ type: 'test',
65
+ duration: Date.now() - this._testStart.getTime()
66
+ };
67
+ /**
68
+ * excluded tests are treated as pending tests
69
+ */
70
+ if (test.status === 'excluded') {
71
+ newTest.status = 'pending';
72
+ }
73
+ if (test.failedExpectations && test.failedExpectations.length) {
74
+ let errors = test.failedExpectations;
75
+ if (this._shouldCleanStack) {
76
+ errors = test.failedExpectations.map(x => this.cleanStack(x));
77
+ }
78
+ newTest.errors = errors;
79
+ // We maintain the single error property for backwards compatibility with reporters
80
+ // However, any reporter wanting to make use of Jasmine's 'soft assertion' type expects
81
+ // should default to looking at 'errors' if they are available
82
+ newTest.error = errors[0];
83
+ }
84
+ const e = 'test:' + (newTest.status ? newTest.status.replace(/ed/, '') : 'unknown');
85
+ this.emit(e, newTest);
86
+ this._failedCount += test.status === 'failed' ? 1 : 0;
87
+ this.emit('test:end', newTest);
88
+ }
89
+ suiteDone(suite) {
90
+ const parentSuite = this._parent[this._parent.length - 1];
91
+ const newSuite = {
92
+ ...suite,
93
+ type: 'suite',
94
+ start: this._suiteStart,
95
+ duration: Date.now() - this._suiteStart.getTime()
96
+ };
97
+ /**
98
+ * in case there is a runtime error within one of the specs
99
+ * create an empty test to attach the error to it
100
+ */
101
+ if (parentSuite.tests === 0 && suite.failedExpectations && suite.failedExpectations.length) {
102
+ const id = 'spec' + Math.random();
103
+ this.specStarted({
104
+ id,
105
+ description: '<unknown test>',
106
+ fullName: '<unknown test>',
107
+ duration: null,
108
+ properties: {},
109
+ failedExpectations: [],
110
+ deprecationWarnings: [],
111
+ passedExpectations: [],
112
+ status: 'unknown',
113
+ pendingReason: ''
114
+ });
115
+ this.specDone({
116
+ id,
117
+ description: '<unknown test>',
118
+ fullName: '<unknown test>',
119
+ failedExpectations: suite.failedExpectations,
120
+ deprecationWarnings: [],
121
+ passedExpectations: [],
122
+ status: 'failed',
123
+ duration: null,
124
+ properties: {},
125
+ pendingReason: ''
126
+ });
127
+ }
128
+ this._parent.pop();
129
+ this.emit('suite:end', newSuite);
130
+ delete this.startedSuite;
131
+ }
132
+ emit(event, payload) {
133
+ let message = {
134
+ cid: this._cid,
135
+ uid: this.getUniqueIdentifier(payload),
136
+ event: event,
137
+ title: payload.description,
138
+ fullTitle: payload.fullName,
139
+ pending: payload.status === 'pending',
140
+ pendingReason: payload.pendingReason,
141
+ parent: this._parent.length ? this.getUniqueIdentifier(this._parent[this._parent.length - 1]) : null,
142
+ type: payload.type,
143
+ // We maintain the single error property for backwards compatibility with reporters
144
+ // However, any reporter wanting to make use of Jasmine's 'soft assertion' type expects
145
+ // should default to looking at 'errors' if they are available
146
+ error: payload.error,
147
+ errors: payload.errors,
148
+ duration: payload.duration || 0,
149
+ specs: this._specs,
150
+ start: payload.start,
151
+ };
152
+ this._reporter.emit(event, message);
153
+ }
154
+ getFailedCount() {
155
+ return this._failedCount;
156
+ }
157
+ getUniqueIdentifier(target) {
158
+ return target.description + target.id;
159
+ }
160
+ cleanStack(error) {
161
+ if (!error.stack) {
162
+ return error;
163
+ }
164
+ let stack = error.stack.split('\n');
165
+ stack = stack.filter((line) => !line.match(STACKTRACE_FILTER));
166
+ error.stack = stack.join('\n');
167
+ return error;
168
+ }
169
+ }
170
+ exports.default = JasmineReporter;
@@ -0,0 +1,139 @@
1
+ export interface ReporterOptions {
2
+ cid: string;
3
+ specs: string[];
4
+ cleanStack?: boolean;
5
+ }
6
+ export interface ParentSuite {
7
+ description: string;
8
+ id: string;
9
+ tests: number;
10
+ }
11
+ export interface SuiteEvent extends jasmine.SuiteResult {
12
+ type: 'suite';
13
+ start: Date;
14
+ duration: number | null;
15
+ errors?: jasmine.FailedExpectation[];
16
+ error?: jasmine.FailedExpectation;
17
+ }
18
+ export interface TestEvent extends jasmine.SpecResult {
19
+ type: 'test' | 'hook';
20
+ start: Date;
21
+ duration: number | null;
22
+ errors?: jasmine.FailedExpectation[];
23
+ error?: jasmine.FailedExpectation;
24
+ }
25
+ export interface ResultHandlerPayload {
26
+ passed: boolean;
27
+ message?: string;
28
+ error?: Error;
29
+ }
30
+ export interface FrameworkMessage {
31
+ type: string;
32
+ payload?: any;
33
+ err?: jasmine.FailedExpectation;
34
+ }
35
+ export interface FormattedMessage {
36
+ type: string;
37
+ cid?: string;
38
+ specs?: string[];
39
+ uid?: string;
40
+ title?: string;
41
+ parent?: string;
42
+ fullTitle?: string;
43
+ pending?: boolean;
44
+ passed?: boolean;
45
+ file?: string;
46
+ duration?: number;
47
+ currentTest?: string;
48
+ error?: jasmine.FailedExpectation;
49
+ context?: any;
50
+ /**
51
+ * jasmine specific
52
+ */
53
+ fullName?: string;
54
+ errors?: jasmine.FailedExpectation[];
55
+ }
56
+ export interface JasmineOpts {
57
+ /**
58
+ * Default Timeout Interval for Jasmine operations.
59
+ * @default 60000
60
+ */
61
+ defaultTimeoutInterval?: number;
62
+ /**
63
+ * Array of filepaths (and globs) relative to spec_dir to include before jasmine specs.
64
+ * @default []
65
+ */
66
+ helpers?: string[];
67
+ /**
68
+ * The `requires` option is useful when you want to add or extend some basic functionality.
69
+ * @default []
70
+ */
71
+ requires?: string[];
72
+ /**
73
+ * Whether to randomize spec execution order.
74
+ * @default true
75
+ */
76
+ random?: boolean;
77
+ /**
78
+ * Seed to use as the basis of randomization. Null causes the seed to be determined randomly at the start of execution.
79
+ * @since v3.3.0
80
+ */
81
+ seed?: Function;
82
+ /**
83
+ * Whether to stop execution of the suite after the first spec failure.
84
+ * @default false
85
+ * @since v3.3.0
86
+ * @deprecated Use the `stopOnSpecFailure` config property instead.
87
+ */
88
+ failFast?: boolean;
89
+ /**
90
+ * Whether to fail the spec if it ran no expectations. By default a spec that ran no expectations is reported as passed.
91
+ * Setting this to true will report such spec as a failure.
92
+ * @default false
93
+ * @since v3.5.0
94
+ */
95
+ failSpecWithNoExpectations?: boolean;
96
+ /**
97
+ * Whether to cause specs to only have one expectation failure.
98
+ * @default false
99
+ * @since v3.3.0
100
+ */
101
+ oneFailurePerSpec?: boolean;
102
+ /**
103
+ * Function to use to filter specs.
104
+ * @since v3.3.0
105
+ */
106
+ specFilter?: () => boolean;
107
+ /**
108
+ * Only run tests matching this string or regexp. (Only applicable if no custom `specFilter` function is set)
109
+ */
110
+ grep?: string | RegExp;
111
+ /**
112
+ * If true it inverts the matching tests and only runs tests that don't match with the expression used in `grep`.
113
+ * (Only applicable if no custom `specFilter` function is set)
114
+ * @default false
115
+ */
116
+ invertGrep?: boolean;
117
+ /**
118
+ * Clean up stack trace and remove all traces of node module packages.
119
+ * @default false
120
+ */
121
+ cleanStack?: boolean;
122
+ /**
123
+ * Stops test suite (`describe`) execution on first spec (`it`) failure (other suites continue running)
124
+ * @default false
125
+ */
126
+ stopOnSpecFailure?: boolean;
127
+ /**
128
+ * Stops a spec (`it`) execution on a first expectation failure (other specs continue running)
129
+ * @default false
130
+ */
131
+ stopSpecOnExpectationFailure?: boolean;
132
+ /**
133
+ * The Jasmine framework allows it to intercept each assertion in order to log the state of the application
134
+ * or website depending on the result. For example it is pretty handy to take a screenshot every time
135
+ * an assertion fails.
136
+ */
137
+ expectationResultHandler?: (passed: boolean, data: ResultHandlerPayload) => void;
138
+ }
139
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/build/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@wdio/jasmine-framework",
3
- "version": "7.17.0",
3
+ "version": "7.19.0",
4
4
  "description": "A WebdriverIO plugin. Adapter for Jasmine testing framework.",
5
- "author": "Christian Bromann <christian@saucelabs.com>",
5
+ "author": "Christian Bromann <mail@bromann.dev>",
6
6
  "homepage": "https://github.com/webdriverio/webdriverio/tree/main/packages/wdio-jasmine-framework",
7
7
  "license": "MIT",
8
8
  "main": "./build/index",
@@ -27,9 +27,9 @@
27
27
  "dependencies": {
28
28
  "@types/jasmine": "3.10.3",
29
29
  "@types/node": "^17.0.4",
30
- "@wdio/logger": "7.16.0",
31
- "@wdio/types": "7.16.14",
32
- "@wdio/utils": "7.17.0",
30
+ "@wdio/logger": "7.19.0",
31
+ "@wdio/types": "7.19.0",
32
+ "@wdio/utils": "7.19.0",
33
33
  "expect-webdriverio": "^3.0.0",
34
34
  "jasmine": "3.10.0"
35
35
  },
@@ -40,5 +40,5 @@
40
40
  "access": "public"
41
41
  },
42
42
  "types": "./build/index.d.ts",
43
- "gitHead": "e18d2cde6ff979758830bdff4c3bc82ca9818b24"
43
+ "gitHead": "a17ba0237dcbafa8f0215534c64ff9634caf4b43"
44
44
  }