@wdio/browserstack-service 7.31.1 → 7.32.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,14 @@
1
+ import type { Capabilities, Options } from '@wdio/types';
2
+ import type { BrowserstackConfig, UserConfigforReporting } from './types';
3
+ export default class CrashReporter {
4
+ static userConfigForReporting: UserConfigforReporting;
5
+ private static credentialsForCrashReportUpload;
6
+ static setCredentialsForCrashReportUpload(options: BrowserstackConfig & Options.Testrunner, config: Options.Testrunner): void;
7
+ static setConfigDetails(userConfig: Options.Testrunner, capabilities: Capabilities.RemoteCapability, options: BrowserstackConfig & Options.Testrunner): void;
8
+ static uploadCrashReport(exception: any, stackTrace: string): Promise<void>;
9
+ static deletePIIKeysFromObject(obj: {
10
+ [key: string]: any;
11
+ }): void;
12
+ static filterPII(userConfig: Options.Testrunner): any;
13
+ }
14
+ //# sourceMappingURL=crash-reporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crash-reporter.d.ts","sourceRoot":"","sources":["../src/crash-reporter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAMxD,OAAO,KAAK,EAAE,kBAAkB,EAAmC,sBAAsB,EAAE,MAAM,SAAS,CAAA;AAK1G,MAAM,CAAC,OAAO,OAAO,aAAa;IAE9B,OAAc,sBAAsB,EAAE,sBAAsB,CAAK;IAEjE,OAAO,CAAC,MAAM,CAAC,+BAA+B,CAAsC;IAEpF,MAAM,CAAC,kCAAkC,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,UAAU;IAQtH,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,YAAY,EAAE,YAAY,CAAC,gBAAgB,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU;WAgBxI,iBAAiB,CAAC,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM;IA6CjE,MAAM,CAAC,uBAAuB,CAAC,GAAG,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;KAAE;IAO1D,MAAM,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,UAAU;CAyBlD"}
@@ -0,0 +1,117 @@
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 got_1 = __importDefault(require("got"));
8
+ // @ts-ignore
9
+ const package_json_1 = require("../package.json");
10
+ const constants_1 = require("./constants");
11
+ const util_1 = require("./util");
12
+ const log = (0, logger_1.default)('@wdio/browserstack-service');
13
+ class CrashReporter {
14
+ static setCredentialsForCrashReportUpload(options, config) {
15
+ this.credentialsForCrashReportUpload = {
16
+ username: (0, util_1.getObservabilityUser)(options, config),
17
+ password: (0, util_1.getObservabilityKey)(options, config)
18
+ };
19
+ process.env.CREDENTIALS_FOR_CRASH_REPORTING = JSON.stringify(this.credentialsForCrashReportUpload);
20
+ }
21
+ static setConfigDetails(userConfig, capabilities, options) {
22
+ const configWithoutPII = this.filterPII(userConfig);
23
+ this.userConfigForReporting = {
24
+ framework: userConfig.framework,
25
+ services: configWithoutPII.services,
26
+ capabilities: capabilities,
27
+ env: {
28
+ 'BROWSERSTACK_BUILD': process.env.BROWSERSTACK_BUILD,
29
+ 'BROWSERSTACK_BUILD_NAME': process.env.BROWSERSTACK_BUILD_NAME,
30
+ 'BUILD_TAG': process.env.BUILD_TAG
31
+ }
32
+ };
33
+ process.env.USER_CONFIG_FOR_REPORTING = JSON.stringify(this.userConfigForReporting);
34
+ this.setCredentialsForCrashReportUpload(options, userConfig);
35
+ }
36
+ static async uploadCrashReport(exception, stackTrace) {
37
+ try {
38
+ if (!this.credentialsForCrashReportUpload.username || !this.credentialsForCrashReportUpload.password) {
39
+ this.credentialsForCrashReportUpload = process.env.CREDENTIALS_FOR_CRASH_REPORTING !== undefined ? JSON.parse(process.env.CREDENTIALS_FOR_CRASH_REPORTING) : this.credentialsForCrashReportUpload;
40
+ }
41
+ }
42
+ catch (error) {
43
+ return log.error(`[Crash_Report_Upload] Failed to parse user credentials while reporting crash due to ${error}`);
44
+ }
45
+ if (!this.credentialsForCrashReportUpload.username || !this.credentialsForCrashReportUpload.password) {
46
+ return log.error('[Crash_Report_Upload] Failed to parse user credentials while reporting crash');
47
+ }
48
+ try {
49
+ if (Object.keys(this.userConfigForReporting).length === 0) {
50
+ this.userConfigForReporting = process.env.USER_CONFIG_FOR_REPORTING !== undefined ? JSON.parse(process.env.USER_CONFIG_FOR_REPORTING) : {};
51
+ }
52
+ }
53
+ catch (error) {
54
+ log.error(`[Crash_Report_Upload] Failed to parse user config while reporting crash due to ${error}`);
55
+ this.userConfigForReporting = {};
56
+ }
57
+ const data = {
58
+ hashed_id: process.env.BS_TESTOPS_BUILD_HASHED_ID,
59
+ observability_version: {
60
+ frameworkName: 'WebdriverIO-' + (this.userConfigForReporting.framework || 'null'),
61
+ sdkVersion: package_json_1.version
62
+ },
63
+ exception: {
64
+ error: exception.toString(),
65
+ stackTrace: stackTrace
66
+ },
67
+ config: this.userConfigForReporting
68
+ };
69
+ const url = `${constants_1.DATA_ENDPOINT}/api/v1/analytics`;
70
+ got_1.default.post(url, {
71
+ ...util_1.DEFAULT_REQUEST_CONFIG,
72
+ ...this.credentialsForCrashReportUpload,
73
+ json: data
74
+ }).text().then(response => {
75
+ log.debug(`[Crash_Report_Upload] Success response: ${JSON.stringify(response)}`);
76
+ }).catch((error) => {
77
+ log.error(`[Crash_Report_Upload] Failed due to ${error}`);
78
+ });
79
+ }
80
+ static deletePIIKeysFromObject(obj) {
81
+ if (!obj) {
82
+ return;
83
+ }
84
+ ['user', 'username', 'key', 'accessKey'].forEach(key => delete obj[key]);
85
+ }
86
+ static filterPII(userConfig) {
87
+ const configWithoutPII = JSON.parse(JSON.stringify(userConfig));
88
+ this.deletePIIKeysFromObject(configWithoutPII);
89
+ const finalServices = [];
90
+ const initialServices = configWithoutPII.services;
91
+ delete configWithoutPII.services;
92
+ try {
93
+ for (const serviceArray of initialServices) {
94
+ if (Array.isArray(serviceArray) && serviceArray.length >= 2 && serviceArray[0] === 'browserstack') {
95
+ for (let idx = 1; idx < serviceArray.length; idx++) {
96
+ this.deletePIIKeysFromObject(serviceArray[idx]);
97
+ serviceArray[idx] && this.deletePIIKeysFromObject(serviceArray[idx].testObservabilityOptions);
98
+ }
99
+ finalServices.push(serviceArray);
100
+ break;
101
+ }
102
+ }
103
+ }
104
+ catch (err) {
105
+ /* Wrong configuration like strings instead of json objects could break this method, needs no action */
106
+ log.error(`Error in parsing user config PII with error ${err ? (err.stack || err) : err}`);
107
+ return configWithoutPII;
108
+ }
109
+ configWithoutPII.services = finalServices;
110
+ return configWithoutPII;
111
+ }
112
+ }
113
+ /* User test config for build run minus PII */
114
+ CrashReporter.userConfigForReporting = {};
115
+ /* User credentials used for reporting crashes in browserstack service */
116
+ CrashReporter.credentialsForCrashReportUpload = {};
117
+ exports.default = CrashReporter;
@@ -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 { Pickle, ITestCaseHookParameter } from './cucumber-types';
5
- export default class InsightsHandler {
5
+ declare class _InsightsHandler {
6
6
  private _browser;
7
7
  private _framework?;
8
8
  private _tests;
@@ -37,4 +37,7 @@ export default class InsightsHandler {
37
37
  private getIntegrationsObject;
38
38
  private getIdentifier;
39
39
  }
40
+ declare const InsightsHandler: typeof _InsightsHandler;
41
+ type InsightsHandler = _InsightsHandler;
42
+ export default InsightsHandler;
40
43
  //# sourceMappingURL=insights-handler.d.ts.map
@@ -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;AAGzE,OAAO,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAOtE,MAAM,CAAC,OAAO,OAAO,eAAe;IASnB,OAAO,CAAC,QAAQ;IAAwI,OAAO,CAAC,UAAU,CAAC;IAPxL,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAc;IACpC,OAAO,CAAC,SAAS,CAA2D;IAC5E,OAAO,CAAC,cAAc,CAAC,CAAQ;IAC/B,OAAO,CAAC,oBAAoB,CAAoC;gBAE3C,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC,EAAE,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,EAAU,UAAU,CAAC,oBAAQ;IAa1L,MAAM;IAWN,UAAU,CAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG;IAa/C,SAAS,CAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,UAAU;IAc/D,UAAU,CAAE,IAAI,EAAE,UAAU,CAAC,IAAI;IASjC,SAAS,CAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,UAAU;IASrE;;QAEI;IAEE,cAAc,CAAE,KAAK,EAAE,sBAAsB;IA+B7C,aAAa,CAAE,KAAK,EAAE,sBAAsB;IAI5C,UAAU,CAAE,IAAI,EAAE,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM;IAuBzD,SAAS,CAAE,IAAI,EAAE,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,YAAY;IAgCzF,aAAa,CACf,WAAW,SAA2C,EACtD,YAAY,SAA4C;IAUtD,QAAQ;IAId;;OAEG;IAEG,cAAc,CAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,gBAAgB,EAAE,IAAI,CAAC,EAAE,UAAU,CAAC,IAAI,GAAG,sBAAsB;IA2DtI,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,YAAY;YAYN,gBAAgB;YAuEhB,2BAA2B;IAsFzC,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,aAAa;CAMxB"}
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;AAGzE,OAAO,KAAK,EAAE,MAAM,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAA;AAsBtE,cAAM,gBAAgB;IASL,OAAO,CAAC,QAAQ;IAAwI,OAAO,CAAC,UAAU,CAAC;IAPxL,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAc;IACpC,OAAO,CAAC,SAAS,CAA2D;IAC5E,OAAO,CAAC,cAAc,CAAC,CAAQ;IAC/B,OAAO,CAAC,oBAAoB,CAAoC;gBAE3C,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,EAAE,WAAW,CAAC,EAAE,YAAY,CAAC,YAAY,EAAE,aAAa,CAAC,EAAE,OAAO,EAAE,SAAS,CAAC,EAAE,MAAM,EAAU,UAAU,CAAC,oBAAQ;IAa1L,MAAM;IAWN,UAAU,CAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG;IAgB/C,SAAS,CAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,UAAU;IAqD/D,UAAU,CAAE,IAAI,EAAE,UAAU,CAAC,IAAI;IAUjC,SAAS,CAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,UAAU;IAUrE;;QAEI;IAEE,cAAc,CAAE,KAAK,EAAE,sBAAsB;IA+B7C,aAAa,CAAE,KAAK,EAAE,sBAAsB;IAI5C,UAAU,CAAE,IAAI,EAAE,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM;IAuBzD,SAAS,CAAE,IAAI,EAAE,UAAU,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,YAAY;IAgCzF,aAAa,CACf,WAAW,SAA2C,EACtD,YAAY,SAA4C;IAUtD,QAAQ;IAId;;OAEG;IAEG,cAAc,CAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,gBAAgB,EAAE,IAAI,CAAC,EAAE,UAAU,CAAC,IAAI,GAAG,sBAAsB;IA4DtI,OAAO,CAAC,cAAc;IActB,OAAO,CAAC,YAAY;YAaN,gBAAgB;YA4EhB,2BAA2B;IAsFzC,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,aAAa;CAMxB;AAGD,QAAA,MAAM,eAAe,EAAE,OAAO,gBAA0D,CAAA;AACxF,KAAK,eAAe,GAAG,gBAAgB,CAAA;AAEvC,eAAe,eAAe,CAAA"}
@@ -5,10 +5,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const path_1 = __importDefault(require("path"));
7
7
  const uuid_1 = require("uuid");
8
+ const reporter_1 = __importDefault(require("./reporter"));
8
9
  const util_1 = require("./util");
9
10
  const request_handler_1 = __importDefault(require("./request-handler"));
10
11
  const constants_1 = require("./constants");
11
- class InsightsHandler {
12
+ class _InsightsHandler {
12
13
  constructor(_browser, browserCaps, isAppAutomate, sessionId, _framework) {
13
14
  this._browser = _browser;
14
15
  this._framework = _framework;
@@ -36,33 +37,69 @@ class InsightsHandler {
36
37
  }
37
38
  }
38
39
  async beforeHook(test, context) {
39
- if (this._framework == 'mocha') {
40
- const fullTitle = `${test.parent} - ${test.title}`;
41
- const hookId = (0, uuid_1.v4)();
42
- this._tests[fullTitle] = {
43
- uuid: hookId,
44
- startedAt: (new Date()).toISOString()
45
- };
46
- this.attachHookData(context, hookId);
47
- await this.sendTestRunEvent(test, 'HookRunStarted');
40
+ if (!(0, util_1.frameworkSupportsHook)('before', this._framework)) {
41
+ return;
48
42
  }
43
+ const fullTitle = (0, util_1.getUniqueIdentifier)(test, this._framework);
44
+ const hookId = (0, uuid_1.v4)();
45
+ this._tests[fullTitle] = {
46
+ uuid: hookId,
47
+ startedAt: (new Date()).toISOString()
48
+ };
49
+ this.attachHookData(context, hookId);
50
+ await this.sendTestRunEvent(test, 'HookRunStarted');
49
51
  }
50
52
  async afterHook(test, result) {
51
- if (this._framework == 'mocha') {
52
- const fullTitle = (0, util_1.getUniqueIdentifier)(test);
53
- if (this._tests[fullTitle]) {
54
- this._tests[fullTitle].finishedAt = (new Date()).toISOString();
55
- }
56
- else {
57
- this._tests[fullTitle] = {
58
- finishedAt: (new Date()).toISOString()
59
- };
60
- }
61
- await this.sendTestRunEvent(test, 'HookRunFinished', result);
53
+ if (!(0, util_1.frameworkSupportsHook)('after', this._framework)) {
54
+ return;
55
+ }
56
+ const fullTitle = (0, util_1.getUniqueIdentifier)(test, this._framework);
57
+ if (this._tests[fullTitle]) {
58
+ this._tests[fullTitle].finishedAt = (new Date()).toISOString();
59
+ }
60
+ else {
61
+ this._tests[fullTitle] = {
62
+ finishedAt: (new Date()).toISOString()
63
+ };
64
+ }
65
+ await this.sendTestRunEvent(test, 'HookRunFinished', result);
66
+ const hookType = (0, util_1.getHookType)(test.title);
67
+ /*
68
+ 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)
69
+ So if any of this hook fails, then we are sending the next tests in the suite as skipped.
70
+ This won't be needed for `afterAll`, as even if `afterAll` fails all the tests that we need are already run by then, so we don't need to send the stats for them separately
71
+ */
72
+ if (!result.passed && (hookType === 'BEFORE_EACH' || hookType === 'BEFORE_ALL' || hookType === 'AFTER_EACH')) {
73
+ const sendTestSkip = async (skippedTest) => {
74
+ // We only need to send the tests that whose state is not determined yet. The state of tests which is determined will already be sent.
75
+ if (skippedTest.state === undefined) {
76
+ const fullTitle = `${skippedTest.parent.title} - ${skippedTest.title}`;
77
+ this._tests[fullTitle] = {
78
+ uuid: (0, uuid_1.v4)(),
79
+ startedAt: (new Date()).toISOString(),
80
+ finishedAt: (new Date()).toISOString()
81
+ };
82
+ await this.sendTestRunEvent(skippedTest, 'TestRunSkipped');
83
+ }
84
+ };
85
+ /*
86
+ Recursively send the tests as skipped for all suites below the hook. This is to handle nested describe blocks
87
+ */
88
+ const sendSuiteSkipped = async (suite) => {
89
+ for (const skippedTest of suite.tests) {
90
+ await sendTestSkip(skippedTest);
91
+ }
92
+ for (const skippedSuite of suite.suites) {
93
+ await sendSuiteSkipped(skippedSuite);
94
+ }
95
+ };
96
+ await sendSuiteSkipped(test.ctx.test.parent);
62
97
  }
63
98
  }
64
99
  async beforeTest(test) {
65
- const fullTitle = (0, util_1.getUniqueIdentifier)(test);
100
+ if (this._framework !== 'mocha')
101
+ return;
102
+ const fullTitle = (0, util_1.getUniqueIdentifier)(test, this._framework);
66
103
  this._tests[fullTitle] = {
67
104
  uuid: (0, uuid_1.v4)(),
68
105
  startedAt: (new Date()).toISOString()
@@ -70,7 +107,9 @@ class InsightsHandler {
70
107
  await this.sendTestRunEvent(test, 'TestRunStarted');
71
108
  }
72
109
  async afterTest(test, result) {
73
- const fullTitle = (0, util_1.getUniqueIdentifier)(test);
110
+ if (this._framework !== 'mocha')
111
+ return;
112
+ const fullTitle = (0, util_1.getUniqueIdentifier)(test, this._framework);
74
113
  this._tests[fullTitle] = {
75
114
  ...(this._tests[fullTitle] || {}),
76
115
  finishedAt: (new Date()).toISOString()
@@ -179,7 +218,8 @@ class InsightsHandler {
179
218
  return;
180
219
  }
181
220
  const identifier = this.getIdentifier(test);
182
- if (!this._tests[identifier]) {
221
+ const testMeta = this._tests[identifier] || reporter_1.default.getTests()[identifier];
222
+ if (!testMeta) {
183
223
  return;
184
224
  }
185
225
  // log screenshot
@@ -187,7 +227,7 @@ class InsightsHandler {
187
227
  await (0, util_1.uploadEventData)([{
188
228
  event_type: 'LogCreated',
189
229
  logs: [{
190
- test_run_uuid: this._tests[identifier].uuid,
230
+ test_run_uuid: testMeta.uuid,
191
231
  timestamp: new Date().toISOString(),
192
232
  message: args.result.value,
193
233
  kind: 'TEST_SCREENSHOT'
@@ -203,7 +243,7 @@ class InsightsHandler {
203
243
  const req = this._requestQueueHandler.add({
204
244
  event_type: 'LogCreated',
205
245
  logs: [{
206
- test_run_uuid: this._tests[identifier].uuid,
246
+ test_run_uuid: testMeta.uuid,
207
247
  timestamp: new Date().toISOString(),
208
248
  kind: 'HTTP',
209
249
  http_response: {
@@ -237,7 +277,8 @@ class InsightsHandler {
237
277
  getHierarchy(test) {
238
278
  const value = [];
239
279
  if (test.ctx && test.ctx.test) {
240
- let parent = test.ctx.test.parent;
280
+ // If we already have the parent object, utilize it else get from context
281
+ let parent = typeof test.parent === 'object' ? test.parent : test.ctx.test.parent;
241
282
  while (parent && parent.title !== '') {
242
283
  value.push(parent.title);
243
284
  parent = parent.parent;
@@ -247,7 +288,7 @@ class InsightsHandler {
247
288
  }
248
289
  async sendTestRunEvent(test, eventType, results) {
249
290
  var _a;
250
- const fullTitle = (0, util_1.getUniqueIdentifier)(test);
291
+ const fullTitle = (0, util_1.getUniqueIdentifier)(test, this._framework);
251
292
  const testMetaData = this._tests[fullTitle];
252
293
  const testData = {
253
294
  uuid: testMetaData.uuid,
@@ -287,13 +328,17 @@ class InsightsHandler {
287
328
  testData.hooks = this._hooks[fullTitle];
288
329
  }
289
330
  }
290
- if (eventType == 'TestRunStarted') {
331
+ if (eventType === 'TestRunStarted' || eventType === 'TestRunSkipped') {
291
332
  testData.integrations = {};
292
333
  if (this._browser && this._platformMeta) {
293
334
  const provider = (0, util_1.getCloudProvider)(this._browser);
294
335
  testData.integrations[provider] = this.getIntegrationsObject();
295
336
  }
296
337
  }
338
+ if (eventType === 'TestRunSkipped') {
339
+ testData.result = 'skipped';
340
+ eventType = 'TestRunFinished';
341
+ }
297
342
  const uploadData = {
298
343
  event_type: eventType,
299
344
  };
@@ -400,7 +445,9 @@ class InsightsHandler {
400
445
  if ('pickle' in test) {
401
446
  return (0, util_1.getUniqueIdentifierForCucumber)(test);
402
447
  }
403
- return (0, util_1.getUniqueIdentifier)(test);
448
+ return (0, util_1.getUniqueIdentifier)(test, this._framework);
404
449
  }
405
450
  }
451
+ // https://github.com/microsoft/TypeScript/issues/6543
452
+ const InsightsHandler = (0, util_1.o11yClassErrorHandler)(_InsightsHandler);
406
453
  exports.default = InsightsHandler;
@@ -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;AAIlE,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAMpF,KAAK,iBAAiB,GAAG,yBAAyB,CAAC,KAAK,GAAG;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;CAC7C,CAAA;AAED,MAAM,CAAC,OAAO,OAAO,2BAA4B,YAAW,QAAQ,CAAC,eAAe;IAQ5E,OAAO,CAAC,QAAQ;IAEhB,OAAO,CAAC,OAAO;IATnB,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;gBAGrB,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,EACzD,YAAY,EAAE,YAAY,CAAC,gBAAgB,EACnC,OAAO,EAAE,OAAO,CAAC,UAAU;IA2DjC,SAAS,CAAE,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,YAAY,CAAC,kBAAkB;IA2GtF,UAAU;IA2CV,UAAU,CAAC,GAAG,EAAC,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkBrD;;;OAGG;IACG,YAAY,CAAE,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAyBhE,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"}
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;AAKlE,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AAapF,KAAK,iBAAiB,GAAG,yBAAyB,CAAC,KAAK,GAAG;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI,CAAC;CAC7C,CAAA;AAED,MAAM,CAAC,OAAO,OAAO,2BAA4B,YAAW,QAAQ,CAAC,eAAe;IAQ5E,OAAO,CAAC,QAAQ;IAEhB,OAAO,CAAC,OAAO;IATnB,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;gBAGrB,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,EACzD,YAAY,EAAE,YAAY,CAAC,gBAAgB,EACnC,OAAO,EAAE,OAAO,CAAC,UAAU;IAqEjC,SAAS,CAAE,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,YAAY,CAAC,kBAAkB;IA2GtF,UAAU;IAsDV,UAAU,CAAC,GAAG,EAAC,GAAG,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAkBrD;;;OAGG;IACG,YAAY,CAAE,SAAS,EAAE,SAAS,GAAG,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC;IAyBhE,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"}
package/build/launcher.js CHANGED
@@ -38,8 +38,10 @@ const BrowserstackLocalLauncher = __importStar(require("browserstack-local"));
38
38
  const logger_1 = __importDefault(require("@wdio/logger"));
39
39
  // @ts-ignore
40
40
  const package_json_1 = require("../package.json");
41
+ const crash_reporter_1 = __importDefault(require("./crash-reporter"));
41
42
  const constants_1 = require("./constants");
42
43
  const util_2 = require("./util");
44
+ const performance_tester_1 = __importDefault(require("./performance-tester"));
43
45
  const log = (0, logger_1.default)('@wdio/browserstack-service');
44
46
  class BrowserstackLauncherService {
45
47
  constructor(_options, capabilities, _config) {
@@ -96,6 +98,9 @@ class BrowserstackLauncherService {
96
98
  }
97
99
  });
98
100
  }
101
+ if (process.env.BROWSERSTACK_O11Y_PERF_MEASUREMENT) {
102
+ performance_tester_1.default.startMonitoring('performance-report-launcher.csv');
103
+ }
99
104
  // by default observability will be true unless specified as false
100
105
  this._options.testObservability = this._options.testObservability == false ? false : true;
101
106
  if (this._options.testObservability &&
@@ -103,6 +108,12 @@ class BrowserstackLauncherService {
103
108
  process.env.BROWSERSTACK_RERUN && process.env.BROWSERSTACK_RERUN_TESTS) {
104
109
  this._config.specs = process.env.BROWSERSTACK_RERUN_TESTS.split(',');
105
110
  }
111
+ try {
112
+ crash_reporter_1.default.setConfigDetails(this._config, capabilities, this._options);
113
+ }
114
+ catch (error) {
115
+ log.error(`[Crash_Report_Upload] Config processing failed due to ${error}`);
116
+ }
106
117
  }
107
118
  async onPrepare(config, capabilities) {
108
119
  /**
@@ -208,6 +219,15 @@ class BrowserstackLauncherService {
208
219
  console.log(`\nVisit https://observability.browserstack.com/builds/${process.env.BS_TESTOPS_BUILD_HASHED_ID} to view build report, insights, and many more debugging information all at one place!\n`);
209
220
  }
210
221
  }
222
+ if (process.env.BROWSERSTACK_O11Y_PERF_MEASUREMENT) {
223
+ await performance_tester_1.default.stopAndGenerate('performance-launcher.html');
224
+ performance_tester_1.default.calculateTimes(['launchTestSession', 'stopBuildUpstream']);
225
+ if (!process.env.START_TIME) {
226
+ return;
227
+ }
228
+ const duration = (new Date()).getTime() - (new Date(process.env.START_TIME)).getTime();
229
+ log.info(`Total duration is ${duration / 1000} s`);
230
+ }
211
231
  if (!this.browserstackLocal || !this.browserstackLocal.isRunning()) {
212
232
  return;
213
233
  }
@@ -0,0 +1,15 @@
1
+ /// <reference types="node" />
2
+ import { PerformanceObserver } from 'perf_hooks';
3
+ export default class PerformanceTester {
4
+ static _observer: PerformanceObserver;
5
+ static _csvWriter: any;
6
+ private static _events;
7
+ static started: boolean;
8
+ static startMonitoring(csvName?: string): void;
9
+ static getPerformance(): import("perf_hooks").Performance;
10
+ static calculateTimes(methods: string[]): number;
11
+ static stopAndGenerate(filename?: string): Promise<void>;
12
+ static generateReport(entries: any[]): string;
13
+ static generateCSV(entries: any[]): void;
14
+ }
15
+ //# sourceMappingURL=performance-tester.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance-tester.d.ts","sourceRoot":"","sources":["../src/performance-tester.ts"],"names":[],"mappings":";AAEA,OAAO,EAAe,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAM7D,MAAM,CAAC,OAAO,OAAO,iBAAiB;IAClC,MAAM,CAAC,SAAS,EAAE,mBAAmB,CAAA;IACrC,MAAM,CAAC,UAAU,EAAE,GAAG,CAAA;IACtB,OAAO,CAAC,MAAM,CAAC,OAAO,CAAyB;IAC/C,MAAM,CAAC,OAAO,UAAQ;IAEtB,MAAM,CAAC,eAAe,CAAC,OAAO,GAAE,MAAiC;IAiBjE,MAAM,CAAC,cAAc;IAIrB,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,GAAI,MAAM;WAepC,eAAe,CAAC,QAAQ,GAAE,MAA+B;IAoBtE,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,GAAG,EAAE;IAWpC,MAAM,CAAC,WAAW,CAAC,OAAO,EAAE,GAAG,EAAE;CAuBpC"}
@@ -0,0 +1,98 @@
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 csv_writer_1 = require("csv-writer");
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const perf_hooks_1 = require("perf_hooks");
9
+ const logger_1 = __importDefault(require("@wdio/logger"));
10
+ const util_1 = require("./util");
11
+ const log = (0, logger_1.default)('@wdio/browserstack-service');
12
+ class PerformanceTester {
13
+ static startMonitoring(csvName = 'performance-report.csv') {
14
+ this._observer = new perf_hooks_1.PerformanceObserver(list => {
15
+ list.getEntries().forEach(entry => {
16
+ this._events.push(entry);
17
+ });
18
+ });
19
+ this._observer.observe({ buffered: true, entryTypes: ['function'] });
20
+ this.started = true;
21
+ this._csvWriter = (0, csv_writer_1.createObjectCsvWriter)({
22
+ path: csvName,
23
+ header: [
24
+ { id: 'name', title: 'Function Name' },
25
+ { id: 'time', title: 'Execution Time (ms)' }
26
+ ]
27
+ });
28
+ }
29
+ static getPerformance() {
30
+ return perf_hooks_1.performance;
31
+ }
32
+ static calculateTimes(methods) {
33
+ const times = {};
34
+ this._events.map((entry) => {
35
+ if (!times[entry.name]) {
36
+ times[entry.name] = 0;
37
+ }
38
+ times[entry.name] += entry.duration;
39
+ });
40
+ const timeTaken = methods.reduce((a, c) => {
41
+ return times[c] + (a || 0);
42
+ }, 0);
43
+ log.info(`Time for ${methods} is `, timeTaken);
44
+ return timeTaken;
45
+ }
46
+ static async stopAndGenerate(filename = 'performance-own.html') {
47
+ if (!this.started)
48
+ return;
49
+ await (0, util_1.sleep)(2000); // Wait to 2s just to finish any running callbacks for timerify
50
+ this._observer.disconnect();
51
+ this.started = false;
52
+ this.generateCSV(this._events);
53
+ const content = this.generateReport(this._events);
54
+ const path = process.cwd() + '/' + filename;
55
+ fs_1.default.writeFile(path, content, err => {
56
+ if (err) {
57
+ log.error('Error in writing html', err);
58
+ return;
59
+ }
60
+ log.info('Performance report is at ', path);
61
+ });
62
+ }
63
+ static generateReport(entries) {
64
+ let html = '<!DOCTYPE html><html><head><title>Performance Report</title></head><body>';
65
+ html += '<h1>Performance Report</h1>';
66
+ html += '<table><thead><tr><th>Function Name</th><th>Duration (ms)</th></tr></thead><tbody>';
67
+ entries.forEach((entry) => {
68
+ html += `<tr><td>${entry.name}</td><td>${entry.duration}</td></tr>`;
69
+ });
70
+ html += '</tbody></table></body></html>';
71
+ return html;
72
+ }
73
+ static generateCSV(entries) {
74
+ const times = {};
75
+ entries.map((entry) => {
76
+ if (!times[entry.name]) {
77
+ times[entry.name] = 0;
78
+ }
79
+ times[entry.name] += entry.duration;
80
+ return {
81
+ name: entry.name,
82
+ time: entry.duration
83
+ };
84
+ });
85
+ const dat = Object.entries(times).map(([key, value]) => {
86
+ return {
87
+ name: key,
88
+ time: value
89
+ };
90
+ });
91
+ this._csvWriter.writeRecords(dat)
92
+ .then(() => log.info('Performance CSV report generated successfully'))
93
+ .catch((error) => console.error(error));
94
+ }
95
+ }
96
+ PerformanceTester._events = [];
97
+ PerformanceTester.started = false;
98
+ exports.default = PerformanceTester;
@@ -1,13 +1,31 @@
1
- import WDIOReporter, { SuiteStats, TestStats, RunnerStats } from '@wdio/reporter';
2
- export default class TestReporter extends WDIOReporter {
1
+ import WDIOReporter, { SuiteStats, TestStats, RunnerStats, HookStats } from '@wdio/reporter';
2
+ import { TestMeta } from './types';
3
+ declare class _TestReporter extends WDIOReporter {
3
4
  private _capabilities;
4
5
  private _config?;
5
6
  private _observability;
6
7
  private _sessionId?;
7
8
  private _suiteName?;
8
9
  private _requestQueueHandler;
9
- onRunnerStart(runnerStats: RunnerStats): void;
10
+ private _suites;
11
+ private static _tests;
12
+ private _gitConfigPath?;
13
+ private _gitConfigured;
14
+ onRunnerStart(runnerStats: RunnerStats): Promise<void>;
15
+ configureGit(): Promise<void>;
16
+ static getTests(): Record<string, TestMeta>;
10
17
  onSuiteStart(suiteStats: SuiteStats): void;
18
+ onSuiteEnd(): void;
19
+ needToSendData(testType?: string, event?: string): boolean;
20
+ onTestEnd(testStats: TestStats): Promise<void>;
21
+ onTestStart(testStats: TestStats): Promise<void>;
22
+ onHookStart(hookStats: HookStats): Promise<void>;
23
+ onHookEnd(hookStats: HookStats): Promise<void>;
24
+ getHookIdentifier(hookStats: HookStats): string;
25
+ sendTestRunEvent(testStats: TestStats | HookStats, eventType: string): Promise<void>;
11
26
  onTestSkip(testStats: TestStats): Promise<void>;
12
27
  }
28
+ declare const TestReporter: typeof _TestReporter;
29
+ type TestReporter = _TestReporter;
30
+ export default TestReporter;
13
31
  //# sourceMappingURL=reporter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAEA,OAAO,YAAY,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAUjF,MAAM,CAAC,OAAO,OAAO,YAAa,SAAQ,YAAY;IAClD,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,OAAO,CAAC,CAAyC;IACzD,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,oBAAoB,CAAoC;IAEhE,aAAa,CAAE,WAAW,EAAE,WAAW;IAOvC,YAAY,CAAE,UAAU,EAAE,UAAU;IAI9B,UAAU,CAAE,SAAS,EAAE,SAAS;CAkDzC"}
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAEA,OAAO,YAAY,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAO5F,OAAO,EAAgC,QAAQ,EAAc,MAAM,SAAS,CAAA;AAa5E,cAAM,aAAc,SAAQ,YAAY;IACpC,OAAO,CAAC,aAAa,CAAgC;IACrD,OAAO,CAAC,OAAO,CAAC,CAAyC;IACzD,OAAO,CAAC,cAAc,CAAO;IAC7B,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,MAAM,CAAC,MAAM,CAA+B;IACpD,OAAO,CAAC,cAAc,CAAC,CAAQ;IAC/B,OAAO,CAAC,cAAc,CAAiB;IAEjC,aAAa,CAAE,WAAW,EAAE,WAAW;IAQvC,YAAY;IAWlB,MAAM,CAAC,QAAQ;IAIf,YAAY,CAAE,UAAU,EAAE,UAAU;IAoBpC,UAAU;IAIV,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAe1C,SAAS,CAAC,SAAS,EAAE,SAAS;IAQ9B,WAAW,CAAC,SAAS,EAAE,SAAS;IAUhC,WAAW,CAAC,SAAS,EAAE,SAAS;IAchC,SAAS,CAAC,SAAS,EAAE,SAAS;IAYpC,iBAAiB,CAAC,SAAS,EAAE,SAAS;IAIhC,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAC,SAAS,EAAE,SAAS,EAAE,MAAM;IAkFlE,UAAU,CAAE,SAAS,EAAE,SAAS;CAQzC;AAED,QAAA,MAAM,YAAY,EAAE,OAAO,aAAoD,CAAA;AAC/E,KAAK,YAAY,GAAG,aAAa,CAAA;AACjC,eAAe,YAAY,CAAA"}