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
8 changes: 4 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,12 @@ jobs:
run: npm install -g @native-sdk/cli@0.4.1
- name: Build headless app (automation enabled)
run: native build -Dplatform=linux -Dautomation=true
- name: Drive the app headless and assert the widget tree
- name: Drive the GUI headless and assert the widget tree
run: ./scripts/smoke.sh
- name: Upload rendered screenshot
- name: Upload rendered screenshots
if: always()
uses: actions/upload-artifact@v4
with:
name: smoke-screenshot
path: .zig-cache/native-sdk-automation/screenshot-main-canvas.png
name: gui-screenshots
path: .zig-cache/native-sdk-automation/screenshot-*.png
if-no-files-found: ignore
12 changes: 7 additions & 5 deletions CURRENT_STATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ criticals raise OS notifications live (#60, ADR-0011) — all **in-runner**
**S6 — public release & launch (0.5.0): in progress.** Packaging is wired
(#70) — `native package` builds a macOS `.app` (ad-hoc `.dmg`) and a Linux
`.tar.gz`, and a tag-triggered [release workflow](.github/workflows/release.yml)
publishes both to a GitHub release (see [RELEASING.md](RELEASING.md)). Remaining:
GUI test suite (#71), the frozen conformance contract (#72), release-ready docs
(#73), and the v0.5.0 cut (#74). The menu-bar monitor and notarization are
post-0.5; see [ROADMAP.md](ROADMAP.md).
publishes both to a GitHub release (see [RELEASING.md](RELEASING.md)). The
automation smoke now **drives** the GUI (#71) — clicking the replay transport,
a filter facet, and the Live tab, asserting the semantics tree after each and
screenshotting the inspector + live surfaces. Remaining: the frozen conformance
contract (#72), release-ready docs (#73), and the v0.5.0 cut (#74). The menu-bar
monitor and notarization are post-0.5; see [ROADMAP.md](ROADMAP.md).

## What's done

Expand Down Expand Up @@ -185,7 +187,7 @@ live inspector surface with notifications (#54–#60) — is landed.

| Area | Status |
| --- | --- |
| `repo` (tooling, CI) | ✅ S0 CI; 🔨 S6 packaging + release workflow (#70) |
| `repo` (tooling, CI) | ✅ S0 CI + S6 packaging/release (#70) + driven GUI suite (#71) |
| `docs` (docs, ADRs) | ✅ done for S0 |
| `ocpp` (engine) | ✅ S2 + ingestion (#29) + reports (#41) + anonymize (#42) + diff (#43) + replay core (#44); O(n) detection pending (#36) |
| `ui` (native views) | ✅ S3 inspector (#27–#32) + replay transport (#44) + live-capture view (#59) + live notifications (#60) |
Expand Down
82 changes: 64 additions & 18 deletions scripts/smoke.sh
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
#!/usr/bin/env bash
#
# Automation smoke test: launch the app, wait for its embedded automation
# server, assert the widget tree actually rendered, and capture a
# deterministic screenshot artifact. Portable — uses a virtual framebuffer
# (Xvfb) when available (CI), otherwise runs the window directly (local
# macOS). The app must be built with -Dautomation=true.
# Automation-driven GUI test: launch the app, wait for its embedded automation
# server, then DRIVE the headline flows through the automation protocol —
# clicking real widgets and asserting the rendered semantics tree after each
# step — and capture deterministic screenshots as artifacts. Portable: uses a
# virtual framebuffer (Xvfb) when available (CI), otherwise runs the window
# directly (local macOS). The app must be built with -Dautomation=true.
#
set -euo pipefail

BIN=zig-out/bin/studio
AUTO_DIR=.zig-cache/native-sdk-automation
# Launch with the vendored sample as a command-line trace argument, so the smoke
# test exercises the real CLI load path and lands on the loaded overview.
CANVAS=main-canvas
# Launch with the vendored sample as a command-line trace argument, so the test
# exercises the real CLI load path and lands on the loaded overview.
SAMPLE=src/ocpp/testdata/normal-session.json

if [ ! -x "$BIN" ]; then
Expand Down Expand Up @@ -44,26 +46,70 @@ launch &
APP_PID=$!
trap 'kill "$APP_PID" >/dev/null 2>&1 || true' EXIT

# The numeric widget id for the widget whose accessibility name equals $1. Each
# snapshot line is `widget @w<win>/<view>#<id> role=<r> name="<label>" …`; anchor
# on the id in that path so a name never mismatches. Prefer the `snapshot`
# command's output (a fresh publish); fall back to the dropbox snapshot.txt.
widget_id() {
local snap
snap="$(native automate snapshot 2>/dev/null || true)"
[ -n "$snap" ] || snap="$(cat "$AUTO_DIR/snapshot.txt" 2>/dev/null || true)"
printf '%s\n' "$snap" | grep -F "name=\"$1\"" \
| sed -E 's/^.*@w[0-9]+\/[a-z0-9-]+#([0-9]+) role=.*/\1/' | head -n1
}

# Click the widget named $1 (fails loudly, dumping the tree, if it is absent).
click() {
local id
id="$(widget_id "$1" || true)"
if [ -z "$id" ]; then
echo "gui-test: no widget named \"$1\" in the snapshot:" >&2
native automate snapshot >&2 || true
exit 1
fi
native automate widget-click "$CANVAS" "$id"
}

shot() { # capture the canvas to a stably-named PNG artifact ($1)
native automate screenshot "$CANVAS"
cp "$AUTO_DIR/screenshot-$CANVAS.png" "$AUTO_DIR/screenshot-$1.png"
test -s "$AUTO_DIR/screenshot-$1.png"
}

# Block until the runtime publishes ready=true (or fail loudly on timeout).
native automate wait --timeout-ms 60000

# The window must exist with the loaded trace rendered. The app was launched
# with the sample trace as a command-line argument, so it opens straight on the
# overview — proving the CLI load → parse → render path works end to end. These
# assertions read the semantics snapshot (GPU-independent), so they hold under
# software GL too.
# 1. The CLI load → parse → render path: launched with the sample trace, the app
# opens straight on the overview. Assertions read the semantics snapshot
# (GPU-independent), so they hold under software GL too.
native automate assert --timeout-ms 30000 \
'ready=true' \
'normal-session.json' \
'BootNotification' \
'22 events' \
'OCPP DebugKit Studio'

# No runtime/dispatch errors surfaced during startup.
native automate assert --absent 'error event='
# 2. Drive the replay transport: with nothing selected, Next selects the first
# event, and the detail pane unpacks it.
click 'Next'
native automate assert --timeout-ms 30000 'Details' 'evt-0001' 'Message ID'
shot inspector

# 3. Drive the filter: the Call facet narrows the timeline (the status bar counts
# the match subset), and Clear restores the full set.
click 'Call'
native automate assert --timeout-ms 30000 'of 22 events'
click 'Clear'
native automate assert --absent --timeout-ms 30000 'of 22 events'

# A real-pixel capture must land as a non-empty PNG.
native automate screenshot main-canvas
test -s "$AUTO_DIR/screenshot-main-canvas.png"
# 4. Switch to the live-capture surface: its control strip renders (endpoints +
# start control + the idle note). No capture is started — that would spawn a
# real proxy subprocess.
click 'Live'
native automate assert --timeout-ms 30000 'Start capture' 'Ready to capture' 'Listen'
shot live

# 5. No runtime/dispatch errors surfaced across the whole run.
native automate assert --absent 'error event='

echo "smoke: ok — window rendered and automation drove the inspector"
echo "smoke: ok — automation drove the inspector, filter, and live surface"
Loading