UNPKG

@storybook/angular

Version:

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

234 lines (233 loc) โ€ข 11.6 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; }; import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; import { Component, ษตresetJitOptions } from '@angular/core'; import { platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing'; import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import { CanvasRenderer } from './CanvasRenderer'; import { RendererFactory } from './RendererFactory'; import { DocsRenderer } from './DocsRenderer'; vi.mock('@angular/platform-browser-dynamic'); describe('RendererFactory', () => { let rendererFactory; let rootTargetDOMNode; let rootDocstargetDOMNode; beforeEach(async () => { rendererFactory = new RendererFactory(); document.body.innerHTML = '<div id="storybook-root"></div><div id="root-docs"><div id="story-in-docs"></div></div>' + '<div id="storybook-docs"></div>'; rootTargetDOMNode = global.document.getElementById('storybook-root'); rootDocstargetDOMNode = global.document.getElementById('root-docs'); platformBrowserDynamic.mockImplementation(platformBrowserDynamicTesting); vi.spyOn(console, 'log').mockImplementation(() => { }); // @ts-expect-error Ignore globalThis.STORYBOOK_ANGULAR_OPTIONS = { experimentalZoneless: false }; }); afterEach(() => { vi.clearAllMocks(); // Necessary to avoid this error "Provided value for `preserveWhitespaces` can not be changed once it has been set." : // Source: https://github.com/angular/angular/commit/e342ffd855ffeb8af7067b42307ffa320d82177e#diff-92b125e532cc22977b46a91f068d6d7ea81fd61b772842a4a0212f1cfd875be6R28 ษตresetJitOptions(); }); describe('CanvasRenderer', () => { it('should get CanvasRenderer instance', async () => { const render = await rendererFactory.getRendererInstance(rootTargetDOMNode); expect(render).toBeInstanceOf(CanvasRenderer); }); it('should render my-story for story template', async () => { const render = await rendererFactory.getRendererInstance(rootTargetDOMNode); await render?.render({ storyFnAngular: { template: '๐ŸฆŠ', props: {}, }, forced: false, targetDOMNode: rootTargetDOMNode, }); expect(document.body.getElementsByTagName('storybook-root')[0].innerHTML).toBe('๐ŸฆŠ'); }); it('should render my-story for story component', async () => { let FooComponent = class FooComponent { }; FooComponent = __decorate([ Component({ selector: 'foo', template: '๐ŸฆŠ' }) ], FooComponent); const render = await rendererFactory.getRendererInstance(rootTargetDOMNode); await render?.render({ storyFnAngular: { props: {}, }, forced: false, component: FooComponent, targetDOMNode: rootTargetDOMNode, }); expect(document.body.getElementsByTagName('storybook-root')[0].innerHTML).toBe('<foo>๐ŸฆŠ</foo><!--container-->'); }); it('should handle circular reference in moduleMetadata', async () => { class Thing { constructor() { this.token = this; } } const token = new Thing(); const render = await rendererFactory.getRendererInstance(rootTargetDOMNode); await render?.render({ storyFnAngular: { template: '๐ŸฆŠ', props: {}, moduleMetadata: { providers: [{ provide: 'foo', useValue: token }] }, }, forced: false, targetDOMNode: rootTargetDOMNode, }); expect(document.body.getElementsByTagName('storybook-root')[0].innerHTML).toBe('๐ŸฆŠ'); }); describe('when forced=true', () => { beforeEach(async () => { // Init first render const render = await rendererFactory.getRendererInstance(rootTargetDOMNode); await render?.render({ storyFnAngular: { template: '{{ logo }}: {{ name }}', props: { logo: '๐ŸฆŠ', name: 'Fox', }, }, forced: true, targetDOMNode: rootTargetDOMNode, }); }); it('should be rendered a first time', async () => { expect(document.body.getElementsByTagName('storybook-root')[0].innerHTML).toBe('๐ŸฆŠ: Fox'); }); it('should not be re-rendered when only props change', async () => { // only props change const render = await rendererFactory.getRendererInstance(rootTargetDOMNode); await render?.render({ storyFnAngular: { props: { logo: '๐Ÿ‘พ', }, }, forced: true, targetDOMNode: rootTargetDOMNode, }); expect(document.body.getElementsByTagName('storybook-root')[0].innerHTML).toBe('๐Ÿ‘พ: Fox'); }); it('should be re-rendered when template change', async () => { const render = await rendererFactory.getRendererInstance(rootTargetDOMNode); await render?.render({ storyFnAngular: { template: '{{ beer }}', props: { beer: '๐Ÿบ', }, }, forced: true, targetDOMNode: rootTargetDOMNode, }); expect(document.body.getElementsByTagName('storybook-root')[0].innerHTML).toBe('๐Ÿบ'); }); }); }); describe('DocsRenderer', () => { describe('when canvas render is done before', () => { beforeEach(async () => { // Init first Canvas render const render = await rendererFactory.getRendererInstance(rootTargetDOMNode); await render?.render({ storyFnAngular: { template: 'Canvas ๐Ÿ–ผ', }, forced: true, targetDOMNode: rootTargetDOMNode, }); }); it('should reset root HTML', async () => { global.document .getElementById('storybook-root') .appendChild(global.document.createElement('๐Ÿ‘พ')); expect(global.document.getElementById('storybook-root').innerHTML).toContain('Canvas ๐Ÿ–ผ'); await rendererFactory.getRendererInstance(rootDocstargetDOMNode); expect(global.document.getElementById('storybook-root').innerHTML).toBe(''); }); }); it('should get DocsRenderer instance', async () => { const render = await rendererFactory.getRendererInstance(rootDocstargetDOMNode); expect(render).toBeInstanceOf(DocsRenderer); }); describe('when multiple story for the same component', () => { it('should render both stories', async () => { let FooComponent = class FooComponent { }; FooComponent = __decorate([ Component({ selector: 'foo', template: '๐ŸฆŠ' }) ], FooComponent); const render = await rendererFactory.getRendererInstance(global.document.getElementById('storybook-docs')); const targetDOMNode1 = global.document.createElement('div'); targetDOMNode1.id = 'story-1'; global.document.getElementById('storybook-docs').appendChild(targetDOMNode1); await render?.render({ storyFnAngular: { props: {}, }, forced: false, component: FooComponent, targetDOMNode: targetDOMNode1, }); const targetDOMNode2 = global.document.createElement('div'); targetDOMNode2.id = 'story-1'; global.document.getElementById('storybook-docs').appendChild(targetDOMNode2); await render?.render({ storyFnAngular: { props: {}, }, forced: false, component: FooComponent, targetDOMNode: targetDOMNode2, }); expect(global.document.querySelectorAll('#story-1 > story-1')[0].innerHTML).toBe('<foo>๐ŸฆŠ</foo><!--container-->'); expect(global.document.querySelectorAll('#story-1 > story-1')[1].innerHTML).toBe('<foo>๐ŸฆŠ</foo><!--container-->'); }); }); describe('when bootstrapping multiple stories in parallel', () => { it('should render both stories', async () => { let FooComponent = class FooComponent { }; FooComponent = __decorate([ Component({ selector: 'foo', template: '๐ŸฆŠ' }) ], FooComponent); const render = await rendererFactory.getRendererInstance(global.document.getElementById('storybook-docs')); const targetDOMNode1 = global.document.createElement('div'); targetDOMNode1.id = 'story-1'; global.document.getElementById('storybook-docs').appendChild(targetDOMNode1); const targetDOMNode2 = global.document.createElement('div'); targetDOMNode2.id = 'story-2'; global.document.getElementById('storybook-docs').appendChild(targetDOMNode2); await Promise.all([ render.render({ storyFnAngular: {}, forced: false, component: FooComponent, targetDOMNode: targetDOMNode1, }), render.render({ storyFnAngular: {}, forced: false, component: FooComponent, targetDOMNode: targetDOMNode2, }), ]); expect(global.document.querySelector('#story-1 > story-1').innerHTML).toBe('<foo>๐ŸฆŠ</foo><!--container-->'); expect(global.document.querySelector('#story-2 > story-2').innerHTML).toBe('<foo>๐ŸฆŠ</foo><!--container-->'); }); }); }); });