@applitools/core 4.67.1 → 4.68.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/CHANGELOG.md +71 -0
- package/dist/autonomous/create-render-results.js +6 -1
- package/dist/autonomous/start-renders.js +16 -7
- package/dist/figma/client/figma-req.js +46 -0
- package/dist/figma/client/figma-rest.js +189 -0
- package/dist/figma/client/index.js +21 -0
- package/dist/figma/figma-images.js +62 -0
- package/dist/figma/figma.js +168 -0
- package/dist/figma/index.js +27 -0
- package/dist/figma/run-figma-check.js +82 -0
- package/dist/figma/seam.js +91 -0
- package/dist/figma/target-config.js +52 -0
- package/dist/figma/types.js +2 -0
- package/dist/figma/utils.js +128 -0
- package/dist/offline/run-offline-snapshots.js +18 -3
- package/dist/open-eyes.js +2 -0
- package/dist/ufg/build-accessibility-report-options.js +40 -0
- package/dist/ufg/check-and-close.js +14 -5
- package/dist/ufg/check.js +10 -1
- package/package.json +16 -15
- package/types/automation/types.d.ts +9 -0
- package/types/autonomous/types.d.ts +1 -0
- package/types/figma/client/figma-req.d.ts +9 -0
- package/types/figma/client/figma-rest.d.ts +24 -0
- package/types/figma/client/index.d.ts +2 -0
- package/types/figma/figma-images.d.ts +6 -0
- package/types/figma/figma.d.ts +21 -0
- package/types/figma/index.d.ts +8 -0
- package/types/figma/run-figma-check.d.ts +13 -0
- package/types/figma/seam.d.ts +12 -0
- package/types/figma/target-config.d.ts +7 -0
- package/types/figma/types.d.ts +69 -0
- package/types/figma/utils.d.ts +26 -0
- package/types/types.d.ts +1 -0
- package/types/ufg/build-accessibility-report-options.d.ts +13 -0
|
@@ -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,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;
|
|
@@ -31,6 +31,7 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
31
31
|
const path_1 = __importDefault(require("path"));
|
|
32
32
|
const core_1 = require("../core");
|
|
33
33
|
const get_ufg_client_1 = require("../ufg/get-ufg-client");
|
|
34
|
+
const build_accessibility_report_options_1 = require("../ufg/build-accessibility-report-options");
|
|
34
35
|
const to_base_check_settings_1 = require("../automation/utils/to-base-check-settings");
|
|
35
36
|
const logger_1 = require("@applitools/logger");
|
|
36
37
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -63,7 +64,13 @@ async function runOfflineSnapshots(options) {
|
|
|
63
64
|
const offlineLocationPath = path_1.default.resolve(options.offlineLocationPath);
|
|
64
65
|
const startTime = Date.now();
|
|
65
66
|
const logger = (_a = options.logger) !== null && _a !== void 0 ? _a : (0, logger_1.makeLogger)({ format: { label: 'offline-exec' } });
|
|
66
|
-
|
|
67
|
+
// figmaBaselines/figmaOptions are off-the-wire open config that the open seam resolves and then strips.
|
|
68
|
+
// This replay path bypasses the seam, so strip them once here — figmaOptions carries a Figma access
|
|
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 };
|
|
67
74
|
const core = (0, core_1.makeCore)({ agentId: `js/core-offline/${version()}`, logger });
|
|
68
75
|
const account = await core.getAccountInfo({ settings: eyesServerSettings });
|
|
69
76
|
const ufgClient = await (0, get_ufg_client_1.makeGetUFGClient)({ logger })({
|
|
@@ -111,7 +118,7 @@ async function runOfflineSnapshots(options) {
|
|
|
111
118
|
const fileOpenSettings = await fs_1.default.promises
|
|
112
119
|
.readFile(path_1.default.join(testPath, 'settings.json'), 'utf-8')
|
|
113
120
|
.then(JSON.parse);
|
|
114
|
-
const openSettings = (0, merge_configs_1.mergeConfigs)(fileOpenSettings,
|
|
121
|
+
const openSettings = (0, merge_configs_1.mergeConfigs)(fileOpenSettings, openConfig);
|
|
115
122
|
messages.push(`Running test: ${openSettings.testName} (${formatEnvironment(openSettings.environment)})`);
|
|
116
123
|
return { testPath, openSettings, testLogger };
|
|
117
124
|
});
|
|
@@ -336,10 +343,14 @@ async function runOfflineSnapshots(options) {
|
|
|
336
343
|
});
|
|
337
344
|
}
|
|
338
345
|
async function render(target, logger) {
|
|
339
|
-
var _a;
|
|
346
|
+
var _a, _b;
|
|
340
347
|
const { elementReferences: selectors, getBaseCheckSettings } = (0, to_base_check_settings_1.toBaseCheckSettings)({
|
|
341
348
|
settings: target.settings,
|
|
342
349
|
});
|
|
350
|
+
// Only request a report when the check configures an accessibility standard.
|
|
351
|
+
const accessibilityReportOptions = target.settings.accessibilitySettings
|
|
352
|
+
? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({ accessibilityOptions: account.accessibilityOptions })
|
|
353
|
+
: undefined;
|
|
343
354
|
const { renderId, selectorRegions, ...baseTarget } = await ufgClient.render({
|
|
344
355
|
target: target.target,
|
|
345
356
|
settings: {
|
|
@@ -351,6 +362,7 @@ async function runOfflineSnapshots(options) {
|
|
|
351
362
|
environment: target.settings.environment,
|
|
352
363
|
uploadUrl: account.uploadUrl,
|
|
353
364
|
stitchingServiceUrl: account.stitchingServiceUrl,
|
|
365
|
+
accessibilityReportOptions,
|
|
354
366
|
},
|
|
355
367
|
logger,
|
|
356
368
|
});
|
|
@@ -365,6 +377,9 @@ async function runOfflineSnapshots(options) {
|
|
|
365
377
|
baseSettings.renderId = renderId;
|
|
366
378
|
// baseSettings.stepIndex = index
|
|
367
379
|
baseTarget.source = target.target.source; // TODO verify
|
|
380
|
+
// Fall back to the minted upload URL when the render result carries no report location.
|
|
381
|
+
if (accessibilityReportOptions === null || accessibilityReportOptions === void 0 ? void 0 : accessibilityReportOptions.uploadUrl)
|
|
382
|
+
(_b = baseTarget.accessibilityReportLocation) !== null && _b !== void 0 ? _b : (baseTarget.accessibilityReportLocation = accessibilityReportOptions.uploadUrl);
|
|
368
383
|
// baseTarget.name = snapshot.title // TODO figure out
|
|
369
384
|
const mergedCheckSettings = (0, merge_configs_1.mergeConfigs)(baseSettings, options.config.check);
|
|
370
385
|
return { mergedCheckSettings, baseTarget };
|
package/dist/open-eyes.js
CHANGED
|
@@ -33,6 +33,7 @@ const check_and_close_1 = require("./check-and-close");
|
|
|
33
33
|
const close_1 = require("./close");
|
|
34
34
|
const abort_1 = require("./abort");
|
|
35
35
|
const get_eyes_results_1 = require("./get-eyes-results");
|
|
36
|
+
const seam_1 = require("./figma/seam");
|
|
36
37
|
const populate_eyes_server_settings_1 = require("./utils/populate-eyes-server-settings");
|
|
37
38
|
const utils = __importStar(require("@applitools/utils"));
|
|
38
39
|
const core_base_1 = require("@applitools/core-base");
|
|
@@ -110,6 +111,7 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
|
|
|
110
111
|
logger.warn(`gitBranchingTimestamp is an invalid ISO date string: ${settings.gitBranchingTimestamp}`);
|
|
111
112
|
settings.gitBranchingTimestamp = undefined;
|
|
112
113
|
}
|
|
114
|
+
await (0, seam_1.applyFigmaBaselines)(settings, logger);
|
|
113
115
|
(_x = settings.bypassCSP) !== null && _x !== void 0 ? _x : (settings.bypassCSP = utils.general.getEnvValue('BYPASS_CSP', 'boolean') === true || undefined);
|
|
114
116
|
const driver = target && (await (0, driver_1.makeDriver)({ spec, driver: target, logger, customConfig: settings }));
|
|
115
117
|
if (settings.bypassCSP) {
|
|
@@ -0,0 +1,40 @@
|
|
|
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.buildAccessibilityReportOptions = void 0;
|
|
27
|
+
const utils = __importStar(require("@applitools/utils"));
|
|
28
|
+
const REPORT_ID_PLACEHOLDER = '__report_id__';
|
|
29
|
+
/**
|
|
30
|
+
* Builds the `accessibilityReportOptions` render option: forwards `runOptions` verbatim and fills the
|
|
31
|
+
* `uploadUrl` template's `__report_id__` with a fresh random id. Returns `undefined` when the account
|
|
32
|
+
* has no accessibility options.
|
|
33
|
+
*/
|
|
34
|
+
function buildAccessibilityReportOptions({ accessibilityOptions, reportId = utils.general.guid(), }) {
|
|
35
|
+
if (!accessibilityOptions)
|
|
36
|
+
return undefined;
|
|
37
|
+
const uploadUrl = accessibilityOptions.uploadUrl.split(REPORT_ID_PLACEHOLDER).join(encodeURIComponent(reportId));
|
|
38
|
+
return { axeCoreOptions: accessibilityOptions.runOptions, uploadUrl };
|
|
39
|
+
}
|
|
40
|
+
exports.buildAccessibilityReportOptions = buildAccessibilityReportOptions;
|
|
@@ -37,6 +37,7 @@ const lang = __importStar(require("../lang"));
|
|
|
37
37
|
const chalk_1 = __importDefault(require("chalk"));
|
|
38
38
|
const take_snapshots_1 = require("./take-snapshots");
|
|
39
39
|
const create_render_target_from_snapshot_1 = require("./create-render-target-from-snapshot");
|
|
40
|
+
const build_accessibility_report_options_1 = require("./build-accessibility-report-options");
|
|
40
41
|
function makeCheckAndClose({ eyes, target: defaultTarget, environments: defaultEnvironments = [], spec, signal, logger: mainLogger, }) {
|
|
41
42
|
return async function checkAndClose({ target = defaultTarget, settings = {}, logger = mainLogger, } = {}) {
|
|
42
43
|
var _a;
|
|
@@ -72,7 +73,7 @@ function makeCheckAndClose({ eyes, target: defaultTarget, environments: defaultE
|
|
|
72
73
|
snapshots = !utils.types.isArray(target) ? Array(uniqueEnvironments.length).fill(target) : target;
|
|
73
74
|
}
|
|
74
75
|
const promises = snapshots.map(async (snapshot, i) => {
|
|
75
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
76
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
76
77
|
const environmentLogger = logger.extend({ tags: [`environment-${utils.general.shortid()}`] });
|
|
77
78
|
const { target } = snapshot;
|
|
78
79
|
const environment = uniqueEnvironments[i];
|
|
@@ -115,6 +116,10 @@ function makeCheckAndClose({ eyes, target: defaultTarget, environments: defaultE
|
|
|
115
116
|
const { elementReferences: selectors, getBaseCheckSettings } = (0, to_base_check_settings_1.toBaseCheckSettings)({
|
|
116
117
|
settings: snapshot.settings,
|
|
117
118
|
});
|
|
119
|
+
// Only request a report when the check configures an accessibility standard.
|
|
120
|
+
const accessibilityReportOptions = settings.accessibilitySettings
|
|
121
|
+
? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({ accessibilityOptions: eyes.test.account.accessibilityOptions })
|
|
122
|
+
: undefined;
|
|
118
123
|
const { renderId, selectorRegions, ...baseTarget } = await ufgClient.render({
|
|
119
124
|
target: renderTarget,
|
|
120
125
|
settings: {
|
|
@@ -126,6 +131,7 @@ function makeCheckAndClose({ eyes, target: defaultTarget, environments: defaultE
|
|
|
126
131
|
environment: { ...environment, environmentId: baseEyes.test.environment.environmentId },
|
|
127
132
|
uploadUrl: baseEyes.test.uploadUrl,
|
|
128
133
|
stitchingServiceUrl: baseEyes.test.stitchingServiceUrl,
|
|
134
|
+
accessibilityReportOptions,
|
|
129
135
|
},
|
|
130
136
|
signal,
|
|
131
137
|
logger: environmentLogger,
|
|
@@ -141,13 +147,16 @@ function makeCheckAndClose({ eyes, target: defaultTarget, environments: defaultE
|
|
|
141
147
|
baseSettings.renderId = renderId;
|
|
142
148
|
baseTarget.source = snapshot.url;
|
|
143
149
|
baseTarget.name = snapshot.title;
|
|
150
|
+
// Fall back to the minted upload URL when the render result carries no report location.
|
|
151
|
+
if (accessibilityReportOptions === null || accessibilityReportOptions === void 0 ? void 0 : accessibilityReportOptions.uploadUrl)
|
|
152
|
+
(_f = baseTarget.accessibilityReportLocation) !== null && _f !== void 0 ? _f : (baseTarget.accessibilityReportLocation = accessibilityReportOptions.uploadUrl);
|
|
144
153
|
if (signal === null || signal === void 0 ? void 0 : signal.aborted) {
|
|
145
154
|
environmentLogger.warn('Command "check" was aborted after rendering');
|
|
146
155
|
throw new abort_error_1.AbortError('Command "check" was aborted after rendering');
|
|
147
156
|
}
|
|
148
157
|
else if (!baseEyes.running) {
|
|
149
|
-
environmentLogger.warn(`Render on environment with id "${(
|
|
150
|
-
throw new abort_error_1.AbortError(`Render on environment with id "${(
|
|
158
|
+
environmentLogger.warn(`Render on environment with id "${(_g = baseEyes.test.environment) === null || _g === void 0 ? void 0 : _g.environmentId}" was aborted during one of the previous steps`);
|
|
159
|
+
throw new abort_error_1.AbortError(`Render on environment with id "${(_h = baseEyes.test.environment) === null || _h === void 0 ? void 0 : _h.environmentId}" was aborted during one of the previous steps`);
|
|
151
160
|
}
|
|
152
161
|
if (settings.assumesMutability) {
|
|
153
162
|
void eyes.core.logEvent({
|
|
@@ -165,7 +174,7 @@ function makeCheckAndClose({ eyes, target: defaultTarget, environments: defaultE
|
|
|
165
174
|
userTestId: baseEyes.test.userTestId,
|
|
166
175
|
appId: baseEyes.test.appId,
|
|
167
176
|
baselineId: baseEyes.test.baselineId,
|
|
168
|
-
environmentId: (
|
|
177
|
+
environmentId: (_j = baseEyes.test.environment) === null || _j === void 0 ? void 0 : _j.environmentId,
|
|
169
178
|
},
|
|
170
179
|
isNew: baseEyes.test.isNew,
|
|
171
180
|
resultsUrl: baseEyes.test.resultsUrl,
|
|
@@ -182,7 +191,7 @@ function makeCheckAndClose({ eyes, target: defaultTarget, environments: defaultE
|
|
|
182
191
|
});
|
|
183
192
|
}
|
|
184
193
|
catch (error) {
|
|
185
|
-
environmentLogger.error(`Render on environment with id "${(
|
|
194
|
+
environmentLogger.error(`Render on environment with id "${(_k = baseEyes.test.environment) === null || _k === void 0 ? void 0 : _k.environmentId}" failed due to an error`, error);
|
|
186
195
|
if (baseEyes.running && !(signal === null || signal === void 0 ? void 0 : signal.aborted))
|
|
187
196
|
await baseEyes.abort({ logger: environmentLogger, settings: { reason: error } });
|
|
188
197
|
}
|
package/dist/ufg/check.js
CHANGED
|
@@ -38,6 +38,7 @@ const take_snapshots_1 = require("./take-snapshots");
|
|
|
38
38
|
const create_render_target_from_snapshot_1 = require("./create-render-target-from-snapshot");
|
|
39
39
|
const to_base_check_settings_1 = require("../automation/utils/to-base-check-settings");
|
|
40
40
|
const to_old_infra_environment_1 = require("./to-old-infra-environment");
|
|
41
|
+
const build_accessibility_report_options_1 = require("./build-accessibility-report-options");
|
|
41
42
|
function makeCheck({ eyes, target: defaultTarget, environments: defaultEnvironments = [], spec, signal, logger: mainLogger, }) {
|
|
42
43
|
// Sessions whose old-infra retry chain has ended: once a retry doesn't confirm an old-infra match,
|
|
43
44
|
// the backend stops expecting retries, so we must stop sending them. baseEyes is cached per
|
|
@@ -122,7 +123,11 @@ function makeCheck({ eyes, target: defaultTarget, environments: defaultEnvironme
|
|
|
122
123
|
settings: snapshot.settings,
|
|
123
124
|
});
|
|
124
125
|
const renderForCheck = async (env) => {
|
|
125
|
-
var _a;
|
|
126
|
+
var _a, _b;
|
|
127
|
+
// Only request a report when the check configures an accessibility standard.
|
|
128
|
+
const accessibilityReportOptions = settings.accessibilitySettings
|
|
129
|
+
? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({ accessibilityOptions: eyes.test.account.accessibilityOptions })
|
|
130
|
+
: undefined;
|
|
126
131
|
const { renderId, selectorRegions, ...target } = await ufgClient.render({
|
|
127
132
|
target: renderTarget,
|
|
128
133
|
settings: {
|
|
@@ -134,6 +139,7 @@ function makeCheck({ eyes, target: defaultTarget, environments: defaultEnvironme
|
|
|
134
139
|
environment: { ...env, environmentId: baseEyes.test.environment.environmentId },
|
|
135
140
|
uploadUrl: baseEyes.test.uploadUrl,
|
|
136
141
|
stitchingServiceUrl: baseEyes.test.stitchingServiceUrl,
|
|
142
|
+
accessibilityReportOptions,
|
|
137
143
|
},
|
|
138
144
|
signal,
|
|
139
145
|
logger: environmentLogger,
|
|
@@ -149,6 +155,9 @@ function makeCheck({ eyes, target: defaultTarget, environments: defaultEnvironme
|
|
|
149
155
|
checkSettings.renderId = renderId;
|
|
150
156
|
target.source = snapshot.url;
|
|
151
157
|
target.name = snapshot.title;
|
|
158
|
+
// Fall back to the minted upload URL when the render result carries no report location.
|
|
159
|
+
if (accessibilityReportOptions === null || accessibilityReportOptions === void 0 ? void 0 : accessibilityReportOptions.uploadUrl)
|
|
160
|
+
(_b = target.accessibilityReportLocation) !== null && _b !== void 0 ? _b : (target.accessibilityReportLocation = accessibilityReportOptions.uploadUrl);
|
|
152
161
|
return { target, settings: checkSettings };
|
|
153
162
|
};
|
|
154
163
|
const { target: baseTarget, settings: baseSettings } = await renderForCheck(environment);
|