feat(storybook): interaction tests generators for angular (#18166)
This commit is contained in:
parent
34cde20426
commit
29a9f8eba8
@ -32,6 +32,13 @@
|
|||||||
"examples": ["awesome.component"],
|
"examples": ["awesome.component"],
|
||||||
"x-priority": "important"
|
"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": {
|
"skipFormat": {
|
||||||
"description": "Skip formatting files.",
|
"description": "Skip formatting files.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -18,15 +18,22 @@
|
|||||||
"x-dropdown": "projects",
|
"x-dropdown": "projects",
|
||||||
"x-priority": "important"
|
"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": {
|
"generateCypressSpecs": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator.",
|
"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-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||||
"x-priority": "important"
|
|
||||||
},
|
},
|
||||||
"cypressProject": {
|
"cypressProject": {
|
||||||
"type": "string",
|
"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": {
|
"skipFormat": {
|
||||||
"description": "Skip formatting files.",
|
"description": "Skip formatting files.",
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
"configureCypress": {
|
"configureCypress": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Specifies whether to configure Cypress or not.",
|
"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": {
|
"generateStories": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"generateCypressSpecs": {
|
"generateCypressSpecs": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Specifies whether to automatically generate test files in the generated Cypress e2e app.",
|
"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": {
|
"configureStaticServe": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"cypressDirectory": {
|
"cypressDirectory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
"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": {
|
"linter": {
|
||||||
"description": "The tool to use for running lint checks.",
|
"description": "The tool to use for running lint checks.",
|
||||||
|
|||||||
@ -20,12 +20,12 @@
|
|||||||
"generateCypressSpecs": {
|
"generateCypressSpecs": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
|
"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": {
|
"cypressProject": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
|
"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": {
|
"interactionTests": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
"configureCypress": {
|
"configureCypress": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Run the cypress-configure generator.",
|
"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": {
|
"generateStories": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"generateCypressSpecs": {
|
"generateCypressSpecs": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
|
"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": {
|
"configureStaticServe": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"cypressDirectory": {
|
"cypressDirectory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
"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": {
|
"js": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -28,12 +28,12 @@
|
|||||||
"configureCypress": {
|
"configureCypress": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Run the cypress-configure generator.",
|
"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": {
|
"cypressDirectory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A directory where the Cypress project will be placed. Added at root by default.",
|
"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": {
|
"linter": {
|
||||||
"description": "The tool to use for running lint checks.",
|
"description": "The tool to use for running lint checks.",
|
||||||
|
|||||||
@ -5,11 +5,8 @@ import {
|
|||||||
newProject,
|
newProject,
|
||||||
runCLI,
|
runCLI,
|
||||||
runCommandUntil,
|
runCommandUntil,
|
||||||
runCypressTests,
|
|
||||||
tmpProjPath,
|
|
||||||
uniq,
|
uniq,
|
||||||
} from '@nx/e2e/utils';
|
} from '@nx/e2e/utils';
|
||||||
import { writeFileSync } from 'fs';
|
|
||||||
|
|
||||||
describe('Storybook executors for Angular', () => {
|
describe('Storybook executors for Angular', () => {
|
||||||
const angularStorybookLib = uniq('test-ui-ng-lib');
|
const angularStorybookLib = uniq('test-ui-ng-lib');
|
||||||
@ -17,7 +14,7 @@ describe('Storybook executors for Angular', () => {
|
|||||||
newProject();
|
newProject();
|
||||||
runCLI(`g @nx/angular:library ${angularStorybookLib} --no-interactive`);
|
runCLI(`g @nx/angular:library ${angularStorybookLib} --no-interactive`);
|
||||||
runCLI(
|
runCLI(
|
||||||
`generate @nx/angular:storybook-configuration ${angularStorybookLib} --configureCypress --generateStories --generateCypressSpecs --no-interactive`
|
`generate @nx/angular:storybook-configuration ${angularStorybookLib} --generateStories --no-interactive`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -43,80 +40,4 @@ describe('Storybook executors for Angular', () => {
|
|||||||
checkFilesExist(`dist/storybook/${angularStorybookLib}/index.html`);
|
checkFilesExist(`dist/storybook/${angularStorybookLib}/index.html`);
|
||||||
}, 200_000);
|
}, 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>
|
|
||||||
`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,18 +1,17 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`componentStory generator should generate the right props 1`] = `
|
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';
|
import { TestButtonComponent } from './test-button.component';
|
||||||
|
|
||||||
export default {
|
const meta: Meta<TestButtonComponent> = {
|
||||||
title: 'TestButtonComponent',
|
|
||||||
component: TestButtonComponent,
|
component: TestButtonComponent,
|
||||||
} as Meta<TestButtonComponent>;
|
title: 'TestButtonComponent',
|
||||||
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<TestButtonComponent>;
|
||||||
|
|
||||||
export const Primary = {
|
export const Primary: Story = {
|
||||||
render: (args: TestButtonComponent) => ({
|
|
||||||
props: args,
|
|
||||||
}),
|
|
||||||
args: {
|
args: {
|
||||||
buttonType: 'button',
|
buttonType: 'button',
|
||||||
style: 'default',
|
style: 'default',
|
||||||
|
|||||||
@ -29,6 +29,8 @@ export async function componentStoryGenerator(
|
|||||||
generateFiles(tree, templatesDir, destinationDir, {
|
generateFiles(tree, templatesDir, destinationDir, {
|
||||||
componentFileName: componentFileName,
|
componentFileName: componentFileName,
|
||||||
componentName: componentName,
|
componentName: componentName,
|
||||||
|
componentNameSimple: componentFileName.replace('.component', ''),
|
||||||
|
interactionTests: options.interactionTests,
|
||||||
props: props.filter((p) => typeof p.defaultValue !== 'undefined'),
|
props: props.filter((p) => typeof p.defaultValue !== 'undefined'),
|
||||||
tmpl: '',
|
tmpl: '',
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,16 +1,31 @@
|
|||||||
import { Meta } from '@storybook/angular';
|
import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
import { <%=componentName%> } from './<%=componentFileName%>';
|
import { <%=componentName%> } from './<%=componentFileName%>';
|
||||||
|
<% if ( interactionTests ) { %>
|
||||||
|
import { within } from '@storybook/testing-library';
|
||||||
|
import { expect } from '@storybook/jest';
|
||||||
|
<% } %>
|
||||||
|
|
||||||
export default {
|
const meta: Meta<<%= componentName %>> = {
|
||||||
|
component: <%= componentName %>,
|
||||||
title: '<%= componentName %>',
|
title: '<%= componentName %>',
|
||||||
component: <%=componentName%>
|
};
|
||||||
} as Meta<<%=componentName%>>;
|
export default meta;
|
||||||
|
type Story = StoryObj<<%=componentName%>>;
|
||||||
|
|
||||||
export const Primary = {
|
export const Primary: Story = {
|
||||||
render: (args: <%=componentName%>) => ({
|
|
||||||
props: args,
|
|
||||||
}),
|
|
||||||
args: {<% for (let prop of props) { %>
|
args: {<% for (let prop of props) { %>
|
||||||
<%= prop.name %>: <%- prop.defaultValue %>,<% } %>
|
<%= 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();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
<% } %>
|
||||||
@ -1,5 +1,6 @@
|
|||||||
export interface ComponentStoryGeneratorOptions {
|
export interface ComponentStoryGeneratorOptions {
|
||||||
projectPath: string;
|
projectPath: string;
|
||||||
|
interactionTests?: boolean;
|
||||||
componentName: string;
|
componentName: string;
|
||||||
componentPath: string;
|
componentPath: string;
|
||||||
componentFileName: string;
|
componentFileName: string;
|
||||||
|
|||||||
@ -29,6 +29,13 @@
|
|||||||
"examples": ["awesome.component"],
|
"examples": ["awesome.component"],
|
||||||
"x-priority": "important"
|
"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": {
|
"skipFormat": {
|
||||||
"description": "Skip formatting files.",
|
"description": "Skip formatting files.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -1,37 +1,85 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`angularStories generator: applications should generate stories file for inline scam component 1`] = `
|
exports[`angularStories generator: applications should generate stories file for inline scam component 1`] = `
|
||||||
"import { Meta } from '@storybook/angular';
|
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
import { MyScamComponent } from './my-scam.component';
|
import { MyScamComponent } from './my-scam.component';
|
||||||
|
|
||||||
export default {
|
import { within } from '@storybook/testing-library';
|
||||||
title: 'MyScamComponent',
|
import { expect } from '@storybook/jest';
|
||||||
component: MyScamComponent,
|
|
||||||
} as Meta<MyScamComponent>;
|
|
||||||
|
|
||||||
export const Primary = {
|
const meta: Meta<MyScamComponent> = {
|
||||||
render: (args: MyScamComponent) => ({
|
component: MyScamComponent,
|
||||||
props: args,
|
title: 'MyScamComponent',
|
||||||
}),
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<MyScamComponent>;
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
args: {},
|
args: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
expect(canvas.getByText(/my-scam works!/gi)).toBeTruthy();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`angularStories generator: applications should generate stories file with interaction tests 1`] = `
|
||||||
|
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
import { within } from '@storybook/testing-library';
|
||||||
|
import { expect } from '@storybook/jest';
|
||||||
|
|
||||||
|
const meta: Meta<AppComponent> = {
|
||||||
|
component: AppComponent,
|
||||||
|
title: 'AppComponent',
|
||||||
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<AppComponent>;
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
|
args: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
expect(canvas.getByText(/app works!/gi)).toBeTruthy();
|
||||||
|
},
|
||||||
|
};
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`angularStories generator: applications should ignore a path that has a nested component, but still generate nested component stories 1`] = `
|
exports[`angularStories generator: applications should ignore a path that has a nested component, but still generate nested component stories 1`] = `
|
||||||
"import { Meta } from '@storybook/angular';
|
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
import { ComponentBComponent } from './component-b.component';
|
import { ComponentBComponent } from './component-b.component';
|
||||||
|
|
||||||
export default {
|
import { within } from '@storybook/testing-library';
|
||||||
title: 'ComponentBComponent',
|
import { expect } from '@storybook/jest';
|
||||||
component: ComponentBComponent,
|
|
||||||
} as Meta<ComponentBComponent>;
|
|
||||||
|
|
||||||
export const Primary = {
|
const meta: Meta<ComponentBComponent> = {
|
||||||
render: (args: ComponentBComponent) => ({
|
component: ComponentBComponent,
|
||||||
props: args,
|
title: 'ComponentBComponent',
|
||||||
}),
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<ComponentBComponent>;
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
args: {},
|
args: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
expect(canvas.getByText(/component-b works!/gi)).toBeTruthy();
|
||||||
|
},
|
||||||
|
};
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,68 +1,76 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`angularStories generator: libraries Stories for non-empty Angular library should generate cypress spec files 1`] = `
|
|
||||||
"describe('test-ui-lib', () => {
|
|
||||||
beforeEach(() =>
|
|
||||||
cy.visit(
|
|
||||||
'/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isOn:false;'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
it('should render the component', () => {
|
|
||||||
cy.get('proj-test-button').should('exist');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`angularStories generator: libraries Stories for non-empty Angular library should generate stories file for standalone components 1`] = `
|
exports[`angularStories generator: libraries Stories for non-empty Angular library should generate stories file for standalone components 1`] = `
|
||||||
"import { Meta } from '@storybook/angular';
|
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
import { StandaloneComponent } from './standalone.component';
|
import { StandaloneComponent } from './standalone.component';
|
||||||
|
|
||||||
export default {
|
import { within } from '@storybook/testing-library';
|
||||||
title: 'StandaloneComponent',
|
import { expect } from '@storybook/jest';
|
||||||
component: StandaloneComponent,
|
|
||||||
} as Meta<StandaloneComponent>;
|
|
||||||
|
|
||||||
export const Primary = {
|
const meta: Meta<StandaloneComponent> = {
|
||||||
render: (args: StandaloneComponent) => ({
|
component: StandaloneComponent,
|
||||||
props: args,
|
title: 'StandaloneComponent',
|
||||||
}),
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<StandaloneComponent>;
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
args: {},
|
args: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
expect(canvas.getByText(/standalone works!/gi)).toBeTruthy();
|
||||||
|
},
|
||||||
|
};
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`angularStories generator: libraries Stories for non-empty Angular library should generate stories file for standalone components 2`] = `
|
exports[`angularStories generator: libraries Stories for non-empty Angular library should generate stories file for standalone components 2`] = `
|
||||||
"import { Meta } from '@storybook/angular';
|
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
import { SecondaryStandaloneComponent } from './secondary-standalone.component';
|
import { SecondaryStandaloneComponent } from './secondary-standalone.component';
|
||||||
|
|
||||||
export default {
|
import { within } from '@storybook/testing-library';
|
||||||
title: 'SecondaryStandaloneComponent',
|
import { expect } from '@storybook/jest';
|
||||||
component: SecondaryStandaloneComponent,
|
|
||||||
} as Meta<SecondaryStandaloneComponent>;
|
|
||||||
|
|
||||||
export const Primary = {
|
const meta: Meta<SecondaryStandaloneComponent> = {
|
||||||
render: (args: SecondaryStandaloneComponent) => ({
|
component: SecondaryStandaloneComponent,
|
||||||
props: args,
|
title: 'SecondaryStandaloneComponent',
|
||||||
}),
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<SecondaryStandaloneComponent>;
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
args: {},
|
args: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
expect(canvas.getByText(/secondary-standalone works!/gi)).toBeTruthy();
|
||||||
|
},
|
||||||
|
};
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`angularStories generator: libraries Stories for non-empty Angular library should generate stories.ts files 1`] = `
|
exports[`angularStories generator: libraries Stories for non-empty Angular library should generate stories.ts files 1`] = `
|
||||||
"import { Meta } from '@storybook/angular';
|
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
import { TestButtonComponent } from './test-button.component';
|
import { TestButtonComponent } from './test-button.component';
|
||||||
|
|
||||||
export default {
|
import { within } from '@storybook/testing-library';
|
||||||
title: 'TestButtonComponent',
|
import { expect } from '@storybook/jest';
|
||||||
component: TestButtonComponent,
|
|
||||||
} as Meta<TestButtonComponent>;
|
|
||||||
|
|
||||||
export const Primary = {
|
const meta: Meta<TestButtonComponent> = {
|
||||||
render: (args: TestButtonComponent) => ({
|
component: TestButtonComponent,
|
||||||
props: args,
|
title: 'TestButtonComponent',
|
||||||
}),
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<TestButtonComponent>;
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
args: {
|
args: {
|
||||||
buttonType: 'button',
|
buttonType: 'button',
|
||||||
style: 'default',
|
style: 'default',
|
||||||
@ -70,22 +78,37 @@ export const Primary = {
|
|||||||
isOn: false,
|
isOn: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
buttonType: 'button',
|
||||||
|
style: 'default',
|
||||||
|
age: 0,
|
||||||
|
isOn: false,
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
expect(canvas.getByText(/test-button works!/gi)).toBeTruthy();
|
||||||
|
},
|
||||||
|
};
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`angularStories generator: libraries Stories for non-empty Angular library should ignore paths 1`] = `
|
exports[`angularStories generator: libraries Stories for non-empty Angular library should ignore paths 1`] = `
|
||||||
"import { Meta } from '@storybook/angular';
|
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
import { TestButtonComponent } from './test-button.component';
|
import { TestButtonComponent } from './test-button.component';
|
||||||
|
|
||||||
export default {
|
import { within } from '@storybook/testing-library';
|
||||||
title: 'TestButtonComponent',
|
import { expect } from '@storybook/jest';
|
||||||
component: TestButtonComponent,
|
|
||||||
} as Meta<TestButtonComponent>;
|
|
||||||
|
|
||||||
export const Primary = {
|
const meta: Meta<TestButtonComponent> = {
|
||||||
render: (args: TestButtonComponent) => ({
|
component: TestButtonComponent,
|
||||||
props: args,
|
title: 'TestButtonComponent',
|
||||||
}),
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<TestButtonComponent>;
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
args: {
|
args: {
|
||||||
buttonType: 'button',
|
buttonType: 'button',
|
||||||
style: 'default',
|
style: 'default',
|
||||||
@ -93,5 +116,18 @@ export const Primary = {
|
|||||||
isOn: false,
|
isOn: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
buttonType: 'button',
|
||||||
|
style: 'default',
|
||||||
|
age: 0,
|
||||||
|
isOn: false,
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
expect(canvas.getByText(/test-button works!/gi)).toBeTruthy();
|
||||||
|
},
|
||||||
|
};
|
||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,7 +1,14 @@
|
|||||||
export interface StoriesGeneratorOptions {
|
export interface StoriesGeneratorOptions {
|
||||||
name: string;
|
name: string;
|
||||||
cypressProject?: string;
|
interactionTests?: boolean;
|
||||||
generateCypressSpecs?: boolean;
|
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
ignorePaths?: string[];
|
ignorePaths?: string[];
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
cypressProject?: string;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
generateCypressSpecs?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,15 +18,22 @@
|
|||||||
"x-dropdown": "projects",
|
"x-dropdown": "projects",
|
||||||
"x-priority": "important"
|
"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": {
|
"generateCypressSpecs": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator.",
|
"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-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||||
"x-priority": "important"
|
|
||||||
},
|
},
|
||||||
"cypressProject": {
|
"cypressProject": {
|
||||||
"type": "string",
|
"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": {
|
"skipFormat": {
|
||||||
"description": "Skip formatting files.",
|
"description": "Skip formatting files.",
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import { angularStoriesGenerator } from './stories';
|
|||||||
// which is v9 while we are testing for the new v10 version
|
// which is v9 while we are testing for the new v10 version
|
||||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||||
|
|
||||||
|
// TODO(v18): remove Cypress
|
||||||
|
|
||||||
describe('angularStories generator: applications', () => {
|
describe('angularStories generator: applications', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
const appName = 'test-app';
|
const appName = 'test-app';
|
||||||
@ -25,12 +27,12 @@ describe('angularStories generator: applications', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate stories file', async () => {
|
it('should generate stories file with interaction tests', async () => {
|
||||||
await angularStoriesGenerator(tree, { name: appName });
|
await angularStoriesGenerator(tree, { name: appName });
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
tree.exists(`apps/${appName}/src/app/app.component.stories.ts`)
|
tree.read(`apps/${appName}/src/app/app.component.stories.ts`, 'utf-8')
|
||||||
).toBeTruthy();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate stories file for scam component', async () => {
|
it('should generate stories file for scam component', async () => {
|
||||||
@ -90,11 +92,10 @@ describe('angularStories generator: applications', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
tree
|
tree.read(
|
||||||
.read(
|
`apps/${appName}/src/app/component-a/component-b/component-b.component.stories.ts`,
|
||||||
`apps/${appName}/src/app/component-a/component-b/component-b.component.stories.ts`
|
'utf-8'
|
||||||
)
|
)
|
||||||
.toString()
|
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
expect(
|
expect(
|
||||||
tree.exists(
|
tree.exists(
|
||||||
@ -113,20 +114,10 @@ describe('angularStories generator: applications', () => {
|
|||||||
await angularStoriesGenerator(tree, { name: appName });
|
await angularStoriesGenerator(tree, { name: appName });
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
tree
|
tree.read(
|
||||||
.read(`apps/${appName}/src/app/my-scam/my-scam.component.stories.ts`)
|
`apps/${appName}/src/app/my-scam/my-scam.component.stories.ts`,
|
||||||
.toString()
|
'utf-8'
|
||||||
|
)
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate cypress spec file', async () => {
|
|
||||||
await angularStoriesGenerator(tree, {
|
|
||||||
name: appName,
|
|
||||||
generateCypressSpecs: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.exists(`apps/${appName}-e2e/src/e2e/app.component.cy.ts`)
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
|
|||||||
import { Tree } from '@nx/devkit';
|
import { Tree } from '@nx/devkit';
|
||||||
import { writeJson } from '@nx/devkit';
|
import { writeJson } from '@nx/devkit';
|
||||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
import { Linter } from '@nx/linter';
|
|
||||||
import { componentGenerator } from '../component/component';
|
import { componentGenerator } from '../component/component';
|
||||||
import { librarySecondaryEntryPointGenerator } from '../library-secondary-entry-point/library-secondary-entry-point';
|
import { librarySecondaryEntryPointGenerator } from '../library-secondary-entry-point/library-secondary-entry-point';
|
||||||
import { scamGenerator } from '../scam/scam';
|
import { scamGenerator } from '../scam/scam';
|
||||||
@ -15,6 +14,7 @@ import { angularStoriesGenerator } from './stories';
|
|||||||
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
// need to mock cypress otherwise it'll use the nx installed version from package.json
|
||||||
// which is v9 while we are testing for the new v10 version
|
// which is v9 while we are testing for the new v10 version
|
||||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||||
|
// TODO(v18): remove Cypress
|
||||||
|
|
||||||
describe('angularStories generator: libraries', () => {
|
describe('angularStories generator: libraries', () => {
|
||||||
const libName = 'test-ui-lib';
|
const libName = 'test-ui-lib';
|
||||||
@ -39,7 +39,6 @@ describe('angularStories generator: libraries', () => {
|
|||||||
async () =>
|
async () =>
|
||||||
await angularStoriesGenerator(tree, {
|
await angularStoriesGenerator(tree, {
|
||||||
name: libName,
|
name: libName,
|
||||||
generateCypressSpecs: false,
|
|
||||||
})
|
})
|
||||||
).not.toThrow();
|
).not.toThrow();
|
||||||
});
|
});
|
||||||
@ -47,13 +46,9 @@ describe('angularStories generator: libraries', () => {
|
|||||||
|
|
||||||
describe('Stories for non-empty Angular library', () => {
|
describe('Stories for non-empty Angular library', () => {
|
||||||
let tree: Tree;
|
let tree: Tree;
|
||||||
let cypressProjectGenerator;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
tree = await createStorybookTestWorkspaceForLib(libName);
|
tree = await createStorybookTestWorkspaceForLib(libName);
|
||||||
cypressProjectGenerator = await (
|
|
||||||
await import('@nx/storybook')
|
|
||||||
).cypressProjectGenerator;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate stories.ts files', async () => {
|
it('should generate stories.ts files', async () => {
|
||||||
@ -105,56 +100,11 @@ describe('angularStories generator: libraries', () => {
|
|||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate cypress spec files', async () => {
|
|
||||||
await cypressProjectGenerator(tree, {
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
name: libName,
|
|
||||||
});
|
|
||||||
|
|
||||||
await angularStoriesGenerator(tree, {
|
|
||||||
name: libName,
|
|
||||||
generateCypressSpecs: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
`apps/${libName}-e2e/src/e2e/barrel-button/barrel-button.component.cy.ts`
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
`apps/${libName}-e2e/src/e2e/nested-button/nested-button.component.cy.ts`
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
`apps/${libName}-e2e/src/e2e/test-button/test-button.component.cy.ts`
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
`apps/${libName}-e2e/src/e2e/test-other/test-other.component.cy.ts`
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.read(
|
|
||||||
`apps/${libName}-e2e/src/e2e/test-button/test-button.component.cy.ts`,
|
|
||||||
'utf-8'
|
|
||||||
)
|
|
||||||
).toMatchSnapshot();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should run twice without errors', async () => {
|
it('should run twice without errors', async () => {
|
||||||
await cypressProjectGenerator(tree, {
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
name: libName,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await angularStoriesGenerator(tree, { name: libName });
|
await angularStoriesGenerator(tree, { name: libName });
|
||||||
await angularStoriesGenerator(tree, {
|
await angularStoriesGenerator(tree, {
|
||||||
name: libName,
|
name: libName,
|
||||||
generateCypressSpecs: true,
|
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
fail('Should not fail when running it twice.');
|
fail('Should not fail when running it twice.');
|
||||||
@ -162,14 +112,8 @@ describe('angularStories generator: libraries', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle modules with variable declarations rather than literals', async () => {
|
it('should handle modules with variable declarations rather than literals', async () => {
|
||||||
await cypressProjectGenerator(tree, {
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
name: libName,
|
|
||||||
});
|
|
||||||
|
|
||||||
await angularStoriesGenerator(tree, {
|
await angularStoriesGenerator(tree, {
|
||||||
name: libName,
|
name: libName,
|
||||||
generateCypressSpecs: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@ -182,27 +126,11 @@ describe('angularStories generator: libraries', () => {
|
|||||||
`libs/${libName}/src/lib/variable-declare/variable-declare-view/variable-declare-view.component.stories.ts`
|
`libs/${libName}/src/lib/variable-declare/variable-declare-view/variable-declare-view.component.stories.ts`
|
||||||
)
|
)
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
`apps/${libName}-e2e/src/e2e/variable-declare-button/variable-declare-button.component.cy.ts`
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
`apps/${libName}-e2e/src/e2e/variable-declare-view/variable-declare-view.component.cy.ts`
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle modules with where components are spread into the declarations array', async () => {
|
it('should handle modules with where components are spread into the declarations array', async () => {
|
||||||
await cypressProjectGenerator(tree, {
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
name: libName,
|
|
||||||
});
|
|
||||||
|
|
||||||
await angularStoriesGenerator(tree, {
|
await angularStoriesGenerator(tree, {
|
||||||
name: libName,
|
name: libName,
|
||||||
generateCypressSpecs: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@ -220,33 +148,11 @@ describe('angularStories generator: libraries', () => {
|
|||||||
`libs/${libName}/src/lib/variable-spread-declare/variable-spread-declare-view/variable-spread-declare-view.component.stories.ts`
|
`libs/${libName}/src/lib/variable-spread-declare/variable-spread-declare-view/variable-spread-declare-view.component.stories.ts`
|
||||||
)
|
)
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
`apps/${libName}-e2e/src/e2e/variable-spread-declare-button/variable-spread-declare-button.component.cy.ts`
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
`apps/${libName}-e2e/src/e2e/variable-spread-declare-view/variable-spread-declare-view.component.cy.ts`
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
`apps/${libName}-e2e/src/e2e/variable-spread-declare-anotherview/variable-spread-declare-anotherview.component.cy.ts`
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle modules using static members for declarations rather than literals', async () => {
|
it('should handle modules using static members for declarations rather than literals', async () => {
|
||||||
await cypressProjectGenerator(tree, {
|
|
||||||
linter: Linter.EsLint,
|
|
||||||
name: libName,
|
|
||||||
});
|
|
||||||
|
|
||||||
await angularStoriesGenerator(tree, {
|
await angularStoriesGenerator(tree, {
|
||||||
name: libName,
|
name: libName,
|
||||||
generateCypressSpecs: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
@ -259,12 +165,6 @@ describe('angularStories generator: libraries', () => {
|
|||||||
`libs/${libName}/src/lib/static-member-declarations/cmp2/cmp2.component.stories.ts`
|
`libs/${libName}/src/lib/static-member-declarations/cmp2/cmp2.component.stories.ts`
|
||||||
)
|
)
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(
|
|
||||||
tree.exists(`apps/${libName}-e2e/src/e2e/cmp1/cmp1.component.cy.ts`)
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.exists(`apps/${libName}-e2e/src/e2e/cmp2/cmp2.component.cy.ts`)
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate stories file for scam component', async () => {
|
it('should generate stories file for scam component', async () => {
|
||||||
|
|||||||
@ -1,4 +1,14 @@
|
|||||||
import { formatFiles, joinPathFragments, logger, Tree } from '@nx/devkit';
|
import {
|
||||||
|
addDependenciesToPackageJson,
|
||||||
|
ensurePackage,
|
||||||
|
formatFiles,
|
||||||
|
GeneratorCallback,
|
||||||
|
joinPathFragments,
|
||||||
|
logger,
|
||||||
|
readProjectConfiguration,
|
||||||
|
runTasksInSerial,
|
||||||
|
Tree,
|
||||||
|
} from '@nx/devkit';
|
||||||
import componentCypressSpecGenerator from '../component-cypress-spec/component-cypress-spec';
|
import componentCypressSpecGenerator from '../component-cypress-spec/component-cypress-spec';
|
||||||
import componentStoryGenerator from '../component-story/component-story';
|
import componentStoryGenerator from '../component-story/component-story';
|
||||||
import type { ComponentInfo } from '../utils/storybook-ast/component-info';
|
import type { ComponentInfo } from '../utils/storybook-ast/component-info';
|
||||||
@ -11,11 +21,12 @@ import { getE2EProject } from './lib/get-e2e-project';
|
|||||||
import { getModuleFilePaths } from '../utils/storybook-ast/module-info';
|
import { getModuleFilePaths } from '../utils/storybook-ast/module-info';
|
||||||
import type { StoriesGeneratorOptions } from './schema';
|
import type { StoriesGeneratorOptions } from './schema';
|
||||||
import minimatch = require('minimatch');
|
import minimatch = require('minimatch');
|
||||||
|
import { nxVersion } from '../../utils/versions';
|
||||||
|
|
||||||
export async function angularStoriesGenerator(
|
export async function angularStoriesGenerator(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
options: StoriesGeneratorOptions
|
options: StoriesGeneratorOptions
|
||||||
): Promise<void> {
|
): Promise<GeneratorCallback> {
|
||||||
const e2eProjectName = options.cypressProject ?? `${options.name}-e2e`;
|
const e2eProjectName = options.cypressProject ?? `${options.name}-e2e`;
|
||||||
const e2eProject = getE2EProject(tree, e2eProjectName);
|
const e2eProject = getE2EProject(tree, e2eProjectName);
|
||||||
const entryPoints = getProjectEntryPoints(tree, options.name);
|
const entryPoints = getProjectEntryPoints(tree, options.name);
|
||||||
@ -59,6 +70,7 @@ export async function angularStoriesGenerator(
|
|||||||
componentName: info.name,
|
componentName: info.name,
|
||||||
componentPath: info.path,
|
componentPath: info.path,
|
||||||
componentFileName: info.componentFileName,
|
componentFileName: info.componentFileName,
|
||||||
|
interactionTests: options.interactionTests ?? true,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -75,10 +87,24 @@ export async function angularStoriesGenerator(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
|
||||||
|
if (options.interactionTests) {
|
||||||
|
const { interactionTestsDependencies, addInteractionsInAddons } =
|
||||||
|
ensurePackage<typeof import('@nx/storybook')>('@nx/storybook', nxVersion);
|
||||||
|
|
||||||
|
const projectConfiguration = readProjectConfiguration(tree, options.name);
|
||||||
|
addInteractionsInAddons(tree, projectConfiguration);
|
||||||
|
|
||||||
|
tasks.push(
|
||||||
|
addDependenciesToPackageJson(tree, {}, interactionTestsDependencies())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!options.skipFormat) {
|
if (!options.skipFormat) {
|
||||||
await formatFiles(tree);
|
await formatFiles(tree);
|
||||||
}
|
}
|
||||||
|
return runTasksInSerial(...tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default angularStoriesGenerator;
|
export default angularStoriesGenerator;
|
||||||
|
|||||||
@ -1,9 +1,77 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`StorybookConfiguration generator should configure everything at once - and interaction tests too 1`] = `
|
||||||
|
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
|
import { TestButtonComponent } from './test-button.component';
|
||||||
|
|
||||||
|
import { within } from '@storybook/testing-library';
|
||||||
|
import { expect } from '@storybook/jest';
|
||||||
|
|
||||||
|
const meta: Meta<TestButtonComponent> = {
|
||||||
|
component: TestButtonComponent,
|
||||||
|
title: 'TestButtonComponent',
|
||||||
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<TestButtonComponent>;
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
|
args: {
|
||||||
|
buttonType: 'button',
|
||||||
|
style: 'default',
|
||||||
|
age: 0,
|
||||||
|
isOn: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
buttonType: 'button',
|
||||||
|
style: 'default',
|
||||||
|
age: 0,
|
||||||
|
isOn: false,
|
||||||
|
},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
expect(canvas.getByText(/test-button works!/gi)).toBeTruthy();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`StorybookConfiguration generator should configure everything at once - and interaction tests too 2`] = `
|
||||||
|
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||||
|
import { TestOtherComponent } from './test-other.component';
|
||||||
|
|
||||||
|
import { within } from '@storybook/testing-library';
|
||||||
|
import { expect } from '@storybook/jest';
|
||||||
|
|
||||||
|
const meta: Meta<TestOtherComponent> = {
|
||||||
|
component: TestOtherComponent,
|
||||||
|
title: 'TestOtherComponent',
|
||||||
|
};
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<TestOtherComponent>;
|
||||||
|
|
||||||
|
export const Primary: Story = {
|
||||||
|
args: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
expect(canvas.getByText(/test-other works!/gi)).toBeTruthy();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`StorybookConfiguration generator should configure storybook to use webpack 5 1`] = `
|
exports[`StorybookConfiguration generator should configure storybook to use webpack 5 1`] = `
|
||||||
"const config = {
|
"import type { StorybookConfig } from '@storybook/angular';
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
||||||
addons: ['@storybook/addon-essentials'],
|
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
|
||||||
framework: {
|
framework: {
|
||||||
name: '@storybook/angular',
|
name: '@storybook/angular',
|
||||||
options: {},
|
options: {},
|
||||||
@ -26,33 +94,12 @@ exports[`StorybookConfiguration generator should generate in the correct folder
|
|||||||
".prettierignore",
|
".prettierignore",
|
||||||
".prettierrc",
|
".prettierrc",
|
||||||
"apps/.gitignore",
|
"apps/.gitignore",
|
||||||
"apps/one/two/test-ui-lib-e2e/.eslintrc.json",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/cypress.config.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/project.json",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/barrel-button/barrel-button.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/cmp1/cmp1.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/cmp2/cmp2.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/nested-button/nested-button.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/secondary-entry-point/secondary-button/secondary-button.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/secondary-entry-point/secondary-standalone/secondary-standalone.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/standalone/standalone.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/test-button/test-button.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/test-other/test-other.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/variable-declare-button/variable-declare-button.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/variable-declare-view/variable-declare-view.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/variable-spread-declare-anotherview/variable-spread-declare-anotherview.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/variable-spread-declare-button/variable-spread-declare-button.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/e2e/variable-spread-declare-view/variable-spread-declare-view.component.cy.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/fixtures/example.json",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/support/commands.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/src/support/e2e.ts",
|
|
||||||
"apps/one/two/test-ui-lib-e2e/tsconfig.json",
|
|
||||||
"jest.config.ts",
|
"jest.config.ts",
|
||||||
"jest.preset.js",
|
"jest.preset.js",
|
||||||
"libs/.gitignore",
|
"libs/.gitignore",
|
||||||
"libs/test-ui-lib/.eslintrc.json",
|
"libs/test-ui-lib/.eslintrc.json",
|
||||||
"libs/test-ui-lib/.storybook/main.js",
|
"libs/test-ui-lib/.storybook/main.ts",
|
||||||
"libs/test-ui-lib/.storybook/preview.js",
|
"libs/test-ui-lib/.storybook/preview.ts",
|
||||||
"libs/test-ui-lib/.storybook/tsconfig.json",
|
"libs/test-ui-lib/.storybook/tsconfig.json",
|
||||||
"libs/test-ui-lib/jest.config.ts",
|
"libs/test-ui-lib/jest.config.ts",
|
||||||
"libs/test-ui-lib/package.json",
|
"libs/test-ui-lib/package.json",
|
||||||
@ -158,33 +205,12 @@ exports[`StorybookConfiguration generator should generate the right files 1`] =
|
|||||||
".prettierignore",
|
".prettierignore",
|
||||||
".prettierrc",
|
".prettierrc",
|
||||||
"apps/.gitignore",
|
"apps/.gitignore",
|
||||||
"apps/test-ui-lib-e2e/.eslintrc.json",
|
|
||||||
"apps/test-ui-lib-e2e/cypress.config.ts",
|
|
||||||
"apps/test-ui-lib-e2e/project.json",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/barrel-button/barrel-button.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/cmp1/cmp1.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/cmp2/cmp2.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/nested-button/nested-button.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/secondary-entry-point/secondary-button/secondary-button.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/secondary-entry-point/secondary-standalone/secondary-standalone.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/standalone/standalone.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/test-button/test-button.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/test-other/test-other.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/variable-declare-button/variable-declare-button.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/variable-declare-view/variable-declare-view.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/variable-spread-declare-anotherview/variable-spread-declare-anotherview.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/variable-spread-declare-button/variable-spread-declare-button.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/e2e/variable-spread-declare-view/variable-spread-declare-view.component.cy.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/fixtures/example.json",
|
|
||||||
"apps/test-ui-lib-e2e/src/support/commands.ts",
|
|
||||||
"apps/test-ui-lib-e2e/src/support/e2e.ts",
|
|
||||||
"apps/test-ui-lib-e2e/tsconfig.json",
|
|
||||||
"jest.config.ts",
|
"jest.config.ts",
|
||||||
"jest.preset.js",
|
"jest.preset.js",
|
||||||
"libs/.gitignore",
|
"libs/.gitignore",
|
||||||
"libs/test-ui-lib/.eslintrc.json",
|
"libs/test-ui-lib/.eslintrc.json",
|
||||||
"libs/test-ui-lib/.storybook/main.js",
|
"libs/test-ui-lib/.storybook/main.ts",
|
||||||
"libs/test-ui-lib/.storybook/preview.js",
|
"libs/test-ui-lib/.storybook/preview.ts",
|
||||||
"libs/test-ui-lib/.storybook/tsconfig.json",
|
"libs/test-ui-lib/.storybook/tsconfig.json",
|
||||||
"libs/test-ui-lib/jest.config.ts",
|
"libs/test-ui-lib/jest.config.ts",
|
||||||
"libs/test-ui-lib/package.json",
|
"libs/test-ui-lib/package.json",
|
||||||
|
|||||||
@ -21,6 +21,7 @@ export async function generateStories(
|
|||||||
options.configureCypress && options.generateCypressSpecs,
|
options.configureCypress && options.generateCypressSpecs,
|
||||||
cypressProject: e2eProjectName,
|
cypressProject: e2eProjectName,
|
||||||
ignorePaths: options.ignorePaths,
|
ignorePaths: options.ignorePaths,
|
||||||
|
interactionTests: options.interactionTests,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,24 @@
|
|||||||
import type { Linter } from '@nx/linter';
|
import type { Linter } from '@nx/linter';
|
||||||
|
|
||||||
export interface StorybookConfigurationOptions {
|
export interface StorybookConfigurationOptions {
|
||||||
configureCypress: boolean;
|
|
||||||
configureStaticServe?: boolean;
|
configureStaticServe?: boolean;
|
||||||
generateCypressSpecs: boolean;
|
|
||||||
generateStories: boolean;
|
generateStories: boolean;
|
||||||
linter: Linter;
|
linter: Linter;
|
||||||
name: string;
|
name: string;
|
||||||
cypressDirectory?: string;
|
|
||||||
tsConfiguration?: boolean;
|
tsConfiguration?: boolean;
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
ignorePaths?: string[];
|
ignorePaths?: string[];
|
||||||
interactionTests?: boolean;
|
interactionTests?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
configureCypress?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
generateCypressSpecs?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
cypressDirectory?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
"configureCypress": {
|
"configureCypress": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Specifies whether to configure Cypress or not.",
|
"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": {
|
"generateStories": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"generateCypressSpecs": {
|
"generateCypressSpecs": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Specifies whether to automatically generate test files in the generated Cypress e2e app.",
|
"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": {
|
"configureStaticServe": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"cypressDirectory": {
|
"cypressDirectory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
"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": {
|
"linter": {
|
||||||
"description": "The tool to use for running lint checks.",
|
"description": "The tool to use for running lint checks.",
|
||||||
|
|||||||
@ -44,31 +44,16 @@ describe('StorybookConfiguration generator', () => {
|
|||||||
jest.resetModules();
|
jest.resetModules();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw when generateCypressSpecs is true and generateStories is false', async () => {
|
|
||||||
await expect(
|
|
||||||
storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
|
||||||
name: libName,
|
|
||||||
generateCypressSpecs: true,
|
|
||||||
generateStories: false,
|
|
||||||
})
|
|
||||||
).rejects.toThrow(
|
|
||||||
'Cannot set generateCypressSpecs to true when generateStories is set to false.'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should only configure storybook', async () => {
|
it('should only configure storybook', async () => {
|
||||||
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
||||||
name: libName,
|
name: libName,
|
||||||
configureCypress: false,
|
|
||||||
generateCypressSpecs: false,
|
|
||||||
generateStories: false,
|
generateStories: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists('libs/test-ui-lib/.storybook/main.js')).toBeTruthy();
|
expect(tree.exists('libs/test-ui-lib/.storybook/main.ts')).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
tree.exists('libs/test-ui-lib/.storybook/tsconfig.json')
|
tree.exists('libs/test-ui-lib/.storybook/tsconfig.json')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(tree.exists('apps/test-ui-lib-e2e/cypress.config.ts')).toBeFalsy();
|
|
||||||
expect(
|
expect(
|
||||||
tree.exists(
|
tree.exists(
|
||||||
'libs/test-ui-lib/src/lib/test-button/test-button.component.stories.ts'
|
'libs/test-ui-lib/src/lib/test-button/test-button.component.stories.ts'
|
||||||
@ -79,65 +64,42 @@ describe('StorybookConfiguration generator', () => {
|
|||||||
'libs/test-ui-lib/src/lib/test-other/test-other.component.stories.ts'
|
'libs/test-ui-lib/src/lib/test-other/test-other.component.stories.ts'
|
||||||
)
|
)
|
||||||
).toBeFalsy();
|
).toBeFalsy();
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
'apps/test-ui-lib-e2e/src/integration/test-button/test-button.component.spec.ts'
|
|
||||||
)
|
|
||||||
).toBeFalsy();
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
'apps/test-ui-lib-e2e/src/integration/test-other/test-other.component.spec.ts'
|
|
||||||
)
|
|
||||||
).toBeFalsy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should configure storybook to use webpack 5', async () => {
|
it('should configure storybook to use webpack 5', async () => {
|
||||||
await storybookConfigurationGenerator(tree, {
|
await storybookConfigurationGenerator(tree, {
|
||||||
name: libName,
|
name: libName,
|
||||||
configureCypress: false,
|
|
||||||
generateCypressSpecs: false,
|
|
||||||
generateStories: false,
|
generateStories: false,
|
||||||
linter: Linter.None,
|
linter: Linter.None,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
tree.read('libs/test-ui-lib/.storybook/main.js', 'utf-8')
|
tree.read('libs/test-ui-lib/.storybook/main.ts', 'utf-8')
|
||||||
).toMatchSnapshot();
|
).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should configure everything at once', async () => {
|
it('should configure everything at once - and interaction tests too', async () => {
|
||||||
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
||||||
name: libName,
|
name: libName,
|
||||||
configureCypress: true,
|
|
||||||
generateCypressSpecs: true,
|
|
||||||
generateStories: true,
|
generateStories: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(tree.exists('libs/test-ui-lib/.storybook/main.js')).toBeTruthy();
|
expect(tree.exists('libs/test-ui-lib/.storybook/main.ts')).toBeTruthy();
|
||||||
expect(
|
expect(
|
||||||
tree.exists('libs/test-ui-lib/.storybook/tsconfig.json')
|
tree.exists('libs/test-ui-lib/.storybook/tsconfig.json')
|
||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
expect(tree.exists('apps/test-ui-lib-e2e/cypress.config.ts')).toBeTruthy();
|
|
||||||
expect(
|
expect(
|
||||||
tree.exists(
|
tree.read(
|
||||||
'libs/test-ui-lib/src/lib/test-button/test-button.component.stories.ts'
|
'libs/test-ui-lib/src/lib/test-button/test-button.component.stories.ts',
|
||||||
|
'utf-8'
|
||||||
)
|
)
|
||||||
).toBeTruthy();
|
).toMatchSnapshot();
|
||||||
expect(
|
expect(
|
||||||
tree.exists(
|
tree.read(
|
||||||
'libs/test-ui-lib/src/lib/test-other/test-other.component.stories.ts'
|
'libs/test-ui-lib/src/lib/test-other/test-other.component.stories.ts',
|
||||||
|
'utf-8'
|
||||||
)
|
)
|
||||||
).toBeTruthy();
|
).toMatchSnapshot();
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
'apps/test-ui-lib-e2e/src/e2e/test-button/test-button.component.cy.ts'
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
expect(
|
|
||||||
tree.exists(
|
|
||||||
'apps/test-ui-lib-e2e/src/e2e/test-other/test-other.component.cy.ts'
|
|
||||||
)
|
|
||||||
).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate the right files', async () => {
|
it('should generate the right files', async () => {
|
||||||
@ -171,8 +133,6 @@ describe('StorybookConfiguration generator', () => {
|
|||||||
|
|
||||||
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
||||||
name: libName,
|
name: libName,
|
||||||
configureCypress: true,
|
|
||||||
generateCypressSpecs: true,
|
|
||||||
generateStories: true,
|
generateStories: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -210,10 +170,7 @@ describe('StorybookConfiguration generator', () => {
|
|||||||
|
|
||||||
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
||||||
name: libName,
|
name: libName,
|
||||||
configureCypress: true,
|
|
||||||
generateCypressSpecs: true,
|
|
||||||
generateStories: true,
|
generateStories: true,
|
||||||
cypressDirectory: 'one/two',
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(listFiles(tree)).toMatchSnapshot();
|
expect(listFiles(tree)).toMatchSnapshot();
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { generateStorybookConfiguration } from './lib/generate-storybook-configu
|
|||||||
import { validateOptions } from './lib/validate-options';
|
import { validateOptions } from './lib/validate-options';
|
||||||
import type { StorybookConfigurationOptions } from './schema';
|
import type { StorybookConfigurationOptions } from './schema';
|
||||||
|
|
||||||
|
// TODO(v18): remove Cypress
|
||||||
export async function storybookConfigurationGenerator(
|
export async function storybookConfigurationGenerator(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
options: StorybookConfigurationOptions
|
options: StorybookConfigurationOptions
|
||||||
@ -14,11 +15,19 @@ export async function storybookConfigurationGenerator(
|
|||||||
|
|
||||||
const storybookGeneratorInstallTask = await generateStorybookConfiguration(
|
const storybookGeneratorInstallTask = await generateStorybookConfiguration(
|
||||||
tree,
|
tree,
|
||||||
options
|
{
|
||||||
|
...options,
|
||||||
|
interactionTests: options.interactionTests ?? true, // default is true
|
||||||
|
tsConfiguration: options.tsConfiguration ?? true, // default is true
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (options.generateStories) {
|
if (options.generateStories) {
|
||||||
await generateStories(tree, { ...options, skipFormat: true });
|
await generateStories(tree, {
|
||||||
|
...options,
|
||||||
|
interactionTests: options.interactionTests ?? true,
|
||||||
|
skipFormat: true,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!options.skipFormat) {
|
if (!options.skipFormat) {
|
||||||
|
|||||||
@ -6,11 +6,11 @@ import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript'
|
|||||||
|
|
||||||
let tsModule: typeof import('typescript');
|
let tsModule: typeof import('typescript');
|
||||||
|
|
||||||
export type KnobType = 'text' | 'boolean' | 'number' | 'select';
|
export type ArgType = 'text' | 'boolean' | 'number' | 'select';
|
||||||
|
|
||||||
export interface InputDescriptor {
|
export interface InputDescriptor {
|
||||||
name: string;
|
name: string;
|
||||||
type: KnobType;
|
type: ArgType;
|
||||||
defaultValue?: string;
|
defaultValue?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,7 +62,7 @@ export function getComponentProps(
|
|||||||
: node.name.getText()
|
: node.name.getText()
|
||||||
: node.name.getText();
|
: node.name.getText();
|
||||||
|
|
||||||
const type = getKnobType(node);
|
const type = getArgType(node);
|
||||||
const defaultValue = getArgsDefaultValueFn(node);
|
const defaultValue = getArgsDefaultValueFn(node);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -76,27 +76,27 @@ export function getComponentProps(
|
|||||||
return props;
|
return props;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getKnobType(property: PropertyDeclaration): KnobType {
|
export function getArgType(property: PropertyDeclaration): ArgType {
|
||||||
if (!tsModule) {
|
if (!tsModule) {
|
||||||
tsModule = ensureTypescript();
|
tsModule = ensureTypescript();
|
||||||
}
|
}
|
||||||
if (property.type) {
|
if (property.type) {
|
||||||
const typeName = property.type.getText();
|
const typeName = property.type.getText();
|
||||||
const typeNameToKnobType: Record<string, KnobType> = {
|
const typeNameToArgType: Record<string, ArgType> = {
|
||||||
string: 'text',
|
string: 'text',
|
||||||
number: 'number',
|
number: 'number',
|
||||||
boolean: 'boolean',
|
boolean: 'boolean',
|
||||||
};
|
};
|
||||||
return typeNameToKnobType[typeName] || 'text';
|
return typeNameToArgType[typeName] || 'text';
|
||||||
}
|
}
|
||||||
if (property.initializer) {
|
if (property.initializer) {
|
||||||
const initializerKindToKnobType: Record<number, KnobType> = {
|
const initializerKindToArgType: Record<number, ArgType> = {
|
||||||
[tsModule.SyntaxKind.StringLiteral]: 'text',
|
[tsModule.SyntaxKind.StringLiteral]: 'text',
|
||||||
[tsModule.SyntaxKind.NumericLiteral]: 'number',
|
[tsModule.SyntaxKind.NumericLiteral]: 'number',
|
||||||
[tsModule.SyntaxKind.TrueKeyword]: 'boolean',
|
[tsModule.SyntaxKind.TrueKeyword]: 'boolean',
|
||||||
[tsModule.SyntaxKind.FalseKeyword]: 'boolean',
|
[tsModule.SyntaxKind.FalseKeyword]: 'boolean',
|
||||||
};
|
};
|
||||||
return initializerKindToKnobType[property.initializer.kind] || 'text';
|
return initializerKindToArgType[property.initializer.kind] || 'text';
|
||||||
}
|
}
|
||||||
return 'text';
|
return 'text';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -52,6 +56,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -82,6 +90,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -112,6 +124,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -142,6 +158,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -172,6 +192,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -202,6 +226,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -232,6 +260,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -262,6 +294,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -292,6 +328,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -322,6 +362,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -352,6 +396,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -382,6 +430,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -412,6 +464,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -442,6 +498,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -472,6 +532,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -502,6 +566,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -532,6 +600,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -559,6 +631,7 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to One!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to One!/gi)).toBeTruthy();
|
||||||
@ -586,6 +659,7 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Two!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Two!/gi)).toBeTruthy();
|
||||||
@ -615,6 +689,9 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Three!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Three!/gi)).toBeTruthy();
|
||||||
@ -648,6 +725,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -678,6 +759,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -705,6 +790,7 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
@ -732,6 +818,7 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to TestUiLib!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to TestUiLib!/gi)).toBeTruthy();
|
||||||
|
|||||||
@ -25,6 +25,9 @@ export const Primary = {
|
|||||||
|
|
||||||
<% if ( interactionTests ) { %>
|
<% if ( interactionTests ) { %>
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {<% for (let prop of props) { %>
|
||||||
|
<%= prop.name %>: <%- prop.defaultValue %>,<% } %>
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to <%=componentName%>!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to <%=componentName%>!/gi)).toBeTruthy();
|
||||||
|
|||||||
@ -19,6 +19,7 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to NxWelcome!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to NxWelcome!/gi)).toBeTruthy();
|
||||||
@ -49,6 +50,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
|
|||||||
@ -19,6 +19,7 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to TestUiLib!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to TestUiLib!/gi)).toBeTruthy();
|
||||||
@ -49,6 +50,10 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {
|
||||||
|
name: '',
|
||||||
|
displayAge: false,
|
||||||
|
},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||||
|
|||||||
@ -20,12 +20,12 @@
|
|||||||
"generateCypressSpecs": {
|
"generateCypressSpecs": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
|
"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": {
|
"cypressProject": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
|
"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": {
|
"interactionTests": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -5,18 +5,23 @@ import {
|
|||||||
getComponentNode,
|
getComponentNode,
|
||||||
} from '../../utils/ast-utils';
|
} from '../../utils/ast-utils';
|
||||||
import {
|
import {
|
||||||
|
addDependenciesToPackageJson,
|
||||||
convertNxGenerator,
|
convertNxGenerator,
|
||||||
|
ensurePackage,
|
||||||
formatFiles,
|
formatFiles,
|
||||||
|
GeneratorCallback,
|
||||||
getProjects,
|
getProjects,
|
||||||
joinPathFragments,
|
joinPathFragments,
|
||||||
logger,
|
logger,
|
||||||
ProjectConfiguration,
|
ProjectConfiguration,
|
||||||
|
runTasksInSerial,
|
||||||
Tree,
|
Tree,
|
||||||
visitNotIgnoredFiles,
|
visitNotIgnoredFiles,
|
||||||
} from '@nx/devkit';
|
} from '@nx/devkit';
|
||||||
import { basename, join } from 'path';
|
import { basename, join } from 'path';
|
||||||
import minimatch = require('minimatch');
|
import minimatch = require('minimatch');
|
||||||
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
||||||
|
import { nxVersion } from '../../utils/versions';
|
||||||
|
|
||||||
let tsModule: typeof import('typescript');
|
let tsModule: typeof import('typescript');
|
||||||
|
|
||||||
@ -24,10 +29,16 @@ export interface StorybookStoriesSchema {
|
|||||||
project: string;
|
project: string;
|
||||||
interactionTests?: boolean;
|
interactionTests?: boolean;
|
||||||
js?: boolean;
|
js?: boolean;
|
||||||
cypressProject?: string;
|
|
||||||
generateCypressSpecs?: boolean;
|
|
||||||
ignorePaths?: string[];
|
ignorePaths?: string[];
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
cypressProject?: string;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
generateCypressSpecs?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function projectRootPath(
|
export async function projectRootPath(
|
||||||
@ -87,13 +98,14 @@ export async function createAllStories(
|
|||||||
projectName: string,
|
projectName: string,
|
||||||
interactionTests: boolean,
|
interactionTests: boolean,
|
||||||
js: boolean,
|
js: boolean,
|
||||||
|
projects: Map<string, ProjectConfiguration>,
|
||||||
|
projectConfiguration: ProjectConfiguration,
|
||||||
generateCypressSpecs?: boolean,
|
generateCypressSpecs?: boolean,
|
||||||
cypressProject?: string,
|
cypressProject?: string,
|
||||||
ignorePaths?: string[]
|
ignorePaths?: string[]
|
||||||
) {
|
) {
|
||||||
const { isTheFileAStory } = await import('@nx/storybook/src/utils/utilities');
|
const { isTheFileAStory } = await import('@nx/storybook/src/utils/utilities');
|
||||||
const projects = getProjects(tree);
|
|
||||||
const projectConfiguration = projects.get(projectName);
|
|
||||||
const { sourceRoot, root } = projectConfiguration;
|
const { sourceRoot, root } = projectConfiguration;
|
||||||
let componentPaths: string[] = [];
|
let componentPaths: string[] = [];
|
||||||
|
|
||||||
@ -164,19 +176,35 @@ export async function storiesGenerator(
|
|||||||
host: Tree,
|
host: Tree,
|
||||||
schema: StorybookStoriesSchema
|
schema: StorybookStoriesSchema
|
||||||
) {
|
) {
|
||||||
|
const projects = getProjects(host);
|
||||||
|
const projectConfiguration = projects.get(schema.project);
|
||||||
await createAllStories(
|
await createAllStories(
|
||||||
host,
|
host,
|
||||||
schema.project,
|
schema.project,
|
||||||
schema.interactionTests ?? true,
|
schema.interactionTests ?? true,
|
||||||
schema.js,
|
schema.js,
|
||||||
|
projects,
|
||||||
|
projectConfiguration,
|
||||||
schema.generateCypressSpecs,
|
schema.generateCypressSpecs,
|
||||||
schema.cypressProject,
|
schema.cypressProject,
|
||||||
schema.ignorePaths
|
schema.ignorePaths
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const tasks: GeneratorCallback[] = [];
|
||||||
|
|
||||||
|
if (schema.interactionTests) {
|
||||||
|
const { interactionTestsDependencies, addInteractionsInAddons } =
|
||||||
|
ensurePackage<typeof import('@nx/storybook')>('@nx/storybook', nxVersion);
|
||||||
|
tasks.push(
|
||||||
|
addDependenciesToPackageJson(host, {}, interactionTestsDependencies())
|
||||||
|
);
|
||||||
|
addInteractionsInAddons(host, projectConfiguration);
|
||||||
|
}
|
||||||
|
|
||||||
if (!schema.skipFormat) {
|
if (!schema.skipFormat) {
|
||||||
await formatFiles(host);
|
await formatFiles(host);
|
||||||
}
|
}
|
||||||
|
return runTasksInSerial(...tasks);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default storiesGenerator;
|
export default storiesGenerator;
|
||||||
|
|||||||
@ -43,6 +43,7 @@ export const Primary = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Heading: Story = {
|
export const Heading: Story = {
|
||||||
|
args: {},
|
||||||
play: async ({ canvasElement }) => {
|
play: async ({ canvasElement }) => {
|
||||||
const canvas = within(canvasElement);
|
const canvas = within(canvasElement);
|
||||||
expect(canvas.getByText(/Welcome to MyComponent!/gi)).toBeTruthy();
|
expect(canvas.getByText(/Welcome to MyComponent!/gi)).toBeTruthy();
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
// TODO(katerina): remove Cypress for Nx 18
|
// TODO(v18): remove Cypress
|
||||||
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
|
import { installedCypressVersion } from '@nx/cypress/src/utils/cypress-version';
|
||||||
import { logger, Tree } from '@nx/devkit';
|
import { logger, Tree } from '@nx/devkit';
|
||||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import {
|
|||||||
import { nxVersion } from '../../utils/versions';
|
import { nxVersion } from '../../utils/versions';
|
||||||
|
|
||||||
async function generateStories(host: Tree, schema: StorybookConfigureSchema) {
|
async function generateStories(host: Tree, schema: StorybookConfigureSchema) {
|
||||||
// TODO(katerina): remove Cypress for Nx 18
|
// TODO(v18): remove Cypress
|
||||||
ensurePackage('@nx/cypress', nxVersion);
|
ensurePackage('@nx/cypress', nxVersion);
|
||||||
const { getE2eProjectName } = await import(
|
const { getE2eProjectName } = await import(
|
||||||
'@nx/cypress/src/utils/project-name'
|
'@nx/cypress/src/utils/project-name'
|
||||||
|
|||||||
@ -4,12 +4,21 @@ export interface StorybookConfigureSchema {
|
|||||||
name: string;
|
name: string;
|
||||||
interactionTests?: boolean;
|
interactionTests?: boolean;
|
||||||
generateStories?: boolean;
|
generateStories?: boolean;
|
||||||
configureCypress?: boolean;
|
|
||||||
generateCypressSpecs?: boolean;
|
|
||||||
js?: boolean;
|
js?: boolean;
|
||||||
tsConfiguration?: boolean;
|
tsConfiguration?: boolean;
|
||||||
linter?: Linter;
|
linter?: Linter;
|
||||||
cypressDirectory?: string;
|
|
||||||
ignorePaths?: string[];
|
ignorePaths?: string[];
|
||||||
configureStaticServe?: boolean;
|
configureStaticServe?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
configureCypress?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
generateCypressSpecs?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
cypressDirectory?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
"configureCypress": {
|
"configureCypress": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Run the cypress-configure generator.",
|
"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": {
|
"generateStories": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"generateCypressSpecs": {
|
"generateCypressSpecs": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
|
"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": {
|
"configureStaticServe": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"cypressDirectory": {
|
"cypressDirectory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
"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": {
|
"js": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
|
|||||||
@ -26,7 +26,18 @@ export function getArgsDefaultValue(property: ts.SyntaxKind): string {
|
|||||||
export function getDefaultsForComponent(
|
export function getDefaultsForComponent(
|
||||||
sourceFile: ts.SourceFile,
|
sourceFile: ts.SourceFile,
|
||||||
cmpDeclaration: ts.Node
|
cmpDeclaration: ts.Node
|
||||||
) {
|
): {
|
||||||
|
propsTypeName: string;
|
||||||
|
props: {
|
||||||
|
name: string;
|
||||||
|
defaultValue: any;
|
||||||
|
}[];
|
||||||
|
argTypes: {
|
||||||
|
name: string;
|
||||||
|
type: string;
|
||||||
|
actionText: string;
|
||||||
|
}[];
|
||||||
|
} {
|
||||||
if (!tsModule) {
|
if (!tsModule) {
|
||||||
tsModule = ensureTypescript();
|
tsModule = ensureTypescript();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
export { configurationGenerator } from './src/generators/configuration/configuration';
|
export { configurationGenerator } from './src/generators/configuration/configuration';
|
||||||
export { cypressProjectGenerator } from './src/generators/cypress-project/cypress-project';
|
export { cypressProjectGenerator } from './src/generators/cypress-project/cypress-project';
|
||||||
export { storybookVersion } from './src/utils/versions';
|
export { storybookVersion } from './src/utils/versions';
|
||||||
|
export {
|
||||||
|
interactionTestsDependencies,
|
||||||
|
addInteractionsInAddons,
|
||||||
|
} from './src/generators/configuration/lib/interaction-testing.utils';
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`@nx/storybook:configuration for Storybook v7 basic functionalities should generate TS config for project if tsConfiguration true 1`] = `
|
exports[`@nx/storybook:configuration for Storybook v7 basic functionalities should generate TS config for project by default 1`] = `
|
||||||
"import type { StorybookConfig } from '@storybook/angular';
|
"import type { StorybookConfig } from '@storybook/angular';
|
||||||
|
|
||||||
const config: StorybookConfig = {
|
const config: StorybookConfig = {
|
||||||
@ -20,7 +20,7 @@ export default config;
|
|||||||
"
|
"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`@nx/storybook:configuration for Storybook v7 basic functionalities should generate TypeScript Configuration files 1`] = `
|
exports[`@nx/storybook:configuration for Storybook v7 basic functionalities should generate TypeScript Configuration files by default 1`] = `
|
||||||
{
|
{
|
||||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||||
"name": "test-ui-lib",
|
"name": "test-ui-lib",
|
||||||
@ -97,9 +97,9 @@ exports[`@nx/storybook:configuration for Storybook v7 basic functionalities shou
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`@nx/storybook:configuration for Storybook v7 basic functionalities should generate TypeScript Configuration files 2`] = `null`;
|
exports[`@nx/storybook:configuration for Storybook v7 basic functionalities should generate TypeScript Configuration files by default 2`] = `null`;
|
||||||
|
|
||||||
exports[`@nx/storybook:configuration for Storybook v7 basic functionalities should generate TypeScript Configuration files 3`] = `
|
exports[`@nx/storybook:configuration for Storybook v7 basic functionalities should generate TypeScript Configuration files by default 3`] = `
|
||||||
"import type { StorybookConfig } from '@storybook/angular';
|
"import type { StorybookConfig } from '@storybook/angular';
|
||||||
|
|
||||||
const config: StorybookConfig = {
|
const config: StorybookConfig = {
|
||||||
|
|||||||
@ -82,7 +82,7 @@ describe('@nx/storybook:configuration for workspaces with Root project', () => {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate files for root app', async () => {
|
it('should generate files for root app - js for tsConfiguration: false', async () => {
|
||||||
await configurationGenerator(tree, {
|
await configurationGenerator(tree, {
|
||||||
name: 'web',
|
name: 'web',
|
||||||
uiFramework: '@storybook/react-webpack5',
|
uiFramework: '@storybook/react-webpack5',
|
||||||
|
|||||||
@ -56,11 +56,10 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
|
|||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate TypeScript Configuration files', async () => {
|
it('should generate TypeScript Configuration files by default', async () => {
|
||||||
await configurationGenerator(tree, {
|
await configurationGenerator(tree, {
|
||||||
name: 'test-ui-lib',
|
name: 'test-ui-lib',
|
||||||
standaloneConfig: false,
|
standaloneConfig: false,
|
||||||
tsConfiguration: true,
|
|
||||||
uiFramework: '@storybook/angular',
|
uiFramework: '@storybook/angular',
|
||||||
});
|
});
|
||||||
const project = readProjectConfiguration(tree, 'test-ui-lib');
|
const project = readProjectConfiguration(tree, 'test-ui-lib');
|
||||||
@ -172,11 +171,10 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
|
|||||||
).toBeTruthy();
|
).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should generate TS config for project if tsConfiguration true', async () => {
|
it('should generate TS config for project by default', async () => {
|
||||||
await configurationGenerator(tree, {
|
await configurationGenerator(tree, {
|
||||||
name: 'test-ui-lib',
|
name: 'test-ui-lib',
|
||||||
standaloneConfig: false,
|
standaloneConfig: false,
|
||||||
tsConfiguration: true,
|
|
||||||
uiFramework: '@storybook/angular',
|
uiFramework: '@storybook/angular',
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -278,7 +276,6 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
|
|||||||
});
|
});
|
||||||
await configurationGenerator(tree, {
|
await configurationGenerator(tree, {
|
||||||
name: 'main-vite-ts',
|
name: 'main-vite-ts',
|
||||||
tsConfiguration: true,
|
|
||||||
uiFramework: '@storybook/react-vite',
|
uiFramework: '@storybook/react-vite',
|
||||||
});
|
});
|
||||||
await configurationGenerator(tree, {
|
await configurationGenerator(tree, {
|
||||||
|
|||||||
@ -37,12 +37,10 @@ import {
|
|||||||
import {
|
import {
|
||||||
coreJsVersion,
|
coreJsVersion,
|
||||||
nxVersion,
|
nxVersion,
|
||||||
storybookJestVersion,
|
|
||||||
storybookTestingLibraryVersion,
|
|
||||||
storybookTestRunnerVersion,
|
|
||||||
storybookVersion,
|
storybookVersion,
|
||||||
tsNodeVersion,
|
tsNodeVersion,
|
||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
|
import { interactionTestsDependencies } from './lib/interaction-testing.utils';
|
||||||
|
|
||||||
export async function configurationGenerator(
|
export async function configurationGenerator(
|
||||||
tree: Tree,
|
tree: Tree,
|
||||||
@ -151,7 +149,7 @@ export async function configurationGenerator(
|
|||||||
addStaticTarget(tree, schema);
|
addStaticTarget(tree, schema);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(katerina): remove Cypress for Nx 18
|
// TODO(v18): remove Cypress
|
||||||
if (schema.configureCypress) {
|
if (schema.configureCypress) {
|
||||||
const e2eProject = await getE2EProjectName(tree, schema.name);
|
const e2eProject = await getE2EProjectName(tree, schema.name);
|
||||||
if (!e2eProject) {
|
if (!e2eProject) {
|
||||||
@ -174,17 +172,17 @@ export async function configurationGenerator(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const devDeps = {};
|
let devDeps = {};
|
||||||
|
|
||||||
if (schema.tsConfiguration) {
|
if (schema.tsConfiguration) {
|
||||||
devDeps['ts-node'] = tsNodeVersion;
|
devDeps['ts-node'] = tsNodeVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.interactionTests) {
|
if (schema.interactionTests) {
|
||||||
devDeps['@storybook/test-runner'] = storybookTestRunnerVersion;
|
devDeps = {
|
||||||
devDeps['@storybook/addon-interactions'] = storybookVersion;
|
...devDeps,
|
||||||
devDeps['@storybook/testing-library'] = storybookTestingLibraryVersion;
|
...interactionTestsDependencies(),
|
||||||
devDeps['@storybook/jest'] = storybookJestVersion;
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (schema.configureStaticServe) {
|
if (schema.configureStaticServe) {
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Helper functions for the Storybook 7 migration generator should add addon-interactions in main.ts if it does not exist 1`] = `
|
||||||
|
"import type { StorybookConfig } from '@storybook/angular';
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
||||||
|
addons: ['@storybook/addon-interactions', '@storybook/addon-essentials'],
|
||||||
|
framework: {
|
||||||
|
name: '@storybook/angular',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Helper functions for the Storybook 7 migration generator should do nothing if addon-interactions already exists in main.ts 1`] = `
|
||||||
|
"import type { StorybookConfig } from '@storybook/angular';
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
||||||
|
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
|
||||||
|
framework: {
|
||||||
|
name: '@storybook/angular',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;"
|
||||||
|
`;
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
import { ProjectConfiguration, Tree } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import { addInteractionsInAddons } from './interaction-testing.utils';
|
||||||
|
describe('Helper functions for the Storybook 7 migration generator', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add addon-interactions in main.ts if it does not exist', () => {
|
||||||
|
tree.write(
|
||||||
|
`.storybook/main.ts`,
|
||||||
|
`import type { StorybookConfig } from '@storybook/angular';
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
||||||
|
addons: ['@storybook/addon-essentials'],
|
||||||
|
framework: {
|
||||||
|
name: '@storybook/angular',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;`
|
||||||
|
);
|
||||||
|
addInteractionsInAddons(tree, {
|
||||||
|
name: 'my-proj',
|
||||||
|
targets: {
|
||||||
|
storybook: {
|
||||||
|
executor: '@nx/storybook:storybook',
|
||||||
|
options: {
|
||||||
|
configDir: `.storybook`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as ProjectConfiguration);
|
||||||
|
expect(tree.read(`.storybook/main.ts`, 'utf-8')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing if addon-interactions already exists in main.ts', () => {
|
||||||
|
tree.write(
|
||||||
|
`.storybook/main.ts`,
|
||||||
|
`import type { StorybookConfig } from '@storybook/angular';
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
||||||
|
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
|
||||||
|
framework: {
|
||||||
|
name: '@storybook/angular',
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;`
|
||||||
|
);
|
||||||
|
addInteractionsInAddons(tree, {
|
||||||
|
name: 'my-proj',
|
||||||
|
targets: {
|
||||||
|
storybook: {
|
||||||
|
executor: '@nx/storybook:storybook',
|
||||||
|
options: {
|
||||||
|
configDir: `.storybook`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as unknown as ProjectConfiguration);
|
||||||
|
expect(tree.read(`.storybook/main.ts`, 'utf-8')).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
import {
|
||||||
|
applyChangesToString,
|
||||||
|
ChangeType,
|
||||||
|
ProjectConfiguration,
|
||||||
|
Tree,
|
||||||
|
} from '@nx/devkit';
|
||||||
|
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||||
|
import {
|
||||||
|
storybookJestVersion,
|
||||||
|
storybookTestingLibraryVersion,
|
||||||
|
storybookTestRunnerVersion,
|
||||||
|
storybookVersion,
|
||||||
|
} from '../../../utils/versions';
|
||||||
|
|
||||||
|
export function interactionTestsDependencies(): { [key: string]: string } {
|
||||||
|
return {
|
||||||
|
'@storybook/test-runner': storybookTestRunnerVersion,
|
||||||
|
'@storybook/addon-interactions': storybookVersion,
|
||||||
|
'@storybook/testing-library': storybookTestingLibraryVersion,
|
||||||
|
'@storybook/jest': storybookJestVersion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addInteractionsInAddons(
|
||||||
|
tree: Tree,
|
||||||
|
projectConfig: ProjectConfiguration
|
||||||
|
) {
|
||||||
|
const mainJsTsPath = getMainTsJsPath(tree, projectConfig);
|
||||||
|
if (!mainJsTsPath) return;
|
||||||
|
let mainJsTs = tree.read(mainJsTsPath, 'utf-8');
|
||||||
|
if (!mainJsTs) return;
|
||||||
|
|
||||||
|
// Find addons array
|
||||||
|
const addonsArray = tsquery.query(
|
||||||
|
mainJsTs,
|
||||||
|
`PropertyAssignment:has(Identifier:has([name="addons"]))`
|
||||||
|
)?.[0];
|
||||||
|
|
||||||
|
// if there's no addons array don't do anything
|
||||||
|
// they may be setting up storybook in another project
|
||||||
|
if (!addonsArray) return;
|
||||||
|
|
||||||
|
// Check if addons array already has addon-interactions
|
||||||
|
const addonsArrayHasAddonInteractions = tsquery.query(
|
||||||
|
addonsArray,
|
||||||
|
`StringLiteral:has([text="@storybook/addon-interactions"])`
|
||||||
|
)?.[0];
|
||||||
|
if (addonsArrayHasAddonInteractions) return;
|
||||||
|
|
||||||
|
// get the array of the addons
|
||||||
|
const arrayLiteralExpression = tsquery.query(
|
||||||
|
addonsArray,
|
||||||
|
`ArrayLiteralExpression`
|
||||||
|
)?.[0];
|
||||||
|
if (!arrayLiteralExpression) return;
|
||||||
|
mainJsTs = applyChangesToString(mainJsTs, [
|
||||||
|
{
|
||||||
|
type: ChangeType.Insert,
|
||||||
|
index: arrayLiteralExpression.getStart() + 1,
|
||||||
|
text: `'@storybook/addon-interactions', `,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
tree.write(mainJsTsPath, mainJsTs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMainTsJsPath(
|
||||||
|
host: Tree,
|
||||||
|
projectConfig: ProjectConfiguration
|
||||||
|
): string | undefined {
|
||||||
|
let mainJsTsPath: string | undefined = undefined;
|
||||||
|
Object.entries(projectConfig.targets).forEach(
|
||||||
|
([_targetName, targetConfig]) => {
|
||||||
|
if (
|
||||||
|
targetConfig.executor === '@nx/storybook:storybook' ||
|
||||||
|
targetConfig.executor === '@storybook/angular:start-storybook'
|
||||||
|
) {
|
||||||
|
const configDir = targetConfig.options?.configDir;
|
||||||
|
if (host.exists(`${configDir}/main.js`)) {
|
||||||
|
mainJsTsPath = `${configDir}/main.js`;
|
||||||
|
}
|
||||||
|
if (host.exists(`${configDir}/main.ts`)) {
|
||||||
|
mainJsTsPath = `${configDir}/main.ts`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return mainJsTsPath;
|
||||||
|
}
|
||||||
@ -4,13 +4,19 @@ import { UiFramework7, UiFramework } from '../../utils/models';
|
|||||||
export interface StorybookConfigureSchema {
|
export interface StorybookConfigureSchema {
|
||||||
name: string;
|
name: string;
|
||||||
uiFramework?: UiFramework7;
|
uiFramework?: UiFramework7;
|
||||||
configureCypress?: boolean;
|
|
||||||
linter?: Linter;
|
linter?: Linter;
|
||||||
js?: boolean;
|
js?: boolean;
|
||||||
interactionTests?: boolean;
|
interactionTests?: boolean;
|
||||||
tsConfiguration?: boolean;
|
tsConfiguration?: boolean;
|
||||||
cypressDirectory?: string;
|
|
||||||
standaloneConfig?: boolean;
|
standaloneConfig?: boolean;
|
||||||
configureStaticServe?: boolean;
|
configureStaticServe?: boolean;
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
configureCypress?: boolean;
|
||||||
|
/**
|
||||||
|
* @deprecated Use interactionTests instead. This option will be removed in v18.
|
||||||
|
*/
|
||||||
|
cypressDirectory?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,12 +28,12 @@
|
|||||||
"configureCypress": {
|
"configureCypress": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"description": "Run the cypress-configure generator.",
|
"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": {
|
"cypressDirectory": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A directory where the Cypress project will be placed. Added at root by default.",
|
"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": {
|
"linter": {
|
||||||
"description": "The tool to use for running lint checks.",
|
"description": "The tool to use for running lint checks.",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user