Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/reqstream/ots/appium.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ sections:
A system-level UI automation tier is needed that exercises the actual compiled application in a real window, complementing the headless and unit test tiers, and Windows is the only platform with a provisioned CI Appium driver today.
tests:
- 'DesktopApp_Launch_ShowsMainWindowWithExpectedTitle'
- 'DesktopApp_ViewMenu_WorkspacePanelMenuItem_TogglesWorkspacePanel'
- 'DesktopApp_ViewMenu_PredefinedViewsMenuItem_TogglesPredefinedViewsPanel'
- 'DesktopApp_ViewMenu_DiagnosticsMenuItem_TogglesDiagnosticsPanel'

- id: 'Appium-LocateControlsByAutomationId'
title: 'The SysML2Workbench repository shall expose interactive controls to Appium via AutomationProperties.AutomationId for reliable, name-independent lookup.'
Expand Down
25 changes: 25 additions & 0 deletions docs/verification/ots/appium.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,31 @@ dialog's `AboutDialogOkButton` becomes visible, then dismisses it - proving
a full menu-click-to-modal-dialog round trip works end-to-end through the
real windowed application.

**DesktopApp_ViewMenu_WorkspacePanelMenuItem_TogglesWorkspacePanel**,
**DesktopApp_ViewMenu_PredefinedViewsMenuItem_TogglesPredefinedViewsPanel**, and
**DesktopApp_ViewMenu_DiagnosticsMenuItem_TogglesDiagnosticsPanel**: Unlike the
`IsDiscoverableAndEnabled` scenarios above, these drive a full close/reopen
round trip through `MainWindowView`'s `ShowOrFocusPanel`, which implements
checkbox-style toggle semantics: a panel that is open (`Tool.IsOpen`) is
destroyed/closed via `WorkbenchDockFactory.CloseDockable` on click, while a
closed panel is restored, activated, and focused on click. Each test clicks
its View-menu item once and polls for the corresponding panel's own
automation id (`WorkspaceAddFileButton`, `PredefinedViewsListBox`,
`DiagnosticsListBox`) to disappear - proving the panel was actually closed,
not merely that the menu item is clickable - then clicks it a second time
and polls for the same control to reappear and be displayed, proving
reopening also brings the panel into view. All three panels start open per
`WorkbenchDockFactory.CreateLayout`, so the first click always closes and
the second always reopens, and each test restores the panel to its
original open state by the end, leaving no residual effect for later
tests. Checked state is verified purely through panel content presence
rather than by reading the menu item's own checked state through UI
Automation, because Avalonia's Win32 automation bridge does not currently
expose the UIA Toggle pattern for `MenuItem` to native automation clients
(confirmed via `System.Windows.Automation` and Inspect.exe, independent of
this codebase) - a real Avalonia platform limitation, not a defect in this
test suite.

**DesktopApp_QueryDialog_AddTypeFilterButton_CapturesInspectionScreenshot**: Opens the Query dialog and captures a
cropped PNG of the shared `ElementFilterView`'s "+" add-type-filter button (`AddTypeFilterButton`) to
`artifacts/inspection/query-dialog-add-type-filter-button.png` via `InspectionScreenshot.CaptureElement`, then
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
<DockPanel Margin="4">
<TextBlock DockPanel.Dock="Top" Text="Diagnostics" FontWeight="Bold" />
<TextBlock DockPanel.Dock="Top" Name="EmptyStateTextBlock" IsVisible="{Binding HasEmptyStateMessage}" Text="{Binding EmptyStateMessage}" TextWrapping="Wrap" />
<ListBox Name="DiagnosticsListBox" ItemsSource="{Binding VisibleDiagnostics}" />
<ListBox Name="DiagnosticsListBox" ItemsSource="{Binding VisibleDiagnostics}"
AutomationProperties.AutomationId="DiagnosticsListBox" />
</DockPanel>

