如何在Playwright中使用Supabase持久化会话?测试认证页面报错
解决Playwright E2E测试中Supabase会话持久化与Setup脚本执行问题
一、Supabase会话未持久化(Cookie未设置)的解决方法
1. 确保登录后等待会话完全落地
页面操作登录后,不要立刻保存状态,需等待Supabase会话初始化完成,避免Cookie还未写入就被存储:
// auth.setup.ts import { test as setup } from '@playwright/test'; setup('完成登录并保存状态', async ({ browser }) => { const context = await browser.newContext(); const page = await context.newPage(); // 执行登录操作 await page.goto('/login'); await page.fill('#email', 'test@example.com'); await page.fill('#password', 'your-test-password'); await page.click('#submit-btn'); // 等待会话确认:可通过页面元素或直接调用Supabase API验证 await page.waitForSelector('#user-profile-avatar'); // 等待登录后出现的认证元素 // 或者直接在浏览器上下文里校验会话 const hasValidSession = await page.evaluate(async () => { const { supabase } = window; const { data } = await supabase.auth.getSession(); return !!data.session; }); if (!hasValidSession) throw new Error('登录后未获取到有效Supabase会话'); // 保存状态到文件 await context.storageState({ path: 'playwright/.auth/user-state.json' }); await context.close(); });
2. 正确加载存储状态
确保在创建浏览器上下文时加载状态,而非页面创建后:
// test.spec.ts import { test } from '@playwright/test'; // 全局应用存储状态 test.use({ storageState: 'playwright/.auth/user-state.json' }); test('访问认证专属页面', async ({ page }) => { await page.goto('/dashboard'); await expect(page.locator('#dashboard-welcome')).toBeVisible(); });
3. 校验Supabase Cookie配置
确认Supabase的会话Cookie(sb-access-token、sb-refresh-token)未被标记为跨域且域名与测试环境一致,Playwright默认会捕获所有HttpOnly Cookie,只要上下文域名匹配即可。
二、Setup脚本不再每次测试前执行的解决方法
1. 区分「一次性前置Setup」和「每个测试前置Setup」
- 一次性前置:如果只需要在所有测试前执行一次登录,在
playwright.config.ts中配置项目依赖:
// playwright.config.ts import { defineConfig } from '@playwright/test'; export default defineConfig({ projects: [ { name: 'auth-setup', testMatch: /auth\.setup\.ts/, }, { name: 'chromium', use: { storageState: 'playwright/.auth/user-state.json' }, dependencies: ['auth-setup'], // 依赖Setup项目,仅执行一次 }, ], });
- 每个测试前置:若需要每个测试用例都重新登录,将登录逻辑放入
test.beforeEach:
// test.spec.ts import { test } from '@playwright/test'; test.beforeEach(async ({ browser }) => { const context = await browser.newContext(); const page = await context.newPage(); // 重复登录逻辑 await page.goto('/login'); await page.fill('#email', 'test@example.com'); await page.fill('#password', 'your-test-password'); await page.click('#submit-btn'); await page.waitForSelector('#user-profile-avatar'); // 保存临时状态供当前测试用例使用 await context.storageState({ path: 'playwright/.auth/temp-state.json' }); await context.close(); }); test('测试1', async ({ page }) => { await page.goto('/dashboard'); // ...测试逻辑 });
2. Node端Supabase客户端登录的正确姿势
如果用Node端@supabase/supabase-js创建客户端,需手动将会话Cookie注入浏览器上下文,避免和浏览器端会话脱节:
// auth.setup.ts import { test as setup } from '@playwright/test'; import { createClient } from '@supabase/supabase-js'; setup('通过Supabase API登录并设置Cookie', async ({ browser }) => { const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_ANON_KEY!); const { data, error } = await supabase.auth.signInWithPassword({ email: 'test@example.com', password: 'your-test-password', }); if (error) throw error; const context = await browser.newContext(); const supabaseDomain = new URL(process.env.SUPABASE_URL!).hostname; // 手动注入Supabase会话Cookie await context.addCookies([ { name: 'sb-access-token', value: data.session!.access_token, domain: supabaseDomain, path: '/', httpOnly: true, secure: process.env.NODE_ENV === 'production', }, { name: 'sb-refresh-token', value: data.session!.refresh_token, domain: supabaseDomain, path: '/', httpOnly: true, secure: process.env.NODE_ENV === 'production', }, ]); await context.storageState({ path: 'playwright/.auth/user-state.json' }); await context.close(); });
内容的提问来源于stack exchange,提问作者Juice
相关产品推荐
相关产品推荐

