Skip to content
Closed
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
4 changes: 4 additions & 0 deletions .github/filters/platform-non-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ web:
- 'platforms/web/**'
- *exclude-markdown
- *exclude-docs-directory
webE2e:
- 'e2e/web/**'
- *exclude-markdown
- *exclude-docs-directory
protocol:
- 'protocol/**'
- *exclude-markdown
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
reactNative: ${{ steps.platform.outputs.reactNative == 'true' || steps.platform.outputs.swift == 'true' || steps.platform.outputs.android == 'true' || steps.platform.outputs.protocol == 'true' || steps.infra.outputs.reactNativeCommon == 'true' || steps.infra.outputs.reactNativeIos == 'true' || steps.infra.outputs.reactNativeAndroid == 'true' }}
reactNativeIos: ${{ steps.platform.outputs.reactNative == 'true' || steps.platform.outputs.swift == 'true' || steps.platform.outputs.protocol == 'true' || steps.infra.outputs.reactNativeCommon == 'true' || steps.infra.outputs.reactNativeIos == 'true' }}
reactNativeAndroid: ${{ steps.platform.outputs.reactNative == 'true' || steps.platform.outputs.android == 'true' || steps.platform.outputs.protocol == 'true' || steps.infra.outputs.reactNativeCommon == 'true' || steps.infra.outputs.reactNativeAndroid == 'true' }}
web: ${{ steps.platform.outputs.web == 'true' || steps.infra.outputs.web == 'true' }}
web: ${{ steps.platform.outputs.web == 'true' || steps.platform.outputs.webE2e == 'true' || steps.infra.outputs.web == 'true' }}
steps:
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3

Expand Down
20 changes: 19 additions & 1 deletion .github/workflows/web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,30 @@ jobs:
uses: ./.github/actions/setup
with:
node-version-file: platforms/web/package.json
cache-dependency-path: platforms/web/pnpm-lock.yaml
cache-dependency-path: |
platforms/web/pnpm-lock.yaml
e2e/web/pnpm-lock.yaml
package-json-file: platforms/web/package.json
working-directory: platforms/web

- name: Install web e2e dependencies
working-directory: e2e/web
run: pnpm install --frozen-lockfile

- name: Install Playwright Chromium
working-directory: e2e/web
run: pnpm exec playwright install --with-deps chromium

- name: Lint (typecheck + oxlint + oxfmt --check)
run: pnpm lint

- name: Test
run: pnpm test

- name: Typecheck web e2e tests
working-directory: e2e/web
run: pnpm typecheck

- name: Coverage comment
if: github.event_name == 'pull_request'
uses: MishaKav/jest-coverage-comment@fb83bcbaeb5ca467936175796f862a2992938833 # main
Expand All @@ -43,6 +57,10 @@ jobs:
- name: Build
run: pnpm build

- name: Web end-to-end tests
working-directory: e2e/web
run: pnpm exec playwright test

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

Expand Down
12 changes: 1 addition & 11 deletions Package.resolved

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

33 changes: 31 additions & 2 deletions dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ up:
- protocol
- platforms/react-native
- platforms/web
- e2e/web
- custom:
name: Install Playwright Chromium for web e2e
met?: |
cd e2e/web && pnpm exec node -e 'const fs = require("fs"); const {chromium} = require("@playwright/test"); if (!fs.existsSync(chromium.executablePath())) process.exit(1)'
meet: cd e2e/web && pnpm exec playwright install chromium
- custom:
name: Copy root env into worktree
met?: ./scripts/copy_worktree_env --check
Expand Down Expand Up @@ -655,12 +661,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, end-to-end 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 @@ -688,6 +694,29 @@ commands:
desc: Run Web package verification
run: cd platforms/web && pnpm verify

e2e:
desc: Run Playwright end-to-end tests against a built copy of the package
long_desc: |
Builds the @shopify/checkout-kit package, then runs the Playwright
suite under e2e/web against the built dist/index.js. Any extra
arguments are forwarded to `playwright test`.

dev web e2e Run the suite headless
dev web e2e --headed Run in a headed browser
dev web e2e --ui Open the Playwright UI runner
dev web e2e --grep "happy" Run only specs matching a pattern
dev web e2e report Open the last HTML report
syntax:
optional: "[playwright test args]"
run: |
set -e
/opt/dev/bin/dev web build
cd e2e/web && pnpm exec playwright test "$@"
subcommands:
report:
desc: Open the last Playwright HTML report
run: cd e2e/web && pnpm report

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

