UNPKG

@storybook/angular

Version:

Storybook for Angular: Develop Angular components in isolation with hot reloading.

320 lines (319 loc) • 15.9 kB
// @vitest-environment happy-dom var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { Component, EventEmitter, Input, Output } from '@angular/core'; import { describe, expect, it } from 'vitest'; import { TestBed } from '@angular/core/testing'; import { BehaviorSubject } from 'rxjs'; import { getApplication } from './StorybookModule'; import { storyPropsProvider } from './StorybookProvider'; import { PropertyExtractor } from './utils/PropertyExtractor'; describe('StorybookModule', () => { describe('getStorybookModuleMetadata', () => { describe('with simple component', () => { let FooComponent = class FooComponent { constructor() { this.output = new EventEmitter(); this.localOutput = new EventEmitter(); this.localFunction = () => ''; this.setterCallNb = 0; } set setter(value) { this.setterCallNb += 1; } }; __decorate([ Input(), __metadata("design:type", String) ], FooComponent.prototype, "input", void 0); __decorate([ Input('inputBindingPropertyName'), __metadata("design:type", String) ], FooComponent.prototype, "localPropertyName", void 0); __decorate([ Input(), __metadata("design:type", String), __metadata("design:paramtypes", [String]) ], FooComponent.prototype, "setter", null); __decorate([ Output(), __metadata("design:type", Object) ], FooComponent.prototype, "output", void 0); __decorate([ Output('outputBindingPropertyName'), __metadata("design:type", Object) ], FooComponent.prototype, "localOutput", void 0); FooComponent = __decorate([ Component({ selector: 'foo', template: ` <p id="input">{{ input }}</p> <p id="inputBindingPropertyName">{{ localPropertyName }}</p> <p id="setterCallNb">{{ setterCallNb }}</p> <p id="localProperty">{{ localProperty }}</p> <p id="localFunction">{{ localFunction() }}</p> <p id="output" (click)="output.emit('outputEmitted')"></p> <p id="outputBindingPropertyName" (click)="localOutput.emit('outputEmitted')"></p> `, }) ], FooComponent); it('should initialize inputs', async () => { const props = { input: 'input', inputBindingPropertyName: 'inputBindingPropertyName', localProperty: 'localProperty', localFunction: () => 'localFunction', }; const analyzedMetadata = new PropertyExtractor({}, FooComponent); await analyzedMetadata.init(); const application = getApplication({ storyFnAngular: { props }, component: FooComponent, targetSelector: 'my-selector', analyzedMetadata, }); const { fixture } = await configureTestingModule({ imports: [application], providers: [storyPropsProvider(new BehaviorSubject(props))], }); fixture.detectChanges(); expect(fixture.nativeElement.querySelector('p#input').innerHTML).toEqual(props.input); expect(fixture.nativeElement.querySelector('p#inputBindingPropertyName').innerHTML).toEqual(props.inputBindingPropertyName); expect(fixture.nativeElement.querySelector('p#localProperty').innerHTML).toEqual(props.localProperty); expect(fixture.nativeElement.querySelector('p#localFunction').innerHTML).toEqual(props.localFunction()); }); it('should initialize outputs', async () => { let expectedOutputValue; let expectedOutputBindingValue; const props = { output: (value) => { expectedOutputValue = value; }, outputBindingPropertyName: (value) => { expectedOutputBindingValue = value; }, }; const analyzedMetadata = new PropertyExtractor({}, FooComponent); await analyzedMetadata.init(); const application = getApplication({ storyFnAngular: { props }, component: FooComponent, targetSelector: 'my-selector', analyzedMetadata, }); const { fixture } = await configureTestingModule({ imports: [application], providers: [storyPropsProvider(new BehaviorSubject(props))], }); fixture.detectChanges(); fixture.nativeElement.querySelector('p#output').click(); fixture.nativeElement.querySelector('p#outputBindingPropertyName').click(); expect(expectedOutputValue).toEqual('outputEmitted'); expect(expectedOutputBindingValue).toEqual('outputEmitted'); }); it('should change inputs if storyProps$ Subject emit', async () => { const initialProps = { input: 'input', inputBindingPropertyName: '', }; const storyProps$ = new BehaviorSubject(initialProps); const analyzedMetadata = new PropertyExtractor({}, FooComponent); await analyzedMetadata.init(); const application = getApplication({ storyFnAngular: { props: initialProps }, component: FooComponent, targetSelector: 'my-selector', analyzedMetadata, }); const { fixture } = await configureTestingModule({ imports: [application], providers: [storyPropsProvider(storyProps$)], }); fixture.detectChanges(); expect(fixture.nativeElement.querySelector('p#input').innerHTML).toEqual(initialProps.input); expect(fixture.nativeElement.querySelector('p#inputBindingPropertyName').innerHTML).toEqual(''); const newProps = { input: 'new input', inputBindingPropertyName: 'new inputBindingPropertyName', localProperty: 'new localProperty', localFunction: () => 'new localFunction', }; storyProps$.next(newProps); fixture.detectChanges(); expect(fixture.nativeElement.querySelector('p#input').innerHTML).toEqual(newProps.input); expect(fixture.nativeElement.querySelector('p#inputBindingPropertyName').innerHTML).toEqual(newProps.inputBindingPropertyName); expect(fixture.nativeElement.querySelector('p#localProperty').innerHTML).toEqual(newProps.localProperty); expect(fixture.nativeElement.querySelector('p#localFunction').innerHTML).toEqual(newProps.localFunction()); }); it('should override outputs if storyProps$ Subject emit', async () => { let expectedOutputValue; let expectedOutputBindingValue; const initialProps = { input: '', output: (value) => { expectedOutputValue = value; }, outputBindingPropertyName: (value) => { expectedOutputBindingValue = value; }, }; const storyProps$ = new BehaviorSubject(initialProps); const analyzedMetadata = new PropertyExtractor({}, FooComponent); await analyzedMetadata.init(); const application = getApplication({ storyFnAngular: { props: initialProps }, component: FooComponent, targetSelector: 'my-selector', analyzedMetadata, }); const { fixture } = await configureTestingModule({ imports: [application], providers: [storyPropsProvider(storyProps$)], }); fixture.detectChanges(); const newProps = { input: 'new input', output: () => { expectedOutputValue = 'should be called'; }, outputBindingPropertyName: () => { expectedOutputBindingValue = 'should be called'; }, }; storyProps$.next(newProps); fixture.detectChanges(); fixture.nativeElement.querySelector('p#output').click(); fixture.nativeElement.querySelector('p#outputBindingPropertyName').click(); expect(fixture.nativeElement.querySelector('p#input').innerHTML).toEqual(newProps.input); expect(expectedOutputValue).toEqual('should be called'); expect(expectedOutputBindingValue).toEqual('should be called'); }); it('should change template inputs if storyProps$ Subject emit', async () => { const initialProps = { color: 'red', input: 'input', }; const storyProps$ = new BehaviorSubject(initialProps); const analyzedMetadata = new PropertyExtractor({}, FooComponent); await analyzedMetadata.init(); const application = getApplication({ storyFnAngular: { props: initialProps, template: '<p [style.color]="color"><foo [input]="input"></foo></p>', }, component: FooComponent, targetSelector: 'my-selector', analyzedMetadata, }); const { fixture } = await configureTestingModule({ imports: [application], providers: [storyPropsProvider(storyProps$)], }); fixture.detectChanges(); expect(fixture.nativeElement.querySelector('p').style.color).toEqual('red'); expect(fixture.nativeElement.querySelector('p#input').innerHTML).toEqual(initialProps.input); const newProps = { color: 'black', input: 'new input', }; storyProps$.next(newProps); fixture.detectChanges(); expect(fixture.nativeElement.querySelector('p').style.color).toEqual('black'); expect(fixture.nativeElement.querySelector('p#input').innerHTML).toEqual(newProps.input); }); it('should call the Input() setter the right number of times', async () => { const initialProps = { setter: 'init', }; const storyProps$ = new BehaviorSubject(initialProps); const analyzedMetadata = new PropertyExtractor({}, FooComponent); await analyzedMetadata.init(); const application = getApplication({ storyFnAngular: { props: initialProps }, component: FooComponent, targetSelector: 'my-selector', analyzedMetadata, }); const { fixture } = await configureTestingModule({ imports: [application], providers: [storyPropsProvider(storyProps$)], }); fixture.detectChanges(); expect(fixture.nativeElement.querySelector('p#setterCallNb').innerHTML).toEqual('1'); const newProps = { setter: 'new setter value', }; storyProps$.next(newProps); fixture.detectChanges(); expect(fixture.nativeElement.querySelector('p#setterCallNb').innerHTML).toEqual('2'); }); }); describe('with component without selector', () => { let WithoutSelectorComponent = class WithoutSelectorComponent { }; WithoutSelectorComponent = __decorate([ Component({ template: `The content`, }) ], WithoutSelectorComponent); it('should display the component', async () => { const props = {}; const analyzedMetadata = new PropertyExtractor({ entryComponents: [WithoutSelectorComponent] }, WithoutSelectorComponent); await analyzedMetadata.init(); const application = getApplication({ storyFnAngular: { props, moduleMetadata: { entryComponents: [WithoutSelectorComponent] }, }, component: WithoutSelectorComponent, targetSelector: 'my-selector', analyzedMetadata, }); const { fixture } = await configureTestingModule({ imports: [application], providers: [storyPropsProvider(new BehaviorSubject(props))], }); fixture.detectChanges(); expect(fixture.nativeElement.innerHTML).toContain('The content'); }); }); it('should keep template with an empty value', async () => { let FooComponent = class FooComponent { }; FooComponent = __decorate([ Component({ selector: 'foo', template: `Should not be displayed`, }) ], FooComponent); const analyzedMetadata = new PropertyExtractor({}, FooComponent); await analyzedMetadata.init(); const application = getApplication({ storyFnAngular: { template: '' }, component: FooComponent, targetSelector: 'my-selector', analyzedMetadata, }); const { fixture } = await configureTestingModule({ imports: [application], providers: [storyPropsProvider(new BehaviorSubject({}))], }); fixture.detectChanges(); expect(fixture.nativeElement.innerHTML).toEqual(''); }); }); async function configureTestingModule(ngModule) { await TestBed.configureTestingModule(ngModule).compileComponents(); const fixture = TestBed.createComponent(ngModule.imports[0]); return { fixture, }; } });