@wdio/browserstack-service 7.34.0 → 7.35.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.
Files changed (39) hide show
  1. package/build/Percy/Percy-Handler.d.ts +34 -0
  2. package/build/Percy/Percy-Handler.d.ts.map +1 -0
  3. package/build/Percy/Percy-Handler.js +187 -0
  4. package/build/Percy/Percy.d.ts +24 -0
  5. package/build/Percy/Percy.d.ts.map +1 -0
  6. package/build/Percy/Percy.js +128 -0
  7. package/build/Percy/PercyBinary.d.ts +17 -0
  8. package/build/Percy/PercyBinary.d.ts.map +1 -0
  9. package/build/Percy/PercyBinary.js +151 -0
  10. package/build/Percy/PercyCaptureMap.d.ts +9 -0
  11. package/build/Percy/PercyCaptureMap.d.ts.map +1 -0
  12. package/build/Percy/PercyCaptureMap.js +46 -0
  13. package/build/Percy/PercyHelper.d.ts +8 -0
  14. package/build/Percy/PercyHelper.d.ts.map +1 -0
  15. package/build/Percy/PercyHelper.js +78 -0
  16. package/build/Percy/PercyLogger.d.ts +15 -0
  17. package/build/Percy/PercyLogger.d.ts.map +1 -0
  18. package/build/Percy/PercyLogger.js +76 -0
  19. package/build/Percy/PercySDK.d.ts +4 -0
  20. package/build/Percy/PercySDK.d.ts.map +1 -0
  21. package/build/Percy/PercySDK.js +42 -0
  22. package/build/constants.d.ts +3 -0
  23. package/build/constants.d.ts.map +1 -1
  24. package/build/constants.js +12 -1
  25. package/build/index.d.ts +2 -0
  26. package/build/index.d.ts.map +1 -1
  27. package/build/index.js +15 -1
  28. package/build/launcher.d.ts +7 -2
  29. package/build/launcher.d.ts.map +1 -1
  30. package/build/launcher.js +68 -0
  31. package/build/service.d.ts +2 -0
  32. package/build/service.d.ts.map +1 -1
  33. package/build/service.js +38 -14
  34. package/build/types.d.ts +15 -0
  35. package/build/types.d.ts.map +1 -1
  36. package/build/util.d.ts +2 -0
  37. package/build/util.d.ts.map +1 -1
  38. package/build/util.js +23 -2
  39. package/package.json +7 -4
