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
11 changes: 11 additions & 0 deletions .cspell.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,17 @@ words:
- xshd
- avaloniaedit
- avares
- Appium
- appium
- NovaWindows
- WinAppDriver
- WebDriver
- webdriver
- novawindows
- atspi
- automatable
- startable
- unvalidated
ignorePaths:
- '**/.git/**'
- '**/node_modules/**'
Expand Down
4 changes: 2 additions & 2 deletions .github/agents/developer.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ Perform software development tasks by determining and applying appropriate stand
4. **Execute work** following standards requirements and quality checks
5. **Formatting**: Run `pwsh ./fix.ps1` to silently apply all
available auto-fixers (dotnet format, markdown, YAML) before committing
6. **Build and test** (code changes only): Run `pwsh ./build.ps1` and confirm it
passes - report FAILED if the build or any tests fail
6. **Build and test** (code changes only): Run `pwsh ./build.ps1 -Build -Test` and
confirm it passes - report FAILED if the build or any tests fail
7. **Generate completion report** per the AGENTS.md reporting requirements - save to
`.agent-logs/{agent-name}-{subject}-{unique-id}.md` and return the summary to the caller

Expand Down
106 changes: 106 additions & 0 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ jobs:
--property:Version=${{ inputs.version }}

- name: Test
# IntegrationTests' Appium-driven tests are excluded here (Category!=Integration) because no Appium
# server is running in this cross-platform matrix job; they run for real only in the dedicated
# appium-windows-integration-tests job below, on windows-latest, where an Appium server is started.
run: >
dotnet test
--no-build
Expand All @@ -188,6 +191,7 @@ jobs:
--collect "XPlat Code Coverage;Format=opencover"
--logger "trx;LogFilePrefix=${{ matrix.os }}"
--results-directory artifacts
--filter "Category!=Integration"

