diff --git a/.github/workflows/tests-pr.yml b/.github/workflows/tests-pr.yml index 77868e965c2..c84a846895c 100644 --- a/.github/workflows/tests-pr.yml +++ b/.github/workflows/tests-pr.yml @@ -254,16 +254,6 @@ jobs: E2E_STORE_FQDN: ${{ secrets.E2E_STORE_FQDN }} E2E_ORG_ID: ${{ secrets.E2E_ORG_ID }} run: pnpm exec playwright test --shard ${{ matrix.shard }} - - name: Cleanup current-run E2E apps - if: ${{ always() }} - continue-on-error: true - 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-apps.ts --pattern "r${RUN_TOKEN}a${GITHUB_RUN_ATTEMPT}" - name: Upload Playwright report uses: actions/upload-artifact@v4 if: ${{ !cancelled() }} @@ -279,6 +269,41 @@ jobs: path: packages/e2e/test-results/ retention-days: 14 + e2e-cleanup: + name: 'Cleanup current-run E2E apps' + needs: e2e-tests + if: ${{ always() && needs.e2e-tests.result != 'skipped' }} + runs-on: ubuntu-latest + timeout-minutes: 10 + 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 }} + fetch-depth: 1 + - name: Setup deps + uses: ./.github/actions/setup-cli-deps + with: + node-version: ${{ env.PLAYWRIGHT_NODE_VERSION }} + - name: Install Playwright Chromium + run: pnpm exec playwright install chromium + working-directory: packages/e2e + - name: Prime cleanup browser auth state + env: + E2E_ACCOUNT_EMAIL: ${{ secrets.E2E_ACCOUNT_EMAIL }} + 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 apps + 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-apps.ts --pattern "r${RUN_TOKEN}a${GITHUB_RUN_ATTEMPT}" + type-diff: if: github.event.pull_request.head.repo.full_name == github.repository name: 'Type-diff' diff --git a/packages/e2e/scripts/prime-browser-auth.ts b/packages/e2e/scripts/prime-browser-auth.ts index a8d25d9e8f9..4f9821bc005 100644 --- a/packages/e2e/scripts/prime-browser-auth.ts +++ b/packages/e2e/scripts/prime-browser-auth.ts @@ -80,8 +80,13 @@ export async function primeBrowserAuthStorage(opts: PrimeBrowserAuthOptions = {} console.log('[prime-browser-auth] Logging in...') await completeLogin(page, 'https://accounts.shopify.com/lookup', email, password) - await visitAndHandleAccountPicker(page, 'https://admin.shopify.com/', email) - await visitAndHandleAccountPicker(page, `https://dev.shopify.com/dashboard/${orgId}/apps`, email) + await attemptVisitAndHandleAccountPicker(page, 'https://admin.shopify.com/', email, 'admin') + await attemptVisitAndHandleAccountPicker( + page, + `https://dev.shopify.com/dashboard/${orgId}/apps`, + email, + 'dev dashboard', + ) await context.storageState({path: storageStatePath}) console.log(`[prime-browser-auth] Browser storage state saved to ${storageStatePath}`) @@ -91,6 +96,17 @@ export async function primeBrowserAuthStorage(opts: PrimeBrowserAuthOptions = {} } } +async function attemptVisitAndHandleAccountPicker(page: Page, url: string, email: string, label: string) { + try { + await visitAndHandleAccountPicker(page, url, email) + // eslint-disable-next-line no-catch-all/no-catch-all + } catch (err) { + console.warn( + `[prime-browser-auth] Browser session prewarm for ${label} failed: ${err instanceof Error ? err.message : err}`, + ) + } +} + /** Navigate to a URL and dismiss the account picker if it appears. */ async function visitAndHandleAccountPicker(page: Page, url: string, email: string) { await page.goto(url, {waitUntil: 'domcontentloaded'}) diff --git a/packages/e2e/setup/global-auth.ts b/packages/e2e/setup/global-auth.ts index c27e67cf962..a0adee5461d 100644 --- a/packages/e2e/setup/global-auth.ts +++ b/packages/e2e/setup/global-auth.ts @@ -109,6 +109,8 @@ export default async function globalSetup() { 'X-Shopify-Loadtest-Bf8d22e7-120e-4b5b-906c-39ca9d5499a9': 'true', }, }) + context.setDefaultTimeout(BROWSER_TIMEOUT.max) + context.setDefaultNavigationTimeout(BROWSER_TIMEOUT.max) const page = await context.newPage() await completeLogin(page, urlMatch[0], email, password) @@ -119,9 +121,14 @@ export default async function globalSetup() { // (completeLogin only authenticates on accounts.shopify.com) const orgId = (process.env.E2E_ORG_ID ?? '').trim() if (orgId) { - await visitAndHandleAccountPicker(page, 'https://admin.shopify.com/', email) - await visitAndHandleAccountPicker(page, `https://dev.shopify.com/dashboard/${orgId}/apps`, email) - globalLog('auth', 'browser sessions established for admin + dev dashboard') + await attemptVisitAndHandleAccountPicker(page, 'https://admin.shopify.com/', email, 'admin') + await attemptVisitAndHandleAccountPicker( + page, + `https://dev.shopify.com/dashboard/${orgId}/apps`, + email, + 'dev dashboard', + ) + globalLog('auth', 'browser session prewarm attempted for admin + dev dashboard') } // Save browser cookies/storage so workers can reuse the session @@ -150,6 +157,15 @@ export default async function globalSetup() { globalLog('auth', `global setup done, config at ${xdgEnv.XDG_CONFIG_HOME}`) } +async function attemptVisitAndHandleAccountPicker(page: Page, url: string, email: string, label: string) { + try { + await visitAndHandleAccountPicker(page, url, email) + // eslint-disable-next-line no-catch-all/no-catch-all + } catch (err) { + globalLog('auth', `browser session prewarm for ${label} failed: ${err instanceof Error ? err.message : err}`) + } +} + /** Navigate to a URL and dismiss the account picker if it appears. */ async function visitAndHandleAccountPicker(page: Page, url: string, email: string) { await page.goto(url, {waitUntil: 'domcontentloaded'})