This directory contains Maestro end-to-end smoke flows for Checkout Kit sample
apps.
This directory contains cross-platform end-to-end tests for Checkout Kit:

The current runnable suite starts with React Native. It verifies a full guest
checkout from a seeded cart in the sample app, through Shopify checkout, and
back to the app after completion.
- **React Native** — Maestro smoke flows against the sample app.
- **Web** — Playwright tests against a built copy of the `@shopify/checkout-kit`
package. See [`web/`](web/).

## Run locally
## React Native (Maestro)

The React Native suite verifies a full guest checkout from a seeded cart in the
sample app, through Shopify checkout, and back to the app after completion.

### Run locally

Run the matching command from the repo root.

Expand All @@ -31,17 +35,62 @@ sample app, then run Maestro. They require the standard storefront `.env` setup,
but the E2E flow seeds its own cart through the bootstrap deep link; no manual
sample cart setup is required.

## Files
### Files

- `config.yaml` configures Maestro for shared platform behavior.
- `flows/` contains reusable Maestro subflows for app setup and checkout steps.
- `tests/react-native/full-guest-checkout.yaml` composes the React Native guest
checkout smoke test from those subflows.

## Scope
### Scope

This smoke flow is intended to catch regressions in the React Native sample app
integration surface: cart bootstrap, checkout presentation, checkout
completion, and return to the sample app. It is not a replacement for
checkout-web's browser-based coverage or for future native Swift and Android
sample-app E2E coverage.

## Web (Playwright)

The web suite loads the built `dist/index.js` in a real Chromium browser and
drives the `<shopify-checkout>` custom element end to end. It is hermetic — the
embedded checkout is stubbed with Playwright network routing, so no storefront
`.env` or network access is required.

### Run locally

From the repo root:

```bash
dev web e2e # build the package, then run headless
dev web e2e --headed # run in a headed browser
dev web e2e --ui # open the Playwright UI runner
dev web e2e --grep "happy" # run only specs matching a pattern
dev web e2e report # open the last HTML report
```

`dev web e2e` rebuilds the package first, then forwards any extra arguments to
`playwright test`, so any Playwright flag works. `report` is the one exception:
it opens the last HTML report instead. `dev up` installs the `e2e/web`
dependencies and the Playwright Chromium browser.

### Layout

The web tests are a standalone package under [`web/`](web/) so they can pin
Playwright independently of the platform packages:

- `web/server.mjs` — zero-dependency static server that serves the test
fixtures and mounts the built `platforms/web/dist/` at `/dist/`.
- `web/fixtures/` — the host page that mounts the component and a synthetic
checkout page used to exercise the protocol handshake.
- `web/support/` — shared TypeScript types and checkout payload constants.
- `web/tests/synthetic/` — hermetic Playwright specs that use local host and
embedded-checkout fixtures (`presentation` + protocol handshake).

### Scope

These tests cover the web component's public surface against its built output:
custom-element registration, `src` handling and the overlay link, popup
open/close, and the embedded checkout protocol handshake (`ec.ready` →
`ec.start`/`ec.complete` and the unrecoverable `ec.error` path). They
complement, and do not replace, the unit tests in `platforms/web`.
4 changes: 4 additions & 0 deletions e2e/web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
test-results/
playwright-report/
playwright/.cache/
60 changes: 60 additions & 0 deletions e2e/web/fixtures/host.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Checkout Kit web e2e host</title>
<script type="module" src="/dist/index.js"></script>
</head>
<body>
<h1>Checkout Kit web e2e host</h1>
<shopify-checkout id="checkout"></shopify-checkout>
<button id="buy" type="button">Buy</button>

<script type="module">
const checkout = document.getElementById("checkout");
const events = [];

window.__ck = {
events,
get checkout() {
return checkout.checkout;
},
get error() {
return checkout.error;
},
configure({ src, target } = {}) {
if (src !== undefined) checkout.setAttribute("src", src);
if (target !== undefined) checkout.setAttribute("target", target);
},
open() {
checkout.open();
},
close() {
checkout.close();
},
};

const record = (type) => (event) => {
events.push({ type, detail: event.detail ?? null });
};

for (const type of [
"ec.ready",
"ec.start",
"ec.complete",
"ec.error",
"ec.close",
"ec.line_items.change",
"ec.totals.change",
"ec.messages.change",
]) {
checkout.addEventListener(type, record(type));
}

document.getElementById("buy").addEventListener("click", () => {
checkout.open();
});
</script>
</body>
</html>
Loading
Loading