GH自托管Runner中Playwright存储状态残留致测试失败问题咨询
问题:GitHub Actions自托管Runner中Playwright会话缓存导致重复登录失败
我正在测试一个采用Microsoft MFA强制登录的开发门户,用otpauth库处理MFA验证,同时通过Playwright的storage state功能实现一次登录复用会话,本地运行正常。但在组织的GitHub Actions自托管Runner环境中,首次测试正常,再次触发时Runner会缓存登录会话,导致测试因重复登录失败。
我有两个问题:
- 为何浏览器存储状态会在该Runner中被保留?
- 如何在会话已缓存时跳过登录步骤?
相关代码
playwright.config.ts
projects: [ { name: 'setup', testMatch: /.*\.setup\.ts/ }, { name: 'chromium', use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json', }, timeout: 60000, dependencies: ['setup'], }, { name: 'firefox', use: { ...devices['Desktop Firefox'], storageState: 'playwright/.auth/user.json', }, timeout: 60000, dependencies: ['setup'], }, ]
auth.setup.ts
import { test as setup, expect } from '@playwright/test'; import UtilLoginPage from '../pages/utilLoginPage'; import path from 'path'; const authFile = 'playwright/.auth/user.json' let loginPage: UtilLoginPage let getCredentials = async () => { const username = process.env.AUTOMATION_USER; const password = process.env.AUTOMATION_PASSWORD; if (!username || !password) { throw new Error('Environment variables AUTOMATION_USER and AUTOMATION_PASSWORD must be set'); } return { username, password } } setup('authenticate', async ({ page }) => { // if (existsSync(authFile)) { // console.log('Auth file already exists, skipping authentication setup'); // return; // } loginPage = new UtilLoginPage(page); await loginPage.open((await getCredentials()).username, (await getCredentials()).password); await page.context().storageState({ path: authFile }); });
补充说明:个人GH自托管Runner可手动删除playwright/.auth/user.json,无此问题,但组织的自托管Runner无法进行此操作。
解答
1. 浏览器存储状态保留的原因
- GitHub自托管Runner默认会复用工作目录,不同于官方托管Runner每次运行都是全新环境,自托管Runner的文件系统会在多次任务间保留,所以
playwright/.auth/user.json这个存储会话的文件不会自动消失。 - 组织的自托管Runner大概率没有配置工作目录自动清理规则,加上你没有权限手动删除该文件,导致旧会话文件一直存在,每次运行都会被读取。
2. 跳过登录步骤的实现方案
方案一:在setup脚本中检查并复用有效会话
恢复你注释掉的文件检查逻辑,同时建议添加会话有效性验证,避免使用过期会话:
修改后的auth.setup.ts:
import { test as setup, expect } from '@playwright/test'; import UtilLoginPage from '../pages/utilLoginPage'; import { existsSync } from 'fs'; // 引入fs模块 const authFile = 'playwright/.auth/user.json' let loginPage: UtilLoginPage let getCredentials = async () => { const username = process.env.AUTOMATION_USER; const password = process.env.AUTOMATION_PASSWORD; if (!username || !password) { throw new Error('Environment variables AUTOMATION_USER and AUTOMATION_PASSWORD must be set'); } return { username, password } } setup('authenticate', async ({ page }) => { // 检查auth文件是否存在 if (existsSync(authFile)) { console.log('Auth file already exists, checking session validity...'); // 尝试访问门户首页,验证是否已登录 await page.goto('/'); try { // 替换成你页面中标识登录状态的元素选择器,比如用户头像、用户名等 await expect(page.locator('#user-profile-icon')).toBeVisible({ timeout: 5000 }); console.log('Session is valid, skipping login'); return; } catch (e) { console.log('Existing session is invalid, re-authenticating...'); } } // 执行登录流程 loginPage = new UtilLoginPage(page); await loginPage.open((await getCredentials()).username, (await getCredentials()).password); await page.context().storageState({ path: authFile }); });
方案二:在GitHub Actions workflow中强制清理auth目录
如果组织Runner允许执行shell命令,可以在测试前添加清理步骤,确保每次运行都是全新的会话:
jobs: test: runs-on: self-hosted steps: - uses: actions/checkout@v4 # 清理auth目录,不存在则忽略错误 - name: Clean up auth storage run: rm -rf playwright/.auth || true - name: Install dependencies run: npm install - name: Run Playwright tests run: npx playwright test
内容的提问来源于stack exchange,提问作者maxim
相关产品推荐
相关产品推荐

