Skip to content

Use App Management API instead of browser for E2E teardown checks - #8316

Draft
isaacroldan wants to merge 5 commits into
mainfrom
isaac/e2e-teardown-via-api
Draft

Use App Management API instead of browser for E2E teardown checks#8316
isaacroldan wants to merge 5 commits into
mainfrom
isaac/e2e-teardown-via-api

Conversation

@isaacroldan

@isaacroldan isaacroldan commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What

Removes most Playwright usage from the E2E per-test teardown. The browser was driving the Dev Dashboard and store admin UI for work the App Management API answers directly:

  • App lookup: findAppOnDevDashboard paginated the dashboard UI by clicking next_cursor links. Replaced with appByKey (client_id from the app TOML or the app URL) plus an appsConnection title-search fallback. An API "not found" now authoritatively means "already deleted".
  • Install gating: the isStoreAppsEmpty page scrape (before store deletion) and the disabled-Delete-button probing (before app deletion, the source of the constant scrollIntoViewIfNeeded: Timeout 5000ms noise in CI logs) are both replaced by one installCount poll after uninstall.
  • Store uninstall: the browser click-through fallback is gone; the Admin API path from Uninstall E2E apps via the Admin API instead of the store admin UI #8309 is the only one in teardown. When appDir is missing the test failed before createApp, so there is nothing to uninstall.
  • App deletion stays in the browser (the API has no delete mutation) but now targets the app's numeric-id settings URL derived from the API lookup — the client-key URL form usually renders without the Delete button, which is why per-test app deletion has been failing and leaking work to the run-level cleanup.

Implementation notes:

  • setup/app-management-api.ts makes minimal raw GraphQL requests (same style as setup/admin-api.ts). The API resolves the request's organization only from specifically-named variables (organizationId, apiKey, appId) — verified against GraphQLOrganizationResolution in core.
  • The token comes from the worker's CLI session via cli-kit, fetched in a tsx subprocess (setup/get-app-management-token.ts): importing cli-kit's dist inside Playwright's transpiled harness crashes Node's require(esm) path on CI's Node version.

Out of scope, intentionally:

  • cleanup-apps.ts / cleanup-stores.ts keep their browser paths — org-wide sweeps have no local app dir to mint Admin API tokens from.
  • The browser login in global setup (the top E2E flake) needs a headless credential path from Identity and can't be fixed in this repo.

Testing

  • pnpm tsc --noEmit and eslint pass in packages/e2e.
  • Validated on this PR's own E2E runs by reading the teardown debug logs (local runs are blocked: accounts.shopify.com serves an hCaptcha challenge outside CI):
    • Store flow (runs 31604865780, 31606322986): 7/7 and 7/7 — Admin API uninstall → installCount gate → store deletion confirmed by CLI.
    • App lookup: 13/13 resolved via API after the apiKey/organizationId variable fixes; zero Cannot find a valid organization errors.
    • App deletion: key-form URLs went 2/13 while the same run's cleanup job deleted 11/11 via numeric URLs; the final commit switches teardown to numeric URLs — validated in run 31607645373 with 13/13 app deletions succeeding.

🤖 Generated with Claude Code

The E2E teardown drove the Dev Dashboard and store admin UI for work
the App Management API can answer directly:

- App lookup: replace the browser pagination in findAppOnDevDashboard
  with appByKey (client_id) and an appsConnection title search fallback.
- Install gating: replace the isStoreAppsEmpty page scrape and the
  disabled-Delete-button probing with installCount polling. The same
  check gates both store deletion and app deletion.
- Store uninstall: drop the browser click-through fallback; the Admin
  API path (from #8309) is now the only one in teardown.

The browser is only used for the final delete-app click, which has no
API mutation. The cleanup scripts keep their browser paths: org-wide
sweeps have no local app dir to mint Admin API tokens from.

API calls reuse the worker's CLI session via cli-kit, following the
cleanup-stores.ts pattern.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added the no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users. label Aug 12, 2026
isaacroldan and others added 3 commits August 12, 2026 15:57
Importing cli-kit's dist ESM inside Playwright's transpiled harness
crashes Node's require(esm) path on CI's Node version ("Unexpected
module status 3"). tsx's loader handles the interop — the same reason
the cleanup scripts import cli-kit under tsx without issues.

This also removes the process.env XDG mutation: the session dirs are
passed to the subprocess environment directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The App Management API resolves the request's organization from
specifically-named GraphQL variables (organizationId, apiKey, appId).
The lookup variable was named "key", so every teardown lookup failed
with 404 "Cannot find a valid organization" and app/store deletion was
skipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- appByKey now sends organizationId: without it a deleted app cannot
  resolve an organization and 404s instead of returning null.
- Client IDs are only taken from /apps/{segment} URL parts that are
  non-numeric; deploy output yields /apps/{numericAppId} URLs, which
  now fall back to a name search.
- The settings-page navigation clicks through the accounts.shopify.com
  account picker, which cold browser contexts bounce to — the main
  reason direct-URL app deletion has been failing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@isaacroldan
isaacroldan force-pushed the isaac/e2e-teardown-via-api branch from c6d8bce to 69bb1f6 Compare August 12, 2026 14:21
Comment thread packages/e2e/setup/app.ts
await page.waitForTimeout(BROWSER_TIMEOUT.medium)

await navigateToDashboard({browserPage: page, email, orgId: org})
if (!page.url().startsWith('https://accounts.shopify.com')) return
Settings pages under client-key URLs usually render without the Delete
button; the numeric-id form (what the dashboard links to and the CLI's
appDeepLink builds) works reliably — the run-level cleanup deletes
11/11 apps with it while key-form teardown deletes went 2/13. The app
GID from the API lookup provides the numeric id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog This PR doesn't include a changeset entry. Is an internal only change not relevant to end users.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants