playwright-core 1.54.0-alpha-2025-06-24 → 1.54.0-alpha-2025-06-25

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.
@@ -1657,6 +1657,12 @@ import_validatorPrimitives.scheme.FrameFrameElementParams = (0, import_validator
1657
1657
  import_validatorPrimitives.scheme.FrameFrameElementResult = (0, import_validatorPrimitives.tObject)({
1658
1658
  element: (0, import_validatorPrimitives.tChannel)(["ElementHandle"])
1659
1659
  });
1660
+ import_validatorPrimitives.scheme.FrameGenerateLocatorStringParams = (0, import_validatorPrimitives.tObject)({
1661
+ selector: import_validatorPrimitives.tString
1662
+ });
1663
+ import_validatorPrimitives.scheme.FrameGenerateLocatorStringResult = (0, import_validatorPrimitives.tObject)({
1664
+ value: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString)
1665
+ });
1660
1666
  import_validatorPrimitives.scheme.FrameHighlightParams = (0, import_validatorPrimitives.tObject)({
1661
1667
  selector: import_validatorPrimitives.tString
1662
1668
  });
@@ -2050,10 +2056,6 @@ import_validatorPrimitives.scheme.ElementHandleFillParams = (0, import_validator
2050
2056
  import_validatorPrimitives.scheme.ElementHandleFillResult = (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({}));
2051
2057
  import_validatorPrimitives.scheme.ElementHandleFocusParams = (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({}));
2052
2058
  import_validatorPrimitives.scheme.ElementHandleFocusResult = (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({}));
2053
- import_validatorPrimitives.scheme.ElementHandleGenerateLocatorStringParams = (0, import_validatorPrimitives.tOptional)((0, import_validatorPrimitives.tObject)({}));
2054
- import_validatorPrimitives.scheme.ElementHandleGenerateLocatorStringResult = (0, import_validatorPrimitives.tObject)({
2055
- value: (0, import_validatorPrimitives.tOptional)(import_validatorPrimitives.tString)
2056
- });
2057
2059
  import_validatorPrimitives.scheme.ElementHandleGetAttributeParams = (0, import_validatorPrimitives.tObject)({
2058
2060
  name: import_validatorPrimitives.tString
2059
2061
  });
@@ -55,9 +55,6 @@ class ElementHandleDispatcher extends import_jsHandleDispatcher.JSHandleDispatch
55
55
  const frame = await this._elementHandle.contentFrame();
56
56
  return { frame: frame ? import_frameDispatcher.FrameDispatcher.from(this._browserContextDispatcher(), frame) : void 0 };
57
57
  }
58
- async generateLocatorString(params, metadata) {
59
- return { value: await this._elementHandle.generateLocatorString() };
60
- }
61
58
  async getAttribute(params, metadata) {
62
59
  const value = await this._elementHandle.getAttribute(metadata, params.name);
63
60
  return { value: value === null ? void 0 : value };
@@ -144,6 +144,9 @@ class FrameDispatcher extends import_dispatcher.Dispatcher {
144
144
  async innerHTML(params, metadata) {
145
145
  return { value: await this._frame.innerHTML(metadata, params.selector, params) };
146
146
  }
147
+ async generateLocatorString(params, metadata) {
148
+ return { value: await this._frame.generateLocatorString(metadata, params.selector) };
149
+ }
147
150
  async getAttribute(params, metadata) {
148
151
  const value = await this._frame.getAttribute(metadata, params.selector, params.name, params);
149
152
  return { value: value === null ? void 0 : value };
package/lib/server/dom.js CHANGED
@@ -161,35 +161,6 @@ class ElementHandle extends js.JSHandle {
161
161
  return null;
162
162
  return this._page.delegate.getContentFrame(this);
163
163
  }
164
- async generateLocatorString() {
165
- const selectors = await this._generateSelectorString();
166
- if (!selectors.length)
167
- return;
168
- return (0, import_utils.asLocator)("javascript", selectors.reverse().join(" >> internal:control=enter-frame >> "));
169
- }
170
- async _generateSelectorString() {
171
- const selector = await this.evaluateInUtility(async ([injected, node]) => {
172
- return injected.generateSelectorSimple(node);
173
- }, {});
174
- if (selector === "error:notconnected")
175
- return [];
176
- let frame = this._frame;
177
- const result = [selector];
178
- while (frame?.parentFrame()) {
179
- const frameElement = await frame.frameElement();
180
- if (frameElement) {
181
- const selector2 = await frameElement.evaluateInUtility(async ([injected, node]) => {
182
- return injected.generateSelectorSimple(node);
183
- }, {});
184
- frameElement.dispose();
185
- if (selector2 === "error:notconnected")
186
- return [];
187
- result.push(selector2);
188
- }
189
- frame = frame.parentFrame();
190
- }
191
- return result;
192
- }
193
164
  async getAttribute(metadata, name) {
194
165
  return this._frame.getAttribute(metadata, ":scope", name, { timeout: 0 }, this);
195
166
  }
@@ -321,6 +292,10 @@ class ElementHandle extends js.JSHandle {
321
292
  progress.log(" did not find some options");
322
293
  continue;
323
294
  }
295
+ if (result === "error:optionnotenabled") {
296
+ progress.log(" option being selected is not enabled");
297
+ continue;
298
+ }
324
299
  if (typeof result === "object" && "hitTargetDescription" in result) {
325
300
  progress.log(` ${result.hitTargetDescription} intercepts pointer events`);
326
301
  continue;
@@ -1015,6 +1015,35 @@ class Frame extends import_instrumentation.SdkObject {
1015
1015
  dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, options.strict, true, (handle) => handle._blur(progress)));
1016
1016
  }, options.timeout);
1017
1017
  }
