Cypress 在自定义命令中加载环境变量

J. *_*ers 5 javascript environment-variables add-custom-command cypress dotenv

我正在构建 Next.js 应用程序并使用 Cypress 编写我的测试。我在本地使用.env.local文件配置环境变量。在 CI 管道中,它们通常被定义。

我正在尝试在 Cypress 中编写一个自定义命令来加密cypress/support/command.ts.

import { encryptSession } from 'utils/sessions';

Cypress.Commands.add(
  'loginWithCookie',
  ({
    issuer = 'some-issuer',
    publicAddress = 'some-address',
    email = 'some-mail',
  } = {}) => {
    const session = { issuer, publicAddress, email };

    return encryptSession(session).then(token => {
      cy.setCookie('my-session-token', token);
      return session;
    });
  },
);
Run Code Online (Sandbox Code Playgroud)

当此命令运行时,它会失败,因为encryptSession使用了TOKEN_SECRET赛普拉斯未加载的环境变量。

import Iron from '@hapi/iron';

const TOKEN_SECRET = process.env.TOKEN_SECRET || '';

export function encryptSession(session: Record<string, unknown>) {
  return Iron.seal(session, TOKEN_SECRET, Iron.defaults);
}
Run Code Online (Sandbox Code Playgroud)

如何让赛普拉斯从该文件加载环境变量,如果它存在(= 仅在本地,因为变量是在 CI 中定义的 - 它应该正常检测管道中的其他变量,因此相当于检测具有已设置export MY_VAR=foo)?

小智 2

Cypress.envprocess.env ,但您想要设置看起来与 Cypress 版本不完全协调的令牌。

我知道任何带有CYPRESS_process.env前缀的密钥都会以结尾,但您想朝相反的方向走。Cypress.env()

我将使用一个任务来让您访问文件系统,并且process.env

/cypress/plugins/index.js

module.exports = (on, config) => {
  on('task', {
    checkEnvToken :() =>  {
      const contents = fs.readFileSync('.env.local', 'utf8'); // get the whole file
      const envVars = contents.split('\n').filter(v => v);    // split by lines 
                                                              // and remove blanks      
      envVars.forEach(v => {
        const [key, value] = v.trim().split('=');     // split the kv pair
        if (!process.env[key]) {                      // check if already set in CI
          process.env[key] = value;                        
        }
      })
      return null;                                    // required for a task
    },
  })
Run Code Online (Sandbox Code Playgroud)

在任何测试之前调用该任务,无论是在/cypress/support/index.js中,还是在before(), 或 在自定义命令中。

在自定义命令中

Cypress.Commands.add(
  'loginWithCookie',
  ({
    issuer = 'some-issuer',
    publicAddress = 'some-address',
    email = 'some-mail',
  } = {}) => {
    cy.task('checkEnvToken').then(() => {  // wait for task to finish 

      const session = { issuer, publicAddress, email };

      return encryptSession(session).then(token => {
        cy.setCookie('my-session-token', token);
          return session;
        });
    })
  });
Run Code Online (Sandbox Code Playgroud)

深入研究 的代码@hapi/iron,有一个crypto对 Node 库的调用,因此您可能需要将整个encryptSession(session)调用移至一个任务中才能使其工作。

import { encryptSession } from 'utils/sessions';

module.exports = (on, config) => {
  on('task', {
    encryptSession: (session) =>  {

      const contents = fs.readFileSync('.env.local', 'utf8'); // get the whole file
      const envVars = contents.split('\n').filter(v => v);    // split by lines 
                                                              // and remove blanks      
      envVars.forEach(v => {
        const [key, value] = v.trim().split('=');     // split the kv pair
        if (!process.env[key]) {                      // check if already set in CI
          process.env[key] = value;                        
        }
      })

      return encryptSession(session);                 // return the token
    },
  })
Run Code Online (Sandbox Code Playgroud)

致电

cy.task('encryptSession', { issuer, publicAddress, email })
  .then(token => {
    cy.setCookie('my-session-token', token);
  });
Run Code Online (Sandbox Code Playgroud)

在哪里运行上面的 cy.task

我想您只需要在每个测试会话中运行一次(以便为多个规范文件设置),在这种情况下,调用它的位置位于before()/cypress/support/index.js

把它放在那里的缺点是它有点隐藏,所以我个人会把它放在before()每个规范文件顶部的 a 中。

时间开销很小,fs.readFileSync但与等待页面加载等相比,这是最小的。