Skip to content

Testing ​

Each package's suite covers its public behavior; packages/e2e holds the React and generated native binding suites. Development Principles sets the testing policy, and the application testing guide explains rendering, queries, input and assertions.

Coverage ownership ​

The JavaScript suites use Vitest. The packages/e2e directory and Nx e2e target names predate the principles' test levels: their React and generated-binding suites call code directly, so they are integration tests.

PackageEssential contract
animatedThe public animated adapter, native updates, refs and cleanup through @gtkx/testing.
cairoPublic bindings against Cairo, including drawing, surfaces, paths, fonts, patterns and regions.
cliCommand parsing and filesystem, build or session behavior.
codegenReal GIR input, emitted bindings/reference and consumer typechecking.
componentsCollection, selection, factory, tab and toast behavior through @gtkx/testing.
configLoading, validation, merging, paths and reload with actual configuration files.
create-gtkxScaffolding options, generated files, errors and installation boundaries.
cssComputed GTK styling from public CSS APIs.
formsControls, submission, validation, defaults and cleanup through @gtkx/testing.
glBinding calls in a real GL context.
i18nCatalogs and React translation behavior through @gtkx/testing.
mcpProtocol requests, resources, tool routing and server lifecycle.
nativeGenerated GNOME test bindings under ASan, with a leak check after every test.
navigationNavigation state, adaptive layouts and lifecycle through @gtkx/testing.
reactDefault element behaviors and reconciliation through @gtkx/testing in packages/e2e.
runtimeRuntime-exposed functions and process lifecycle without generated GIR library imports.
storybookStory composition, controls, actions and sessions through @gtkx/testing.
testingRendering, queries, input, assertions, waiting and isolation.
utilsGTKX-owned utility behavior through focused unit tests.
vitestReal fixture runs, worker preloads, isolation and teardown.

Prefer one clear scenario for each distinct contract. Consolidate historical bug cases into that coverage instead of growing a separate regression suite. Upstream libraries own their generic algorithms; GTKX tests cover how GTKX integrates with them. Test counts and file counts are not coverage goals.

The React suite covers the GtkDragSource icon prop but has no case for a live drag session.

CI verification and failure reproduction ​

Rely on the normal CI workflow for verification. CI distributes affected build, test, typecheck, lint and test-asan targets through Nx Agents, then runs affected e2e targets on the GitHub coordinator with Nx caching. The coordinator container allows nested namespaces and mounts needed by WebKit's Bubblewrap sandbox; managed agent templates do not expose these container permissions. A manual CI dispatch runs all projects. CI also runs the current Rust advisory audit. The verify GitHub job reports the combined result as Verify workspace, the only check the main branch ruleset requires.

Package test targets run integration and unit tests. The @gtkx/e2e:e2e target runs the React suite and depends on @gtkx/native:test-asan, which runs the sanitized native suite once. Native tests have no ordinary unsanitized test target. Nx prepares prerequisite builds and generated bindings and caches each suite.

When diagnosing a CI failure, reproduce the owning target and optionally pass a Vitest file filter through Nx:

bash
pnpm exec nx run @gtkx/components:test -- tests/drop-down.test.tsx

Nx and Vitest run with their default concurrency; Nx Cloud schedules distributed tasks on the agents.

Native fixtures and sanitizers ​

The native integration suite generates bindings from a pinned checkout of GNOME's gobject-introspection-tests. GIMarshallingTests supplies marshalling cases; Regress supplies general binding cases. Exercise the generated APIs with inputs and assertions appropriate to their upstream contracts, including ownership, callbacks, containers, fields, properties, signals and vfuncs.

A complete @gtkx/native:test-asan run collects V8 execution coverage of both generated namespaces. It checks every exported function, concrete constructor, class/interface method, getter and setter by its receiver and source range. The gate rejects untested APIs and obsolete exclusions. Filtered runs support diagnosis without enforcing whole-suite coverage. Signals are asserted through their generated emitters and listeners, including zero, one and multiple listeners for transferred arguments.

The checked-in packages/e2e/tests/native/coverage-inventory.json records the coverage summary and precise exclusions. An independent raw GIR audit requires bindings for every introspectable callable, signal, property access mode and virtual method in both namespaces. Lifecycle-only vfuncs require metadata but have no generated call proxy. The audit lists declarations explicitly disabled by the upstream scanner separately. A few scanner declarations have no C implementation: their tests assert that symbol resolution fails. Other exclusions are fields on abstract records with no upstream factory and unsafe writes to private ownership bookkeeping.

