@applitools/core 4.67.2 → 4.68.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +187 -0
  2. package/dist/autonomous/create-render-results.js +6 -1
  3. package/dist/autonomous/start-renders.js +16 -7
  4. package/dist/check-and-close.js +7 -1
  5. package/dist/check.js +8 -1
  6. package/dist/classic/utils/extract-default-environments.js +17 -2
  7. package/dist/figma/client/figma-req.js +46 -0
  8. package/dist/figma/client/figma-rest.js +189 -0
  9. package/dist/figma/client/index.js +21 -0
  10. package/dist/figma/figma-images.js +62 -0
  11. package/dist/figma/figma.js +168 -0
  12. package/dist/figma/index.js +27 -0
  13. package/dist/figma/run-figma-check.js +82 -0
  14. package/dist/figma/seam.js +91 -0
  15. package/dist/figma/target-config.js +52 -0
  16. package/dist/figma/types.js +2 -0
  17. package/dist/figma/utils.js +128 -0
  18. package/dist/offline/run-offline-snapshots.js +18 -3
  19. package/dist/open-eyes.js +4 -2
  20. package/dist/ufg/build-accessibility-report-options.js +40 -0
  21. package/dist/ufg/check-and-close.js +14 -5
  22. package/dist/ufg/check.js +10 -1
  23. package/package.json +23 -22
  24. package/types/automation/types.d.ts +9 -0
  25. package/types/autonomous/types.d.ts +1 -0
  26. package/types/check-and-close.d.ts +3 -2
  27. package/types/check.d.ts +3 -2
  28. package/types/figma/client/figma-req.d.ts +9 -0
  29. package/types/figma/client/figma-rest.d.ts +24 -0
  30. package/types/figma/client/index.d.ts +2 -0
  31. package/types/figma/figma-images.d.ts +6 -0
  32. package/types/figma/figma.d.ts +21 -0
  33. package/types/figma/index.d.ts +8 -0
  34. package/types/figma/run-figma-check.d.ts +13 -0
  35. package/types/figma/seam.d.ts +12 -0
  36. package/types/figma/target-config.d.ts +7 -0
  37. package/types/figma/types.d.ts +69 -0
  38. package/types/figma/utils.d.ts +26 -0
  39. package/types/types.d.ts +1 -0
  40. package/types/ufg/build-accessibility-report-options.d.ts +13 -0
