@wdio/browserstack-service 7.28.0 → 7.29.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/constants.d.ts +9 -0
- package/build/constants.d.ts.map +1 -1
- package/build/constants.js +12 -2
- package/build/cucumber-types.d.ts +177 -0
- package/build/cucumber-types.d.ts.map +1 -0
- package/build/cucumber-types.js +3 -0
- package/build/insights-handler.d.ts +40 -0
- package/build/insights-handler.d.ts.map +1 -0
- package/build/insights-handler.js +398 -0
- package/build/launcher.d.ts +5 -3
- package/build/launcher.d.ts.map +1 -1
- package/build/launcher.js +35 -4
- package/build/reporter.d.ts +13 -0
- package/build/reporter.d.ts.map +1 -0
- package/build/reporter.js +74 -0
- package/build/request-handler.d.ts +26 -0
- package/build/request-handler.d.ts.map +1 -0
- package/build/request-handler.js +93 -0
- package/build/service.d.ts +16 -16
- package/build/service.d.ts.map +1 -1
- package/build/service.js +84 -33
- package/build/types.d.ts +116 -0
- package/build/types.d.ts.map +1 -1
- package/build/util.d.ts +85 -1
- package/build/util.d.ts.map +1 -1
- package/build/util.js +486 -1
- package/package.json +8 -4
package/build/launcher.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as BrowserstackLocalLauncher from 'browserstack-local';
|
|
2
2
|
import type { Capabilities, Services, Options } from '@wdio/types';
|
|
3
|
-
import { App, AppConfig, AppUploadResponse } from './types';
|
|
4
|
-
import { BrowserstackConfig } from './types';
|
|
3
|
+
import type { App, AppConfig, AppUploadResponse, BrowserstackConfig } from './types';
|
|
5
4
|
type BrowserstackLocal = BrowserstackLocalLauncher.Local & {
|
|
6
5
|
pid?: number;
|
|
7
6
|
stop(callback: (err?: any) => void): void;
|
|
@@ -10,9 +9,12 @@ export default class BrowserstackLauncherService implements Services.ServiceInst
|
|
|
10
9
|
private _options;
|
|
11
10
|
private _config;
|
|
12
11
|
browserstackLocal?: BrowserstackLocal;
|
|
12
|
+
private _buildName?;
|
|
13
|
+
private _projectName?;
|
|
14
|
+
private _buildTag?;
|
|
13
15
|
constructor(_options: BrowserstackConfig & Options.Testrunner, capabilities: Capabilities.RemoteCapability, _config: Options.Testrunner);
|
|
14
16
|
onPrepare(config?: Options.Testrunner, capabilities?: Capabilities.RemoteCapabilities): Promise<unknown>;
|
|
15
|
-
onComplete():
|
|
17
|
+
onComplete(): Promise<unknown>;
|
|
16
18
|
_uploadApp(app: App): Promise<AppUploadResponse>;
|
|
17
19
|
/**
|
|
18
20
|
* @param {String | AppConfig} appConfig <string>: should be "app file path" or "app_url" or "custom_id" or "shareable_id".
|
package/build/launcher.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launcher.d.ts","sourceRoot":"","sources":["../src/launcher.ts"],"names":[],"mappings":"AAQA,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":"AAQA,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;IAO5E,OAAO,CAAC,QAAQ;IAEhB,OAAO,CAAC,OAAO;IARnB,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,OAAO,CAAC,UAAU,CAAC,CAAQ;IAC3B,OAAO,CAAC,YAAY,CAAC,CAAQ;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAQ;gBAGd,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,EACzD,YAAY,EAAE,YAAY,CAAC,gBAAgB,EACnC,OAAO,EAAE,OAAO,CAAC,UAAU;IAkDjC,SAAS,CAAE,MAAM,CAAC,EAAE,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,EAAE,YAAY,CAAC,kBAAkB;IAwFtF,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;CA+C9F"}
|
package/build/launcher.js
CHANGED
|
@@ -38,6 +38,7 @@ const logger_1 = __importDefault(require("@wdio/logger"));
|
|
|
38
38
|
// @ts-ignore
|
|
39
39
|
const package_json_1 = require("../package.json");
|
|
40
40
|
const constants_1 = require("./constants");
|
|
41
|
+
const util_2 = require("./util");
|
|
41
42
|
const log = (0, logger_1.default)('@wdio/browserstack-service');
|
|
42
43
|
class BrowserstackLauncherService {
|
|
43
44
|
constructor(_options, capabilities, _config) {
|
|
@@ -52,12 +53,15 @@ class BrowserstackLauncherService {
|
|
|
52
53
|
if (extensionCaps.length) {
|
|
53
54
|
capability['bstack:options'] = { wdioService: package_json_1.version };
|
|
54
55
|
}
|
|
55
|
-
else {
|
|
56
|
+
else if ((0, util_2.shouldAddServiceVersion)(this._config, this._options.testObservability)) {
|
|
56
57
|
capability['browserstack.wdioService'] = package_json_1.version;
|
|
57
58
|
}
|
|
58
59
|
}
|
|
59
60
|
else {
|
|
60
61
|
capability['bstack:options'].wdioService = package_json_1.version;
|
|
62
|
+
this._buildName = capability['bstack:options'].buildName;
|
|
63
|
+
this._projectName = capability['bstack:options'].projectName;
|
|
64
|
+
this._buildTag = capability['bstack:options'].buildTag;
|
|
61
65
|
}
|
|
62
66
|
});
|
|
63
67
|
}
|
|
@@ -68,15 +72,26 @@ class BrowserstackLauncherService {
|
|
|
68
72
|
if (extensionCaps.length) {
|
|
69
73
|
caps.capabilities['bstack:options'] = { wdioService: package_json_1.version };
|
|
70
74
|
}
|
|
71
|
-
else {
|
|
75
|
+
else if ((0, util_2.shouldAddServiceVersion)(this._config, this._options.testObservability)) {
|
|
72
76
|
caps.capabilities['browserstack.wdioService'] = package_json_1.version;
|
|
73
77
|
}
|
|
74
78
|
}
|
|
75
79
|
else {
|
|
76
|
-
caps.capabilities['bstack:options']
|
|
80
|
+
const bstackOptions = caps.capabilities['bstack:options'];
|
|
81
|
+
bstackOptions.wdioService = package_json_1.version;
|
|
82
|
+
this._buildName = bstackOptions.buildName;
|
|
83
|
+
this._projectName = bstackOptions.projectName;
|
|
84
|
+
this._buildTag = bstackOptions.buildTag;
|
|
77
85
|
}
|
|
78
86
|
});
|
|
79
87
|
}
|
|
88
|
+
// by default observability will be true unless specified as false
|
|
89
|
+
this._options.testObservability = this._options.testObservability == false ? false : true;
|
|
90
|
+
if (this._options.testObservability &&
|
|
91
|
+
// update files to run if it's a rerun
|
|
92
|
+
process.env.BROWSERSTACK_RERUN && process.env.BROWSERSTACK_RERUN_TESTS) {
|
|
93
|
+
this._config.specs = process.env.BROWSERSTACK_RERUN_TESTS.split(',');
|
|
94
|
+
}
|
|
80
95
|
}
|
|
81
96
|
async onPrepare(config, capabilities) {
|
|
82
97
|
/**
|
|
@@ -112,6 +127,15 @@ class BrowserstackLauncherService {
|
|
|
112
127
|
log.info(`Using app: ${app.app}`);
|
|
113
128
|
this._updateCaps(capabilities, 'app', app.app);
|
|
114
129
|
}
|
|
130
|
+
if (this._options.testObservability) {
|
|
131
|
+
log.debug('Sending launch start event');
|
|
132
|
+
await (0, util_2.launchTestSession)(this._options, this._config, {
|
|
133
|
+
projectName: this._projectName,
|
|
134
|
+
buildName: this._buildName,
|
|
135
|
+
buildTag: this._buildTag,
|
|
136
|
+
bstackServiceVersion: package_json_1.version
|
|
137
|
+
});
|
|
138
|
+
}
|
|
115
139
|
if (!this._options.browserstackLocal) {
|
|
116
140
|
return log.info('browserstackLocal is not enabled - skipping...');
|
|
117
141
|
}
|
|
@@ -149,7 +173,14 @@ class BrowserstackLauncherService {
|
|
|
149
173
|
return Promise.reject(err);
|
|
150
174
|
});
|
|
151
175
|
}
|
|
152
|
-
onComplete() {
|
|
176
|
+
async onComplete() {
|
|
177
|
+
if (this._options.testObservability) {
|
|
178
|
+
log.debug('Sending stop launch event');
|
|
179
|
+
await (0, util_2.stopBuildUpstream)();
|
|
180
|
+
if (process.env.BS_TESTOPS_BUILD_HASHED_ID) {
|
|
181
|
+
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`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
153
184
|
if (!this.browserstackLocal || !this.browserstackLocal.isRunning()) {
|
|
154
185
|
return;
|
|
155
186
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import WDIOReporter, { SuiteStats, TestStats, RunnerStats } from '@wdio/reporter';
|
|
2
|
+
export default class TestReporter extends WDIOReporter {
|
|
3
|
+
private _capabilities;
|
|
4
|
+
private _config?;
|
|
5
|
+
private _observability;
|
|
6
|
+
private _sessionId?;
|
|
7
|
+
private _suiteName?;
|
|
8
|
+
private _requestQueueHandler;
|
|
9
|
+
onRunnerStart(runnerStats: RunnerStats): void;
|
|
10
|
+
onSuiteStart(suiteStats: SuiteStats): void;
|
|
11
|
+
onTestSkip(testStats: TestStats): Promise<void>;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
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 reporter_1 = __importDefault(require("@wdio/reporter"));
|
|
7
|
+
const uuid_1 = require("uuid");
|
|
8
|
+
const util_1 = require("./util");
|
|
9
|
+
const request_handler_1 = __importDefault(require("./request-handler"));
|
|
10
|
+
class TestReporter extends reporter_1.default {
|
|
11
|
+
constructor() {
|
|
12
|
+
super(...arguments);
|
|
13
|
+
this._capabilities = {};
|
|
14
|
+
this._observability = true;
|
|
15
|
+
this._requestQueueHandler = request_handler_1.default.getInstance();
|
|
16
|
+
}
|
|
17
|
+
onRunnerStart(runnerStats) {
|
|
18
|
+
this._capabilities = runnerStats.capabilities;
|
|
19
|
+
this._config = runnerStats.config;
|
|
20
|
+
this._sessionId = runnerStats.sessionId;
|
|
21
|
+
if (typeof this._config.testObservability !== 'undefined')
|
|
22
|
+
this._observability = this._config.testObservability;
|
|
23
|
+
}
|
|
24
|
+
onSuiteStart(suiteStats) {
|
|
25
|
+
this._suiteName = suiteStats.file;
|
|
26
|
+
}
|
|
27
|
+
async onTestSkip(testStats) {
|
|
28
|
+
var _a, _b, _c, _d, _e;
|
|
29
|
+
// cucumber steps call this method. We don't want step skipped state so skip for cucumber
|
|
30
|
+
const framework = (_a = this._config) === null || _a === void 0 ? void 0 : _a.framework;
|
|
31
|
+
if (this._observability && framework !== 'cucumber') {
|
|
32
|
+
let testData = {
|
|
33
|
+
uuid: (0, uuid_1.v4)(),
|
|
34
|
+
type: testStats.type,
|
|
35
|
+
name: testStats.title,
|
|
36
|
+
body: {
|
|
37
|
+
lang: 'webdriverio',
|
|
38
|
+
code: null
|
|
39
|
+
},
|
|
40
|
+
scope: testStats.fullTitle,
|
|
41
|
+
scopes: (0, util_1.getHierarchy)(testStats.fullTitle),
|
|
42
|
+
identifier: testStats.fullTitle,
|
|
43
|
+
file_name: this._suiteName,
|
|
44
|
+
location: this._suiteName,
|
|
45
|
+
started_at: (new Date()).toISOString(),
|
|
46
|
+
framework: framework,
|
|
47
|
+
finished_at: (new Date()).toISOString(),
|
|
48
|
+
duration_in_ms: testStats._duration,
|
|
49
|
+
retries: { limit: 0, attempts: 0 },
|
|
50
|
+
result: testStats.state,
|
|
51
|
+
};
|
|
52
|
+
/* istanbul ignore next */
|
|
53
|
+
const cloudProvider = (0, util_1.getCloudProvider)({ options: { hostname: (_b = this._config) === null || _b === void 0 ? void 0 : _b.hostname } });
|
|
54
|
+
testData.integrations = {};
|
|
55
|
+
/* istanbul ignore next */
|
|
56
|
+
testData.integrations[cloudProvider] = {
|
|
57
|
+
capabilities: this._capabilities,
|
|
58
|
+
session_id: this._sessionId,
|
|
59
|
+
browser: (_c = this._capabilities) === null || _c === void 0 ? void 0 : _c.browserName,
|
|
60
|
+
browser_version: (_d = this._capabilities) === null || _d === void 0 ? void 0 : _d.browserVersion,
|
|
61
|
+
platform: (_e = this._capabilities) === null || _e === void 0 ? void 0 : _e.platformName,
|
|
62
|
+
};
|
|
63
|
+
const uploadData = {
|
|
64
|
+
event_type: 'TestRunFinished',
|
|
65
|
+
test_run: testData
|
|
66
|
+
};
|
|
67
|
+
const req = this._requestQueueHandler.add(uploadData);
|
|
68
|
+
if (req.proceed && req.data) {
|
|
69
|
+
await (0, util_1.uploadEventData)(req.data, req.url);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.default = TestReporter;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { UploadType } from './types';
|
|
2
|
+
export default class RequestQueueHandler {
|
|
3
|
+
private queue;
|
|
4
|
+
private started;
|
|
5
|
+
private pollEventBatchInterval?;
|
|
6
|
+
pendingUploads: number;
|
|
7
|
+
static instance: RequestQueueHandler;
|
|
8
|
+
private constructor();
|
|
9
|
+
static getInstance(): RequestQueueHandler;
|
|
10
|
+
start(): void;
|
|
11
|
+
add(event: UploadType): {
|
|
12
|
+
proceed: boolean;
|
|
13
|
+
data?: undefined;
|
|
14
|
+
url?: undefined;
|
|
15
|
+
} | {
|
|
16
|
+
proceed: boolean;
|
|
17
|
+
data: UploadType[] | undefined;
|
|
18
|
+
url: string;
|
|
19
|
+
};
|
|
20
|
+
shutdown(): Promise<void>;
|
|
21
|
+
startEventBatchPolling(): void;
|
|
22
|
+
resetEventBatchPolling(): void;
|
|
23
|
+
removeEventBatchPolling(tag: string): void;
|
|
24
|
+
shouldProceed(): boolean;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=request-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-handler.d.ts","sourceRoot":"","sources":["../src/request-handler.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAKzC,MAAM,CAAC,OAAO,OAAO,mBAAmB;IACpC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,OAAO,CAAQ;IACvB,OAAO,CAAC,sBAAsB,CAAC,CAAgC;IACxD,cAAc,SAAI;IAEzB,MAAM,CAAC,QAAQ,EAAE,mBAAmB,CAAA;IAGpC,OAAO;WAGO,WAAW,IAAI,mBAAmB;IAOhD,KAAK;IAOL,GAAG,CAAE,KAAK,EAAE,UAAU;;;;;;;;;IAuChB,QAAQ;IAQd,sBAAsB;IAUtB,sBAAsB;IAKtB,uBAAuB,CAAE,GAAG,EAAE,MAAM;IAQpC,aAAa;CAGhB"}
|
|
@@ -0,0 +1,93 @@
|
|
|
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 constants_1 = require("./constants");
|
|
8
|
+
const util_1 = require("./util");
|
|
9
|
+
const log = (0, logger_1.default)('@wdio/browserstack-service');
|
|
10
|
+
class RequestQueueHandler {
|
|
11
|
+
// making it private to use singleton pattern
|
|
12
|
+
constructor() {
|
|
13
|
+
this.queue = [];
|
|
14
|
+
this.started = false;
|
|
15
|
+
this.pendingUploads = 0;
|
|
16
|
+
}
|
|
17
|
+
static getInstance() {
|
|
18
|
+
if (!RequestQueueHandler.instance) {
|
|
19
|
+
RequestQueueHandler.instance = new RequestQueueHandler();
|
|
20
|
+
}
|
|
21
|
+
return RequestQueueHandler.instance;
|
|
22
|
+
}
|
|
23
|
+
start() {
|
|
24
|
+
if (!this.started) {
|
|
25
|
+
this.started = true;
|
|
26
|
+
this.startEventBatchPolling();
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
add(event) {
|
|
30
|
+
if (!process.env.BS_TESTOPS_BUILD_COMPLETED) {
|
|
31
|
+
return {
|
|
32
|
+
proceed: false
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (!constants_1.BATCH_EVENT_TYPES.includes(event.event_type)) {
|
|
36
|
+
return {
|
|
37
|
+
proceed: true
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
if (event.logs && event.logs[0] && event.logs[0].kind === 'TEST_SCREENSHOT') {
|
|
41
|
+
return {
|
|
42
|
+
proceed: true,
|
|
43
|
+
data: [event],
|
|
44
|
+
url: constants_1.DATA_SCREENSHOT_ENDPOINT
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
this.queue.push(event);
|
|
48
|
+
log.debug(`Added data to request queue. Queue length = ${this.queue.length}`);
|
|
49
|
+
let data;
|
|
50
|
+
const shouldProceed = this.shouldProceed();
|
|
51
|
+
if (shouldProceed) {
|
|
52
|
+
data = this.queue.splice(0, constants_1.DATA_BATCH_SIZE);
|
|
53
|
+
this.resetEventBatchPolling();
|
|
54
|
+
log.debug(`Sending data from request queue. Data length = ${data.length}, Queue length after removal = ${this.queue.length}`);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
proceed: shouldProceed,
|
|
58
|
+
data: data,
|
|
59
|
+
url: constants_1.DATA_BATCH_ENDPOINT
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async shutdown() {
|
|
63
|
+
this.removeEventBatchPolling('Shutting down');
|
|
64
|
+
while (this.queue.length > 0) {
|
|
65
|
+
const data = this.queue.splice(0, constants_1.DATA_BATCH_SIZE);
|
|
66
|
+
await (0, util_1.batchAndPostEvents)(constants_1.DATA_BATCH_ENDPOINT, 'SHUTDOWN_QUEUE', data);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
startEventBatchPolling() {
|
|
70
|
+
this.pollEventBatchInterval = setInterval(async () => {
|
|
71
|
+
if (this.queue.length > 0) {
|
|
72
|
+
const data = this.queue.splice(0, constants_1.DATA_BATCH_SIZE);
|
|
73
|
+
log.debug(`Sending data from request queue. Data length = ${data.length}, Queue length after removal = ${this.queue.length}`);
|
|
74
|
+
await (0, util_1.batchAndPostEvents)(constants_1.DATA_BATCH_ENDPOINT, 'INTERVAL_QUEUE', data);
|
|
75
|
+
}
|
|
76
|
+
}, constants_1.DATA_BATCH_INTERVAL);
|
|
77
|
+
}
|
|
78
|
+
resetEventBatchPolling() {
|
|
79
|
+
this.removeEventBatchPolling('Resetting');
|
|
80
|
+
this.startEventBatchPolling();
|
|
81
|
+
}
|
|
82
|
+
removeEventBatchPolling(tag) {
|
|
83
|
+
if (this.pollEventBatchInterval) {
|
|
84
|
+
log.debug(`${tag} request queue`);
|
|
85
|
+
clearInterval(this.pollEventBatchInterval);
|
|
86
|
+
this.started = false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
shouldProceed() {
|
|
90
|
+
return this.queue.length >= constants_1.DATA_BATCH_SIZE;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
exports.default = RequestQueueHandler;
|
package/build/service.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Services, Capabilities, Options, Frameworks } from '@wdio/types';
|
|
2
2
|
import type { Browser, MultiRemoteBrowser } from 'webdriverio';
|
|
3
|
-
import { BrowserstackConfig, MultiRemoteAction } from './types';
|
|
3
|
+
import type { BrowserstackConfig, MultiRemoteAction } from './types';
|
|
4
|
+
import type { Pickle, Feature, ITestCaseHookParameter } from './cucumber-types';
|
|
4
5
|
export default class BrowserstackService implements Services.ServiceInstance {
|
|
5
6
|
private _caps;
|
|
6
7
|
private _config;
|
|
@@ -12,6 +13,10 @@ export default class BrowserstackService implements Services.ServiceInstance {
|
|
|
12
13
|
private _suiteTitle?;
|
|
13
14
|
private _fullTitle?;
|
|
14
15
|
private _options;
|
|
16
|
+
private _specsRan;
|
|
17
|
+
private _observability;
|
|
18
|
+
private _currentTest?;
|
|
19
|
+
private _insightsHandler?;
|
|
15
20
|
constructor(options: BrowserstackConfig & Options.Testrunner, _caps: Capabilities.RemoteCapability, _config: Options.Testrunner);
|
|
16
21
|
_updateCaps(fn: (caps: Capabilities.Capabilities | Capabilities.DesiredCapabilities) => void): void;
|
|
17
22
|
beforeSession(config: Omit<Options.Testrunner, 'capabilities'>): void;
|
|
@@ -24,33 +29,28 @@ export default class BrowserstackService implements Services.ServiceInstance {
|
|
|
24
29
|
* and `suite.fullTitle` is `undefined`, so no alternative to use for the job name.
|
|
25
30
|
*/
|
|
26
31
|
beforeSuite(suite: Frameworks.Suite): Promise<void>;
|
|
32
|
+
beforeHook(test: Frameworks.Test, context: any): Promise<void>;
|
|
33
|
+
afterHook(test: Frameworks.Test, context: unknown, result: Frameworks.TestResult): Promise<void>;
|
|
27
34
|
beforeTest(test: Frameworks.Test): Promise<void>;
|
|
35
|
+
afterTest(test: Frameworks.Test, context: never, results: Frameworks.TestResult): Promise<void>;
|
|
36
|
+
after(result: number): Promise<void>;
|
|
28
37
|
/**
|
|
29
38
|
* For CucumberJS
|
|
30
39
|
*/
|
|
31
|
-
beforeFeature(uri:
|
|
32
|
-
name: string;
|
|
33
|
-
}): Promise<void>;
|
|
40
|
+
beforeFeature(uri: string, feature: Feature): Promise<void>;
|
|
34
41
|
/**
|
|
35
42
|
* Runs before a Cucumber Scenario.
|
|
36
43
|
* @param world world object containing information on pickle and test step
|
|
37
44
|
*/
|
|
38
|
-
beforeScenario(world:
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
beforeStep(step: Frameworks.PickleStep): Promise<void>;
|
|
43
|
-
afterTest(test: Frameworks.Test, context: never, results: Frameworks.TestResult): void;
|
|
44
|
-
after(result: number): Promise<void>;
|
|
45
|
-
/**
|
|
46
|
-
* For CucumberJS
|
|
47
|
-
*/
|
|
48
|
-
afterScenario(world: Frameworks.World): void;
|
|
45
|
+
beforeScenario(world: ITestCaseHookParameter): Promise<void>;
|
|
46
|
+
afterScenario(world: ITestCaseHookParameter): Promise<void>;
|
|
47
|
+
beforeStep(step: Frameworks.PickleStep, scenario: Pickle): Promise<void>;
|
|
48
|
+
afterStep(step: Frameworks.PickleStep, scenario: Pickle, result: Frameworks.PickleResult): Promise<void>;
|
|
49
49
|
onReload(oldSessionId: string, newSessionId: string): Promise<void>;
|
|
50
50
|
_isAppAutomate(): boolean;
|
|
51
51
|
_updateJob(requestBody: any): Promise<any>;
|
|
52
52
|
_multiRemoteAction(action: MultiRemoteAction): Promise<any>;
|
|
53
|
-
_update(sessionId: string, requestBody: any): import("got").CancelableRequest<import("got").Response<string>>;
|
|
53
|
+
_update(sessionId: string, requestBody: any): Promise<void> | import("got").CancelableRequest<import("got").Response<string>>;
|
|
54
54
|
_printSessionURL(): Promise<void>;
|
|
55
55
|
private _setSessionName;
|
|
56
56
|
private _setAnnotation;
|
package/build/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
|
package/build/service.js
CHANGED
|
@@ -3,22 +3,30 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
6
7
|
const logger_1 = __importDefault(require("@wdio/logger"));
|
|
8
|
+
const constants_1 = require("./constants");
|
|
7
9
|
const got_1 = __importDefault(require("got"));
|
|
10
|
+
const insights_handler_1 = __importDefault(require("./insights-handler"));
|
|
8
11
|
const util_1 = require("./util");
|
|
9
|
-
const constants_1 = require("./constants");
|
|
10
12
|
const log = (0, logger_1.default)('@wdio/browserstack-service');
|
|
11
13
|
class BrowserstackService {
|
|
12
14
|
constructor(options, _caps, _config) {
|
|
15
|
+
var _a;
|
|
13
16
|
this._caps = _caps;
|
|
14
17
|
this._config = _config;
|
|
15
18
|
this._sessionBaseUrl = 'https://api.browserstack.com/automate/sessions';
|
|
16
19
|
this._failReasons = [];
|
|
17
20
|
this._scenariosThatRan = [];
|
|
18
21
|
this._failureStatuses = ['failed', 'ambiguous', 'undefined', 'unknown'];
|
|
22
|
+
this._specsRan = false;
|
|
19
23
|
this._options = { ...constants_1.DEFAULT_OPTIONS, ...options };
|
|
20
24
|
// added to maintain backward compatibility with webdriverIO v5
|
|
21
25
|
this._config || (this._config = this._options);
|
|
26
|
+
this._observability = this._options.testObservability;
|
|
27
|
+
if (this._observability) {
|
|
28
|
+
(_a = this._config.reporters) === null || _a === void 0 ? void 0 : _a.push(path_1.default.join(__dirname, 'reporter.js'));
|
|
29
|
+
}
|
|
22
30
|
// Cucumber specific
|
|
23
31
|
const strict = Boolean(this._config.cucumberOpts && this._config.cucumberOpts.strict);
|
|
24
32
|
// See https://github.com/cucumber/cucumber-js/blob/master/src/runtime/index.ts#L136
|
|
@@ -46,7 +54,7 @@ class BrowserstackService {
|
|
|
46
54
|
this._config.user = config.user;
|
|
47
55
|
this._config.key = config.key;
|
|
48
56
|
}
|
|
49
|
-
before(caps, specs, browser) {
|
|
57
|
+
async before(caps, specs, browser) {
|
|
50
58
|
// added to maintain backward compatibility with webdriverIO v5
|
|
51
59
|
this._browser = browser ? browser : global.browser;
|
|
52
60
|
// Ensure capabilities are not null in case of multiremote
|
|
@@ -54,7 +62,25 @@ class BrowserstackService {
|
|
|
54
62
|
this._sessionBaseUrl = 'https://api-cloud.browserstack.com/app-automate/sessions';
|
|
55
63
|
}
|
|
56
64
|
this._scenariosThatRan = [];
|
|
57
|
-
|
|
65
|
+
if (this._observability && this._browser) {
|
|
66
|
+
this._insightsHandler = new insights_handler_1.default(this._browser, this._browser.capabilities, this._isAppAutomate(), this._browser.sessionId, this._config.framework);
|
|
67
|
+
await this._insightsHandler.before();
|
|
68
|
+
/**
|
|
69
|
+
* register command event
|
|
70
|
+
*/
|
|
71
|
+
this._browser.on('command', async (command) => {
|
|
72
|
+
var _a, _b;
|
|
73
|
+
return await ((_a = this._insightsHandler) === null || _a === void 0 ? void 0 : _a.browserCommand('client:beforeCommand', Object.assign(command, { sessionId: (_b = this._browser) === null || _b === void 0 ? void 0 : _b.sessionId }), this._currentTest));
|
|
74
|
+
});
|
|
75
|
+
/**
|
|
76
|
+
* register result event
|
|
77
|
+
*/
|
|
78
|
+
this._browser.on('result', async (result) => {
|
|
79
|
+
var _a, _b;
|
|
80
|
+
return await ((_a = this._insightsHandler) === null || _a === void 0 ? void 0 : _a.browserCommand('client:afterCommand', Object.assign(result, { sessionId: (_b = this._browser) === null || _b === void 0 ? void 0 : _b.sessionId }), this._currentTest));
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return await this._printSessionURL();
|
|
58
84
|
}
|
|
59
85
|
/**
|
|
60
86
|
* Set the default job name at the suite level to make sure we account
|
|
@@ -69,8 +95,19 @@ class BrowserstackService {
|
|
|
69
95
|
await this._setSessionName(suite.title);
|
|
70
96
|
}
|
|
71
97
|
}
|
|
72
|
-
async
|
|
98
|
+
async beforeHook(test, context) {
|
|
99
|
+
var _a;
|
|
100
|
+
if (this._config.framework !== 'cucumber')
|
|
101
|
+
this._currentTest = test; // not update currentTest when this is called for cucumber step
|
|
102
|
+
await ((_a = this._insightsHandler) === null || _a === void 0 ? void 0 : _a.beforeHook(test, context));
|
|
103
|
+
}
|
|
104
|
+
async afterHook(test, context, result) {
|
|
73
105
|
var _a;
|
|
106
|
+
await ((_a = this._insightsHandler) === null || _a === void 0 ? void 0 : _a.afterHook(test, result));
|
|
107
|
+
}
|
|
108
|
+
async beforeTest(test) {
|
|
109
|
+
var _a, _b;
|
|
110
|
+
this._currentTest = test;
|
|
74
111
|
let suiteTitle = this._suiteTitle;
|
|
75
112
|
if (test.fullName) {
|
|
76
113
|
// For Jasmine, `suite.title` is `Jasmine__TopLevel__Suite`.
|
|
@@ -85,37 +122,19 @@ class BrowserstackService {
|
|
|
85
122
|
}
|
|
86
123
|
await this._setSessionName(suiteTitle, test);
|
|
87
124
|
await this._setAnnotation(`Test: ${(_a = test.fullName) !== null && _a !== void 0 ? _a : test.title}`);
|
|
125
|
+
await ((_b = this._insightsHandler) === null || _b === void 0 ? void 0 : _b.beforeTest(test));
|
|
88
126
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
async beforeFeature(uri, feature) {
|
|
93
|
-
this._suiteTitle = feature.name;
|
|
94
|
-
await this._setSessionName(feature.name);
|
|
95
|
-
await this._setAnnotation(`Feature: ${feature.name}`);
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Runs before a Cucumber Scenario.
|
|
99
|
-
* @param world world object containing information on pickle and test step
|
|
100
|
-
*/
|
|
101
|
-
async beforeScenario(world) {
|
|
102
|
-
const scenarioName = world.pickle.name || 'unknown scenario';
|
|
103
|
-
await this._setAnnotation(`Scenario: ${scenarioName}`);
|
|
104
|
-
}
|
|
105
|
-
/**
|
|
106
|
-
* For CucumberJS
|
|
107
|
-
*/
|
|
108
|
-
async beforeStep(step) {
|
|
109
|
-
const { keyword, text } = step;
|
|
110
|
-
await this._setAnnotation(`Step: ${keyword}${text}`);
|
|
111
|
-
}
|
|
112
|
-
afterTest(test, context, results) {
|
|
127
|
+
async afterTest(test, context, results) {
|
|
128
|
+
var _a;
|
|
129
|
+
this._specsRan = true;
|
|
113
130
|
const { error, passed } = results;
|
|
114
131
|
if (!passed) {
|
|
115
132
|
this._failReasons.push((error && error.message) || 'Unknown Error');
|
|
116
133
|
}
|
|
134
|
+
await ((_a = this._insightsHandler) === null || _a === void 0 ? void 0 : _a.afterTest(test, results));
|
|
117
135
|
}
|
|
118
136
|
async after(result) {
|
|
137
|
+
var _a, _b;
|
|
119
138
|
const { preferScenarioName, setSessionName, setSessionStatus } = this._options;
|
|
120
139
|
// For Cucumber: Checks scenarios that ran (i.e. not skipped) on the session
|
|
121
140
|
// Only 1 Scenario ran and option enabled => Redefine session name to Scenario's name
|
|
@@ -125,17 +144,36 @@ class BrowserstackService {
|
|
|
125
144
|
if (setSessionStatus) {
|
|
126
145
|
const hasReasons = this._failReasons.length > 0;
|
|
127
146
|
await this._updateJob({
|
|
128
|
-
status: result === 0 ? 'passed' : 'failed',
|
|
147
|
+
status: result === 0 && this._specsRan ? 'passed' : 'failed',
|
|
129
148
|
...(setSessionName ? { name: this._fullTitle } : {}),
|
|
130
149
|
...(hasReasons ? { reason: this._failReasons.join('\n') } : {})
|
|
131
150
|
});
|
|
132
151
|
}
|
|
152
|
+
await ((_a = this._insightsHandler) === null || _a === void 0 ? void 0 : _a.uploadPending());
|
|
153
|
+
await ((_b = this._insightsHandler) === null || _b === void 0 ? void 0 : _b.teardown());
|
|
133
154
|
}
|
|
134
155
|
/**
|
|
135
156
|
* For CucumberJS
|
|
136
157
|
*/
|
|
137
|
-
|
|
158
|
+
async beforeFeature(uri, feature) {
|
|
159
|
+
this._suiteTitle = feature.name;
|
|
160
|
+
await this._setSessionName(feature.name);
|
|
161
|
+
await this._setAnnotation(`Feature: ${feature.name}`);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Runs before a Cucumber Scenario.
|
|
165
|
+
* @param world world object containing information on pickle and test step
|
|
166
|
+
*/
|
|
167
|
+
async beforeScenario(world) {
|
|
138
168
|
var _a;
|
|
169
|
+
this._currentTest = world;
|
|
170
|
+
await ((_a = this._insightsHandler) === null || _a === void 0 ? void 0 : _a.beforeScenario(world));
|
|
171
|
+
const scenarioName = world.pickle.name || 'unknown scenario';
|
|
172
|
+
await this._setAnnotation(`Scenario: ${scenarioName}`);
|
|
173
|
+
}
|
|
174
|
+
async afterScenario(world) {
|
|
175
|
+
var _a, _b;
|
|
176
|
+
this._specsRan = true;
|
|
139
177
|
const status = (_a = world.result) === null || _a === void 0 ? void 0 : _a.status.toLowerCase();
|
|
140
178
|
if (status !== 'skipped') {
|
|
141
179
|
this._scenariosThatRan.push(world.pickle.name || 'unknown pickle name');
|
|
@@ -147,6 +185,16 @@ class BrowserstackService {
|
|
|
147
185
|
: 'Unknown Error'));
|
|
148
186
|
this._failReasons.push(exception);
|
|
149
187
|
}
|
|
188
|
+
await ((_b = this._insightsHandler) === null || _b === void 0 ? void 0 : _b.afterScenario(world));
|
|
189
|
+
}
|
|
190
|
+
async beforeStep(step, scenario) {
|
|
191
|
+
var _a;
|
|
192
|
+
await ((_a = this._insightsHandler) === null || _a === void 0 ? void 0 : _a.beforeStep(step, scenario));
|
|
193
|
+
await this._setAnnotation(`Step: ${step.keyword}${step.text}`);
|
|
194
|
+
}
|
|
195
|
+
async afterStep(step, scenario, result) {
|
|
196
|
+
var _a;
|
|
197
|
+
await ((_a = this._insightsHandler) === null || _a === void 0 ? void 0 : _a.afterStep(step, scenario, result));
|
|
150
198
|
}
|
|
151
199
|
async onReload(oldSessionId, newSessionId) {
|
|
152
200
|
if (!this._browser) {
|
|
@@ -198,13 +246,16 @@ class BrowserstackService {
|
|
|
198
246
|
return action(_browser.sessionId);
|
|
199
247
|
}
|
|
200
248
|
return Promise.all(_browser.instances
|
|
201
|
-
.filter(
|
|
249
|
+
.filter(browserName => {
|
|
202
250
|
const cap = (0, util_1.getBrowserCapabilities)(_browser, this._caps, browserName);
|
|
203
251
|
return (0, util_1.isBrowserstackCapability)(cap);
|
|
204
252
|
})
|
|
205
253
|
.map((browserName) => (action(_browser[browserName].sessionId, browserName))));
|
|
206
254
|
}
|
|
207
255
|
_update(sessionId, requestBody) {
|
|
256
|
+
if (!(0, util_1.isBrowserstackSession)(this._browser)) {
|
|
257
|
+
return Promise.resolve();
|
|
258
|
+
}
|
|
208
259
|
const sessionUrl = `${this._sessionBaseUrl}/${sessionId}.json`;
|
|
209
260
|
log.debug(`Updating Browserstack session at ${sessionUrl} with request body: `, requestBody);
|
|
210
261
|
return got_1.default.put(sessionUrl, {
|
|
@@ -214,7 +265,7 @@ class BrowserstackService {
|
|
|
214
265
|
});
|
|
215
266
|
}
|
|
216
267
|
async _printSessionURL() {
|
|
217
|
-
if (!this._browser) {
|
|
268
|
+
if (!this._browser || !(0, util_1.isBrowserstackSession)(this._browser)) {
|
|
218
269
|
return Promise.resolve();
|
|
219
270
|
}
|
|
220
271
|
await this._multiRemoteAction(async (sessionId, browserName) => {
|
|
@@ -256,7 +307,7 @@ class BrowserstackService {
|
|
|
256
307
|
return this._executeCommand('annotate', { data, level: 'info' });
|
|
257
308
|
}
|
|
258
309
|
async _executeCommand(action, args) {
|
|
259
|
-
if (!this._browser) {
|
|
310
|
+
if (!this._browser || !(0, util_1.isBrowserstackSession)(this._browser)) {
|
|
260
311
|
return Promise.resolve();
|
|
261
312
|
}
|
|
262
313
|
const cmd = { action, ...(args ? { arguments: args } : {}) };
|