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
1 change: 1 addition & 0 deletions .cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ language: en
words:
- APFS
- Argb
- assertable
- axaml
- BGRA
- Blockquotes
Expand Down
1 change: 0 additions & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,6 @@ jobs:
env:
SYSML2WORKBENCH_APP_PATH: ${{ github.workspace }}\publish\win-x64\DemaConsulting.SysML2Workbench.Desktop.exe
SYSML2WORKBENCH_ARTIFACTS_DIR: ${{ github.workspace }}\artifacts
SYSML2WORKBENCH_STARTUP_FILE: ${{ github.workspace }}\test\DemaConsulting.SysML2Workbench.IntegrationTests\TestData\InspectionSample.sysml
run: >
./run-under-appium.ps1 --
dotnet test
Expand Down
2 changes: 0 additions & 2 deletions build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,10 @@ if ($IntegrationTest) {
Write-Host "Running Appium IntegrationTests..."
$env:SYSML2WORKBENCH_APP_PATH = Join-Path (Get-Location) "publish/$rid/$exeName"
$env:SYSML2WORKBENCH_ARTIFACTS_DIR = Join-Path (Get-Location) "artifacts"
$env:SYSML2WORKBENCH_STARTUP_FILE = Join-Path (Get-Location) "test/DemaConsulting.SysML2Workbench.IntegrationTests/TestData/InspectionSample.sysml"
./run-under-appium.ps1 -- dotnet test test/DemaConsulting.SysML2Workbench.IntegrationTests/DemaConsulting.SysML2Workbench.IntegrationTests.csproj --configuration Release --logger "trx;LogFilePrefix=appium-integration" --results-directory artifacts/tests
if ($LASTEXITCODE -ne 0) { $buildError = $true }
Remove-Item Env:\SYSML2WORKBENCH_APP_PATH -ErrorAction SilentlyContinue
Remove-Item Env:\SYSML2WORKBENCH_ARTIFACTS_DIR -ErrorAction SilentlyContinue
Remove-Item Env:\SYSML2WORKBENCH_STARTUP_FILE -ErrorAction SilentlyContinue
}
}
}
Expand Down
9 changes: 2 additions & 7 deletions docs/reqstream/ots/appium.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,8 @@ sections:
justification: |
Appium's accessibility-tree-based lookups need a stable identifier that does not depend on a control''s displayed Name/x:Name, so tests remain reliable as UI text changes.
tests:
- 'DesktopApp_FileMenu_AddFileSourceMenuItem_IsDiscoverableAndEnabled'
- 'DesktopApp_FileMenu_AddFolderSourceMenuItem_IsDiscoverableAndEnabled'
- 'DesktopApp_ViewMenu_WorkspacePanelMenuItem_IsDiscoverableAndEnabled'
- 'DesktopApp_ViewMenu_PredefinedViewsMenuItem_IsDiscoverableAndEnabled'
- 'DesktopApp_ViewMenu_DiagnosticsMenuItem_IsDiscoverableAndEnabled'
- 'DesktopApp_ViewMenu_ViewBuilderDialogMenuItem_IsDiscoverableAndEnabled'
- 'DesktopApp_FileMenu_Items_AreDiscoverableAndEnabled'
- 'DesktopApp_ViewMenu_Items_AreDiscoverableAndEnabled'
- 'DesktopApp_QueryMenu_QueryDialogMenuItem_IsDiscoverableAndEnabled'
- 'DesktopApp_QueryMenu_QueryDialogMenuItem_OpensAndClosesQueryDialog'
- 'DesktopApp_HelpMenu_AboutMenuItem_OpensAndClosesAboutDialog'
- 'DesktopApp_FileMenu_CloseAllMenuItem_IsDiscoverableAndEnabled'
143 changes: 77 additions & 66 deletions docs/verification/ots/appium.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

### Verification Approach

