如何解决使用Storybook和Cypress测试角度分量@Output
我正在尝试测试角度分量的输出。
我有一个复选框组件,该组件使用EventEmitter输出其值。复选框组件包装在一个故事书中,用于演示和测试:
export const basic = () => ({
moduleMetadata: {
imports: [InputCheckboxModule],},template: `
<div style="color: orange">
<checkbox (changeValue)="changeValue($event)" [selected]="checked" label="Awesome">
</checkbox>
</div>`,props: {
checked: boolean('checked',true),changeValue: action('Value Changed'),});
我正在使用一项操作来捕获值更改并将其记录到屏幕上。
但是,当为此组件编写赛普拉斯e2e时,我仅使用iFrame,而不是整个故事书应用程序。
我想找到一种测试输出是否正常的方法。我尝试对iFrame中的postMessage方法使用间谍,但这不起作用。
beforeEach(() => {
cy.visit('/iframe.html?id=inputcheckboxcomponent--basic',{
onBeforeLoad(win) {
cy.spy(window,'postMessage').as('postMessage');
},});
});
,那么断言将是:
cy.get('@postMessage').should('be.called');
还有其他方法可以断言(changeValue)="changeValue($event)"
开除了?
解决方法
方法 1:模板
我们可以将最后发出的值绑定到模板并检查它。
{
moduleMetadata: { imports: [InputCheckboxModule] },template: `
<checkbox (changeValue)="value = $event" [selected]="checked" label="Awesome">
</checkbox>
<div id="changeValue">{{ value }}</div> <!-- ❗️? -->
`,}
it("emits `changeValue`",() => {
// ...
cy.get("#changeValue").contains("true"); // ❗️?
});
方法 2:窗口
我们可以将最后发出的值分配给全局 window
对象,在 Cypress 中检索它并验证该值。
export default {
title: "InputCheckbox",component: InputCheckboxComponent,argTypes: {
selected: { type: "boolean",defaultValue: false },label: { type: "string",defaultValue: "Default label" },},} as Meta;
const Template: Story<InputCheckboxComponent> = (
args: InputCheckboxComponent
) =>
({
moduleMetadata: { imports: [InputCheckboxModule] },props: args,} as StoryFnAngularReturnType);
export const E2E = Template.bind({});
E2E.args = {
label: 'E2e label',selected: true,changeValue: value => (window.changeValue = value),// ❗️?
};
it("emits `changeValue`",() => {
// ...
cy.window().its("changeValue").should("equal",true); // ❗️?
});
方法 3:角度
我们可以使用存储在 ng
下的全局命名空间中的 Angular's functions 来获取对 Angular 组件的引用并监视输出。
⚠️注意:
-
ng.getComponent()
仅在 Angular 在开发模式下运行时可用。 IE。enableProdMode()
没有被调用。 - 在
process.env.NODE_ENV = "development";
中设置.storybook/main.js
以防止 Storybook 在生产模式下构建 Angular(请参阅 source)。
export const E2E = Template.bind({});
E2E.args = {
label: 'E2e label',// Story stays unchanged
};
describe("InputCheckbox",() => {
beforeEach(() => {
cy.visit(
"/iframe.html?id=inputcheckboxcomponent--e-2-e",registerComponentOutputs("checkbox") // ❗️?
);
});
it("emits `changeValue`",() => {
// ...
cy.get("@changeValue").should("be.calledWith",true); // ❗️?
});
});
function registerComponentOutputs(
componentSelector: string
): Partial<Cypress.VisitOptions> {
return {
// https://docs.cypress.io/api/commands/visit.html#Provide-an-onLoad-callback-function
onLoad(win) {
const componentElement: HTMLElement = win.document.querySelector(
componentSelector
);
// https://angular.io/api/core/global/ngGetComponent
const component = win.ng.getComponent(componentElement);
// Spy on all `EventEmitters` (i.e. `emit()`) and create equally named alias
Object.keys(component)
.filter(key => !!component[key].emit)
.forEach(key => cy.spy(component[key],"emit").as(key)); // ❗️?
},};
}
总结
- 我喜欢在方法 1 中没有魔法。它易于阅读和理解。不幸的是,它需要指定一个模板,其中包含用于验证输出的附加元素。
- 方法 2 的优点是我们不再需要指定模板。但是我们需要为每个要测试额外代码的
@Output
添加。此外,它使用全局window
来“通信”。 - Apprach 3 也不需要模板。它的优点是 Storybook 代码(story)不需要任何调整。我们只需要将一个参数传递给
cy.visit()
(很可能已经被使用过)以便能够执行检查。因此,如果我们想通过 Storybook 的iframe
测试更多组件,这感觉就像是一个可扩展的解决方案。最后但并非最不重要的是,我们检索对 Angular 组件的引用。有了这个,我们还可以直接在组件本身上调用方法或设置属性。这与ng.applyChanges
的结合似乎为其他测试用例打开了一些大门。
您正在监视window.postMessage()
,这是一种在窗口对象(弹出窗口,页面,iframe,...)之间实现跨域通信的方法。
Storybook中的iFrame不会将任何消息传递给另一个窗口对象,但是您可以在应用程序上安装Kuker或另一个外部Web调试器来监视两者之间的消息,从而使Cypress间谍方法起作用。
如果您选择在角度应用程序上安装Kuker,请按以下步骤操作:
npm install -S kuker-emitters
还要添加Kuker Chrome扩展程序以使其正常工作。
,如果您使用 cypress-storybook 包和 @storybook/addon-actions,有一种方法可用于此用例,我认为它提供了最简单的解决方案。
使用 Storybook-actions 插件,您可以像这样声明 @Output 事件
export default {
title: 'Components/YourComponent',component: YourComponent,decorators: [
moduleMetadata({
imports: [YourModule]
})
]
} as Meta;
const Template: Story<YourStory> = (args: YourComponent) => ({
props: args
});
export const default = Template.bind({});
default.args = {
// ...
changeValue: action('Value Changed'),// action from @storybook/addon-actions
};
在您的 cypress 测试中,您现在可以调用 cy.storyAction()
方法并对它应用 expect 语句。
it('should execute event',() => {
// ...
cy.storyAction('Value Changed').should('have.been.calledWith','value');
})
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。