在 Cypress 中跨测试保留 cookies / localStorage 会话

Preserve cookies / localStorage session across tests in Cypress

我想要 save/persist/preserve 由 cy.request() 设置的 cookie 或 localStorage 令牌,这样我就不必使用用于在每次测试时登录的自定义命令。这应该适用于存储在客户端本地存储中的 jwt(json 网络令牌)等令牌。

来自柏树 docs

对于持久化 cookie:默认情况下,赛普拉斯会在每次测试前自动清除所有 cookie,以防止状态累积。

您可以使用 Cypress.Cookies api:

配置要在测试中保留的特定 cookie
// now any cookie with the name 'session_id' will
// not be cleared before each test runs
Cypress.Cookies.defaults({
  preserve: "session_id"
})

注意:在 Cypress v5.0 之前,配置键是“whitelist”,而不是“preserve”。

持久化localStorage:它不是内置在ATM中,但你现在可以手动实现它,因为清除本地存储的方法公开公开为Cypress.LocalStorage.clear。

您可以备份此方法并根据发送的密钥覆盖它。

const clear = Cypress.LocalStorage.clear

Cypress.LocalStorage.clear = function (keys, ls, rs) {
  // do something with the keys here
  if (keys) {
    return clear.apply(this, arguments)
  }

}

要更新此线程,已经有更好的解决方案可用于保存 cookie(@bkucera);但现在有一种解决方法可以在测试之间保存和恢复本地存储(以备不时之需)。我最近遇到了这个问题;并发现此解决方案有效。

此解决方案是使用辅助命令并在测试中使用它们,

内部 - cypress/support/<some_command>.js

let LOCAL_STORAGE_MEMORY = {};

Cypress.Commands.add("saveLocalStorage", () => {
  Object.keys(localStorage).forEach(key => {
    LOCAL_STORAGE_MEMORY[key] = localStorage[key];
  });
});

Cypress.Commands.add("restoreLocalStorage", () => {
  Object.keys(LOCAL_STORAGE_MEMORY).forEach(key => {
    localStorage.setItem(key, LOCAL_STORAGE_MEMORY[key]);
  });
});

然后在测试中,

beforeEach(() => {
  cy.restoreLocalStorage();
});

afterEach(() => {
  cy.saveLocalStorage();
});

参考:https://github.com/cypress-io/cypress/issues/461#issuecomment-392070888

这是对我有用的解决方案:

 Cypress.LocalStorage.clear = function (keys, ls, rs) {
    return;
 before(() => {
    LocalStorage.clear();
    Login();
  })

Cypress 支持对 cookie 清除的控制:https://docs.cypress.io/api/cypress-api/cookies.html

您可以将自己的登录命令添加到 Cypress,并使用 cypress-localstorage-commands 包在测试之间保留 localStorage。

support/commands中:

import "cypress-localstorage-commands";

Cypress.Commands.add('loginAs', (UserEmail, UserPwd) => {
  cy.request({
    method: 'POST',
    url: "/loginWithToken",
    body: {
      user: {
        email: UserEmail,
        password: UserPwd,
      }
    }
  })
    .its('body')
    .then((body) => {
      cy.setLocalStorage("accessToken", body.accessToken);
      cy.setLocalStorage("refreshToken", body.refreshToken);
    });
});

在你的测试中:

describe("when user FOO is logged in", ()=> {
  before(() => {
    cy.loginAs("foo@foo.com", "fooPassword");
    cy.saveLocalStorage();
  });

  beforeEach(() => {
    cy.visit("/your-private-page");
    cy.restoreLocalStorage();
  });

  it('should exist accessToken in localStorage', () => {
    cy.getLocalStorage("accessToken").should("exist");
  });

  it('should exist refreshToken in localStorage', () => {
    cy.getLocalStorage("refreshToken").should("exist");
  });
});

为了保存 google 令牌 cookie,有一个名为的库 cypress-social-login
似乎有其他 OAuth 提供商作为一个里程碑。
它是由 cypress 团队推荐的,可以在 cypress 插件页面上找到。

https://github.com/lirantal/cypress-social-logins

This Cypress library makes it possible to perform third-party logins (think oauth) for services such as GitHub, Google or Facebook.

It does so by delegating the login process to a puppeteer flow that performs the login and returns the cookies for the application under test so they can be set by the calling Cypress flow for the duration of the test.

我可以看到使用白名单的建议。但它似乎在 cypress run 期间不起作用。 分别在 before() 和 beforeEach() 中尝试了以下方法:

Cypress.Cookies.defaults({
  whitelist: "token"
})

Cypress.Cookies.preserveOnce('token');

但 none 似乎有效。但是任何一种方法都可以在 cypress open 即 GUI 模式下正常工作。我有什么不足之处吗?

我不确定本地存储,但对于 cookie,我最终执行了以下操作以在测试之间存储所有 cookie 一次

beforeEach(function () {
  cy.getCookies().then(cookies => {
    const namesOfCookies = cookies.map(c => c.name)
    Cypress.Cookies.preserveOnce(...namesOfCookies)
  })
})

根据文档,Cypress.Cookies.defaults 将在之后的每个测试 运行 中保持更改。在我看来,这并不理想,因为这会增加测试套件的耦合度。

我在这个 Cypress 问题中添加了更强大的响应:https://github.com/cypress-io/cypress/issues/959#issuecomment-828077512

我知道这是一个老问题,但我想以任何一种方式分享我的解决方案,以备不时之需。