Integration tests in `test/DemaConsulting.SysML2Workbench.IntegrationTests/MainWindowShellIntegrationTests.cs`
qualify the Windows/NovaWindows Appium session by launching the real,
published `DemaConsulting.SysML2Workbench.Desktop` executable through
`AppFixture` and driving it through its actual accessibility tree, rather
than a mocked or headless UI harness, because the end-to-end platform
integration itself is what this tier is qualifying. These tests only run for
Integration tests in `test/DemaConsulting.SysML2Workbench.IntegrationTests/` (split by feature area into
`MainWindowLaunchIntegrationTests.cs`, `FileMenuIntegrationTests.cs`, `ViewMenuIntegrationTests.cs`,
`QueryDialogIntegrationTests.cs`, and `HelpMenuIntegrationTests.cs`) qualify the Windows/NovaWindows Appium
session by launching the real, published `DemaConsulting.SysML2Workbench.Desktop` executable through
`AppFixture` and driving it through its actual accessibility tree, rather than a mocked or headless UI harness,
because the end-to-end platform integration itself is what this tier is qualifying. These tests only run for
real in `.github/workflows/build.yaml`'s `appium-windows-integration-tests`
job (and locally via `build.ps1 -IntegrationTest`/`run-under-appium.ps1`,
which is cross-platform but only validated on Windows), where an Appium
Expand All @@ -16,15 +16,19 @@ cross-platform `build` job's `Test` step and `build.ps1 -Test`) they are
excluded via `--filter "Category!=Integration"`, since no Appium/AT-SPI
server is started in those runs.

