@applitools/core 4.62.0 → 4.63.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,230 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.63.1](https://github.com/Applitools-Dev/sdk/compare/js/core@4.63.0...js/core@4.63.1) (2026-05-26)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * prevent BrightCove/video.js player corruption during layoutBreakpoints snapshots ([#3828](https://github.com/Applitools-Dev/sdk/issues/3828)) ([380fbae](https://github.com/Applitools-Dev/sdk/commit/380fbaeb1c6a964c640975f1a109417db51f691c))
9
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
10
+ * truly disable NML broker URL cache between checks | FLD-4283 ([#3837](https://github.com/Applitools-Dev/sdk/issues/3837)) ([da76dd3](https://github.com/Applitools-Dev/sdk/commit/da76dd3227c3297320be0d19558a7437b5733999))
11
+
12
+
13
+ ### Dependencies
14
+
15
+ * @applitools/utils bumped to 1.14.5
16
+ #### Bug Fixes
17
+
18
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
19
+ * @applitools/logger bumped to 2.2.12
20
+ #### Bug Fixes
21
+
22
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
23
+
24
+
25
+
26
+ * @applitools/dom-shared bumped to 1.2.1
27
+ #### Bug Fixes
28
+
29
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
30
+ * @applitools/dom-snapshot bumped to 4.17.2
31
+ #### Bug Fixes
32
+
33
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
34
+
35
+
36
+
37
+ * @applitools/socket bumped to 1.3.13
38
+ #### Bug Fixes
39
+
40
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
41
+
42
+
43
+
44
+ * @applitools/req bumped to 1.10.2
45
+ #### Bug Fixes
46
+
47
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
48
+
49
+
50
+
51
+ * @applitools/image bumped to 1.2.11
52
+ #### Bug Fixes
53
+
54
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
55
+
56
+
57
+
58
+ * @applitools/snippets bumped to 2.9.2
59
+ #### Bug Fixes
60
+
61
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
62
+
63
+
64
+
65
+ * @applitools/dom-capture bumped to 11.8.1
66
+ #### Bug Fixes
67
+
68
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
69
+
70
+
71
+
72
+ * @applitools/driver bumped to 1.26.2
73
+ #### Bug Fixes
74
+
75
+ * redact cookie values from logs to prevent sensitive data exposure | AD-12834 ([#3835](https://github.com/Applitools-Dev/sdk/issues/3835)) ([d883647](https://github.com/Applitools-Dev/sdk/commit/d883647d8e007467bd2770ddb79f89be057067b9))
76
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
77
+
78
+
79
+
80
+ * @applitools/spec-driver-webdriver bumped to 1.6.2
81
+ #### Bug Fixes
82
+
83
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
84
+
85
+
86
+
87
+ * @applitools/spec-driver-selenium bumped to 1.8.2
88
+ #### Bug Fixes
89
+
90
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
91
+
92
+
93
+
94
+ * @applitools/spec-driver-playwright bumped to 1.9.2
95
+ #### Bug Fixes
96
+
97
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
98
+
99
+
100
+
101
+ * @applitools/spec-driver-puppeteer bumped to 1.8.2
102
+ #### Bug Fixes
103
+
104
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
105
+
106
+
107
+
108
+ * @applitools/screenshoter bumped to 3.12.21
109
+ #### Bug Fixes
110
+
111
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
112
+
113
+
114
+
115
+ * @applitools/nml-client bumped to 1.11.28
116
+ #### Bug Fixes
117
+
118
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
119
+
120
+
121
+
122
+ * @applitools/tunnel-client bumped to 1.11.14
123
+ #### Bug Fixes
124
+
125
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
126
+
127
+
128
+
129
+ * @applitools/ufg-client bumped to 1.22.2
130
+ #### Bug Fixes
131
+
132
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
133
+ * strip extra fields from checkResources log output ([#3836](https://github.com/Applitools-Dev/sdk/issues/3836)) ([6d75209](https://github.com/Applitools-Dev/sdk/commit/6d752094abb6fcc0e998cf95e1f266964dbfd6f3))
134
+
135
+
136
+
137
+ * @applitools/core-base bumped to 1.34.1
138
+ #### Bug Fixes
139
+
140
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
141
+
142
+
143
+
144
+ * @applitools/ec-client bumped to 1.12.30
145
+ #### Bug Fixes
146
+
147
+ * strip .d.ts extension from import() type specifiers | FLD-4429 ([#3830](https://github.com/Applitools-Dev/sdk/issues/3830)) ([37aac45](https://github.com/Applitools-Dev/sdk/commit/37aac45958b713702bea87c144623dd3e65fce79))
148
+
149
+
150
+
151
+ * @applitools/test-server bumped to 1.4.4
152
+
153
+
154
+ ## [4.63.0](https://github.com/Applitools-Dev/sdk/compare/js/core@4.62.0...js/core@4.63.0) (2026-05-19)
155
+
156
+
157
+ ### Features
158
+
159
+ * soft-deprecate CheckSettings.timeout() with telemetry [[#3705](https://github.com/Applitools-Dev/sdk/issues/3705)] ([#3805](https://github.com/Applitools-Dev/sdk/issues/3805)) ([a5d6390](https://github.com/Applitools-Dev/sdk/commit/a5d6390f17fc0380736b88cbe05dd147e66a86cf))
160
+
161
+
162
+ ### Bug Fixes
163
+
164
+ * avoid double-scaling environment viewport in classic mode | FLD-4359 ([#3793](https://github.com/Applitools-Dev/sdk/issues/3793)) ([246050b](https://github.com/Applitools-Dev/sdk/commit/246050b8a7984b869c068262162a69191c834947))
165
+ * http2 support via settings AD 13119 ([#3804](https://github.com/Applitools-Dev/sdk/issues/3804)) ([759d451](https://github.com/Applitools-Dev/sdk/commit/759d4518254828140aa50e2c5de14e80af0b997c))
166
+
167
+
168
+ ### Dependencies
169
+
170
+ * @applitools/snippets bumped to 2.9.1
171
+ #### Bug Fixes
172
+
173
+ * declare @babel/preset-typescript dep for sandbox build ([#3815](https://github.com/Applitools-Dev/sdk/issues/3815)) ([10c44c6](https://github.com/Applitools-Dev/sdk/commit/10c44c66635f1ed4e0361f22ecebf2221c2cc912))
174
+ * @applitools/dom-snapshot bumped to 4.17.1
175
+
176
+ * @applitools/req bumped to 1.10.1
177
+ #### Bug Fixes
178
+
179
+ * http2 support via settings AD 13119 ([#3804](https://github.com/Applitools-Dev/sdk/issues/3804)) ([759d451](https://github.com/Applitools-Dev/sdk/commit/759d4518254828140aa50e2c5de14e80af0b997c))
180
+ * @applitools/driver bumped to 1.26.1
181
+ #### Bug Fixes
182
+
183
+ * address CI regressions from executeBrowserCommands and Chromium 140+ service worker ([#3848](https://github.com/Applitools-Dev/sdk/issues/3848)) ([099ada5](https://github.com/Applitools-Dev/sdk/commit/099ada5e52d4153157b98c2203df428579527e49))
184
+
185
+
186
+
187
+ * @applitools/spec-driver-playwright bumped to 1.9.1
188
+ #### Bug Fixes
189
+
190
+ * address CI regressions from executeBrowserCommands and Chromium 140+ service worker ([#3848](https://github.com/Applitools-Dev/sdk/issues/3848)) ([099ada5](https://github.com/Applitools-Dev/sdk/commit/099ada5e52d4153157b98c2203df428579527e49))
191
+
192
+
193
+
194
+ * @applitools/ufg-client bumped to 1.22.1
195
+ #### Bug Fixes
196
+
197
+ * http2 support via settings AD 13119 ([#3804](https://github.com/Applitools-Dev/sdk/issues/3804)) ([759d451](https://github.com/Applitools-Dev/sdk/commit/759d4518254828140aa50e2c5de14e80af0b997c))
198
+
199
+
200
+
201
+ * @applitools/spec-driver-webdriver bumped to 1.6.1
202
+
203
+ * @applitools/spec-driver-selenium bumped to 1.8.1
204
+
205
+ * @applitools/spec-driver-puppeteer bumped to 1.8.1
206
+
207
+ * @applitools/screenshoter bumped to 3.12.20
208
+
209
+ * @applitools/nml-client bumped to 1.11.27
210
+
211
+ * @applitools/tunnel-client bumped to 1.11.13
212
+
213
+ * @applitools/core-base bumped to 1.34.0
214
+ #### Features
215
+
216
+ * soft-deprecate CheckSettings.timeout() with telemetry [[#3705](https://github.com/Applitools-Dev/sdk/issues/3705)] ([#3805](https://github.com/Applitools-Dev/sdk/issues/3805)) ([a5d6390](https://github.com/Applitools-Dev/sdk/commit/a5d6390f17fc0380736b88cbe05dd147e66a86cf))
217
+
218
+
219
+ #### Bug Fixes
220
+
221
+ * http2 support via settings AD 13119 ([#3804](https://github.com/Applitools-Dev/sdk/issues/3804)) ([759d451](https://github.com/Applitools-Dev/sdk/commit/759d4518254828140aa50e2c5de14e80af0b997c))
222
+
223
+
224
+
225
+ * @applitools/ec-client bumped to 1.12.29
226
+
227
+
3
228
  ## [4.62.0](https://github.com/Applitools-Dev/sdk/compare/js/core@4.61.1...js/core@4.62.0) (2026-05-05)
4
229
 
5
230
 
@@ -4,15 +4,17 @@ exports.makeGetNMLClient = exports.clearNMLClients = void 0;
4
4
  const nml_client_1 = require("@applitools/nml-client");
5
5
  const utils_1 = require("@applitools/utils");
6
6
  const clients = new Map();
7
+ const cacheDisabled = new Set();
7
8
  // for tests
8
9
  function clearNMLClients() {
9
10
  clients.clear();
11
+ cacheDisabled.clear();
10
12
  }
11
13
  exports.clearNMLClients = clearNMLClients;
12
14
  function makeGetNMLClient({ client, logger: mainLogger }) {
13
15
  return {
14
16
  getNMLClient,
15
- clearNMLClientCache,
17
+ disableNMLClientCache,
16
18
  };
17
19
  async function getNMLClient({ driver, settings, logger, }) {
18
20
  if (client)
@@ -20,26 +22,30 @@ function makeGetNMLClient({ client, logger: mainLogger }) {
20
22
  logger = logger.extend(mainLogger);
21
23
  const { sessionId } = await driver.getDriverInfo();
22
24
  const cacheKey = sessionId !== null && sessionId !== void 0 ? sessionId : driver.guid;
23
- const clientFromCache = clients.get(cacheKey);
24
- if (clientFromCache) {
25
- return clientFromCache;
25
+ if (cacheDisabled.has(cacheKey)) {
26
+ logger.log('NML broker url cache is disabled for this session — re-extracting broker url');
26
27
  }
27
28
  else {
28
- const brokerUrl = await driver.extractBrokerUrl();
29
- if (!brokerUrl)
30
- throw new Error('Unable to extract broker url from the device');
31
- if (utils_1.types.has(brokerUrl, 'error')) {
32
- logger.error('Broker url extraction has failed with error', brokerUrl.error);
33
- throw new Error(brokerUrl.error);
34
- }
35
- const nmlClient = (0, nml_client_1.makeNMLClient)({ settings: { brokerUrl, ...settings }, logger });
36
- clients.set(cacheKey, nmlClient);
37
- return nmlClient;
29
+ const clientFromCache = clients.get(cacheKey);
30
+ if (clientFromCache)
31
+ return clientFromCache;
32
+ }
33
+ const brokerUrl = await driver.extractBrokerUrl();
34
+ if (!brokerUrl)
35
+ throw new Error('Unable to extract broker url from the device');
36
+ if (utils_1.types.has(brokerUrl, 'error')) {
37
+ logger.error('Broker url extraction has failed with error', brokerUrl.error);
38
+ throw new Error(brokerUrl.error);
38
39
  }
40
+ const nmlClient = (0, nml_client_1.makeNMLClient)({ settings: { brokerUrl, ...settings }, logger });
41
+ if (!cacheDisabled.has(cacheKey))
42
+ clients.set(cacheKey, nmlClient);
43
+ return nmlClient;
39
44
  }
40
- async function clearNMLClientCache(driver) {
45
+ async function disableNMLClientCache(driver) {
41
46
  const { sessionId } = await driver.getDriverInfo();
42
47
  const cacheKey = sessionId !== null && sessionId !== void 0 ? sessionId : driver.guid;
48
+ cacheDisabled.add(cacheKey);
43
49
  clients.delete(cacheKey);
44
50
  }
45
51
  }
package/dist/check.js CHANGED
@@ -35,8 +35,8 @@ const chalk_1 = __importDefault(require("chalk"));
35
35
  function makeCheck({ type: defaultType = 'classic', eyes, target: defaultTarget, spec, logger: mainLogger, }) {
36
36
  let stepIndex = 0;
37
37
  return async function check({ type = defaultType, target = defaultTarget, settings, config, logger = mainLogger, } = {}) {
38
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
39
- var _r, _s, _t, _u, _v;
38
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
39
+ var _s, _t, _u, _v, _w;
40
40
  logger = logger.extend(mainLogger, { tags: [`check-${type}-${utils.general.shortid()}`] });
41
41
  settings = { ...config === null || config === void 0 ? void 0 : config.screenshot, ...config === null || config === void 0 ? void 0 : config.check, ...settings };
42
42
  (_a = settings.fully) !== null && _a !== void 0 ? _a : (settings.fully = !settings.region && (!settings.frames || settings.frames.length === 0));
@@ -56,23 +56,32 @@ function makeCheck({ type: defaultType = 'classic', eyes, target: defaultTarget,
56
56
  !utils.types.isEmpty(settings.accessibilitySettings));
57
57
  (_h = settings.autProxy) !== null && _h !== void 0 ? _h : (settings.autProxy = eyes.test.eyesServer.proxy);
58
58
  (_j = settings.useDom) !== null && _j !== void 0 ? _j : (settings.useDom = false);
59
- (_k = (_r = settings).matchTimeout) !== null && _k !== void 0 ? _k : (_r.matchTimeout = 0);
59
+ (_k = (_s = settings).matchTimeout) !== null && _k !== void 0 ? _k : (_s.matchTimeout = 0);
60
60
  settings.lazyLoad = settings.lazyLoad === true ? {} : settings.lazyLoad;
61
61
  if (settings.lazyLoad) {
62
- (_l = (_s = settings.lazyLoad).scrollLength) !== null && _l !== void 0 ? _l : (_s.scrollLength = defaults_1.DEFAULT_LAZY_LOAD.scrollLength);
63
- (_m = (_t = settings.lazyLoad).waitingTime) !== null && _m !== void 0 ? _m : (_t.waitingTime = defaults_1.DEFAULT_LAZY_LOAD.waitingTime);
64
- (_o = (_u = settings.lazyLoad).maxAmountToScroll) !== null && _o !== void 0 ? _o : (_u.maxAmountToScroll = defaults_1.DEFAULT_LAZY_LOAD.maxAmountToScroll);
62
+ (_l = (_t = settings.lazyLoad).scrollLength) !== null && _l !== void 0 ? _l : (_t.scrollLength = defaults_1.DEFAULT_LAZY_LOAD.scrollLength);
63
+ (_m = (_u = settings.lazyLoad).waitingTime) !== null && _m !== void 0 ? _m : (_u.waitingTime = defaults_1.DEFAULT_LAZY_LOAD.waitingTime);
64
+ (_o = (_v = settings.lazyLoad).maxAmountToScroll) !== null && _o !== void 0 ? _o : (_v.maxAmountToScroll = defaults_1.DEFAULT_LAZY_LOAD.maxAmountToScroll);
65
65
  }
66
66
  settings.stepIndex = stepIndex++;
67
67
  (_p = settings.waitBetweenStitches) !== null && _p !== void 0 ? _p : (settings.waitBetweenStitches = utils.types.isObject(settings.lazyLoad)
68
68
  ? settings.lazyLoad.waitingTime
69
69
  : defaults_1.DEFAULT_WAIT_BEFORE_CAPTURE);
70
70
  if (settings.mobileOptions) {
71
- (_q = (_v = settings.mobileOptions).keepNavigationBar) !== null && _q !== void 0 ? _q : (_v.keepNavigationBar = false);
71
+ (_q = (_w = settings.mobileOptions).keepNavigationBar) !== null && _q !== void 0 ? _q : (_w.keepNavigationBar = false);
72
72
  }
73
73
  if (settings.matchLevel === 'Content') {
74
74
  logger.console.log(chalk_1.default.yellow(lang.matchLevelContentDeprecatedWarning));
75
75
  }
76
+ if ((_r = settings.usedDeprecations) === null || _r === void 0 ? void 0 : _r.length) {
77
+ void eyes.core.logEvent({
78
+ settings: {
79
+ ...eyes.test.eyesServer,
80
+ level: 'Notice',
81
+ event: { type: 'SDK.deprecation', deprecations: settings.usedDeprecations },
82
+ },
83
+ });
84
+ }
76
85
  const driver = (0, driver_1.isDriver)(target, spec)
77
86
  ? await (0, driver_1.makeDriver)({ spec, driver: target, reset: target === defaultTarget, logger })
78
87
  : null;
@@ -34,8 +34,8 @@ async function extractDefaultEnvironments({ driver, settings, }) {
34
34
  try {
35
35
  const driverEnvironment = await driver.getEnvironment();
36
36
  const viewport = await driver.getViewport();
37
- const size = await driver.getViewportSize();
38
- environment.viewportSize = utils.geometry.scale(size, viewport.viewportScale);
37
+ // scale layout viewport by visualViewport.scale to get visible CSS viewport (avoids double-scaling spec drivers that already return CSS pixels)
38
+ environment.viewportSize = utils.geometry.scale(viewport.viewportSize, viewport.viewportScale);
39
39
  environment.environmentId = utils.general.guid();
40
40
  if (driverEnvironment.isEC)
41
41
  environment.ecSessionId = (_a = (await driver.getSessionId())) !== null && _a !== void 0 ? _a : undefined;
package/dist/open-eyes.js CHANGED
@@ -145,7 +145,7 @@ function makeOpenEyes({ type: defaultType = 'classic', clients, batch, removeDup
145
145
  logger,
146
146
  });
147
147
  if (settings.disableBrokerUrlCache && driver) {
148
- await core.clearNMLClientCache(driver);
148
+ await core.disableNMLClientCache(driver);
149
149
  }
150
150
  const getTypedEyes = (0, get_typed_eyes_1.makeGetTypedEyes)({
151
151
  type,
package/dist/ufg/check.js CHANGED
@@ -52,6 +52,7 @@ function makeCheck({ eyes, target: defaultTarget, environments: defaultEnvironme
52
52
  ...eyes.test.ufgServer,
53
53
  eyesServerUrl: eyes.test.eyesServer.eyesServerUrl,
54
54
  apiKey: eyes.test.eyesServer.apiKey,
55
+ httpVersion: eyes.test.eyesServer.httpVersion,
55
56
  },
56
57
  logger,
57
58
  });
@@ -16,6 +16,7 @@ async function createRenderTargetFromSnapshot({ ufgClient, snapshot, logger, url
16
16
  proxy: snapshot.account.eyesServer.proxy,
17
17
  autProxy: snapshot.settings.autProxy,
18
18
  skipRootHtmlResource: snapshot.settings.skipRootHtmlResource,
19
+ httpVersion: snapshot.settings.httpVersion,
19
20
  },
20
21
  logger,
21
22
  });
@@ -0,0 +1,382 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.pagePayload = exports.makeMediaPlaybackGuard = void 0;
4
+ /**
5
+ * Prevents media playback side-effects during the layoutBreakpoints snapshot loop.
6
+ *
7
+ * dom-snapshot calls video.play() per breakpoint to capture a video frame. For
8
+ * BrightCove / video.js players this triggers CSS class changes (has-started, is-playing)
9
+ * and subtitle text injection that outlast the corresponding pause() call. With multiple
10
+ * breakpoints the mutations accumulate and corrupt visual snapshots.
11
+ *
12
+ * Lifecycle:
13
+ * const guard = makeMediaPlaybackGuard({context, logger})
14
+ * await guard.setup() // once, before the loop
15
+ * for (const entry of entries) {
16
+ * await guard.beforeResize() // before setViewportSize
17
+ * // setViewportSize / reload / lazyLoad
18
+ * await guard.beforeSnapshot() // after resize, before takeDomSnapshot
19
+ * // takeDomSnapshot
20
+ * await guard.afterSnapshot() // after takeDomSnapshot
21
+ * }
22
+ * // setViewportSize (restore)
23
+ * await guard.teardown() // once, after final viewport restore
24
+ *
25
+ * Implementation notes:
26
+ *
27
+ * The TypeScript layer is just orchestration — `setup` / `beforeResize` / etc. each
28
+ * dispatch a phase name to a single self-contained page-side payload (`pagePayload`
29
+ * below) via `context.execute`. The first call installs a library at `window[ns]`
30
+ * with one handler per lifecycle method; subsequent calls just look up and run
31
+ * `window[ns][phase]()`. Teardown deletes `window[ns]` so a fresh guard starts clean.
32
+ *
33
+ * - `ns` is randomised per guard instance so concurrent guards on the same page
34
+ * (e.g. nested checks) do not collide.
35
+ * - Per-element state lives in a `WeakMap`, not on the elements themselves — no
36
+ * marker properties leak onto the page's DOM.
37
+ * - A single `walkAll(root, fn)` helper handles every Document/ShadowRoot traversal.
38
+ * - A `MutationObserver` covers the gap between phase calls (e.g. videos that
39
+ * BrightCove creates during setViewportSize). The synchronous walks in
40
+ * `beforeSnapshot` remain because the observer fires on a microtask and would
41
+ * otherwise lose the race against dom-snapshot's serialization.
42
+ */
43
+ function makeMediaPlaybackGuard({ context, logger, }) {
44
+ const ns = '__applitools_mpg_' + Math.random().toString(36).slice(2) + '__';
45
+ return {
46
+ setup: () => runPhase('setup'),
47
+ beforeResize: () => runPhase('beforeResize'),
48
+ beforeSnapshot: () => runPhase('beforeSnapshot'),
49
+ afterSnapshot: () => runPhase('afterSnapshot'),
50
+ teardown: () => runPhase('teardown'),
51
+ };
52
+ function runPhase(phase) {
53
+ return context
54
+ .execute(pagePayload, { ns, phase })
55
+ .catch((err) => logger.warn(`media guard: ${phase} failed`, err));
56
+ }
57
+ }
58
+ exports.makeMediaPlaybackGuard = makeMediaPlaybackGuard;
59
+ /**
60
+ * The entire page-side library — a single self-contained function shipped to the
61
+ * browser by `context.execute`. No closure variables from the TypeScript side are
62
+ * visible inside this body.
63
+ *
64
+ * Contract: on first invocation, installs `window[ns]` with `{setup, beforeResize,
65
+ * beforeSnapshot, afterSnapshot, teardown}` handlers; on every invocation, calls
66
+ * `window[ns][phase]()` if present. The handlers all close over the same set of
67
+ * private helpers and a `WeakMap`-backed per-element state.
68
+ */
69
+ /* istanbul ignore next — runs only inside the browser, not in node */
70
+ function pagePayload(arg) {
71
+ const w = window;
72
+ const ns = arg.ns;
73
+ if (!w[ns])
74
+ install();
75
+ const state = w[ns];
76
+ if (state && typeof state[arg.phase] === 'function')
77
+ state[arg.phase]();
78
+ function install() {
79
+ // ----- private state -----
80
+ const videoUndos = new WeakMap();
81
+ const captionUndos = new WeakMap();
82
+ const playerSnapshots = [];
83
+ const originalProtoPlay = HTMLMediaElement.prototype.play;
84
+ const originalCreateElement = document.createElement.bind(document);
85
+ const originalCreateElementNS = document.createElementNS.bind(document);
86
+ let observer = null;
87
+ // ----- helpers -----
88
+ /** Single DOM traversal helper. Visits every element under `root`, recursing into shadow roots. */
89
+ function walkAll(root, fn) {
90
+ const els = root.querySelectorAll('*');
91
+ for (let i = 0; i < els.length; i++) {
92
+ const el = els[i];
93
+ fn(el);
94
+ const sr = el.shadowRoot;
95
+ if (sr)
96
+ walkAll(sr, fn);
97
+ }
98
+ }
99
+ function walkVideos(root, fn) {
100
+ walkAll(root, el => {
101
+ if (el.tagName === 'VIDEO')
102
+ fn(el);
103
+ });
104
+ }
105
+ function walkCaptions(root, fn) {
106
+ walkAll(root, el => {
107
+ const cl = el.classList;
108
+ if (cl && cl.contains('vjs-text-track-display'))
109
+ fn(el);
110
+ });
111
+ }
112
+ /**
113
+ * Replace an own-property on `obj` with `replacement` and return a closure that
114
+ * puts the original back. Restores correctly whether the original lived on the
115
+ * instance or only on the prototype.
116
+ */
117
+ function overrideOwn(obj, key, replacement) {
118
+ const wasOwn = Object.prototype.hasOwnProperty.call(obj, key);
119
+ const original = wasOwn ? obj[key] : undefined;
120
+ obj[key] = replacement;
121
+ return function restore() {
122
+ if (wasOwn)
123
+ obj[key] = original;
124
+ else
125
+ delete obj[key];
126
+ };
127
+ }
128
+ function runUndos(undos) {
129
+ for (let i = undos.length - 1; i >= 0; i--) {
130
+ try {
131
+ undos[i]();
132
+ }
133
+ catch (_) {
134
+ // best-effort restore: a failure here must not block the rest
135
+ }
136
+ }
137
+ }
138
+ // ----- locking primitives -----
139
+ function lockVideo(video) {
140
+ if (videoUndos.has(video))
141
+ return;
142
+ const undos = [];
143
+ // 1. Mask the `autoplay` IDL attribute so reads return false and writes are dropped.
144
+ const hadAutoplay = video.hasAttribute('autoplay');
145
+ if (hadAutoplay)
146
+ video.removeAttribute('autoplay');
147
+ try {
148
+ Object.defineProperty(video, 'autoplay', {
149
+ configurable: true,
150
+ get: () => false,
151
+ set: () => { },
152
+ });
153
+ }
154
+ catch (_) { }
155
+ undos.push(() => {
156
+ try {
157
+ delete video.autoplay;
158
+ }
159
+ catch (_) { }
160
+ if (hadAutoplay)
161
+ video.setAttribute('autoplay', '');
162
+ });
163
+ // 2. setAttribute('autoplay', ...) — BrightCove uses this directly, bypassing the IDL setter.
164
+ undos.push(overrideOwn(video, 'setAttribute', function (name, value) {
165
+ if (name === 'autoplay')
166
+ return;
167
+ return Element.prototype.setAttribute.call(this, name, value);
168
+ }));
169
+ // 3. Make captureStream() return zero video tracks so dom-snapshot's buildBlobVideos
170
+ // skips its play()/record/pause cycle entirely.
171
+ if (video.captureStream) {
172
+ undos.push(overrideOwn(video, 'captureStream', function () {
173
+ return { getVideoTracks: () => [] };
174
+ }));
175
+ }
176
+ // 4. Disable text tracks (caption rendering source).
177
+ const tracks = video.textTracks;
178
+ const savedModes = [];
179
+ for (let i = 0; i < tracks.length; i++) {
180
+ savedModes.push(tracks[i].mode);
181
+ tracks[i].mode = 'disabled';
182
+ }
183
+ undos.push(() => {
184
+ const t = video.textTracks;
185
+ for (let i = 0; i < t.length && i < savedModes.length; i++)
186
+ t[i].mode = savedModes[i];
187
+ });
188
+ videoUndos.set(video, undos);
189
+ }
190
+ function unlockVideo(video) {
191
+ const undos = videoUndos.get(video);
192
+ if (!undos)
193
+ return;
194
+ videoUndos.delete(video);
195
+ runUndos(undos);
196
+ }
197
+ /**
198
+ * Lock a .vjs-text-track-display caption container. Instance-level overrides because
199
+ * BrightCove's event handlers re-inject cue elements in microtasks between our
200
+ * phase call returning and dom-snapshot serializing the DOM — synchronous, persistent
201
+ * overrides intercept every insertion path regardless of timing.
202
+ */
203
+ function lockCaption(c) {
204
+ if (captionUndos.has(c))
205
+ return;
206
+ const undos = [];
207
+ while (c.firstChild)
208
+ c.removeChild(c.firstChild);
209
+ undos.push(overrideOwn(c, 'appendChild', (n) => n));
210
+ undos.push(overrideOwn(c, 'insertBefore', (n) => n));
211
+ undos.push(overrideOwn(c, 'insertAdjacentHTML', () => { }));
212
+ undos.push(overrideOwn(c, 'insertAdjacentElement', (_pos, n) => n));
213
+ try {
214
+ Object.defineProperty(c, 'innerHTML', {
215
+ configurable: true,
216
+ get: () => '',
217
+ set: () => { },
218
+ });
219
+ undos.push(() => {
220
+ try {
221
+ delete c.innerHTML;
222
+ }
223
+ catch (_) { }
224
+ });
225
+ }
226
+ catch (_) { }
227
+ captionUndos.set(c, undos);
228
+ }
229
+ function unlockCaption(c) {
230
+ const undos = captionUndos.get(c);
231
+ if (!undos)
232
+ return;
233
+ captionUndos.delete(c);
234
+ runUndos(undos);
235
+ }
236
+ // ----- player container snapshot / restore -----
237
+ function snapshotPlayers() {
238
+ walkVideos(document, video => {
239
+ const playerEl = video.closest('video-js, [data-vjs-player], .video-js');
240
+ playerSnapshots.push({
241
+ video,
242
+ paused: video.paused,
243
+ currentTime: video.currentTime,
244
+ playerEl,
245
+ playerClasses: playerEl ? Array.from(playerEl.classList) : [],
246
+ });
247
+ });
248
+ }
249
+ function restorePlayers() {
250
+ for (let i = 0; i < playerSnapshots.length; i++) {
251
+ const s = playerSnapshots[i];
252
+ if (s.paused && !s.video.paused) {
253
+ s.video.pause();
254
+ try {
255
+ s.video.currentTime = s.currentTime;
256
+ }
257
+ catch (_) { } // MSE may reject seeks
258
+ }
259
+ if (s.playerEl) {
260
+ const current = Array.from(s.playerEl.classList);
261
+ for (let j = 0; j < current.length; j++) {
262
+ if (s.playerClasses.indexOf(current[j]) === -1)
263
+ s.playerEl.classList.remove(current[j]);
264
+ }
265
+ for (let j = 0; j < s.playerClasses.length; j++)
266
+ s.playerEl.classList.add(s.playerClasses[j]);
267
+ }
268
+ }
269
+ }
270
+ // ----- global patches (live for the entire loop) -----
271
+ function patchGlobals() {
272
+ // video.js calls HTMLMediaElement.prototype.play.call(el) directly — prototype patch is mandatory.
273
+ // Stays alive through teardown's restorePlayers() so BrightCove's pause-event handler
274
+ // cannot call the (now-live) native play() and restart playback.
275
+ ;
276
+ HTMLMediaElement.prototype.play = function () {
277
+ return Promise.resolve();
278
+ };
279
+ document.createElement = function (tag) {
280
+ const el = originalCreateElement(tag);
281
+ if (typeof tag === 'string' && tag.toLowerCase() === 'video') {
282
+ lockVideo(el);
283
+ }
284
+ return el;
285
+ };
286
+ document.createElementNS = function (nsArg, tag) {
287
+ const el = originalCreateElementNS(nsArg, tag);
288
+ if (typeof tag === 'string' && tag.toLowerCase() === 'video') {
289
+ lockVideo(el);
290
+ }
291
+ return el;
292
+ };
293
+ }
294
+ function unpatchGlobals() {
295
+ ;
296
+ HTMLMediaElement.prototype.play = originalProtoPlay;
297
+ document.createElement = originalCreateElement;
298
+ document.createElementNS = originalCreateElementNS;
299
+ }
300
+ // ----- mutation observer (belt-and-suspenders for late-arriving nodes) -----
301
+ function startObserver() {
302
+ observer = new MutationObserver(mutations => {
303
+ for (let i = 0; i < mutations.length; i++) {
304
+ const added = mutations[i].addedNodes;
305
+ for (let j = 0; j < added.length; j++) {
306
+ const node = added[j];
307
+ if (!(node instanceof Element))
308
+ continue;
309
+ if (node.tagName === 'VIDEO')
310
+ lockVideo(node);
311
+ else if (node.classList && node.classList.contains('vjs-text-track-display'))
312
+ lockCaption(node);
313
+ walkVideos(node, lockVideo);
314
+ walkCaptions(node, lockCaption);
315
+ }
316
+ }
317
+ });
318
+ observer.observe(document, { childList: true, subtree: true });
319
+ }
320
+ function stopObserver() {
321
+ if (observer) {
322
+ observer.disconnect();
323
+ observer = null;
324
+ }
325
+ }
326
+ // ----- bootstrap -----
327
+ // Player state is NOT snapshotted here. It is captured fresh in `beforeSnapshot`
328
+ // so that responsive classes (vjs-layout-small, vjs-fluid, orientation classes)
329
+ // applied during setViewportSize are preserved across afterSnapshot's restore.
330
+ // This also automatically covers videos BrightCove injects mid-loop, since the
331
+ // walk re-runs each iteration.
332
+ patchGlobals();
333
+ walkCaptions(document, lockCaption);
334
+ startObserver();
335
+ // ----- phase handlers (called from TS via runPhase) -----
336
+ w[ns] = {
337
+ // `setup` is already complete by the time we get here; the no-op handler keeps the dispatch shape uniform.
338
+ setup() { },
339
+ beforeResize() {
340
+ walkVideos(document, lockVideo);
341
+ walkCaptions(document, lockCaption);
342
+ },
343
+ beforeSnapshot() {
344
+ // Safety net first — pause videos and disable tracks before snapshotting state,
345
+ // so playback classes (vjs-playing) settle to a consistent paused-state.
346
+ walkVideos(document, video => {
347
+ if (!video.paused)
348
+ video.pause();
349
+ const t = video.textTracks;
350
+ for (let i = 0; i < t.length; i++)
351
+ t[i].mode = 'disabled';
352
+ });
353
+ // Capture fresh per-player state — runs after resize so responsive classes
354
+ // reflect the current breakpoint, and so videos BrightCove injected during
355
+ // the resize are included.
356
+ playerSnapshots.length = 0;
357
+ snapshotPlayers();
358
+ // MutationObserver is async; lock new caption containers synchronously before
359
+ // dom-snapshot's next-macrotask serialization runs.
360
+ walkCaptions(document, lockCaption);
361
+ },
362
+ afterSnapshot() {
363
+ walkVideos(document, unlockVideo);
364
+ restorePlayers();
365
+ // Clear after consume — the captured state is breakpoint-specific and must not
366
+ // leak into the next iteration or teardown, where the viewport differs.
367
+ playerSnapshots.length = 0;
368
+ },
369
+ teardown() {
370
+ // Restore players BEFORE unpatching globals: BrightCove's pause-event handler can call
371
+ // play() during pause(), and we need that call to still hit the no-op prototype patch.
372
+ restorePlayers();
373
+ stopObserver();
374
+ unpatchGlobals();
375
+ walkVideos(document, unlockVideo);
376
+ walkCaptions(document, unlockCaption);
377
+ delete w[ns];
378
+ },
379
+ };
380
+ }
381
+ }
382
+ exports.pagePayload = pagePayload;
@@ -34,6 +34,7 @@ const take_dom_snapshot_1 = require("./take-dom-snapshot");
34
34
  const generate_safe_selectors_1 = require("./generate-safe-selectors");
35
35
  const wait_for_lazy_load_1 = require("../../automation/utils/wait-for-lazy-load");
36
36
  const calculate_required_sizes_1 = require("../../automation/utils/calculate-required-sizes");
37
+ const media_playback_guard_1 = require("./media-playback-guard");
37
38
  const chalk_1 = __importDefault(require("chalk"));
38
39
  const utils = __importStar(require("@applitools/utils"));
39
40
  const lang = __importStar(require("../../lang"));
@@ -85,9 +86,12 @@ async function takeDomSnapshots({ driver, settings, hooks, provides, logger, })
85
86
  context: currentContext,
86
87
  elementReferences: settings.elementReferences,
87
88
  });
89
+ const mediaGuard = (0, media_playback_guard_1.makeMediaPlaybackGuard)({ context: currentContext, logger });
90
+ await mediaGuard.setup();
88
91
  const snapshots = Array(settings.environments.length);
89
92
  for (const [index, { viewportSize, browsers }] of entries.entries()) {
90
93
  logger.log(`Taking dom snapshot for viewport size ${viewportSize}`);
94
+ await mediaGuard.beforeResize();
91
95
  if (viewportSize) {
92
96
  try {
93
97
  await driver.setViewportSize(viewportSize);
@@ -121,10 +125,13 @@ async function takeDomSnapshots({ driver, settings, hooks, provides, logger, })
121
125
  await (0, wait_for_lazy_load_1.waitForLazyLoad)({ context: currentContext, settings: settings.lazyLoad, logger });
122
126
  }
123
127
  await beforeEachSnapshot();
128
+ await mediaGuard.beforeSnapshot();
124
129
  const snapshot = await (0, take_dom_snapshot_1.takeDomSnapshot)({ context: currentContext, settings, logger });
125
130
  browsers.forEach(({ index }) => (snapshots[index] = snapshot));
131
+ await mediaGuard.afterSnapshot();
126
132
  }
127
133
  await driver.setViewportSize(initialViewportSize);
134
+ await mediaGuard.teardown();
128
135
  if (settings.layoutBreakpoints.reload) {
129
136
  await driver.reloadPage();
130
137
  await beforeEachSnapshot();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applitools/core",
3
- "version": "4.62.0",
3
+ "version": "4.63.1",
4
4
  "homepage": "https://applitools.com",
5
5
  "bugs": {
6
6
  "url": "https://github.com/applitools/eyes.sdk.javascript1/issues"
@@ -61,19 +61,19 @@
61
61
  "setup:standalone": "sh -c 'yarn chromedriver --port=4444 --verbose &'"
62
62
  },
63
63
  "dependencies": {
64
- "@applitools/core-base": "1.33.0",
65
- "@applitools/dom-capture": "11.8.0",
66
- "@applitools/dom-snapshot": "4.17.0",
67
- "@applitools/driver": "1.26.0",
68
- "@applitools/ec-client": "1.12.28",
69
- "@applitools/logger": "2.2.11",
70
- "@applitools/nml-client": "1.11.26",
71
- "@applitools/req": "1.10.0",
72
- "@applitools/screenshoter": "3.12.19",
73
- "@applitools/snippets": "2.9.0",
74
- "@applitools/socket": "1.3.12",
75
- "@applitools/ufg-client": "1.22.0",
76
- "@applitools/utils": "1.14.4",
64
+ "@applitools/core-base": "1.34.1",
65
+ "@applitools/dom-capture": "11.8.1",
66
+ "@applitools/dom-snapshot": "4.17.2",
67
+ "@applitools/driver": "1.26.2",
68
+ "@applitools/ec-client": "1.12.30",
69
+ "@applitools/logger": "2.2.12",
70
+ "@applitools/nml-client": "1.11.28",
71
+ "@applitools/req": "1.10.2",
72
+ "@applitools/screenshoter": "3.12.21",
73
+ "@applitools/snippets": "2.9.2",
74
+ "@applitools/socket": "1.3.13",
75
+ "@applitools/ufg-client": "1.22.2",
76
+ "@applitools/utils": "1.14.5",
77
77
  "abort-controller": "3.0.0",
78
78
  "chalk": "4.1.2",
79
79
  "node-fetch": "2.6.7",
@@ -82,23 +82,28 @@
82
82
  },
83
83
  "devDependencies": {
84
84
  "@applitools/bongo": "^5.10.0",
85
- "@applitools/spec-driver-playwright": "^1.9.0",
86
- "@applitools/spec-driver-puppeteer": "^1.8.0",
87
- "@applitools/spec-driver-selenium": "^1.8.0",
88
- "@applitools/test-server": "^1.4.3",
85
+ "@applitools/spec-driver-playwright": "^1.9.2",
86
+ "@applitools/spec-driver-puppeteer": "^1.8.2",
87
+ "@applitools/spec-driver-selenium": "^1.8.2",
88
+ "@applitools/test-server": "^1.4.4",
89
89
  "@applitools/test-utils": "^1.5.17",
90
- "@applitools/tunnel-client": "^1.11.12",
90
+ "@applitools/tunnel-client": "^1.11.14",
91
+ "@swc-node/register": "^1.6.8",
91
92
  "@types/mocha": "^10.0.7",
92
93
  "@types/node": "^12.20.55",
93
94
  "@types/selenium-webdriver": "^4.1.2",
94
95
  "@types/semver": "^7.5.8",
96
+ "chai": "^4.2.0",
95
97
  "chromedriver": "^131.0.5",
96
98
  "crypto": "^1.0.1",
99
+ "express": "^4.10.6",
100
+ "mocha": "^11.7.5",
97
101
  "nock": "^13.3.2",
98
102
  "playwright": "1.47.0",
99
103
  "png-async": "^0.9.4",
100
104
  "puppeteer": "^24.11.1",
101
- "selenium-webdriver": "^4.15.0"
105
+ "selenium-webdriver": "^4.15.0",
106
+ "webdriver": "^5.23.0"
102
107
  },
103
108
  "engines": {
104
109
  "node": ">=12.13.0"
@@ -113,6 +118,7 @@
113
118
  "ws>bufferutil": false,
114
119
  "ws>utf-8-validate": false,
115
120
  "@applitools/core-base>@applitools/image>sharp": false,
121
+ "@swc-node/register>@swc/core": false,
116
122
  "selenium-webdriver>ws>bufferutil": false,
117
123
  "selenium-webdriver>ws>utf-8-validate": false
118
124
  }
@@ -12,6 +12,6 @@ export declare function makeGetNMLClient({ client, logger: mainLogger }: Options
12
12
  settings: Omit<NMLClientSettings, 'brokerUrl'>;
13
13
  logger: Logger;
14
14
  }) => Promise<NMLClient>;
15
- clearNMLClientCache: (driver: Driver<SpecType>) => Promise<void>;
15
+ disableNMLClientCache: (driver: Driver<SpecType>) => Promise<void>;
16
16
  };
17
17
  export {};
@@ -67,7 +67,7 @@ export interface Core<TSpec extends SpecType> extends Omit<BaseCore.Core, 'openE
67
67
  driver: Driver<TSpec>;
68
68
  logger: Logger;
69
69
  }): Promise<NMLClient>;
70
- clearNMLClientCache(driver: Driver<TSpec>): Promise<void>;
70
+ disableNMLClientCache(driver: Driver<TSpec>): Promise<void>;
71
71
  openEyes(options: {
72
72
  target?: DriverTarget<TSpec>;
73
73
  settings: BaseCore.OpenSettings;
@@ -0,0 +1,67 @@
1
+ import { type SpecType, type Context } from '@applitools/driver';
2
+ import { type Logger } from '@applitools/logger';
3
+ /**
4
+ * Prevents media playback side-effects during the layoutBreakpoints snapshot loop.
5
+ *
6
+ * dom-snapshot calls video.play() per breakpoint to capture a video frame. For
7
+ * BrightCove / video.js players this triggers CSS class changes (has-started, is-playing)
8
+ * and subtitle text injection that outlast the corresponding pause() call. With multiple
9
+ * breakpoints the mutations accumulate and corrupt visual snapshots.
10
+ *
11
+ * Lifecycle:
12
+ * const guard = makeMediaPlaybackGuard({context, logger})
13
+ * await guard.setup() // once, before the loop
14
+ * for (const entry of entries) {
15
+ * await guard.beforeResize() // before setViewportSize
16
+ * // setViewportSize / reload / lazyLoad
17
+ * await guard.beforeSnapshot() // after resize, before takeDomSnapshot
18
+ * // takeDomSnapshot
19
+ * await guard.afterSnapshot() // after takeDomSnapshot
20
+ * }
21
+ * // setViewportSize (restore)
22
+ * await guard.teardown() // once, after final viewport restore
23
+ *
24
+ * Implementation notes:
25
+ *
26
+ * The TypeScript layer is just orchestration — `setup` / `beforeResize` / etc. each
27
+ * dispatch a phase name to a single self-contained page-side payload (`pagePayload`
28
+ * below) via `context.execute`. The first call installs a library at `window[ns]`
29
+ * with one handler per lifecycle method; subsequent calls just look up and run
30
+ * `window[ns][phase]()`. Teardown deletes `window[ns]` so a fresh guard starts clean.
31
+ *
32
+ * - `ns` is randomised per guard instance so concurrent guards on the same page
33
+ * (e.g. nested checks) do not collide.
34
+ * - Per-element state lives in a `WeakMap`, not on the elements themselves — no
35
+ * marker properties leak onto the page's DOM.
36
+ * - A single `walkAll(root, fn)` helper handles every Document/ShadowRoot traversal.
37
+ * - A `MutationObserver` covers the gap between phase calls (e.g. videos that
38
+ * BrightCove creates during setViewportSize). The synchronous walks in
39
+ * `beforeSnapshot` remain because the observer fires on a microtask and would
40
+ * otherwise lose the race against dom-snapshot's serialization.
41
+ */
42
+ export declare function makeMediaPlaybackGuard<TSpec extends SpecType>({ context, logger, }: {
43
+ context: Context<TSpec>;
44
+ logger: Logger;
45
+ }): {
46
+ setup: () => Promise<void>;
47
+ beforeResize: () => Promise<void>;
48
+ beforeSnapshot: () => Promise<void>;
49
+ afterSnapshot: () => Promise<void>;
50
+ teardown: () => Promise<void>;
51
+ };
52
+ type Phase = 'setup' | 'beforeResize' | 'beforeSnapshot' | 'afterSnapshot' | 'teardown';
53
+ /**
54
+ * The entire page-side library — a single self-contained function shipped to the
55
+ * browser by `context.execute`. No closure variables from the TypeScript side are
56
+ * visible inside this body.
57
+ *
58
+ * Contract: on first invocation, installs `window[ns]` with `{setup, beforeResize,
59
+ * beforeSnapshot, afterSnapshot, teardown}` handlers; on every invocation, calls
60
+ * `window[ns][phase]()` if present. The handlers all close over the same set of
61
+ * private helpers and a `WeakMap`-backed per-element state.
62
+ */
63
+ export declare function pagePayload(arg: {
64
+ ns: string;
65
+ phase: Phase;
66
+ }): void;
67
+ export {};