Add integration testing with testcontainers and Playwright#442
Add integration testing with testcontainers and Playwright#442
Conversation
The integration-tests crate defines traits, error types, and helpers consumed by Docker-dependent tests. Without Docker the compiler sees them as unused and -D warnings promotes them to errors. A crate-level allow keeps CI green.
Proves that a next/script with strategy="afterInteractive" executes after a client-side navigation through the trusted-server proxy. The test SPA-navigates from / to /dashboard, waits for the script's global marker, asserts it ran exactly once, and confirms no document-level request fired (true SPA, not full reload).
…hub.com-prkjr:IABTechLab/trusted-server into feature/integration-testing-with-testcontainers
aram356
left a comment
There was a problem hiding this comment.
Solid integration testing infrastructure. The RuntimeEnvironment + FrontendFramework matrix design is clean and extensible, the two-layer approach (Rust HTTP + Playwright browser) gives good coverage, and the assertion library having its own unit tests is a nice touch.
Cross-cutting notes
📝 Separate Cargo.lock — The 3400-line crates/integration-tests/Cargo.lock is disconnected from the workspace lock. If error-stack (or any shared dep) is updated in the workspace but not here, they silently diverge. Consider documenting the upgrade process or adding a CI check that validates shared dependency versions stay in sync.
📝 Viceroy config placeholders — viceroy-template.toml uses key = "placeholder" / data = "placeholder" for all KV stores. This means integration tests don't exercise KV store functionality, which is fine but worth a comment in the config noting this intentional scope limitation.
⛏ .gitignore missing trailing newline — The last line lacks a POSIX-compliant trailing newline.
What's done well
👍 Drop impl on ViceroyHandle ensures process cleanup even on panics — good defensive design.
👍 Assertion functions (assertions.rs) have thorough unit tests covering both passing and failing cases.
👍 Browser tests use hydration detection (data-hydrated attribute) before SPA navigation — avoids the classic "clicked before client router was ready" flake.
👍 Health check endpoint is namespaced (/__trusted-server/health) to minimize publisher route conflicts.
👍 Shell scripts detect native target dynamically via rustc -vV — no hardcoded platform assumptions.
👍 Smart use of PHP built-in server for WordPress fixture — avoids MySQL dependency while still testing real HTML processing behavior.
| branches: [main] | ||
| pull_request: | ||
| pull_request_review: | ||
| types: [submitted] |
There was a problem hiding this comment.
🌱 pull_request_review trigger means integration tests re-run on every review submission, even when no code changed. Combined with the pull_request trigger, an approval on an existing PR triggers a full redundant run. Consider removing this trigger or using workflow_dispatch for on-demand runs.
There was a problem hiding this comment.
One follow-up I still recommend:
.github/workflows/integration-tests.yml currently runs on pull_request_review approvals, which can trigger full integration + browser reruns without any code change.
Severity: P2 (efficiency/cost)
Suggestion: remove the pull_request_review trigger and keep pull_request + workflow_dispatch for canonical/manual runs.
Verdict: needs discussion (not a hard blocker).
| pull_request_review: | ||
| types: [submitted] |
There was a problem hiding this comment.
P2 (efficiency/cost): pull_request_review triggers will re-run the full integration + browser test suites on every review submission — even when there are no code changes. Consider removing this trigger and keeping just pull_request + workflow_dispatch for canonical/manual runs.
Summary
/healthendpoint and CI workflow for running both test suites separately from the main CIChanges
crates/fastly/src/main.rs/healthGET endpoint returning 200 with "ok" bodycrates/integration-tests/Cargo.tomlcrates/integration-tests/tests/common/assert_form_action_rewritten(9 unit tests)crates/integration-tests/tests/environments/crates/integration-tests/tests/frameworks/crates/integration-tests/tests/integration.rstest_all_combinations, per-framework tests)crates/integration-tests/fixtures/configs/crates/integration-tests/fixtures/frameworks/wordpress//wp-admin/stubcrates/integration-tests/fixtures/frameworks/nextjs/crates/integration-tests/browser/crates/integration-tests/browser/tests/shared/crates/integration-tests/browser/tests/nextjs/crates/integration-tests/browser/tests/wordpress/.github/workflows/integration-tests.ymlscripts/integration-tests.shscripts/integration-tests-browser.shcrates/integration-tests/README.mdINTEGRATION_TESTS_PLAN.mdCargo.tomlCargo.lockTest coverage
HTTP-level (Rust + testcontainers)
HtmlInjectionScriptServingAttributeRewritingScriptServingUnknownFile404WordPressAdminInjectionNextJsRscFlightNextJsServerActionsNextJsApiRouteNextJsFormActionBrowser-level (Playwright + Chromium)
script-injectionscript-bundlenavigation(4-page SPA chain + back button + deferred route script)api-passthroughform-rewritingadmin-injectionTotals: 12 Next.js + 5 WordPress browser tests, 0 flaky
Known gaps
AuctionRequestbut is not yet populated upstream. Requires implementation before it can be tested.testcontainerscrate; browser tests usedocker run/docker stopvia Playwright's globalSetup/globalTeardown (Node.js cannot use the Rust crate).Closes
Closes #398
Test plan
cargo test --workspace(excluding integration-tests, which requires native target)cargo clippy --all-targets --all-features -- -D warnings(excluding integration-tests)cargo fmt --all -- --checkcd crates/js/lib && npm run format-- passescd docs && npm run format-- passescargo test -p integration-tests --target aarch64-apple-darwin --no-run./scripts/integration-tests.sh-- all pass./scripts/integration-tests-browser.sh-- 17 pass, 0 failcargo clippy --manifest-path crates/integration-tests/Cargo.toml --tests-- cleanChecklist
unwrap()in production code — useexpect("should ...")logmacros (notprintln!)