1018
+ async generateLocatorString(metadata, selector) {
1019
+ const controller = new import_progress.ProgressController(metadata, this);
1020
+ return controller.run(async (progress) => {
1021
+ const element = await progress.race(this.selectors.query(selector));
1022
+ if (!element)
1023
+ throw new Error(`No element matching ${this._asLocator(selector)}`);
1024
+ const generated = await progress.race(element.evaluateInUtility(async ([injected, node]) => {
1025
+ return injected.generateSelectorSimple(node);
1026
+ }, {}));
1027
+ if (!generated)
1028
+ throw new Error(`Unable to generate locator for ${this._asLocator(selector)}`);
1029
+ let frame = element._frame;
1030
+ const result = [generated];
1031
+ while (frame?.parentFrame()) {
1032
+ const frameElement = await progress.race(frame.frameElement());
1033
+ if (frameElement) {
1034
+ const generated2 = await progress.race(frameElement.evaluateInUtility(async ([injected, node]) => {
1035
+ return injected.generateSelectorSimple(node);
1036
+ }, {}));
1037
+ frameElement.dispose();
1038
+ if (generated2 === "error:notconnected" || !generated2)
1039
+ throw new Error(`Unable to generate locator for ${this._asLocator(selector)}`);
1040
+ result.push(generated2);
1041
+ }
1042
+ frame = frame.parentFrame();
1043
+ }
1044
+ return (0, import_utils.asLocator)(this._page.browserContext._browser.sdkLanguage(), result.reverse().join(" >> internal:control=enter-frame >> "));
1045
+ });
1046
+ }
1018
1047
  async textContent(metadata, selector, options, scope) {
1019
1048
  return this._callOnElementOnceMatches(metadata, selector, (injected, element) => element.textContent, void 0, options, scope);
1020
1049
  }
@@ -1162,43 +1191,32 @@ class Frame extends import_instrumentation.SdkObject {
1162
1191
  return result;
1163
1192
  }
