@applitools/core 4.68.1 → 4.69.1
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/CHANGELOG.md +94 -0
- package/dist/automation/utils/wait-for-lazy-load.js +18 -1
- package/dist/figma/client/figma-req.js +46 -9
- package/dist/figma/client/figma-rest.js +47 -18
- package/dist/figma/client/index.js +2 -2
- package/dist/figma/figma.js +23 -9
- package/dist/figma/seam.js +6 -4
- package/dist/lang.js +2 -1
- package/dist/offline/run-offline-snapshots.js +25 -14
- package/dist/ufg/utils/take-dom-snapshot.js +1 -0
- package/package.json +16 -16
- package/types/figma/client/figma-req.d.ts +28 -2
- package/types/figma/client/figma-rest.d.ts +5 -8
- package/types/figma/figma.d.ts +3 -6
- package/types/lang.d.ts +1 -0
- package/types/offline/run-offline-snapshots.d.ts +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,99 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.69.1](https://github.com/Applitools-Dev/sdk/compare/js/core@4.69.0...js/core@4.69.1) (2026-08-23)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* keep the figma access token out of logs when masking is enabled | AD-15515 ([#4062](https://github.com/Applitools-Dev/sdk/issues/4062)) ([17bc177](https://github.com/Applitools-Dev/sdk/commit/17bc17789d88e2d92f32c66c3a43acb14eec4a70))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Performance Improvements
|
|
12
|
+
|
|
13
|
+
* CDT node dedup pass | AD-15243 ([baeab05](https://github.com/Applitools-Dev/sdk/commit/baeab05507e12d4d499050cd770191ef3cd9d0df))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Dependencies
|
|
17
|
+
|
|
18
|
+
* @applitools/dom-snapshot bumped to 4.18.1
|
|
19
|
+
#### Performance Improvements
|
|
20
|
+
|
|
21
|
+
* CDT node dedup pass | AD-15243 ([baeab05](https://github.com/Applitools-Dev/sdk/commit/baeab05507e12d4d499050cd770191ef3cd9d0df))
|
|
22
|
+
* @applitools/socket bumped to 1.3.17
|
|
23
|
+
#### Bug Fixes
|
|
24
|
+
|
|
25
|
+
* keep the figma access token out of logs when masking is enabled | AD-15515 ([#4062](https://github.com/Applitools-Dev/sdk/issues/4062)) ([17bc177](https://github.com/Applitools-Dev/sdk/commit/17bc17789d88e2d92f32c66c3a43acb14eec4a70))
|
|
26
|
+
* @applitools/image bumped to 1.3.2
|
|
27
|
+
#### Bug Fixes
|
|
28
|
+
|
|
29
|
+
* bump sharp to ^0.35.3 to clear libvips advisories | FLD-4829 ([#4113](https://github.com/Applitools-Dev/sdk/issues/4113)) ([90c2d08](https://github.com/Applitools-Dev/sdk/commit/90c2d0824cf3dfe56a074800907da6eb940ba350))
|
|
30
|
+
* @applitools/screenshoter bumped to 3.12.30
|
|
31
|
+
|
|
32
|
+
* @applitools/nml-client bumped to 1.12.1
|
|
33
|
+
|
|
34
|
+
* @applitools/tunnel-client bumped to 1.12.4
|
|
35
|
+
|
|
36
|
+
* @applitools/ufg-client bumped to 1.23.2
|
|
37
|
+
|
|
38
|
+
* @applitools/core-base bumped to 1.38.2
|
|
39
|
+
|
|
40
|
+
* @applitools/ec-client bumped to 1.12.44
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## [4.69.0](https://github.com/Applitools-Dev/sdk/compare/js/core@4.68.1...js/core@4.69.0) (2026-08-19)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
### Features
|
|
47
|
+
|
|
48
|
+
* label and tag figma logs, warn that the feature is experimental | AD-15681 ([#4108](https://github.com/Applitools-Dev/sdk/issues/4108)) ([5b20bf3](https://github.com/Applitools-Dev/sdk/commit/5b20bf327a857f09b9d3b1d55d59976390fae9fb))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
### Bug Fixes
|
|
52
|
+
|
|
53
|
+
* fail loudly when the Figma rate limit blocks further requests | AD-15531 ([#4065](https://github.com/Applitools-Dev/sdk/issues/4065)) ([9240e3e](https://github.com/Applitools-Dev/sdk/commit/9240e3ea6bed5225e8c9802cfb4669099968fc11))
|
|
54
|
+
* report a stalled lazyLoad pass instead of success under a scroll lock | FLD-3965 ([#4101](https://github.com/Applitools-Dev/sdk/issues/4101)) ([6244790](https://github.com/Applitools-Dev/sdk/commit/62447903464d27027f169d6b058d20e3e943ffda))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
### Code Refactoring
|
|
58
|
+
|
|
59
|
+
* make the figma logger and req explicit inputs | AD-15681 ([#4109](https://github.com/Applitools-Dev/sdk/issues/4109)) ([2a29778](https://github.com/Applitools-Dev/sdk/commit/2a2977865f1f26b0b55f7ab004c8ff737ed2573c))
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
### Dependencies
|
|
63
|
+
|
|
64
|
+
* @applitools/dom-snapshot bumped to 4.18.0
|
|
65
|
+
#### Features
|
|
66
|
+
|
|
67
|
+
* report canvases captured before they are drawn | FLD-3965 ([#4097](https://github.com/Applitools-Dev/sdk/issues/4097)) ([48b92ec](https://github.com/Applitools-Dev/sdk/commit/48b92ecba0804ad77333a6b4c799435d998f1418))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
* @applitools/snippets bumped to 2.9.7
|
|
72
|
+
#### Bug Fixes
|
|
73
|
+
|
|
74
|
+
* report a stalled lazyLoad pass instead of success under a scroll lock | FLD-3965 ([#4101](https://github.com/Applitools-Dev/sdk/issues/4101)) ([6244790](https://github.com/Applitools-Dev/sdk/commit/62447903464d27027f169d6b058d20e3e943ffda))
|
|
75
|
+
* @applitools/nml-client bumped to 1.12.0
|
|
76
|
+
#### Features
|
|
77
|
+
|
|
78
|
+
* seat region-check DOM via NML crop targetRegion | AD-14690 ([#4054](https://github.com/Applitools-Dev/sdk/issues/4054)) ([d442ff3](https://github.com/Applitools-Dev/sdk/commit/d442ff336b66144b55102d04b74175305b05169e))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
* @applitools/driver bumped to 1.26.9
|
|
83
|
+
|
|
84
|
+
* @applitools/spec-driver-webdriver bumped to 1.6.9
|
|
85
|
+
|
|
86
|
+
* @applitools/spec-driver-selenium bumped to 1.8.9
|
|
87
|
+
|
|
88
|
+
* @applitools/spec-driver-playwright bumped to 1.9.9
|
|
89
|
+
|
|
90
|
+
* @applitools/spec-driver-puppeteer bumped to 1.8.9
|
|
91
|
+
|
|
92
|
+
* @applitools/screenshoter bumped to 3.12.29
|
|
93
|
+
|
|
94
|
+
* @applitools/ec-client bumped to 1.12.43
|
|
95
|
+
|
|
96
|
+
|
|
3
97
|
## [4.68.1](https://github.com/Applitools-Dev/sdk/compare/js/core@4.68.0...js/core@4.68.1) (2026-08-10)
|
|
4
98
|
|
|
5
99
|
|
|
@@ -6,7 +6,7 @@ async function waitForLazyLoad({ context, settings = {}, logger, }) {
|
|
|
6
6
|
var _a;
|
|
7
7
|
logger.log('Running scrolling sequence to lazy load a view content');
|
|
8
8
|
const { scrollLength = 300, waitingTime = 2000, maxAmountToScroll = 15000 } = settings;
|
|
9
|
-
await context.executePoll(lazyLoad, {
|
|
9
|
+
const result = await context.executePoll(lazyLoad, {
|
|
10
10
|
main: [
|
|
11
11
|
await context.getScrollingElement(),
|
|
12
12
|
{
|
|
@@ -19,5 +19,22 @@ async function waitForLazyLoad({ context, settings = {}, logger, }) {
|
|
|
19
19
|
executionTimeout: 300000 /* 5min */,
|
|
20
20
|
pollTimeout: (_a = settings.pollTimeout) !== null && _a !== void 0 ? _a : waitingTime,
|
|
21
21
|
});
|
|
22
|
+
try {
|
|
23
|
+
const steps = Array.isArray(result) ? result : undefined;
|
|
24
|
+
const last = steps === null || steps === void 0 ? void 0 : steps[steps.length - 1];
|
|
25
|
+
if ((last === null || last === void 0 ? void 0 : last.reason) === 'stalled' || (last === null || last === void 0 ? void 0 : last.reason) === 'cappedAt') {
|
|
26
|
+
const coverage = `at ${last.maxScrollTop}px of ${last.maxScrollHeight}px`;
|
|
27
|
+
logger.console.log(last.reason === 'stalled'
|
|
28
|
+
? `Lazy loading stopped early: the page stopped scrolling ${coverage}, ` +
|
|
29
|
+
'which usually means something locked the page (for example a modal) while the content was being ' +
|
|
30
|
+
'scrolled through. Content below that point may be missing from the captured page.'
|
|
31
|
+
: `Lazy loading stopped early: the scrolling attempt limit was reached ${coverage}. ` +
|
|
32
|
+
'Content below that point may be missing from the captured page.');
|
|
33
|
+
logger.log('lazy load ended early', last);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
logger.log('Failed to inspect the lazy load result', error);
|
|
38
|
+
}
|
|
22
39
|
}
|
|
23
40
|
exports.waitForLazyLoad = waitForLazyLoad;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.makeFigmaReq = exports.FIGMA_API = void 0;
|
|
3
|
+
exports.makeFigmaReq = exports.figmaRateLimitMessage = exports.parseRetryAfterSeconds = exports.DEFAULT_MAX_RATE_LIMIT_WAIT_SECONDS = exports.FIGMA_API = void 0;
|
|
4
4
|
// The Figma REST transport: `@applitools/req` configured for Figma's rate limits.
|
|
5
5
|
const req_1 = require("@applitools/req");
|
|
6
6
|
exports.FIGMA_API = 'https://api.figma.com/v1';
|
|
@@ -9,7 +9,42 @@ exports.FIGMA_API = 'https://api.figma.com/v1';
|
|
|
9
9
|
const RETRY_LIMIT = 5;
|
|
10
10
|
// Only used when Figma omits `Retry-After`.
|
|
11
11
|
const RETRY_BACKOFF_MS = [1000, 2000, 5000, 10000];
|
|
12
|
-
|
|
12
|
+
exports.DEFAULT_MAX_RATE_LIMIT_WAIT_SECONDS = 60;
|
|
13
|
+
/** Undefined when Figma sent no `Retry-After`, or one this doesn't parse (e.g. an HTTP-date). */
|
|
14
|
+
function parseRetryAfterSeconds(headers) {
|
|
15
|
+
const seconds = Number(headers.get('Retry-After'));
|
|
16
|
+
return headers.has('Retry-After') && Number.isFinite(seconds) ? seconds : undefined;
|
|
17
|
+
}
|
|
18
|
+
exports.parseRetryAfterSeconds = parseRetryAfterSeconds;
|
|
19
|
+
/**
|
|
20
|
+
* The single wording for "Figma throttled us and we stopped trying", shared by the log line here and by
|
|
21
|
+
* the errors `figma-rest` throws — a user who sees only one of the two still gets the whole cause.
|
|
22
|
+
*/
|
|
23
|
+
function figmaRateLimitMessage(ending, maxRateLimitWaitSeconds) {
|
|
24
|
+
const budget = `${maxRateLimitWaitSeconds}s wait budget (figmaOptions.maxRateLimitWaitSeconds)`;
|
|
25
|
+
let cause;
|
|
26
|
+
switch (ending.reason) {
|
|
27
|
+
case 'retry-after-over-budget':
|
|
28
|
+
cause =
|
|
29
|
+
`Figma's Retry-After asked for ${ending.retryAfterSeconds}s, over the ${budget}, so no retry was ` +
|
|
30
|
+
`attempted. Raising that budget only helps for short waits — a Retry-After of hours or days cannot ` +
|
|
31
|
+
`be waited out.`;
|
|
32
|
+
break;
|
|
33
|
+
case 'wait-budget-exhausted':
|
|
34
|
+
cause = `retrying consumed the ${budget}. A larger budget can help when Figma's Retry-After is short.`;
|
|
35
|
+
break;
|
|
36
|
+
case 'attempts-exhausted':
|
|
37
|
+
cause =
|
|
38
|
+
`Figma was still throttling after the retry limit of ${RETRY_LIMIT} attempts, which is not ` +
|
|
39
|
+
`configurable — raising the ${budget} does not change it.`;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
return (`Figma API rate limit (HTTP 429): ${cause} Rerun against a warm on-disk Figma cache ` +
|
|
43
|
+
`(figmaOptions.tempPath / APPLITOOLS_FIGMA_TEMP_PATH — an earlier run must have populated it, and ` +
|
|
44
|
+
`figmaOptions.nodesCacheTtlSeconds must still cover it), or use a token that is not throttled.`);
|
|
45
|
+
}
|
|
46
|
+
exports.figmaRateLimitMessage = figmaRateLimitMessage;
|
|
47
|
+
function makeFigmaReq({ proxy, logger, maxRateLimitWaitSeconds = exports.DEFAULT_MAX_RATE_LIMIT_WAIT_SECONDS, } = {}) {
|
|
13
48
|
const maxWaitMs = maxRateLimitWaitSeconds * 1000;
|
|
14
49
|
return (0, req_1.makeReq)({
|
|
15
50
|
proxy,
|
|
@@ -26,17 +61,19 @@ function makeFigmaReq({ proxy, logger, maxRateLimitWaitSeconds = 60 } = {}) {
|
|
|
26
61
|
validate: ({ response }) => {
|
|
27
62
|
if ((response === null || response === void 0 ? void 0 : response.status) !== 429)
|
|
28
63
|
return false;
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
|
|
64
|
+
// No duration to weigh against the budget, so retry: on the backoff schedule when Figma sent no
|
|
65
|
+
// `Retry-After` at all, but immediately when it sent one this can't parse — req reads the header
|
|
66
|
+
// itself and turns an unparseable value into a NaN delay. Tracked in AD-15577.
|
|
67
|
+
const retryAfterSeconds = parseRetryAfterSeconds(response.headers);
|
|
68
|
+
if (retryAfterSeconds === undefined)
|
|
32
69
|
return true;
|
|
33
|
-
if (
|
|
34
|
-
logger === null || logger === void 0 ? void 0 : logger.
|
|
70
|
+
if (retryAfterSeconds > maxRateLimitWaitSeconds) {
|
|
71
|
+
logger === null || logger === void 0 ? void 0 : logger.error(figmaRateLimitMessage({ reason: 'retry-after-over-budget', retryAfterSeconds }, maxRateLimitWaitSeconds));
|
|
35
72
|
return false;
|
|
36
73
|
}
|
|
37
74
|
// Logged here because `beforeRetry` fires only after the sleep, leaving a long wait unexplained.
|
|
38
|
-
if (
|
|
39
|
-
logger === null || logger === void 0 ? void 0 : logger.log(`Figma rate limit — waiting ${
|
|
75
|
+
if (retryAfterSeconds > 0)
|
|
76
|
+
logger === null || logger === void 0 ? void 0 : logger.log(`Figma rate limit — waiting ${retryAfterSeconds}s before retry...`);
|
|
40
77
|
return true;
|
|
41
78
|
},
|
|
42
79
|
},
|
|
@@ -32,6 +32,7 @@ const fs = __importStar(require("fs"));
|
|
|
32
32
|
const os = __importStar(require("os"));
|
|
33
33
|
const path = __importStar(require("path"));
|
|
34
34
|
const buffer_1 = require("buffer");
|
|
35
|
+
const req_1 = require("@applitools/req");
|
|
35
36
|
const utils_1 = require("../utils");
|
|
36
37
|
const figma_req_1 = require("./figma-req");
|
|
37
38
|
const DEFAULT_NODES_CACHE_TTL_SECONDS = 120;
|
|
@@ -54,8 +55,34 @@ function figmaImageCacheFileName(fileId, nodeId, lastModifiedUnix) {
|
|
|
54
55
|
return `figma-image-${hash}.png`;
|
|
55
56
|
}
|
|
56
57
|
exports.figmaImageCacheFileName = figmaImageCacheFileName;
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Every `req` call in this file goes through here — the Figma API legs and the CDN image download alike.
|
|
60
|
+
* A terminal rate limit surfaces two ways, neither of which carries a diagnosis of its own, and each
|
|
61
|
+
* caller's own status message would bury it.
|
|
62
|
+
*/
|
|
63
|
+
async function figmaFetch(req, url, maxRateLimitWaitSeconds, init = {}) {
|
|
64
|
+
const response = await req(url, init).catch(error => {
|
|
65
|
+
// Waits that each fit the budget but together outlast it end here: req abandons the retry loop
|
|
66
|
+
// itself, so no 429 response is left to read. `name` rather than `instanceof` alone so an injected
|
|
67
|
+
// `opts.req` or a duplicated req copy still matches. `cause` keeps req's own error (and its
|
|
68
|
+
// `RETRY_DURATION_EXCEEDED` code) reachable — `Error`'s two-argument form is not typed at es2019.
|
|
69
|
+
if (error instanceof req_1.RetryTimeoutError || (error === null || error === void 0 ? void 0 : error.name) === 'RetryTimeoutError') {
|
|
70
|
+
throw Object.assign(new Error((0, figma_req_1.figmaRateLimitMessage)({ reason: 'wait-budget-exhausted' }, maxRateLimitWaitSeconds)), { cause: error });
|
|
71
|
+
}
|
|
72
|
+
throw error;
|
|
73
|
+
});
|
|
74
|
+
if (response.status === 429) {
|
|
75
|
+
// The retry policy in ./figma-req refuses an over-budget `Retry-After` outright and otherwise stops at
|
|
76
|
+
// the attempt limit. Which of the two happened decides what the user can do about it.
|
|
77
|
+
const retryAfterSeconds = (0, figma_req_1.parseRetryAfterSeconds)(response.headers);
|
|
78
|
+
throw new Error((0, figma_req_1.figmaRateLimitMessage)(retryAfterSeconds !== undefined && retryAfterSeconds > maxRateLimitWaitSeconds
|
|
79
|
+
? { reason: 'retry-after-over-budget', retryAfterSeconds }
|
|
80
|
+
: { reason: 'attempts-exhausted' }, maxRateLimitWaitSeconds));
|
|
81
|
+
}
|
|
82
|
+
return response;
|
|
83
|
+
}
|
|
84
|
+
async function figmaGet(req, endpoint, accessToken, maxRateLimitWaitSeconds) {
|
|
85
|
+
const response = await figmaFetch(req, `${figma_req_1.FIGMA_API}${endpoint}`, maxRateLimitWaitSeconds, {
|
|
59
86
|
headers: { 'X-Figma-Token': accessToken },
|
|
60
87
|
});
|
|
61
88
|
if (!response.ok) {
|
|
@@ -94,7 +121,7 @@ async function fetchFigmaFileNodes(urls, accessToken, opts) {
|
|
|
94
121
|
throw new Error(`APPLITOOLS_FIGMA_SKIP_API is set but no nodes cache found at ${nodesFilePath}`);
|
|
95
122
|
}
|
|
96
123
|
const ids = uniqueNodeIds.map(id => encodeURIComponent(id)).join(',');
|
|
97
|
-
nodesResp = await figmaGet(opts.req, `/files/${fileId}/nodes?ids=${ids}`, accessToken);
|
|
124
|
+
nodesResp = await figmaGet(opts.req, `/files/${fileId}/nodes?ids=${ids}`, accessToken, opts.maxRateLimitWaitSeconds);
|
|
98
125
|
fs.writeFileSync(nodesFilePath, JSON.stringify(nodesResp));
|
|
99
126
|
}
|
|
100
127
|
resolved.push({ fileId, nodesResp, nodes });
|
|
@@ -124,20 +151,23 @@ function metadataFromNodesResp(nodesResp, fileId, nodeId) {
|
|
|
124
151
|
function resolveRestOptions(opts) {
|
|
125
152
|
var _a, _b, _c, _d;
|
|
126
153
|
return {
|
|
127
|
-
tempPath: (_a = opts
|
|
128
|
-
ttlSeconds: (_b = opts
|
|
129
|
-
skipApi: (_c = opts
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
logger: opts === null || opts === void 0 ? void 0 : opts.logger,
|
|
133
|
-
maxRateLimitWaitSeconds: opts === null || opts === void 0 ? void 0 : opts.maxRateLimitWaitSeconds,
|
|
134
|
-
}),
|
|
154
|
+
tempPath: (_a = opts.tempPath) !== null && _a !== void 0 ? _a : os.tmpdir(),
|
|
155
|
+
ttlSeconds: (_b = opts.nodesCacheTtlSeconds) !== null && _b !== void 0 ? _b : DEFAULT_NODES_CACHE_TTL_SECONDS,
|
|
156
|
+
skipApi: (_c = opts.skipApi) !== null && _c !== void 0 ? _c : false,
|
|
157
|
+
maxRateLimitWaitSeconds: (_d = opts.maxRateLimitWaitSeconds) !== null && _d !== void 0 ? _d : figma_req_1.DEFAULT_MAX_RATE_LIMIT_WAIT_SECONDS,
|
|
158
|
+
req: opts.req,
|
|
135
159
|
};
|
|
136
160
|
}
|
|
137
161
|
// No /images render or PNG download — `test-baseline` mode sizes the viewport from metadata alone.
|
|
138
162
|
async function fetchFigmaMetadata(urls, accessToken, opts) {
|
|
139
|
-
const { tempPath, ttlSeconds, skipApi, req } = resolveRestOptions(opts);
|
|
140
|
-
const fileGroups = await fetchFigmaFileNodes(urls, accessToken, {
|
|
163
|
+
const { tempPath, ttlSeconds, skipApi, req, maxRateLimitWaitSeconds } = resolveRestOptions(opts);
|
|
164
|
+
const fileGroups = await fetchFigmaFileNodes(urls, accessToken, {
|
|
165
|
+
tempPath,
|
|
166
|
+
ttlSeconds,
|
|
167
|
+
skipApi,
|
|
168
|
+
req,
|
|
169
|
+
maxRateLimitWaitSeconds,
|
|
170
|
+
});
|
|
141
171
|
const results = new Array(urls.length);
|
|
142
172
|
for (const { fileId, nodesResp, nodes } of fileGroups) {
|
|
143
173
|
for (const { nodeId, urlIndex } of nodes) {
|
|
@@ -148,9 +178,8 @@ async function fetchFigmaMetadata(urls, accessToken, opts) {
|
|
|
148
178
|
}
|
|
149
179
|
exports.fetchFigmaMetadata = fetchFigmaMetadata;
|
|
150
180
|
async function fetchFigmaImages(urls, accessToken, opts) {
|
|
151
|
-
const { tempPath, skipApi, req } = resolveRestOptions(opts);
|
|
152
|
-
|
|
153
|
-
const metadataList = await fetchFigmaMetadata(urls, accessToken, { ...opts, req });
|
|
181
|
+
const { tempPath, skipApi, req, maxRateLimitWaitSeconds } = resolveRestOptions(opts);
|
|
182
|
+
const metadataList = await fetchFigmaMetadata(urls, accessToken, opts);
|
|
154
183
|
const results = new Array(urls.length);
|
|
155
184
|
for (const [fileId, nodes] of groupByFile(urls)) {
|
|
156
185
|
const uniqueNodeIds = [...new Set(nodes.map(n => n.nodeId))].sort();
|
|
@@ -165,7 +194,7 @@ async function fetchFigmaImages(urls, accessToken, opts) {
|
|
|
165
194
|
throw new Error(`APPLITOOLS_FIGMA_SKIP_API is set but no image cache found for node ${missNodeIds[0]} in file ${fileId}`);
|
|
166
195
|
}
|
|
167
196
|
const ids = missNodeIds.map(id => encodeURIComponent(id)).join(',');
|
|
168
|
-
const imagesResp = await figmaGet(req, `/images/${fileId}?ids=${ids}&format=png&scale=1`, accessToken);
|
|
197
|
+
const imagesResp = await figmaGet(req, `/images/${fileId}?ids=${ids}&format=png&scale=1`, accessToken, maxRateLimitWaitSeconds);
|
|
169
198
|
await Promise.all(missNodeIds.map(async (nodeId) => {
|
|
170
199
|
var _a;
|
|
171
200
|
const imageUrl = (_a = imagesResp.images) === null || _a === void 0 ? void 0 : _a[nodeId];
|
|
@@ -173,7 +202,7 @@ async function fetchFigmaImages(urls, accessToken, opts) {
|
|
|
173
202
|
throw new Error(`No image URL returned for node "${nodeId}"`);
|
|
174
203
|
}
|
|
175
204
|
// The CDN URL is pre-signed and carries no Figma token — no auth header here.
|
|
176
|
-
const imageResponse = await req
|
|
205
|
+
const imageResponse = await figmaFetch(req, imageUrl, maxRateLimitWaitSeconds);
|
|
177
206
|
if (!imageResponse.ok) {
|
|
178
207
|
throw new Error(`Failed to download Figma image: HTTP ${imageResponse.status}`);
|
|
179
208
|
}
|
|
@@ -15,7 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
// The Figma REST client: transport configuration (./figma-req) plus the batched /nodes + /images calls
|
|
18
|
-
// and their disk caches (./figma-rest). Takes
|
|
19
|
-
// it reads no environment of its own.
|
|
18
|
+
// and their disk caches (./figma-rest). Takes the `req` instance, token, skipApi and cache settings as
|
|
19
|
+
// inputs — it reads no environment of its own.
|
|
20
20
|
__exportStar(require("./figma-req"), exports);
|
|
21
21
|
__exportStar(require("./figma-rest"), exports);
|
package/dist/figma/figma.js
CHANGED
|
@@ -22,6 +22,9 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
25
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
29
|
exports.setFigmaBaseline = exports.setFigmaBaselineFlow = void 0;
|
|
27
30
|
// Figma baseline resolver (AD-14881): orchestration only, operating directly on core's OpenSettings
|
|
@@ -34,7 +37,18 @@ const target_config_1 = require("./target-config");
|
|
|
34
37
|
const client_1 = require("./client");
|
|
35
38
|
const figma_images_1 = require("./figma-images");
|
|
36
39
|
const run_figma_check_1 = require("./run-figma-check");
|
|
37
|
-
|
|
40
|
+
const lang = __importStar(require("../lang"));
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
let experimentalWarned = false;
|
|
43
|
+
function warnExperimental(logger) {
|
|
44
|
+
logger.warn(lang.figmaExperimentalWarning);
|
|
45
|
+
// The console copy is once per process — a storybook run resolves a design per story.
|
|
46
|
+
if (experimentalWarned)
|
|
47
|
+
return;
|
|
48
|
+
experimentalWarned = true;
|
|
49
|
+
logger.console.warn(chalk_1.default.yellow(lang.figmaExperimentalWarning));
|
|
50
|
+
}
|
|
51
|
+
async function setFigmaBaselineFlow(figmaUrls, originalConfig, deps, logger, mode = 'auto-baseline', viewportOpts = {}) {
|
|
38
52
|
var _a, _b;
|
|
39
53
|
const maxViewportHeight = (_a = viewportOpts.maxViewportHeight) !== null && _a !== void 0 ? _a : target_config_1.DEFAULT_MAX_VIEWPORT_HEIGHT;
|
|
40
54
|
const minViewportSize = (_b = viewportOpts.minViewportSize) !== null && _b !== void 0 ? _b : null;
|
|
@@ -124,9 +138,8 @@ async function setFigmaBaselineFlow(figmaUrls, originalConfig, deps, mode = 'aut
|
|
|
124
138
|
return (0, target_config_1.buildTargetTestConfig)(originalConfig, realResults[0].metadata, figmaViewport, { figmaEnvName, useLatestEnvBaseline: mode === 'auto-baseline' }, mode);
|
|
125
139
|
}
|
|
126
140
|
exports.setFigmaBaselineFlow = setFigmaBaselineFlow;
|
|
127
|
-
async function setFigmaBaseline(configuration, figmaBaselines, opts) {
|
|
128
|
-
|
|
129
|
-
const logger = (_a = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _a !== void 0 ? _a : (0, logger_1.makeLogger)();
|
|
141
|
+
async function setFigmaBaseline(configuration, figmaBaselines, logger, opts) {
|
|
142
|
+
logger = (0, logger_1.makeLogger)({ logger, format: { label: 'figma' } });
|
|
130
143
|
const mode = (0, utils_1.resolveFigmaMode)(opts === null || opts === void 0 ? void 0 : opts.mode);
|
|
131
144
|
if (mode === 'disabled') {
|
|
132
145
|
logger.log('setFigmaBaseline: disabled (APPLITOOLS_FIGMA_MODE=disabled) — skipping.');
|
|
@@ -145,9 +158,9 @@ async function setFigmaBaseline(configuration, figmaBaselines, opts) {
|
|
|
145
158
|
nodesCacheTtlSeconds: opts === null || opts === void 0 ? void 0 : opts.nodesCacheTtlSeconds,
|
|
146
159
|
maxRateLimitWaitSeconds: opts === null || opts === void 0 ? void 0 : opts.maxRateLimitWaitSeconds,
|
|
147
160
|
skipApi: opts === null || opts === void 0 ? void 0 : opts.skipApi,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
logger,
|
|
161
|
+
// The instance enforces the wait budget and figma-rest names it in the error, so both are given
|
|
162
|
+
// the same value — unset, they fall back to the one DEFAULT_MAX_RATE_LIMIT_WAIT_SECONDS.
|
|
163
|
+
req: (0, client_1.makeFigmaReq)({ proxy: opts === null || opts === void 0 ? void 0 : opts.proxy, logger, maxRateLimitWaitSeconds: opts === null || opts === void 0 ? void 0 : opts.maxRateLimitWaitSeconds }),
|
|
151
164
|
};
|
|
152
165
|
const deps = {
|
|
153
166
|
fetchFigmaImages: urlList => (0, client_1.fetchFigmaImages)(urlList, getToken(), restOpts),
|
|
@@ -159,10 +172,11 @@ async function setFigmaBaseline(configuration, figmaBaselines, opts) {
|
|
|
159
172
|
const figmaUrls = (0, utils_1.resolveFigmaUrls)(figmaBaselines, configuration.testName);
|
|
160
173
|
if (figmaUrls === null)
|
|
161
174
|
return;
|
|
162
|
-
|
|
175
|
+
warnExperimental(logger);
|
|
176
|
+
const updated = await setFigmaBaselineFlow(figmaUrls, configuration, deps, logger, mode, {
|
|
163
177
|
maxViewportHeight: opts === null || opts === void 0 ? void 0 : opts.maxViewportHeight,
|
|
164
178
|
minViewportSize: opts === null || opts === void 0 ? void 0 : opts.minViewportSize,
|
|
165
|
-
}
|
|
179
|
+
});
|
|
166
180
|
Object.assign(configuration, updated);
|
|
167
181
|
}
|
|
168
182
|
exports.setFigmaBaseline = setFigmaBaseline;
|
package/dist/figma/seam.js
CHANGED
|
@@ -45,6 +45,7 @@ async function applyFigmaBaselines(settings, logger, resolve = figma_1.setFigmaB
|
|
|
45
45
|
delete settings.figmaOptions;
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
+
logger = logger.extend({ label: 'figma', tags: [`figma-${utils.general.shortid()}`] });
|
|
48
49
|
try {
|
|
49
50
|
// A shared open config applies to every test, so an unmapped test must not fail — only skip.
|
|
50
51
|
if (typeof figmaBaselines !== 'string' && figmaBaselines[settings.testName] === undefined) {
|
|
@@ -58,13 +59,14 @@ async function applyFigmaBaselines(settings, logger, resolve = figma_1.setFigmaB
|
|
|
58
59
|
// A long TTL lets a pre-warmed cache (committed fixtures) serve through the normal cache-hit path
|
|
59
60
|
// instead of the all-or-nothing skipApi bypass. Number.isFinite also rejects a non-numeric env value.
|
|
60
61
|
const nodesCacheTtlSeconds = (_d = figmaOptions.nodesCacheTtlSeconds) !== null && _d !== void 0 ? _d : envTtl;
|
|
61
|
-
// Registered with the logger the way apiKey/secretToken are elsewhere
|
|
62
|
-
//
|
|
62
|
+
// Registered with the logger the way apiKey/secretToken are elsewhere. Masking is process-wide but
|
|
63
|
+
// takes effect only from this point on, and only when it is enabled (`maskLog` /
|
|
64
|
+
// APPLITOOLS_MASK_LOG). Log lines written before open reaches this seam — the universal socket's
|
|
65
|
+
// payload logs, for one — register the token themselves.
|
|
63
66
|
const accessToken = (_e = figmaOptions.accessToken) !== null && _e !== void 0 ? _e : process.env.FIGMA_ACCESS_TOKEN;
|
|
64
67
|
if (accessToken)
|
|
65
68
|
logger.mask(accessToken);
|
|
66
|
-
await resolve(settings, figmaBaselines, {
|
|
67
|
-
logger,
|
|
69
|
+
await resolve(settings, figmaBaselines, logger, {
|
|
68
70
|
skipApi,
|
|
69
71
|
mode: (0, utils_1.resolveFigmaMode)((_f = figmaOptions.mode) !== null && _f !== void 0 ? _f : utils.general.getEnvValue('FIGMA_MODE')),
|
|
70
72
|
accessToken,
|
package/dist/lang.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.applitoolsLibConflictingCapabilitiesWarning = exports.requestedEnvironmentOutOfDriverBoundsWarning = exports.breakpointOutOfDriverBoundsWarning = exports.requestedEnvironmentSmallerThenBreakpointWarning = exports.outdatedSdkVersionWarning = exports.edgeUfgEnvironmentDeprecatedWarning = exports.matchLevelContentDeprecatedWarning = void 0;
|
|
3
|
+
exports.applitoolsLibConflictingCapabilitiesWarning = exports.requestedEnvironmentOutOfDriverBoundsWarning = exports.breakpointOutOfDriverBoundsWarning = exports.requestedEnvironmentSmallerThenBreakpointWarning = exports.outdatedSdkVersionWarning = exports.figmaExperimentalWarning = exports.edgeUfgEnvironmentDeprecatedWarning = exports.matchLevelContentDeprecatedWarning = void 0;
|
|
4
4
|
exports.matchLevelContentDeprecatedWarning = `The "Content" match level value has been deprecated, use "IgnoreColors" instead.`;
|
|
5
5
|
exports.edgeUfgEnvironmentDeprecatedWarning = `The 'edge' option that is being used in your browsers' configuration will soon be deprecated. Please change it to either 'edgelegacy' for the legacy version or to 'edgechromium' for the new Chromium-based version. Please note, when using the built-in BrowserType enum, then the values are BrowserType.EDGE_LEGACY and BrowserType.EDGE_CHROMIUM, respectively.`;
|
|
6
|
+
exports.figmaExperimentalWarning = `Figma support is experimental. It is not officially supported and may change or be removed in a future release.`;
|
|
6
7
|
const outdatedSdkVersionWarning = (options) => {
|
|
7
8
|
return `You are using "${options.currentVersion}" and there is a newer version "${options.latestVersion}" of the "${options.name}" SDK.`;
|
|
8
9
|
};
|
|
@@ -26,7 +26,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
26
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.runOfflineSnapshots = void 0;
|
|
29
|
+
exports.runOfflineSnapshots = exports.toEyesServerSettings = void 0;
|
|
30
30
|
const fs_1 = __importDefault(require("fs"));
|
|
31
31
|
const path_1 = __importDefault(require("path"));
|
|
32
32
|
const core_1 = require("../core");
|
|
@@ -43,6 +43,22 @@ const os_1 = __importDefault(require("os"));
|
|
|
43
43
|
const extract_git_info_1 = require("../utils/extract-git-info");
|
|
44
44
|
const open_eyes_1 = require("../open-eyes");
|
|
45
45
|
const throttledReadFile = (0, throat_1.default)(Number(process.env.APPLITOOLS_FILE_READING_CONCURRENCY) || 1000, fs_1.default.promises.readFile);
|
|
46
|
+
/**
|
|
47
|
+
* @internal
|
|
48
|
+
* The settings a replay run hands to `getAccountInfo`, which logs them, and the sanitized config the
|
|
49
|
+
* run works with afterwards. `figmaBaselines`/`figmaOptions` are off-the-wire open config and
|
|
50
|
+
* `figmaOptions.accessToken` is a Figma credential; the open-eyes seam that strips them is bypassed by
|
|
51
|
+
* replay, and no mask is registered on this path, so both the flattened open config and the nested
|
|
52
|
+
* `config` are stripped here — spreading `options` re-introduces `config.open` in full otherwise.
|
|
53
|
+
*/
|
|
54
|
+
function toEyesServerSettings(options) {
|
|
55
|
+
const open = { ...options.config.open };
|
|
56
|
+
delete open.figmaBaselines;
|
|
57
|
+
delete open.figmaOptions;
|
|
58
|
+
const config = { ...options.config, open };
|
|
59
|
+
return { ...open, ...options, config };
|
|
60
|
+
}
|
|
61
|
+
exports.toEyesServerSettings = toEyesServerSettings;
|
|
46
62
|
function getConsoleLogLimit() {
|
|
47
63
|
var _a;
|
|
48
64
|
return (_a = utils.general.getEnvValue('OFFLINE_LOG_LIMIT', 'number')) !== null && _a !== void 0 ? _a : 20;
|
|
@@ -64,13 +80,8 @@ async function runOfflineSnapshots(options) {
|
|
|
64
80
|
const offlineLocationPath = path_1.default.resolve(options.offlineLocationPath);
|
|
65
81
|
const startTime = Date.now();
|
|
66
82
|
const logger = (_a = options.logger) !== null && _a !== void 0 ? _a : (0, logger_1.makeLogger)({ format: { label: 'offline-exec' } });
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// token, and getAccountInfo logs the settings it is called with.
|
|
70
|
-
const openConfig = { ...options.config.open };
|
|
71
|
-
delete openConfig.figmaBaselines;
|
|
72
|
-
delete openConfig.figmaOptions;
|
|
73
|
-
const eyesServerSettings = { ...openConfig, ...options };
|
|
83
|
+
const eyesServerSettings = toEyesServerSettings(options);
|
|
84
|
+
const config = eyesServerSettings.config;
|
|
74
85
|
const core = (0, core_1.makeCore)({ agentId: `js/core-offline/${version()}`, logger });
|
|
75
86
|
const account = await core.getAccountInfo({ settings: eyesServerSettings });
|
|
76
87
|
const ufgClient = await (0, get_ufg_client_1.makeGetUFGClient)({ logger })({
|
|
@@ -97,7 +108,7 @@ async function runOfflineSnapshots(options) {
|
|
|
97
108
|
testResults: allTestResults,
|
|
98
109
|
totalTime: Date.now() - startTime,
|
|
99
110
|
jsonFilePath: options.jsonFilePath,
|
|
100
|
-
saveNewTests:
|
|
111
|
+
saveNewTests: config.close.updateBaselineIfNew,
|
|
101
112
|
});
|
|
102
113
|
if (!isSuccess.onErrors && options.failOnError) {
|
|
103
114
|
throw new Error(outputStr);
|
|
@@ -118,7 +129,7 @@ async function runOfflineSnapshots(options) {
|
|
|
118
129
|
const fileOpenSettings = await fs_1.default.promises
|
|
119
130
|
.readFile(path_1.default.join(testPath, 'settings.json'), 'utf-8')
|
|
120
131
|
.then(JSON.parse);
|
|
121
|
-
const openSettings = (0, merge_configs_1.mergeConfigs)(fileOpenSettings,
|
|
132
|
+
const openSettings = (0, merge_configs_1.mergeConfigs)(fileOpenSettings, config.open);
|
|
122
133
|
messages.push(`Running test: ${openSettings.testName} (${formatEnvironment(openSettings.environment)})`);
|
|
123
134
|
return { testPath, openSettings, testLogger };
|
|
124
135
|
});
|
|
@@ -138,7 +149,7 @@ async function runOfflineSnapshots(options) {
|
|
|
138
149
|
const batchIds = [...new Set(results.map(t => t.batchId))];
|
|
139
150
|
const allTestResults = results.map(t => t.results);
|
|
140
151
|
logger.log('done running all tests', allTestResults);
|
|
141
|
-
const keepBatchOpen = utils.general.getEnvValue('DONT_CLOSE_BATCHES', 'boolean') ||
|
|
152
|
+
const keepBatchOpen = utils.general.getEnvValue('DONT_CLOSE_BATCHES', 'boolean') || config.open.keepBatchOpen;
|
|
142
153
|
if (!keepBatchOpen) {
|
|
143
154
|
await core.closeBatch({ settings: batchIds.map(batchId => ({ batchId, ...account.eyesServer })) });
|
|
144
155
|
logger.log('done closing batches');
|
|
@@ -218,7 +229,7 @@ async function runOfflineSnapshots(options) {
|
|
|
218
229
|
const closeSettingsPath = path_1.default.resolve(testPath, 'close.json');
|
|
219
230
|
if (fs_1.default.existsSync(closeSettingsPath)) {
|
|
220
231
|
const fileCloseSettings = await throttledReadFile(closeSettingsPath, 'utf-8').then(JSON.parse);
|
|
221
|
-
const closeSettings = (0, merge_configs_1.mergeConfigs)(fileCloseSettings,
|
|
232
|
+
const closeSettings = (0, merge_configs_1.mergeConfigs)(fileCloseSettings, config.close);
|
|
222
233
|
return closeSettings;
|
|
223
234
|
}
|
|
224
235
|
return undefined;
|
|
@@ -381,7 +392,7 @@ async function runOfflineSnapshots(options) {
|
|
|
381
392
|
if (accessibilityReportOptions === null || accessibilityReportOptions === void 0 ? void 0 : accessibilityReportOptions.uploadUrl)
|
|
382
393
|
(_b = baseTarget.accessibilityReportLocation) !== null && _b !== void 0 ? _b : (baseTarget.accessibilityReportLocation = accessibilityReportOptions.uploadUrl);
|
|
383
394
|
// baseTarget.name = snapshot.title // TODO figure out
|
|
384
|
-
const mergedCheckSettings = (0, merge_configs_1.mergeConfigs)(baseSettings,
|
|
395
|
+
const mergedCheckSettings = (0, merge_configs_1.mergeConfigs)(baseSettings, config.check);
|
|
385
396
|
return { mergedCheckSettings, baseTarget };
|
|
386
397
|
}
|
|
387
398
|
async function logStartupInfo() {
|
|
@@ -398,7 +409,7 @@ async function runOfflineSnapshots(options) {
|
|
|
398
409
|
testCount: testFolders.length,
|
|
399
410
|
ufgServerUrl: account.ufgServer.ufgServerUrl,
|
|
400
411
|
CI: process.env.CI,
|
|
401
|
-
config
|
|
412
|
+
config,
|
|
402
413
|
driver: {
|
|
403
414
|
platform: os_1.default.platform(),
|
|
404
415
|
version: os_1.default.release(),
|
|
@@ -50,6 +50,7 @@ async function takeDomSnapshot({ context, settings, logger, }) {
|
|
|
50
50
|
skipRootHtmlResource: settings === null || settings === void 0 ? void 0 : settings.skipRootHtmlResource,
|
|
51
51
|
fetchConcurrency: settings === null || settings === void 0 ? void 0 : settings.fetchConcurrency,
|
|
52
52
|
removeReverseProxyURLPrefixes: Boolean(process.env.APPLITOOLS_SCRIPT_REMOVE_REVERSE_PROXY_URL_PREFIXES),
|
|
53
|
+
skipCDTDedup: utils.general.getEnvValue('SKIP_CDT_DEDUP', 'boolean'),
|
|
53
54
|
chunkByteLength: (_a = settings === null || settings === void 0 ? void 0 : settings.chunkByteLength) !== null && _a !== void 0 ? _a : (Number(process.env.APPLITOOLS_SCRIPT_RESULT_MAX_BYTE_LENGTH) ||
|
|
54
55
|
(environment.isIOS ? 100000 : 250 * 1024 * 1024)),
|
|
55
56
|
serializeResources: true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applitools/core",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.69.1",
|
|
4
4
|
"homepage": "https://applitools.com",
|
|
5
5
|
"bugs": {
|
|
6
6
|
"url": "https://github.com/applitools/eyes.sdk.javascript1/issues"
|
|
@@ -61,19 +61,19 @@
|
|
|
61
61
|
"setup:standalone": "sh -c 'yarn chromedriver --port=4444 --verbose &'"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@applitools/core-base": "1.38.
|
|
64
|
+
"@applitools/core-base": "1.38.2",
|
|
65
65
|
"@applitools/dom-capture": "11.8.5",
|
|
66
|
-
"@applitools/dom-snapshot": "4.
|
|
67
|
-
"@applitools/driver": "1.26.
|
|
68
|
-
"@applitools/ec-client": "1.12.
|
|
69
|
-
"@applitools/image": "1.3.
|
|
66
|
+
"@applitools/dom-snapshot": "4.18.1",
|
|
67
|
+
"@applitools/driver": "1.26.9",
|
|
68
|
+
"@applitools/ec-client": "1.12.44",
|
|
69
|
+
"@applitools/image": "1.3.2",
|
|
70
70
|
"@applitools/logger": "2.3.1",
|
|
71
|
-
"@applitools/nml-client": "1.
|
|
71
|
+
"@applitools/nml-client": "1.12.1",
|
|
72
72
|
"@applitools/req": "1.11.3",
|
|
73
|
-
"@applitools/screenshoter": "3.12.
|
|
74
|
-
"@applitools/snippets": "2.9.
|
|
75
|
-
"@applitools/socket": "1.3.
|
|
76
|
-
"@applitools/ufg-client": "1.23.
|
|
73
|
+
"@applitools/screenshoter": "3.12.30",
|
|
74
|
+
"@applitools/snippets": "2.9.7",
|
|
75
|
+
"@applitools/socket": "1.3.17",
|
|
76
|
+
"@applitools/ufg-client": "1.23.2",
|
|
77
77
|
"@applitools/utils": "1.15.2",
|
|
78
78
|
"abort-controller": "3.0.0",
|
|
79
79
|
"chalk": "4.1.2",
|
|
@@ -84,13 +84,13 @@
|
|
|
84
84
|
},
|
|
85
85
|
"devDependencies": {
|
|
86
86
|
"@applitools/bongo": "^5.10.0",
|
|
87
|
-
"@applitools/spec-driver-playwright": "^1.9.
|
|
88
|
-
"@applitools/spec-driver-puppeteer": "^1.8.
|
|
89
|
-
"@applitools/spec-driver-selenium": "^1.8.
|
|
90
|
-
"@applitools/spec-driver-webdriver": "^1.6.
|
|
87
|
+
"@applitools/spec-driver-playwright": "^1.9.9",
|
|
88
|
+
"@applitools/spec-driver-puppeteer": "^1.8.9",
|
|
89
|
+
"@applitools/spec-driver-selenium": "^1.8.9",
|
|
90
|
+
"@applitools/spec-driver-webdriver": "^1.6.9",
|
|
91
91
|
"@applitools/test-server": "^1.4.7",
|
|
92
92
|
"@applitools/test-utils": "^1.5.17",
|
|
93
|
-
"@applitools/tunnel-client": "^1.12.
|
|
93
|
+
"@applitools/tunnel-client": "^1.12.4",
|
|
94
94
|
"@swc-node/register": "^1.6.8",
|
|
95
95
|
"@types/mocha": "^10.0.7",
|
|
96
96
|
"@types/node": "^12.20.55",
|
|
@@ -1,9 +1,35 @@
|
|
|
1
|
-
import { type Req, type Proxy } from '@applitools/req';
|
|
1
|
+
import { type Req, type Proxy, type Headers } from '@applitools/req';
|
|
2
2
|
import { type Logger } from '@applitools/logger';
|
|
3
3
|
export declare const FIGMA_API = "https://api.figma.com/v1";
|
|
4
|
+
export declare const DEFAULT_MAX_RATE_LIMIT_WAIT_SECONDS = 60;
|
|
5
|
+
/** Undefined when Figma sent no `Retry-After`, or one this doesn't parse (e.g. an HTTP-date). */
|
|
6
|
+
export declare function parseRetryAfterSeconds(headers: Headers): number | undefined;
|
|
7
|
+
/**
|
|
8
|
+
* Retrying a 429 can end three ways, and they have different remedies — the wait budget is the binding
|
|
9
|
+
* constraint in only two of them. Whoever observes the ending names it; nothing infers it after the fact.
|
|
10
|
+
*/
|
|
11
|
+
export type FigmaRateLimitEnding =
|
|
12
|
+
/** Figma asked for longer than the budget allows, so no retry was attempted. */
|
|
13
|
+
{
|
|
14
|
+
reason: 'retry-after-over-budget';
|
|
15
|
+
retryAfterSeconds: number;
|
|
16
|
+
}
|
|
17
|
+
/** Retried up to the attempt limit and Figma was still throttling. */
|
|
18
|
+
| {
|
|
19
|
+
reason: 'attempts-exhausted';
|
|
20
|
+
}
|
|
21
|
+
/** The waits themselves consumed the budget, so `req` abandoned the retry loop. */
|
|
22
|
+
| {
|
|
23
|
+
reason: 'wait-budget-exhausted';
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The single wording for "Figma throttled us and we stopped trying", shared by the log line here and by
|
|
27
|
+
* the errors `figma-rest` throws — a user who sees only one of the two still gets the whole cause.
|
|
28
|
+
*/
|
|
29
|
+
export declare function figmaRateLimitMessage(ending: FigmaRateLimitEnding, maxRateLimitWaitSeconds: number): string;
|
|
4
30
|
export type FigmaReqOptions = {
|
|
5
31
|
proxy?: Proxy;
|
|
6
32
|
logger?: Logger;
|
|
7
33
|
maxRateLimitWaitSeconds?: number;
|
|
8
34
|
};
|
|
9
|
-
export declare function makeFigmaReq({ proxy, logger, maxRateLimitWaitSeconds }?: FigmaReqOptions): Req;
|
|
35
|
+
export declare function makeFigmaReq({ proxy, logger, maxRateLimitWaitSeconds, }?: FigmaReqOptions): Req;
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import type { Proxy, Req } from '@applitools/req';
|
|
1
|
+
import { type Req } from '@applitools/req';
|
|
3
2
|
import type { FigmaImageResult, FigmaNodeMetadata } from '../types';
|
|
4
3
|
export type FigmaRestOptions = {
|
|
4
|
+
req: Req;
|
|
5
5
|
tempPath?: string;
|
|
6
6
|
nodesCacheTtlSeconds?: number;
|
|
7
|
+
/** Must be the budget `req` was built with: `req` enforces the wait, this names it in the error. */
|
|
7
8
|
maxRateLimitWaitSeconds?: number;
|
|
8
9
|
/** Serve from the on-disk caches only; throw on any miss. */
|
|
9
10
|
skipApi?: boolean;
|
|
10
|
-
proxy?: Proxy;
|
|
11
|
-
/** Defaults to `makeFigmaReq(…)`; tests inject a stub. */
|
|
12
|
-
req?: Req;
|
|
13
|
-
logger?: Logger;
|
|
14
11
|
};
|
|
15
12
|
/**
|
|
16
13
|
* Cache names are hashed because Figma node ids are not filename-safe — an instance node id looks like
|
|
@@ -20,5 +17,5 @@ export type FigmaRestOptions = {
|
|
|
20
17
|
*/
|
|
21
18
|
export declare function figmaNodesCacheFileName(fileId: string, uniqueNodeIds: string[]): string;
|
|
22
19
|
export declare function figmaImageCacheFileName(fileId: string, nodeId: string, lastModifiedUnix: number): string;
|
|
23
|
-
export declare function fetchFigmaMetadata(urls: string[], accessToken: string, opts
|
|
24
|
-
export declare function fetchFigmaImages(urls: string[], accessToken: string, opts
|
|
20
|
+
export declare function fetchFigmaMetadata(urls: string[], accessToken: string, opts: FigmaRestOptions): Promise<FigmaNodeMetadata[]>;
|
|
21
|
+
export declare function fetchFigmaImages(urls: string[], accessToken: string, opts: FigmaRestOptions): Promise<FigmaImageResult[]>;
|
package/types/figma/figma.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type Logger } from '@applitools/logger';
|
|
2
|
-
import type { Proxy
|
|
2
|
+
import type { Proxy } from '@applitools/req';
|
|
3
3
|
import type { OpenSettings } from '../automation/types';
|
|
4
4
|
import { type FigmaMode } from './utils';
|
|
5
5
|
import type { FigmaViewportOptions, FigmaBaselineDeps } from './types';
|
|
6
|
-
export declare function setFigmaBaselineFlow(figmaUrls: string[], originalConfig: Partial<OpenSettings>, deps: FigmaBaselineDeps, mode?: Exclude<FigmaMode, 'disabled'>, viewportOpts?: FigmaViewportOptions
|
|
7
|
-
export declare function setFigmaBaseline(configuration: Partial<OpenSettings>, figmaBaselines: string | Record<string, string | string[]>, opts?: {
|
|
6
|
+
export declare function setFigmaBaselineFlow(figmaUrls: string[], originalConfig: Partial<OpenSettings>, deps: FigmaBaselineDeps, logger: Logger, mode?: Exclude<FigmaMode, 'disabled'>, viewportOpts?: FigmaViewportOptions): Promise<Partial<OpenSettings>>;
|
|
7
|
+
export declare function setFigmaBaseline(configuration: Partial<OpenSettings>, figmaBaselines: string | Record<string, string | string[]>, logger: Logger, opts?: {
|
|
8
8
|
accessToken?: string;
|
|
9
9
|
maxRateLimitWaitSeconds?: number;
|
|
10
10
|
nodesCacheTtlSeconds?: number;
|
|
@@ -15,7 +15,4 @@ export declare function setFigmaBaseline(configuration: Partial<OpenSettings>, f
|
|
|
15
15
|
/** Serve from the disk caches only; throw on any miss. */
|
|
16
16
|
skipApi?: boolean;
|
|
17
17
|
proxy?: Proxy;
|
|
18
|
-
/** Defaults to the client's own req; tests inject a stub. */
|
|
19
|
-
req?: Req;
|
|
20
|
-
logger?: Logger;
|
|
21
18
|
}): Promise<void>;
|
package/types/lang.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const matchLevelContentDeprecatedWarning = "The \"Content\" match level value has been deprecated, use \"IgnoreColors\" instead.";
|
|
2
2
|
export declare const edgeUfgEnvironmentDeprecatedWarning = "The 'edge' option that is being used in your browsers' configuration will soon be deprecated. Please change it to either 'edgelegacy' for the legacy version or to 'edgechromium' for the new Chromium-based version. Please note, when using the built-in BrowserType enum, then the values are BrowserType.EDGE_LEGACY and BrowserType.EDGE_CHROMIUM, respectively.";
|
|
3
|
+
export declare const figmaExperimentalWarning = "Figma support is experimental. It is not officially supported and may change or be removed in a future release.";
|
|
3
4
|
export declare const outdatedSdkVersionWarning: (options: {
|
|
4
5
|
name: string;
|
|
5
6
|
currentVersion: string;
|
|
@@ -10,4 +10,15 @@ export type OfflineSnapshotsSettings = EyesServerSettings & {
|
|
|
10
10
|
logger?: Logger;
|
|
11
11
|
jsonFilePath?: string;
|
|
12
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* @internal
|
|
15
|
+
* The settings a replay run hands to `getAccountInfo`, which logs them, and the sanitized config the
|
|
16
|
+
* run works with afterwards. `figmaBaselines`/`figmaOptions` are off-the-wire open config and
|
|
17
|
+
* `figmaOptions.accessToken` is a Figma credential; the open-eyes seam that strips them is bypassed by
|
|
18
|
+
* replay, and no mask is registered on this path, so both the flattened open config and the nested
|
|
19
|
+
* `config` are stripped here — spreading `options` re-introduces `config.open` in full otherwise.
|
|
20
|
+
*/
|
|
21
|
+
export declare function toEyesServerSettings(options: OfflineSnapshotsSettings): EyesServerSettings & {
|
|
22
|
+
config: Config<SpecType, 'ufg'>;
|
|
23
|
+
};
|
|
13
24
|
export declare function runOfflineSnapshots(options: OfflineSnapshotsSettings): Promise<TestResult[]>;
|