- name: End Sonar Scanner
env:
Expand Down Expand Up @@ -347,6 +351,108 @@ jobs:
name: package-msi
path: package/*.msi

# Builds and publishes the Desktop application, then drives it end-to-end through a locally
# started Appium server and its NovaWindows driver (the maintained successor to the deprecated
# WinAppDriver), exercising test/DemaConsulting.SysML2Workbench.IntegrationTests' real Windows-path
# tests via run-under-appium.ps1 (the same script a developer uses locally via
# `build.ps1 -IntegrationTest`). This job is intentionally windows-latest-only for now: Avalonia's
# AutomationPeer also exposes controls to macOS's NSAccessibility (Appium's Mac2 driver) and Linux's
# AT-SPI2 (via KDE's selenium-webdriver-at-spi), and both AppFixture.cs and run-under-appium.ps1
# already contain correct OS-branching code for all three platforms, but only Windows has a
# provisioned CI runner/driver install today. This job is independent of (does not `need`) the build
# job above, so it does not block or duplicate that job's cross-platform packaging; it publishes its
# own copy of the Desktop app instead of depending on build's artifact, keeping its job graph simple
# and self-contained in the same style as the rest of this workflow.
appium-windows-integration-tests:
name: Appium Windows Integration Tests
needs: quality-checks
runs-on: windows-latest
permissions:
contents: read

steps:
# === INSTALL DEPENDENCIES ===
# This section installs all required dependencies and tools for the Appium/NovaWindows session.
# Downstream projects: Add any additional dependency installations here.

- name: Checkout
uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Setup dotnet
uses: actions/setup-dotnet@v6
with:
dotnet-version: 9.x

- name: Setup Node.js
uses: actions/setup-node@v7
with:
node-version: 24.x

- name: Install Appium and the NovaWindows driver
run: |
npm install -g appium
appium driver install --source=npm appium-novawindows-driver

# === BUILD AND PUBLISH THE DESKTOP APPLICATION ===
# This section builds the solution and publishes the self-contained Desktop executable that the
# Appium session below launches.
# Downstream projects: Add any additional build or publish steps here.

- name: Restore Dependencies
run: >
dotnet restore

- name: Build
run: >
dotnet build
--no-restore
--configuration Release
--property:Version=${{ inputs.version }}

- name: Publish self-contained build (win-x64)
run: >
dotnet publish
src/DemaConsulting.SysML2Workbench.Desktop/DemaConsulting.SysML2Workbench.Desktop.csproj
--configuration Release
--runtime win-x64
--self-contained true
--property:Version=${{ inputs.version }}
--property:PublishSingleFile=true
--output publish/win-x64

# === START APPIUM AND RUN THE INTEGRATION TESTS ===
# This section delegates to run-under-appium.ps1 (the same wrapper a developer uses locally via
# `build.ps1 -IntegrationTest`) to start a local Appium server, run the IntegrationTests project
# against the published Desktop application, then stop the server - sharing one tested code path
# instead of duplicating the start/poll/stop logic here.
# Downstream projects: Add any additional Appium setup or test steps here.

- name: Run Appium IntegrationTests
shell: pwsh
env:
SYSML2WORKBENCH_APP_PATH: ${{ github.workspace }}\publish\win-x64\DemaConsulting.SysML2Workbench.Desktop.exe
run: >
./run-under-appium.ps1 --
dotnet test
test/DemaConsulting.SysML2Workbench.IntegrationTests/DemaConsulting.SysML2Workbench.IntegrationTests.csproj
--configuration Release
--property:Version=${{ inputs.version }}
--logger "trx;LogFilePrefix=appium-windows"
--results-directory artifacts

# === UPLOAD ARTIFACTS ===
# This section uploads the Appium test results for use by downstream jobs.
# Downstream projects: Add any additional artifact uploads here.

- name: Upload Appium test results
if: always()
uses: actions/upload-artifact@v7
with:
name: artifacts-appium-windows-integration-tests
path: artifacts/

# Runs CodeQL security and quality analysis, gathering results to include
# in the code quality report.
codeql:
Expand Down
11 changes: 11 additions & 0 deletions .reviewmark.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,9 @@ reviews:
- src/**/AppShellSubsystem/SourceTextDocumentView.axaml.cs
- test/**/AppShellSubsystem/MainWindowShellTests.cs
- test/**/AppShellSubsystem/SourceTextDocumentViewModelTests.cs
- test/**/AppShellSubsystem/MainWindowShellUiTests.cs
- test/DemaConsulting.SysML2Workbench.UiTests/GlobalUsings.cs
- test/DemaConsulting.SysML2Workbench.UiTests/TestAppBuilder.cs
- id: SysML2Workbench-AppShellSubsystem-WorkspacePanel
title: Review that SysML2Workbench AppShellSubsystem WorkspacePanel Implementation
is Correct
Expand All @@ -326,6 +329,7 @@ reviews:
- src/**/AppShellSubsystem/WorkspacePanelToolView.axaml
- src/**/AppShellSubsystem/WorkspacePanelToolView.axaml.cs
- test/**/AppShellSubsystem/WorkspacePanelToolViewModelTests.cs
- test/**/AppShellSubsystem/WorkspacePanelUiTests.cs
- id: SysML2Workbench-AppShellSubsystem-AboutDialog
title: Review that SysML2Workbench AppShellSubsystem AboutDialog Implementation
is Correct
Expand Down Expand Up @@ -462,3 +466,10 @@ reviews:
- docs/design/ots/avaloniaedit.md
- docs/verification/ots/avaloniaedit.md
- test/OtsSoftwareTests/AvaloniaEditTests.cs
- id: OTS-Appium
title: Review that Appium Provides Required Functionality
paths:
- docs/reqstream/ots/appium.yaml
- docs/design/ots/appium.md
- docs/verification/ots/appium.md
- test/DemaConsulting.SysML2Workbench.IntegrationTests/**
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ yet cover something.
- **`pip-requirements.txt`** - Python dependencies for yamllint and yamlfix
- **`docs/sysml2/`** - SysML2 architecture model; authoritative source for software structure
- **`fix.ps1`** - Applies all auto-fixers silently (dotnet format, markdown, YAML). Always exits 0.
- **`build.ps1`** - Builds the solution and runs all tests.
- **`build.ps1`** - Builds the solution and/or runs its test tiers on demand via
switches (`-Build -Test -IntegrationTest -All`); run with no arguments for usage.

# Standards Application (ALL Agents Must Follow)

Expand Down
29 changes: 26 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ building a custom view.
(Windows/Linux/macOS entry point).
- `test/DemaConsulting.SysML2Workbench.Tests/` - unit and subsystem-level
tests mirroring `src/`.
- `test/DemaConsulting.SysML2Workbench.UiTests/` - headless, in-process
Avalonia UI tests (view/view-model interaction, no real window).
- `test/DemaConsulting.SysML2Workbench.IntegrationTests/` - Appium/AT-SPI-driven
end-to-end tests against the compiled Desktop application (Windows, macOS,
and Linux; CI runs the Windows job today; requires a running automation
server - see "Building and Testing" below).
- `test/OtsSoftwareTests/` - integration tests for the off-the-shelf (OTS)
dependencies (SysML2Tools, Rendering, Avalonia, xUnit).
- `docs/` - requirements (`docs/reqstream/`), design (`docs/design/`),
Expand All @@ -52,7 +58,24 @@ building a custom view.
## Building and Testing

```powershell
pwsh ./build.ps1 # restore, build, and run all tests
pwsh ./fix.ps1 # auto-fix formatting
pwsh ./lint.ps1 # lint and compliance checks
pwsh ./build.ps1 -Build -Test # restore, build, and run the unit/headless test suite
pwsh ./build.ps1 # no switches: prints usage and exits non-zero
pwsh ./fix.ps1 # auto-fix formatting
pwsh ./lint.ps1 # lint and compliance checks
```

`build.ps1` accepts combinable switches: `-Build` (restore + build),
`-Test` (build if needed, then run the unit/headless suite), `-IntegrationTest`
(build if needed, then run the Appium/AT-SPI suite), and `-All`
(equivalent to all three). `build.ps1 -Test` and CI's cross-platform build
job both run `dotnet test --filter "Category!=Integration"`, so
`test/DemaConsulting.SysML2Workbench.IntegrationTests`' Appium-driven tests
are excluded from that default test run. That tier runs for real in CI's
dedicated `appium-windows-integration-tests` job (`windows-latest`), and can
now also be run locally on Windows, macOS, or Linux via
`pwsh ./build.ps1 -IntegrationTest`, which delegates to `run-under-appium.ps1`:
on Windows/macOS it installs Appium and the NovaWindows/Mac2 driver, publishes
the Desktop application, starts and polls a local Appium server, runs the
tests, and always stops the server afterward; on Linux it publishes the
Desktop application and delegates the whole test run to the pre-installed
`selenium-webdriver-at-spi-run` wrapper (see `docs/design/ots/appium.md`).
2 changes: 2 additions & 0 deletions SysML2Workbench.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
</Folder>
<Folder Name="/test/">
<Project Path="test/DemaConsulting.SysML2Workbench.Tests/DemaConsulting.SysML2Workbench.Tests.csproj" />
<Project Path="test/DemaConsulting.SysML2Workbench.UiTests/DemaConsulting.SysML2Workbench.UiTests.csproj" />
<Project Path="test/DemaConsulting.SysML2Workbench.IntegrationTests/DemaConsulting.SysML2Workbench.IntegrationTests.csproj" />
<Project Path="test/OtsSoftwareTests/OtsSoftwareTests.csproj" />
</Folder>
</Solution>
119 changes: 107 additions & 12 deletions build.ps1
Original file line number Diff line number Diff line change
@@ -1,22 +1,117 @@
# build.ps1
#
# TODO: Update this script to build and test your solution.
# Replace SysML2Workbench with your actual solution/project name.
# PURPOSE:
# Builds the solution and/or runs its test tiers on demand, selected via
# switch parameters so contributors and agents can run only the tier(s)
# they need instead of always paying for a full restore+build+test+
# integration-test cycle.
#
# USAGE:
# ./build.ps1 [-Build] [-Test] [-IntegrationTest] [-All]
# Run with no switches to print usage and exit non-zero.

param(
[switch]$Build,
[switch]$Test,
[switch]$IntegrationTest,
[switch]$All
)

function Show-Usage {
Write-Host "Usage: build.ps1 [-Build] [-Test] [-IntegrationTest] [-All]"
Write-Host " -Build Restore and build the solution (Release configuration)"
Write-Host " -Test Run the unit/headless test suite (excludes Appium integration tests)"
Write-Host " -IntegrationTest Publish the Desktop app and run the Appium/AT-SPI integration tests (Windows/macOS/Linux)"
Write-Host " -All Equivalent to -Build -Test -IntegrationTest"
Write-Host "Multiple switches may be combined, e.g.: ./build.ps1 -Build -Test"
}

if ($All) { $Build = $true; $Test = $true; $IntegrationTest = $true }

if (-not ($Build -or $Test -or $IntegrationTest)) {
Show-Usage
exit 1
}

$buildError = $false
$solutionBuilt = $false # tracks whether restore+build already ran this invocation

function Invoke-SolutionBuild {
# Shared by -Build, -Test, and -IntegrationTest so each switch is usable
# standalone from a clean checkout without requiring -Build in the same
# invocation; only runs once per script invocation.
if ($script:solutionBuilt) { return $true }

Write-Host "Restoring dependencies..."
dotnet restore
if ($LASTEXITCODE -ne 0) { return $false }

Write-Host "Building..."
dotnet build --no-restore --configuration Release
if ($LASTEXITCODE -ne 0) { return $false }

$script:solutionBuilt = $true
return $true
}

if ($Build) {
if (-not (Invoke-SolutionBuild)) { $buildError = $true }

# [PROJECT-SPECIFIC] Add additional build steps here (e.g., packaging, publishing).
}

if ($Test) {
if (-not (Invoke-SolutionBuild)) {
$buildError = $true
} else {
Write-Host "Running tests..."
# IntegrationTests' Appium-driven tests are excluded (Category!=Integration) because this
# step does not start an Appium server; that tier runs via -IntegrationTest below (locally)
# or CI's dedicated appium-windows-integration-tests job. See docs/design/introduction.md
# for the full three-(plus-OTS)-tier test strategy.
dotnet test --no-build --configuration Release --logger trx --results-directory artifacts/tests --filter "Category!=Integration"
if ($LASTEXITCODE -ne 0) { $buildError = $true }
}
}

if ($IntegrationTest) {
if (-not (Invoke-SolutionBuild)) {
$buildError = $true
} else {
if ($IsWindows -or $IsMacOS) {
Write-Host "Installing Appium..."
npm install -g appium
if ($LASTEXITCODE -ne 0) { $buildError = $true }

Write-Host "Restoring dependencies..."
dotnet restore
if ($LASTEXITCODE -ne 0) { $buildError = $true }
$driverName = $IsWindows ? "appium-novawindows-driver" : "mac2"
$driverArgs = $IsWindows ? @("--source=npm", $driverName) : @($driverName)
Write-Host "Installing the $driverName driver..."
$driverInstallOutput = appium driver install @driverArgs 2>&1 | Out-String
Write-Host $driverInstallOutput
if ($LASTEXITCODE -ne 0 -and $driverInstallOutput -notmatch "(?i)already installed") {
$buildError = $true
}
} else {
Write-Host "Linux: not installing Appium/a driver - selenium-webdriver-at-spi must already be built and installed (see docs/design/ots/appium.md)."
}

Write-Host "Building..."
dotnet build --no-restore --configuration Release
if ($LASTEXITCODE -ne 0) { $buildError = $true }
if (-not $buildError) {
$rid = $IsWindows ? "win-x64" : ($IsMacOS ? ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq [System.Runtime.InteropServices.Architecture]::Arm64 ? "osx-arm64" : "osx-x64") : "linux-x64")
$exeName = $IsWindows ? "DemaConsulting.SysML2Workbench.Desktop.exe" : "DemaConsulting.SysML2Workbench.Desktop"

Write-Host "Running tests..."
dotnet test --no-build --configuration Release --logger trx --results-directory artifacts/tests
if ($LASTEXITCODE -ne 0) { $buildError = $true }
Write-Host "Publishing self-contained Desktop build ($rid)..."
dotnet publish src/DemaConsulting.SysML2Workbench.Desktop/DemaConsulting.SysML2Workbench.Desktop.csproj --configuration Release --runtime $rid --self-contained true --property:PublishSingleFile=true --output publish/$rid
if ($LASTEXITCODE -ne 0) { $buildError = $true }

# [PROJECT-SPECIFIC] Add additional build steps here (e.g., packaging, publishing).
if (-not $buildError) {
Write-Host "Running Appium IntegrationTests..."
$env:SYSML2WORKBENCH_APP_PATH = Join-Path (Get-Location) "publish/$rid/$exeName"
./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
}
}
}
}

exit ($buildError ? 1 : 0)
4 changes: 2 additions & 2 deletions docs/build_notes/toolchain_setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ After installing the toolchain, validate the environment with these commands:

dotnet --info

pwsh ./build.ps1
pwsh ./build.ps1 -Build -Test

```

If `build.ps1` completes successfully, the local machine has the minimum toolchain
If `build.ps1 -Build -Test` completes successfully, the local machine has the minimum toolchain

needed for the current repository state.
Loading
Loading