Cypress 在自定义命令中加载环境变量
Posted
技术标签:
【中文标题】Cypress 在自定义命令中加载环境变量【英文标题】:Cypress load environment variables in custom commands 【发布时间】:2021-05-10 20:46:54 【问题描述】:我正在构建一个 Next.js 应用程序并使用 Cypress 编写我的测试。我在本地使用.env.local
file 配置环境变量。在 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;
);
,
);
当此命令运行时,它会失败,因为 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);
如何让赛普拉斯从该文件加载环境变量,如果它存在(= 仅在本地,因为变量是在 CI 中定义的 - 它应该正常检测管道中的其他变量,因此相当于检测export MY_VAR=foo
) 设置的变量?
【问题讨论】:
【参考方案1】:Steve 的回答实际上帮助我在 cypress/plugins/index.ts
中得到了这段代码。
import dotenv from 'dotenv';
dotenv.config( path: '.env.local' );
import encryptSession from 'utils/sessions';
/**
* @type Cypress.PluginConfig
*/
const pluginConfig: Cypress.PluginConfig = (on, config) =>
on('task',
encryptSession: (session:
issuer: string;
publicAddress: string;
email: string;
) => encryptSession(session),
);
;
export default pluginConfig;
然后在cypress/support/commands.ts
。
Cypress.Commands.add(
'loginWithCookie',
(
issuer = 'some-issuer',
publicAddress = 'some-address',
email = 'some-email',
= ) =>
const session = issuer, publicAddress, email ;
return cy.task<string>('encryptSession', session).then(token =>
return cy.setCookie('my-secret-token', token).then(() =>
return session;
);
);
,
);
【讨论】:
看起来不错 - 你能展示它如何适合(假定)插件//index.js 吗?【参考方案2】:有Cypress.env,但您想在process.env
上设置令牌,这看起来与赛普拉斯版本不完全协调。
我知道任何带有前缀为 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
,
)
在 /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;
);
)
);
深入研究@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
,
)
打电话给
cy.task('encryptSession', issuer, publicAddress, email )
.then(token =>
cy.setCookie('my-session-token', token);
);
在哪里运行上述 cy.task
我猜你只需要在每个测试会话中运行一次(以便为许多规范文件设置它),在这种情况下,调用它的位置在 /cypress/support/ 中的 before()
内index.js.
将它放在那里的缺点是它有点隐藏,所以我个人会将它放在每个规范文件顶部的 before()
中。
fs.readFileSync
的时间开销很小,但与等待页面加载等相比,它是最小的。
【讨论】:
谢谢,这看起来不错。我的问题是,这段代码会只运行一次,还是fs.readFileSync
和其余代码会在每次有人使用loginWithCookie
时运行(它调用cy.task('encryptSession' ...)
?如果它每次都运行,有没有办法避免这种情况或者那很好?
我在上面添加了关于运行任务的注释。
在support/index.ts
或.spec
文件中运行fs.readFileSync
会导致fs__WEBPACK_IMPORTED_MODULE_0___default.a.readFileSync is not a function
崩溃。
啊啊我明白了!所以你说的是在before()
钩子中运行cy.loginWithCookie
,而不是使用fs
设置环境变量的部分。抱歉,在我的第二条评论中,我想说我尝试将fs
部分放在pluginConfig
的(on, config)
和on('task', ...)
范围之间。
我认为正如你所说,使用before()
钩子很不幸,尽管该过程会运行多次。非常感谢您的帮助! ?以上是关于Cypress 在自定义命令中加载环境变量的主要内容,如果未能解决你的问题,请参考以下文章
在自定义 ASP.NET Core 配置提供程序中停止 SqlDependency