Repository navigation
Add Locator.Within and fix frame-relative highlights #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1289,11 +1289,7 @@ Task<IResponse> IPage.GoForwardAsync(PageGoForwardOptions options) | |
| Task<IResponse> IPage.GotoAsync(string url, PageGotoOptions options) | ||
| => GoToAsync(url, options?.WaitUntil ?? default, options?.Timeout, options?.Referer); | ||
|
|
||
| async Task IPage.HideHighlightAsync() | ||
| { | ||
| PageHighlights.Clear(this); | ||
| await EvaluateAsync(ElementStateScript.HideAllHighlightsFunction).ConfigureAwait(false); | ||
| } | ||
| Task IPage.HideHighlightAsync() => PageHighlights.HideAllAsync(this); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win Keep Firefox page-level hiding usable.
🤖 Prompt for AI Agents |
||
|
|
||
| Task IPage.HoverAsync(string selector, PageHoverOptions options) | ||
| => HoverAsync(selector, options?.Position, options?.Modifiers, options?.Force, options?.Timeout, options?.Trial, ActionScrollBridge.FromScrollOption(options?.Scroll), options?.Strict); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -36,10 +36,11 @@ internal static class PageHighlights | |
| /// after a later navigation. | ||
| /// </summary> | ||
| /// <param name="page">The page that owns the overlay.</param> | ||
| /// <param name="frame">The frame the locator resolves relative to.</param> | ||
| /// <param name="locator">The highlighted locator.</param> | ||
| /// <param name="style">Optional extra inline CSS.</param> | ||
| /// <param name="id">Stable overlay id for this locator.</param> | ||
| internal static void Remember(IPage page, ILocator locator, string style, string id) | ||
| internal static void Remember(IPage page, IFrame frame, ILocator locator, string style, string id) | ||
| { | ||
| if (page == null || locator == null || string.IsNullOrEmpty(id)) | ||
| { | ||
|
|
@@ -49,9 +50,10 @@ internal static void Remember(IPage page, ILocator locator, string style, string | |
| Registry registry = _pages.GetOrCreateValue(page); | ||
| lock (registry.Sync) | ||
| { | ||
| RemoveId(registry, id); | ||
| RemoveId(registry, frame, id); | ||
| registry.Entries.Add(new Entry | ||
| { | ||
| Frame = frame, | ||
| Locator = locator, | ||
| Style = style, | ||
| Id = id, | ||
|
|
@@ -69,8 +71,9 @@ internal static void Remember(IPage page, ILocator locator, string style, string | |
| /// Forgets one locator overlay after <c>locator.hideHighlight()</c>. | ||
| /// </summary> | ||
| /// <param name="page">The page that owns the overlay.</param> | ||
| /// <param name="frame">The frame the locator resolves relative to.</param> | ||
| /// <param name="id">Stable overlay id for this locator.</param> | ||
| internal static void Forget(IPage page, string id) | ||
| internal static void Forget(IPage page, IFrame frame, string id) | ||
| { | ||
| if (page == null || string.IsNullOrEmpty(id) || !_pages.TryGetValue(page, out Registry registry)) | ||
| { | ||
|
|
@@ -79,24 +82,36 @@ internal static void Forget(IPage page, string id) | |
|
|
||
| lock (registry.Sync) | ||
| { | ||
| RemoveId(registry, id); | ||
| RemoveId(registry, frame, id); | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Forgets every overlay after <c>page.hideHighlight()</c>. | ||
| /// <c>page.hideHighlight()</c>: forgets every overlay and removes it | ||
| /// from every frame, since locators may highlight in any frame. | ||
| /// </summary> | ||
| /// <param name="page">The page that owns the overlays.</param> | ||
| internal static void Clear(IPage page) | ||
| /// <returns>A task that completes when every frame is cleared.</returns> | ||
| internal static async Task HideAllAsync(IPage page) | ||
| { | ||
| if (page == null || !_pages.TryGetValue(page, out Registry registry)) | ||
| if (_pages.TryGetValue(page, out Registry registry)) | ||
| { | ||
| return; | ||
| lock (registry.Sync) | ||
| { | ||
| registry.Entries.Clear(); | ||
| } | ||
| } | ||
|
|
||
| lock (registry.Sync) | ||
| foreach (IFrame frame in page.Frames) | ||
| { | ||
| registry.Entries.Clear(); | ||
| try | ||
| { | ||
| await frame.EvaluateAsync(ElementStateScript.HideAllHighlightsFunction).ConfigureAwait(false); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win 🔎 Supported by static analysis🏁 Script executed: #!/bin/bash
# Inspect the WebKit frame evaluation path without running repository code.
fd -i 'WebKitFrame.cs' src/PlaywrightNative | while IFS= read -r file; do
ast-grep outline "$file" --match EvaluateAsync --view expanded
rg -n -C 6 'EvaluateAsync|EvaluateSerializedInFrameAsync|WaitForFrameContextAsync' "$file"
doneRepository: hardkoded/playwright-native Length of output: 9492 Avoid waiting on unloaded frames during page-level hiding.
🤖 Prompt for AI Agents |
||
| } | ||
| catch (PlaywrightException) when (frame != page.MainFrame) | ||
| { | ||
| // A child frame may detach or navigate while we clear it. | ||
| } | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -132,11 +147,11 @@ private static async Task ReapplyAsync(IPage page) | |
| } | ||
| } | ||
|
|
||
| private static void RemoveId(Registry registry, string id) | ||
| private static void RemoveId(Registry registry, IFrame frame, string id) | ||
| { | ||
| for (int i = registry.Entries.Count - 1; i >= 0; i--) | ||
| { | ||
| if (string.Equals(registry.Entries[i].Id, id, StringComparison.Ordinal)) | ||
| if (ReferenceEquals(registry.Entries[i].Frame, frame) && string.Equals(registry.Entries[i].Id, id, StringComparison.Ordinal)) | ||
| { | ||
| registry.Entries.RemoveAt(i); | ||
| } | ||
|
|
@@ -154,6 +169,8 @@ private sealed class Registry | |
|
|
||
| private sealed class Entry | ||
| { | ||
| internal IFrame Frame { get; set; } | ||
|
|
||
| internal ILocator Locator { get; set; } | ||
|
|
||
| internal string Style { get; set; } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -67,7 +67,8 @@ static bool NameIsValid(string value) | |
| bool ok = (c >= 'a' && c <= 'z') | ||
| || (c >= 'A' && c <= 'Z') | ||
| || (c >= '0' && c <= '9') | ||
| || c == '_'; | ||
| || c == '_' | ||
| || c == '-'; | ||
|
Comment on lines
+70
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Update the validation error to include hyphens.
Proposed fix- throw new PlaywrightException("selectors.register: Selector engine name may only contain [a-zA-Z0-9_] characters");
+ throw new PlaywrightException("selectors.register: Selector engine name may only contain [a-zA-Z0-9_-] characters");🤖 Prompt for AI Agents |
||
| if (!ok) | ||
| { | ||
| return false; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1053,8 +1053,8 @@ public async Task HideHighlightAsync(float? timeout = default) | |
| { | ||
| _ = timeout; | ||
| string id = ToString(); | ||
| PageHighlights.Forget(Page, id); | ||
| await Page.EvaluateAsync(ElementStateScript.HideHighlightByIdFunction, id).ConfigureAwait(false); | ||
| PageHighlights.Forget(Page, _frame, id); | ||
| await _frame.EvaluateAsync(ElementStateScript.HideHighlightByIdFunction, id).ConfigureAwait(false); | ||
| } | ||
|
|
||
| /// <inheritdoc/> | ||
|
|
@@ -1340,7 +1340,7 @@ internal async Task HighlightInternalAsync(float? timeout, string style) | |
| string tooltip = ToString(); | ||
| string payload = "{\"tooltip\":" + JsonSerializer.Serialize(tooltip) + ",\"style\":" + JsonSerializer.Serialize(style ?? string.Empty) + ",\"id\":" + JsonSerializer.Serialize(tooltip) + "}"; | ||
| await handle.EvaluateAsync<bool>(ElementStateScript.HighlightFunction, payload).ConfigureAwait(false); | ||
| PageHighlights.Remember(Page, this, style, tooltip); | ||
| PageHighlights.Remember(Page, _frame, this, style, tooltip); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift Register and hide overlays in the resolved element’s frame. When a locator enters an iframe through 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| /// <summary> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Add the required
Asyncsuffix.LocatorsShouldNotBeAffectedByGlobalAndStickyRegexFlagsis an async method without theAsyncsuffix. Rename it toLocatorsShouldNotBeAffectedByGlobalAndStickyRegexFlagsAsync. As per coding guidelines, “VSTHRD200 (error) | Async methods must haveAsyncsuffix.” (microsoft.github.io)🤖 Prompt for AI Agents
Source: Coding guidelines