@@ -0,0 +1,34 @@
1
+ import type { Capabilities } from '@wdio/types';
2
+ import type { BeforeCommandArgs, AfterCommandArgs } from '@wdio/reporter';
3
+ import type { Browser, MultiRemoteBrowser } from 'webdriverio';
4
+ declare class _PercyHandler {
5
+ private _percyAutoCaptureMode;
6
+ private _browser;
7
+ private _capabilities;
8
+ private _framework?;
9
+ private _testMetadata;
10
+ private _sessionName?;
11
+ private _isAppAutomate?;
12
+ private _isPercyCleanupProcessingUnderway?;
13
+ private _percyScreenshotCounter;
14
+ private _percyDeferredScreenshots;
15
+ private _percyScreenshotInterval;
16
+ private _percyCaptureMap?;
17
+ constructor(_percyAutoCaptureMode: string | undefined, _browser: Browser<'async'> | MultiRemoteBrowser<'async'>, _capabilities: Capabilities.RemoteCapability, isAppAutomate?: boolean, _framework?: string | undefined);
18
+ _setSessionName(name: string): void;
19
+ teardown(): Promise<void>;
20
+ percyAutoCapture(eventName: string | null, sessionName: string | null): Promise<void>;
21
+ before(): Promise<void>;
22
+ deferCapture(sessionName: string, eventName: string | null): void;
23
+ isDOMChangingCommand(args: BeforeCommandArgs): boolean;
24
+ cleanupDeferredScreenshots(): Promise<void>;
25
+ sleep(ms: number): Promise<unknown>;
26
+ browserBeforeCommand(args: BeforeCommandArgs): Promise<void>;
27
+ browserAfterCommand(args: BeforeCommandArgs & AfterCommandArgs): Promise<void>;
28
+ afterTest(): Promise<void>;
29
+ afterScenario(): Promise<void>;
30
+ }
31
+ declare const PercyHandler: typeof _PercyHandler;
32
+ type PercyHandler = _PercyHandler;
33
+ export default PercyHandler;
34
+ //# sourceMappingURL=Percy-Handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Percy-Handler.d.ts","sourceRoot":"","sources":["../../src/Percy/Percy-Handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAY9D,cAAM,aAAa;IAWX,OAAO,CAAC,qBAAqB;IAC7B,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,aAAa;IAErB,OAAO,CAAC,UAAU,CAAC;IAdvB,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,YAAY,CAAC,CAAQ;IAC7B,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,iCAAiC,CAAC,CAAiB;IAC3D,OAAO,CAAC,uBAAuB,CAAS;IACxC,OAAO,CAAC,yBAAyB,CAAU;IAC3C,OAAO,CAAC,wBAAwB,CAAY;IAC5C,OAAO,CAAC,gBAAgB,CAAC,CAAiB;gBAG9B,qBAAqB,EAAE,MAAM,GAAG,SAAS,EACzC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,EACxD,aAAa,EAAE,YAAY,CAAC,gBAAgB,EACpD,aAAa,CAAC,EAAE,OAAO,EACf,UAAU,CAAC,oBAAQ;IAQ/B,eAAe,CAAC,IAAI,EAAE,MAAM;IAItB,QAAQ;IAUR,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAmBrE,MAAM;IAIZ,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAM1D,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO;IAuBhD,0BAA0B;IAS1B,KAAK,CAAC,EAAE,EAAE,MAAM;IAIhB,oBAAoB,CAAE,IAAI,EAAE,iBAAiB;IAmB7C,mBAAmB,CAAE,IAAI,EAAE,iBAAiB,GAAG,gBAAgB;IAwB/D,SAAS;IAMT,aAAa;CAKtB;AAGD,QAAA,MAAM,YAAY,EAAE,OAAO,aAAoD,CAAA;AAC/E,KAAK,YAAY,GAAG,aAAa,CAAA;AAEjC,eAAe,YAAY,CAAA"}
@@ -0,0 +1,187 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ const util_1 = require("../util");
30
+ const PercyCaptureMap_1 = __importDefault(require("./PercyCaptureMap"));
31
+ const PercySDK = __importStar(require("./PercySDK"));
32
+ const PercyLogger_1 = require("./PercyLogger");
33
+ const constants_1 = require("../constants");
34
+ class _PercyHandler {
35
+ constructor(_percyAutoCaptureMode, _browser, _capabilities, isAppAutomate, _framework) {
36
+ this._percyAutoCaptureMode = _percyAutoCaptureMode;
37
+ this._browser = _browser;
38
+ this._capabilities = _capabilities;
39
+ this._framework = _framework;
40
+ this._testMetadata = {};
41
+ this._isPercyCleanupProcessingUnderway = false;
42
+ this._percyScreenshotCounter = 0;
43
+ this._percyDeferredScreenshots = [];
44
+ this._percyScreenshotInterval = null;
45
+ this._isAppAutomate = isAppAutomate;
46
+ if (!_percyAutoCaptureMode || !constants_1.CAPTURE_MODES.includes(_percyAutoCaptureMode)) {
47
+ this._percyAutoCaptureMode = 'auto';
48
+ }
49
+ }
50
+ _setSessionName(name) {
51
+ this._sessionName = name;
52
+ }
53
+ async teardown() {
54
+ await new Promise((resolve) => {
55
+ setInterval(() => {
56
+ if (this._percyScreenshotCounter === 0) {
57
+ resolve();
58
+ }
59
+ }, 1000);
60
+ });
61
+ }
62
+ async percyAutoCapture(eventName, sessionName) {
63
+ var _a, _b, _c, _d;
64
+ try {
65
+ if (eventName) {
66
+ if (!sessionName) {
67
+ /* Service doesn't wait for handling of browser commands so the below counter is used in teardown method to delay service exit */
68
+ this._percyScreenshotCounter += 1;
69
+ }
70
+ (_a = this._percyCaptureMap) === null || _a === void 0 ? void 0 : _a.increment(sessionName ? sessionName : this._sessionName, eventName);
71
+ await (this._isAppAutomate ? PercySDK.screenshotApp((_b = this._percyCaptureMap) === null || _b === void 0 ? void 0 : _b.getName(sessionName ? sessionName : this._sessionName, eventName)) : PercySDK.screenshot(this._browser, (_c = this._percyCaptureMap) === null || _c === void 0 ? void 0 : _c.getName(sessionName ? sessionName : this._sessionName, eventName)));
72
+ this._percyScreenshotCounter -= 1;
73
+ }
74
+ }
75
+ catch (err) {
76
+ this._percyScreenshotCounter -= 1;
77
+ (_d = this._percyCaptureMap) === null || _d === void 0 ? void 0 : _d.decrement(sessionName ? sessionName : this._sessionName, eventName);
78
+ PercyLogger_1.PercyLogger.error(`Error while trying to auto capture Percy screenshot ${err}`);
79
+ }
80
+ }
81
+ async before() {
82
+ this._percyCaptureMap = new PercyCaptureMap_1.default();
83
+ }
84
+ deferCapture(sessionName, eventName) {
85
+ /* Service doesn't wait for handling of browser commands so the below counter is used in teardown method to delay service exit */
86
+ this._percyScreenshotCounter += 1;
87
+ this._percyDeferredScreenshots.push({ sessionName, eventName });
88
+ }
89
+ isDOMChangingCommand(args) {
90
+ var _a;
91
+ if (args.method === 'POST') {
92
+ if (constants_1.PERCY_DOM_CHANGING_COMMANDS_ENDPOINTS.includes(args.endpoint)) {
93
+ return true;
94
+ }
95
+ else if (args.endpoint.includes('/session/:sessionId/element') && args.endpoint.includes('click')) {
96
+ /* click element */
97
+ return true;
98
+ }
99
+ else if (args.endpoint.includes('/session/:sessionId/element') && args.endpoint.includes('clear')) {
100
+ /* clear element */
101
+ return true;
102
+ }
103
+ else if (args.endpoint.includes('/session/:sessionId/execute') && ((_a = args.body) === null || _a === void 0 ? void 0 : _a.script)) {
104
+ /* execute script sync / async */
105
+ return true;
106
+ }
107
+ else if (args.endpoint.includes('/session/:sessionId/touch')) {
108
+ /* Touch action for Appium */
109
+ return true;
110
+ }
111
+ }
112
+ else if (args.method === 'DELETE' && args.endpoint === '/session/:sessionId') {
113
+ return true;
114
+ }
115
+ return false;
116
+ }
117
+ async cleanupDeferredScreenshots() {
118
+ this._isPercyCleanupProcessingUnderway = true;
119
+ for await (const entry of this._percyDeferredScreenshots) {
120
+ await this.percyAutoCapture(entry.eventName, entry.sessionName);
121
+ }
122
+ this._percyDeferredScreenshots = [];
123
+ this._isPercyCleanupProcessingUnderway = false;
124
+ }
125
+ async sleep(ms) {
126
+ return new Promise(resolve => setTimeout(resolve, ms));
127
+ }
128
+ async browserBeforeCommand(args) {
129
+ try {
130
+ if (this.isDOMChangingCommand(args)) {
131
+ do {
132
+ await this.sleep(1000);
133
+ } while (this._percyScreenshotInterval);
134
+ this._percyScreenshotInterval = setInterval(async () => {
135
+ if (!this._isPercyCleanupProcessingUnderway) {
136
+ clearInterval(this._percyScreenshotInterval);
137
+ await this.cleanupDeferredScreenshots();
138
+ this._percyScreenshotInterval = null;
139
+ }
140
+ }, 1000);
141
+ }
142
+ }
143
+ catch (err) {
144
+ PercyLogger_1.PercyLogger.error(`Error while trying to cleanup deferred screenshots ${err}`);
145
+ }
146
+ }
147
+ async browserAfterCommand(args) {
148
+ try {
149
+ if (args.endpoint && this._percyAutoCaptureMode) {
150
+ let eventName = null;
151
+ if (args.endpoint.includes('click') && ['click', 'auto'].includes(this._percyAutoCaptureMode)) {
152
+ eventName = 'click';
153
+ }
154
+ else if (args.endpoint.includes('screenshot') && ['screenshot', 'auto'].includes(this._percyAutoCaptureMode)) {
155
+ eventName = 'screenshot';
156
+ }
157
+ else if (args.endpoint.includes('actions') && ['auto'].includes(this._percyAutoCaptureMode)) {
158
+ if (args.body && args.body.actions && Array.isArray(args.body.actions) && args.body.actions.length && args.body.actions[0].type === 'key') {
159
+ eventName = 'keys';
160
+ }
161
+ }
162
+ else if (args.endpoint.includes('/session/:sessionId/element') && args.endpoint.includes('value') && ['auto'].includes(this._percyAutoCaptureMode)) {
163
+ eventName = 'keys';
164
+ }
165
+ if (eventName) {
166
+ this.deferCapture(this._sessionName, eventName);
167
+ }
168
+ }
169
+ }
170
+ catch (err) {
171
+ PercyLogger_1.PercyLogger.error(`Error while trying to calculate auto capture parameters ${err}`);
172
+ }
173
+ }
174
+ async afterTest() {
175
+ if (this._percyAutoCaptureMode && this._percyAutoCaptureMode === 'testcase') {
176
+ await this.percyAutoCapture('testcase', null);
177
+ }
178
+ }
179
+ async afterScenario() {
180
+ if (this._percyAutoCaptureMode && this._percyAutoCaptureMode === 'testcase') {
181
+ await this.percyAutoCapture('testcase', null);
182
+ }
183
+ }
184
+ }
185
+ // https://github.com/microsoft/TypeScript/issues/6543
186
+ const PercyHandler = (0, util_1.o11yClassErrorHandler)(_PercyHandler);
187
+ exports.default = PercyHandler;
@@ -0,0 +1,24 @@
1
+ import type { BrowserstackConfig, UserConfig } from '../types';
2
+ import type { Options } from '@wdio/types';
3
+ declare class Percy {
4
+ _logfile: string;
5
+ _address: string;
6
+ _binaryPath: string | any;
7
+ _options: BrowserstackConfig & Options.Testrunner;
8
+ _config: Options.Testrunner;
9
+ _proc: any;
10
+ _isApp: boolean;
11
+ _projectName: string | undefined;
12
+ isProcessRunning: boolean;
13
+ constructor(options: BrowserstackConfig & Options.Testrunner, config: Options.Testrunner, bsConfig: UserConfig);
14
+ private getBinaryPath;
15
+ private sleep;
16
+ healthcheck(): Promise<boolean | undefined>;
17
+ start(): Promise<boolean>;
18
+ stop(): Promise<unknown>;
19
+ isRunning(): boolean;
20
+ fetchPercyToken(): Promise<any>;
21
+ createPercyConfig(): Promise<unknown>;
22
+ }
23
+ export default Percy;
24
+ //# sourceMappingURL=Percy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Percy.d.ts","sourceRoot":"","sources":["../../src/Percy/Percy.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAI1C,cAAM,KAAK;IACP,QAAQ,EAAE,MAAM,CAAiC;IACjD,QAAQ,EAAE,MAAM,CAA8D;IAE9E,WAAW,EAAE,MAAM,GAAG,GAAG,CAAO;IAChC,QAAQ,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAA;IACjD,OAAO,EAAE,OAAO,CAAC,UAAU,CAAA;IAC3B,KAAK,EAAE,GAAG,CAAO;IACjB,MAAM,EAAE,OAAO,CAAA;IACf,YAAY,EAAE,MAAM,GAAG,SAAS,CAAY;IAE5C,gBAAgB,UAAQ;gBAEZ,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,UAAU,EAAE,QAAQ,EAAE,UAAU;YAOhG,aAAa;YAQb,KAAK;IAIb,WAAW;IAWX,KAAK;IA4CL,IAAI;IAWV,SAAS;IAIH,eAAe;IAsBf,iBAAiB;CA8B1B;AAED,eAAe,KAAK,CAAA"}
@@ -0,0 +1,128 @@
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 node_fs_1 = __importDefault(require("node:fs"));
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const node_os_1 = __importDefault(require("node:os"));
9
+ const node_child_process_1 = require("node:child_process");
10
+ const util_1 = require("../util");
11
+ const PercyLogger_1 = require("./PercyLogger");
12
+ const PercyBinary_1 = __importDefault(require("./PercyBinary"));
13
+ const logDir = 'logs';
14
+ class Percy {
15
+ constructor(options, config, bsConfig) {
16
+ this._logfile = node_path_1.default.join(logDir, 'percy.log');
17
+ this._address = process.env.PERCY_SERVER_ADDRESS || 'http://127.0.0.1:5338';
18
+ this._binaryPath = null;
19
+ this._proc = null;
20
+ this._projectName = undefined;
21
+ this.isProcessRunning = false;
22
+ this._options = options;
23
+ this._config = config;
24
+ this._isApp = Boolean(options.app);
25
+ this._projectName = bsConfig.projectName;
26
+ }
27
+ async getBinaryPath() {
28
+ if (!this._binaryPath) {
29
+ const pb = new PercyBinary_1.default();
30
+ this._binaryPath = await pb.getBinaryPath(this._config);
31
+ }
32
+ return this._binaryPath;
33
+ }
34
+ async sleep(ms) {
35
+ return new Promise(resolve => setTimeout(resolve, ms));
36
+ }
37
+ async healthcheck() {
38
+ try {
39
+ const resp = await (0, util_1.nodeRequest)('GET', 'percy/healthcheck', null, this._address);
40
+ if (resp) {
41
+ return true;
42
+ }
43
+ }
44
+ catch (err) {
45
+ return false;
46
+ }
47
+ }
48
+ async start() {
49
+ const binaryPath = await this.getBinaryPath();
50
+ const logStream = node_fs_1.default.createWriteStream(this._logfile, { flags: 'a' });
51
+ const token = await this.fetchPercyToken();
52
+ const configPath = await this.createPercyConfig();
53
+ if (!token) {
54
+ return false;
55
+ }
56
+ const commandArgs = [`${this._isApp ? 'app:exec' : 'exec'}:start`];
57
+ if (configPath) {
58
+ commandArgs.push('-c', configPath);
59
+ }
60
+ this._proc = (0, node_child_process_1.spawn)(binaryPath, commandArgs, { env: { ...process.env, PERCY_TOKEN: token } });
61
+ this._proc.stdout.pipe(logStream);
62
+ this._proc.stderr.pipe(logStream);
63
+ this.isProcessRunning = true;
64
+ const that = this;
65
+ this._proc.on('close', function () {
66
+ that.isProcessRunning = false;
67
+ });
68
+ do {
69
+ const healthcheck = await this.healthcheck();
70
+ if (healthcheck) {
71
+ PercyLogger_1.PercyLogger.debug('Percy healthcheck successful');
72
+ return true;
73
+ }
74
+ await this.sleep(1000);
75
+ } while (this.isProcessRunning);
76
+ return false;
77
+ }
78
+ async stop() {
79
+ const binaryPath = await this.getBinaryPath();
80
+ return new Promise((resolve) => {
81
+ const proc = (0, node_child_process_1.spawn)(binaryPath, ['exec:stop']);
82
+ proc.on('close', (code) => {
83
+ this.isProcessRunning = false;
84
+ resolve(code);
85
+ });
86
+ });
87
+ }
88
+ isRunning() {
89
+ return this.isProcessRunning;
90
+ }
91
+ async fetchPercyToken() {
92
+ const projectName = this._projectName;
93
+ try {
94
+ const type = this._isApp ? 'app' : 'automate';
95
+ const response = await (0, util_1.nodeRequest)('GET', `api/app_percy/get_project_token?name=${projectName}&type=${type}`, {
96
+ username: (0, util_1.getBrowserStackUser)(this._config),
97
+ password: (0, util_1.getBrowserStackKey)(this._config)
98
+ }, 'https://api.browserstack.com');
99
+ PercyLogger_1.PercyLogger.debug('Percy fetch token success : ' + response.token);
100
+ return response.token;
101
+ }
102
+ catch (err) {
103
+ PercyLogger_1.PercyLogger.error(`Percy unable to fetch project token: ${err}`);
104
+ return null;
105
+ }
106
+ }
107
+ async createPercyConfig() {
108
+ if (!this._options.percyOptions) {
109
+ return null;
110
+ }
111
+ const configPath = node_path_1.default.join(node_os_1.default.tmpdir(), 'percy.json');
112
+ const percyOptions = this._options.percyOptions;
113
+ if (!percyOptions.version) {
114
+ percyOptions.version = '2';
115
+ }
116
+ return new Promise((resolve) => {
117
+ node_fs_1.default.writeFile(configPath, JSON.stringify(percyOptions), (err) => {
118
+ if (err) {
119
+ PercyLogger_1.PercyLogger.error(`Error creating percy config: ${err}`);
120
+ resolve(null);
121
+ }
122
+ PercyLogger_1.PercyLogger.debug('Percy config created at ' + configPath);
123
+ resolve(configPath);
124
+ });
125
+ });
126
+ }
127
+ }
128
+ exports.default = Percy;
@@ -0,0 +1,17 @@
1
+ /// <reference types="node" />
2
+ import type { Options } from '@wdio/types';
3
+ declare class PercyBinary {
4
+ _hostOS: NodeJS.Platform;
5
+ _httpPath: any;
6
+ _binaryName: string;
7
+ _orderedPaths: string[];
8
+ constructor();
9
+ private makePath;
10
+ private checkPath;
11
+ private _getAvailableDirs;
12
+ getBinaryPath(conf: Options.Testrunner): Promise<string>;
13
+ validateBinary(binaryPath: string): Promise<unknown>;
14
+ download(conf: any, destParentDir: any): Promise<string>;
15
+ }
16
+ export default PercyBinary;
17
+ //# sourceMappingURL=PercyBinary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PercyBinary.d.ts","sourceRoot":"","sources":["../../src/Percy/PercyBinary.ts"],"names":[],"mappings":";AASA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAE1C,cAAM,WAAW;IACb,OAAO,kBAAmB;IAC1B,SAAS,EAAE,GAAG,CAAO;IACrB,WAAW,SAAU;IAErB,aAAa,WAIZ;;YAca,QAAQ;YAOR,SAAS;YAWT,iBAAiB;IAUzB,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAgBxD,cAAc,CAAC,UAAU,EAAE,MAAM;IAgBjC,QAAQ,CAAC,IAAI,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC;CAkEjE;AAED,eAAe,WAAW,CAAA"}
@@ -0,0 +1,151 @@
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 yauzl_1 = __importDefault(require("yauzl"));
7
+ const fs = require('node:fs');
8
+ const got_1 = __importDefault(require("got"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const node_os_1 = __importDefault(require("node:os"));
11
+ const promises_1 = __importDefault(require("node:fs/promises"));
12
+ const node_child_process_1 = require("node:child_process");
13
+ const PercyLogger_1 = require("./PercyLogger");
14
+ class PercyBinary {
15
+ constructor() {
16
+ this._hostOS = process.platform;
17
+ this._httpPath = null;
18
+ this._binaryName = 'percy';
19
+ this._orderedPaths = [
20
+ node_path_1.default.join(node_os_1.default.homedir(), '.browserstack'),
21
+ process.cwd(),
22
+ node_os_1.default.tmpdir()
23
+ ];
24
+ const base = 'https://github.com/percy/cli/releases/latest/download';
25
+ if (this._hostOS.match(/darwin|mac os/i)) {
26
+ this._httpPath = base + '/percy-osx.zip';
27
+ }
28
+ else if (this._hostOS.match(/mswin|msys|mingw|cygwin|bccwin|wince|emc|win32/i)) {
29
+ this._httpPath = base + '/percy-win.zip';
30
+ this._binaryName = 'percy.exe';
31
+ }
32
+ else {
33
+ this._httpPath = base + '/percy-linux.zip';
34
+ }
35
+ }
36
+ async makePath(path) {
37
+ if (await this.checkPath(path)) {
38
+ return true;
39
+ }
40
+ return promises_1.default.mkdir(path).then(() => true).catch(() => false);
41
+ }
42
+ async checkPath(path) {
43
+ try {
44
+ const hasDir = await promises_1.default.access(path).then(() => true, () => false);
45
+ if (hasDir) {
46
+ return true;
47
+ }
48
+ }
49
+ catch (err) {
50
+ return false;
51
+ }
52
+ }
53
+ async _getAvailableDirs() {
54
+ for (let i = 0; i < this._orderedPaths.length; i++) {
55
+ const path = this._orderedPaths[i];
56
+ if (await this.makePath(path)) {
57
+ return path;
58
+ }
59
+ }
60
+ throw new Error('Error trying to download percy binary');
61
+ }
62
+ async getBinaryPath(conf) {
63
+ const destParentDir = await this._getAvailableDirs();
64
+ const binaryPath = node_path_1.default.join(destParentDir, this._binaryName);
65
+ if (await this.checkPath(binaryPath)) {
66
+ return binaryPath;
67
+ }
68
+ const downloadedBinaryPath = await this.download(conf, destParentDir);
69
+ const isValid = await this.validateBinary(downloadedBinaryPath);
70
+ if (!isValid) {
71
+ // retry once
72
+ PercyLogger_1.PercyLogger.error('Corrupt percy binary, retrying');
73
+ return await this.download(conf, destParentDir);
74
+ }
75
+ return downloadedBinaryPath;
76
+ }
77
+ async validateBinary(binaryPath) {
78
+ const versionRegex = /^.*@percy\/cli \d.\d+.\d+/;
79
+ return new Promise((resolve) => {
80
+ const proc = (0, node_child_process_1.spawn)(binaryPath, ['--version']);
81
+ proc.stdout.on('data', (data) => {
82
+ if (versionRegex.test(data)) {
83
+ resolve(true);
84
+ }
85
+ });
86
+ proc.on('close', () => {
87
+ resolve(false);
88
+ });
89
+ });
90
+ }
91
+ async download(conf, destParentDir) {
92
+ if (!await this.checkPath(destParentDir)) {
93
+ await promises_1.default.mkdir(destParentDir);
94
+ }
95
+ const binaryName = this._binaryName;
96
+ const zipFilePath = node_path_1.default.join(destParentDir, binaryName + '.zip');
97
+ const binaryPath = node_path_1.default.join(destParentDir, binaryName);
98
+ const downloadedFileStream = fs.createWriteStream(zipFilePath);
99
+ return new Promise((resolve, reject) => {
100
+ const stream = got_1.default.extend({ followRedirect: true }).get(this._httpPath, { isStream: true });
101
+ stream.on('error', (err) => {
102
+ PercyLogger_1.PercyLogger.error(`Got Error in percy binary download response: ${err}`);
103
+ });
104
+ stream.pipe(downloadedFileStream)
105
+ .on('finish', () => {
106
+ yauzl_1.default.open(zipFilePath, { lazyEntries: true }, function (err, zipfile) {
107
+ if (err) {
108
+ return reject(err);
109
+ }
110
+ zipfile.readEntry();
111
+ zipfile.on('entry', (entry) => {
112
+ if (/\/$/.test(entry.fileName)) {
113
+ // Directory file names end with '/'.
114
+ zipfile.readEntry();
115
+ }
116
+ else {
117
+ // file entry
118
+ const writeStream = fs.createWriteStream(node_path_1.default.join(destParentDir, entry.fileName));
119
+ zipfile.openReadStream(entry, function (zipErr, readStream) {
120
+ if (zipErr) {
121
+ reject(err);
122
+ }
123
+ readStream.on('end', function () {
124
+ writeStream.close();
125
+ zipfile.readEntry();
126
+ });
127
+ readStream.pipe(writeStream);
128
+ });
129
+ if (entry.fileName === binaryName) {
130
+ zipfile.close();
131
+ }
132
+ }
133
+ });
134
+ zipfile.on('error', (zipErr) => {
135
+ reject(zipErr);
136
+ });
137
+ zipfile.once('end', () => {
138
+ fs.chmod(binaryPath, '0755', function (zipErr) {
139
+ if (zipErr) {
140
+ reject(zipErr);
141
+ }
142
+ resolve(binaryPath);
143
+ });
144
+ zipfile.close();
145
+ });
146
+ });
147
+ });
148
+ });
149
+ }
150
+ }
151
+ exports.default = PercyBinary;
@@ -0,0 +1,9 @@
1
+ declare class PercyCaptureMap {
2
+ #private;
3
+ increment(sessionName: string, eventName: string): void;
4
+ decrement(sessionName: string, eventName: string): void;
5
+ getName(sessionName: string, eventName: string): string;
6
+ get(sessionName: string, eventName: string): number;
7
+ }
8
+ export default PercyCaptureMap;
9
+ //# sourceMappingURL=PercyCaptureMap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PercyCaptureMap.d.ts","sourceRoot":"","sources":["../../src/Percy/PercyCaptureMap.ts"],"names":[],"mappings":"AAKA,cAAM,eAAe;;IAGjB,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAYhD,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAQhD,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAI9C,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAW7C;AAED,eAAe,eAAe,CAAA"}
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ /*
3
+ * Maintains a counter for each driver to get consistent and
4
+ * unique screenshot names for percy
5
+ */
6
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
7
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
8
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
9
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
10
+ };
11
+ var _PercyCaptureMap_map;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ class PercyCaptureMap {
14
+ constructor() {
15
+ _PercyCaptureMap_map.set(this, {});
16
+ }
17
+ increment(sessionName, eventName) {
18
+ if (!__classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName]) {
19
+ __classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName] = {};
20
+ }
21
+ if (!__classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName][eventName]) {
22
+ __classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName][eventName] = 0;
23
+ }
24
+ __classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName][eventName]++;
25
+ }
26
+ decrement(sessionName, eventName) {
27
+ if (!__classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName] || !__classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName][eventName]) {
28
+ return;
29
+ }
30
+ __classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName][eventName]--;
31
+ }
32
+ getName(sessionName, eventName) {
33
+ return `${sessionName}-${eventName}-${this.get(sessionName, eventName)}`;
34
+ }
35
+ get(sessionName, eventName) {
36
+ if (!__classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName]) {
37
+ return 0;
38
+ }
39
+ if (!__classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName][eventName]) {
40
+ return 0;
41
+ }
42
+ return __classPrivateFieldGet(this, _PercyCaptureMap_map, "f")[sessionName][eventName] - 1;
43
+ }
44
+ }
45
+ _PercyCaptureMap_map = new WeakMap();
46
+ exports.default = PercyCaptureMap;
@@ -0,0 +1,8 @@
1
+ import type { Capabilities } from '@wdio/types';
2
+ import type { BrowserstackConfig, UserConfig } from '../types';
3
+ import type { Options } from '@wdio/types';
4
+ import Percy from './Percy';
5
+ export declare const startPercy: (options: BrowserstackConfig & Options.Testrunner, config: Options.Testrunner, bsConfig: UserConfig) => Promise<Percy>;
6
+ export declare const stopPercy: (percy: Percy) => Promise<unknown>;
7
+ export declare const getBestPlatformForPercySnapshot: (capabilities?: Capabilities.RemoteCapabilities) => any;
8
+ //# sourceMappingURL=PercyHelper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PercyHelper.d.ts","sourceRoot":"","sources":["../../src/Percy/PercyHelper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAG1C,OAAO,KAAK,MAAM,SAAS,CAAA;AAE3B,eAAO,MAAM,UAAU,YAAmB,kBAAkB,GAAG,QAAQ,UAAU,UAAU,QAAQ,UAAU,YAAY,UAAU,KAAG,QAAQ,KAAK,CAQlJ,CAAA;AAED,eAAO,MAAM,SAAS,UAAiB,KAAK,qBAG3C,CAAA;AAED,eAAO,MAAM,+BAA+B,kBAAmB,aAAa,kBAAkB,KAAI,GAgDjG,CAAA"}