Skip to content

Commit

Permalink
feat(storybook): interaction tests generators for angular
Browse files Browse the repository at this point in the history
  • Loading branch information
mandarini committed Jul 19, 2023
1 parent 3fcd84a commit fe0f81b
Show file tree
Hide file tree
Showing 47 changed files with 777 additions and 456 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@
"examples": ["awesome.component"],
"x-priority": "important"
},
"interactionTests": {
"type": "boolean",
"description": "Set up Storybook interaction tests.",
"x-prompt": "Do you want to set up Storybook interaction tests?",
"x-priority": "important",
"default": true
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
Expand Down
13 changes: 10 additions & 3 deletions docs/generated/packages/angular/generators/stories.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,22 @@
"x-dropdown": "projects",
"x-priority": "important"
},
"interactionTests": {
"type": "boolean",
"description": "Set up Storybook interaction tests.",
"x-prompt": "Do you want to set up Storybook interaction tests?",
"x-priority": "important",
"default": true
},
"generateCypressSpecs": {
"type": "boolean",
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator.",
"x-prompt": "Do you want to generate Cypress specs as well?",
"x-priority": "important"
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"cypressProject": {
"type": "string",
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default."
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default.",
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"skipFormat": {
"description": "Skip formatting files.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"configureCypress": {
"type": "boolean",
"description": "Specifies whether to configure Cypress or not.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"generateStories": {
"type": "boolean",
Expand All @@ -41,7 +41,7 @@
"generateCypressSpecs": {
"type": "boolean",
"description": "Specifies whether to automatically generate test files in the generated Cypress e2e app.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"configureStaticServe": {
"type": "boolean",
Expand All @@ -53,7 +53,7 @@
"cypressDirectory": {
"type": "string",
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"linter": {
"description": "The tool to use for running lint checks.",
Expand Down
4 changes: 2 additions & 2 deletions docs/generated/packages/react/generators/stories.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@
"generateCypressSpecs": {
"type": "boolean",
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"cypressProject": {
"type": "string",
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"interactionTests": {
"type": "boolean",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"configureCypress": {
"type": "boolean",
"description": "Run the cypress-configure generator.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"generateStories": {
"type": "boolean",
Expand All @@ -41,7 +41,7 @@
"generateCypressSpecs": {
"type": "boolean",
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"configureStaticServe": {
"type": "boolean",
Expand All @@ -53,7 +53,7 @@
"cypressDirectory": {
"type": "string",
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"js": {
"type": "boolean",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@
"configureCypress": {
"type": "boolean",
"description": "Run the cypress-configure generator.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"cypressDirectory": {
"type": "string",
"description": "A directory where the Cypress project will be placed. Added at root by default.",
"x-deprecated": "Please use Storybook interaction tests instead."
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
},
"linter": {
"description": "The tool to use for running lint checks.",
Expand Down
81 changes: 1 addition & 80 deletions e2e/storybook-angular/src/storybook-angular.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,16 @@ import {
newProject,
runCLI,
runCommandUntil,
runCypressTests,
tmpProjPath,
uniq,
} from '@nx/e2e/utils';
import { writeFileSync } from 'fs';

describe('Storybook executors for Angular', () => {
const angularStorybookLib = uniq('test-ui-ng-lib');
beforeAll(() => {
newProject();
runCLI(`g @nx/angular:library ${angularStorybookLib} --no-interactive`);
runCLI(
`generate @nx/angular:storybook-configuration ${angularStorybookLib} --configureCypress --generateStories --generateCypressSpecs --no-interactive`
`generate @nx/angular:storybook-configuration ${angularStorybookLib} --generateStories --no-interactive`
);
});

Expand All @@ -43,80 +40,4 @@ describe('Storybook executors for Angular', () => {
checkFilesExist(`dist/storybook/${angularStorybookLib}/index.html`);
}, 200_000);
});

// However much I increase the timeout, this takes forever?
xdescribe('run cypress tests using storybook', () => {
it('should execute e2e tests using Cypress running against Storybook', async () => {
if (runCypressTests()) {
addTestButtonToUILib(angularStorybookLib);
writeFileSync(
tmpProjPath(
`apps/${angularStorybookLib}-e2e/src/e2e/test-button/test-button.component.cy.ts`
),
`
describe('${angularStorybookLib}, () => {
it('should render the correct text', () => {
cy.visit(
'/iframe.html?id=testbuttoncomponent--primary&args=text:Click+me;color:#ddffdd;disabled:false;'
)
cy.get('button').should('contain', 'Click me');
cy.get('button').should('not.be.disabled');
});
it('should adjust the controls', () => {
cy.visit(
'/iframe.html?id=testbuttoncomponent--primary&args=text:Click+me;color:#ddffdd;disabled:true;'
)
cy.get('button').should('be.disabled');
});
});
`
);

const e2eResults = runCLI(`e2e ${angularStorybookLib}-e2e --no-watch`);
expect(e2eResults).toContain('All specs passed!');
expect(await killPorts()).toBeTruthy();
}
}, 1000_000);
});
});

function addTestButtonToUILib(libName: string): void {
runCLI(
`g @nx/angular:component test-button --project=${libName} --no-interactive`
);

writeFileSync(
tmpProjPath(`libs/${libName}/src/lib/test-button/test-button.component.ts`),
`
import { Component, Input } from '@angular/core';
@Component({
selector: 'proj-test-button',
templateUrl: './test-button.component.html',
styleUrls: ['./test-button.component.css'],
})
export class TestButtonComponent {
@Input() text = 'Click me';
@Input() color = '#ddffdd';
@Input() disabled = false;
}
`
);

writeFileSync(
tmpProjPath(
`libs/${libName}/src/lib/test-button/test-button.component.html`
),
`
<button
class="my-btn"
[ngStyle]="{ backgroundColor: color }"
[disabled]="disabled"
>
{{ text }}
</button>
`
);
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`componentStory generator should generate the right props 1`] = `
"import { Meta } from '@storybook/angular';
"import type { Meta, StoryObj } from '@storybook/angular';
import { TestButtonComponent } from './test-button.component';
export default {
title: 'TestButtonComponent',
const meta: Meta<TestButtonComponent> = {
component: TestButtonComponent,
} as Meta<TestButtonComponent>;
title: 'TestButtonComponent',
};
export default meta;
type Story = StoryObj<TestButtonComponent>;
export const Primary = {
render: (args: TestButtonComponent) => ({
props: args,
}),
export const Primary: Story = {
args: {
buttonType: 'button',
style: 'default',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export async function componentStoryGenerator(
generateFiles(tree, templatesDir, destinationDir, {
componentFileName: componentFileName,
componentName: componentName,
componentNameSimple: componentFileName.replace('.component', ''),
interactionTests: options.interactionTests,
props: props.filter((p) => typeof p.defaultValue !== 'undefined'),
tmpl: '',
});
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
import { Meta } from '@storybook/angular';
import type { Meta, StoryObj } from '@storybook/angular';
import { <%=componentName%> } from './<%=componentFileName%>';
<% if ( interactionTests ) { %>
import { within } from '@storybook/testing-library';
import { expect } from '@storybook/jest';
<% } %>

export default {
title: '<%=componentName%>',
component: <%=componentName%>
} as Meta<<%=componentName%>>;
const meta: Meta<<%= componentName %>> = {
component: <%= componentName %>,
title: '<%= componentName %>',
};
export default meta;
type Story = StoryObj<<%=componentName%>>;

export const Primary = {
render: (args: <%=componentName%>) => ({
props: args,
}),
export const Primary: Story = {
args: {<% for (let prop of props) { %>
<%= prop.name %>: <%- prop.defaultValue %>,<% } %>
},
};
};

<% if ( interactionTests ) { %>
export const Heading: Story = {
args: {<% for (let prop of props) { %>
<%= prop.name %>: <%- prop.defaultValue %>,<% } %>
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByText(/<%=componentNameSimple%> works!/gi)).toBeTruthy();
},
};
<% } %>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export interface ComponentStoryGeneratorOptions {
projectPath: string;
interactionTests?: boolean;
componentName: string;
componentPath: string;
componentFileName: string;
Expand Down
7 changes: 7 additions & 0 deletions packages/angular/src/generators/component-story/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@
"examples": ["awesome.component"],
"x-priority": "important"
},
"interactionTests": {
"type": "boolean",
"description": "Set up Storybook interaction tests.",
"x-prompt": "Do you want to set up Storybook interaction tests?",
"x-priority": "important",
"default": true
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
Expand Down
Loading

0 comments on commit fe0f81b

Please sign in to comment.