Files
supabase/e2e/studio/features/_global.setup.ts
Alaister Young 5950b6ca04 test(e2e/studio): stabilize flaky and TZ/OS-sensitive specs (#46039)
Backports a batch of e2e test stabilization fixes — each commit is
scoped to a single failure class and only touches `e2e/studio/` files.

**Changed:**
- **`_global.setup` — playwright-locks cleanup was dead code**: the
lock-cleanup block was at the bottom of `Global Setup`, but every branch
above it returns early — so it never ran. Tests that use
`withFileOnceSetup` (cron-jobs) would see a stale `setup.done.json`
marker from the previous run and silently skip their setup, leaving e.g.
`pg_cron` uninstalled and all 11 cron-jobs specs failing. Moved the
cleanup to before any early return.
- **filter-bar — Home key**: macOS Chromium doesn't honor a standalone
`Home` keypress inside text inputs (macOS routes "go to line start" via
`Cmd+ArrowLeft` / `Fn+ArrowLeft`). Tests that expected the cursor to
jump to position 0 silently kept the previous selection. Replaced with
`el.setSelectionRange(0, 0)` so the assertion runs against a known
cursor position on every OS.
- **filter-bar — date filters**: tests inserted rows with `CURRENT_DATE`
/ `NOW()` (postgres session TZ = UTC) and asserted with JS-local dates
from `getDateValue()`. Near midnight the two diverged and the filter
returned 0 rows. Switched the inserts to explicit `getDateValue()`
strings so insert and assert use the same calendar day.
- **queue-table-operations — `networkidle`**: Studio holds long-poll /
SSE connections (PostHog, realtime), so `page.reload({ waitUntil:
'networkidle' })` never resolves and timed out. Replaced with a targeted
`waitForTableToLoad` API waiter.
- **sql-editor — RLS smoke test**: a hard-coded table name
(`pw_rls_smoke_test`) collided across 3 parallel workers running against
the same db. Suffixed with `test.info().parallelIndex`.
- **table-editor — FK spec timeout**: `waitForApiResponseWithTimeout`
for `query?key=table-update` returns `null` on timeout (silent), then
the panel-close assertion fails. Bumped 15s → 30s to absorb
parallel-load latency.
- **table-editor / storage-helpers — URL encoding & redirect race**:
post-action URL assertions were over-specific (`%20` vs `+` encoding)
and the bucket-delete redirect could race other history updates. Relaxed
the regex to accept both encodings; asserting the row removal directly
is a more stable signal than the redirect URL.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Tests**
* Improved end-to-end determinism with explicit dates/timestamps and
stable cursor positioning
* Prevented parallel-test collisions by using unique identifiers for
resources
* Made page reloads and API waits more robust for long-lived connections
and increased timeouts
* Strengthened assertions to rely on stable UI signals instead of
transient navigation/network state
* Ensured test setup reliably cleans up temporary locks before any setup
steps run

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/supabase/supabase/pull/46039?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
2026-05-18 17:31:40 +08:00

135 lines
3.4 KiB
TypeScript

import fs from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import { test as setup } from '@playwright/test'
import dotenv from 'dotenv'
import { env } from '../env.config.js'
import { loginWithEmail } from '../scripts/login/email.js'
import { loginWithGithubWithRetry } from '../scripts/login/github.js'
import { setupProjectForTests } from '../scripts/setup-platform-tests.js'
/**
* Run any setup tasks for the tests.
* Catch errors and show useful messages.
*/
dotenv.config({
path: path.resolve(import.meta.dirname, '..', '.env.local'),
override: true,
})
const IS_PLATFORM = process.env.IS_PLATFORM
const doAuthentication = env.AUTHENTICATION
setup('Global Setup', async ({ page }) => {
console.log(`\n 🧪 Setting up test environment.
- Studio URL: ${env.STUDIO_URL}
- API URL: ${env.API_URL}
- Auth: ${doAuthentication ? 'enabled' : 'disabled'}
- Is Platform: ${IS_PLATFORM}
`)
// Cleanup once-per-file locks before any of the early returns below —
// tests using `withFileOnceSetup` rely on this running unconditionally.
const locksDirPath = path.join(os.tmpdir(), 'playwright-locks')
try {
await fs.access(locksDirPath)
await fs.rm(locksDirPath, { recursive: true, force: true })
} catch {
// Silently catch, no directory
}
/**
* Studio Check
*/
const studioUrl = env.STUDIO_URL
const apiUrl = env.API_URL
await page.goto(studioUrl).catch((err) => {
console.error(
`\n 🚨 Setup Error
Studio is not available at: ${studioUrl}
Please ensure:
1. Studio is running in the expected URL
2. You have proper network access
`
)
throw err
})
console.log(`\n ✅ Studio is running at ${studioUrl}`)
/**
* API Check
*/
await fetch(apiUrl).catch((err) => {
console.error(`\n 🚨 Setup Error
API is not available at: ${apiUrl}
Please ensure:
1. API is running in the expected URL
2. You have proper network access
To start API locally, run:
npm run dev:api`)
throw new Error('API is not available')
})
console.log(`\n ✅ API is running at ${apiUrl}`)
/**
* Setup Project for tests
*/
const projectRef = await setupProjectForTests()
process.env.PROJECT_REF = projectRef
env.PROJECT_REF = projectRef
/**
* Only run authentication if the environment requires it
*/
if (!doAuthentication) {
console.log(`\n 🔑 Skipping authentication for ${env.STUDIO_URL}`)
return
}
const { EMAIL, PASSWORD } = env
if (EMAIL && PASSWORD) {
console.log(`\n 🔑 Authenticating user with email and password`)
try {
await loginWithEmail(page, studioUrl, {
email: EMAIL,
password: PASSWORD,
})
console.log(`\n ✅ Successfully authenticated with email`)
return
} catch (err) {
console.error(`\n 🚨 Authentication failed with email/password`)
throw err
}
}
const { GITHUB_USER, GITHUB_PASS, GITHUB_TOTP } = env
if (GITHUB_USER && GITHUB_PASS && GITHUB_TOTP) {
console.log(`\n 🔑 Authenticating user with GitHub`)
try {
await loginWithGithubWithRetry({
page,
githubTotp: GITHUB_TOTP,
githubUser: GITHUB_USER,
githubPass: GITHUB_PASS,
supaDashboard: studioUrl,
})
console.log(`\n ✅ Successfully authenticated with GitHub`)
return
} catch (err) {
console.error(`\n 🚨 Authentication failed with GitHub`)
throw err
}
}
})