Skip to content

Commit 6fc572d

Browse files
Merge pull request #7162 from Shopify/psyw-0401-E2E-migrate-remaining-tests-to-use-new-infra
E2E: migrate tests to use new infra
2 parents b72644f + 38ed413 commit 6fc572d

24 files changed

Lines changed: 468 additions & 442 deletions

.github/workflows/tests-pr.yml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ jobs:
219219
name: 'E2E tests'
220220
if: github.event.pull_request.head.repo.full_name == github.repository
221221
runs-on: ubuntu-latest
222-
timeout-minutes: 15
222+
timeout-minutes: 30
223223
continue-on-error: true
224224
steps:
225225
- uses: actions/checkout@v3
@@ -241,11 +241,9 @@ jobs:
241241
- name: Run E2E tests
242242
working-directory: packages/e2e
243243
env:
244-
SHOPIFY_FLAG_CLIENT_ID: ${{ secrets.E2E_CLIENT_ID }}
245244
E2E_ACCOUNT_EMAIL: ${{ secrets.E2E_ACCOUNT_EMAIL }}
246245
E2E_ACCOUNT_PASSWORD: ${{ secrets.E2E_ACCOUNT_PASSWORD }}
247246
E2E_STORE_FQDN: ${{ secrets.E2E_STORE_FQDN }}
248-
E2E_SECONDARY_CLIENT_ID: ${{ secrets.E2E_SECONDARY_CLIENT_ID }}
249247
E2E_ORG_ID: ${{ secrets.E2E_ORG_ID }}
250248
run: npx playwright test
251249
- name: Upload Playwright report

packages/e2e/.env.example

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
# Required: Client ID of the primary test app (must be in the genghis account's org)
2-
# CI secret: E2E_CLIENT_ID
3-
SHOPIFY_FLAG_CLIENT_ID=
4-
51
# Required: Genghis account email for browser-based OAuth login
62
# CI secret: E2E_ACCOUNT_EMAIL
73
E2E_ACCOUNT_EMAIL=
@@ -14,6 +10,5 @@ E2E_ACCOUNT_PASSWORD=
1410
# CI secret: E2E_STORE_FQDN
1511
E2E_STORE_FQDN=
1612

17-
# Optional: Client ID of a secondary app for config link tests
18-
# CI secret: E2E_SECONDARY_CLIENT_ID
19-
E2E_SECONDARY_CLIENT_ID=
13+
# Required: dedicated e2e org ID
14+
E2E_ORG_ID=
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Invalid TOML: malformed syntax (missing closing quote)
2-
client_id = "__E2E_CLIENT_ID__"
2+
client_id = "1234567890"
33
name = "Bad Syntax App
44
application_url = "https://example.com"
55
embedded = true

packages/e2e/data/invalid-tomls/invalid-webhook.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Invalid TOML: bad webhook config (missing required uri)
2-
client_id = "__E2E_CLIENT_ID__"
2+
client_id = "1234567890"
33
name = "Invalid Webhook App"
44
application_url = "https://example.com"
55
embedded = true

packages/e2e/data/invalid-tomls/wrong-type.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Invalid TOML: wrong types for known fields
2-
client_id = "__E2E_CLIENT_ID__"
2+
client_id = "1234567890"
33
name = "Wrong Type App"
44
application_url = "https://example.com"
55
embedded = "not-a-boolean"

packages/e2e/data/valid-app/shopify.app.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Comprehensive shopify.app.toml for E2E regression testing
2-
# client_id is injected at runtime by the toml-app fixture
3-
client_id = "__E2E_CLIENT_ID__"
4-
name = "E2E TOML Regression Test"
2+
# client_id and name are patched at runtime via injectFixtureToml()
3+
client_id = "placeholder"
4+
name = "placeholder"
55
application_url = "https://example.com"
66
embedded = true
77

packages/e2e/helpers/browser-login.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import {BROWSER_TIMEOUT} from '../setup/constants.js'
12
import type {Page} from '@playwright/test'
23

