## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Tests, plus one small test hook in Studio. ## What is the current behavior? The Storage file explorer's move dialog was recently reworked: the free-text "Path to new directory" input was replaced with an embedded folder picker (folder browsing, bucket-wide folder search, a responsive breadcrumb, and a confirm button that targets the folder currently open). That work shipped with unit and component tests, but nothing exercises it end to end against a real bucket. ## What is the new behavior? New `e2e/studio/features/storage-move.spec.ts` with seven tests: | Test | What it covers | | --- | --- | | moves a file into a folder picked from the explorer | The core path: open the picker, click a folder, confirm, and assert the file left the root and landed in the destination | | offers folders only, never files, as destinations | Files are excluded from the listing entirely | | blocks confirming a move into the folder the file already sits in | The confirm button reports `aria-disabled` when the destination matches the source | | finds a nested folder by search and moves into it | Bucket-wide folder search, including the "`<folder>` in `<location>`" row label | | reports when a search matches no folders | The empty-search message instead of a blank list | | collapses the middle of a deep path into a breadcrumb dropdown | The responsive breadcrumb: bucket and the two deepest folders stay inline, the middle collapses, and picking a collapsed folder navigates to it | | walks back up the path with the up-one-level button | Disabled at the bucket root, and drops the deepest folder otherwise | Supporting changes: - `utils/storage/queries.ts` gains `uploadObject` and `seedBucket`. Storage has no standalone folders — a folder exists because an object sits under that prefix — so seeding a folder tree means uploading objects at the paths a test needs. Doing this through the API keeps setup off the UI, which is both faster and less flaky than clicking through "Create folder" for each level. - `utils/storage/client.ts` accepts a string body so object uploads can send raw content alongside the existing JSON requests. - `utils/storage-helpers.ts` gains `openMoveDialog` and `confirmMove`. - `MoveItemsFolderPicker.tsx` gains `data-testid="folder-picker-list"` on its list container. ## Additional context **Why the `data-testid`.** Once a path is deep enough for the breadcrumb to collapse, the breadcrumb renders crumb buttons whose accessible names are folder names — so `getByRole('button', { name: 'beta' })` scoped to the dialog can match either a folder row or a breadcrumb crumb depending on depth. Scoping row lookups to the list container removes that ambiguity. This follows the e2e guidance about adding explicit test hooks where a component lacks an unambiguous accessible name. **These tests have not been executed.** They were written against the merged implementation and verified as far as the environment allows: - `npx playwright test --list` collects all seven - `tsc --noEmit` is clean for the new spec and helpers (the pre-existing errors in `column-editor-types.spec.ts`, `table-editor.spec.ts`, and `wait-for-response-with-timeout.ts` are untouched) - Studio's unit and component tests (82) still pass, and typecheck, eslint, prettier, the lint ratchet, and knip are all clean The suite needs Docker to bring up the local Supabase stack, which wasn't available where this was authored, so a real run in CI is the first actual execution. Selectors were all read off the merged source rather than guessed, but timing assumptions in particular deserve attention on the first CI run. **One thing this surfaced, not fixed here.** The success toast reads `Successfully moved 1 files to docs` — it doesn't singularize. The tests assert on `/Successfully moved/` rather than the full string so they don't encode that, but it's worth a follow-up. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Q94G7pWso6vQn5FQz6TUns --- _Generated by [Claude Code](https://claude.ai/code/session_01Q94G7pWso6vQn5FQz6TUns)_ <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Tests** - Expanded end-to-end coverage for moving files between folders in Storage. - Validated folder selection, nested-folder search, empty search results, collapsed breadcrumbs, and navigation to parent folders. - Confirmed files are excluded from destination choices and moving to the current folder is prevented. - Added coverage for creating isolated test buckets, uploading fixture files, and confirming successful move operations. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <noreply@anthropic.com>
Supabase Studio E2E Tests
Set up
Prerequisites
For Self-Hosted Tests
- Nothing is required, running with IS_PLATFORM=false should run the tests locally with a self hosted docker container
For Platform Tests
- Create a platform account - You can authenticate using either:
- Email and password
- GitHub OAuth (requires TOTP 2FA)
- Create an organization on the platform, this can be done if run locally through
mise fullstack - Generate a Personal Access Token (PAT) for API access
- Configure the environment variables below (see Authentication section for details on email vs GitHub auth)
Configure Environment
Choose the appropriate example file based on your testing scenario:
For self-hosted tests:
cp .env.local.self-hosted.example .env.local
For platform tests with email authentication:
cp .env.local.email.example .env.local
For platform tests with GitHub authentication:
cp .env.local.github.example .env.local
Edit .env.local and set the appropriate values based on your test environment (see Environment Variables section below).
Install the playwright browser
⚠️ This should be done in the e2e/studio directory
cd e2e/studio
pnpm exec playwright install
Environment Variables
Configure your tests by setting the following environment variables in .env.local. We have examples of what required on self hosted and platform:
Core Configuration
STUDIO_URL: The URL where Studio is running (default:http://localhost:8082)API_URL: The Supabase API endpoint (default:https://localhost:8080)IS_PLATFORM: Set totruefor platform tests,falsefor self-hosted (default:false)- When
true: Tests run serially (1 worker) due to API rate limits - When
false: Tests run in parallel (5 workers)
- When
Authentication (Required for Platform Tests)
⚠️ Before running platform tests, you must create an account and organization on the platform you're testing.
Authentication is automatically enabled when either email/password OR GitHub credentials are configured.
Email Authentication
EMAIL: Your platform account emailPASSWORD: Your platform account passwordPROJECT_REF: Project reference (optional, will be auto-created if not provided)
When both EMAIL and PASSWORD are set, the tests will authenticate using email/password. HCaptcha is mocked during test setup. Note this only works on local and staging environments
GitHub Authentication
GITHUB_USER: Your GitHub usernameGITHUB_PASS: Your GitHub passwordGITHUB_TOTP: Your GitHub TOTP secret for 2FA (required, as GitHub enforces 2FA)
When GITHUB_USER, GITHUB_PASS, and GITHUB_TOTP are all set, the tests will authenticate using GitHub OAuth with TOTP-based 2FA. The authentication flow handles:
- Clicking "Sign In with GitHub" button
- Filling GitHub credentials
- Generating and submitting TOTP codes
- Handling GitHub authorization prompts
- Automatic retry on failure (up to 3 attempts)
Getting your GitHub TOTP secret:
When setting up 2FA on GitHub, you'll see a QR code. Click "enter this text code instead" to reveal the secret key. This is the value to use for GITHUB_TOTP.
Platform-Specific Variables (Required when IS_PLATFORM=true)
ORG_SLUG: Organization slug (default:default)SUPA_REGION: Supabase region (default:us-east-1)SUPA_PAT: Personal Access Token for API authentication (default:test)BRANCH_NAME: Name for the test branch/project (default:e2e-test-local)
Optional Variables
OPENAI_API_KEY: Required for the AI Assistant test (assistant.spec.ts). Without this variable, the assistant test will be skipped.VERCEL_AUTOMATION_BYPASS_SELFHOSTED_STUDIO: Bypass token for Vercel protection (default:false)
Setup Commands Based on Configuration
The test setup automatically runs different commands based on your environment:
- Platform + Localhost (
IS_PLATFORM=trueandSTUDIO_URL=localhost): Runspnpm run e2e:setup:platform - Platform + Remote (
IS_PLATFORM=trueand remoteSTUDIO_URL): No web server setup - Self-hosted (
IS_PLATFORM=false): Runspnpm run e2e:setup:selfhosted
Running the tests
Check the package.json for the available commands and environments.
pnpm run e2e
With Playwright UI:
pnpm run e2e -- --ui
Tips for development
- Read Playwright Best Practices
- Use
pnpm run e2e -- --uito get the playwright UI. - Add the tests in
examples/examples.tsto Cursor as context. - Add messages to expect statements to make them easier to debug.
Example:
await expect(page.getByRole('heading', { name: 'Logs', exact: true }), {
message: 'Logs heading should be visible',
}).toBeVisible()
- Use the test utility instead of playwrights test.
import { test } from '../utils/test'
- Use the PWDEBUG environment variable to debug the tests.
PWDEBUG=1 pnpm run e2e -- --ui
What should I test?
- Can the feature be navigated to?
- Does the feature load correctly?
- Can you do the actions (filtering, sorting, opening dialogs, etc)?
API Mocks
Read here: https://playwright.dev/docs/mock#mock-api-requests
Example:
await page.route(`*/**/logs.all*`, async (route) => {
await route.fulfill({ body: JSON.stringify(mockAPILogs) })
})