336 lines
10 KiB
TypeScript
336 lines
10 KiB
TypeScript
import {
|
|
checkFilesExist,
|
|
cleanupProject,
|
|
getPackageManagerCommand,
|
|
isNotWindows,
|
|
killPorts,
|
|
newProject,
|
|
readFile,
|
|
runCLI,
|
|
runCommand,
|
|
runCypressTests,
|
|
tmpProjPath,
|
|
uniq,
|
|
updateJson,
|
|
} from '@nrwl/e2e/utils';
|
|
import { writeFileSync } from 'fs';
|
|
|
|
describe('Storybook for Angular', () => {
|
|
let proj: string;
|
|
|
|
beforeAll(() => {
|
|
proj = newProject();
|
|
|
|
// TODO(jack): Overriding enhanced-resolve to 5.10.0 now until the package is fixed.
|
|
// See: https://github.com/webpack/enhanced-resolve/issues/362
|
|
updateJson('package.json', (json) => {
|
|
if (process.env.SELECTED_PM === 'yarn') {
|
|
json['resolutions'] = {
|
|
'enhanced-resolve': '5.10.0',
|
|
};
|
|
} else if (process.env.SELECTED_PM === 'npm') {
|
|
json['overrides'] = {
|
|
'enhanced-resolve': '5.10.0',
|
|
};
|
|
} else {
|
|
json['pnpm'] = {
|
|
overrides: {
|
|
'enhanced-resolve': '5.10.0',
|
|
},
|
|
};
|
|
}
|
|
return json;
|
|
});
|
|
runCommand(getPackageManagerCommand().install);
|
|
});
|
|
|
|
afterAll(() => cleanupProject());
|
|
|
|
it('should not overwrite global storybook config files', () => {
|
|
const angularStorybookLib = uniq('test-ui-lib-angular');
|
|
runCLI(
|
|
`generate @nrwl/angular:lib ${angularStorybookLib} --no-interactive`
|
|
);
|
|
runCLI(
|
|
`generate @nrwl/angular:storybook-configuration ${angularStorybookLib} --generateStories --no-interactive`
|
|
);
|
|
|
|
checkFilesExist(`.storybook/main.js`);
|
|
writeFileSync(
|
|
tmpProjPath(`.storybook/main.js`),
|
|
`
|
|
module.exports = {
|
|
stories: [],
|
|
addons: ['@storybook/addon-essentials'],
|
|
};
|
|
|
|
console.log('hi there');
|
|
`
|
|
);
|
|
|
|
// generate another lib with storybook config
|
|
const anotherAngularStorybookLib = uniq('test-ui-lib-angular2');
|
|
runCLI(
|
|
`generate @nrwl/angular:lib ${anotherAngularStorybookLib} --no-interactive`
|
|
);
|
|
runCLI(
|
|
`generate @nrwl/angular:storybook-configuration ${anotherAngularStorybookLib} --generateStories --no-interactive`
|
|
);
|
|
|
|
expect(readFile(`.storybook/main.js`)).toContain(
|
|
`console.log('hi there');`
|
|
);
|
|
});
|
|
|
|
describe('build storybook', () => {
|
|
let angularStorybookLib;
|
|
|
|
beforeAll(() => {
|
|
angularStorybookLib = uniq('test-ui-lib');
|
|
createTestUILib(angularStorybookLib);
|
|
runCLI(
|
|
`generate @nrwl/angular:storybook-configuration ${angularStorybookLib} --generateStories --no-interactive`
|
|
);
|
|
});
|
|
|
|
xit('should execute e2e tests using Cypress running against Storybook', async () => {
|
|
if (isNotWindows()) {
|
|
const myapp = uniq('myapp');
|
|
runCLI(`generate @nrwl/angular:app ${myapp} --no-interactive`);
|
|
|
|
const myAngularLib = uniq('test-ui-lib');
|
|
createTestUILib(myAngularLib);
|
|
const myReactLib = uniq('test-ui-lib-react');
|
|
runCLI(`generate @nrwl/react:lib ${myReactLib} --no-interactive`);
|
|
runCLI(
|
|
`generate @nrwl/react:component Button --project=${myReactLib} --no-interactive`
|
|
);
|
|
writeFileSync(
|
|
tmpProjPath(`libs/${myReactLib}/src/lib/button.tsx`),
|
|
`
|
|
import React from 'react';
|
|
|
|
import './button.css';
|
|
|
|
export type ButtonStyle = 'default' | 'primary' | 'warning';
|
|
|
|
/* eslint-disable-next-line */
|
|
export interface ButtonProps {
|
|
text?: string;
|
|
style?: ButtonStyle;
|
|
padding?: number;
|
|
}
|
|
|
|
export const Button = (props: ButtonProps) => {
|
|
return (
|
|
<button className={props.style} style={{ padding: \`\${props.padding}px\` }}>
|
|
{props.text}
|
|
</button>
|
|
);
|
|
};
|
|
|
|
export default Button;
|
|
`
|
|
);
|
|
writeFileSync(
|
|
tmpProjPath(`libs/${myReactLib}/src/lib/button.stories.tsx`),
|
|
`
|
|
import { Story, Meta } from '@storybook/react';
|
|
import { Button, ButtonProps } from './button';
|
|
|
|
export default {
|
|
component: Button,
|
|
title: 'Button',
|
|
} as Meta;
|
|
|
|
const Template: Story<ButtonProps> = (args) => <Button {...args} />;
|
|
|
|
export const Primary = Template.bind({});
|
|
Primary.args = {
|
|
text: 'Click me',
|
|
padding: 0,
|
|
style: 'default',
|
|
};
|
|
`
|
|
);
|
|
|
|
runCLI(
|
|
`generate @nrwl/angular:storybook-configuration ${myAngularLib} --configureCypress --generateStories --generateCypressSpecs --no-interactive`
|
|
);
|
|
runCLI(
|
|
`generate @nrwl/angular:stories ${myAngularLib} --generateCypressSpecs --no-interactive`
|
|
);
|
|
|
|
// TODO: need to fix the issue `ENOENT: no such file or directory` for below test-button.component.spec.ts
|
|
writeFileSync(
|
|
tmpProjPath(
|
|
`apps/${myAngularLib}-e2e/src/integration/test-button/test-button.component.spec.ts`
|
|
),
|
|
`
|
|
describe('${myAngularLib}', () => {
|
|
|
|
it('should render the component', () => {
|
|
cy.visit('/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:default;age;isDisabled:false');
|
|
cy.get('proj-test-button').should('exist');
|
|
cy.get('button').should('not.be.disabled');
|
|
cy.get('button').should('have.class', 'default');
|
|
cy.contains('You are 0 years old.');
|
|
});
|
|
it('should adjust the controls', () => {
|
|
cy.visit('/iframe.html?id=testbuttoncomponent--primary&args=buttonType:button;style:primary;age:10;isDisabled:true');
|
|
cy.get('button').should('be.disabled');
|
|
cy.get('button').should('have.class', 'primary');
|
|
cy.contains('You are 10 years old.');
|
|
});
|
|
});
|
|
`
|
|
);
|
|
|
|
runCLI(
|
|
`generate @nrwl/react:storybook-configuration ${myReactLib} --configureCypress --no-interactive`
|
|
);
|
|
|
|
// The following line (mkdirSync...) is not needed,
|
|
// since the above schematic creates this directory.
|
|
// So, if we leave it there, there's an error saying the directory exists.
|
|
// I am not sure how it worked as it was :/
|
|
|
|
// mkdirSync(tmpProjPath(`apps/${myReactLib}-e2e/src/integration`));
|
|
writeFileSync(
|
|
tmpProjPath(`apps/${myReactLib}-e2e/src/integration/button.spec.ts`),
|
|
`
|
|
describe('react-ui', () => {
|
|
it('should render the component', () => {
|
|
cy.visit(
|
|
'/iframe.html?id=button--primary&args=style:default;padding;text:Click%20me'
|
|
);
|
|
cy.get('button').should('exist');
|
|
cy.get('button').should('have.class', 'default');
|
|
});
|
|
it('should adjust the controls', () => {
|
|
cy.visit(
|
|
'/iframe.html?id=button--primary&args=style:primary;padding:10;text:Other'
|
|
);
|
|
cy.get('button').should('have.class', 'primary');
|
|
});
|
|
});
|
|
`
|
|
);
|
|
|
|
if (runCypressTests()) {
|
|
const e2eResults = runCLI(`e2e ${myAngularLib}-e2e --no-watch`);
|
|
expect(e2eResults).toContain('All specs passed!');
|
|
expect(await killPorts()).toBeTruthy();
|
|
}
|
|
|
|
runCLI(`run ${myAngularLib}:build-storybook`);
|
|
|
|
checkFilesExist(`dist/storybook/${myAngularLib}/index.html`);
|
|
}
|
|
}, 1000000);
|
|
|
|
it('should build an Angular based storybook that references another lib', () => {
|
|
// create another lib with a component
|
|
const anotherTestLib = uniq('test-another-lib');
|
|
runCLI(
|
|
`g @nrwl/angular:library ${anotherTestLib} --buildable --no-interactive`
|
|
);
|
|
runCLI(
|
|
`g @nrwl/angular:component my-test-cmp --project=${anotherTestLib} --no-interactive`
|
|
);
|
|
|
|
// update index.ts and export it
|
|
writeFileSync(
|
|
tmpProjPath(`libs/${anotherTestLib}/src/index.ts`),
|
|
`
|
|
export * from './lib/my-test-cmp/my-test-cmp.component';
|
|
`
|
|
);
|
|
|
|
// create a story in the first lib to reference the cmp from the 2nd lib
|
|
writeFileSync(
|
|
tmpProjPath(
|
|
`libs/${angularStorybookLib}/src/lib/myteststory.stories.ts`
|
|
),
|
|
`
|
|
import { moduleMetadata, Story, Meta } from '@storybook/angular';
|
|
import { MyTestCmpComponent } from '@${proj}/${anotherTestLib}';
|
|
|
|
export default {
|
|
title: 'My Test Cmp',
|
|
component: MyTestCmpComponent,
|
|
decorators: [
|
|
moduleMetadata({
|
|
imports: [],
|
|
})
|
|
],
|
|
} as Meta<MyTestCmpComponent>;
|
|
|
|
const Template: Story<MyTestCmpComponent> = (args: MyTestCmpComponent) => ({
|
|
component: MyTestCmpComponent,
|
|
props: args,
|
|
});
|
|
|
|
|
|
export const Primary = Template.bind({});
|
|
Primary.args = {
|
|
}
|
|
`
|
|
);
|
|
|
|
// build Angular lib
|
|
runCLI(`run ${angularStorybookLib}:build-storybook`);
|
|
checkFilesExist(`dist/storybook/${angularStorybookLib}/index.html`);
|
|
}, 1000000);
|
|
});
|
|
});
|
|
|
|
export function createTestUILib(libName: string): void {
|
|
runCLI(
|
|
`g @nrwl/angular:library ${libName} --no-interactive --buildable=true`
|
|
);
|
|
runCLI(
|
|
`g @nrwl/angular:component test-button --project=${libName} --no-interactive`
|
|
);
|
|
|
|
writeFileSync(
|
|
tmpProjPath(`libs/${libName}/src/lib/test-button/test-button.component.ts`),
|
|
`
|
|
import { Component, OnInit, Input } from '@angular/core';
|
|
|
|
export type ButtonStyle = 'default' | 'primary' | 'accent';
|
|
|
|
@Component({
|
|
selector: 'proj-test-button',
|
|
templateUrl: './test-button.component.html',
|
|
styleUrls: ['./test-button.component.css']
|
|
})
|
|
export class TestButtonComponent implements OnInit {
|
|
@Input('buttonType') buttonType = 'button';
|
|
@Input() style: ButtonStyle = 'default';
|
|
@Input() age!: number;
|
|
@Input() isDisabled = false;
|
|
|
|
constructor() { }
|
|
|
|
ngOnInit() {
|
|
}
|
|
|
|
}
|
|
`
|
|
);
|
|
|
|
writeFileSync(
|
|
tmpProjPath(
|
|
`libs/${libName}/src/lib/test-button/test-button.component.html`
|
|
),
|
|
`
|
|
<button [disabled]="isDisabled" [attr.type]="buttonType" [ngClass]="style">Click me</button>
|
|
<p>You are {{age}} years old.</p>
|
|
`
|
|
);
|
|
runCLI(
|
|
`g @nrwl/angular:component test-other --project=${libName} --no-interactive`
|
|
);
|
|
}
|