@@ -0,0 +1,62 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.createPaddedDesignImage = exports.createPlaceholderPng = void 0;
27
+ // Image helpers for the Figma resolver: the white placeholder PNG for '' steps and the minViewportSize
28
+ // padding that centers a small design on a larger canvas. Both keyed by output dimensions so repeats reuse the file.
29
+ const fs = __importStar(require("fs"));
30
+ const path = __importStar(require("path"));
31
+ const image_1 = require("@applitools/image");
32
+ const utils_1 = require("./utils");
33
+ // Writes (or reuses) a placeholder PNG keyed by dimensions alone — content is always identical.
34
+ async function createPlaceholderPng(width, height, tempPath) {
35
+ const imagePath = path.join(tempPath, `figma-placeholder-${width}x${height}.png`);
36
+ if (!fs.existsSync(imagePath)) {
37
+ fs.writeFileSync(imagePath, await (0, image_1.generateWhitePng)(width, height));
38
+ }
39
+ return imagePath;
40
+ }
41
+ exports.createPlaceholderPng = createPlaceholderPng;
42
+ // Pads imagePath onto a centered white canvas at paddedDesignSize, returning the new path + canvas size
43
+ // (unchanged if it already meets minSize on both axes). Keyed by basename + canvas size so repeats reuse the file.
44
+ async function createPaddedDesignImage(imagePath, width, height, minSize, tempPath) {
45
+ const canvas = (0, utils_1.paddedDesignSize)(width, height, minSize);
46
+ if (canvas.width === width && canvas.height === height) {
47
+ return { imagePath, width, height };
48
+ }
49
+ const baseName = path.basename(imagePath, path.extname(imagePath));
50
+ const outputPath = path.join(tempPath, `figma-padded-${baseName}-${canvas.width}x${canvas.height}.png`);
51
+ if (!fs.existsSync(outputPath)) {
52
+ const canvasImage = (0, image_1.makeImage)(await (0, image_1.generateWhitePng)(canvas.width, canvas.height));
53
+ canvasImage.copy((0, image_1.makeImage)(imagePath), {
54
+ x: Math.floor((canvas.width - width) / 2),
55
+ y: Math.floor((canvas.height - height) / 2),
56
+ });
57
+ // toPng (not toFile) — Image.toFile writes the pre-transform pixels, dropping the copy above.
58
+ fs.writeFileSync(outputPath, await canvasImage.toPng());
59
+ }
60
+ return { imagePath: outputPath, ...canvas };
61
+ }
62
+ exports.createPaddedDesignImage = createPaddedDesignImage;
@@ -0,0 +1,168 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.setFigmaBaseline = exports.setFigmaBaselineFlow = void 0;
27
+ // Figma baseline resolver (AD-14881): orchestration only, operating directly on core's OpenSettings
28
+ // (the eyes/cypress/non-JS bindings adapt the flat config to this shape). Helpers split into ./utils,
29
+ // ./client, ./figma-images, ./target-config.
30
+ const os = __importStar(require("os"));
31
+ const logger_1 = require("@applitools/logger");
32
+ const utils_1 = require("./utils");
33
+ const target_config_1 = require("./target-config");
34
+ const client_1 = require("./client");
35
+ const figma_images_1 = require("./figma-images");
36
+ const run_figma_check_1 = require("./run-figma-check");
37
+ async function setFigmaBaselineFlow(figmaUrls, originalConfig, deps, mode = 'auto-baseline', viewportOpts = {}, logger = (0, logger_1.makeLogger)()) {
38
+ var _a, _b;
39
+ const maxViewportHeight = (_a = viewportOpts.maxViewportHeight) !== null && _a !== void 0 ? _a : target_config_1.DEFAULT_MAX_VIEWPORT_HEIGHT;
40
+ const minViewportSize = (_b = viewportOpts.minViewportSize) !== null && _b !== void 0 ? _b : null;
41
+ for (const url of figmaUrls) {
42
+ if (url !== '' && !(0, utils_1.isFigmaUrl)(url)) {
43
+ throw new Error(`setFigmaBaseline: "${url}" is not a valid Figma URL. ` + `To skip a test, use an empty string.`);
44
+ }
45
+ }
46
+ // '' entries are placeholder steps; resolveFigmaUrls guarantees at least one real URL here.
47
+ const realUrls = figmaUrls.filter(url => url !== '');
48
+ if (mode === 'test-baseline') {
49
+ logger.log(`Fetching Figma design metadata: ${realUrls[0]}${realUrls.length > 1 ? ` (+${realUrls.length - 1} more)` : ''}`);
50
+ const rawMetadataList = await deps.fetchFigmaMetadata(realUrls);
51
+ logger.log(`Figma node: "${rawMetadataList[0].name}" (${rawMetadataList[0].type}) ${rawMetadataList[0].width}x${rawMetadataList[0].height}, last modified ${rawMetadataList[0].lastModifiedAt}`);
52
+ logger.log(`Mode is test-baseline — skipping Figma change detection; adjusting viewport only.`);
53
+ // test-baseline never fetches images, so minViewportSize floors the metadata dimensions directly.
54
+ const metadataList = minViewportSize != null
55
+ ? rawMetadataList.map(m => ({ ...m, ...(0, utils_1.paddedDesignSize)(m.width, m.height, minViewportSize) }))
56
+ : rawMetadataList;
57
+ const stepViewports = metadataList.map(m => ({ width: m.width, height: m.height }));
58
+ const figmaViewport = (0, utils_1.computeFigmaViewport)(stepViewports, maxViewportHeight);
59
+ logger.log(`Figma viewport: ${figmaViewport.width}x${figmaViewport.height}`);
60
+ return (0, target_config_1.buildTargetTestConfig)(originalConfig, metadataList[0], figmaViewport, null, mode);
61
+ }
62
+ logger.log(`Fetching Figma design: ${realUrls[0]}${realUrls.length > 1 ? ` (+${realUrls.length - 1} more)` : ''}`);
63
+ const rawResults = await deps.fetchFigmaImages(realUrls);
64
+ logger.log(`Figma node: "${rawResults[0].metadata.name}" (${rawResults[0].metadata.type}) ${rawResults[0].metadata.width}x${rawResults[0].metadata.height}, last modified ${rawResults[0].metadata.lastModifiedAt}`);
65
+ const realResults = minViewportSize != null
66
+ ? await Promise.all(rawResults.map(async (r) => {
67
+ const padded = await deps.padDesignImage(r.imagePath, r.metadata.width, r.metadata.height, minViewportSize);
68
+ return { imagePath: padded.imagePath, metadata: { ...r.metadata, width: padded.width, height: padded.height } };
69
+ }))
70
+ : rawResults;
71
+ // The figma viewport bounds every step, so a multi-step test with differently-sized frames renders at one consistent size.
72
+ const stepViewports = realResults.map(r => ({ width: r.metadata.width, height: r.metadata.height }));
73
+ const figmaViewport = (0, utils_1.computeFigmaViewport)(stepViewports, maxViewportHeight);
74
+ const figmaEnvName = `Figma ${figmaViewport.width}x${figmaViewport.height}`;
75
+ logger.log(`Figma viewport: ${figmaViewport.width}x${figmaViewport.height}`);
76
+ // Splice placeholder steps back at their original positions, using a blank image at the shared figma viewport.
77
+ const results = [];
78
+ let realResultIndex = 0;
79
+ for (const url of figmaUrls) {
80
+ if (url === '') {
81
+ const imagePath = await deps.createPlaceholderImage(figmaViewport.width, figmaViewport.height);
82
+ results.push({
83
+ imagePath,
84
+ metadata: {
85
+ name: target_config_1.PLACEHOLDER_TAG,
86
+ type: 'PLACEHOLDER',
87
+ width: figmaViewport.width,
88
+ height: figmaViewport.height,
89
+ revision: 'placeholder',
90
+ lastModifiedAt: '',
91
+ },
92
+ });
93
+ }
94
+ else {
95
+ results.push(realResults[realResultIndex]);
96
+ realResultIndex++;
97
+ }
98
+ }
99
+ logger.log(`Establishing Figma baseline...`);
100
+ const figmaReferenceConfig = {
101
+ apiKey: originalConfig.apiKey,
102
+ // Same server + proxy as the real test — otherwise on-prem users get the baseline on the public cloud.
103
+ eyesServerUrl: originalConfig.eyesServerUrl,
104
+ proxy: originalConfig.proxy,
105
+ batch: originalConfig.batch,
106
+ appName: originalConfig.appName,
107
+ testName: originalConfig.testName,
108
+ environment: { hostingApp: 'Figma', viewportSize: figmaViewport },
109
+ properties: (0, utils_1.figmaMetadataToProperties)(realResults[0].metadata, mode),
110
+ baselineEnvName: figmaEnvName,
111
+ saveNewTests: true,
112
+ saveDiffs: true,
113
+ keepBatchOpen: true,
114
+ };
115
+ if (originalConfig.branchName !== undefined)
116
+ figmaReferenceConfig.branchName = originalConfig.branchName;
117
+ const checks = results.map(({ imagePath }, i) => ({
118
+ imagePath,
119
+ tag: figmaUrls[i] === '' ? target_config_1.PLACEHOLDER_TAG : (0, utils_1.figmaStepTag)(figmaUrls[i]),
120
+ }));
121
+ const { isNew: figmaIsNew, diffs: figmaDiffs } = await deps.runFigmaCheck(figmaReferenceConfig, checks);
122
+ logger.log(`Figma baseline: ${figmaIsNew ? 'new (first run)' : !figmaDiffs ? 'passed (design unchanged)' : 'changed (design updated)'}`);
123
+ logger.log(`Linking test to Figma baseline: ${figmaEnvName}`);
124
+ return (0, target_config_1.buildTargetTestConfig)(originalConfig, realResults[0].metadata, figmaViewport, { figmaEnvName, useLatestEnvBaseline: mode === 'auto-baseline' }, mode);
125
+ }
126
+ exports.setFigmaBaselineFlow = setFigmaBaselineFlow;
127
+ async function setFigmaBaseline(configuration, figmaBaselines, opts) {
128
+ var _a;
129
+ const logger = (_a = opts === null || opts === void 0 ? void 0 : opts.logger) !== null && _a !== void 0 ? _a : (0, logger_1.makeLogger)();
130
+ const mode = (0, utils_1.resolveFigmaMode)(opts === null || opts === void 0 ? void 0 : opts.mode);
131
+ if (mode === 'disabled') {
132
+ logger.log('setFigmaBaseline: disabled (APPLITOOLS_FIGMA_MODE=disabled) — skipping.');
133
+ return;
134
+ }
135
+ const getToken = () => {
136
+ var _a;
137
+ if (!(opts === null || opts === void 0 ? void 0 : opts.skipApi) && !(opts === null || opts === void 0 ? void 0 : opts.accessToken)) {
138
+ throw new Error('setFigmaBaseline: no Figma access token. ' +
139
+ 'Pass accessToken or set the FIGMA_ACCESS_TOKEN environment variable.');
140
+ }
141
+ return (_a = opts === null || opts === void 0 ? void 0 : opts.accessToken) !== null && _a !== void 0 ? _a : '';
142
+ };
143
+ const restOpts = {
144
+ tempPath: opts === null || opts === void 0 ? void 0 : opts.tempPath,
145
+ nodesCacheTtlSeconds: opts === null || opts === void 0 ? void 0 : opts.nodesCacheTtlSeconds,
146
+ maxRateLimitWaitSeconds: opts === null || opts === void 0 ? void 0 : opts.maxRateLimitWaitSeconds,
147
+ skipApi: opts === null || opts === void 0 ? void 0 : opts.skipApi,
148
+ proxy: opts === null || opts === void 0 ? void 0 : opts.proxy,
149
+ req: opts === null || opts === void 0 ? void 0 : opts.req,
150
+ logger,
151
+ };
152
+ const deps = {
153
+ fetchFigmaImages: urlList => (0, client_1.fetchFigmaImages)(urlList, getToken(), restOpts),
154
+ fetchFigmaMetadata: urlList => (0, client_1.fetchFigmaMetadata)(urlList, getToken(), restOpts),
155
+ createPlaceholderImage: (width, height) => { var _a; return (0, figma_images_1.createPlaceholderPng)(width, height, (_a = opts === null || opts === void 0 ? void 0 : opts.tempPath) !== null && _a !== void 0 ? _a : os.tmpdir()); },
156
+ padDesignImage: (imagePath, width, height, minViewportSize) => { var _a; return (0, figma_images_1.createPaddedDesignImage)(imagePath, width, height, minViewportSize, (_a = opts === null || opts === void 0 ? void 0 : opts.tempPath) !== null && _a !== void 0 ? _a : os.tmpdir()); },
157
+ runFigmaCheck: run_figma_check_1.runFigmaCheck,
158
+ };
159
+ const figmaUrls = (0, utils_1.resolveFigmaUrls)(figmaBaselines, configuration.testName);
160
+ if (figmaUrls === null)
161
+ return;
162
+ const updated = await setFigmaBaselineFlow(figmaUrls, configuration, deps, mode, {
163
+ maxViewportHeight: opts === null || opts === void 0 ? void 0 : opts.maxViewportHeight,
164
+ minViewportSize: opts === null || opts === void 0 ? void 0 : opts.minViewportSize,
165
+ }, logger);
166
+ Object.assign(configuration, updated);
167
+ }
168
+ exports.setFigmaBaseline = setFigmaBaseline;
@@ -0,0 +1,27 @@
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.applyFigmaBaselines = exports.runFigmaCheck = void 0;
18
+ __exportStar(require("./types"), exports);
19
+ __exportStar(require("./utils"), exports);
20
+ __exportStar(require("./target-config"), exports);
21
+ __exportStar(require("./client"), exports);
22
+ __exportStar(require("./figma-images"), exports);
23
+ __exportStar(require("./figma"), exports);
24
+ var run_figma_check_1 = require("./run-figma-check");
25
+ Object.defineProperty(exports, "runFigmaCheck", { enumerable: true, get: function () { return run_figma_check_1.runFigmaCheck; } });
26
+ var seam_1 = require("./seam");
27
+ Object.defineProperty(exports, "applyFigmaBaselines", { enumerable: true, get: function () { return seam_1.applyFigmaBaselines; } });
@@ -0,0 +1,82 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.runFigmaCheck = void 0;
27
+ const utils = __importStar(require("@applitools/utils"));
28
+ const core_1 = require("../core");
29
+ // Cached because this runs once per open — eyes-storybook opens per story, and a fresh Core each time
30
+ // re-initialises and forfeits HTTP agent reuse. Safe to share: all state arrives via `openEyes`.
31
+ const getFigmaCore = utils.general.cachify(() => (0, core_1.makeCore)({ agentId: 'core.figma-baseline' }));
32
+ /**
33
+ * Establishes/updates the Figma-reference baseline via a throwaway headless classic image test (checks
34
+ * each PNG, closes, deletes the result — the saved baseline persists). Uses core's own API, not the
35
+ * eyes layer (core cannot depend on `@applitools/eyes`).
36
+ */
37
+ async function runFigmaCheck(config, checks) {
38
+ var _a;
39
+ const core = getFigmaCore();
40
+ const eyes = await core.openEyes({
41
+ type: 'classic',
42
+ settings: {
43
+ appName: config.appName,
44
+ testName: config.testName,
45
+ apiKey: config.apiKey,
46
+ eyesServerUrl: config.eyesServerUrl,
47
+ proxy: config.proxy,
48
+ baselineEnvName: config.baselineEnvName,
49
+ properties: config.properties,
50
+ batch: config.batch,
51
+ keepBatchOpen: config.keepBatchOpen,
52
+ saveDiffs: config.saveDiffs,
53
+ ...(config.branchName !== undefined ? { branchName: config.branchName } : {}),
54
+ environment: config.environment,
55
+ // Throwaway reference test keyed purely by baselineEnvName; skip the (slow) git extraction.
56
+ ignoreGitBranching: true,
57
+ },
58
+ });
59
+ for (const { imagePath, tag } of checks) {
60
+ await eyes.check({ target: { image: imagePath }, settings: { name: tag } });
61
+ }
62
+ // core's close-time equivalent of saveNewTests (defaulted true there too).
63
+ await eyes.close({ settings: { updateBaselineIfNew: (_a = config.saveNewTests) !== null && _a !== void 0 ? _a : true } });
64
+ const [result] = await eyes.getResults({ settings: { throwErr: false } });
65
+ try {
66
+ await core.deleteTest({
67
+ settings: {
68
+ testId: result.id,
69
+ batchId: result.batchId,
70
+ secretToken: result.secretToken,
71
+ eyesServerUrl: result.eyesServer.eyesServerUrl,
72
+ apiKey: result.eyesServer.apiKey,
73
+ proxy: result.eyesServer.proxy,
74
+ },
75
+ });
76
+ }
77
+ catch {
78
+ // best-effort cleanup
79
+ }
80
+ return { isNew: result.isNew, diffs: result.mismatches > 0 };
81
+ }
82
+ exports.runFigmaCheck = runFigmaCheck;
@@ -0,0 +1,91 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.applyFigmaBaselines = void 0;
27
+ const utils = __importStar(require("@applitools/utils"));
28
+ const figma_1 = require("./figma");
29
+ const utils_1 = require("./utils");
30
+ /**
31
+ * @internal
32
+ * Resolves the `figmaBaselines` / `figmaOptions` open config once per open, then always strips both — they
33
+ * are off-the-wire and must never be logged or sent. The resolver mutates `settings` in place.
34
+ *
35
+ * The only place the Figma feature reads the environment; everything below it takes parameters. Each
36
+ * `figmaOptions` field wins over its env fallback.
37
+ */
38
+ async function applyFigmaBaselines(settings, logger, resolve = figma_1.setFigmaBaseline) {
39
+ var _a, _b, _c, _d, _e, _f;
40
+ const figmaBaselines = settings.figmaBaselines;
41
+ const figmaOptions = (_a = settings.figmaOptions) !== null && _a !== void 0 ? _a : {};
42
+ if (!figmaBaselines) {
43
+ // Undefined-valued keys must not survive to the wire/log.
44
+ delete settings.figmaBaselines;
45
+ delete settings.figmaOptions;
46
+ return;
47
+ }
48
+ try {
49
+ // A shared open config applies to every test, so an unmapped test must not fail — only skip.
50
+ if (typeof figmaBaselines !== 'string' && figmaBaselines[settings.testName] === undefined) {
51
+ logger.warn(`figmaBaselines is set but has no entry for test "${settings.testName}"; skipping Figma baseline resolution for this test.`);
52
+ return;
53
+ }
54
+ // skipApi has no figmaOptions equivalent by design — CI/debug switch, not a user option.
55
+ const skipApi = (_b = utils.general.getEnvValue('FIGMA_SKIP_API', 'boolean')) !== null && _b !== void 0 ? _b : false;
56
+ const envTtl = utils.general.getEnvValue('FIGMA_NODES_CACHE_TTL', 'number');
57
+ const tempPath = (_c = figmaOptions.tempPath) !== null && _c !== void 0 ? _c : utils.general.getEnvValue('FIGMA_TEMP_PATH');
58
+ // A long TTL lets a pre-warmed cache (committed fixtures) serve through the normal cache-hit path
59
+ // instead of the all-or-nothing skipApi bypass. Number.isFinite also rejects a non-numeric env value.
60
+ const nodesCacheTtlSeconds = (_d = figmaOptions.nodesCacheTtlSeconds) !== null && _d !== void 0 ? _d : envTtl;
61
+ // Registered with the logger the way apiKey/secretToken are elsewhere, so a token supplied through
62
+ // figmaOptions is redacted wherever settings are logged downstream.
63
+ const accessToken = (_e = figmaOptions.accessToken) !== null && _e !== void 0 ? _e : process.env.FIGMA_ACCESS_TOKEN;
64
+ if (accessToken)
65
+ logger.mask(accessToken);
66
+ await resolve(settings, figmaBaselines, {
67
+ logger,
68
+ skipApi,
69
+ mode: (0, utils_1.resolveFigmaMode)((_f = figmaOptions.mode) !== null && _f !== void 0 ? _f : utils.general.getEnvValue('FIGMA_MODE')),
70
+ accessToken,
71
+ // On-prem accounts have no direct route to api.figma.com.
72
+ proxy: settings.proxy,
73
+ ...(tempPath ? { tempPath } : {}),
74
+ ...(Number.isFinite(nodesCacheTtlSeconds) ? { nodesCacheTtlSeconds } : {}),
75
+ // These three have no env fallback, so omit them when absent and let the resolver default them.
76
+ // `!= null` not `!== undefined`: bindings that serialize an unset nested field as null (Ruby) would
77
+ // otherwise reach `maxRateLimitWaitSeconds = 60`, which only defaults on undefined, and null*1000
78
+ // silently zeroes the rate-limit budget.
79
+ ...(figmaOptions.maxRateLimitWaitSeconds != null
80
+ ? { maxRateLimitWaitSeconds: figmaOptions.maxRateLimitWaitSeconds }
81
+ : {}),
82
+ ...(figmaOptions.maxViewportHeight != null ? { maxViewportHeight: figmaOptions.maxViewportHeight } : {}),
83
+ ...(figmaOptions.minViewportSize != null ? { minViewportSize: figmaOptions.minViewportSize } : {}),
84
+ });
85
+ }
86
+ finally {
87
+ delete settings.figmaBaselines;
88
+ delete settings.figmaOptions;
89
+ }
90
+ }
91
+ exports.applyFigmaBaselines = applyFigmaBaselines;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildTargetTestConfig = exports.deduplicateBrowsers = exports.PLACEHOLDER_TAG = exports.DEFAULT_MAX_VIEWPORT_HEIGHT = void 0;
4
+ const utils_1 = require("./utils");
5
+ // Default height cap (overridable via opts.maxViewportHeight): designs can be arbitrarily tall, so
6
+ // height is capped but width never is — width is the form factor the design and real test must share.
7
+ exports.DEFAULT_MAX_VIEWPORT_HEIGHT = 800;
8
+ // Dashboard tag for a '' (placeholder) step; real steps use the Figma URL as the tag.
9
+ exports.PLACEHOLDER_TAG = '(no design)';
10
+ // UFG opens request per-browser environments; classic sizes a single viewport instead.
11
+ function deduplicateBrowsers(environments, targetWidth, targetHeight) {
12
+ const desktop = new Map();
13
+ for (const environment of environments) {
14
+ const db = environment;
15
+ const entry = typeof db.width === 'number'
16
+ ? db
17
+ : { name: 'iosDeviceInfo' in environment ? 'safari' : 'chrome', width: targetWidth, height: targetHeight };
18
+ desktop.set(String(entry.name), { ...entry, width: targetWidth, height: targetHeight });
19
+ }
20
+ return [...desktop.values()];
21
+ }
22
+ exports.deduplicateBrowsers = deduplicateBrowsers;
23
+ // baselineLink is null in "test-baseline" mode: baselineEnvName is left untouched (no change detection
24
+ // happened to justify linking), but viewport/properties are still adjusted. figmaViewport always wins.
25
+ function buildTargetTestConfig(originalConfig, metadata, figmaViewport, baselineLink, mode) {
26
+ var _a, _b, _c;
27
+ const isUFG = ((_b = (_a = originalConfig.environments) === null || _a === void 0 ? void 0 : _a.length) !== null && _b !== void 0 ? _b : 0) > 0;
28
+ const mergedProperties = [...((_c = originalConfig.properties) !== null && _c !== void 0 ? _c : []), ...(0, utils_1.figmaMetadataToProperties)(metadata, mode)];
29
+ // baselineEnvOptions is the internal field backing Configuration.setBaselineEnvName(name, {useLatestEnvBaseline}) — same effect without a live Configuration.
30
+ const baselineFields = baselineLink
31
+ ? {
32
+ baselineEnvName: baselineLink.figmaEnvName,
33
+ baselineEnvOptions: { useLatestEnvBaseline: baselineLink.useLatestEnvBaseline },
34
+ }
35
+ : {};
36
+ // The figma viewport always wins; merged into environment.viewportSize so os/hostingApp survive.
37
+ const environment = { ...originalConfig.environment, viewportSize: figmaViewport };
38
+ if (isUFG) {
39
+ return {
40
+ ...baselineFields,
41
+ properties: mergedProperties,
42
+ environments: deduplicateBrowsers(originalConfig.environments, figmaViewport.width, figmaViewport.height),
43
+ environment,
44
+ };
45
+ }
46
+ return {
47
+ ...baselineFields,
48
+ properties: mergedProperties,
49
+ environment,
50
+ };
51
+ }
52
+ exports.buildTargetTestConfig = buildTargetTestConfig;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,128 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.paddedDesignSize = exports.computeFigmaViewport = exports.boundingViewport = exports.figmaMetadataToProperties = exports.resolveFigmaMode = exports.FIGMA_MODES = exports.resolveFigmaUrls = exports.figmaStepTag = exports.parseFigmaNodeUrl = exports.isFigmaUrl = void 0;
4
+ // ---------------------------------------------------------------------------
5
+ // URL detection and parsing
6
+ // ---------------------------------------------------------------------------
7
+ function isFigmaUrl(url) {
8
+ try {
9
+ const { hostname } = new URL(url);
10
+ return hostname === 'figma.com' || hostname === 'www.figma.com';
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ exports.isFigmaUrl = isFigmaUrl;
17
+ function parseFigmaNodeUrl(url) {
18
+ let u;
19
+ try {
20
+ u = new URL(url);
21
+ }
22
+ catch {
23
+ throw new Error(`Invalid URL: "${url}"`);
24
+ }
25
+ if (!isFigmaUrl(url)) {
26
+ throw new Error(`Not a Figma URL: "${url}"`);
27
+ }
28
+ // Pathname: /{type}/{fileId}/{title} e.g. /design/abc123/Untitled
29
+ const segments = u.pathname.split('/').filter(Boolean);
30
+ const fileId = segments[1];
31
+ if (!fileId) {
32
+ throw new Error(`Could not extract file ID from Figma URL: "${url}"`);
33
+ }
34
+ const rawNodeId = u.searchParams.get('node-id');
35
+ if (!rawNodeId) {
36
+ throw new Error(`No node-id parameter in Figma URL: "${url}"`);
37
+ }
38
+ // Figma encodes "1:2" as "1-2" in URLs; %3A is decoded to ":" automatically by URLSearchParams
39
+ const nodeId = rawNodeId.includes(':') ? rawNodeId : rawNodeId.replace('-', ':');
40
+ return { fileId, nodeId };
41
+ }
42
+ exports.parseFigmaNodeUrl = parseFigmaNodeUrl;
43
+ /**
44
+ * The tag becomes the Eyes baseline step name, and Eyes matches steps by name, so it must derive only from
45
+ * stable identity. Figma's "Copy link to selection" appends a `t=` param that differs on every copy —
46
+ * keeping it means re-copying the link renames the step and orphans its accepted baseline.
47
+ *
48
+ * Drop only `t`: `node-id` is what distinguishes two frames of the same file, so stripping the whole query
49
+ * would give every frame in a file the same step name.
50
+ */
51
+ function figmaStepTag(figmaUrl) {
52
+ const url = new URL(figmaUrl);
53
+ url.searchParams.delete('t');
54
+ return url.toString();
55
+ }
56
+ exports.figmaStepTag = figmaStepTag;
57
+ // ---------------------------------------------------------------------------
58
+ // figmaBaselines / mode resolution
59
+ // ---------------------------------------------------------------------------
60
+ // Resolves figmaBaselines + testName to the list of Figma URLs, or null when the test opts out.
61
+ function resolveFigmaUrls(figmaBaselines, testName) {
62
+ if (typeof figmaBaselines === 'string') {
63
+ return figmaBaselines === '' ? null : [figmaBaselines];
64
+ }
65
+ // No testName (should be set upstream) → no matching entry → falls through to the no-entry throw below.
66
+ const entry = testName === undefined ? undefined : figmaBaselines[testName];
67
+ if (entry === '' || (Array.isArray(entry) && entry.length === 0))
68
+ return null;
69
+ // An all-'' array (no real design anywhere) is treated as a missing entry, not an opt-out.
70
+ const isAllPlaceholders = Array.isArray(entry) && entry.every((url) => url === '');
71
+ if (entry === undefined || isAllPlaceholders) {
72
+ throw new Error(`setFigmaBaseline: no Figma URL configured for test "${testName}". ` +
73
+ `Add an entry in the figmaBaselines config option.`);
74
+ }
75
+ return Array.isArray(entry) ? entry : [entry];
76
+ }
77
+ exports.resolveFigmaUrls = resolveFigmaUrls;
78
+ exports.FIGMA_MODES = ['figma-baseline', 'test-baseline', 'auto-baseline', 'disabled'];
79
+ // The value can arrive from figmaOptions.mode or the env var, so the error names both.
80
+ function resolveFigmaMode(value) {
81
+ if (value === undefined)
82
+ return 'auto-baseline';
83
+ if (!exports.FIGMA_MODES.includes(value)) {
84
+ throw new Error(`setFigmaBaseline: invalid Figma mode "${value}". Expected one of: ${exports.FIGMA_MODES.join(', ')}. ` +
85
+ `Set it via figmaOptions.mode or APPLITOOLS_FIGMA_MODE.`);
86
+ }
87
+ return value;
88
+ }
89
+ exports.resolveFigmaMode = resolveFigmaMode;
90
+ // figma:mode is last because the cross-language generic tests assert the earlier properties by index —
91
+ // emitters can only translate index access, not a lookup by name. Reordering breaks 4 languages' CI.
92
+ function figmaMetadataToProperties(metadata, mode) {
93
+ return [
94
+ { name: 'figma:name', value: metadata.name },
95
+ { name: 'figma:type', value: metadata.type },
96
+ { name: 'figma:file-revision', value: metadata.revision },
97
+ { name: 'figma:file-last-modified', value: metadata.lastModifiedAt },
98
+ { name: 'figma:mode', value: mode },
99
+ ];
100
+ }
101
+ exports.figmaMetadataToProperties = figmaMetadataToProperties;
102
+ // ---------------------------------------------------------------------------
103
+ // Viewport math
104
+ // ---------------------------------------------------------------------------
105
+ // The smallest box that fits every given viewport (max width, max height).
106
+ function boundingViewport(viewports) {
107
+ return {
108
+ width: Math.max(...viewports.map(v => v.width)),
109
+ height: Math.max(...viewports.map(v => v.height)),
110
+ };
111
+ }
112
+ exports.boundingViewport = boundingViewport;
113
+ // THE figma viewport: bounding box across every step, height capped at maxViewportHeight. Drives the
114
+ // baseline env label, the reference Eyes session, and the user's test viewport — so everything renders at one size.
115
+ function computeFigmaViewport(stepViewports, maxViewportHeight) {
116
+ const bounds = boundingViewport(stepViewports);
117
+ return { width: bounds.width, height: Math.min(bounds.height, maxViewportHeight) };
118
+ }
119
+ exports.computeFigmaViewport = computeFigmaViewport;
120
+ // minViewportSize floors a design's dimensions so a consumer with a minimum viewport (e.g. the UFG on a
121
+ // small Storybook component) won't reject it; padding centers the design on a >=minSize white canvas.
122
+ function paddedDesignSize(width, height, minSize) {
123
+ return {
124
+ width: Math.max(width, minSize),
125
+ height: Math.max(height, minSize),
126
+ };
127
+ }
128
+ exports.paddedDesignSize = paddedDesignSize;