After reviewing a deliberate API or suite change, refresh the summary from a complete run:

bash
pnpm exec nx run @gtkx/native:test-asan --skip-nx-cache
pnpm exec tsx packages/e2e/tests/helpers/native-fixtures-coverage.ts --write

The first command reports a stale inventory after the tests pass if its summary needs updating. Detailed API-to-suite execution evidence is written to build/native-tests/coverage/inventory.json. Coverage establishes which generated wrappers ran; assertions and sanitizers establish their behavior.

Most native cases use the upstream GNOME fixtures. Local C fixtures in packages/e2e/tests/native/fixtures cover pointer and callback ownership and Cairo path conversion. Their tests compile these fixtures into temporary shared libraries. Observable runtime behavior such as keep-alive, quit and logging belongs in the runtime suite, using TypeScript child processes where process isolation is necessary.

The fixture preparation target downloads and builds the pinned upstream source and generates bindings:

bash
pnpm nx run @gtkx/e2e:_test:fixtures

The first run needs network access and builds the upstream fixtures with their Cairo cases enabled. Outputs live under build/native-tests; Nx caches compilation and generated bindings. Preserve the pinned revision when reproducing a failure.

Both the Rust addon and upstream C libraries are instrumented. Before the suite starts, an isolated TypeScript canary deliberately leaves an allocation unowned; the shared per-test hook must reject it.

ASan uses the nightly Rust toolchain pinned in packages/native/tools/rust-toolchain.toml and the system libasan runtime. The instrumented addon is separate from the ordinary build, under packages/native/build/asan. The launcher selects it with a preload before the test worker loads GTKX.

Native test setup requires ASan and exposed garbage collection. After each test it drains pending cleanup and collects garbage before calling LeakSanitizer. Test-specific teardown must release retained references first. Cases in one process run sequentially so allocations and cleanup are attributable to the active test. Sanitizer errors and leak reports fail the suite; an absent sanitizer must not silently pass.

Review leak suppressions narrowly. Some upstream signal emitters leak their original argument storage even without listeners; their exact allocation stacks are suppressed and documented in lsan.supp. The tests still exercise every ownership variant and check for other leaks after each case. Reachable objects can still be retained incorrectly, so assertions about observable ownership or finalization remain useful where the upstream fixtures expose them.

Native UI integration ​

@gtkx/vitest gives each test worker a private Wayland compositor, runtime directory and D-Bus session. Worker teardown releases these resources during normal shutdown. Subsequent Vitest, dev or Storybook startup, or gtkx cleanup, reclaims stale sessions left by interrupted runs. Package suites merge the shared source-resolution configuration, which resolves workspace TypeScript and inlines GTKX dependencies.

Importing @gtkx/testing registers its matchers and per-test cleanup through Vitest's global hooks, which @gtkx/vitest enables.

Package suites use @gtkx/vitest directly. The React integration suite and the Storybook example, the only example with tests, use @gtkx/cli/vitest-plugin, which adds application transformations such as settings schemas, assets and custom elements. The tutorial application is outside the workspace and runs its tests from its own directory.

Consumer and packaging checks ​

The CLI suite creates temporary projects and exercises development, builds, and deployment through the command parser. These projects link built workspace packages; they do not validate installation from a registry. The codegen suite likewise checks generated imports in temporary consumers linked to the workspace.

The tutorial/ application consumes registry packages outside the workspace. Run its typecheck and application tests from that directory when validating a published release. For package-resolution or publication changes, also verify an ordinary project using the packaged artifacts; a workspace build alone cannot establish that those artifacts install and run correctly.

Other checks ​

CI builds, typechecking and static analysis complement tests. Lint includes formatting, unused-code checks, workflow checks, rustfmt and Clippy. The root Vitest suite covers release tooling, native artifact restoration and the CI environment hash.

CI builds the website for documentation changes. Inspect affected pages when changing layout or navigation. For a visible widget or application change, run the affected example and inspect its live widget tree, interactions and screenshots.

To diagnose a failure, reproduce it with the owning suite's configuration. Check prerequisites first when no test starts: native build, GIR discovery, fixture compilation, generated bindings, compositor and session bus. The testing guide's debugging helpers explain UI assertions. The live application also exposes inspection through MCP.