1164
1193
  async _expectImpl(metadata, selector, options) {
1194
+ const controller = new import_progress.ProgressController(metadata, this);
1165
1195
  const lastIntermediateResult = { isSet: false };
1166
- try {
1167
- let timeout = options.timeout;
1168
- const start = timeout > 0 ? (0, import_utils.monotonicTime)() : 0;
1169
- await new import_progress.ProgressController(metadata, this).run(async (progress) => {
1170
- progress.log(`${(0, import_utils.renderTitleForCall)(metadata)}${timeout ? ` with timeout ${timeout}ms` : ""}`);
1171
- if (selector)
1172
- progress.log(`waiting for ${this._asLocator(selector)}`);
1173
- await this._page.performActionPreChecks(progress);
1174
- }, timeout);
1196
+ return await controller.run(async (progress) => {
1197
+ progress.log(`${(0, import_utils.renderTitleForCall)(metadata)}${options.timeout ? ` with timeout ${options.timeout}ms` : ""}`);
1198
+ if (selector)
1199
+ progress.log(`waiting for ${this._asLocator(selector)}`);
1200
+ await this._page.performActionPreChecks(progress);
1201
+ progress.legacyDisableTimeout();
1175
1202
  try {
1176
- const resultOneShot = await new import_progress.ProgressController(metadata, this).run(async (progress) => {
1177
- return await this._expectInternal(progress, selector, options, lastIntermediateResult);
1178
- });
1203
+ const resultOneShot = await this._expectInternal(progress, selector, options, lastIntermediateResult, true);
1179
1204
  if (resultOneShot.matches !== options.isNot)
1180
1205
  return resultOneShot;
1181
1206
  } catch (e) {
1182
1207
  if (this.isNonRetriableError(e))
1183
1208
  throw e;
1184
1209
  }
1185
- if (timeout > 0) {
1186
- const elapsed = (0, import_utils.monotonicTime)() - start;
1187
- timeout -= elapsed;
1188
- }
1189
- if (timeout < 0)
1190
- return { matches: options.isNot, log: (0, import_callLog.compressCallLog)(metadata.log), timedOut: true, received: lastIntermediateResult.received };
1191
- return await new import_progress.ProgressController(metadata, this).run(async (progress) => {
1192
- return await this.retryWithProgressAndTimeouts(progress, [100, 250, 500, 1e3], async (continuePolling) => {
1193
- await this._page.performActionPreChecks(progress);
1194
- const { matches, received } = await this._expectInternal(progress, selector, options, lastIntermediateResult);
1195
- if (matches === options.isNot) {
1196
- return continuePolling;
1197
- }
1198
- return { matches, received };
1199
- });
1200
- }, timeout);
1201
- } catch (e) {
1210
+ progress.legacyEnableTimeout();
1211
+ return await this.retryWithProgressAndTimeouts(progress, [100, 250, 500, 1e3], async (continuePolling) => {
1212
+ await this._page.performActionPreChecks(progress);
1213
+ const { matches, received } = await this._expectInternal(progress, selector, options, lastIntermediateResult, false);
1214
+ if (matches === options.isNot) {
1215
+ return continuePolling;
1216
+ }
1217
+ return { matches, received };
1218
+ });
1219
+ }, options.timeout).catch((e) => {
1202
1220
  if (js.isJavaScriptErrorInEvaluate(e) || (0, import_selectorParser.isInvalidSelectorError)(e))
1203
1221
  throw e;
1204
1222
  const result = { matches: options.isNot, log: (0, import_callLog.compressCallLog)(metadata.log) };
@@ -1207,15 +1225,16 @@ class Frame extends import_instrumentation.SdkObject {
1207
1225
  if (e instanceof import_errors.TimeoutError)
1208
1226
  result.timedOut = true;
1209
1227
  return result;
1210
- }
1228
+ });
1211
1229
  }
1212
- async _expectInternal(progress, selector, options, lastIntermediateResult) {
1213
- const selectorInFrame = selector ? await progress.race(this.selectors.resolveFrameForSelector(selector, { strict: true })) : void 0;
1230
+ async _expectInternal(progress, selector, options, lastIntermediateResult, noAbort) {
1231
+ const race = (p) => noAbort ? p : progress.race(p);
1232
+ const selectorInFrame = selector ? await race(this.selectors.resolveFrameForSelector(selector, { strict: true })) : void 0;
1214
1233
  const { frame, info } = selectorInFrame || { frame: this, info: void 0 };
1215
1234
  const world = options.expression === "to.have.property" ? "main" : info?.world ?? "utility";
1216
- const context = await progress.race(frame._context(world));
1217
- const injected = await progress.race(context.injectedScript());
1218
- const { log, matches, received, missingReceived } = await progress.race(injected.evaluate(async (injected2, { info: info2, options: options2, callId }) => {
1235
+ const context = await race(frame._context(world));
1236
+ const injected = await race(context.injectedScript());
1237
+ const { log, matches, received, missingReceived } = await race(injected.evaluate(async (injected2, { info: info2, options: options2, callId }) => {
1219
1238
  const elements = info2 ? injected2.querySelectorAll(info2.parsed, document) : [];
1220
1239
  if (callId)
1221
1240
  injected2.markTargetElements(new Set(elements), callId);
@@ -61,6 +61,24 @@ class ProgressController {
61
61
  (0, import_utils.assert)(this._state === "before");
62
62
  this._state = "running";
63
63
  this.sdkObject.attribution.context?._activeProgressControllers.add(this);
64
+ const deadline = timeout ? Math.min((0, import_utils.monotonicTime)() + timeout, 2147483647) : 0;
65
+ const timeoutError = new import_errors.TimeoutError(`Timeout ${timeout}ms exceeded.`);
66
+ let timer;
67
+ const startTimer = () => {
68
+ if (!deadline)
69
+ return;
70
+ const onTimeout = () => {
71
+ if (this._state === "running") {
72
+ this._state = { error: timeoutError };
73
+ this._forceAbortPromise.reject(timeoutError);
74
+ }
75
+ };
76
+ const remaining = deadline - (0, import_utils.monotonicTime)();
77
+ if (remaining <= 0)
78
+ onTimeout();
79
+ else
80
+ timer = setTimeout(onTimeout, remaining);
81
+ };
64
82
  const progress = {
65
83
  log: (message) => {
66
84
  if (this._state === "running")
@@ -94,18 +112,19 @@ class ProgressController {
94
112
  let timer2;
95
113
  const promise = new Promise((f) => timer2 = setTimeout(f, timeout2));
96
114
  return progress.race(promise).finally(() => clearTimeout(timer2));
115
+ },
116
+ legacyDisableTimeout: () => {
117
+ if (this._strictMode)
118
+ return;
119
+ clearTimeout(timer);
120
+ },
121
+ legacyEnableTimeout: () => {
122
+ if (this._strictMode)
123
+ return;
124
+ startTimer();
97
125
  }
98
126
  };
99
- let timer;
100
- if (timeout) {
101
- const timeoutError = new import_errors.TimeoutError(`Timeout ${timeout}ms exceeded.`);
102
- timer = setTimeout(() => {
103
- if (this._state === "running") {
104
- this._state = { error: timeoutError };
105
- this._forceAbortPromise.reject(timeoutError);
106
- }
107
- }, Math.min(timeout, 2147483647));
108
- }
127
+ startTimer();
109
128
  try {
110
129
  const promise = task(progress);
111
130
  const result = this._strictMode ? await promise : await Promise.race([promise, this._forceAbortPromise]);
@@ -159,6 +159,7 @@ const methodMetainfo = /* @__PURE__ */ new Map([
159
159
  ["Frame.fill", { title: 'Fill "{value}"', slowMo: true, snapshot: true, pausesBeforeInput: true }],
160
160
  ["Frame.focus", { title: "Focus", slowMo: true, snapshot: true }],
161
161
  ["Frame.frameElement", { internal: true }],
162
+ ["Frame.generateLocatorString", { internal: true }],
162
163
  ["Frame.highlight", { internal: true }],
163
164
  ["Frame.getAttribute", { internal: true, snapshot: true }],
164
165
  ["Frame.goto", { title: 'Navigate to "{url}"', slowMo: true, snapshot: true }],
@@ -212,7 +213,6 @@ const methodMetainfo = /* @__PURE__ */ new Map([
212
213
  ["ElementHandle.dispatchEvent", { title: "Dispatch event", slowMo: true, snapshot: true }],
213
214
  ["ElementHandle.fill", { title: 'Fill "{value}"', slowMo: true, snapshot: true, pausesBeforeInput: true }],
214
215
  ["ElementHandle.focus", { title: "Focus", slowMo: true, snapshot: true }],
215
- ["ElementHandle.generateLocatorString", { internal: true }],
216
216
  ["ElementHandle.getAttribute", { internal: true }],
217
217
  ["ElementHandle.hover", { title: "Hover", slowMo: true, snapshot: true, pausesBeforeInput: true }],
218
218
  ["ElementHandle.innerHTML", { title: "Get HTML", snapshot: true }],