Skip to content
Open
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
16 changes: 12 additions & 4 deletions .github/workflows/tests-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -270,17 +270,17 @@ jobs:
retention-days: 14

e2e-cleanup:
name: 'Cleanup current-run E2E apps'
name: 'Cleanup current-run E2E resources'
needs: e2e-tests
if: ${{ always() && needs.e2e-tests.result != 'skipped' }}
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 20
continue-on-error: true
steps:
- uses: actions/checkout@v6
with:
repository: ${{ github.event.pull_request.head.repo.full_name || github.event.repository.full_name }}
ref: ${{ github.event.pull_request.head.ref || github.event.merge_group.head_ref }}
repository: ${{ github.repository }}
ref: ${{ github.sha }}
fetch-depth: 1
- name: Setup deps
uses: ./.github/actions/setup-cli-deps
Expand All @@ -295,6 +295,14 @@ jobs:
E2E_ACCOUNT_PASSWORD: ${{ secrets.E2E_ACCOUNT_PASSWORD }}
E2E_ORG_ID: ${{ secrets.E2E_ORG_ID }}
run: pnpm --filter e2e exec tsx scripts/prime-browser-auth.ts
- name: Cleanup current-run E2E stores
env:
E2E_ACCOUNT_EMAIL: ${{ secrets.E2E_ACCOUNT_EMAIL }}
E2E_ACCOUNT_PASSWORD: ${{ secrets.E2E_ACCOUNT_PASSWORD }}
E2E_ORG_ID: ${{ secrets.E2E_ORG_ID }}
run: |
RUN_TOKEN=$(node -e "process.stdout.write(BigInt(process.env.GITHUB_RUN_ID).toString(36))")
pnpm --filter e2e exec tsx scripts/cleanup-stores.ts --pattern "r${RUN_TOKEN}a${GITHUB_RUN_ATTEMPT}"
- name: Cleanup current-run E2E apps
env:
E2E_ACCOUNT_EMAIL: ${{ secrets.E2E_ACCOUNT_EMAIL }}
Expand Down
50 changes: 42 additions & 8 deletions packages/e2e/scripts/cleanup-stores.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import {config} from 'dotenv'
import * as path from 'path'
import * as fs from 'fs'
import {fileURLToPath} from 'url'
import {chromium} from '@playwright/test'
import {BROWSER_TIMEOUT} from '../setup/constants.js'
Expand Down Expand Up @@ -56,6 +57,28 @@ export interface CleanupStoresOptions {
headed?: boolean
/** Organization ID (default: from E2E_ORG_ID env) */
orgId?: string
/** Playwright browser storage state path (default: E2E_BROWSER_STATE_PATH or global-auth path) */
storageStatePath?: string
}

function isAccountsShopifyUrl(rawUrl: string): boolean {
try {
return new URL(rawUrl).hostname === 'accounts.shopify.com'
// eslint-disable-next-line no-catch-all/no-catch-all
} catch {
return false
}
}

function defaultStorageStatePath(): string {
const tmpBase = process.env.E2E_TEMP_DIR ?? path.resolve(__dirname, '../../../.e2e-tmp')
return path.join(tmpBase, 'global-auth', 'browser-storage-state.json')
}

function existingStorageStatePath(candidate?: string): string | undefined {
return [candidate, process.env.E2E_BROWSER_STATE_PATH, defaultStorageStatePath()].find(
(storageStatePath): storageStatePath is string => Boolean(storageStatePath && fs.existsSync(storageStatePath)),
)
}

export async function cleanupStores(opts: CleanupStoresOptions = {}): Promise<void> {
Expand All @@ -64,15 +87,16 @@ export async function cleanupStores(opts: CleanupStoresOptions = {}): Promise<vo
const orgId = opts.orgId ?? (process.env.E2E_ORG_ID ?? '').trim()
const email = process.env.E2E_ACCOUNT_EMAIL
const password = process.env.E2E_ACCOUNT_PASSWORD
const storageStatePath = existingStorageStatePath(opts.storageStatePath)

console.log('')
console.log(`[cleanup-stores] Mode: ${MODE_LABELS[mode]}`)
console.log(`[cleanup-stores] Org: ${orgId || '(not set)'}`)
console.log(`[cleanup-stores] Pattern: "${pattern}"`)
console.log('')

if (!email || !password) {
throw new Error('E2E_ACCOUNT_EMAIL and E2E_ACCOUNT_PASSWORD are required')
if (!storageStatePath && (!email || !password)) {
throw new Error('E2E_ACCOUNT_EMAIL and E2E_ACCOUNT_PASSWORD are required when no browser storage state is available')
}
if (!orgId) {
throw new Error('E2E_ORG_ID is required')
Expand All @@ -83,6 +107,7 @@ export async function cleanupStores(opts: CleanupStoresOptions = {}): Promise<vo
extraHTTPHeaders: {
'X-Shopify-Loadtest-Bf8d22e7-120e-4b5b-906c-39ca9d5499a9': 'true',
},
...(storageStatePath ? {storageState: storageStatePath} : {}),
})
context.setDefaultTimeout(BROWSER_TIMEOUT.max)
context.setDefaultNavigationTimeout(BROWSER_TIMEOUT.max)
Expand All @@ -92,19 +117,28 @@ export async function cleanupStores(opts: CleanupStoresOptions = {}): Promise<vo
const totalStart = Date.now()

try {
// Step 1: Log in
console.log('[cleanup-stores] Logging in...')
await completeLogin(page, 'https://accounts.shopify.com/lookup', email, password)
console.log('[cleanup-stores] Logged in successfully.')
// Step 1: Reuse Playwright's global auth storage when available; otherwise log in directly.
if (storageStatePath) {
console.log('[cleanup-stores] Reusing browser storage state.')
} else if (email && password) {
console.log('[cleanup-stores] Logging in...')
await completeLogin(page, 'https://accounts.shopify.com/lookup', email, password)
console.log('[cleanup-stores] Logged in successfully.')
}

// Step 2: Navigate to stores page and find matching stores
console.log('[cleanup-stores] Navigating to stores page...')
await page.goto(`https://dev.shopify.com/dashboard/${orgId}/stores`, {waitUntil: 'domcontentloaded'})
if (isAccountsShopifyUrl(page.url()) && email && password) {
console.log('[cleanup-stores] Browser storage state was not accepted; logging in...')
await completeLogin(page, page.url(), email, password)
await page.goto(`https://dev.shopify.com/dashboard/${orgId}/stores`, {waitUntil: 'domcontentloaded'})
}
await page.waitForTimeout(BROWSER_TIMEOUT.medium)

// Handle account picker
const accountButton = page.locator(`text=${email}`).first()
if (await accountButton.isVisible({timeout: BROWSER_TIMEOUT.long}).catch(() => false)) {
const accountButton = email ? page.locator(`text=${email}`).first() : undefined
if (accountButton && (await accountButton.isVisible({timeout: BROWSER_TIMEOUT.long}).catch(() => false))) {
await accountButton.click()
await page.waitForTimeout(BROWSER_TIMEOUT.medium)
}
Expand Down
Loading