Several of these tests mutate shared workspace state (adding a real source, or relying on the
`SYSML2WORKBENCH_STARTUP_FILE`-preloaded fixture) against the one `AppFixture`-launched application process shared
across every `[Fact]` in this class (`[Collection("AppFixture")]`). Rather than resetting the workspace before
every test - which would deterministically wipe the `STARTUP_FILE` preload before the one test that depends on it
ever ran, since that preload only happens once at process launch and cannot be redone per test without driving the
very native "Open File" dialog this design avoids - cleanup is owned only by the one state-mutating test itself,
via `AppFixture.CloseAllSources()` (clicks File > Close All through the real accessibility tree) in a `finally`
block. Every other test in this class only asserts menu-item discoverability/enablement, which holds regardless of
whether a source happens to be loaded.
Every test class derives from `AppiumTestBase`, which gives each individual `[Fact]` its own dedicated
`AppFixture` launch and WebDriver session - xUnit constructs a fresh test-class instance per fact, so a plain
`AppFixture` field owned by the base class already gives full per-test isolation with no shared-collection
fixture, and `Dispose()` quits that one test's application process again once it finishes, whether it passed
or failed. `AppiumTestBase.StartApp(string startupArguments = "")` additionally lets a test relaunch mid-test
with different startup arguments (quitting/disposing any previous session first) - `AppFixture` forwards these
arguments to the launched process via the `appium:appArguments`/`appium:arguments` capabilities (recognized by
`App.axaml.cs`'s `ApplyStartupSourceArgumentsForTesting` as repeatable `--startup-source <path>` tokens), which
is how `DesktopApp_QueryDialog_PopulatedWithSourceAndChip_CapturesInspectionScreenshot` preloads a real
workspace without driving the unautomatable native "Open File" dialog. Because every test gets its own
application instance, no test needs bespoke cleanup (a shared-session `CloseAllSources` reset, restoring a
panel to its originally-open state, and so on) purely to protect other tests it has no other relationship
with.

### Test Scenarios

Expand All @@ -33,49 +37,63 @@ session's `Title` reports "SysML2Workbench" immediately after launch, proving
the session is genuinely driving the compiled application's real main
window rather than a stub.

**DesktopApp_FileMenu_AddFileSourceMenuItem_IsDiscoverableAndEnabled**:
Opens the File menu (`MobileBy.Name("File")`) and locates the "Open File..."
item by its `AddFileSourceMenuItem` automation id
(`MobileBy.AccessibilityId`), proving Avalonia's UIA automation peer exposes
the `AutomationProperties.AutomationId` values added to `MainWindowView.axaml`
**DesktopApp_FileMenu_Items_AreDiscoverableAndEnabled**: Opens the File menu
(`MobileBy.Name("File")`) once and locates "Open File..." (`AddFileSourceMenuItem`),
"Open Folder..." (`AddFolderSourceMenuItem`), and "Close All"
(`CloseAllMenuItem`) by automation id (`MobileBy.AccessibilityId`), asserting
each is displayed and enabled, then closes the menu once via Escape without
clicking any item - proving Avalonia's UIA automation peer exposes the
`AutomationProperties.AutomationId` values added to `MainWindowView.axaml`
through the real accessibility tree that Appium's NovaWindows driver reads.
The item is not actually clicked, since doing so would open the OS-native
"Open File" dialog, which lives outside Avalonia's own accessibility tree.
All three checks share one menu-open/close and one dedicated application
launch since they are pure, side-effect-free discoverability checks against
the same menu. None of the items are actually clicked: "Open File..."/"Open
Folder..." would open an OS-native dialog that lives outside Avalonia's own
accessibility tree, and "Close All" has no assertable close/reopen
counterpart the way a panel-toggle item does.

**DesktopApp_FileMenu_AddFolderSourceMenuItem_IsDiscoverableAndEnabled**,
**DesktopApp_ViewMenu_WorkspacePanelMenuItem_IsDiscoverableAndEnabled**,
**DesktopApp_ViewMenu_PredefinedViewsMenuItem_IsDiscoverableAndEnabled**,
**DesktopApp_ViewMenu_DiagnosticsMenuItem_IsDiscoverableAndEnabled**,
**DesktopApp_ViewMenu_ViewBuilderDialogMenuItem_IsDiscoverableAndEnabled**,
**DesktopApp_QueryMenu_QueryDialogMenuItem_IsDiscoverableAndEnabled**, and
**DesktopApp_FileMenu_CloseAllMenuItem_IsDiscoverableAndEnabled**:
Each opens its respective top-level menu (`File`/`View`/`Query`) and locates
one child menu item by automation id, asserting it is both displayed and
enabled, then closes the menu via Escape without clicking the item. These
extend automation-id coverage breadth-first across every subsystem's
top-level entry point (workspace panel, predefined views, diagnostics,
custom view builder, query dialog, close-all). The View menu's toggle items
and the Query dialog now also have dedicated click-through round-trip
tests (see below); the others are not yet clicked because they open a
dialog whose controls don't carry automation ids yet
(`ViewBuilderDialogMenuItem`) or a native OS surface outside Avalonia's own
accessibility tree (`AddFileSourceMenuItem`/`AddFolderSourceMenuItem`), or
because a menu item click here would leave the shared `AppFixture` session
in a different state for whichever test runs next (`CloseAllMenuItem`).
**DesktopApp_ViewMenu_Items_AreDiscoverableAndEnabled**: The same
discoverability-only shape as the File menu scenario above, applied to the
View menu's "Workspace" (`WorkspacePanelMenuItem`), "Predefined Views"
(`PredefinedViewsMenuItem`), "Diagnostics" (`DiagnosticsMenuItem`), and
"Custom View Builder..." (`ViewBuilderDialogMenuItem`) items, all checked
against one menu open/close in one test/application launch. The panel-toggle
items also have their own dedicated click-through round-trip tests (see
below); `ViewBuilderDialogMenuItem` is not yet clicked because its opened
dialog's controls don't carry automation ids that would let this fixture
close it deterministically.

**DesktopApp_QueryMenu_QueryDialogMenuItem_IsDiscoverableAndEnabled**: Opens
the Query menu and locates "Run Query..." (`QueryDialogMenuItem`) by
automation id, asserting it is displayed and enabled, then closes the menu
via Escape without clicking it - the same discoverability-only shape as the
File/View menu scenarios above, kept as its own single-item test since the
Query menu has only this one item to check.

**DesktopApp_HelpMenu_AboutMenuItem_OpensAndClosesAboutDialog**: Opens the
Help menu, clicks "About" (`AboutMenuItem`), confirms the modal About
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.
dialog's `AboutDialogOkButton` becomes visible, then dismisses it and
confirms `AboutDialogOkButton` disappears from the accessibility tree -
proving a full menu-click-to-modal-dialog-and-back round trip works
end-to-end through the real windowed application.

**DesktopApp_QueryMenu_QueryDialogMenuItem_OpensAndClosesQueryDialog**: The
same open/click/assert/close round-trip shape as the About dialog test
above, applied to the Query dialog - opens the Query menu, clicks "Run
Query..." (`QueryDialogMenuItem`), confirms the dialog's own
`AddTypeFilterButton` (part of the shared `ElementFilterView`) becomes
visible, then dismisses it via `QueryDialogCloseButton`, reusing the same
close button already exercised by the inspection-screenshot tests below.
visible, captures a cropped PNG of that button to
`artifacts/inspection/query-dialog-add-type-filter-button.png` via
`InspectionScreenshot.CaptureElement` (folded into this same round trip
rather than a separate open/close cycle, since the screenshot is just an
incidental capture of a control this test already has open and asserted
on), then dismisses the dialog via `QueryDialogCloseButton` and confirms
`AddTypeFilterButton` disappears from the accessibility tree. The
screenshot capture is not itself a pass/fail correctness assertion (only
that the button is displayed and the capture mechanism completes without
error) - the actual visual review of the saved image is a human/agent task,
since automatically detecting styling defects is not a suitable CI gate.
Honors `SYSML2WORKBENCH_THEME` for which theme the capture reflects.

**DesktopApp_ViewMenu_WorkspacePanelMenuItem_TogglesWorkspacePanel**,
**DesktopApp_ViewMenu_PredefinedViewsMenuItem_TogglesPredefinedViewsPanel**, and
Expand All @@ -92,30 +110,23 @@ 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
the second always reopens; no residual-state cleanup is needed since each
test's dedicated application instance is quit at the end of the test
regardless of outcome. 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
closes the dialog. Not a pass/fail correctness assertion (only that the button is displayed and the capture
mechanism completes without error) - the actual visual review of the saved image is a human/agent task, since
automatically detecting styling defects is not a suitable CI gate. Honors `SYSML2WORKBENCH_THEME` for which theme
the single capture reflects.

**DesktopApp_QueryDialog_PopulatedWithSourceAndChip_CapturesInspectionScreenshot**: Opens the Query dialog against
a workspace already populated by the `SYSML2WORKBENCH_STARTUP_FILE`-preloaded `TestData/InspectionSample.sysml`
fixture (avoiding the unautomatable native "Open File" dialog), clicks `AddTypeFilterButton`, selects the
"attribute" entry from its flyout `ListBox` to add a real filter chip, then captures the whole filter-row region
(`ElementFilterRoot`) to `artifacts/inspection/query-dialog-populated-with-chip.png` - proving the already-applied
**DesktopApp_QueryDialog_PopulatedWithSourceAndChip_CapturesInspectionScreenshot**: Relaunches its own dedicated
application instance with `StartApp("--startup-source <path>")` pointing at
`TestData/InspectionSample.sysml` (avoiding the unautomatable native "Open File" dialog), opens the Query
dialog, clicks `AddTypeFilterButton`, selects the "attribute" entry from its flyout `ListBox` to add a real
filter chip, then captures the whole filter-row region (`ElementFilterRoot`) to
`artifacts/inspection/query-dialog-populated-with-chip.png` - proving the already-applied
`ElementFilterView.axaml` chip-foreground contrast fix (`Foreground="#212121"` on the chip text and its "X" remove
button) is legible with a real populated chip, not just an empty "+" button. Resets the shared session's workspace
back to empty via `AppFixture.CloseAllSources()` in a `finally` block (see the Verification Approach section
above for why cleanup is owned only by this one test).
button) is legible with a real populated chip, not just an empty "+" button. No cleanup is needed afterward: this
test's own dedicated application instance is quit once the test finishes, whether it passed or failed, so the
preloaded workspace can never leak into another test.
Loading
Loading