@percy/client 1.31.0-alpha.2 → 1.31.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/dist/client.js +48 -7
- package/dist/proxy.js +41 -3
- package/dist/utils.js +8 -1
- package/package.json +6 -5
package/dist/client.js
CHANGED
|
@@ -3,13 +3,13 @@ import PercyEnv from '@percy/env';
|
|
|
3
3
|
import { git } from '@percy/env/utils';
|
|
4
4
|
import logger from '@percy/logger';
|
|
5
5
|
import Pako from 'pako';
|
|
6
|
-
import { pool, request, formatBytes, sha256hash, base64encode, getPackageJSON, waitForTimeout, validateTiles, formatLogErrors, tagsList } from './utils.js';
|
|
6
|
+
import { pool, request, formatBytes, sha256hash, base64encode, getPackageJSON, waitForTimeout, validateTiles, formatLogErrors, tagsList, normalizeBrowsers } from './utils.js';
|
|
7
7
|
|
|
8
8
|
// Default client API URL can be set with an env var for API development
|
|
9
9
|
const {
|
|
10
10
|
PERCY_CLIENT_API_URL = 'https://percy.io/api/v1'
|
|
11
11
|
} = process.env;
|
|
12
|
-
|
|
12
|
+
let pkg = getPackageJSON(import.meta.url);
|
|
13
13
|
// minimum polling interval milliseconds
|
|
14
14
|
const MIN_POLLING_INTERVAL = 1_000;
|
|
15
15
|
const INVALID_TOKEN_ERROR_MESSAGE = 'Unable to retrieve snapshot details with write access token. Kindly use a full access token for retrieving snapshot details with Synchronous CLI.';
|
|
@@ -21,6 +21,25 @@ function validateId(type, id) {
|
|
|
21
21
|
throw new Error(`Invalid ${type} ID`);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
|
+
function makeRegions(regions, algorithm, algorithmConfiguration) {
|
|
25
|
+
let regionObj;
|
|
26
|
+
if (algorithm) {
|
|
27
|
+
regionObj = {};
|
|
28
|
+
regionObj.algorithm = algorithm;
|
|
29
|
+
regionObj.configuration = algorithmConfiguration;
|
|
30
|
+
}
|
|
31
|
+
if (!Array.isArray(regions) && !regionObj) return null;
|
|
32
|
+
if (regionObj) {
|
|
33
|
+
regions || (regions = []);
|
|
34
|
+
regions.push(regionObj);
|
|
35
|
+
}
|
|
36
|
+
return regions.map(region => ({
|
|
37
|
+
...region,
|
|
38
|
+
elementSelector: region.elementSelector || {
|
|
39
|
+
fullpage: true
|
|
40
|
+
}
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
24
43
|
|
|
25
44
|
// Validate project path arguments
|
|
26
45
|
function validateProjectPath(path) {
|
|
@@ -77,6 +96,9 @@ export class PercyClient {
|
|
|
77
96
|
|
|
78
97
|
// Stringifies client and environment info.
|
|
79
98
|
userAgent() {
|
|
99
|
+
// forcedPkgValue has been added since when percy package is bundled inside Electron app (LCNC)
|
|
100
|
+
// we can't read Percy's package json for package name and version, so we are passing it via env variables
|
|
101
|
+
if (this.env.forcedPkgValue) pkg = this.env.forcedPkgValue;
|
|
80
102
|
let client = new Set([`Percy/${/\w+$/.exec(this.apiUrl)}`].concat(`${pkg.name}/${pkg.version}`, ...this.clientInfo).filter(Boolean));
|
|
81
103
|
let environment = new Set([...this.environmentInfo].concat(`node/${process.version}`, this.env.info).filter(Boolean));
|
|
82
104
|
return `${[...client].join(' ')} (${[...environment].join('; ')})`;
|
|
@@ -143,9 +165,12 @@ export class PercyClient {
|
|
|
143
165
|
projectType,
|
|
144
166
|
cliStartTime = null
|
|
145
167
|
} = {}) {
|
|
168
|
+
var _this$config$percy2;
|
|
146
169
|
this.log.debug('Creating a new build...');
|
|
147
170
|
let source = 'user_created';
|
|
148
|
-
if (process.env.
|
|
171
|
+
if (process.env.PERCY_ORIGINATED_SOURCE) {
|
|
172
|
+
source = 'bstack_sdk_created';
|
|
173
|
+
} else if (process.env.PERCY_AUTO_ENABLED_GROUP_BUILD === 'true') {
|
|
149
174
|
source = 'auto_enabled_group';
|
|
150
175
|
}
|
|
151
176
|
let tagsArr = tagsList(this.labels);
|
|
@@ -170,7 +195,9 @@ export class PercyClient {
|
|
|
170
195
|
partial: this.env.partial,
|
|
171
196
|
tags: tagsArr,
|
|
172
197
|
'cli-start-time': cliStartTime,
|
|
173
|
-
source: source
|
|
198
|
+
source: source,
|
|
199
|
+
'skip-base-build': (_this$config$percy2 = this.config.percy) === null || _this$config$percy2 === void 0 ? void 0 : _this$config$percy2.skipBaseBuild,
|
|
200
|
+
'testhub-build-uuid': this.env.testhubBuildUuid
|
|
174
201
|
},
|
|
175
202
|
relationships: {
|
|
176
203
|
resources: {
|
|
@@ -378,8 +405,10 @@ export class PercyClient {
|
|
|
378
405
|
sha: resource.sha,
|
|
379
406
|
...meta
|
|
380
407
|
};
|
|
381
|
-
yield this.uploadResource(buildId, resource, resourceMeta)
|
|
382
|
-
|
|
408
|
+
yield this.uploadResource(buildId, resource, resourceMeta).then(result => {
|
|
409
|
+
this.log.debug(`Uploaded resource ${resource.url}`, resourceMeta);
|
|
410
|
+
return result;
|
|
411
|
+
});
|
|
383
412
|
}
|
|
384
413
|
}, this, uploadConcurrency);
|
|
385
414
|
}
|
|
@@ -399,6 +428,10 @@ export class PercyClient {
|
|
|
399
428
|
testCase,
|
|
400
429
|
labels,
|
|
401
430
|
thTestCaseExecutionId,
|
|
431
|
+
browsers,
|
|
432
|
+
regions,
|
|
433
|
+
algorithm,
|
|
434
|
+
algorithmConfiguration,
|
|
402
435
|
resources = [],
|
|
403
436
|
meta
|
|
404
437
|
} = {}) {
|
|
@@ -409,6 +442,7 @@ export class PercyClient {
|
|
|
409
442
|
this.log.warn('Warning: Missing `clientInfo` and/or `environmentInfo` properties', meta);
|
|
410
443
|
}
|
|
411
444
|
let tagsArr = tagsList(labels);
|
|
445
|
+
let regionsArr = makeRegions(regions, algorithm, algorithmConfiguration);
|
|
412
446
|
this.log.debug(`Validating resources: ${name}...`, meta);
|
|
413
447
|
for (let resource of resources) {
|
|
414
448
|
if (resource.sha || resource.content || !resource.filepath) continue;
|
|
@@ -426,10 +460,12 @@ export class PercyClient {
|
|
|
426
460
|
'test-case': testCase || null,
|
|
427
461
|
tags: tagsArr,
|
|
428
462
|
'scope-options': scopeOptions || {},
|
|
463
|
+
regions: regionsArr || null,
|
|
429
464
|
'minimum-height': minHeight || null,
|
|
430
465
|
'enable-javascript': enableJavaScript || null,
|
|
431
466
|
'enable-layout': enableLayout || false,
|
|
432
|
-
'th-test-case-execution-id': thTestCaseExecutionId || null
|
|
467
|
+
'th-test-case-execution-id': thTestCaseExecutionId || null,
|
|
468
|
+
browsers: normalizeBrowsers(browsers) || null
|
|
433
469
|
},
|
|
434
470
|
relationships: {
|
|
435
471
|
resources: {
|
|
@@ -495,6 +531,9 @@ export class PercyClient {
|
|
|
495
531
|
consideredElementsData,
|
|
496
532
|
metadata,
|
|
497
533
|
sync,
|
|
534
|
+
regions,
|
|
535
|
+
algorithm,
|
|
536
|
+
algorithmConfiguration,
|
|
498
537
|
meta = {}
|
|
499
538
|
} = {}) {
|
|
500
539
|
validateId('snapshot', snapshotId);
|
|
@@ -509,6 +548,7 @@ export class PercyClient {
|
|
|
509
548
|
tile.content = await fs.promises.readFile(tile.filepath);
|
|
510
549
|
}
|
|
511
550
|
}
|
|
551
|
+
let regionsArr = makeRegions(regions, algorithm, algorithmConfiguration);
|
|
512
552
|
this.log.debug(`${tiles.length} tiles for comparision: ${tag.name}...`, meta);
|
|
513
553
|
return this.post(`snapshots/${snapshotId}/comparisons`, {
|
|
514
554
|
data: {
|
|
@@ -516,6 +556,7 @@ export class PercyClient {
|
|
|
516
556
|
attributes: {
|
|
517
557
|
'external-debug-url': externalDebugUrl || null,
|
|
518
558
|
'ignore-elements-data': ignoredElementsData || null,
|
|
559
|
+
regions: regionsArr || null,
|
|
519
560
|
'consider-elements-data': consideredElementsData || null,
|
|
520
561
|
'dom-info-sha': domInfoSha || null,
|
|
521
562
|
sync: !!sync,
|
package/dist/proxy.js
CHANGED
|
@@ -4,9 +4,26 @@ import http from 'http';
|
|
|
4
4
|
import https from 'https';
|
|
5
5
|
import logger from '@percy/logger';
|
|
6
6
|
import { stripQuotesAndSpaces } from '@percy/env/utils';
|
|
7
|
+
import { PacProxyAgent } from 'pac-proxy-agent';
|
|
7
8
|
const CRLF = '\r\n';
|
|
8
9
|
const STATUS_REG = /^HTTP\/1.[01] (\d*)/;
|
|
9
10
|
|
|
11
|
+
// function to create PAC proxy agent
|
|
12
|
+
export function createPacAgent(pacUrl, options = {}) {
|
|
13
|
+
pacUrl = stripQuotesAndSpaces(pacUrl);
|
|
14
|
+
try {
|
|
15
|
+
const agent = new PacProxyAgent(pacUrl, {
|
|
16
|
+
keepAlive: true,
|
|
17
|
+
...options
|
|
18
|
+
});
|
|
19
|
+
logger('client:proxy').info(`Successfully loaded PAC file from: ${pacUrl}`);
|
|
20
|
+
return agent;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger('client:proxy').error(`Failed to load PAC file, error message: ${error.message}, stack: ${error.stack}`);
|
|
23
|
+
throw new Error(`Failed to initialize PAC proxy: ${error.message}`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
10
27
|
// Returns true if the URL hostname matches any patterns
|
|
11
28
|
export function hostnameMatches(patterns, url) {
|
|
12
29
|
let subject = new URL(url);
|
|
@@ -196,8 +213,29 @@ export function proxyAgentFor(url, options) {
|
|
|
196
213
|
hostname
|
|
197
214
|
} = new URL(url);
|
|
198
215
|
let cachekey = `${protocol}//${hostname}`;
|
|
199
|
-
|
|
200
|
-
|
|
216
|
+
|
|
217
|
+
// If we already have a cached agent, return it
|
|
218
|
+
if (cache.has(cachekey)) {
|
|
219
|
+
return cache.get(cachekey);
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
let agent;
|
|
223
|
+
const pacUrl = process.env.PERCY_PAC_FILE_URL;
|
|
224
|
+
|
|
225
|
+
// If PAC URL is provided, use PAC proxy
|
|
226
|
+
if (pacUrl) {
|
|
227
|
+
logger('client:proxy').info(`Using PAC file from: ${pacUrl}`);
|
|
228
|
+
agent = createPacAgent(pacUrl, options);
|
|
229
|
+
} else {
|
|
230
|
+
// Fall back to other proxy configuration
|
|
231
|
+
agent = protocol === 'https:' ? new ProxyHttpsAgent(options) : new ProxyHttpAgent(options);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Cache the created agent
|
|
235
|
+
cache.set(cachekey, agent);
|
|
236
|
+
return agent;
|
|
237
|
+
} catch (error) {
|
|
238
|
+
logger('client:proxy').error(`Failed to create proxy agent: ${error.message}`);
|
|
239
|
+
throw error;
|
|
201
240
|
}
|
|
202
|
-
return cache.get(cachekey);
|
|
203
241
|
}
|
package/dist/utils.js
CHANGED
|
@@ -4,6 +4,7 @@ import url from 'url';
|
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import crypto from 'crypto';
|
|
6
6
|
import logger from '@percy/logger';
|
|
7
|
+
import { snakecase } from '@percy/config/utils';
|
|
7
8
|
|
|
8
9
|
// Formats a raw byte integer as a string
|
|
9
10
|
export function formatBytes(int) {
|
|
@@ -141,9 +142,11 @@ export async function request(url, options = {}, callback) {
|
|
|
141
142
|
} = new URL(url);
|
|
142
143
|
|
|
143
144
|
// reference the default export so tests can mock it
|
|
145
|
+
// bundling cli inside electron or another package fails if we import it
|
|
146
|
+
// like this: await import(protocol === 'https:' ? 'https' : 'http');
|
|
144
147
|
let {
|
|
145
148
|
default: http
|
|
146
|
-
} =
|
|
149
|
+
} = protocol === 'https:' ? await import('https') : await import('http');
|
|
147
150
|
let {
|
|
148
151
|
proxyAgentFor
|
|
149
152
|
} = await import('./proxy.js');
|
|
@@ -278,4 +281,8 @@ export function tagsList(tags) {
|
|
|
278
281
|
}
|
|
279
282
|
return tagsArr;
|
|
280
283
|
}
|
|
284
|
+
export function normalizeBrowsers(browserValues) {
|
|
285
|
+
if (!browserValues) return null;
|
|
286
|
+
return browserValues.map(browser => snakecase(browser));
|
|
287
|
+
}
|
|
281
288
|
export { hostnameMatches, getProxy, ProxyHttpAgent, ProxyHttpsAgent, proxyAgentFor } from './proxy.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/client",
|
|
3
|
-
"version": "1.31.0
|
|
3
|
+
"version": "1.31.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"publishConfig": {
|
|
11
11
|
"access": "public",
|
|
12
|
-
"tag": "
|
|
12
|
+
"tag": "latest"
|
|
13
13
|
},
|
|
14
14
|
"engines": {
|
|
15
15
|
"node": ">=14"
|
|
@@ -33,9 +33,10 @@
|
|
|
33
33
|
"test:coverage": "yarn test --coverage"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@percy/env": "1.31.0
|
|
37
|
-
"@percy/logger": "1.31.0
|
|
36
|
+
"@percy/env": "1.31.0",
|
|
37
|
+
"@percy/logger": "1.31.0",
|
|
38
|
+
"pac-proxy-agent": "^7.0.2",
|
|
38
39
|
"pako": "^2.1.0"
|
|
39
40
|
},
|
|
40
|
-
"gitHead": "
|
|
41
|
+
"gitHead": "49895470c0dfa7242881db43e293317d1fb8f8b6"
|
|
41
42
|
}
|