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
20 changes: 20 additions & 0 deletions .github/workflows/web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,26 @@ jobs:
- name: Build
run: pnpm build

- name: Typecheck web E2E tests
run: pnpm --dir test/e2e typecheck

- name: Install Playwright Chromium
run: pnpm --dir test/e2e exec playwright install --with-deps chromium

- name: Web browser happy path
run: pnpm --dir test/e2e test

- name: Upload browser test report and failure traces
if: ${{ !cancelled() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: web-playwright
path: |
platforms/web/test/e2e/playwright-report/
platforms/web/test/e2e/test-results/
if-no-files-found: ignore
retention-days: 7

- name: Verify package (publint)
run: pnpm verify

Expand Down
35 changes: 33 additions & 2 deletions dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ up:
- protocol
- platforms/react-native
- platforms/web
- custom:
name: Install Playwright Chromium for web E2E
# Playwright checks its own browser revisions; current installs are a no-op.
met?: pnpm --dir platforms/web/test/e2e exec playwright install chromium
# Do not hide installation failures behind a successful meet command.
meet: "false"
- jdk:
version: "17"
distribution: openjdk
Expand Down Expand Up @@ -968,12 +974,12 @@ commands:
pnpm run format

check:
desc: Run Web lint, unit tests, build, package verification, and sample build checks
desc: Run Web lint, unit tests, browser tests, package verification, and sample build checks
run: |
set -e
/opt/dev/bin/dev web lint
/opt/dev/bin/dev web test
/opt/dev/bin/dev web build
/opt/dev/bin/dev web e2e
/opt/dev/bin/dev web verify
/opt/dev/bin/dev web sample build

Expand Down Expand Up @@ -1001,6 +1007,31 @@ commands:
desc: Run Web package verification
run: cd platforms/web && pnpm verify

e2e:
desc: Build the web package and run Playwright in Chromium
long_desc: |
`dev up` installs Chromium for the current Playwright version.
Use `dev web e2e install` for targeted setup or recovery.
Tests use synthetic checkout fixtures and require no storefront config.
Extra arguments are forwarded to Playwright, for example:

dev web e2e --headed
dev web e2e --ui
syntax:
optional: "[playwright test args]"
run: |
set -e
/opt/dev/bin/dev web build
pnpm --dir platforms/web/test/e2e typecheck
pnpm --dir platforms/web/test/e2e test "$@"
subcommands:
install:
desc: Install or repair Playwright Chromium and its system dependencies
run: pnpm --dir platforms/web/test/e2e exec playwright install --with-deps chromium
report:
desc: Open the last Playwright HTML report
run: pnpm --dir platforms/web/test/e2e report

snapshot:
desc: Manage snapshots for packaged contents
run: |
Expand Down
5 changes: 5 additions & 0 deletions e2e/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Checkout Kit End-to-End Tests

The web platform has a
[Playwright browser suite](../platforms/web/test/e2e/README.md) against the built
package with synthetic checkout fixtures. Run it with `dev web e2e`; it needs no
storefront configuration. The sections below describe the native Maestro suites.

This directory contains Maestro end-to-end flows and configuration for Checkout
Kit sample apps. Two complementary setups live here:

Expand Down
8 changes: 4 additions & 4 deletions platforms/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,10 @@
"test:watch": "vitest",
"lint": "pnpm run typecheck && pnpm run sample:typecheck && pnpm run lint:dependencies && pnpm run lint:js && pnpm run format:check",
"lint:dependencies": "depcruise --config dependency-cruiser.config.mjs src",
"lint:js": "oxlint --report-unused-disable-directives --max-warnings 0 src sample",
"lint:js:fix": "oxlint --fix src sample",
"format": "oxfmt src sample dependency-cruiser.config.mjs '!sample/dist/**'",
"format:check": "oxfmt --check src sample dependency-cruiser.config.mjs '!sample/dist/**'",
"lint:js": "oxlint --report-unused-disable-directives --max-warnings 0 src sample && pnpm --dir test/e2e lint",
"lint:js:fix": "oxlint --fix src sample && pnpm --dir test/e2e lint:fix",
"format": "oxfmt src sample dependency-cruiser.config.mjs '!sample/dist/**' && pnpm --dir test/e2e format",
"format:check": "oxfmt --check src sample dependency-cruiser.config.mjs '!sample/dist/**' && pnpm --dir test/e2e format:check",
"typecheck": "tsc --noEmit",
"sample": "vite --config sample/vite.config.ts",
"sample:build": "vite build --config sample/vite.config.ts",
Expand Down
46 changes: 46 additions & 0 deletions platforms/web/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions platforms/web/pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
packages:
- 'test/e2e'
- '../../protocol/languages/typescript'
- '../../telemetry/languages/typescript'
3 changes: 3 additions & 0 deletions platforms/web/test/e2e/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
test-results/
playwright-report/
playwright/.cache/
45 changes: 45 additions & 0 deletions platforms/web/test/e2e/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Web browser tests

Playwright loads the built `platforms/web/dist/index.js` in Chromium and exercises
the real custom element, popup, and cross-origin `postMessage` exchange. A routed
HTTPS checkout fixture supplies synthetic protocol messages; telemetry is disabled
and unexpected external requests are blocked and fail the test. No storefront,
tokens, or `.env` values are needed to run the suite.

## Run

From the repository root:

```sh
dev up # Install dependencies and the matching Chromium browsers
dev web e2e # Build, typecheck the tests, then run Chromium
dev web e2e --headed
dev web e2e --ui
dev web e2e report
```

The package is a member of the web pnpm workspace and uses
`platforms/web/pnpm-lock.yaml`. For a web-only setup, install dependencies with
`pnpm --dir platforms/web install --frozen-lockfile`, then run `dev web e2e install`.
That command also provides targeted browser setup or recovery, including system
dependencies on Linux. After Playwright updates, rerun `dev up` or
`dev web e2e install` to install the matching browsers. Existing browser installs
are reused when they match the installed Playwright version.
Once the package is built, `pnpm --dir platforms/web/test/e2e test` runs without rebuilding.
`dev web check` also includes the browser suite; `dev web format` formats its code.

The fixture server binds to `127.0.0.1:4321`. Set `WEB_E2E_PORT` to use another port.
Each run starts its own server and each test gets an isolated browser context.
Failures retain traces and screenshots in `test-results/`; the HTML report is in
`playwright-report/`. Web CI uploads both as the `web-playwright` artifact.

## Scope and layout

The first test covers opening checkout, the `ec.ready` handshake, public `start`
and `complete` events and snapshots, and host-driven closing. It tests the built
SDK's browser integration; it does not place a real order or exercise checkout-web.

- `server.mjs` serves the host page and built bundle.
- `fixtures/` contains the host page and synthetic checkout.
- `support/fixtures.ts` installs checkout routing and the network guard.
- `tests/checkout.spec.ts` drives the happy path through browser interactions.
23 changes: 23 additions & 0 deletions platforms/web/test/e2e/fixtures/host.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Checkout Kit browser test</title>
</head>
<body>
<shopify-checkout id="checkout" target="popup" telemetry="false"></shopify-checkout>
<button id="buy" type="button">Buy</button>
<script type="module">
import "/dist/index.js";

const checkout = document.getElementById("checkout");
window.checkoutEvents = [];
for (const type of ["start", "update", "complete", "error", "close"]) {
checkout.addEventListener(type, (event) => {
window.checkoutEvents.push({ type, detail: event.detail ?? null });
});
}
document.getElementById("buy").addEventListener("click", () => checkout.open());
</script>
</body>
</html>
77 changes: 77 additions & 0 deletions platforms/web/test/e2e/fixtures/synthetic-checkout.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Synthetic checkout</title>
</head>
<body>
<p id="status">Waiting for handshake</p>
<button id="start" type="button" disabled>Start checkout</button>
<button id="complete" type="button" disabled>Complete checkout</button>
<script>
const hostOrigin = new URL(location.href).searchParams.get("host_origin");
const readyId = "ready-1";
let protocolVersion;

window.addEventListener("message", (event) => {
if (event.source !== window.opener || event.origin !== hostOrigin) return;
const response = event.data;
if (response?.jsonrpc !== "2.0" || response.id !== readyId) return;
if (response.result?.ucp?.status !== "success") return;
protocolVersion = response.result.ucp.version;
document.getElementById("status").textContent = "Ready";
document.getElementById("start").disabled = false;
});

// Use the negotiated version rather than coupling the harness to a
// particular SDK release. The payload remains a synthetic wire fixture.
function checkout(status) {
return {
ucp: { version: protocolVersion, payment_handlers: {} },
id: "checkout_test_1",
currency: "USD",
status,
line_items: [
{
id: "li_1",
item: { id: "variant_1", title: "Test Product", price: 2000 },
quantity: 1,
totals: [{ type: "total", amount: 2000 }],
},
],
totals: [{ type: "total", amount: 2000 }],
links: [],
};
}

function notify(method, resource) {
window.opener.postMessage(
{ jsonrpc: "2.0", method, params: { checkout: resource } },
hostOrigin,
);
}

document.getElementById("start").addEventListener("click", () => {
notify("ec.start", checkout("incomplete"));
document.getElementById("start").disabled = true;
document.getElementById("complete").disabled = false;
});

document.getElementById("complete").addEventListener("click", () => {
notify("ec.complete", {
...checkout("completed"),
order: {
id: "order_test_1",
permalink_url: "https://checkout.example.test/orders/order_test_1",
},
});
document.getElementById("complete").disabled = true;
});

window.opener.postMessage(
{ jsonrpc: "2.0", id: readyId, method: "ec.ready", params: { delegate: ["window.open"] } },
hostOrigin,
);
</script>
</body>
</html>
23 changes: 23 additions & 0 deletions platforms/web/test/e2e/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"name": "@shopify/checkout-kit-web-e2e",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"test": "playwright test",
"typecheck": "tsc --noEmit",
"lint": "oxlint --config ../../.oxlintrc.json --report-unused-disable-directives --max-warnings 0 support tests playwright.config.ts server.mjs",
"lint:fix": "pnpm lint --fix",
"format": "oxfmt support tests fixtures playwright.config.ts server.mjs package.json tsconfig.json",
"format:check": "pnpm format --check",
"report": "playwright show-report"
},
"devDependencies": {
"@playwright/test": "^1.61.1",
"@shopify/checkout-kit": "workspace:*",
"@types/node": "^22.10.0",
"oxfmt": "^0.47.0",
"oxlint": "^1.62.0",
"typescript": "5.9.3"
}
}
Loading
Loading