</UserControl>
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,17 @@
</MenuItem>
</MenuItem>
<MenuItem Header="_View">
<MenuItem Name="WorkspacePanelMenuItem" Header="_Workspace" Click="OnWorkspacePanelMenuItemClick" x:DataType="dockControls:Tool" IsChecked="{Binding IsOpen, Mode=OneWay}" AutomationProperties.AutomationId="WorkspacePanelMenuItem">
<MenuItem Name="WorkspacePanelMenuItem" Header="_Workspace" Click="OnWorkspacePanelMenuItemClick" x:DataType="dockControls:Tool" ToggleType="CheckBox" IsChecked="{Binding IsOpen, Mode=OneWay}" AutomationProperties.AutomationId="WorkspacePanelMenuItem">
<MenuItem.Icon>
<mi:MaterialIcon Kind="FileTree" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Name="PredefinedViewsMenuItem" Header="_Predefined Views" Click="OnPredefinedViewsMenuItemClick" x:DataType="dockControls:Tool" IsChecked="{Binding IsOpen, Mode=OneWay}" AutomationProperties.AutomationId="PredefinedViewsMenuItem">
<MenuItem Name="PredefinedViewsMenuItem" Header="_Predefined Views" Click="OnPredefinedViewsMenuItemClick" x:DataType="dockControls:Tool" ToggleType="CheckBox" IsChecked="{Binding IsOpen, Mode=OneWay}" AutomationProperties.AutomationId="PredefinedViewsMenuItem">
<MenuItem.Icon>
<mi:MaterialIcon Kind="ViewList" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Name="DiagnosticsMenuItem" Header="_Diagnostics" Click="OnDiagnosticsMenuItemClick" x:DataType="dockControls:Tool" IsChecked="{Binding IsOpen, Mode=OneWay}" AutomationProperties.AutomationId="DiagnosticsMenuItem">
<MenuItem Name="DiagnosticsMenuItem" Header="_Diagnostics" Click="OnDiagnosticsMenuItemClick" x:DataType="dockControls:Tool" ToggleType="CheckBox" IsChecked="{Binding IsOpen, Mode=OneWay}" AutomationProperties.AutomationId="DiagnosticsMenuItem">
<MenuItem.Icon>
<mi:MaterialIcon Kind="AlertCircleOutline" />
</MenuItem.Icon>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,14 +149,23 @@ private async void OnOpenQueryDialogClick(object? sender, RoutedEventArgs e)
}

/// <summary>
/// Restores <paramref name="tool" /> to its original dock if it was hidden by a prior close (via
/// <see cref="WorkbenchDockFactory" />'s <c>HideToolsOnClose</c> setting, a safe no-op if it is not
/// currently hidden), then makes it the active and focused dockable in its owning dock. This never hides
/// an already-open panel: clicking a View-menu item for a visible panel simply (re)focuses it.
/// Toggles <paramref name="tool" />'s open/closed state, matching its View-menu item's checkable
/// <c>IsChecked</c> binding (bound to <see cref="Tool.IsOpen" />). If the tool is currently closed
/// (hidden by a prior close via <see cref="WorkbenchDockFactory" />'s <c>HideToolsOnClose</c> setting, or
/// never opened), this restores it to its original dock and makes it the active and focused dockable
/// there. If the tool is already open - regardless of whether it is the currently visible/front tab in a
/// shared <see cref="ToolDock" /> (Workspace and Predefined Views share one) - this closes it instead,
/// so the menu item behaves as a plain open/close toggle rather than requiring it to already be focused.
/// </summary>
/// <param name="tool">The panel to show or bring into focus, reusing its existing long-lived instance.</param>
/// <param name="tool">The panel to show or hide, reusing its existing long-lived instance either way.</param>
private void ShowOrFocusPanel(Tool tool)
{
if (tool.IsOpen)
{
_dockFactory.CloseDockable(tool);
return;
}

_dockFactory.RestoreDockable(tool);
_dockFactory.SetActiveDockable(tool);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,106 @@ public void DesktopApp_ViewMenu_DiagnosticsMenuItem_IsDiscoverableAndEnabled()
AssertMenuItemIsDiscoverableAndEnabled("View", "DiagnosticsMenuItem");
}

/// <summary>
/// Validates that clicking the View menu's "Workspace" item, found by the <c>WorkspacePanelMenuItem</c>
/// automation id, toggles the Workspace panel open/closed - proving <c>MainWindowView</c>'s
/// <c>ShowOrFocusPanel</c> genuinely destroys/recreates the panel's dock presence (checkbox semantics:
/// checked means the panel exists and clicking removes it, unchecked means it does not and clicking
/// creates/shows it), and that opening it also brings it into focus. See
/// <see cref="AssertMenuItemTogglesPanel" /> for the full round-trip this drives.
/// </summary>
[Fact]
public void DesktopApp_ViewMenu_WorkspacePanelMenuItem_TogglesWorkspacePanel()
{
AssertMenuItemTogglesPanel("View", "WorkspacePanelMenuItem", "WorkspaceAddFileButton");
}

/// <summary>
/// Validates that clicking the View menu's "Predefined Views" item, found by the
/// <c>PredefinedViewsMenuItem</c> automation id, toggles the Predefined Views panel open/closed - see
/// <see cref="DesktopApp_ViewMenu_WorkspacePanelMenuItem_TogglesWorkspacePanel" /> for the shared
/// checkbox-toggle semantics this proves.
/// </summary>
[Fact]
public void DesktopApp_ViewMenu_PredefinedViewsMenuItem_TogglesPredefinedViewsPanel()
{
AssertMenuItemTogglesPanel("View", "PredefinedViewsMenuItem", "PredefinedViewsListBox");
}

