You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Playwright实现一次性认证时报Cookie expires字段值非法错误

Playwright 会话存储复用偶发Cookie非法报错排查

报错信息

browser.newContext: Cookie should have a valid expires, only -1 or a positive number for the unix timestamp in seconds is allowed

    at C:\Users\v.shivarama.krishnan\source\repos\PlaywrightDemo\node_modules\@playwright\test\lib\index.js:595:23
    at Object.context [as fn] (C:\Users\v.shivarama.krishnan\source\repos\PlaywrightDemo\node_modules\@playwright\test\lib\index.js:642:15)

根因

该报错为两个问题叠加导致的偶发故障:

  • 存储状态导出时机错误:global-setup.ts中完成登录点击后,没有等待页面跳转完成、所有认证Cookie完全写入浏览器上下文,就立刻执行storageState导出。此时部分Cookie还处于初始化阶段,expires字段可能是空值、0或者毫秒格式时间戳(Playwright要求该字段必须是-1或者秒级Unix时间戳正数),生成的admin.json包含非法Cookie数据。后续测试加载这个损坏文件创建浏览器上下文时,就会触发参数校验报错。
  • API逻辑用反:测试用例中调用context.storageState({path: 'xxx'})的作用是将当前上下文的会话状态导出到文件,不是从文件加载已有登录态。在每个测试用例的全新空上下文(默认无任何Cookie数据)里执行该导出操作,会直接覆盖global-setup提前生成的有效会话文件,大幅提升损坏文件出现的概率。

另外贴出的Spec.ts代码中第一个测试用例缺失test函数声明,属于基础语法错误,运行时也会抛出异常。

修复方案

  1. 调整playwright.config.ts配置,在全局层级统一配置存储状态加载路径,不要在测试用例内手动处理会话加载/导出:
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
  globalSetup: require.resolve('./global-setup'),
  use: {
    // 所有测试默认加载global-setup生成的登录态
    storageState: 'storage/admin.json',
    baseURL: 'https://abc.com' // 替换为实际业务站点地址
  },
  // 其余原有配置保持不变
})
  1. 修正global-setup.ts逻辑,等待登录完全完成、所有Cookie落盘后再导出存储状态,同时替换不稳定的输入方法:
import { Browser, chromium, FullConfig } from '@playwright/test'

async function globalSetup(config: FullConfig) {
  const browser = await chromium.launch({
    headless: false
  });
  await saveStorage(browser, 'storage/admin.json')
  await browser.close()
}

async function saveStorage(browser: Browser, saveStoragePath: string) {
  const page = await browser.newPage()
  await page.goto('/');
  await page.waitForSelector("//input[@type='email']", { state: 'visible' });
  await page.locator("//input[@type='email']").fill('ABC@com'); // 用fill替代type,输入稳定性更高
  await page.locator("//input[@type='submit']").click();
  await page.locator("//input[@type='password']").fill('(&^%');
  await page.locator("//input[@type='submit']").click();
  await page.locator('#idSIButton9').click();

  // 关键修复:等待登录后业务页面的标识元素可见,确认登录流程完全走完
  await page.waitForSelector('#ebiLink', { state: 'visible', timeout: 15000 });
  // 加短等待兜底,确保跨域Cookie、过期时间字段全部写入完成
  await page.waitForTimeout(1000);
  await page.context().storageState({ path: saveStoragePath })
}

export default globalSetup
  1. 修正Spec.ts测试用例代码,删除错误的storageState导出调用,补全缺失的测试用例声明:
import { test, expect } from "@playwright/test";

test.describe('Launch Browser', () => {
    // 补全第一个测试用例的声明
    test('Verify interface search flow', async ({ page }) => {
        // 配置文件已自动加载登录态,无需手动处理会话
        await page.goto('/');
        await expect(page.locator('#ebiLink')).toBeVisible();

        const texts = await page.locator('#ebiLink').textContent();
        console.log("text of ebi is " + texts);

        await page.goto('/');

        await expect(page.locator('text= Detailed Interfaces ')).toBeVisible();

        await page.waitForSelector('#searchTab');
        await page.waitForSelector('#InterfaceCard');

        await page.locator('#searchTab').fill('VISHW-I7939');
        await page.locator("button[type='button']").click();
        await page.locator('#InterfaceCard').first().click();

        await expect(page.locator('#ngb-nav-0')).toBeVisible();

        const interfaceID = await page.locator("//span[@class='value-text']").first().allInnerTexts();
        console.log('interface id is' + interfaceID);

        await page.waitForSelector('role=listbox');
        await page.locator('role=listbox').click();
        await page.locator('role=option').nth(1).click();

        await expect(page.locator('#ngb-nav-0')).toBeVisible();
    });

    test('Move to shells screen', async ({ page }) => {
      await page.goto('/');
      await expect(page.locator('#ListHeader')).toBeVisible();

      const shells = await page.locator('#ListHeader').textContent();
      console.log('Text of shells header is '+shells);
    });
});
  1. 每次执行测试需要重新生成登录态前,先删除storage目录下旧的json文件,避免残留损坏文件被加载。

内容的提问来源于stack exchange,提问作者Vishwa nathan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 08:54:21