@applitools/core 4.70.3 → 4.72.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 CHANGED
@@ -1,5 +1,111 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.72.0](https://github.com/Applitools-Dev/sdk/compare/js/core@4.71.0...js/core@4.72.0) (2026-10-12)
4
+
5
+
6
+ ### Features
7
+
8
+ * per-run accessibility run options for UFG renders | AD-16443 ([#4207](https://github.com/Applitools-Dev/sdk/issues/4207)) ([ea0e789](https://github.com/Applitools-Dev/sdk/commit/ea0e789826304f3ce7adbea32cc85de0718b2722))
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * version-check/telemetry no longer hold the process — connect deadline + unref | AD-16613 ([#4232](https://github.com/Applitools-Dev/sdk/issues/4232)) ([42bcbb5](https://github.com/Applitools-Dev/sdk/commit/42bcbb5dd8d75ad4aa98c2ec0d284f107fdc98e3))
14
+
15
+
16
+ ### Performance Improvements
17
+
18
+ * upload inline data: URIs as resources | AD-15789 ([#4119](https://github.com/Applitools-Dev/sdk/issues/4119)) ([cbd9afa](https://github.com/Applitools-Dev/sdk/commit/cbd9afa8de664b32f23119690b14a0aef998acaf))
19
+
20
+
21
+ ### Dependencies
22
+
23
+ * @applitools/dom-snapshot bumped to 4.18.5
24
+ #### Bug Fixes
25
+
26
+ * keep the video src when there is no source fallback | FLD-4948 ([#4209](https://github.com/Applitools-Dev/sdk/issues/4209)) ([50d0909](https://github.com/Applitools-Dev/sdk/commit/50d09091291dd64055c42104695f8726594558ac))
27
+ * record blob videos so HLS videos render in UFG | AD-16937 ([#4277](https://github.com/Applitools-Dev/sdk/issues/4277)) ([3bdcdcc](https://github.com/Applitools-Dev/sdk/commit/3bdcdccf068ce9f827065f24bd00cb543004f2b3))
28
+
29
+
30
+ #### Performance Improvements
31
+
32
+ * upload inline data: URIs as resources | AD-15789 ([#4119](https://github.com/Applitools-Dev/sdk/issues/4119)) ([cbd9afa](https://github.com/Applitools-Dev/sdk/commit/cbd9afa8de664b32f23119690b14a0aef998acaf))
33
+
34
+
35
+
36
+ * @applitools/req bumped to 1.11.4
37
+ #### Bug Fixes
38
+
39
+ * version-check/telemetry no longer hold the process — connect deadline + unref | AD-16613 ([#4232](https://github.com/Applitools-Dev/sdk/issues/4232)) ([42bcbb5](https://github.com/Applitools-Dev/sdk/commit/42bcbb5dd8d75ad4aa98c2ec0d284f107fdc98e3))
40
+ * @applitools/snippets bumped to 2.9.8
41
+ #### Bug Fixes
42
+
43
+ * stop element id path at the document the snippet runs in | FLD-5098 ([#4285](https://github.com/Applitools-Dev/sdk/issues/4285)) ([94c4296](https://github.com/Applitools-Dev/sdk/commit/94c4296988a94832435cac92096d10ec78e96ce8))
44
+ * @applitools/dom-capture bumped to 11.8.7
45
+ #### Bug Fixes
46
+
47
+ * check element visibility in its own frame and realm | AD-14874 ([#4267](https://github.com/Applitools-Dev/sdk/issues/4267)) ([222025b](https://github.com/Applitools-Dev/sdk/commit/222025b5b77394b661ab49c307146ec6f191ba14))
48
+ * @applitools/screenshoter bumped to 3.12.34
49
+ #### Bug Fixes
50
+
51
+ * do not write an identity transform in mixed+ scrolling | FLD-4370 ([#4272](https://github.com/Applitools-Dev/sdk/issues/4272)) ([b17532a](https://github.com/Applitools-Dev/sdk/commit/b17532ae1b311d5eb092cb866b42accc8f0fc3ae))
52
+
53
+
54
+
55
+ * @applitools/driver bumped to 1.26.12
56
+
57
+ * @applitools/spec-driver-webdriver bumped to 1.6.12
58
+
59
+ * @applitools/spec-driver-selenium bumped to 1.8.12
60
+
61
+ * @applitools/spec-driver-playwright bumped to 1.9.12
62
+
63
+ * @applitools/spec-driver-puppeteer bumped to 1.8.12
64
+
65
+ * @applitools/nml-client bumped to 1.12.7
66
+
67
+ * @applitools/tunnel-client bumped to 1.12.5
68
+
69
+ * @applitools/ufg-client bumped to 1.23.6
70
+
71
+ * @applitools/core-base bumped to 1.41.0
72
+ #### Features
73
+
74
+ * report NLP step counts per Eyes session | AD-16681 ([#4257](https://github.com/Applitools-Dev/sdk/issues/4257)) ([b84a1cc](https://github.com/Applitools-Dev/sdk/commit/b84a1ccac25c4d61a98ee382a18f54c1011c53e6))
75
+
76
+
77
+ #### Bug Fixes
78
+
79
+ * version-check/telemetry no longer hold the process — connect deadline + unref | AD-16613 ([#4232](https://github.com/Applitools-Dev/sdk/issues/4232)) ([42bcbb5](https://github.com/Applitools-Dev/sdk/commit/42bcbb5dd8d75ad4aa98c2ec0d284f107fdc98e3))
80
+
81
+
82
+
83
+ * @applitools/ec-client bumped to 1.12.50
84
+
85
+
86
+ ## [4.71.0](https://github.com/Applitools-Dev/sdk/compare/js/core@4.70.3...js/core@4.71.0) (2026-10-01)
87
+
88
+
89
+ ### Features
90
+
91
+ * detect git cherry-picks and report them in StartSession | AD-16372 ([#4235](https://github.com/Applitools-Dev/sdk/issues/4235)) ([92db51e](https://github.com/Applitools-Dev/sdk/commit/92db51e3ea713ee7c59be334d8e11d2c312d1095))
92
+
93
+
94
+ ### Dependencies
95
+
96
+ * @applitools/ufg-client bumped to 1.23.5
97
+ #### Bug Fixes
98
+
99
+ * add a deadline for reading a resource body | FLD-4198 ([#4247](https://github.com/Applitools-Dev/sdk/issues/4247)) ([a9faac2](https://github.com/Applitools-Dev/sdk/commit/a9faac2ade1b851e6a00f5bc4a3990f10a345541))
100
+ * @applitools/nml-client bumped to 1.12.6
101
+
102
+ * @applitools/core-base bumped to 1.40.0
103
+ #### Features
104
+
105
+ * detect git cherry-picks and report them in StartSession | AD-16372 ([#4235](https://github.com/Applitools-Dev/sdk/issues/4235)) ([92db51e](https://github.com/Applitools-Dev/sdk/commit/92db51e3ea713ee7c59be334d8e11d2c312d1095))
106
+ * @applitools/ec-client bumped to 1.12.49
107
+
108
+
3
109
  ## [4.70.3](https://github.com/Applitools-Dev/sdk/compare/js/core@4.70.2...js/core@4.70.3) (2026-09-27)
4
110
 
5
111
 
@@ -27,7 +27,10 @@ function makeStartRenders({ core, logger: defaultLogger }) {
27
27
  }, Promise.resolve(new Map()));
28
28
  const renders = await Promise.all(Array.from(clients.entries(), async ([ufgClient, targets]) => {
29
29
  const accessibilityReportOptions = targets.map(target => target.settings.accessibilitySettings
30
- ? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({ accessibilityOptions: target.account.accessibilityOptions })
30
+ ? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({
31
+ accountOptions: target.account.accessibilityOptions,
32
+ runOptions: target.settings.accessibilityRunOptions,
33
+ })
31
34
  : undefined);
32
35
  const [renders, ...environments] = await Promise.all([
33
36
  ufgClient.startRenders({
@@ -26,6 +26,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.makeCheckAndClose = void 0;
27
27
  const driver_1 = require("@applitools/driver");
28
28
  const utils = __importStar(require("@applitools/utils"));
29
+ const lang = __importStar(require("./lang"));
29
30
  function makeCheckAndClose({ type: defaultType = 'classic', eyes, target: defaultTarget, environments: openEnvironments, spec, logger: mainLogger, }) {
30
31
  return async function checkAndClose({ type = defaultType, target = defaultTarget, settings = {}, config, logger = mainLogger, } = {}) {
31
32
  logger = logger.extend(mainLogger, { tags: [`check-and-close-${type}-${utils.general.shortid()}`] });
@@ -36,6 +37,9 @@ function makeCheckAndClose({ type: defaultType = 'classic', eyes, target: defaul
36
37
  ;
37
38
  settings.environments = openEnvironments;
38
39
  }
40
+ if (type === 'classic' && settings.accessibilityRunOptions) {
41
+ throw new Error(lang.accessibilityRunOptionsUnsupportedInClassicError);
42
+ }
39
43
  const driver = (0, driver_1.isDriver)(target, spec)
40
44
  ? await (0, driver_1.makeDriver)({ spec, driver: target, reset: target === defaultTarget, logger })
41
45
  : null;
package/dist/check.js CHANGED
@@ -46,6 +46,9 @@ function makeCheck({ type: defaultType = 'classic', eyes, target: defaultTarget,
46
46
  ;
47
47
  settings.environments = openEnvironments;
48
48
  }
49
+ if (type === 'classic' && settings.accessibilityRunOptions) {
50
+ throw new Error(lang.accessibilityRunOptionsUnsupportedInClassicError);
51
+ }
49
52
  (_a = settings.fully) !== null && _a !== void 0 ? _a : (settings.fully = !settings.region && (!settings.frames || settings.frames.length === 0));
50
53
  (_b = settings.waitBeforeCapture) !== null && _b !== void 0 ? _b : (settings.waitBeforeCapture = defaults_1.DEFAULT_WAIT_BEFORE_CAPTURE);
51
54
  (_c = settings.hideScrollbars) !== null && _c !== void 0 ? _c : (settings.hideScrollbars = true);
package/dist/lang.js CHANGED
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.applitoolsLibConflictingCapabilitiesWarning = exports.requestedEnvironmentOutOfDriverBoundsWarning = exports.breakpointOutOfDriverBoundsWarning = exports.requestedEnvironmentSmallerThenBreakpointWarning = exports.outdatedSdkVersionWarning = exports.figmaExperimentalWarning = exports.edgeUfgEnvironmentDeprecatedWarning = exports.matchLevelContentDeprecatedWarning = void 0;
3
+ exports.applitoolsLibConflictingCapabilitiesWarning = exports.requestedEnvironmentOutOfDriverBoundsWarning = exports.breakpointOutOfDriverBoundsWarning = exports.requestedEnvironmentSmallerThenBreakpointWarning = exports.outdatedSdkVersionWarning = exports.accessibilityRunOptionsUnsupportedInClassicError = 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
6
  exports.figmaExperimentalWarning = `Figma support is experimental. It is not officially supported and may change or be removed in a future release.`;
7
+ exports.accessibilityRunOptionsUnsupportedInClassicError = `Accessibility run options are only supported in the Ultrafast Grid, not with classic runners.`;
7
8
  const outdatedSdkVersionWarning = (options) => {
8
9
  return `You are using "${options.currentVersion}" and there is a newer version "${options.latestVersion}" of the "${options.name}" SDK.`;
9
10
  };
@@ -360,7 +360,10 @@ async function runOfflineSnapshots(options) {
360
360
  });
361
361
  // Only request a report when the check configures an accessibility standard.
362
362
  const accessibilityReportOptions = target.settings.accessibilitySettings
363
- ? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({ accessibilityOptions: account.accessibilityOptions })
363
+ ? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({
364
+ accountOptions: account.accessibilityOptions,
365
+ runOptions: target.settings.accessibilityRunOptions,
366
+ })
364
367
  : undefined;
365
368
  const { renderId, selectorRegions, ...baseTarget } = await ufgClient.render({
366
369
  target: target.target,
package/dist/open-eyes.js CHANGED
@@ -226,7 +226,7 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
226
226
  }
227
227
  };
228
228
  async function handleBranchFallbackList(account, settings, logger) {
229
- var _a, _b;
229
+ var _a, _b, _c;
230
230
  const scm = getScmSettings(account.scmSettings);
231
231
  const integrationActive = !settings.ignoreGitBranching && scm.lookupFallbackList;
232
232
  // Extract gitBranchName (the actual git branch name)
@@ -264,6 +264,18 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
264
264
  logger,
265
265
  });
266
266
  settings.branchLookupFallbackList = outcome.list;
267
+ // Walks from the same tip as latestCommitInfo (the PR head on pull_request events): the server compares the
268
+ // pick positions with a cutoff taken from it.
269
+ if (((_c = outcome.list) === null || _c === void 0 ? void 0 : _c.length) && settings.latestCommitInfo) {
270
+ const cherryPicks = await (0, extract_git_info_1.extractCherryPicks)({
271
+ headSha: settings.latestCommitInfo.sha,
272
+ fallbackBranchNames: outcome.list.map(branch => branch.branchName),
273
+ execOptions: { cwd },
274
+ logger,
275
+ });
276
+ if (cherryPicks === null || cherryPicks === void 0 ? void 0 : cherryPicks.length)
277
+ settings.cherryPicks = cherryPicks;
278
+ }
267
279
  if (outcome.status === 'precondition-failure') {
268
280
  reportScmPreconditionFailure({
269
281
  key: scmPreconditionKey(cwd, settings.gitBranchName),
@@ -27,14 +27,18 @@ exports.buildAccessibilityReportOptions = void 0;
27
27
  const utils = __importStar(require("@applitools/utils"));
28
28
  const REPORT_ID_PLACEHOLDER = '__report_id__';
29
29
  /**
30
- * Builds the `accessibilityReportOptions` render option: forwards `runOptions` verbatim and fills the
30
+ * Builds the `accessibilityReportOptions` render option: forwards run options verbatim and fills the
31
31
  * `uploadUrl` template's `__report_id__` with a fresh random id. Returns `undefined` when the account
32
- * has no accessibility options.
32
+ * has no accessibility options — the upload URL is always the backend's, so a run cannot enable a
33
+ * report on its own.
34
+ *
35
+ * The two sources of run options meet here rather than at each render call site: `accountOptions` is
36
+ * the account-wide config from renderinfo, `runOptions` the per-run override that wins over it.
33
37
  */
34
- function buildAccessibilityReportOptions({ accessibilityOptions, reportId = utils.general.guid(), }) {
35
- if (!accessibilityOptions)
38
+ function buildAccessibilityReportOptions({ accountOptions, runOptions, reportId = utils.general.guid(), }) {
39
+ if (!accountOptions)
36
40
  return undefined;
37
- const uploadUrl = accessibilityOptions.uploadUrl.split(REPORT_ID_PLACEHOLDER).join(encodeURIComponent(reportId));
38
- return { axeCoreOptions: accessibilityOptions.runOptions, uploadUrl };
41
+ const uploadUrl = accountOptions.uploadUrl.split(REPORT_ID_PLACEHOLDER).join(encodeURIComponent(reportId));
42
+ return { axeCoreOptions: runOptions !== null && runOptions !== void 0 ? runOptions : accountOptions.runOptions, uploadUrl };
39
43
  }
40
44
  exports.buildAccessibilityReportOptions = buildAccessibilityReportOptions;
@@ -118,7 +118,10 @@ function makeCheckAndClose({ eyes, target: defaultTarget, environments: defaultE
118
118
  });
119
119
  // Only request a report when the check configures an accessibility standard.
120
120
  const accessibilityReportOptions = settings.accessibilitySettings
121
- ? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({ accessibilityOptions: eyes.test.account.accessibilityOptions })
121
+ ? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({
122
+ accountOptions: eyes.test.account.accessibilityOptions,
123
+ runOptions: settings.accessibilityRunOptions,
124
+ })
122
125
  : undefined;
123
126
  const { renderId, selectorRegions, ...baseTarget } = await ufgClient.render({
124
127
  target: renderTarget,
package/dist/ufg/check.js CHANGED
@@ -126,7 +126,10 @@ function makeCheck({ eyes, target: defaultTarget, environments: defaultEnvironme
126
126
  var _a, _b;
127
127
  // Only request a report when the check configures an accessibility standard.
128
128
  const accessibilityReportOptions = settings.accessibilitySettings
129
- ? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({ accessibilityOptions: eyes.test.account.accessibilityOptions })
129
+ ? (0, build_accessibility_report_options_1.buildAccessibilityReportOptions)({
130
+ accountOptions: eyes.test.account.accessibilityOptions,
131
+ runOptions: settings.accessibilityRunOptions,
132
+ })
130
133
  : undefined;
131
134
  const { renderId, selectorRegions, ...target } = await ufgClient.render({
132
135
  target: renderTarget,
@@ -51,6 +51,7 @@ async function takeDomSnapshot({ context, settings, logger, }) {
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
53
  skipCDTDedup: utils.general.getEnvValue('SKIP_CDT_DEDUP', 'boolean'),
54
+ skipDataUriExtraction: utils.general.getEnvValue('SKIP_DATA_URI_EXTRACTION', 'boolean'),
54
55
  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) ||
55
56
  (environment.isIOS ? 100000 : 250 * 1024 * 1024)),
56
57
  serializeResources: true,
@@ -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.extractBranchLookupFallbackList = exports.getBranchAncestryByLocalBranches = exports.getBranchAncestryByCommits = exports.isISODate = exports.extractBranchingTimestamp = exports.extractBuildIdFromCI = exports.extractCIBranchName = exports.extractGitRepo = exports.extractGitBranch = exports.extractLatestCommitInfo = exports.extractDefaultBranch = exports.gitEnvironmentFindings = exports.checkGitEnvironment = exports.resolveBranchToLocalRef = exports.getPrimaryRemoteName = exports.cacheKey = void 0;
29
+ exports.extractCherryPicks = exports.extractBranchLookupFallbackList = exports.getBranchAncestryByLocalBranches = exports.getBranchAncestryByCommits = exports.isISODate = exports.extractBranchingTimestamp = exports.extractBuildIdFromCI = exports.extractCIBranchName = exports.extractGitRepo = exports.extractGitBranch = exports.extractLatestCommitInfo = exports.extractDefaultBranch = exports.gitEnvironmentFindings = exports.checkGitEnvironment = exports.resolveBranchToLocalRef = exports.getPrimaryRemoteName = exports.cacheKey = void 0;
30
30
  const utils = __importStar(require("@applitools/utils"));
31
31
  const fs_1 = __importDefault(require("fs"));
32
32
  const path_1 = __importDefault(require("path"));
@@ -1703,3 +1703,131 @@ exports.extractBranchLookupFallbackList = Object.assign(async function (options)
1703
1703
  withDiagnostics: (options) => computeBranchLookupFallbackList(options),
1704
1704
  clearCache: computeBranchLookupFallbackList.clearCache,
1705
1705
  });
1706
+ // The server considers at most 50 picks per session.
1707
+ const MAX_CHERRY_PICKS = 50;
1708
+ const CHERRY_PICK_GIT_CONCURRENCY = 10;
1709
+ // Written by `git cherry-pick -x`; a pick of a pick carries one line per hop, the original first.
1710
+ const CHERRY_PICK_LINE = /^\(cherry picked from commit ([0-9a-f]{7,64})\)$/m;
1711
+ function cherryPickLimit(name, defaultValue) {
1712
+ const value = utils.general.getEnvValue(name, 'number');
1713
+ return Number.isInteger(value) && value >= 0 ? value : defaultValue;
1714
+ }
1715
+ /** The full sha of `rev`, or `undefined` when it is not in the local history. Throws on a git error. */
1716
+ async function resolveCommit(git, rev) {
1717
+ const result = await git(['rev-parse', '--verify', '--quiet', `${rev}^{commit}`]);
1718
+ const found = classifyGitPredicate(result);
1719
+ if (found === 'error')
1720
+ throw new Error(`git rev-parse ${rev} failed`);
1721
+ return found ? result.stdout.trim() : undefined;
1722
+ }
1723
+ async function resolveFallbackBranch(git, remoteName, name) {
1724
+ for (const ref of [`${remoteName}/${name}`, name]) {
1725
+ if (await resolveCommit(git, ref))
1726
+ return { name, ref };
1727
+ }
1728
+ throw new Error(`fallback branch "${name}" does not resolve to a commit`);
1729
+ }
1730
+ function parsePickCandidate(record) {
1731
+ var _a;
1732
+ const [pickedSha, pickedCommitTime, body = ''] = record.replace(/^\n/, '').split('\x1f');
1733
+ // Hand-edited lines are not validated; the first line is trusted.
1734
+ const originalSha = (_a = body.match(CHERRY_PICK_LINE)) === null || _a === void 0 ? void 0 : _a[1];
1735
+ return originalSha ? { pickedSha, pickedCommitTime, originalSha } : undefined;
1736
+ }
1737
+ async function collectPickCandidates(git, branches, headSha, logger) {
1738
+ const maxCommits = cherryPickLimit('CHERRY_PICK_MAX_COMMITS', 1000);
1739
+ const recordsPerBranch = await parallelWithLimit(branches, CHERRY_PICK_GIT_CONCURRENCY, async (branch) => {
1740
+ const range = `${branch.ref}..${headSha}`;
1741
+ // The pick time is when it landed on this branch: the committer date (a pick keeps the original author date).
1742
+ const log = await git(['log', `--max-count=${maxCommits}`, '--format=%H%x1f%cI%x1f%B%x1e', range]);
1743
+ if (log.code !== 0)
1744
+ throw new Error(`git log ${range} failed: ${log.stderr.trim()}`);
1745
+ const records = log.stdout.split('\x1e').filter(record => record.trim());
1746
+ if (records.length === maxCommits)
1747
+ logger.log(`Cherry-pick detection read only the newest ${maxCommits} commits of ${range}`);
1748
+ return records;
1749
+ });
1750
+ const candidates = new Map();
1751
+ for (const records of recordsPerBranch) {
1752
+ for (const record of records) {
1753
+ const candidate = parsePickCandidate(record);
1754
+ if (candidate && !candidates.has(candidate.pickedSha))
1755
+ candidates.set(candidate.pickedSha, candidate);
1756
+ }
1757
+ }
1758
+ return Array.from(candidates.values());
1759
+ }
1760
+ function newestCandidates(candidates, logger) {
1761
+ if (candidates.length > MAX_CHERRY_PICKS) {
1762
+ logger.log(`Cherry-pick detection checks only the newest ${MAX_CHERRY_PICKS} of ${candidates.length} picks`);
1763
+ }
1764
+ return [...candidates]
1765
+ .sort((a, b) => epochOrOldest(b.pickedCommitTime) - epochOrOldest(a.pickedCommitTime))
1766
+ .slice(0, MAX_CHERRY_PICKS);
1767
+ }
1768
+ /** The first fallback branch containing `sha` — the server resolves a sha on several branches the same way. */
1769
+ async function findContainingBranch(git, sha, branches) {
1770
+ for (const branch of branches) {
1771
+ const contains = classifyGitPredicate(await git(['merge-base', '--is-ancestor', sha, branch.ref]));
1772
+ if (contains === 'error')
1773
+ throw new Error(`git merge-base --is-ancestor ${sha} ${branch.ref} failed`);
1774
+ if (contains)
1775
+ return branch.name;
1776
+ }
1777
+ return undefined;
1778
+ }
1779
+ async function resolvePick(git, candidate, branches, logger) {
1780
+ const originalSha = await resolveCommit(git, candidate.originalSha);
1781
+ if (!originalSha) {
1782
+ logger.log(`Cherry-pick ${candidate.pickedSha} dropped: original ${candidate.originalSha} is not in the local history`);
1783
+ return undefined;
1784
+ }
1785
+ const originalBranch = await findContainingBranch(git, originalSha, branches);
1786
+ if (!originalBranch) {
1787
+ logger.log(`Cherry-pick ${candidate.pickedSha} dropped: original ${originalSha} is not on a fallback-list branch`);
1788
+ return undefined;
1789
+ }
1790
+ const shown = await git(['show', '-s', '--format=%aI', originalSha]);
1791
+ const originalCommitTime = shown.stdout.trim();
1792
+ if (shown.code !== 0 || !isISODate(originalCommitTime))
1793
+ throw new Error(`no commit time for ${originalSha}`);
1794
+ return {
1795
+ pickedSha: candidate.pickedSha,
1796
+ pickedCommitTime: candidate.pickedCommitTime,
1797
+ originalBranch,
1798
+ originalSha,
1799
+ originalCommitTime,
1800
+ };
1801
+ }
1802
+ async function detectCherryPicks({ headSha, fallbackBranchNames, execOptions, logger = (0, logger_1.makeLogger)(), }) {
1803
+ const timeout = cherryPickLimit('CHERRY_PICK_GIT_TIMEOUT', 10000);
1804
+ const git = args => executeWithLog((0, shell_quote_1.quote)(['git', ...args]), { execOptions: { ...execOptions, timeout }, logger });
1805
+ try {
1806
+ const remoteName = await (0, exports.getPrimaryRemoteName)({ execOptions, logger });
1807
+ const branches = await parallelWithLimit(fallbackBranchNames, CHERRY_PICK_GIT_CONCURRENCY, name => resolveFallbackBranch(git, remoteName, name));
1808
+ const candidates = await collectPickCandidates(git, branches, headSha, logger);
1809
+ const resolved = await parallelWithLimit(newestCandidates(candidates, logger), CHERRY_PICK_GIT_CONCURRENCY, candidate => resolvePick(git, candidate, branches, logger));
1810
+ const picks = resolved.filter((pick) => Boolean(pick));
1811
+ logger.log('Detected cherry-picks', JSON.stringify(picks));
1812
+ return picks;
1813
+ }
1814
+ catch (err) {
1815
+ // Any git error means nothing is sent: a partial list would look like "no more picks".
1816
+ logger.log(`Cherry-pick detection skipped: ${err instanceof Error ? err.message : err}`);
1817
+ return undefined;
1818
+ }
1819
+ }
1820
+ /**
1821
+ * `git cherry-pick -x` commits between `headSha` and the fork point with each fallback-list branch, resolved to
1822
+ * the original commit and kept only when a fallback-list branch contains it. Newest first.
1823
+ *
1824
+ * @returns `[]` when there are no picks, `undefined` when detection could not run.
1825
+ */
1826
+ exports.extractCherryPicks = utils.general.cachify(detectCherryPicks, args => {
1827
+ var _a;
1828
+ return ({
1829
+ headSha: args[0].headSha,
1830
+ fallbackBranchNames: args[0].fallbackBranchNames.join('\n'),
1831
+ cwd: (_a = args[0].execOptions) === null || _a === void 0 ? void 0 : _a.cwd,
1832
+ });
1833
+ });
@@ -32,6 +32,10 @@ const lt_1 = __importDefault(require("semver/functions/lt"));
32
32
  const chalk_1 = __importDefault(require("chalk"));
33
33
  const utils = __importStar(require("@applitools/utils"));
34
34
  const lang = __importStar(require("../lang"));
35
+ // `unref` frees an already-connected socket, but a connect that stalls (unproxied traffic silently
36
+ // dropped on a proxy-only network) keeps the process alive until the OS gives up (~75s) — only a
37
+ // deadline ends it. The check is best-effort, so the deadline is short.
38
+ const versionReqOptions = { unref: true, connectionTimeout: 5000 };
35
39
  const extractors = {
36
40
  js: utils.general.cachify(extractLatestJsPackageVersion),
37
41
  java: utils.general.cachify(extractLatestJavaPackageVersion),
@@ -58,31 +62,31 @@ async function validateSdkVersion(sdk, { logger }) {
58
62
  }
59
63
  exports.validateSdkVersion = validateSdkVersion;
60
64
  async function extractLatestJsPackageVersion(name) {
61
- const response = await (0, req_1.req)(`https://registry.npmjs.org/${name}/latest`);
65
+ const response = await (0, req_1.req)(`https://registry.npmjs.org/${name}/latest`, versionReqOptions);
62
66
  const info = await response.json();
63
67
  return info.version;
64
68
  }
65
69
  exports.extractLatestJsPackageVersion = extractLatestJsPackageVersion;
66
70
  async function extractLatestJavaPackageVersion(name) {
67
- const response = await (0, req_1.req)(`https://search.maven.org/solrsearch/select?q=a:${name}&wt=json`);
71
+ const response = await (0, req_1.req)(`https://search.maven.org/solrsearch/select?q=a:${name}&wt=json`, versionReqOptions);
68
72
  const info = await response.json();
69
73
  return info.response.docs[0].latestVersion;
70
74
  }
71
75
  exports.extractLatestJavaPackageVersion = extractLatestJavaPackageVersion;
72
76
  async function extractLatestDotNetPackageVersion(name) {
73
- const response = await (0, req_1.req)(`https://api.nuget.org/v3-flatcontainer/${name.toLowerCase()}/index.json`);
77
+ const response = await (0, req_1.req)(`https://api.nuget.org/v3-flatcontainer/${name.toLowerCase()}/index.json`, versionReqOptions);
74
78
  const info = await response.json();
75
79
  return info.versions[info.versions.length - 1];
76
80
  }
77
81
  exports.extractLatestDotNetPackageVersion = extractLatestDotNetPackageVersion;
78
82
  async function extractLatestPythonPackageVersion(name) {
79
- const response = await (0, req_1.req)(`https://pypi.org/pypi/${name}/json`);
83
+ const response = await (0, req_1.req)(`https://pypi.org/pypi/${name}/json`, versionReqOptions);
80
84
  const info = await response.json();
81
85
  return info.info.version;
82
86
  }
83
87
  exports.extractLatestPythonPackageVersion = extractLatestPythonPackageVersion;
84
88
  async function extractLatestRubyPackageVersion(name) {
85
- const response = await (0, req_1.req)(`https://rubygems.org/api/v1/gems/${name}.json`);
89
+ const response = await (0, req_1.req)(`https://rubygems.org/api/v1/gems/${name}.json`, versionReqOptions);
86
90
  const info = await response.json();
87
91
  return info.version;
88
92
  }
package/package.json CHANGED
@@ -1,15 +1,7 @@
1
1
  {
2
2
  "name": "@applitools/core",
3
- "version": "4.70.3",
3
+ "version": "4.72.0",
4
4
  "homepage": "https://applitools.com",
5
- "bugs": {
6
- "url": "https://github.com/applitools/eyes.sdk.javascript1/issues"
7
- },
8
- "repository": {
9
- "type": "git",
10
- "url": "git://github.com/applitools/eyes.sdk.javascript1.git",
11
- "directory": "js/packages/core-base"
12
- },
13
5
  "license": "SEE LICENSE IN LICENSE",
14
6
  "author": {
15
7
  "name": "Applitools Team",
@@ -63,19 +55,19 @@
63
55
  "test:ios-simulator": "MOCHA_ONLY_TAGS=ios-simulator MOCHA_GROUP=ios-simulator run --top-level mocha ./test/e2e/classic/webview.spec.ts --exit --require ./test/mocha-global-setup.js"
64
56
  },
65
57
  "dependencies": {
66
- "@applitools/core-base": "1.39.1",
67
- "@applitools/dom-capture": "11.8.6",
68
- "@applitools/dom-snapshot": "4.18.4",
69
- "@applitools/driver": "1.26.11",
70
- "@applitools/ec-client": "1.12.48",
58
+ "@applitools/core-base": "1.41.0",
59
+ "@applitools/dom-capture": "11.8.7",
60
+ "@applitools/dom-snapshot": "4.18.5",
61
+ "@applitools/driver": "1.26.12",
62
+ "@applitools/ec-client": "1.12.50",
71
63
  "@applitools/image": "1.3.3",
72
64
  "@applitools/logger": "2.3.1",
73
- "@applitools/nml-client": "1.12.5",
74
- "@applitools/req": "1.11.3",
75
- "@applitools/screenshoter": "3.12.33",
76
- "@applitools/snippets": "2.9.7",
65
+ "@applitools/nml-client": "1.12.7",
66
+ "@applitools/req": "1.11.4",
67
+ "@applitools/screenshoter": "3.12.34",
68
+ "@applitools/snippets": "2.9.8",
77
69
  "@applitools/socket": "1.3.17",
78
- "@applitools/ufg-client": "1.23.4",
70
+ "@applitools/ufg-client": "1.23.6",
79
71
  "@applitools/utils": "1.15.2",
80
72
  "abort-controller": "3.0.0",
81
73
  "chalk": "4.1.2",
@@ -87,13 +79,13 @@
87
79
  },
88
80
  "devDependencies": {
89
81
  "@applitools/bongo": "^5.10.0",
90
- "@applitools/spec-driver-playwright": "^1.9.11",
91
- "@applitools/spec-driver-puppeteer": "^1.8.11",
92
- "@applitools/spec-driver-selenium": "^1.8.11",
93
- "@applitools/spec-driver-webdriver": "^1.6.11",
82
+ "@applitools/spec-driver-playwright": "^1.9.12",
83
+ "@applitools/spec-driver-puppeteer": "^1.8.12",
84
+ "@applitools/spec-driver-selenium": "^1.8.12",
85
+ "@applitools/spec-driver-webdriver": "^1.6.12",
94
86
  "@applitools/test-server": "^1.4.7",
95
87
  "@applitools/test-utils": "^1.5.17",
96
- "@applitools/tunnel-client": "^1.12.4",
88
+ "@applitools/tunnel-client": "^1.12.5",
97
89
  "@swc-node/register": "^1.6.8",
98
90
  "@types/mocha": "^10.0.7",
99
91
  "@types/node": "^12.20.55",
@@ -108,6 +100,7 @@
108
100
  "nock": "^13.3.2",
109
101
  "playwright": "1.47.0",
110
102
  "png-async": "^0.9.4",
103
+ "pngjs": "^3.0.0",
111
104
  "puppeteer": "^24.11.1",
112
105
  "selenium-webdriver": "^4.15.0",
113
106
  "webdriver": "^5.23.0"
package/types/lang.d.ts CHANGED
@@ -1,6 +1,7 @@
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
3
  export declare const figmaExperimentalWarning = "Figma support is experimental. It is not officially supported and may change or be removed in a future release.";
4
+ export declare const accessibilityRunOptionsUnsupportedInClassicError = "Accessibility run options are only supported in the Ultrafast Grid, not with classic runners.";
4
5
  export declare const outdatedSdkVersionWarning: (options: {
5
6
  name: string;
6
7
  currentVersion: string;
@@ -1,11 +1,16 @@
1
1
  import { type Account } from '@applitools/core-base';
2
2
  /**
3
- * Builds the `accessibilityReportOptions` render option: forwards `runOptions` verbatim and fills the
3
+ * Builds the `accessibilityReportOptions` render option: forwards run options verbatim and fills the
4
4
  * `uploadUrl` template's `__report_id__` with a fresh random id. Returns `undefined` when the account
5
- * has no accessibility options.
5
+ * has no accessibility options — the upload URL is always the backend's, so a run cannot enable a
6
+ * report on its own.
7
+ *
8
+ * The two sources of run options meet here rather than at each render call site: `accountOptions` is
9
+ * the account-wide config from renderinfo, `runOptions` the per-run override that wins over it.
6
10
  */
7
- export declare function buildAccessibilityReportOptions({ accessibilityOptions, reportId, }: {
8
- accessibilityOptions?: Account['accessibilityOptions'];
11
+ export declare function buildAccessibilityReportOptions({ accountOptions, runOptions, reportId, }: {
12
+ accountOptions?: Account['accessibilityOptions'];
13
+ runOptions?: unknown;
9
14
  reportId?: string;
10
15
  }): {
11
16
  axeCoreOptions: unknown;
@@ -33,6 +33,7 @@ export declare function makeCheckAndClose<TSpec extends SpecType>({ eyes, target
33
33
  }) | undefined;
34
34
  headers?: Record<string, string> | undefined;
35
35
  skipRootHtmlResource?: boolean | undefined;
36
+ accessibilityRunOptions?: unknown;
36
37
  } & import("@applitools/core-base").CloseSettings & {
37
38
  environments?: Environment[] | undefined;
38
39
  }) | undefined;
@@ -69,6 +69,8 @@ export type SnapshotSettings<TSpec extends SpecType> = AutomationCore.CheckSetti
69
69
  headers?: Record<string, string>;
70
70
  /** @internal */
71
71
  skipRootHtmlResource?: boolean;
72
+ /** @internal axe.run() options for this run, forwarded verbatim in place of the account's. */
73
+ accessibilityRunOptions?: unknown;
72
74
  };
73
75
  export type CheckSettings<TSpec extends SpecType> = SnapshotSettings<TSpec>;
74
76
  export type SafeCheckSettings = CheckSettings<SpecType<never, never, UFGSelector, never, never>>;
@@ -1,6 +1,7 @@
1
1
  /// <reference types="node" />
2
2
  import type { ExecOptions } from 'child_process';
3
3
  import { type Logger } from '@applitools/logger';
4
+ import type { CherryPick } from '@applitools/core-base';
4
5
  type Options = {
5
6
  execOptions?: ExecOptions;
6
7
  logger: Logger;
@@ -254,4 +255,23 @@ export declare const extractBranchLookupFallbackList: ((options: BranchLookupFal
254
255
  withDiagnostics: (options: BranchLookupFallbackListOptions) => Promise<BranchLookupOutcome>;
255
256
  clearCache: () => void;
256
257
  };
258
+ type CherryPicksOptions = {
259
+ headSha: string;
260
+ fallbackBranchNames: string[];
261
+ execOptions?: ExecOptions;
262
+ logger?: Logger;
263
+ };
264
+ declare function detectCherryPicks({ headSha, fallbackBranchNames, execOptions, logger, }: CherryPicksOptions): Promise<CherryPick[] | undefined>;
265
+ /**
266
+ * `git cherry-pick -x` commits between `headSha` and the fork point with each fallback-list branch, resolved to
267
+ * the original commit and kept only when a fallback-list branch contains it. Newest first.
268
+ *
269
+ * @returns `[]` when there are no picks, `undefined` when detection could not run.
270
+ */
271
+ export declare const extractCherryPicks: typeof detectCherryPicks & {
272
+ getCachedValues(): Promise<CherryPick[] | undefined>[];
273
+ setCachedValue(key: any, value: Promise<CherryPick[] | undefined>): void;
274
+ clearCache(): void;
275
+ TTL?: number | undefined;
276
+ };
257
277
  export {};