From 79e32d9ea591ccf991d2b1ecf773362607852fae Mon Sep 17 00:00:00 2001 From: Ngo Quoc Dat Date: Wed, 23 Sep 2026 12:46:06 +0700 Subject: [PATCH 1/2] test(hig): accept a menu named through the accessibility element that contains it --- .../Views/MenuDisclosureIndicatorTests.swift | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/TableProTests/Views/MenuDisclosureIndicatorTests.swift b/TableProTests/Views/MenuDisclosureIndicatorTests.swift index 9fb485cfcb..5c3c1acf0b 100644 --- a/TableProTests/Views/MenuDisclosureIndicatorTests.swift +++ b/TableProTests/Views/MenuDisclosureIndicatorTests.swift @@ -37,6 +37,11 @@ struct MenuDisclosureIndicatorTests { /// The modifier replaces what the label was providing, with nothing. Four controls in this app /// carried it and were silent to VoiceOver because of it, the result-set chooser among them, /// which is why two suites asserting on its name could never have passed. + /// + /// `.accessibilityElement(children: .contain)` ahead of the label is a different construction: + /// the label then names the container that modifier creates rather than the menu, and that is + /// the one form that names a pull-down whose label draws only an icon. The trailing pane's + /// ellipsis menu relies on it, and `TrailingPaneSurfaceUITests` reads its name on CI. @Test("No menu names itself with accessibilityLabel") func menusCarryTheirNameInTheirLabel() throws { let viewsRoot = Self.repositoryRoot.appendingPathComponent("TablePro/Views") @@ -47,7 +52,9 @@ struct MenuDisclosureIndicatorTests { var inspected = 0 var offenders: [String] = [] for case let url as URL in enumerator where url.pathExtension == "swift" { - let result = Self.scanForMisplacedNames(url, root: Self.repositoryRoot) + guard let source = try? String(contentsOf: url, encoding: .utf8) else { continue } + let relativePath = url.path.replacingOccurrences(of: Self.repositoryRoot.path + "/", with: "") + let result = Self.scanForMisplacedNames(source, relativePath: relativePath) inspected += result.inspected offenders += result.offenders } @@ -59,10 +66,37 @@ struct MenuDisclosureIndicatorTests { ) } - private static func scanForMisplacedNames(_ url: URL, root: URL) -> (inspected: Int, offenders: [String]) { - guard let source = try? String(contentsOf: url, encoding: .utf8) else { return (0, []) } + @Test("A label on the menu itself is flagged, a label on a container wrapping it is not") + func scannerTellsTheMenuFromItsContainer() { + let namedMenu = """ + Menu { + Button("Fields") {} + } label: { + Image(systemName: "ellipsis") + } + .menuStyle(.button) + .accessibilityLabel("Options") + """ + let namedContainer = """ + Menu { + Button("Fields") {} + } label: { + Image(systemName: "ellipsis") + } + .menuStyle(.button) + .accessibilityElement(children: .contain) + .accessibilityLabel("Options") + """ + + #expect(Self.scanForMisplacedNames(namedMenu, relativePath: "Menu.swift").offenders == ["Menu.swift:1"]) + #expect(Self.scanForMisplacedNames(namedContainer, relativePath: "Menu.swift").offenders.isEmpty) + } + + private static func scanForMisplacedNames( + _ source: String, + relativePath: String + ) -> (inspected: Int, offenders: [String]) { let lines = source.components(separatedBy: .newlines) - let relativePath = url.path.replacingOccurrences(of: root.path + "/", with: "") var inspected = 0 var offenders: [String] = [] @@ -72,9 +106,11 @@ struct MenuDisclosureIndicatorTests { let end = labelBlockEnd(lines, from: index) let chain = lines[end ..< min(end + 14, lines.count)].joined(separator: "\n") guard let modifiers = chain.range(of: ".accessibilityLabel") else { continue } + let precedingModifiers = chain[.. Date: Wed, 23 Sep 2026 12:46:06 +0700 Subject: [PATCH 2/2] test(ios): queue the prompt test's second question after the first is on screen --- .../TableProMobileTests/SSH/ConnectionPromptQueueTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/TableProMobile/TableProMobileTests/SSH/ConnectionPromptQueueTests.swift b/TableProMobile/TableProMobileTests/SSH/ConnectionPromptQueueTests.swift index 6dd2639e2a..0418223f12 100644 --- a/TableProMobile/TableProMobileTests/SSH/ConnectionPromptQueueTests.swift +++ b/TableProMobile/TableProMobileTests/SSH/ConnectionPromptQueueTests.swift @@ -101,6 +101,7 @@ struct ConnectionPromptQueueTests { let second = makePrompt("Second") async let firstAnswer = queue.ask(first) + await waitUntil({ queue.current?.id == first.id }, "the first question should be on screen") async let secondAnswer = queue.ask(second) await waitUntil({ queue.pending.count == 2 }, "both questions should be queued")