@wdio/browserstack-service 7.35.0 → 7.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/accessibility-handler.d.ts +5 -3
- package/build/accessibility-handler.d.ts.map +1 -1
- package/build/accessibility-handler.js +61 -30
- package/build/bstackLogger.d.ts +14 -0
- package/build/bstackLogger.d.ts.map +1 -0
- package/build/bstackLogger.js +52 -0
- package/build/cleanup.d.ts +6 -2
- package/build/cleanup.d.ts.map +1 -1
- package/build/cleanup.js +104 -12
- package/build/config.d.ts +23 -0
- package/build/config.d.ts.map +1 -0
- package/build/config.js +32 -0
- package/build/constants.d.ts +17 -0
- package/build/constants.d.ts.map +1 -1
- package/build/constants.js +34 -1
- package/build/crash-reporter.js +1 -1
- package/build/data-store.d.ts +3 -0
- package/build/data-store.d.ts.map +1 -0
- package/build/data-store.js +49 -0
- package/build/exitHandler.d.ts +4 -0
- package/build/exitHandler.d.ts.map +1 -0
- package/build/exitHandler.js +37 -0
- package/build/insights-handler.d.ts +7 -12
- package/build/insights-handler.d.ts.map +1 -1
- package/build/insights-handler.js +49 -107
- package/build/instrumentation/funnelInstrumentation.d.ts +6 -0
- package/build/instrumentation/funnelInstrumentation.d.ts.map +1 -0
- package/build/instrumentation/funnelInstrumentation.js +127 -0
- package/build/launcher.d.ts +1 -2
- package/build/launcher.d.ts.map +1 -1
- package/build/launcher.js +21 -20
- package/build/reporter.d.ts +5 -3
- package/build/reporter.d.ts.map +1 -1
- package/build/reporter.js +17 -23
- package/build/request-handler.d.ts +5 -13
- package/build/request-handler.d.ts.map +1 -1
- package/build/request-handler.js +27 -48
- package/build/scripts/accessibility-scripts.d.ts +26 -0
- package/build/scripts/accessibility-scripts.d.ts.map +1 -0
- package/build/scripts/accessibility-scripts.js +67 -0
- package/build/service.d.ts +1 -0
- package/build/service.d.ts.map +1 -1
- package/build/service.js +24 -16
- package/build/testOps/featureStats.d.ts +45 -0
- package/build/testOps/featureStats.d.ts.map +1 -0
- package/build/testOps/featureStats.js +120 -0
- package/build/testOps/featureUsage.d.ts +22 -0
- package/build/testOps/featureUsage.d.ts.map +1 -0
- package/build/testOps/featureUsage.js +46 -0
- package/build/testOps/listener.d.ts +33 -0
- package/build/testOps/listener.d.ts.map +1 -0
- package/build/testOps/listener.js +228 -0
- package/build/testOps/requestUtils.d.ts +4 -0
- package/build/testOps/requestUtils.d.ts.map +1 -0
- package/build/testOps/requestUtils.js +47 -0
- package/build/testOps/testOpsConfig.d.ts +11 -0
- package/build/testOps/testOpsConfig.d.ts.map +1 -0
- package/build/testOps/testOpsConfig.js +17 -0
- package/build/testOps/usageStats.d.ts +404 -0
- package/build/testOps/usageStats.d.ts.map +1 -0
- package/build/testOps/usageStats.js +110 -0
- package/build/types.d.ts +34 -7
- package/build/types.d.ts.map +1 -1
- package/build/util.d.ts +6 -6
- package/build/util.d.ts.map +1 -1
- package/build/util.js +114 -71
- package/package.json +3 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exitHandler.d.ts","sourceRoot":"","sources":["../src/exitHandler.ts"],"names":[],"mappings":"AAEA,OAAO,kBAAkB,MAAM,UAAU,CAAA;AAKzC,wBAAgB,iBAAiB,SAWhC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,kBAAkB,GAAG,MAAM,EAAE,CAYtE"}
|
|
@@ -0,0 +1,37 @@
|
|
|
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
|
+
exports.shouldCallCleanup = exports.setupExitHandlers = void 0;
|
|
7
|
+
const node_child_process_1 = require("node:child_process");
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const config_1 = __importDefault(require("./config"));
|
|
10
|
+
const funnelInstrumentation_1 = require("./instrumentation/funnelInstrumentation");
|
|
11
|
+
const constants_1 = require("./constants");
|
|
12
|
+
const bstackLogger_1 = require("./bstackLogger");
|
|
13
|
+
function setupExitHandlers() {
|
|
14
|
+
process.on('exit', (code) => {
|
|
15
|
+
bstackLogger_1.BStackLogger.debug('Exit hook called');
|
|
16
|
+
const args = shouldCallCleanup(config_1.default.getInstance());
|
|
17
|
+
if (Array.isArray(args) && args.length) {
|
|
18
|
+
bstackLogger_1.BStackLogger.debug('Spawning cleanup with args ' + args.toString());
|
|
19
|
+
const childProcess = (0, node_child_process_1.spawn)('node', [`${node_path_1.default.join(__dirname, 'cleanup.js')}`, ...args], { detached: true, stdio: 'inherit', env: { ...process.env } });
|
|
20
|
+
childProcess.unref();
|
|
21
|
+
process.exit(code);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
exports.setupExitHandlers = setupExitHandlers;
|
|
26
|
+
function shouldCallCleanup(config) {
|
|
27
|
+
const args = [];
|
|
28
|
+
if (!!process.env[constants_1.TESTOPS_JWT_ENV] && !config.testObservability.buildStopped) {
|
|
29
|
+
args.push('--observability');
|
|
30
|
+
}
|
|
31
|
+
if (config.userName && config.accessKey && !config.funnelDataSent) {
|
|
32
|
+
const savedFilePath = (0, funnelInstrumentation_1.saveFunnelData)('SDKTestSuccessful', config);
|
|
33
|
+
args.push('--funnelData', savedFilePath);
|
|
34
|
+
}
|
|
35
|
+
return args;
|
|
36
|
+
}
|
|
37
|
+
exports.shouldCallCleanup = shouldCallCleanup;
|
|
@@ -2,7 +2,7 @@ import type { Capabilities, Frameworks } from '@wdio/types';
|
|
|
2
2
|
import type { Browser, MultiRemoteBrowser } from 'webdriverio';
|
|
3
3
|
import type { BeforeCommandArgs, AfterCommandArgs } from '@wdio/reporter';
|
|
4
4
|
import type { Feature, Scenario, CucumberHook, CucumberHookParams, Pickle, ITestCaseHookParameter } from './cucumber-types';
|
|
5
|
-
import type { TestMeta, CurrentRunInfo, StdLog } from './types';
|
|
5
|
+
import type { TestData, TestMeta, CurrentRunInfo, StdLog } from './types';
|
|
6
6
|
declare class _InsightsHandler {
|
|
7
7
|
private _browser;
|
|
8
8
|
private _framework?;
|
|
@@ -11,11 +11,12 @@ declare class _InsightsHandler {
|
|
|
11
11
|
private _platformMeta?;
|
|
12
12
|
private _commands;
|
|
13
13
|
private _gitConfigPath?;
|
|
14
|
-
private _requestQueueHandler;
|
|
15
14
|
private _currentTest;
|
|
16
15
|
private _currentHook;
|
|
17
16
|
private _cucumberData;
|
|
18
|
-
|
|
17
|
+
private _userCaps?;
|
|
18
|
+
private listener;
|
|
19
|
+
constructor(_browser: Browser<'async'> | MultiRemoteBrowser<'async'>, browserCaps?: Capabilities.Capabilities, isAppAutomate?: boolean, sessionId?: string, _framework?: string | undefined, _userCaps?: Capabilities.RemoteCapability);
|
|
19
20
|
registerListeners(): void;
|
|
20
21
|
before(): Promise<void>;
|
|
21
22
|
beforeHook(test: Frameworks.Test | CucumberHook | undefined, context: any): Promise<void>;
|
|
@@ -32,14 +33,8 @@ declare class _InsightsHandler {
|
|
|
32
33
|
afterStep(step: Frameworks.PickleStep, scenario: Pickle, result: Frameworks.PickleResult): Promise<void>;
|
|
33
34
|
sendScenarioObjectSkipped(scenario: Scenario, feature: Feature, uri: string): Promise<void>;
|
|
34
35
|
processCucumberHook(test: CucumberHook | undefined, params: CucumberHookParams, result?: Frameworks.TestResult): Promise<void>;
|
|
35
|
-
uploadPending(waitTimeout?: number, waitInterval?: number): any;
|
|
36
|
-
teardown(): Promise<void>;
|
|
37
|
-
/**
|
|
38
|
-
* misc methods
|
|
39
|
-
*/
|
|
40
36
|
browserCommand(commandType: string, args: BeforeCommandArgs & AfterCommandArgs, test?: Frameworks.Test | ITestCaseHookParameter): Promise<void>;
|
|
41
37
|
appendTestItemLog: (stdLog: StdLog) => Promise<void>;
|
|
42
|
-
private sendData;
|
|
43
38
|
private attachHookData;
|
|
44
39
|
private setHooksFromSuite;
|
|
45
40
|
getCucumberHookType(test: CucumberHook | undefined): string | null;
|
|
@@ -50,9 +45,9 @@ declare class _InsightsHandler {
|
|
|
50
45
|
private getHierarchy;
|
|
51
46
|
private getTestRunId;
|
|
52
47
|
private getTestRunIdFromSuite;
|
|
53
|
-
private
|
|
54
|
-
private
|
|
55
|
-
|
|
48
|
+
private getRunData;
|
|
49
|
+
private getTestRunDataForCucumber;
|
|
50
|
+
getHookRunDataForCucumber(hookData: TestMeta, eventType: string, result?: Frameworks.TestResult): TestData;
|
|
56
51
|
private getIntegrationsObject;
|
|
57
52
|
private getIdentifier;
|
|
58
53
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"insights-handler.d.ts","sourceRoot":"","sources":["../src/insights-handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AAIzE,OAAO,KAAK,EAAiB,OAAO,EAAE,QAAQ,EAAsB,YAAY,EAAE,kBAAkB,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"insights-handler.d.ts","sourceRoot":"","sources":["../src/insights-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,17 +8,16 @@ const logger_1 = __importDefault(require("@wdio/logger"));
|
|
|
8
8
|
const uuid_1 = require("uuid");
|
|
9
9
|
const reporter_1 = __importDefault(require("./reporter"));
|
|
10
10
|
const util_1 = require("./util");
|
|
11
|
-
const
|
|
11
|
+
const listener_1 = __importDefault(require("./testOps/listener"));
|
|
12
12
|
const constants_1 = require("./constants");
|
|
13
13
|
const log = (0, logger_1.default)('@wdio/browserstack-service');
|
|
14
14
|
class _InsightsHandler {
|
|
15
|
-
constructor(_browser, browserCaps, isAppAutomate, sessionId, _framework) {
|
|
15
|
+
constructor(_browser, browserCaps, isAppAutomate, sessionId, _framework, _userCaps) {
|
|
16
16
|
this._browser = _browser;
|
|
17
17
|
this._framework = _framework;
|
|
18
18
|
this._tests = {};
|
|
19
19
|
this._hooks = {};
|
|
20
20
|
this._commands = {};
|
|
21
|
-
this._requestQueueHandler = request_handler_1.default.getInstance();
|
|
22
21
|
this._currentTest = {};
|
|
23
22
|
this._currentHook = {};
|
|
24
23
|
this._cucumberData = {
|
|
@@ -26,6 +25,8 @@ class _InsightsHandler {
|
|
|
26
25
|
scenariosStarted: false,
|
|
27
26
|
steps: []
|
|
28
27
|
};
|
|
28
|
+
this._userCaps = {};
|
|
29
|
+
this.listener = listener_1.default.getInstance();
|
|
29
30
|
this.appendTestItemLog = async (stdLog) => {
|
|
30
31
|
try {
|
|
31
32
|
if (this._currentHook.uuid && !this._currentHook.finished && (this._framework === 'mocha' || this._framework === 'cucumber')) {
|
|
@@ -35,17 +36,13 @@ class _InsightsHandler {
|
|
|
35
36
|
stdLog.test_run_uuid = this._currentTest.uuid;
|
|
36
37
|
}
|
|
37
38
|
if (stdLog.hook_run_uuid || stdLog.test_run_uuid) {
|
|
38
|
-
|
|
39
|
-
event_type: 'LogCreated',
|
|
40
|
-
logs: [stdLog]
|
|
41
|
-
});
|
|
39
|
+
this.listener.logCreated([stdLog]);
|
|
42
40
|
}
|
|
43
41
|
}
|
|
44
42
|
catch (error) {
|
|
45
43
|
log.debug(`Exception in uploading log data to Observability with error : ${error}`);
|
|
46
44
|
}
|
|
47
45
|
};
|
|
48
|
-
this._requestQueueHandler.start();
|
|
49
46
|
this._platformMeta = {
|
|
50
47
|
browserName: browserCaps === null || browserCaps === void 0 ? void 0 : browserCaps.browserName,
|
|
51
48
|
browserVersion: browserCaps === null || browserCaps === void 0 ? void 0 : browserCaps.browserVersion,
|
|
@@ -54,6 +51,7 @@ class _InsightsHandler {
|
|
|
54
51
|
sessionId: sessionId,
|
|
55
52
|
product: isAppAutomate ? 'app-automate' : 'automate'
|
|
56
53
|
};
|
|
54
|
+
this._userCaps = _userCaps;
|
|
57
55
|
this.registerListeners();
|
|
58
56
|
}
|
|
59
57
|
registerListeners() {
|
|
@@ -90,7 +88,7 @@ class _InsightsHandler {
|
|
|
90
88
|
};
|
|
91
89
|
this.setCurrentHook({ uuid: hookUUID });
|
|
92
90
|
this.attachHookData(context, hookUUID);
|
|
93
|
-
|
|
91
|
+
this.listener.hookStarted(this.getRunData(test, 'HookRunStarted'));
|
|
94
92
|
}
|
|
95
93
|
async afterHook(test, result) {
|
|
96
94
|
if (!(0, util_1.frameworkSupportsHook)('after', this._framework)) {
|
|
@@ -111,7 +109,7 @@ class _InsightsHandler {
|
|
|
111
109
|
};
|
|
112
110
|
}
|
|
113
111
|
this.setCurrentHook({ uuid: this._tests[fullTitle].uuid, finished: true });
|
|
114
|
-
|
|
112
|
+
this.listener.hookFinished(this.getRunData(test, 'HookRunFinished', result));
|
|
115
113
|
const hookType = (0, util_1.getHookType)(test.title);
|
|
116
114
|
/*
|
|
117
115
|
If any of the `beforeAll`, `beforeEach`, `afterEach` then the tests after the hook won't run in mocha (https://github.com/mochajs/mocha/issues/4392)
|
|
@@ -128,7 +126,7 @@ class _InsightsHandler {
|
|
|
128
126
|
startedAt: (new Date()).toISOString(),
|
|
129
127
|
finishedAt: (new Date()).toISOString()
|
|
130
128
|
};
|
|
131
|
-
|
|
129
|
+
this.listener.testFinished(this.getRunData(skippedTest, 'TestRunSkipped'));
|
|
132
130
|
}
|
|
133
131
|
};
|
|
134
132
|
/*
|
|
@@ -157,7 +155,7 @@ class _InsightsHandler {
|
|
|
157
155
|
uuid: uuid,
|
|
158
156
|
startedAt: (new Date()).toISOString()
|
|
159
157
|
};
|
|
160
|
-
|
|
158
|
+
this.listener.testStarted(this.getRunData(test, 'TestRunStarted'));
|
|
161
159
|
}
|
|
162
160
|
async afterTest(test, result) {
|
|
163
161
|
if (this._framework !== 'mocha')
|
|
@@ -167,7 +165,7 @@ class _InsightsHandler {
|
|
|
167
165
|
...(this._tests[fullTitle] || {}),
|
|
168
166
|
finishedAt: (new Date()).toISOString()
|
|
169
167
|
};
|
|
170
|
-
|
|
168
|
+
this.listener.testFinished(this.getRunData(test, 'TestRunFinished', result));
|
|
171
169
|
}
|
|
172
170
|
/**
|
|
173
171
|
* Cucumber Only
|
|
@@ -206,11 +204,11 @@ class _InsightsHandler {
|
|
|
206
204
|
};
|
|
207
205
|
}
|
|
208
206
|
this._tests[uniqueId] = testMetaData;
|
|
209
|
-
|
|
207
|
+
this.listener.testStarted(this.getTestRunDataForCucumber(world, 'TestRunStarted'));
|
|
210
208
|
}
|
|
211
209
|
async afterScenario(world) {
|
|
212
210
|
this._cucumberData.scenario = undefined;
|
|
213
|
-
|
|
211
|
+
this.listener.testFinished(this.getTestRunDataForCucumber(world, 'TestRunFinished'));
|
|
214
212
|
}
|
|
215
213
|
async beforeStep(step, scenario) {
|
|
216
214
|
var _a;
|
|
@@ -286,7 +284,7 @@ class _InsightsHandler {
|
|
|
286
284
|
};
|
|
287
285
|
}),
|
|
288
286
|
};
|
|
289
|
-
|
|
287
|
+
this.listener.testFinished(this.getTestRunDataForCucumber(null, 'TestRunSkipped', testMetaData));
|
|
290
288
|
}
|
|
291
289
|
async processCucumberHook(test, params, result) {
|
|
292
290
|
const hookType = this.getCucumberHookType(test);
|
|
@@ -307,12 +305,12 @@ class _InsightsHandler {
|
|
|
307
305
|
hookType: hookType
|
|
308
306
|
};
|
|
309
307
|
this._tests[hookId] = hookMetaData;
|
|
310
|
-
|
|
308
|
+
this.listener.hookStarted(this.getHookRunDataForCucumber(hookMetaData, 'HookRunStarted'));
|
|
311
309
|
}
|
|
312
310
|
else {
|
|
313
311
|
this._tests[hookId].finishedAt = (new Date()).toISOString();
|
|
314
312
|
this.setCurrentHook({ uuid: this._tests[hookId].uuid, finished: true });
|
|
315
|
-
|
|
313
|
+
this.listener.hookFinished(this.getHookRunDataForCucumber(this._tests[hookId], 'HookRunFinished', result));
|
|
316
314
|
if (hookType === 'BEFORE_ALL' && result && !result.passed) {
|
|
317
315
|
const { feature, uri } = this._cucumberData;
|
|
318
316
|
if (!feature) {
|
|
@@ -333,21 +331,6 @@ class _InsightsHandler {
|
|
|
333
331
|
}
|
|
334
332
|
}
|
|
335
333
|
}
|
|
336
|
-
//@ts-ignore
|
|
337
|
-
async uploadPending(waitTimeout = constants_1.DEFAULT_WAIT_TIMEOUT_FOR_PENDING_UPLOADS, waitInterval = constants_1.DEFAULT_WAIT_INTERVAL_FOR_PENDING_UPLOADS) {
|
|
338
|
-
if (this._requestQueueHandler.pendingUploads <= 0 || waitTimeout <= 0) {
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
await (0, util_1.sleep)(waitInterval);
|
|
342
|
-
return this.uploadPending(waitTimeout - waitInterval);
|
|
343
|
-
}
|
|
344
|
-
async teardown() {
|
|
345
|
-
this._requestQueueHandler.tearDownInvoked = true;
|
|
346
|
-
await this._requestQueueHandler.shutdown();
|
|
347
|
-
}
|
|
348
|
-
/**
|
|
349
|
-
* misc methods
|
|
350
|
-
*/
|
|
351
334
|
async browserCommand(commandType, args, test) {
|
|
352
335
|
if (commandType === 'client:beforeCommand') {
|
|
353
336
|
this._commands[`${args.sessionId}_${args.method}_${args.endpoint}`] = args;
|
|
@@ -362,16 +345,13 @@ class _InsightsHandler {
|
|
|
362
345
|
return;
|
|
363
346
|
}
|
|
364
347
|
// log screenshot
|
|
365
|
-
if (Boolean(process.env.
|
|
366
|
-
await
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
kind: 'TEST_SCREENSHOT'
|
|
373
|
-
}]
|
|
374
|
-
}], constants_1.DATA_SCREENSHOT_ENDPOINT);
|
|
348
|
+
if (Boolean(process.env[constants_1.TESTOPS_SCREENSHOT_ENV]) && (0, util_1.isScreenshotCommand)(args) && args.result.value) {
|
|
349
|
+
await this.listener.onScreenshot([{
|
|
350
|
+
test_run_uuid: testMeta.uuid,
|
|
351
|
+
timestamp: new Date().toISOString(),
|
|
352
|
+
message: args.result.value,
|
|
353
|
+
kind: 'TEST_SCREENSHOT'
|
|
354
|
+
}]);
|
|
375
355
|
}
|
|
376
356
|
const dataKey = `${args.sessionId}_${args.method}_${args.endpoint}`;
|
|
377
357
|
const requestData = this._commands[dataKey];
|
|
@@ -379,32 +359,17 @@ class _InsightsHandler {
|
|
|
379
359
|
return;
|
|
380
360
|
}
|
|
381
361
|
// log http request
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
}]
|
|
395
|
-
});
|
|
396
|
-
if (req.proceed && req.data) {
|
|
397
|
-
await (0, util_1.uploadEventData)(req.data, req.url);
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
/*
|
|
401
|
-
* private methods
|
|
402
|
-
*/
|
|
403
|
-
async sendData(data) {
|
|
404
|
-
const req = this._requestQueueHandler.add(data);
|
|
405
|
-
if (req.proceed && req.data) {
|
|
406
|
-
await (0, util_1.uploadEventData)(req.data, req.url);
|
|
407
|
-
}
|
|
362
|
+
this.listener.logCreated([{
|
|
363
|
+
test_run_uuid: testMeta.uuid,
|
|
364
|
+
timestamp: new Date().toISOString(),
|
|
365
|
+
kind: 'HTTP',
|
|
366
|
+
http_response: {
|
|
367
|
+
path: requestData.endpoint,
|
|
368
|
+
method: requestData.method,
|
|
369
|
+
body: requestData.body,
|
|
370
|
+
response: args.result
|
|
371
|
+
}
|
|
372
|
+
}]);
|
|
408
373
|
}
|
|
409
374
|
attachHookData(context, hookId) {
|
|
410
375
|
if (context.currentTest && context.currentTest.parent) {
|
|
@@ -542,7 +507,7 @@ class _InsightsHandler {
|
|
|
542
507
|
}
|
|
543
508
|
return;
|
|
544
509
|
}
|
|
545
|
-
|
|
510
|
+
getRunData(test, eventType, results) {
|
|
546
511
|
var _a;
|
|
547
512
|
const fullTitle = (0, util_1.getUniqueIdentifier)(test, this._framework);
|
|
548
513
|
const testMetaData = this._tests[fullTitle];
|
|
@@ -595,24 +560,14 @@ class _InsightsHandler {
|
|
|
595
560
|
testData.result = 'skipped';
|
|
596
561
|
eventType = 'TestRunFinished';
|
|
597
562
|
}
|
|
598
|
-
const uploadData = {
|
|
599
|
-
event_type: eventType,
|
|
600
|
-
};
|
|
601
563
|
/* istanbul ignore if */
|
|
602
564
|
if (eventType.match(/HookRun/)) {
|
|
603
565
|
testData.hook_type = ((_a = testData.name) === null || _a === void 0 ? void 0 : _a.toLowerCase()) ? (0, util_1.getHookType)(testData.name.toLowerCase()) : 'undefined';
|
|
604
566
|
testData.test_run_id = this.getTestRunId(test.ctx);
|
|
605
|
-
uploadData.hook_run = testData;
|
|
606
|
-
}
|
|
607
|
-
else {
|
|
608
|
-
uploadData.test_run = testData;
|
|
609
|
-
}
|
|
610
|
-
const req = this._requestQueueHandler.add(uploadData);
|
|
611
|
-
if (req.proceed && req.data) {
|
|
612
|
-
await (0, util_1.uploadEventData)(req.data, req.url);
|
|
613
567
|
}
|
|
568
|
+
return testData;
|
|
614
569
|
}
|
|
615
|
-
|
|
570
|
+
getTestRunDataForCucumber(worldObj, eventType, testMetaData = null) {
|
|
616
571
|
const world = worldObj;
|
|
617
572
|
const dataHub = testMetaData ? testMetaData : (this._tests[(0, util_1.getUniqueIdentifierForCucumber)(world)] || {});
|
|
618
573
|
const { feature, scenario, steps, uuid, startedAt, finishedAt } = dataHub;
|
|
@@ -686,18 +641,10 @@ class _InsightsHandler {
|
|
|
686
641
|
}
|
|
687
642
|
if (eventType === 'TestRunSkipped') {
|
|
688
643
|
testData.result = 'skipped';
|
|
689
|
-
eventType = 'TestRunFinished';
|
|
690
|
-
}
|
|
691
|
-
const uploadData = {
|
|
692
|
-
event_type: eventType,
|
|
693
|
-
test_run: testData
|
|
694
|
-
};
|
|
695
|
-
const req = this._requestQueueHandler.add(uploadData);
|
|
696
|
-
if (req.proceed && req.data) {
|
|
697
|
-
await (0, util_1.uploadEventData)(req.data, req.url);
|
|
698
644
|
}
|
|
645
|
+
return testData;
|
|
699
646
|
}
|
|
700
|
-
|
|
647
|
+
getHookRunDataForCucumber(hookData, eventType, result) {
|
|
701
648
|
const { uri, feature } = this._cucumberData;
|
|
702
649
|
const testData = {
|
|
703
650
|
uuid: hookData.uuid,
|
|
@@ -734,24 +681,19 @@ class _InsightsHandler {
|
|
|
734
681
|
testData.integrations[provider] = this.getIntegrationsObject();
|
|
735
682
|
}
|
|
736
683
|
}
|
|
737
|
-
|
|
738
|
-
event_type: eventType,
|
|
739
|
-
hook_run: testData
|
|
740
|
-
};
|
|
741
|
-
const req = this._requestQueueHandler.add(uploadData);
|
|
742
|
-
if (req.proceed && req.data) {
|
|
743
|
-
await (0, util_1.uploadEventData)(req.data, req.url);
|
|
744
|
-
}
|
|
684
|
+
return testData;
|
|
745
685
|
}
|
|
746
686
|
getIntegrationsObject() {
|
|
747
|
-
var _a, _b
|
|
687
|
+
var _a, _b;
|
|
688
|
+
const browserCaps = this._browser.capabilities;
|
|
748
689
|
return {
|
|
749
|
-
capabilities:
|
|
750
|
-
session_id: (
|
|
751
|
-
browser:
|
|
752
|
-
browser_version:
|
|
753
|
-
platform:
|
|
754
|
-
product: (
|
|
690
|
+
capabilities: browserCaps,
|
|
691
|
+
session_id: (_a = this._browser) === null || _a === void 0 ? void 0 : _a.sessionId,
|
|
692
|
+
browser: browserCaps === null || browserCaps === void 0 ? void 0 : browserCaps.browserName,
|
|
693
|
+
browser_version: browserCaps === null || browserCaps === void 0 ? void 0 : browserCaps.browserVersion,
|
|
694
|
+
platform: browserCaps === null || browserCaps === void 0 ? void 0 : browserCaps.platformName,
|
|
695
|
+
product: (_b = this._platformMeta) === null || _b === void 0 ? void 0 : _b.product,
|
|
696
|
+
platform_version: (0, util_1.getPlatformVersion)(this._userCaps)
|
|
755
697
|
};
|
|
756
698
|
}
|
|
757
699
|
getIdentifier(test) {
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type BrowserStackConfig from '../config';
|
|
2
|
+
export declare function sendStart(config: BrowserStackConfig): Promise<void>;
|
|
3
|
+
export declare function sendFinish(config: BrowserStackConfig): Promise<void>;
|
|
4
|
+
export declare function saveFunnelData(eventType: string, config: BrowserStackConfig): string;
|
|
5
|
+
export declare function fireFunnelRequest(data: any): Promise<void>;
|
|
6
|
+
//# sourceMappingURL=funnelInstrumentation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"funnelInstrumentation.d.ts","sourceRoot":"","sources":["../../src/instrumentation/funnelInstrumentation.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,kBAAkB,MAAM,WAAW,CAAA;AAsB/C,wBAAsB,SAAS,CAAC,MAAM,EAAE,kBAAkB,iBAEzD;AAED,wBAAsB,UAAU,CAAC,MAAM,EAAE,kBAAkB,iBAE1D;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,kBAAkB,GAAG,MAAM,CAOpF;AAGD,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAOhE"}
|
|
@@ -0,0 +1,127 @@
|
|
|
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
|
+
exports.fireFunnelRequest = exports.saveFunnelData = exports.sendFinish = exports.sendStart = void 0;
|
|
7
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
8
|
+
const node_util_1 = __importDefault(require("node:util"));
|
|
9
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
10
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
+
const got_1 = __importDefault(require("got"));
|
|
12
|
+
const usageStats_1 = __importDefault(require("../testOps/usageStats"));
|
|
13
|
+
const bstackLogger_1 = require("../bstackLogger");
|
|
14
|
+
const constants_1 = require("../constants");
|
|
15
|
+
const data_store_1 = require("../data-store");
|
|
16
|
+
async function fireFunnelTestEvent(eventType, config) {
|
|
17
|
+
if (!config.userName || !config.accessKey) {
|
|
18
|
+
bstackLogger_1.BStackLogger.debug('username/accesskey not passed');
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
const data = buildEventData(eventType, config);
|
|
23
|
+
await fireFunnelRequest(data);
|
|
24
|
+
bstackLogger_1.BStackLogger.debug('Funnel event success');
|
|
25
|
+
if (eventType === 'SDKTestSuccessful') {
|
|
26
|
+
config.sentFunnelData();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
bstackLogger_1.BStackLogger.debug('Exception in sending funnel data: ' + error);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function sendStart(config) {
|
|
34
|
+
await fireFunnelTestEvent('SDKTestAttempted', config);
|
|
35
|
+
}
|
|
36
|
+
exports.sendStart = sendStart;
|
|
37
|
+
async function sendFinish(config) {
|
|
38
|
+
await fireFunnelTestEvent('SDKTestSuccessful', config);
|
|
39
|
+
}
|
|
40
|
+
exports.sendFinish = sendFinish;
|
|
41
|
+
function saveFunnelData(eventType, config) {
|
|
42
|
+
const data = buildEventData(eventType, config);
|
|
43
|
+
bstackLogger_1.BStackLogger.ensureLogsFolder();
|
|
44
|
+
const filePath = node_path_1.default.join(bstackLogger_1.BStackLogger.logFolderPath, 'funnelData.json');
|
|
45
|
+
node_fs_1.default.writeFileSync(filePath, JSON.stringify(data));
|
|
46
|
+
return filePath;
|
|
47
|
+
}
|
|
48
|
+
exports.saveFunnelData = saveFunnelData;
|
|
49
|
+
// Called from two different process
|
|
50
|
+
async function fireFunnelRequest(data) {
|
|
51
|
+
bstackLogger_1.BStackLogger.debug('Sending SDK event with data ' + node_util_1.default.inspect(data, { depth: 6 }));
|
|
52
|
+
await got_1.default.post(constants_1.FUNNEL_INSTRUMENTATION_URL, {
|
|
53
|
+
headers: {
|
|
54
|
+
'content-type': 'application/json'
|
|
55
|
+
}, username: data.userName, password: data.accessKey, json: data
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
exports.fireFunnelRequest = fireFunnelRequest;
|
|
59
|
+
function getProductList(config) {
|
|
60
|
+
const products = [];
|
|
61
|
+
if (config.testObservability.enabled) {
|
|
62
|
+
products.push('observability');
|
|
63
|
+
}
|
|
64
|
+
if (config.accessibility) {
|
|
65
|
+
products.push('accessibility');
|
|
66
|
+
}
|
|
67
|
+
if (config.percy) {
|
|
68
|
+
products.push('percy');
|
|
69
|
+
}
|
|
70
|
+
if (config.automate) {
|
|
71
|
+
products.push('automate');
|
|
72
|
+
}
|
|
73
|
+
if (config.appAutomate) {
|
|
74
|
+
products.push('app-automate');
|
|
75
|
+
}
|
|
76
|
+
return products;
|
|
77
|
+
}
|
|
78
|
+
function getProductMap(config) {
|
|
79
|
+
return {
|
|
80
|
+
'observability': config.testObservability.enabled,
|
|
81
|
+
'accessibility': config.accessibility,
|
|
82
|
+
'percy': config.percy,
|
|
83
|
+
'automate': config.automate,
|
|
84
|
+
'app_automate': config.appAutomate
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function buildEventData(eventType, config) {
|
|
88
|
+
const eventProperties = {
|
|
89
|
+
// Framework Details
|
|
90
|
+
language_framework: getLanguageFramework(config.framework),
|
|
91
|
+
referrer: getReferrer(config.framework),
|
|
92
|
+
language: 'WebdriverIO',
|
|
93
|
+
languageVersion: process.version,
|
|
94
|
+
// Build Details
|
|
95
|
+
buildName: config.buildName || 'undefined',
|
|
96
|
+
buildIdentifier: String(config.buildIdentifier),
|
|
97
|
+
// Host details
|
|
98
|
+
os: node_os_1.default.type() || 'unknown',
|
|
99
|
+
hostname: node_os_1.default.hostname() || 'unknown',
|
|
100
|
+
// Product Details
|
|
101
|
+
productMap: getProductMap(config),
|
|
102
|
+
product: getProductList(config),
|
|
103
|
+
};
|
|
104
|
+
if (eventType === 'SDKTestSuccessful') {
|
|
105
|
+
const workerData = (0, data_store_1.getDataFromWorkers)();
|
|
106
|
+
eventProperties.productUsage = getProductUsage(workerData);
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
userName: config.userName,
|
|
110
|
+
accessKey: config.accessKey,
|
|
111
|
+
event_type: eventType,
|
|
112
|
+
detectedFramework: 'WebdriverIO-' + config.framework,
|
|
113
|
+
event_properties: eventProperties
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function getProductUsage(workersData) {
|
|
117
|
+
return {
|
|
118
|
+
testObservability: usageStats_1.default.getInstance().getFormattedData(workersData)
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function getLanguageFramework(framework) {
|
|
122
|
+
return 'WebdriverIO_' + framework;
|
|
123
|
+
}
|
|
124
|
+
function getReferrer(framework) {
|
|
125
|
+
const fullName = framework ? 'WebdriverIO-' + framework : 'WebdriverIO';
|
|
126
|
+
return `${fullName}/${constants_1.BSTACK_SERVICE_VERSION}`;
|
|
127
|
+
}
|
package/build/launcher.d.ts
CHANGED
|
@@ -16,10 +16,9 @@ export default class BrowserstackLauncherService implements Services.ServiceInst
|
|
|
16
16
|
private _accessibilityAutomation?;
|
|
17
17
|
private _percy?;
|
|
18
18
|
private _percyBestPlatformCaps?;
|
|
19
|
-
|
|
19
|
+
private readonly browserStackConfig;
|
|
20
20
|
constructor(_options: BrowserstackConfig & Options.Testrunner, capabilities: Capabilities.RemoteCapability, _config: Options.Testrunner);
|
|
21
21
|
onWorkerStart(cid: any, caps: any): Promise<void>;
|
|
22
|
-
setupExitHandlers(): void;
|
|
23
22
|
onPrepare(config?: Options.Testrunner, capabilities?: Capabilities.RemoteCapabilities): Promise<unknown>;
|
|
24
23
|
onComplete(): Promise<unknown>;
|
|
25
24
|
setupPercy(options: BrowserstackConfig & Options.Testrunner, config: Options.Testrunner, bsConfig: UserConfig): Promise<void>;
|
package/build/launcher.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../src/launcher.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,yBAAyB,MAAM,oBAAoB,CAAA;AAE/D,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../src/launcher.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,yBAAyB,MAAM,oBAAoB,CAAA;AAE/D,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAMlE,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AA8BhG,KAAK,iBAAiB,GAAG,yBAAyB,CAAC,KAAK,GAAG;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAC;CAC/C,CAAA;AAED,MAAM,CAAC,OAAO,OAAO,2BAA4B,YAAW,QAAQ,CAAC,eAAe;IAY5E,OAAO,CAAC,QAAQ;IAEhB,OAAO,CAAC,OAAO;IAbnB,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,YAAY,CAAC,CAAQ;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAQ;IAC1B,OAAO,CAAC,gBAAgB,CAAC,CAAQ;IACjC,OAAO,CAAC,wBAAwB,CAAC,CAAS;IAC1C,OAAO,CAAC,MAAM,CAAC,CAAO;IACtB,OAAO,CAAC,sBAAsB,CAAC,CAAkC;IACjE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;gBAG3C,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,EACzD,YAAY,EAAE,YAAY,CAAC,gBAAgB,EACnC,OAAO,EAAE,OAAO,CAAC,UAAU;IAsGjC,aAAa,CAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG;IAalC,SAAS,CAAE,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,YAAY,CAAC,kBAAkB;IA2JtF,UAAU;IAqEV,UAAU,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,UAAU;IAuB7G,SAAS;IAYT,UAAU,CAAC,GAAG,EAAC,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAqBrD;;;OAGG;IACG,YAAY,CAAE,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAyBhE,qBAAqB,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,kBAAkB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;KAAE;IAiFvH,WAAW,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,kBAAkB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAC,MAAM;IAoF3F,sBAAsB,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC,kBAAkB;IAyCrE;;;OAGG;IACH,oBAAoB;IA6BpB,sBAAsB,CAAC,QAAQ,CAAC,EAAC,MAAM,EAAE,SAAS,CAAC,EAAC,MAAM,EAAE,eAAe,CAAC,EAAC,MAAM;CAQtF"}
|