Supabase Studio E2E 测试实战:基于 Playwright 的自托管与云端平台双模式配置指南
导读:Supabase Studio 拥有超过 30 个功能模块(表编辑器、SQL 编辑器、日志、存储、RLS 策略、定时任务等),其质量保障依赖一套可同时跑在自托管与云端平台上的端到端(E2E)测试体系。本文基于仓库中 e2e/studio/README.md 展开,系统讲解这套 Playwright 测试框架的安装配置、环境变量语义、双模式鉴权流程、运行方式与编写规范,让你能在本地或 CI 中快速复现、扩展 Studio 的 E2E 测试。
一、测试框架全景:从一个目录看懂 Studio 的 E2E 体系
Supabase 仓库将 Studio 的 E2E 测试集中在 e2e/studio 目录下,它与 Playwright 的官方约定略有差异,先理清整体结构再动手更高效:
| 目录 / 文件 | 作用 |
|---|---|
e2e/studio/playwright.config.ts |
Playwright 主配置:双模式(platform / self-hosted)下自动切换串并行、启动 webServer、浏览器参数与 reporter |
e2e/studio/playwright.merge.config.ts |
用于 CI 场景下合并多个 blob 测试报告的配置 |
e2e/studio/env.config.ts |
统一读取并规范化 .env.local 中全部环境变量,供配置与测试代码共享 |
e2e/studio/features/ |
全部测试用例(*.spec.ts)与全局 setup(_global.setup.ts) |
e2e/studio/scripts/ |
支撑脚本:setup-platform-tests.ts(平台建项目)、login/email.ts 与 login/github.ts(两种登录)、common/(平台 API 客户端等) |
e2e/studio/utils/ |
测试公共工具:自定义 test 实例、数据库/存储操作封装、剪贴板与调试辅助等 |
e2e/studio/package.json |
依赖(@playwright/test、@supabase/supabase-js、otpauth 等)与 e2e/e2e:ui 脚本 |
从 features 目录 的文件清单可以看出覆盖范围:table-editor.spec.ts、sql-editor.spec.ts、logs.spec.ts、storage.spec.ts、rls-policies.spec.ts、auth-users.spec.ts、database-webhooks.spec.ts、edge-functions.spec.ts、cron-jobs.spec.ts、wrappers.spec.ts、assistant.spec.ts(AI 助手,依赖 OpenAI API Key)、realtime-inspector.spec.ts、queue-integration.spec.ts 等。这基本覆盖了 Studio 仪表盘的主要用户旅程。
需要特别注意的是:这套测试跑在真实的 Supabase 环境之上(自托管 Docker 或平台真实项目),通过 UI 与后台 API 双通道操作数据,而不是用一层 mock 把后端完全替换掉。
二、环境准备与两种运行模式
2.1 两条测试路径:Self-Hosted 与 Platform
测试框架设计了两种截然不同的后端环境,由 IS_PLATFORM 环境变量区分:
- 自托管模式(Self-Hosted,
IS_PLATFORM=false):不需要准备任何账号。直接通过mise/ Docker 拉起本地的 Supabase 与 Studio 即可运行,测试会落在一个本地临时项目上。这是零成本、可并行、最适合日常开发的路径。 - 平台模式(Platform,
IS_PLATFORM=true):针对云端真实部署(如https://supabase.com/dashboard或 Vercel staging)的回归测试。必须准备平台账号、组织与 PAT,测试会通过 Management API 自动创建或复用项目(默认项目名e2e-test-local)。
2.2 Platform 模式的前置条件
README 明确列出三条步骤:
- 创建平台账号——二选一:
- 邮箱 + 密码注册;
- GitHub OAuth(GitHub 强制 2FA,需要 TOTP)。
- 创建组织(organization)——本地通过
mise fullstack拉起完整开发环境时即可在平台上建组织。 - 生成 Personal Access Token(PAT),供 Management API 调用鉴权。
对应源码可以在 scripts/setup-platform-tests.ts 中看到完整校验逻辑:只要 IS_PLATFORM=true,ORG_SLUG、SUPA_REGION、API_URL、SUPA_PAT 四个变量缺一不可,否则直接抛出明确的错误提示;随后通过 getProjectRef() 按 (orgSlug, region, projectName) 查找已有项目,找不到就用 PAT 调 Management API 新建。
2.3 安装 Playwright 浏览器
务必在 e2e/studio 目录内执行(README 用 ⚠️ 特别标注了这一点),避免装到错误的包上下文:
cd e2e/studio
pnpm exec playwright install
同时建议先安装依赖:在仓库根目录执行 pnpm install,确保 package.json 中声明的 @playwright/test、dotenv、otpauth、@supabase/supabase-js、@faker-js/faker 等依赖就位。其中 otpauth 正是后面 GitHub TOTP 登录环节生成动态验证码所用。
三、环境变量全解析:三种示例文件与每个变量的语义
框架用 dotenv 在 env.config.ts 中加载 e2e/studio/.env.local(override: true,优先于进程已有值),因此所有测试配置都收敛到这一个文件。仓库已提供三份模板,按场景复制:
# 自托管测试
cp .env.local.self-hosted.example .env.local
# 平台测试 + 邮箱认证
cp .env.local.email.example .env.local
# 平台测试 + GitHub 认证
cp .env.local.github.example .env.local
3.1 核心配置
| 变量 | 说明 | 默认值(见 env.config.ts) |
|---|---|---|
STUDIO_URL |
Studio 运行地址 | http://localhost:8082 |
API_URL |
Supabase API 端点(平台模式下为 Management API) | http://127.0.0.1:54321 |
IS_PLATFORM |
true 平台模式 / false 自托管 |
false |
IS_PLATFORM 不只是开关,它会直接改变运行策略,这一点在 playwright.config.ts 有硬性约束:
IS_PLATFORM=true:串行运行,仅 1 个 worker,因为平台 API 有速率限制(README 原文:"Tests run serially (1 worker) due to API rate limits");IS_PLATFORM=false:并行运行,本地 CI 场景配 3 个 worker。
注意 README 描述与配置文件存在轻微差异:README 提到自托管默认 5 workers,而当前仓库的 playwright.config.ts 写的是 workers: env.IS_PLATFORM ? 1 : 3。以仓库实际代码为准,自托管当前是 3 个 worker。
3.2 认证变量(平台测试必需)
认证是否启用是自动推导的,无需单独开关。逻辑见 env.config.ts:
- 设置了
EMAIL且PASSWORD→ 邮箱认证; - 设置了
GITHUB_USER、GITHUB_PASS、GITHUB_TOTP三者 → GitHub OAuth 认证; - 两者都未配全 → 不认证(自托管场景天然满足)。
邮箱认证:
EMAIL:平台账号邮箱;PASSWORD:平台账号密码;PROJECT_REF:项目引用 ID(可选;不填则自动创建)。- ⚠️ 局限:邮箱认证仅在 local 与 staging 环境可用,因为测试会 mock HCaptcha(见下文 4.2)。
GitHub 认证:
GITHUB_USER:GitHub 用户名;GITHUB_PASS:GitHub 密码;GITHUB_TOTP:GitHub 2FA 的 TOTP 密钥——注意它不是一次性验证码,而是密钥本身。
关于 TOTP 密钥的获取,README 有非常实用的提示:GitHub 配置 2FA 时页面会展示二维码,点击 “enter this text code instead” 即可看到明文 secret,把它填入 GITHUB_TOTP。
3.3 平台专用变量(IS_PLATFORM=true 时必需)
| 变量 | 说明 | 默认值 |
|---|---|---|
ORG_SLUG |
组织标识 | default |
SUPA_REGION |
项目部署区域 | us-east-1 |
SUPA_PAT |
Personal Access Token | test |
BRANCH_NAME |
测试项目名 | e2e-test-local |
3.4 可选变量
OPENAI_API_KEY:仅 assistant.spec.ts(AI Assistant 测试)需要;未设置时该用例会被跳过,不影响其它测试。VERCEL_AUTOMATION_BYPASS_SELFHOSTED_STUDIO:Vercel 部署保护(Protection Bypass)令牌,默认false。它会被注入请求头x-vercel-protection-bypass与x-vercel-set-bypass-cookie,用于直接访问被 Vercel 保护的自托管 Studio(见 playwright.config.ts)。
3.5 环境变量与启动命令的自动联动
.env.local 的取值组合决定了 Playwright webServer 的启动策略,README 中的表格在 playwright.config.ts 有完整实现:
| 场景 | 判断条件 | 动作 |
|---|---|---|
| 平台 + 本地 Studio | IS_PLATFORM=true 且 STUDIO_URL 含 localhost/127.0.0.1 |
自动执行 pnpm --workspace-root run e2e:setup:platform,等待 8082 端口就绪 |
| 平台 + 远端 Studio | IS_PLATFORM=true 且 URL 为远端(如 https://supabase.com/dashboard) |
不启动任何 webServer,直接测远端 |
| 自托管(本机开发) | 非 CI | 执行 pnpm --workspace-root run e2e:setup:selfhosted |
| 自托管(CI) | 非平台且 CI 已设置 |
执行 pnpm --workspace-root run e2e:setup:selfhosted:start-studio |
其中「是否本地」通过检查 STUDIO_URL 是否包含 localhost 或 127.0.0.1 判定(见 env.config.ts)。webServer 默认端口 8082、超时 10 分钟(均可用 WEB_SERVER_PORT / WEB_SERVER_TIMEOUT 覆盖),且 reuseExistingServer: true 允许复用已运行的 Studio。这里目录常被误读的点是:这三个根命令(e2e:setup:platform、e2e:setup:selfhosted 等)实际定义在仓库根 package.json,运行时以 workspace 方式从根目录触发。
四、全局 Setup 阶段发生了什么
正式用例执行前,Playwright 会先跑 features/_global.setup.ts(在 playwright.config.ts 中通过 dependencies: ['setup'] 声明,且项目 Features 会使用 setup 阶段保存的登录态 storageState)。它按顺序做四件事:
- 健康检查:打开
STUDIO_URL确认 Studio 活着;fetch(API_URL)确认后端活着。任一失败都会打印带排查建议的报错(例如提示本地 API 需运行npm run dev:api)。 - 清理临时锁:删除系统临时目录下的
playwright-locks(供once-per-file类工具使用)。 - 准备项目:调用
setupProjectForTests()——自托管直接返回default;平台模式则按(ORG_SLUG, SUPA_REGION, BRANCH_NAME)查找或创建项目,并把PROJECT_REF写回process.env供后续用例使用。 - 按需登录:仅当检测到认证变量才执行。登录成功后 Playwright 会保存
storageState到e2e/studio/playwright/.auth/user.json(STORAGE_STATE_PATH),让每个用例共享已认证的 Cookie/LocalStorage,避免重复登录。
4.1 GitHub OAuth + TOTP 自动化流程
scripts/login/github.ts 把 GitHub 登录的机械操作全部脚本化,README 描述的流程与代码一一对应:
- 点击 “Sign In with GitHub”;
- 填写用户名与密码;
- 用
otpauth库按 GitHub 规范生成 TOTP:algorithm: SHA1、digits: 6、period: 30(见 github.ts); - 处理 GitHub 授权确认页;
- 失败自动重试最多 3 次(
loginWithGithubWithRetry)。
代码里还藏着一个细节:为了绕开 auth.supabase.io 的 CORS 问题,登录脚本会 route 拦截 **/auth.supabase.io/auth/v1/user 返回 401,并处理对应 OPTIONS 预检请求——这也是 Playwright 自动化登录第三方 OAuth 时的常见套路。
4.2 邮箱登录与 HCaptcha 的 Mock
scripts/login/email.ts 在 page.addInitScript 中注入一个假的 window.hcaptcha(execute/render/reset 全部用官方测试 token 10000000-aaaa-bbbb-cccc-000000000001 桩替代),同时 route 拦截所有 hcaptcha.com 请求并返回自研 stub。README 中 "HCaptcha is mocked during test setup" 正是此实现,注释也点明了原因:HCaptcha 能识别自动化浏览器并阻止 Playwright,且自定义 Vercel 请求头会引发 CORS 问题。这也解释了为什么邮箱认证只在 local / staging 可用。
五、运行测试:命令行与报告
直接查看 package.json 的 scripts 即可确认可用命令:
# 标准运行(按 env 配置自动选择模式)
pnpm run e2e
# 带 Playwright UI(可视化查看用例、逐步调试)
pnpm run e2e -- --ui
# 等价命令
pnpm exec playwright test
pnpm exec playwright test --ui
若只希望本地跑通且不想碰平台,把 .env.local 配成自托管模板(IS_PLATFORM=false)再执行即可。
CI 与本地报告策略不同(playwright.config.ts):
- CI 下 reporter 为
list + blob(blob 报告供后续合并分析); - 本地为
list + html(不自动打开)+json(输出到test-results/test-results.json)。
其它内置策略值得留意:单测超时 120s、expect 超时 20s、maxFailures: 3(失败达 3 即熔断)、CI 下自动重试 5 次并开启 forbidOnly;视频仅在失败时保留(retain-on-failure),失败保留 trace。浏览器启动参数针对 CI/容器做了大量优化(--no-sandbox、--disable-dev-shm-usage、--headless=new、4GB V8 堆等),自托管测试因可能访问带自签证书的 localhost 还带上了 --allow-insecure-localhost(见 playwright.config.ts)。同时 contextOptions.reducedMotion: 'reduce' 减少动画干扰,并预设了剪贴板读写权限。
六、开发 Tips:写出可维护的用例
README 的 "Tips for development" 部分是团队沉淀的工程规范,逐条拆解如下:
- 研读 Playwright 官方最佳实践(
@playwright/test的稳定 API 与本文框架一致)。 - 善用 UI 模式:
pnpm run e2e -- --ui可逐步回放、分步调试用例。 - 把
examples/examples.ts作为上下文喂给 AI 辅助工具,让代码补全贴合本项目的操作范式。 - 给 expect 加 message,失败时一眼定位意图:
await expect(page.getByRole('heading', { name: 'Logs', exact: true }), {
message: 'Logs heading should be visible',
}).toBeVisible()
- 用仓库封装的
test而非 Playwright 原生test。自定义实例定义在 utils/test.ts,它通过test.extend注入env/ref/apiUrl固定变量,并在page上预置addInitScript自动写入若干本地存储标记(如关闭队列操作横幅、关闭 2026-08-01 服务条款弹窗),从而保证被测 UI 的稳定初始状态。同文件还导出了withSetupCleanup工具,配合await using语法即可在用例结束时自动执行清理(典型用法是建表测试后自动删表):
import { test } from '../utils/test'
await using _ = await withSetupCleanup(
() => createTableWithRLS('pw_table', 'pw_column'),
async () => {
await dropTable('pw_table')
}
)
- 用
PWDEBUG调试:
PWDEBUG=1 pnpm run e2e -- --ui
七、该测什么:用例设计的三条铁律
README 给出非常聚焦的提问式清单,回答 "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)?——核心操作可用吗?(筛选、排序、弹窗等交互闭环)
这套提问对应的正是 features 下每个 spec 的骨架:先导航进入目标页面,再断言关键 UI 就绪,最后执行代表性操作并验证结果。多个 spec 还演示了「测试的边界设计」:
env.IS_PLATFORM作为 skip 条件,例如 log-drains.spec.ts(平台不支持 Log Drains)与 monaco-graphiql-coexistence.spec.ts(仅自托管单项目场景成立)会在不合适的环境自动跳过;- data-api-type-generation.spec.ts 甚至在断言信息里写明这是自托管(
IS_PLATFORM=false)专属代码路径,避免后续维护者误改。
八、API Mocks:不需要真实后端时如何拦截
当某个接口不稳定、成本高或数据不可控时,README 建议使用 Playwright 的 page.route mock API 请求,官方文档见 Playwright Mocking 章节。仓库中大量用例印证了这一模式,最直接的例子来自 logs.spec.ts:
// 先用静态数据占位
const mockAPILogs = {
result: [
{ event_message: 'test message', ... }
],
// ...
}
// 拦截请求并返回 mock 数据
await page.route(`*/**/logs.all*`, async (route) => {
await route.fulfill({ body: JSON.stringify(mockAPILogs) })
})
// 之后照常断言 UI
await expect(page.getByText(mockAPILogs.result[0].event_message), {
message: 'Mocked log message should be rendered in the Logs table',
}).toBeVisible()
mock 的典型使用场景在仓库中还有不少佐证:edge-functions.spec.ts 拦截函数 CRUD 接口、enabled-features-overrides.spec.ts 拦截特性开关、cron-jobs.spec.ts 系列用 pg-meta/*/query mock 来稳定构造调度器状态。实践要点:
- 拦截粒度尽量精确(如
**/pg-meta/*/query**),避免误伤同前缀的真实请求; - mock 与真实后端混用时,给 mock 场景写
test.skip条件保持用例语义清晰; route.fulfill别忘了contentType与status,否则可能引发前端解析异常。
九、写在最后
理解 Supabase Studio E2E 测试体系的关键在于抓住「双模式 + 真实环境」这条主线:IS_PLATFORM 决定跑在本地 Docker 还是云端平台,env.config.ts 把散落的配置收敛成单一数据源,全局 setup 负责把项目与登录态准备好,utils/test.ts 则统一了用例的初始化。若想在仓库外复刻这套方案,最省力的路径是:复制自托管模板 → pnpm exec playwright install → pnpm run e2e;待基础跑通后再按 .env.local.email.example 与 .env.local.github.example 补齐平台账号与 PAT,把回归面扩展到真实的云端部署。更细的源码细节可在仓库中继续深入 playwright.config.ts、env.config.ts、_global.setup.ts 以及 features 下各个 spec 逐一研读。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00