fix(reader): expose the reading scroller and preserve native focus
Name and focus the original document scroll region for real browser UIA scrolling. Defer duplicate-reader activation until menus finish closing. Use visible menu peers and preserve Settings focus during UIA invocation. Retain exact font-growth, PDF width/edge, source-color and palette assertions. Give the font fixture a clear rounding margin and center its narrow print body; sample the real empty reading gutter. Record actual failure values. Validation: zero-warning Release build;586 core tests and100% measured line coverage; native84/97 on the prior head;75 real pipe stress tests pass. Current hosted/published/installed and production signatures remain pending. Co-Authored-By: GPT-6 <noreply@openai.com> Signed-off-by: James Maes <jmaes@dmdbrands.com>
This commit is contained in:
11 files changed
+73
-35
No files matched your search
@@ -56,6 +56,8 @@ jobs:
|
||||
artifacts/reader-regressions/*.trx
|
||||
artifacts/installed-smoke-ui/*.png
|
||||
artifacts/installed-smoke-ui/*.log
|
||||
artifacts/installed-smoke-print/*.pdf
|
||||
artifacts/installed-smoke-print/*.png
|
||||
retention-days: 7
|
||||
|
||||
installed-updates:
|
||||
|
||||
+33
-23
@@ -4,31 +4,41 @@ Updated: 2026-10-11
|
||||
|
||||
## Current release verification - 2026-10-11
|
||||
|
||||
Signed/DCO0ed608e is pushed to PR#3. Exact CI38166230860 is terminal:
|
||||
source-hover regression passes (actual native pointer/focus/selection colors,
|
||||
TRX in artifacts/source-hover-38166230860); full native74/97,23failures,
|
||||
zero skips. Build/core/format pass. Published acceptance again exceeded its
|
||||
600second process deadline before installed channel outcomes; no signed release
|
||||
or current installed acceptance is established. Retained exact job logs:
|
||||
artifacts/current-native-0ed608e.log and artifacts/current-installed-0ed608e.log.
|
||||
Signed/DCO05bf612 is pushed to PR#3. Exact CI38167591076 full native run
|
||||
completed84/97,13failures,zero skips; previous0ed608e was74/97. Current
|
||||
focused12-case TRX is retained in artifacts/reader-regressions-38167591076:
|
||||
Settings panel defaults, toolbar alignment, picker cancellation, custom theme
|
||||
paint, source theme/mode and exact copying pass. Source-hover passes the full
|
||||
run but has a focused initial-color timeout before pointer input; retain the
|
||||
variant instead of calling all highlighting accepted. Build/core/format pass.
|
||||
Published/installed job114553267448 is terminal: published dotnet tests again
|
||||
exceeded600seconds before any installed outcomes; retain
|
||||
artifacts/current-installed-05bf612.log. No deadline was increased.
|
||||
Exact full-native log: artifacts/current-native-05bf612.log.
|
||||
|
||||
Next reviewed patch corrects the observed Open picker filename selector1148
|
||||
(control1001 is its address toolbar), checks the actual outline-depth peer,
|
||||
and aligns Contents/Print48px hit targets with the other toolbar commands.
|
||||
Full-page printing now resets custom outer body width as well as inner width.
|
||||
Document paint checks sample the owned browser instead of a split divider;
|
||||
custom CSS fixture targets the visible body layer of the original stylesheet.
|
||||
Unchanged native pane-width/scroll/glyph assertions now retain precise failed
|
||||
geometry/provider evidence; no cases are skipped or relaxed. The focused hosted
|
||||
job covers the actual failing paths, with full acceptance still required.
|
||||
Next patch gives the original #content div a named, focusable main landmark so
|
||||
Chromium exposes the actual inner scroll surface. The prior root reports Scroll
|
||||
support but no vertical extent, while the unnamed inner container is omitted.
|
||||
Native menu lookup now selects visible owned peers and dismisses leaf View
|
||||
menus; UIA Invoke no longer forces reader foreground over Settings/menu owners.
|
||||
Recent activation defers until the invoking menu finishes restoring focus.
|
||||
|
||||
Local Release build is zero-warning;586 core tests and100% measured line
|
||||
coverage pass in artifacts/reader-fixes-core-verified. Formatting remains a
|
||||
required final gate. Owner preview PID55472 remains responsive and untouched.
|
||||
Azure access is unavailable; exact repository-specific immutable OIDC trust,
|
||||
resource verification and profile role commands are committed in
|
||||
[azure-signing-admin.md](azure-signing-admin.md). No Azure mutations, human
|
||||
review acknowledgement, merges or release publication have been fabricated.
|
||||
Existing exact font/pixel/print assertions remain: typography fixture requests
|
||||
28px body because24px produces20→28px glyph heights, exactly40%, rejected by its
|
||||
strict greater-than40% comparison. The narrow print fixture explicitly centers
|
||||
its custom body before testing the Full-page margin/width reset. Palette checks
|
||||
sample the owned empty8px reading gutter rather than a glyph/find mark; no
|
||||
color expectation is changed. Source color/picker failures retain actual values.
|
||||
No test is skipped or weakened; actual hosted behavior must prove these changes.
|
||||
|
||||
Local zero-warning Release build,586 core tests/100% measured lines pass in
|
||||
artifacts/reader-accessibility-core. Real pipe stress25runs/75tests passes,
|
||||
zero skips; the older intermittent disconnect failure did not reproduce, and
|
||||
no speculative pipe change was made. Formatting passes.
|
||||
Owner preview55472 stays responsive and preserved. Azure access unavailable;
|
||||
[azure-signing-admin.md](azure-signing-admin.md) provides exact immutable OIDC,
|
||||
resource/profile verification and profile-role commands. No signing/release,
|
||||
review acknowledgement, Azure mutation or merge is claimed.
|
||||
|
||||
## Current owner review launch - 2026-10-11
|
||||
|
||||
|
||||
@@ -20,6 +20,6 @@ public static class DocumentPage
|
||||
ArgumentNullException.ThrowIfNull(title);
|
||||
ArgumentNullException.ThrowIfNull(theme);
|
||||
ArgumentNullException.ThrowIfNull(appearance);
|
||||
return $"""<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>{WebUtility.HtmlEncode(title)}</title><style media="screen">{theme.GetReadingStyles(appearance, zoomScale)}</style></head><body><div id="content"><div id="content-inner">{sanitizedHtml}</div></div></body></html>""";
|
||||
return $"""<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>{WebUtility.HtmlEncode(title)}</title><style media="screen">{theme.GetReadingStyles(appearance, zoomScale)}</style></head><body><div id="content" role="main" aria-label="Document reading area" tabindex="0"><div id="content-inner">{sanitizedHtml}</div></div></body></html>""";
|
||||
}
|
||||
}
|
||||
@@ -66,7 +66,11 @@ public partial class App
|
||||
MainWindow? existing = _windows.FirstOrDefault(window => string.Equals(window.DocumentPath, canonical, StringComparison.OrdinalIgnoreCase));
|
||||
if(existing is not null)
|
||||
{
|
||||
existing.Activate();
|
||||
// The invoking menu must finish restoring focus before the existing reader activates.
|
||||
if(!existing.DispatcherQueue.TryEnqueue(() => existing.Activate()))
|
||||
{
|
||||
existing.Activate();
|
||||
}
|
||||
}
|
||||
else if(requester.DocumentPath is null)
|
||||
{
|
||||
|
||||
@@ -213,9 +213,16 @@ public sealed class AppSession : IDisposable
|
||||
Invoke(menu);
|
||||
}
|
||||
Wait.UntilInputIsProcessed();
|
||||
item = Retry.WhileNull(() => FindInApp(cf => cf.ByAutomationId(itemAutomationId)), timeout: TimeSpan.FromSeconds(3), interval: s_pollInterval, ignoreException: true).Result;
|
||||
item = Retry.WhileNull(
|
||||
() => TopLevelWindows().Prepend(MainWindow).SelectMany(root => root.FindAllDescendants(cf => cf.ByAutomationId(itemAutomationId))).FirstOrDefault(candidate => !candidate.IsOffscreen),
|
||||
timeout: TimeSpan.FromSeconds(3), interval: s_pollInterval, ignoreException: true).Result;
|
||||
}
|
||||
Invoke(item ?? throw new TimeoutException($"Menu item '{itemAutomationId}' never appeared under '{menuName}'. (app exited: {App.HasExited})"));
|
||||
if(string.Equals(menuName, "View", StringComparison.Ordinal) && !item.Patterns.ExpandCollapse.IsSupported)
|
||||
{
|
||||
menu.Patterns.ExpandCollapse.Pattern.Collapse();
|
||||
Wait.UntilInputIsProcessed();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -323,7 +330,6 @@ public sealed class AppSession : IDisposable
|
||||
/// <summary>Invokes an element through UI Automation, falling back to a mouse click.</summary>
|
||||
private void Invoke(AutomationElement element)
|
||||
{
|
||||
MainWindow.SetForeground();
|
||||
if(element.Patterns.Invoke.TryGetPattern(out IInvokePattern? invoke))
|
||||
{
|
||||
LastInvocation = $"UIA Invoke: {element.Properties.AutomationId.ValueOrDefault}.";
|
||||
|
||||
@@ -120,7 +120,7 @@ public partial class AppTests
|
||||
|
||||
|
||||
|
||||
/// <summary>Samples only the explicitly foreground owned WebView surface, excluding native source panes and other readers.</summary>
|
||||
/// <summary>Samples the foreground owned reading gutter, excluding native panes, glyphs and active find highlights.</summary>
|
||||
private static void WaitForAppearanceWindowColor(AppSession session, Window window, Color expected)
|
||||
{
|
||||
window.SetForeground();
|
||||
@@ -137,7 +137,7 @@ public partial class AppTests
|
||||
return null;
|
||||
}
|
||||
using Bitmap image = view.Capture();
|
||||
return image.GetPixel(image.Width / 2, image.Height / 2).ToArgb() == expected.ToArgb() ? view : null;
|
||||
return image.GetPixel(Math.Min(8, image.Width - 1), Math.Min(8, image.Height - 1)).ToArgb() == expected.ToArgb() ? view : null;
|
||||
}, "actual owned reader background " + expected);
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,14 @@ public partial class AppTests
|
||||
SendReadingKey(VirtualKeyShort.CONTROL, VirtualKeyShort.KEY_A);
|
||||
Keyboard.Type(path);
|
||||
Wait.UntilInputIsProcessed();
|
||||
session.WaitFor(() => string.Equals(filename.AsTextBox().Text, path, StringComparison.Ordinal) ? filename : null, "the fully committed exact owned picker filename");
|
||||
try
|
||||
{
|
||||
session.WaitFor(() => string.Equals(filename.AsTextBox().Text, path, StringComparison.Ordinal) ? filename : null, "the fully committed exact owned picker filename");
|
||||
}
|
||||
catch(TimeoutException exception)
|
||||
{
|
||||
throw new TimeoutException(exception.Message + "; Expected=" + path + "; Actual=" + filename.AsTextBox().Text + "; Last invocation=" + session.LastInvocation, exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public partial class AppTests
|
||||
session.WaitFor(() => session.FindInApp(cf => cf.ByAutomationId("mv-heading-0")), "the print width heading", TimeSpan.FromSeconds(30));
|
||||
OpenSettingsAction(session, "EditDocumentThemeMenu");
|
||||
session.EnterText("ThemeEditorName", "Print geometry");
|
||||
session.EnterText("ThemeEditorCss", "body { max-width: 240px; } pre { border: none; border-radius: 0; }");
|
||||
session.EnterText("ThemeEditorCss", "body { max-width: 240px; margin: 0 auto; } pre { border: none; border-radius: 0; }");
|
||||
session.ClickButton("Save theme");
|
||||
session.WaitForElementGone("ThemeEditorName");
|
||||
CloseSettings(session);
|
||||
|
||||
@@ -110,7 +110,15 @@ public partial class AppTests
|
||||
int green = int.Parse(expected.AsSpan(3, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
int blue = int.Parse(expected.AsSpan(5, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
|
||||
int color = red | green << 8 | blue << 16;
|
||||
session.WaitFor(() => NativeSourceAttribute(SourceRange(source, literal), session.Automation.TextAttributeLibrary.ForegroundColor) == color ? source : null, "the actual native source color of " + literal);
|
||||
try
|
||||
{
|
||||
session.WaitFor(() => NativeSourceAttribute(SourceRange(source, literal), session.Automation.TextAttributeLibrary.ForegroundColor) == color ? source : null, "the actual native source color of " + literal);
|
||||
}
|
||||
catch(TimeoutException exception)
|
||||
{
|
||||
string actual = NativeSourceColorHex(SourceRange(source, literal), session);
|
||||
throw new TimeoutException(exception.Message + "; Expected=" + expected + "; Actual=" + actual + "; Last native invocation=" + session.LastInvocation + "; Native tree: " + session.DescribeDocumentStatus(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,9 +29,10 @@ public partial class AppTests
|
||||
{
|
||||
session.EnterText(id, "Consolas");
|
||||
}
|
||||
// Give native rounded glyph metrics a clear growth margin while retaining the40% assertion.
|
||||
var sizes = new Dictionary<string, (string Id, double Value)>(StringComparer.Ordinal)
|
||||
{
|
||||
["baseFontSize"] = ("ThemeEditorBodySize", 24),
|
||||
["baseFontSize"] = ("ThemeEditorBodySize", 28),
|
||||
["h1Size"] = ("ThemeEditorHeading1Size", 56),
|
||||
["h2Size"] = ("ThemeEditorHeading2Size", 48),
|
||||
["h3Size"] = ("ThemeEditorHeading3Size", 44),
|
||||
|
||||
@@ -1344,7 +1344,7 @@ public partial class AppTests
|
||||
|
||||
|
||||
|
||||
/// <summary>Waits for the actual painted document palette rather than just a saved preference or old heading.</summary>
|
||||
/// <summary>Samples the actual owned browser reading gutter, excluding glyphs, find marks and native dividers.</summary>
|
||||
private static void WaitForDocumentColor(AppSession session, System.Drawing.Color expected)
|
||||
{
|
||||
session.WaitFor(
|
||||
@@ -1352,7 +1352,7 @@ public partial class AppTests
|
||||
{
|
||||
AutomationElement view = session.WaitForElement("DocumentView");
|
||||
using System.Drawing.Bitmap image = view.Capture();
|
||||
return image.GetPixel(image.Width / 2, image.Height / 2).ToArgb() == expected.ToArgb() ? view : null;
|
||||
return image.GetPixel(Math.Min(8, image.Width - 1), Math.Min(8, image.Height - 1)).ToArgb() == expected.ToArgb() ? view : null;
|
||||
},
|
||||
"the rendered document background " + expected);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user