Wim*_*oet 5 angular storybook cypress storybook-addon-specifications angular-storybook
我正在尝试测试角度分量的输出。
我有一个复选框组件,它使用 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'),
},
});
Run Code Online (Sandbox Code Playgroud)
我正在使用一个操作来捕获值更改并将其记录到屏幕上。
然而,当为此组件编写 cypress e2e 时,我仅使用 iFrame 而不是整个故事书应用程序。
我想找到一种方法来测试输出是否正常。我尝试在 iFrame 中的 postMessage 方法上使用间谍,但这不起作用。
beforeEach(() => {
cy.visit('/iframe.html?id=inputcheckboxcomponent--basic', {
onBeforeLoad(win) {
cy.spy(window, 'postMessage').as('postMessage');
},
});
});
Run Code Online (Sandbox Code Playgroud)
然后断言将是:
cy.get('@postMessage').should('be.called');
Run Code Online (Sandbox Code Playgroud)
还有其他方法可以断言已(changeValue)="changeValue($event)"
触发吗?
@storybook/addon-actions受到@jb17的回答和cypress-storybook的启发。
\n/**\n * my-component.component.ts\n */\n@Component({\n selector: \'my-component\',\n template: `<button (click)="outputChange.emit(\'test-argument\')"></button>`,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class MyComponent {\n @Output()\n outputChange = new EventEmitter<string>();\n}\nRun Code Online (Sandbox Code Playgroud)\n/**\n * my-component.stories.ts\n */\nexport default {\n title: \'MyComponent\',\n component: MyComponent,\n argTypes: {\n outputChange: { action: \'outputChange\' },\n },\n} as Meta<MyComponent>;\n\nconst Template: Story<MyComponent> = (args: MyComponent) => ({\n props: args,\n});\n\nexport const Primary = Template.bind({});\nPrimary.args = {};\nRun Code Online (Sandbox Code Playgroud)\n/**\n * my-component.spec.ts\n */\ndescribe(\'MyComponent @Output Test\', () => {\n beforeEach(() =>\n cy.visit(\'/iframe.html?id=mycomponent--primary\', {\n onLoad: registerActionsAsAlias(), // \xe2\x9d\x97\xef\xb8\x8f\n })\n );\n\n it(\'triggers output\', () => {\n cy.get(\'button\').click();\n\n // Get spy via alias set by `registerActionsAsAlias()`\n cy.get(\'@outputChange\').should(\'have.been.calledWith\', \'test-argument\');\n });\n});\nRun Code Online (Sandbox Code Playgroud)\n/**\n * somewhere.ts\n */\nimport { ActionDisplay } from \'@storybook/addon-actions\';\nimport { AddonStore } from \'@storybook/addons\';\n\nexport function registerActionsAsAlias(): (win: Cypress.AUTWindow) => void {\n // Store spies in the returned functions\' closure\n const actionSpies = {};\n\n return (win: Cypress.AUTWindow) => {\n // https://github.com/storybookjs/storybook/blob/master/lib/addons/src/index.ts\n const addons: AddonStore = win[\'__STORYBOOK_ADDONS\'];\n\n if (addons) {\n // https://github.com/storybookjs/storybook/blob/master/addons/actions/src/constants.ts\n addons.getChannel().addListener(\'storybook/actions/action-event\', (event: ActionDisplay) => {\n if (!actionSpies[event.data.name]) {\n actionSpies[event.data.name] = cy.spy().as(event.data.name);\n }\n\n actionSpies[event.data.name](event.data.args);\n });\n }\n };\n}\nRun Code Online (Sandbox Code Playgroud)\n我们可以将最后发出的值绑定到模板并检查它。
\n{\n moduleMetadata: { imports: [InputCheckboxModule] },\n template: `\n <checkbox (changeValue)="value = $event" [selected]="checked" label="Awesome">\n </checkbox>\n \n <div id="changeValue">{{ value }}</div> <!-- \xe2\x9d\x97\xef\xb8\x8f -->\n `,\n}\nRun Code Online (Sandbox Code Playgroud)\nit("emits `changeValue`", () => {\n // ...\n\n cy.get("#changeValue").contains("true"); // \xe2\x9d\x97\xef\xb8\x8f\n});\n\nRun Code Online (Sandbox Code Playgroud)\n我们可以将最后发出的值分配给全局window对象,在 Cypress 中检索它并验证该值。
export default {\n title: "InputCheckbox",\n component: InputCheckboxComponent,\n argTypes: {\n selected: { type: "boolean", defaultValue: false },\n label: { type: "string", defaultValue: "Default label" },\n },\n} as Meta;\n\n\nconst Template: Story<InputCheckboxComponent> = (\n args: InputCheckboxComponent\n) =>\n ({\n moduleMetadata: { imports: [InputCheckboxModule] },\n component: InputCheckboxComponent,\n props: args,\n } as StoryFnAngularReturnType);\n\n\nexport const E2E = Template.bind({});\nE2E.args = {\n label: \'E2e label\',\n selected: true,\n changeValue: value => (window.changeValue = value), // \xe2\x9d\x97\xef\xb8\x8f\n};\n\nRun Code Online (Sandbox Code Playgroud)\nit("emits `changeValue`", () => {\n // ...\n\n cy.window().its("changeValue").should("equal", true); // \xe2\x9d\x97\xef\xb8\x8f\n});\nRun Code Online (Sandbox Code Playgroud)\n我们可以使用存储在全局命名空间中的Angular 函数ng来获取对 Angular 组件的引用并监视输出。
\xe2\x9a\xa0\xef\xb8\x8f 注意:
\nng.getComponent()仅当 Angular 在开发模式下运行时才可用。即enableProdMode()不被调用。process.env.NODE_ENV = "development";源代码)。.storybook/main.jsexport const E2E = Template.bind({});\nE2E.args = {\n label: \'E2e label\',\n selected: true,\n // Story stays unchanged\n};\n\nRun Code Online (Sandbox Code Playgroud)\ndescribe("InputCheckbox", () => {\n beforeEach(() => {\n cy.visit(\n "/iframe.html?id=inputcheckboxcomponent--e-2-e",\n registerComponentOutputs("checkbox") // \xe2\x9d\x97\xef\xb8\x8f\n );\n });\n\n it("emits `changeValue`", () => {\n // ...\n\n cy.get("@changeValue").should("be.calledWith", true); // \xe2\x9d\x97\xef\xb8\x8f\n });\n});\nRun Code Online (Sandbox Code Playgroud)\nfunction registerComponentOutputs(\n componentSelector: string\n): Partial<Cypress.VisitOptions> {\n return {\n // https://docs.cypress.io/api/commands/visit.html#Provide-an-onLoad-callback-function\n onLoad(win) {\n const componentElement: HTMLElement = win.document.querySelector(\n componentSelector\n );\n // https://angular.io/api/core/global/ngGetComponent\n const component = win.ng.getComponent(componentElement);\n\n // Spy on all `EventEmitters` (i.e. `emit()`) and create equally named alias\n Object.keys(component)\n .filter(key => !!component[key].emit)\n .forEach(key => cy.spy(component[key], "emit").as(key)); // \xe2\x9d\x97\xef\xb8\x8f\n },\n };\n}\nRun Code Online (Sandbox Code Playgroud)\n@Output我们想要测试的附加代码添加。此外,它使用全局window来“通信”。cy.visit()(很可能已经使用过)以便能够执行检查。因此,如果我们想通过 Storybook 测试更多组件,这感觉像是一个可扩展的解决方案iframe。最后但并非最不重要的一点是,我们检索对 Angular 组件的引用。这样我们还可以直接在组件本身上调用方法或设置属性。这ng.applyChanges似乎为额外的测试用例打开了一些大门。小智 0
您正在监视window.postMessage(),这是一种启用窗口对象(弹出窗口、页面、iframe 等)之间跨源通信的方法。
Storybook 中的 iFrame 不会向另一个窗口对象传达任何消息,但您可以在应用程序上安装 Kuker 或另一个外部 Web 调试器来监视两者之间的消息,从而使 Cypress 间谍方法正常工作。
如果您选择在 Angular 应用程序上安装 Kuker,请按以下步骤操作:
npm install -S kuker-emitters
Run Code Online (Sandbox Code Playgroud)
还要添加 Kuker Chrome 扩展以使其正常工作。
| 归档时间: |
|
| 查看次数: |
6165 次 |
| 最近记录: |