34
/**
@@ -23,24 +24,50 @@ async function fillSensitive(page: Page, selector: string, value: string): Promi
2324
* Completes the Shopify OAuth login flow on a Playwright page.
2425
*/
2526
export async function completeLogin(page: Page, loginUrl: string, email: string, password: string): Promise<void> {
27+
// Disable WebAuthn so passkey/security key system dialogs never appear when headed
28+
const cdp = await page.context().newCDPSession(page)
29+
await cdp.send('WebAuthn.enable', {enableUI: false})
30+
2631
await page.goto(loginUrl)
2732

2833
try {
2934
// Fill in email
30-
await page.waitForSelector('input[name="account[email]"], input[type="email"]', {timeout: 60_000})
35+
await page.waitForSelector('input[name="account[email]"], input[type="email"]', {timeout: BROWSER_TIMEOUT.max})
3136
await fillSensitive(page, 'input[name="account[email]"], input[type="email"]', email)
3237
await page.locator('button[type="submit"]').first().click()
3338

39+
// Handle passkey prompt — navigate to password login if needed
40+
const passwordInput = page.locator('input[name="account[password]"], input[type="password"]')
41+
const differentMethodBtn = page.locator('text=Log in using a different method')
42+
43+
// Wait for either password field or passkey page
44+
await Promise.race([
45+
passwordInput.waitFor({timeout: BROWSER_TIMEOUT.max}),
46+
differentMethodBtn.waitFor({timeout: BROWSER_TIMEOUT.max}),
47+
]).catch(() => {})
48+
49+
// If passkey page shown, navigate to password login
50+
if (await differentMethodBtn.isVisible({timeout: BROWSER_TIMEOUT.short}).catch(() => false)) {
51+
await differentMethodBtn.click()
52+
await page.waitForTimeout(BROWSER_TIMEOUT.short)
53+
54+
const continueWithPassword = page.locator('text=Continue with password')
55+
if (await continueWithPassword.isVisible({timeout: BROWSER_TIMEOUT.medium}).catch(() => false)) {
56+
await continueWithPassword.click()
57+
await page.waitForTimeout(BROWSER_TIMEOUT.short)
58+
}
59+
}
60+
3461
// Fill in password
35-
await page.waitForSelector('input[name="account[password]"], input[type="password"]', {timeout: 60_000})
62+
await passwordInput.waitFor({timeout: BROWSER_TIMEOUT.max})
3663
await fillSensitive(page, 'input[name="account[password]"], input[type="password"]', password)
3764
await page.locator('button[type="submit"]').first().click()
3865

3966
// Handle any confirmation/approval page
40-
await page.waitForTimeout(3000)
67+
await page.waitForTimeout(BROWSER_TIMEOUT.medium)
4168
try {
4269
const btn = page.locator('button[type="submit"]').first()
43-
if (await btn.isVisible({timeout: 5000})) await btn.click()
70+
if (await btn.isVisible({timeout: BROWSER_TIMEOUT.long})) await btn.click()
4471
// eslint-disable-next-line no-catch-all/no-catch-all
4572
} catch (_error) {
4673
// No confirmation page — expected

packages/e2e/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
}
3030
},
3131
"devDependencies": {
32+
"@iarna/toml": "2.2.5",
3233
"@playwright/test": "^1.50.0",
34+
"@shopify/toml-patch": "0.3.0",
3335
"@types/node": "18.19.70",
3436
"dotenv": "16.4.7",
3537
"execa": "^7.2.0",

packages/e2e/playwright.config.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* eslint-disable line-comment-position */
2+
import {TEST_TIMEOUT} from './setup/constants.js'
23
import {config} from 'dotenv'
34
import {defineConfig} from '@playwright/test'
45

@@ -14,8 +15,8 @@ export default defineConfig({
1415
workers: 1,
1516
maxFailures: isCI ? 3 : 0, // Stop early in CI after 3 failures
1617
reporter: isCI ? [['html', {open: 'never'}], ['list']] : [['list']],
17-
timeout: 3 * 60 * 1000, // 3 minutes per test
18-
globalTimeout: 15 * 60 * 1000, // 15 minutes total
18+
timeout: TEST_TIMEOUT.default, // Heavy tests override via test.setTimeout()
19+
globalTimeout: 20 * 60 * 1000,
1920

2021
use: {
2122
trace: isCI ? 'on' : 'off',

0 commit comments

Comments
 (0)