CI环境下Cypress执行NextAuth OAuth登录测试时浏览器启动失败
我用NextAuth.js和Cypress.js做OAuth登录测试,本地Windows 11、Ubuntu WSL 2.5.9.0环境下用Electron浏览器一切正常,但在GitHub Actions的ubuntu-latest CI环境执行cypress run --browser electron时,其他测试都正常,唯独OAuth测试重试2次后失败,报错如下:
[cypress] 1) Signin login with OAuth [cypress] Sucessfully Signin with Github OAuth: [cypress] CypressError: `cy.task('GitHubSocialLogin')` failed with the following error: [cypress] [cypress] > Failed to launch the browser process! [cypress] [3255:3255:0728/010000.290723:FATAL:zygote_host_impl_linux.cc(126)] No usable sandbox! Update your kernel or see ... for more information on developing with the SUID sandbox. If you want to live dangerously and need an immediate workaround, you can try using --no-sandbox. [cypress] [0728/010000.300637:ERROR:file_io_posix.cc(145)] open /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq: No such file or directory (2) [cypress] [0728/010000.300685:ERROR:file_io_posix.cc(145)] open /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq: No such file or directory (2) [cypress] [cypress] TROUBLESHOOTING: ...
试过在before:browser:launch里加--no-sandbox、重装Chromium、切换Electron浏览器,都没解决。怀疑是Docker镜像的问题,求解决方案。
使用版本:
- NextAuth:
4.24.11 - Cypress:
14.5.2 - Cypress-Social-Logins:
1.14.2
相关配置:
cypress.config.js
e2e: { baseUrl: "http://localhost:3000", chromeWebSecurity: false, supportFile: "cypress/support/e2e.js", video: false, screenshotOnRunFailure: false, retries: { runMode: 2, }, browser: "chrome", setupNodeEvents: async (on, config) => { on("before:browser:launch", (browser = {}, launchOptions) => { launchOptions.preferences.webPreferences.nodeIntegration = true; launchOptions.preferences.webPreferences.contextIsolation = false; launchOptions.preferences.devTools = true; return launchOptions; }); on("task", { GitHubSocialLogin: GitHubSocialLogin, }); return config; }, }, });
tests.yaml
name: Automated Tests on: pull_request: jobs: cypress: name: Cypress Ubuntu runs-on: ubuntu-latest env: CYPRESS_GITHUB_USER: ${{ secrets.GHUB_OAUTH_USER }} CYPRESS_GITHUB_PASSWORD: ${{ secrets.GHUB_OAUTH_PASSWORD }} CYPRESS_COOKIE_NAME: "next-auth.session-token" CYPRESS_SITE_NAME: "http://localhost:3000/status" GHUB_ID: ${{ secrets.GHUB_ID }} GHUB_SECRET: ${{ secrets.GHUB_SECRET }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "lts/hydrogen" - run: npm ci - run: npm run concurrently -n next,cypress --hide next -k -s command-cypress \"next dev\" \"wait-on --timeout 60000 http://localhost:3000 && npm run test:e2e:run\"
signin.cy.js
describe("Signin login with OAuth", () => { before(() => { cy.waitForAllServices(); cy.clearDatabase(); cy.runPendingMigrations(); }); it("Sucessfully Signin with Github OAuth", () => { const username = Cypress.env("GITHUB_USER"); const password = Cypress.env("GITHUB_PASSWORD"); const loginUrl = Cypress.env("SITE_NAME"); const cookieName = Cypress.env("COOKIE_NAME"); cy.session("github-oauth", () => { const socialLoginOptions = { username, password, loginUrl, cookieName, headless: true, logs: false, isPopup: false, loginSelector: `form[action*="github"] button`, postLoginSelector: "h1", }; return cy .task("GitHubSocialLogin", socialLoginOptions) .then(({ cookies }) => { cy.then(() => { cookies.forEach((cookie) => { cy.setCookie(cookie.name, cookie.value, { domain: cookie.domain, path: cookie.path, secure: cookie.secure, httpOnly: cookie.httpOnly, }); }); }); }); }); cy.visit("/status"); cy.getCookie(cookieName).should("exist"); cy.url({ timeout: 10000 }).should("include", "/status"); }); });
你的问题核心是**cypress-social-logins插件内部启动的Puppeteer实例没有禁用沙箱**,而不是Cypress本身的浏览器配置问题。GitHub Actions的ubuntu-latest环境默认没有Chrome沙箱运行的权限,所以需要给插件的Puppeteer启动参数添加--no-sandbox。
1. 给Cypress-Social-Logins传递Puppeteer参数
直接在测试用例的socialLoginOptions里添加puppeteerOptions,指定禁用沙箱的参数:
// signin.cy.js中修改socialLoginOptions const socialLoginOptions = { username, password, loginUrl, cookieName, headless: true, logs: false, isPopup: false, loginSelector: `form[action*="github"] button`, postLoginSelector: "h1", // 添加这行,给Puppeteer传递启动参数 puppeteerOptions: { args: ['--no-sandbox', '--disable-setuid-sandbox'] } };
这会让插件内部的Puppeteer实例以无沙箱模式启动,适配GitHub Actions的环境。
2. 补充Cypress浏览器的沙箱配置(可选)
虽然不是本次问题的根源,但可以给Cypress本身的Chrome/Chromium浏览器也添加沙箱参数,避免其他测试出现类似问题:
// cypress.config.js中修改before:browser:launch on("before:browser:launch", (browser = {}, launchOptions) => { launchOptions.preferences.webPreferences.nodeIntegration = true; launchOptions.preferences.webPreferences.contextIsolation = false; launchOptions.preferences.devTools = true; // 给Chromium系浏览器添加沙箱参数 if (browser.family === 'chromium' && browser.name !== 'electron') { launchOptions.args.push('--no-sandbox'); launchOptions.args.push('--disable-setuid-sandbox'); } return launchOptions; });
3. 优化GitHub Actions环境依赖(可选)
在GitHub Actions的步骤中添加系统依赖安装,确保Chrome相关库齐全:
# tests.yaml中添加依赖安装步骤 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "lts/hydrogen" # 安装Chrome运行依赖 - run: | sudo apt-get update sudo apt-get install -y libnss3 libgconf-2-4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxfixes3 libxrandr2 libgbm1 libasound2 - run: npm ci - run: npm run concurrently -n next,cypress --hide next -k -s command-cypress "next dev" "wait-on --timeout 60000 http://localhost:3000 && npm run test:e2e:run"
4. 更可靠的替代方案:模拟NextAuth会话
如果不想依赖真实的OAuth登录流程,推荐直接模拟NextAuth的会话,跳过浏览器跳转,测试效率更高也更稳定:
// 在cypress/support/commands.js中添加自定义命令 Cypress.Commands.add('loginWithNextAuth', () => { cy.session('next-auth-session', () => { // 调用NextAuth的登录接口,或者直接生成测试用的session-token cy.request({ method: 'POST', url: '/api/auth/signin/github', // 根据你的NextAuth配置传递参数,比如测试账号的凭证 body: { // 具体参数参考NextAuth的API文档 } }).then((res) => { // 从响应头中提取session-token const sessionCookie = res.headers['set-cookie'].find(c => c.includes('next-auth.session-token')); if (!sessionCookie) throw new Error('Session token not found'); const sessionToken = sessionCookie.split('=')[1].split(';')[0]; // 设置cookie cy.setCookie('next-auth.session-token', sessionToken, { domain: 'localhost', path: '/', secure: false, httpOnly: true }); }); }); }); // 在测试用例中使用 it('Successfully access status page', () => { cy.loginWithNextAuth(); cy.visit('/status'); cy.getCookie('next-auth.session-token').should('exist'); cy.url().should('include', '/status'); });
内容的提问来源于stack exchange,提问作者Hond4