/// <summary>
/// Validates that clicking the View menu's "Diagnostics" item, found by the <c>DiagnosticsMenuItem</c>
/// automation id, toggles the Diagnostics panel open/closed - see
/// <see cref="DesktopApp_ViewMenu_WorkspacePanelMenuItem_TogglesWorkspacePanel" /> for the shared
/// checkbox-toggle semantics this proves.
/// </summary>
[Fact]
public void DesktopApp_ViewMenu_DiagnosticsMenuItem_TogglesDiagnosticsPanel()
{
AssertMenuItemTogglesPanel("View", "DiagnosticsMenuItem", "DiagnosticsListBox");
}

/// <summary>
/// Drives one full close/reopen round trip through a View-menu panel-toggle item, leaving the panel open
/// again afterward - the state every panel starts in per <c>WorkbenchDockFactory.CreateLayout</c> - so
/// later tests in this shared <see cref="AppFixture" /> session see no residual side effect. Confirms
/// the toggle behavior purely via the panel's own control (<paramref name="panelControlAutomationId" />)
/// becoming absent then present again, rather than reading the menu item's checked state through UI
/// Automation: Avalonia's Win32 automation bridge does not currently surface the UIA Toggle pattern for
/// <c>MenuItem</c> to native automation clients (its cross-platform <c>MenuItemAutomationPeer</c>
/// implements <c>IToggleProvider</c> internally, but that provider is never reachable from a real UI
/// Automation client such as NovaWindows/System.Windows.Automation, which always throws "Unsupported
/// Pattern" attempting to read it) - so content presence is the only reliable ground truth available.
/// </summary>
/// <param name="topLevelMenuName">The top-level menu's display name (e.g. "View").</param>
/// <param name="menuItemAutomationId">The child menu item's <c>AutomationProperties.AutomationId</c> value.</param>
/// <param name="panelControlAutomationId">
/// An automation id unique to a control that is always present while the panel is open and the active
/// tab (not conditionally hidden by panel content state, e.g. an always-visible toolbar button), used to
/// confirm the panel is genuinely shown or hidden.
/// </param>
private void AssertMenuItemTogglesPanel(string topLevelMenuName, string menuItemAutomationId, string panelControlAutomationId)
{
var reopened = false;
try
{
// Act 1 - click once; the panel starts open, so this closes it.
ClickMenuItem(topLevelMenuName, menuItemAutomationId);
var closed = WaitUntil(() => _session.FindElements(MobileBy.AccessibilityId(panelControlAutomationId)).Count == 0);
Assert.True(closed, $"The panel control '{panelControlAutomationId}' was still present after closing it via '{menuItemAutomationId}'.");

// Act 2 - click again; the panel is now closed, so this reopens and focuses it, restoring the
// originally-open state this test found the panel in.
ClickMenuItem(topLevelMenuName, menuItemAutomationId);
reopened = WaitUntil(() => _session.FindElements(MobileBy.AccessibilityId(panelControlAutomationId)).Count > 0);
Assert.True(reopened, $"The panel control '{panelControlAutomationId}' did not reappear after reopening it via '{menuItemAutomationId}'.");
Assert.True(_session.FindElement(MobileBy.AccessibilityId(panelControlAutomationId)).Displayed);
}
finally
{
// Guarantee the panel ends up open even if an assertion above failed partway through, so a failure
// here never leaks a closed panel into later tests sharing this AppFixture session. This is
// idempotent on current content presence rather than a fixed click count: if the close assertion
// failed, the panel is presumably still open and no further action is needed; if the reopen
// assertion failed, the panel is presumably still closed and one more click restores it.
if (!reopened && _session.FindElements(MobileBy.AccessibilityId(panelControlAutomationId)).Count == 0)
{
ClickMenuItem(topLevelMenuName, menuItemAutomationId);
}
}
}

/// <summary>
/// Opens the named top-level menu, clicks the child item by automation id (which also closes the menu,
/// matching normal menu-click behavior), then returns.
/// </summary>
/// <param name="topLevelMenuName">The top-level menu's display name (e.g. "View").</param>
/// <param name="menuItemAutomationId">The child menu item's <c>AutomationProperties.AutomationId</c> value.</param>
private void ClickMenuItem(string topLevelMenuName, string menuItemAutomationId)
{
_session.FindElement(MobileBy.Name(topLevelMenuName)).Click();
_session.FindElement(MobileBy.AccessibilityId(menuItemAutomationId)).Click();
}

/// <summary>
/// Validates that the View menu's "Custom View Builder..." item, found by the
/// <c>ViewBuilderDialogMenuItem</c> automation id, is discoverable and enabled. Not clicked, since the
Expand Down
Loading