feat(storybook): interaction tests generators for angular (#18166)
This commit is contained in:
parent
34cde20426
commit
29a9f8eba8
@ -32,6 +32,13 @@
|
||||
"examples": ["awesome.component"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
"x-prompt": "Do you want to set up Storybook interaction tests?",
|
||||
"x-priority": "important",
|
||||
"default": true
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
|
||||
@ -18,15 +18,22 @@
|
||||
"x-dropdown": "projects",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
"x-prompt": "Do you want to set up Storybook interaction tests?",
|
||||
"x-priority": "important",
|
||||
"default": true
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator.",
|
||||
"x-prompt": "Do you want to generate Cypress specs as well?",
|
||||
"x-priority": "important"
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default."
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure Cypress or not.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
@ -41,7 +41,7 @@
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate test files in the generated Cypress e2e app.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
@ -53,7 +53,7 @@
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
|
||||
@ -20,12 +20,12 @@
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
@ -41,7 +41,7 @@
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
@ -53,7 +53,7 @@
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -28,12 +28,12 @@
|
||||
"configureCypress": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Added at root by default.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
|
||||
@ -5,11 +5,8 @@ import {
|
||||
newProject,
|
||||
runCLI,
|
||||
runCommandUntil,
|
||||
runCypressTests,
|
||||
tmpProjPath,
|
||||
uniq,
|
||||
} from '@nx/e2e/utils';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
describe('Storybook executors for Angular', () => {
|
||||
const angularStorybookLib = uniq('test-ui-ng-lib');
|
||||
@ -17,7 +14,7 @@ describe('Storybook executors for Angular', () => {
|
||||
newProject();
|
||||
runCLI(`g @nx/angular:library ${angularStorybookLib} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/angular:storybook-configuration ${angularStorybookLib} --configureCypress --generateStories --generateCypressSpecs --no-interactive`
|
||||
`generate @nx/angular:storybook-configuration ${angularStorybookLib} --generateStories --no-interactive`
|
||||
);
|
||||
});
|
||||
|
||||
@ -43,80 +40,4 @@ describe('Storybook executors for Angular', () => {
|
||||
checkFilesExist(`dist/storybook/${angularStorybookLib}/index.html`);
|
||||
}, 200_000);
|
||||
});
|
||||
|
||||
// However much I increase the timeout, this takes forever?
|
||||
xdescribe('run cypress tests using storybook', () => {
|
||||
it('should execute e2e tests using Cypress running against Storybook', async () => {
|
||||
if (runCypressTests()) {
|
||||
addTestButtonToUILib(angularStorybookLib);
|
||||
writeFileSync(
|
||||
tmpProjPath(
|
||||
`apps/${angularStorybookLib}-e2e/src/e2e/test-button/test-button.component.cy.ts`
|
||||
),
|
||||
`
|
||||
describe('${angularStorybookLib}, () => {
|
||||
|
||||
it('should render the correct text', () => {
|
||||
cy.visit(
|
||||
'/iframe.html?id=testbuttoncomponent--primary&args=text:Click+me;color:#ddffdd;disabled:false;'
|
||||
)
|
||||
cy.get('button').should('contain', 'Click me');
|
||||
cy.get('button').should('not.be.disabled');
|
||||
});
|
||||
|
||||
it('should adjust the controls', () => {
|
||||
cy.visit(
|
||||
'/iframe.html?id=testbuttoncomponent--primary&args=text:Click+me;color:#ddffdd;disabled:true;'
|
||||
)
|
||||
cy.get('button').should('be.disabled');
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
const e2eResults = runCLI(`e2e ${angularStorybookLib}-e2e --no-watch`);
|
||||
expect(e2eResults).toContain('All specs passed!');
|
||||
expect(await killPorts()).toBeTruthy();
|
||||
}
|
||||
}, 1000_000);
|
||||
});
|
||||
});
|
||||
|
||||
function addTestButtonToUILib(libName: string): void {
|
||||
runCLI(
|
||||
`g @nx/angular:component test-button --project=${libName} --no-interactive`
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
tmpProjPath(`libs/${libName}/src/lib/test-button/test-button.component.ts`),
|
||||
`
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'proj-test-button',
|
||||
templateUrl: './test-button.component.html',
|
||||
styleUrls: ['./test-button.component.css'],
|
||||
})
|
||||
export class TestButtonComponent {
|
||||
@Input() text = 'Click me';
|
||||
@Input() color = '#ddffdd';
|
||||
@Input() disabled = false;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
writeFileSync(
|
||||
tmpProjPath(
|
||||
`libs/${libName}/src/lib/test-button/test-button.component.html`
|
||||
),
|
||||
`
|
||||
<button
|
||||
class="my-btn"
|
||||
[ngStyle]="{ backgroundColor: color }"
|
||||
[disabled]="disabled"
|
||||
>
|
||||
{{ text }}
|
||||
</button>
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,18 +1,17 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`componentStory generator should generate the right props 1`] = `
|
||||
"import { Meta } from '@storybook/angular';
|
||||
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { TestButtonComponent } from './test-button.component';
|
||||
|
||||
export default {
|
||||
title: 'TestButtonComponent',
|
||||
const meta: Meta<TestButtonComponent> = {
|
||||
component: TestButtonComponent,
|
||||
} as Meta<TestButtonComponent>;
|
||||
title: 'TestButtonComponent',
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<TestButtonComponent>;
|
||||
|
||||
export const Primary = {
|
||||
render: (args: TestButtonComponent) => ({
|
||||
props: args,
|
||||
}),
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
buttonType: 'button',
|
||||
style: 'default',
|
||||
|
||||
@ -29,6 +29,8 @@ export async function componentStoryGenerator(
|
||||
generateFiles(tree, templatesDir, destinationDir, {
|
||||
componentFileName: componentFileName,
|
||||
componentName: componentName,
|
||||
componentNameSimple: componentFileName.replace('.component', ''),
|
||||
interactionTests: options.interactionTests,
|
||||
props: props.filter((p) => typeof p.defaultValue !== 'undefined'),
|
||||
tmpl: '',
|
||||
});
|
||||
|
||||
@ -1,16 +1,31 @@
|
||||
import { Meta } from '@storybook/angular';
|
||||
import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { <%=componentName%> } from './<%=componentFileName%>';
|
||||
<% if ( interactionTests ) { %>
|
||||
import { within } from '@storybook/testing-library';
|
||||
import { expect } from '@storybook/jest';
|
||||
<% } %>
|
||||
|
||||
export default {
|
||||
title: '<%=componentName%>',
|
||||
component: <%=componentName%>
|
||||
} as Meta<<%=componentName%>>;
|
||||
const meta: Meta<<%= componentName %>> = {
|
||||
component: <%= componentName %>,
|
||||
title: '<%= componentName %>',
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<<%=componentName%>>;
|
||||
|
||||
export const Primary = {
|
||||
render: (args: <%=componentName%>) => ({
|
||||
props: args,
|
||||
}),
|
||||
export const Primary: Story = {
|
||||
args: {<% for (let prop of props) { %>
|
||||
<%= prop.name %>: <%- prop.defaultValue %>,<% } %>
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
<% if ( interactionTests ) { %>
|
||||
export const Heading: Story = {
|
||||
args: {<% for (let prop of props) { %>
|
||||
<%= prop.name %>: <%- prop.defaultValue %>,<% } %>
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/<%=componentNameSimple%> works!/gi)).toBeTruthy();
|
||||
},
|
||||
};
|
||||
<% } %>
|
||||
@ -1,5 +1,6 @@
|
||||
export interface ComponentStoryGeneratorOptions {
|
||||
projectPath: string;
|
||||
interactionTests?: boolean;
|
||||
componentName: string;
|
||||
componentPath: string;
|
||||
componentFileName: string;
|
||||
|
||||
@ -29,6 +29,13 @@
|
||||
"examples": ["awesome.component"],
|
||||
"x-priority": "important"
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
"x-prompt": "Do you want to set up Storybook interaction tests?",
|
||||
"x-priority": "important",
|
||||
"default": true
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
|
||||
@ -1,37 +1,85 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
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';
|
||||
|
||||
export default {
|
||||
title: 'MyScamComponent',
|
||||
component: MyScamComponent,
|
||||
} as Meta<MyScamComponent>;
|
||||
import { within } from '@storybook/testing-library';
|
||||
import { expect } from '@storybook/jest';
|
||||
|
||||
export const Primary = {
|
||||
render: (args: MyScamComponent) => ({
|
||||
props: args,
|
||||
}),
|
||||
const meta: Meta<MyScamComponent> = {
|
||||
component: MyScamComponent,
|
||||
title: 'MyScamComponent',
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<MyScamComponent>;
|
||||
|
||||
export const Primary: Story = {
|
||||
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`] = `
|
||||
"import { Meta } from '@storybook/angular';
|
||||
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { ComponentBComponent } from './component-b.component';
|
||||
|
||||
export default {
|
||||
title: 'ComponentBComponent',
|
||||
component: ComponentBComponent,
|
||||
} as Meta<ComponentBComponent>;
|
||||
import { within } from '@storybook/testing-library';
|
||||
import { expect } from '@storybook/jest';
|
||||
|
||||
export const Primary = {
|
||||
render: (args: ComponentBComponent) => ({
|
||||
props: args,
|
||||
}),
|
||||
const meta: Meta<ComponentBComponent> = {
|
||||
component: ComponentBComponent,
|
||||
title: 'ComponentBComponent',
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<ComponentBComponent>;
|
||||
|
||||
export const Primary: Story = {
|
||||
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
|
||||
|
||||
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`] = `
|
||||
"import { Meta } from '@storybook/angular';
|
||||
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { StandaloneComponent } from './standalone.component';
|
||||
|
||||
export default {
|
||||
title: 'StandaloneComponent',
|
||||
component: StandaloneComponent,
|
||||
} as Meta<StandaloneComponent>;
|
||||
import { within } from '@storybook/testing-library';
|
||||
import { expect } from '@storybook/jest';
|
||||
|
||||
export const Primary = {
|
||||
render: (args: StandaloneComponent) => ({
|
||||
props: args,
|
||||
}),
|
||||
const meta: Meta<StandaloneComponent> = {
|
||||
component: StandaloneComponent,
|
||||
title: 'StandaloneComponent',
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<StandaloneComponent>;
|
||||
|
||||
export const Primary: Story = {
|
||||
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`] = `
|
||||
"import { Meta } from '@storybook/angular';
|
||||
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { SecondaryStandaloneComponent } from './secondary-standalone.component';
|
||||
|
||||
export default {
|
||||
title: 'SecondaryStandaloneComponent',
|
||||
component: SecondaryStandaloneComponent,
|
||||
} as Meta<SecondaryStandaloneComponent>;
|
||||
import { within } from '@storybook/testing-library';
|
||||
import { expect } from '@storybook/jest';
|
||||
|
||||
export const Primary = {
|
||||
render: (args: SecondaryStandaloneComponent) => ({
|
||||
props: args,
|
||||
}),
|
||||
const meta: Meta<SecondaryStandaloneComponent> = {
|
||||
component: SecondaryStandaloneComponent,
|
||||
title: 'SecondaryStandaloneComponent',
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<SecondaryStandaloneComponent>;
|
||||
|
||||
export const Primary: Story = {
|
||||
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`] = `
|
||||
"import { Meta } from '@storybook/angular';
|
||||
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { TestButtonComponent } from './test-button.component';
|
||||
|
||||
export default {
|
||||
title: 'TestButtonComponent',
|
||||
component: TestButtonComponent,
|
||||
} as Meta<TestButtonComponent>;
|
||||
import { within } from '@storybook/testing-library';
|
||||
import { expect } from '@storybook/jest';
|
||||
|
||||
export const Primary = {
|
||||
render: (args: TestButtonComponent) => ({
|
||||
props: args,
|
||||
}),
|
||||
const meta: Meta<TestButtonComponent> = {
|
||||
component: TestButtonComponent,
|
||||
title: 'TestButtonComponent',
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<TestButtonComponent>;
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
buttonType: 'button',
|
||||
style: 'default',
|
||||
@ -70,22 +78,37 @@ export const Primary = {
|
||||
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`] = `
|
||||
"import { Meta } from '@storybook/angular';
|
||||
"import type { Meta, StoryObj } from '@storybook/angular';
|
||||
import { TestButtonComponent } from './test-button.component';
|
||||
|
||||
export default {
|
||||
title: 'TestButtonComponent',
|
||||
component: TestButtonComponent,
|
||||
} as Meta<TestButtonComponent>;
|
||||
import { within } from '@storybook/testing-library';
|
||||
import { expect } from '@storybook/jest';
|
||||
|
||||
export const Primary = {
|
||||
render: (args: TestButtonComponent) => ({
|
||||
props: args,
|
||||
}),
|
||||
const meta: Meta<TestButtonComponent> = {
|
||||
component: TestButtonComponent,
|
||||
title: 'TestButtonComponent',
|
||||
};
|
||||
export default meta;
|
||||
type Story = StoryObj<TestButtonComponent>;
|
||||
|
||||
export const Primary: Story = {
|
||||
args: {
|
||||
buttonType: 'button',
|
||||
style: 'default',
|
||||
@ -93,5 +116,18 @@ export const Primary = {
|
||||
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 {
|
||||
name: string;
|
||||
cypressProject?: string;
|
||||
generateCypressSpecs?: boolean;
|
||||
interactionTests?: boolean;
|
||||
skipFormat?: boolean;
|
||||
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-priority": "important"
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
"description": "Set up Storybook interaction tests.",
|
||||
"x-prompt": "Do you want to set up Storybook interaction tests?",
|
||||
"x-priority": "important",
|
||||
"default": true
|
||||
},
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate `*.spec.ts` files in the Cypress e2e app generated by the `cypress-configure` generator.",
|
||||
"x-prompt": "Do you want to generate Cypress specs as well?",
|
||||
"x-priority": "important"
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default."
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `name` by default.",
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
|
||||
@ -10,6 +10,8 @@ import { angularStoriesGenerator } from './stories';
|
||||
// which is v9 while we are testing for the new v10 version
|
||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||
|
||||
// TODO(v18): remove Cypress
|
||||
|
||||
describe('angularStories generator: applications', () => {
|
||||
let tree: Tree;
|
||||
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 });
|
||||
|
||||
expect(
|
||||
tree.exists(`apps/${appName}/src/app/app.component.stories.ts`)
|
||||
).toBeTruthy();
|
||||
tree.read(`apps/${appName}/src/app/app.component.stories.ts`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should generate stories file for scam component', async () => {
|
||||
@ -90,11 +92,10 @@ describe('angularStories generator: applications', () => {
|
||||
});
|
||||
|
||||
expect(
|
||||
tree
|
||||
.read(
|
||||
`apps/${appName}/src/app/component-a/component-b/component-b.component.stories.ts`
|
||||
)
|
||||
.toString()
|
||||
tree.read(
|
||||
`apps/${appName}/src/app/component-a/component-b/component-b.component.stories.ts`,
|
||||
'utf-8'
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.exists(
|
||||
@ -113,20 +114,10 @@ describe('angularStories generator: applications', () => {
|
||||
await angularStoriesGenerator(tree, { name: appName });
|
||||
|
||||
expect(
|
||||
tree
|
||||
.read(`apps/${appName}/src/app/my-scam/my-scam.component.stories.ts`)
|
||||
.toString()
|
||||
tree.read(
|
||||
`apps/${appName}/src/app/my-scam/my-scam.component.stories.ts`,
|
||||
'utf-8'
|
||||
)
|
||||
).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 { writeJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/linter';
|
||||
import { componentGenerator } from '../component/component';
|
||||
import { librarySecondaryEntryPointGenerator } from '../library-secondary-entry-point/library-secondary-entry-point';
|
||||
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
|
||||
// which is v9 while we are testing for the new v10 version
|
||||
jest.mock('@nx/cypress/src/utils/cypress-version');
|
||||
// TODO(v18): remove Cypress
|
||||
|
||||
describe('angularStories generator: libraries', () => {
|
||||
const libName = 'test-ui-lib';
|
||||
@ -39,7 +39,6 @@ describe('angularStories generator: libraries', () => {
|
||||
async () =>
|
||||
await angularStoriesGenerator(tree, {
|
||||
name: libName,
|
||||
generateCypressSpecs: false,
|
||||
})
|
||||
).not.toThrow();
|
||||
});
|
||||
@ -47,13 +46,9 @@ describe('angularStories generator: libraries', () => {
|
||||
|
||||
describe('Stories for non-empty Angular library', () => {
|
||||
let tree: Tree;
|
||||
let cypressProjectGenerator;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = await createStorybookTestWorkspaceForLib(libName);
|
||||
cypressProjectGenerator = await (
|
||||
await import('@nx/storybook')
|
||||
).cypressProjectGenerator;
|
||||
});
|
||||
|
||||
it('should generate stories.ts files', async () => {
|
||||
@ -105,56 +100,11 @@ describe('angularStories generator: libraries', () => {
|
||||
).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 () => {
|
||||
await cypressProjectGenerator(tree, {
|
||||
linter: Linter.EsLint,
|
||||
name: libName,
|
||||
});
|
||||
|
||||
try {
|
||||
await angularStoriesGenerator(tree, { name: libName });
|
||||
await angularStoriesGenerator(tree, {
|
||||
name: libName,
|
||||
generateCypressSpecs: true,
|
||||
});
|
||||
} catch {
|
||||
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 () => {
|
||||
await cypressProjectGenerator(tree, {
|
||||
linter: Linter.EsLint,
|
||||
name: libName,
|
||||
});
|
||||
|
||||
await angularStoriesGenerator(tree, {
|
||||
name: libName,
|
||||
generateCypressSpecs: true,
|
||||
});
|
||||
|
||||
expect(
|
||||
@ -182,27 +126,11 @@ describe('angularStories generator: libraries', () => {
|
||||
`libs/${libName}/src/lib/variable-declare/variable-declare-view/variable-declare-view.component.stories.ts`
|
||||
)
|
||||
).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 () => {
|
||||
await cypressProjectGenerator(tree, {
|
||||
linter: Linter.EsLint,
|
||||
name: libName,
|
||||
});
|
||||
|
||||
await angularStoriesGenerator(tree, {
|
||||
name: libName,
|
||||
generateCypressSpecs: true,
|
||||
});
|
||||
|
||||
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`
|
||||
)
|
||||
).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 () => {
|
||||
await cypressProjectGenerator(tree, {
|
||||
linter: Linter.EsLint,
|
||||
name: libName,
|
||||
});
|
||||
|
||||
await angularStoriesGenerator(tree, {
|
||||
name: libName,
|
||||
generateCypressSpecs: true,
|
||||
});
|
||||
|
||||
expect(
|
||||
@ -259,12 +165,6 @@ describe('angularStories generator: libraries', () => {
|
||||
`libs/${libName}/src/lib/static-member-declarations/cmp2/cmp2.component.stories.ts`
|
||||
)
|
||||
).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 () => {
|
||||
|
||||
@ -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 componentStoryGenerator from '../component-story/component-story';
|
||||
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 type { StoriesGeneratorOptions } from './schema';
|
||||
import minimatch = require('minimatch');
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
export async function angularStoriesGenerator(
|
||||
tree: Tree,
|
||||
options: StoriesGeneratorOptions
|
||||
): Promise<void> {
|
||||
): Promise<GeneratorCallback> {
|
||||
const e2eProjectName = options.cypressProject ?? `${options.name}-e2e`;
|
||||
const e2eProject = getE2EProject(tree, e2eProjectName);
|
||||
const entryPoints = getProjectEntryPoints(tree, options.name);
|
||||
@ -59,6 +70,7 @@ export async function angularStoriesGenerator(
|
||||
componentName: info.name,
|
||||
componentPath: info.path,
|
||||
componentFileName: info.componentFileName,
|
||||
interactionTests: options.interactionTests ?? 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) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
return runTasksInSerial(...tasks);
|
||||
}
|
||||
|
||||
export default angularStoriesGenerator;
|
||||
|
||||
@ -1,9 +1,77 @@
|
||||
// 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`] = `
|
||||
"const config = {
|
||||
"import type { StorybookConfig } from '@storybook/angular';
|
||||
|
||||
const config: StorybookConfig = {
|
||||
stories: ['../**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
||||
addons: ['@storybook/addon-essentials'],
|
||||
addons: ['@storybook/addon-essentials', '@storybook/addon-interactions'],
|
||||
framework: {
|
||||
name: '@storybook/angular',
|
||||
options: {},
|
||||
@ -26,33 +94,12 @@ exports[`StorybookConfiguration generator should generate in the correct folder
|
||||
".prettierignore",
|
||||
".prettierrc",
|
||||
"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.preset.js",
|
||||
"libs/.gitignore",
|
||||
"libs/test-ui-lib/.eslintrc.json",
|
||||
"libs/test-ui-lib/.storybook/main.js",
|
||||
"libs/test-ui-lib/.storybook/preview.js",
|
||||
"libs/test-ui-lib/.storybook/main.ts",
|
||||
"libs/test-ui-lib/.storybook/preview.ts",
|
||||
"libs/test-ui-lib/.storybook/tsconfig.json",
|
||||
"libs/test-ui-lib/jest.config.ts",
|
||||
"libs/test-ui-lib/package.json",
|
||||
@ -158,33 +205,12 @@ exports[`StorybookConfiguration generator should generate the right files 1`] =
|
||||
".prettierignore",
|
||||
".prettierrc",
|
||||
"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.preset.js",
|
||||
"libs/.gitignore",
|
||||
"libs/test-ui-lib/.eslintrc.json",
|
||||
"libs/test-ui-lib/.storybook/main.js",
|
||||
"libs/test-ui-lib/.storybook/preview.js",
|
||||
"libs/test-ui-lib/.storybook/main.ts",
|
||||
"libs/test-ui-lib/.storybook/preview.ts",
|
||||
"libs/test-ui-lib/.storybook/tsconfig.json",
|
||||
"libs/test-ui-lib/jest.config.ts",
|
||||
"libs/test-ui-lib/package.json",
|
||||
|
||||
@ -21,6 +21,7 @@ export async function generateStories(
|
||||
options.configureCypress && options.generateCypressSpecs,
|
||||
cypressProject: e2eProjectName,
|
||||
ignorePaths: options.ignorePaths,
|
||||
interactionTests: options.interactionTests,
|
||||
skipFormat: true,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
import type { Linter } from '@nx/linter';
|
||||
|
||||
export interface StorybookConfigurationOptions {
|
||||
configureCypress: boolean;
|
||||
configureStaticServe?: boolean;
|
||||
generateCypressSpecs: boolean;
|
||||
generateStories: boolean;
|
||||
linter: Linter;
|
||||
name: string;
|
||||
cypressDirectory?: string;
|
||||
tsConfiguration?: boolean;
|
||||
skipFormat?: boolean;
|
||||
ignorePaths?: string[];
|
||||
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": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to configure Cypress or not.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
@ -41,7 +41,7 @@
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Specifies whether to automatically generate test files in the generated Cypress e2e app.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
@ -53,7 +53,7 @@
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
|
||||
@ -44,31 +44,16 @@ describe('StorybookConfiguration generator', () => {
|
||||
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 () => {
|
||||
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
||||
name: libName,
|
||||
configureCypress: false,
|
||||
generateCypressSpecs: 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(
|
||||
tree.exists('libs/test-ui-lib/.storybook/tsconfig.json')
|
||||
).toBeTruthy();
|
||||
expect(tree.exists('apps/test-ui-lib-e2e/cypress.config.ts')).toBeFalsy();
|
||||
expect(
|
||||
tree.exists(
|
||||
'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'
|
||||
)
|
||||
).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 () => {
|
||||
await storybookConfigurationGenerator(tree, {
|
||||
name: libName,
|
||||
configureCypress: false,
|
||||
generateCypressSpecs: false,
|
||||
generateStories: false,
|
||||
linter: Linter.None,
|
||||
});
|
||||
|
||||
expect(
|
||||
tree.read('libs/test-ui-lib/.storybook/main.js', 'utf-8')
|
||||
tree.read('libs/test-ui-lib/.storybook/main.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should configure everything at once', async () => {
|
||||
it('should configure everything at once - and interaction tests too', async () => {
|
||||
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
||||
name: libName,
|
||||
configureCypress: true,
|
||||
generateCypressSpecs: 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(
|
||||
tree.exists('libs/test-ui-lib/.storybook/tsconfig.json')
|
||||
).toBeTruthy();
|
||||
expect(tree.exists('apps/test-ui-lib-e2e/cypress.config.ts')).toBeTruthy();
|
||||
expect(
|
||||
tree.exists(
|
||||
'libs/test-ui-lib/src/lib/test-button/test-button.component.stories.ts'
|
||||
tree.read(
|
||||
'libs/test-ui-lib/src/lib/test-button/test-button.component.stories.ts',
|
||||
'utf-8'
|
||||
)
|
||||
).toBeTruthy();
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.exists(
|
||||
'libs/test-ui-lib/src/lib/test-other/test-other.component.stories.ts'
|
||||
tree.read(
|
||||
'libs/test-ui-lib/src/lib/test-other/test-other.component.stories.ts',
|
||||
'utf-8'
|
||||
)
|
||||
).toBeTruthy();
|
||||
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();
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should generate the right files', async () => {
|
||||
@ -171,8 +133,6 @@ describe('StorybookConfiguration generator', () => {
|
||||
|
||||
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
||||
name: libName,
|
||||
configureCypress: true,
|
||||
generateCypressSpecs: true,
|
||||
generateStories: true,
|
||||
});
|
||||
|
||||
@ -210,10 +170,7 @@ describe('StorybookConfiguration generator', () => {
|
||||
|
||||
await storybookConfigurationGenerator(tree, <StorybookConfigurationOptions>{
|
||||
name: libName,
|
||||
configureCypress: true,
|
||||
generateCypressSpecs: true,
|
||||
generateStories: true,
|
||||
cypressDirectory: 'one/two',
|
||||
});
|
||||
|
||||
expect(listFiles(tree)).toMatchSnapshot();
|
||||
|
||||
@ -5,6 +5,7 @@ import { generateStorybookConfiguration } from './lib/generate-storybook-configu
|
||||
import { validateOptions } from './lib/validate-options';
|
||||
import type { StorybookConfigurationOptions } from './schema';
|
||||
|
||||
// TODO(v18): remove Cypress
|
||||
export async function storybookConfigurationGenerator(
|
||||
tree: Tree,
|
||||
options: StorybookConfigurationOptions
|
||||
@ -14,11 +15,19 @@ export async function storybookConfigurationGenerator(
|
||||
|
||||
const storybookGeneratorInstallTask = await generateStorybookConfiguration(
|
||||
tree,
|
||||
options
|
||||
{
|
||||
...options,
|
||||
interactionTests: options.interactionTests ?? true, // default is true
|
||||
tsConfiguration: options.tsConfiguration ?? true, // default is true
|
||||
}
|
||||
);
|
||||
|
||||
if (options.generateStories) {
|
||||
await generateStories(tree, { ...options, skipFormat: true });
|
||||
await generateStories(tree, {
|
||||
...options,
|
||||
interactionTests: options.interactionTests ?? true,
|
||||
skipFormat: true,
|
||||
});
|
||||
}
|
||||
|
||||
if (!options.skipFormat) {
|
||||
|
||||
@ -6,11 +6,11 @@ import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript'
|
||||
|
||||
let tsModule: typeof import('typescript');
|
||||
|
||||
export type KnobType = 'text' | 'boolean' | 'number' | 'select';
|
||||
export type ArgType = 'text' | 'boolean' | 'number' | 'select';
|
||||
|
||||
export interface InputDescriptor {
|
||||
name: string;
|
||||
type: KnobType;
|
||||
type: ArgType;
|
||||
defaultValue?: string;
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ export function getComponentProps(
|
||||
: node.name.getText()
|
||||
: node.name.getText();
|
||||
|
||||
const type = getKnobType(node);
|
||||
const type = getArgType(node);
|
||||
const defaultValue = getArgsDefaultValueFn(node);
|
||||
|
||||
return {
|
||||
@ -76,27 +76,27 @@ export function getComponentProps(
|
||||
return props;
|
||||
}
|
||||
|
||||
export function getKnobType(property: PropertyDeclaration): KnobType {
|
||||
export function getArgType(property: PropertyDeclaration): ArgType {
|
||||
if (!tsModule) {
|
||||
tsModule = ensureTypescript();
|
||||
}
|
||||
if (property.type) {
|
||||
const typeName = property.type.getText();
|
||||
const typeNameToKnobType: Record<string, KnobType> = {
|
||||
const typeNameToArgType: Record<string, ArgType> = {
|
||||
string: 'text',
|
||||
number: 'number',
|
||||
boolean: 'boolean',
|
||||
};
|
||||
return typeNameToKnobType[typeName] || 'text';
|
||||
return typeNameToArgType[typeName] || 'text';
|
||||
}
|
||||
if (property.initializer) {
|
||||
const initializerKindToKnobType: Record<number, KnobType> = {
|
||||
const initializerKindToArgType: Record<number, ArgType> = {
|
||||
[tsModule.SyntaxKind.StringLiteral]: 'text',
|
||||
[tsModule.SyntaxKind.NumericLiteral]: 'number',
|
||||
[tsModule.SyntaxKind.TrueKeyword]: 'boolean',
|
||||
[tsModule.SyntaxKind.FalseKeyword]: 'boolean',
|
||||
};
|
||||
return initializerKindToKnobType[property.initializer.kind] || 'text';
|
||||
return initializerKindToArgType[property.initializer.kind] || 'text';
|
||||
}
|
||||
return 'text';
|
||||
}
|
||||
|
||||
@ -22,6 +22,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -52,6 +56,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -82,6 +90,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -112,6 +124,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -142,6 +158,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -172,6 +192,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -202,6 +226,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -232,6 +260,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -262,6 +294,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -292,6 +328,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -322,6 +362,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -352,6 +396,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -382,6 +430,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -412,6 +464,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -442,6 +498,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -472,6 +532,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -502,6 +566,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -532,6 +600,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -559,6 +631,7 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to One!/gi)).toBeTruthy();
|
||||
@ -586,6 +659,7 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Two!/gi)).toBeTruthy();
|
||||
@ -615,6 +689,9 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Three!/gi)).toBeTruthy();
|
||||
@ -648,6 +725,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -678,6 +759,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -705,6 +790,7 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
@ -732,6 +818,7 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to TestUiLib!/gi)).toBeTruthy();
|
||||
|
||||
@ -25,6 +25,9 @@ export const Primary = {
|
||||
|
||||
<% 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(/Welcome to <%=componentName%>!/gi)).toBeTruthy();
|
||||
|
||||
@ -19,6 +19,7 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to NxWelcome!/gi)).toBeTruthy();
|
||||
@ -49,6 +50,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
|
||||
@ -19,6 +19,7 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to TestUiLib!/gi)).toBeTruthy();
|
||||
@ -49,6 +50,10 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {
|
||||
name: '',
|
||||
displayAge: false,
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
expect(canvas.getByText(/Welcome to Test!/gi)).toBeTruthy();
|
||||
|
||||
@ -20,12 +20,12 @@
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate `*.spec.ts` files in the cypress e2e app generated by the cypress-configure generator.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"cypressProject": {
|
||||
"type": "string",
|
||||
"description": "The Cypress project to generate the stories under. This is inferred from `project` by default.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"interactionTests": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -5,18 +5,23 @@ import {
|
||||
getComponentNode,
|
||||
} from '../../utils/ast-utils';
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
convertNxGenerator,
|
||||
ensurePackage,
|
||||
formatFiles,
|
||||
GeneratorCallback,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
ProjectConfiguration,
|
||||
runTasksInSerial,
|
||||
Tree,
|
||||
visitNotIgnoredFiles,
|
||||
} from '@nx/devkit';
|
||||
import { basename, join } from 'path';
|
||||
import minimatch = require('minimatch');
|
||||
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
let tsModule: typeof import('typescript');
|
||||
|
||||
@ -24,10 +29,16 @@ export interface StorybookStoriesSchema {
|
||||
project: string;
|
||||
interactionTests?: boolean;
|
||||
js?: boolean;
|
||||
cypressProject?: string;
|
||||
generateCypressSpecs?: boolean;
|
||||
ignorePaths?: string[];
|
||||
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(
|
||||
@ -87,13 +98,14 @@ export async function createAllStories(
|
||||
projectName: string,
|
||||
interactionTests: boolean,
|
||||
js: boolean,
|
||||
projects: Map<string, ProjectConfiguration>,
|
||||
projectConfiguration: ProjectConfiguration,
|
||||
generateCypressSpecs?: boolean,
|
||||
cypressProject?: string,
|
||||
ignorePaths?: string[]
|
||||
) {
|
||||
const { isTheFileAStory } = await import('@nx/storybook/src/utils/utilities');
|
||||
const projects = getProjects(tree);
|
||||
const projectConfiguration = projects.get(projectName);
|
||||
|
||||
const { sourceRoot, root } = projectConfiguration;
|
||||
let componentPaths: string[] = [];
|
||||
|
||||
@ -164,19 +176,35 @@ export async function storiesGenerator(
|
||||
host: Tree,
|
||||
schema: StorybookStoriesSchema
|
||||
) {
|
||||
const projects = getProjects(host);
|
||||
const projectConfiguration = projects.get(schema.project);
|
||||
await createAllStories(
|
||||
host,
|
||||
schema.project,
|
||||
schema.interactionTests ?? true,
|
||||
schema.js,
|
||||
projects,
|
||||
projectConfiguration,
|
||||
schema.generateCypressSpecs,
|
||||
schema.cypressProject,
|
||||
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) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
return runTasksInSerial(...tasks);
|
||||
}
|
||||
|
||||
export default storiesGenerator;
|
||||
|
||||
@ -43,6 +43,7 @@ export const Primary = {
|
||||
};
|
||||
|
||||
export const Heading: Story = {
|
||||
args: {},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
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 { logger, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
@ -10,7 +10,7 @@ import {
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
async function generateStories(host: Tree, schema: StorybookConfigureSchema) {
|
||||
// TODO(katerina): remove Cypress for Nx 18
|
||||
// TODO(v18): remove Cypress
|
||||
ensurePackage('@nx/cypress', nxVersion);
|
||||
const { getE2eProjectName } = await import(
|
||||
'@nx/cypress/src/utils/project-name'
|
||||
|
||||
@ -4,12 +4,21 @@ export interface StorybookConfigureSchema {
|
||||
name: string;
|
||||
interactionTests?: boolean;
|
||||
generateStories?: boolean;
|
||||
configureCypress?: boolean;
|
||||
generateCypressSpecs?: boolean;
|
||||
js?: boolean;
|
||||
tsConfiguration?: boolean;
|
||||
linter?: Linter;
|
||||
cypressDirectory?: string;
|
||||
ignorePaths?: string[];
|
||||
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": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"generateStories": {
|
||||
"type": "boolean",
|
||||
@ -41,7 +41,7 @@
|
||||
"generateCypressSpecs": {
|
||||
"type": "boolean",
|
||||
"description": "Automatically generate test files in the Cypress E2E app generated by the `cypress-configure` generator.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"configureStaticServe": {
|
||||
"type": "boolean",
|
||||
@ -53,7 +53,7 @@
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Placed at the root by default.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"js": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -26,7 +26,18 @@ export function getArgsDefaultValue(property: ts.SyntaxKind): string {
|
||||
export function getDefaultsForComponent(
|
||||
sourceFile: ts.SourceFile,
|
||||
cmpDeclaration: ts.Node
|
||||
) {
|
||||
): {
|
||||
propsTypeName: string;
|
||||
props: {
|
||||
name: string;
|
||||
defaultValue: any;
|
||||
}[];
|
||||
argTypes: {
|
||||
name: string;
|
||||
type: string;
|
||||
actionText: string;
|
||||
}[];
|
||||
} {
|
||||
if (!tsModule) {
|
||||
tsModule = ensureTypescript();
|
||||
}
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
export { configurationGenerator } from './src/generators/configuration/configuration';
|
||||
export { cypressProjectGenerator } from './src/generators/cypress-project/cypress-project';
|
||||
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
|
||||
|
||||
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';
|
||||
|
||||
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",
|
||||
"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';
|
||||
|
||||
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, {
|
||||
name: 'web',
|
||||
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, {
|
||||
name: 'test-ui-lib',
|
||||
standaloneConfig: false,
|
||||
tsConfiguration: true,
|
||||
uiFramework: '@storybook/angular',
|
||||
});
|
||||
const project = readProjectConfiguration(tree, 'test-ui-lib');
|
||||
@ -172,11 +171,10 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
|
||||
).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, {
|
||||
name: 'test-ui-lib',
|
||||
standaloneConfig: false,
|
||||
tsConfiguration: true,
|
||||
uiFramework: '@storybook/angular',
|
||||
});
|
||||
|
||||
@ -278,7 +276,6 @@ describe('@nx/storybook:configuration for Storybook v7', () => {
|
||||
});
|
||||
await configurationGenerator(tree, {
|
||||
name: 'main-vite-ts',
|
||||
tsConfiguration: true,
|
||||
uiFramework: '@storybook/react-vite',
|
||||
});
|
||||
await configurationGenerator(tree, {
|
||||
|
||||
@ -37,12 +37,10 @@ import {
|
||||
import {
|
||||
coreJsVersion,
|
||||
nxVersion,
|
||||
storybookJestVersion,
|
||||
storybookTestingLibraryVersion,
|
||||
storybookTestRunnerVersion,
|
||||
storybookVersion,
|
||||
tsNodeVersion,
|
||||
} from '../../utils/versions';
|
||||
import { interactionTestsDependencies } from './lib/interaction-testing.utils';
|
||||
|
||||
export async function configurationGenerator(
|
||||
tree: Tree,
|
||||
@ -151,7 +149,7 @@ export async function configurationGenerator(
|
||||
addStaticTarget(tree, schema);
|
||||
}
|
||||
|
||||
// TODO(katerina): remove Cypress for Nx 18
|
||||
// TODO(v18): remove Cypress
|
||||
if (schema.configureCypress) {
|
||||
const e2eProject = await getE2EProjectName(tree, schema.name);
|
||||
if (!e2eProject) {
|
||||
@ -174,17 +172,17 @@ export async function configurationGenerator(
|
||||
}
|
||||
}
|
||||
|
||||
const devDeps = {};
|
||||
let devDeps = {};
|
||||
|
||||
if (schema.tsConfiguration) {
|
||||
devDeps['ts-node'] = tsNodeVersion;
|
||||
}
|
||||
|
||||
if (schema.interactionTests) {
|
||||
devDeps['@storybook/test-runner'] = storybookTestRunnerVersion;
|
||||
devDeps['@storybook/addon-interactions'] = storybookVersion;
|
||||
devDeps['@storybook/testing-library'] = storybookTestingLibraryVersion;
|
||||
devDeps['@storybook/jest'] = storybookJestVersion;
|
||||
devDeps = {
|
||||
...devDeps,
|
||||
...interactionTestsDependencies(),
|
||||
};
|
||||
}
|
||||
|
||||
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 {
|
||||
name: string;
|
||||
uiFramework?: UiFramework7;
|
||||
configureCypress?: boolean;
|
||||
linter?: Linter;
|
||||
js?: boolean;
|
||||
interactionTests?: boolean;
|
||||
tsConfiguration?: boolean;
|
||||
cypressDirectory?: string;
|
||||
standaloneConfig?: boolean;
|
||||
configureStaticServe?: 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": {
|
||||
"type": "boolean",
|
||||
"description": "Run the cypress-configure generator.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"cypressDirectory": {
|
||||
"type": "string",
|
||||
"description": "A directory where the Cypress project will be placed. Added at root by default.",
|
||||
"x-deprecated": "Please use Storybook interaction tests instead."
|
||||
"x-deprecated": "Use interactionTests instead. This option will be removed in v18."
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user