Skip to content

E2E: Page Object pattern restructure #7267

@phyllis-sy-wu

Description

@phyllis-sy-wu

Problem/Feature

The E2E test infrastructure (packages/e2e/) has grown organically across multiple PRs. Several problems have emerged:

Duplicated browser automation logic:

  • Login + account picker flow is repeated in setup/auth.ts, setup/global-auth.ts, scripts/cleanup-apps.ts, scripts/cleanup-stores.ts
  • Dev Console dismissal is duplicated across setup/store.ts, setup/app.ts, scripts/cleanup-stores.ts
  • Browser creation (loadtest header, WebAuthn disable, storageState loading, default timeouts) is duplicated in setup/browser.ts, setup/global-auth.ts, scripts/cleanup-apps.ts, scripts/cleanup-stores.ts
  • PTY spawn + env filtering is identical in setup/cli.ts, setup/auth.ts, setup/global-auth.ts
  • App finding/deleting logic exists in both setup/app.ts and scripts/cleanup-apps.ts
  • Store uninstall/delete logic exists in both setup/store.ts and scripts/cleanup-stores.ts

No clear folder boundaries:

  • setup/ mixes Playwright fixtures (appTestFixture, storeTestFixture), browser page actions (uninstallAppFromStoreAdmin, deleteStoreFromAdmin), CLI helpers (createApp, deployApp), and lifecycle hooks (global-auth.ts)
  • helpers/ has only two files (browser-login.ts, strip-ansi.ts) that could live elsewhere
  • No separation between "how to interact with a page" and "how to wire up test lifecycle"

This makes it hard to:

  • Know where to add new browser automation (do I put it in setup/app.ts? scripts/cleanup.ts? A new file?)
  • Avoid duplication when a new script or test needs the same page interaction
  • Understand dependencies between files

Proposed Solution

Adopt the Page Object pattern (industry standard for Playwright/Selenium, used by admin-web) to establish clear dependency boundaries.

New folder structure:

packages/e2e/
  ├── pages/                          — Browser page objects (one per domain/page)
  │   ├── accounts.ts                 — accounts.shopify.com: login, passkey bypass, account picker
  │   ├── dev-dashboard-apps.ts       — dev.shopify.com/dashboard/.../apps: find apps, delete app, pagination
  │   ├── dev-dashboard-stores.ts     — dev.shopify.com/dashboard/.../stores: find stores
  │   ├── admin-store.ts              — admin.shopify.com/store/.../settings: uninstall app, dismiss Dev Console, delete store
  │   └── admin-store-create.ts       — admin.shopify.com/store-create/...: create store form (shadow DOM)
  │
  ├── fixtures/                       — Playwright lifecycle hooks (setup/teardown only)
  │   ├── env.ts                      — E2EEnv interface, envFixture, createIsolatedEnv
  │   ├── cli.ts                      — cliFixture, CLIProcess, SpawnedProcess
  │   ├── browser.ts                  — browserFixture + browser factory (single place for headers, timeouts, WebAuthn, storageState)
  │   ├── auth.ts                     — authFixture (copies global session or runs login)
  │   ├── global-auth.ts              — Playwright globalSetup (one-time auth + admin session)
  │   ├── app.ts                      — appTestFixture + CLI app helpers (createApp, deployApp, etc.)
  │   ├── store.ts                    — storeTestFixture + store name generation
  │   └── teardown.ts                 — teardownAll orchestrator
  │
  ├── utils/                          — Pure utilities, no Playwright/fixture dependency
  │   ├── constants.ts                — TEST_TIMEOUT, CLI_TIMEOUT, BROWSER_TIMEOUT
  │   ├── extension-templates.ts      — REQUIRED_EXTENSIONS, EXTRA_EXTENSIONS
  │   ├── logger.ts                   — createLogger(), e2eSection(), globalLog()
  │   ├── strip-ansi.ts               — stripAnsi()
  │   └── wait-for-text.ts            — waitForText()
  │
  ├── scripts/                        — Standalone CLI tools (not run by Playwright)
  │   ├── cleanup-apps.ts             — Bulk app cleanup (uses pages/ for browser actions)
  │   └── cleanup-stores.ts           — Bulk store cleanup (uses pages/ for browser actions)
  │
  ├── tests/                          — Test spec files
  └── data/                           — Static test fixture files (TOMLs, etc.)

Dependency flow:

tests → fixtures → pages → utils
scripts → pages → utils

No upward imports. pages/ never imports from fixtures/. utils/ never imports from anything except npm packages.

What each page object contains:

Page object Extracted from Key functions
pages/accounts.ts helpers/browser-login.ts, account picker logic in browser.ts/store.ts fillSensitive(), completeLogin(), handleAccountPicker()
pages/dev-dashboard-apps.ts setup/app.ts, scripts/cleanup-apps.ts navigateToDashboard(), findApps(), deleteApp()
pages/dev-dashboard-stores.ts scripts/cleanup-stores.ts findStores()
pages/admin-store.ts setup/store.ts, scripts/cleanup-stores.ts dismissDevConsole(), uninstallApp(), uninstallAllApps(), countInstalledApps(), deleteStore()
pages/admin-store-create.ts setup/store.ts createStore()

Additional cleanup included in this refactor:

  • Browser factory: All browser creation goes through one function in fixtures/browser.ts that handles the loadtest header, default timeouts, WebAuthn disable, and storageState loading (currently duplicated in 4 places)
  • PTY spawn consolidation: Identical spawnEnv construction in fixtures/cli.ts, fixtures/auth.ts, fixtures/global-auth.ts extracted to a shared helper
  • Login consolidation: completeLogin + account picker handling unified in pages/accounts.ts (currently repeated in 4 files)
  • Dev Console dismissal: Shared dismissDevConsole(page) in pages/admin-store.ts replaces 3 duplicate implementations
  • Delete legacy scripts: Remove scripts/create-test-apps.ts and scripts/cleanup-test-apps.ts (superseded by current cleanup scripts)
  • Rename setup/fixtures/: Aligns naming with Playwright conventions

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions