Best JavaScript code snippet using storybook-root
PreviewWeb.test.ts
Source:PreviewWeb.test.ts
1import global from 'global';2import * as ReactDOM from 'react-dom';3import merge from 'lodash/merge';4import Events, { IGNORED_EXCEPTION } from '@storybook/core-events';5import { logger } from '@storybook/client-logger';6import { addons, mockChannel as createMockChannel } from '@storybook/addons';7import type { AnyFramework } from '@storybook/csf';8import type { ModuleImportFn } from '@storybook/store';9import { PreviewWeb } from './PreviewWeb';10import {11 componentOneExports,12 componentTwoExports,13 importFn,14 projectAnnotations,15 getProjectAnnotations,16 storyIndex,17 emitter,18 mockChannel,19 waitForEvents,20 waitForRender,21 waitForQuiescence,22 waitForRenderPhase,23} from './PreviewWeb.mockdata';24import type { WebProjectAnnotations } from './types';25jest.mock('./WebView');26const { history, document } = global;27const mockStoryIndex = jest.fn(() => storyIndex);28let mockFetchResult;29jest.mock('global', () => ({30 ...(jest.requireActual('global') as any),31 history: { replaceState: jest.fn() },32 document: {33 location: {34 pathname: 'pathname',35 search: '?id=*',36 },37 },38 window: {39 location: {40 reload: jest.fn(),41 },42 },43 FEATURES: {44 storyStoreV7: true,45 breakingChangesV7: true,46 // xxx47 },48 fetch: async () => mockFetchResult,49}));50jest.mock('@storybook/client-logger');51jest.mock('react-dom');52const createGate = (): [Promise<any | undefined>, (_?: any) => void] => {53 let openGate = (_?: any) => {};54 const gate = new Promise<any | undefined>((resolve) => {55 openGate = resolve;56 });57 return [gate, openGate];58};59// SET_CURRENT_STORY does some stuff in promises, then waits for60// a timer, so we need to first setImmediate (to get past the resolution), then run the timers61// Probably jest modern timers do this but they aren't working for some bizzarre reason.62async function waitForSetCurrentStory() {63 await new Promise((r) => setImmediate(r));64 jest.runAllTimers();65}66async function createAndRenderPreview({67 importFn: inputImportFn = importFn,68 getProjectAnnotations: inputGetProjectAnnotations = getProjectAnnotations,69}: {70 importFn?: ModuleImportFn;71 getProjectAnnotations?: () => WebProjectAnnotations<AnyFramework>;72} = {}) {73 const preview = new PreviewWeb();74 (75 preview.view.prepareForDocs as jest.MockedFunction<typeof preview.view.prepareForDocs>76 ).mockReturnValue('docs-element' as any);77 await preview.initialize({78 importFn: inputImportFn,79 getProjectAnnotations: inputGetProjectAnnotations,80 });81 await waitForRender();82 return preview;83}84beforeEach(() => {85 document.location.search = '';86 mockChannel.emit.mockClear();87 emitter.removeAllListeners();88 componentOneExports.default.loaders[0].mockReset().mockImplementation(async () => ({ l: 7 }));89 componentOneExports.default.parameters.docs.container.mockClear();90 componentOneExports.a.play.mockReset();91 projectAnnotations.renderToDOM.mockReset();92 projectAnnotations.render.mockClear();93 projectAnnotations.decorators[0].mockClear();94 // @ts-ignore95 ReactDOM.render.mockReset().mockImplementation((_: any, _2: any, cb: () => any) => cb());96 // @ts-ignore97 logger.warn.mockClear();98 mockStoryIndex.mockReset().mockReturnValue(storyIndex);99 addons.setChannel(mockChannel as any);100 addons.setServerChannel(createMockChannel());101 mockFetchResult = { status: 200, json: mockStoryIndex, text: () => 'error text' };102});103describe('PreviewWeb', () => {104 describe('initialize', () => {105 it('shows an error if getProjectAnnotations throws', async () => {106 const err = new Error('meta error');107 const preview = new PreviewWeb();108 await expect(109 preview.initialize({110 importFn,111 getProjectAnnotations: () => {112 throw err;113 },114 })115 ).rejects.toThrow(err);116 expect(preview.view.showErrorDisplay).toHaveBeenCalled();117 expect(mockChannel.emit).toHaveBeenCalledWith(Events.CONFIG_ERROR, err);118 });119 it('shows an error if the stories.json endpoint 500s', async () => {120 const err = new Error('sort error');121 mockFetchResult = { status: 500, text: async () => err.toString() };122 const preview = new PreviewWeb();123 await expect(preview.initialize({ importFn, getProjectAnnotations })).rejects.toThrow(124 'sort error'125 );126 expect(preview.view.showErrorDisplay).toHaveBeenCalled();127 expect(mockChannel.emit).toHaveBeenCalledWith(Events.CONFIG_ERROR, expect.any(Error));128 });129 it('sets globals from the URL', async () => {130 document.location.search = '?id=*&globals=a:c';131 const preview = await createAndRenderPreview();132 expect(preview.storyStore.globals.get()).toEqual({ a: 'c' });133 });134 it('emits the SET_GLOBALS event', async () => {135 await createAndRenderPreview();136 expect(mockChannel.emit).toHaveBeenCalledWith(Events.SET_GLOBALS, {137 globals: { a: 'b' },138 globalTypes: {},139 });140 });141 it('SET_GLOBALS sets globals and types even when undefined', async () => {142 await createAndRenderPreview({ getProjectAnnotations: () => ({ renderToDOM: jest.fn() }) });143 expect(mockChannel.emit).toHaveBeenCalledWith(Events.SET_GLOBALS, {144 globals: {},145 globalTypes: {},146 });147 });148 it('emits the SET_GLOBALS event from the URL', async () => {149 document.location.search = '?id=*&globals=a:c';150 await createAndRenderPreview();151 expect(mockChannel.emit).toHaveBeenCalledWith(Events.SET_GLOBALS, {152 globals: { a: 'c' },153 globalTypes: {},154 });155 });156 it('sets args from the URL', async () => {157 document.location.search = '?id=component-one--a&args=foo:url';158 const preview = await createAndRenderPreview();159 expect(preview.storyStore.args.get('component-one--a')).toEqual({160 foo: 'url',161 });162 });163 it('updates args from the URL', async () => {164 document.location.search = '?id=component-one--a&args=foo:url';165 await createAndRenderPreview();166 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ARGS_UPDATED, {167 storyId: 'component-one--a',168 args: { foo: 'url' },169 });170 });171 it('allows async getProjectAnnotations', async () => {172 const preview = new PreviewWeb();173 await preview.initialize({174 importFn,175 getProjectAnnotations: async () => {176 return getProjectAnnotations();177 },178 });179 expect(preview.storyStore.globals.get()).toEqual({ a: 'b' });180 });181 });182 describe('initial selection', () => {183 it('selects the story specified in the URL', async () => {184 document.location.search = '?id=component-one--a';185 const preview = await createAndRenderPreview();186 expect(preview.urlStore.selection).toEqual({187 storyId: 'component-one--a',188 viewMode: 'story',189 });190 expect(history.replaceState).toHaveBeenCalledWith(191 {},192 '',193 'pathname?id=component-one--a&viewMode=story'194 );195 });196 it('emits the STORY_SPECIFIED event', async () => {197 document.location.search = '?id=component-one--a';198 await createAndRenderPreview();199 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_SPECIFIED, {200 storyId: 'component-one--a',201 viewMode: 'story',202 });203 });204 it('emits the CURRENT_STORY_WAS_SET event', async () => {205 document.location.search = '?id=component-one--a';206 await createAndRenderPreview();207 expect(mockChannel.emit).toHaveBeenCalledWith(Events.CURRENT_STORY_WAS_SET, {208 storyId: 'component-one--a',209 viewMode: 'story',210 });211 });212 describe('if the story specified does not exist', () => {213 it('renders a loading error', async () => {214 document.location.search = '?id=random';215 const preview = await createAndRenderPreview();216 expect(preview.view.showErrorDisplay).toHaveBeenCalled();217 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_MISSING, 'random');218 });219 it('tries again with a specifier if CSF file changes', async () => {220 document.location.search = '?id=component-one--d';221 const preview = await createAndRenderPreview();222 expect(preview.view.showErrorDisplay).toHaveBeenCalled();223 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_MISSING, 'component-one--d');224 mockChannel.emit.mockClear();225 const newComponentOneExports = merge({}, componentOneExports, {226 d: { args: { foo: 'd' }, play: jest.fn() },227 });228 const newImportFn = jest.fn(async (path) => {229 return path === './src/ComponentOne.stories.js'230 ? newComponentOneExports231 : componentTwoExports;232 });233 preview.onStoriesChanged({234 importFn: newImportFn,235 storyIndex: {236 v: 3,237 stories: {238 ...storyIndex.stories,239 'component-one--d': {240 id: 'component-one--d',241 title: 'Component One',242 name: 'D',243 importPath: './src/ComponentOne.stories.js',244 },245 },246 },247 });248 await waitForRender();249 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_SPECIFIED, {250 storyId: 'component-one--d',251 viewMode: 'story',252 });253 });254 describe('after selection changes', () => {255 beforeEach(() => jest.useFakeTimers());256 afterEach(() => jest.useRealTimers());257 it('DOES NOT try again if CSF file changes', async () => {258 document.location.search = '?id=component-one--d';259 const preview = await createAndRenderPreview();260 expect(preview.view.showErrorDisplay).toHaveBeenCalled();261 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_MISSING, 'component-one--d');262 emitter.emit(Events.SET_CURRENT_STORY, {263 storyId: 'component-one--b',264 viewMode: 'story',265 });266 await waitForSetCurrentStory();267 const newComponentOneExports = merge({}, componentOneExports, {268 d: { args: { foo: 'd' }, play: jest.fn() },269 });270 const newImportFn = jest.fn(async (path) => {271 return path === './src/ComponentOne.stories.js'272 ? newComponentOneExports273 : componentTwoExports;274 });275 preview.onStoriesChanged({276 importFn: newImportFn,277 storyIndex: {278 v: 3,279 stories: {280 ...storyIndex.stories,281 'component-one--d': {282 id: 'component-one--d',283 title: 'Component One',284 name: 'D',285 importPath: './src/ComponentOne.stories.js',286 },287 },288 },289 });290 expect(mockChannel.emit).not.toHaveBeenCalledWith(Events.STORY_SPECIFIED, {291 storyId: 'component-one--d',292 viewMode: 'story',293 });294 });295 });296 });297 it('renders missing if no selection', async () => {298 const preview = await createAndRenderPreview();299 expect(preview.view.showNoPreview).toHaveBeenCalled();300 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_MISSING);301 });302 describe('in story viewMode', () => {303 it('calls view.prepareForStory', async () => {304 document.location.search = '?id=component-one--a';305 const preview = await createAndRenderPreview();306 expect(preview.view.prepareForStory).toHaveBeenCalledWith(307 expect.objectContaining({308 id: 'component-one--a',309 })310 );311 });312 it('emits STORY_PREPARED', async () => {313 document.location.search = '?id=component-one--a';314 await createAndRenderPreview();315 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_PREPARED, {316 id: 'component-one--a',317 parameters: {318 __isArgsStory: false,319 docs: { container: expect.any(Function) },320 fileName: './src/ComponentOne.stories.js',321 },322 initialArgs: { foo: 'a' },323 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },324 args: { foo: 'a' },325 });326 });327 it('applies loaders with story context', async () => {328 document.location.search = '?id=component-one--a';329 await createAndRenderPreview();330 expect(componentOneExports.default.loaders[0]).toHaveBeenCalledWith(331 expect.objectContaining({332 id: 'component-one--a',333 parameters: {334 __isArgsStory: false,335 docs: { container: expect.any(Function) },336 fileName: './src/ComponentOne.stories.js',337 },338 initialArgs: { foo: 'a' },339 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },340 args: { foo: 'a' },341 })342 );343 });344 it('passes loaded context to renderToDOM', async () => {345 document.location.search = '?id=component-one--a';346 await createAndRenderPreview();347 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(348 expect.objectContaining({349 forceRemount: true,350 storyContext: expect.objectContaining({351 id: 'component-one--a',352 parameters: {353 __isArgsStory: false,354 docs: { container: expect.any(Function) },355 fileName: './src/ComponentOne.stories.js',356 },357 globals: { a: 'b' },358 initialArgs: { foo: 'a' },359 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },360 args: { foo: 'a' },361 loaded: { l: 7 },362 }),363 }),364 undefined // this is coming from view.prepareForStory, not super important365 );366 });367 it('renders exception if a loader throws', async () => {368 const error = new Error('error');369 componentOneExports.default.loaders[0].mockImplementationOnce(() => {370 throw error;371 });372 document.location.search = '?id=component-one--a';373 const preview = await createAndRenderPreview();374 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);375 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);376 });377 it('renders exception if renderToDOM throws', async () => {378 const error = new Error('error');379 projectAnnotations.renderToDOM.mockImplementationOnce(() => {380 throw error;381 });382 document.location.search = '?id=component-one--a';383 const preview = await createAndRenderPreview();384 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);385 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);386 });387 it('renders helpful message if renderToDOM is undefined', async () => {388 const originalRenderToDOM = projectAnnotations.renderToDOM;389 try {390 projectAnnotations.renderToDOM = undefined;391 document.location.search = '?id=component-one--a';392 const preview = new PreviewWeb();393 await expect(preview.initialize({ importFn, getProjectAnnotations })).rejects.toThrow();394 expect(preview.view.showErrorDisplay).toHaveBeenCalled();395 expect((preview.view.showErrorDisplay as jest.Mock).mock.calls[0][0])396 .toMatchInlineSnapshot(`397 [Error: Expected your framework's preset to export a \`renderToDOM\` field.398 Perhaps it needs to be upgraded for Storybook 6.4?399 More info: https://github.com/storybookjs/storybook/blob/next/MIGRATION.md#mainjs-framework-field ]400 `);401 } finally {402 projectAnnotations.renderToDOM = originalRenderToDOM;403 }404 });405 it('renders exception if the play function throws', async () => {406 const error = new Error('error');407 componentOneExports.a.play.mockImplementationOnce(() => {408 throw error;409 });410 document.location.search = '?id=component-one--a';411 const preview = await createAndRenderPreview();412 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);413 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);414 });415 it('renders error if the story calls showError', async () => {416 const error = { title: 'title', description: 'description' };417 projectAnnotations.renderToDOM.mockImplementationOnce((context) =>418 context.showError(error)419 );420 document.location.search = '?id=component-one--a';421 const preview = await createAndRenderPreview();422 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ERRORED, error);423 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith({424 message: error.title,425 stack: error.description,426 });427 });428 it('renders exception if the story calls showException', async () => {429 const error = new Error('error');430 projectAnnotations.renderToDOM.mockImplementationOnce((context) =>431 context.showException(error)432 );433 document.location.search = '?id=component-one--a';434 const preview = await createAndRenderPreview();435 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);436 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);437 });438 it('executes playFunction', async () => {439 document.location.search = '?id=component-one--a';440 await createAndRenderPreview();441 expect(componentOneExports.a.play).toHaveBeenCalled();442 });443 it('emits STORY_RENDERED', async () => {444 document.location.search = '?id=component-one--a';445 await createAndRenderPreview();446 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--a');447 });448 it('does not show error display if the render function throws IGNORED_EXCEPTION', async () => {449 document.location.search = '?id=component-one--a';450 projectAnnotations.renderToDOM.mockImplementationOnce(() => {451 throw IGNORED_EXCEPTION;452 });453 const preview = new PreviewWeb();454 await preview.initialize({ importFn, getProjectAnnotations });455 await waitForRender();456 expect(mockChannel.emit).toHaveBeenCalledWith(457 Events.STORY_THREW_EXCEPTION,458 IGNORED_EXCEPTION459 );460 expect(preview.view.showErrorDisplay).not.toHaveBeenCalled();461 });462 });463 describe('in docs viewMode', () => {464 it('calls view.prepareForDocs', async () => {465 document.location.search = '?id=component-one--a&viewMode=docs';466 const preview = await createAndRenderPreview();467 expect(preview.view.prepareForDocs).toHaveBeenCalled();468 });469 it('emits STORY_PREPARED', async () => {470 document.location.search = '?id=component-one--a&viewMode=docs';471 await createAndRenderPreview();472 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_PREPARED, {473 id: 'component-one--a',474 parameters: {475 __isArgsStory: false,476 docs: { container: expect.any(Function) },477 fileName: './src/ComponentOne.stories.js',478 },479 initialArgs: { foo: 'a' },480 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },481 args: { foo: 'a' },482 });483 });484 it('render the docs container with the correct context', async () => {485 document.location.search = '?id=component-one--a&viewMode=docs';486 await createAndRenderPreview();487 expect(ReactDOM.render).toHaveBeenCalledWith(488 expect.objectContaining({489 type: componentOneExports.default.parameters.docs.container,490 props: expect.objectContaining({491 context: expect.objectContaining({492 id: 'component-one--a',493 title: 'Component One',494 name: 'A',495 }),496 }),497 }),498 'docs-element',499 expect.any(Function)500 );501 });502 it('emits DOCS_RENDERED', async () => {503 document.location.search = '?id=component-one--a&viewMode=docs';504 await createAndRenderPreview();505 expect(mockChannel.emit).toHaveBeenCalledWith(Events.DOCS_RENDERED, 'component-one--a');506 });507 });508 });509 describe('onUpdateGlobals', () => {510 it('emits GLOBALS_UPDATED', async () => {511 document.location.search = '?id=component-one--a';512 await createAndRenderPreview();513 emitter.emit(Events.UPDATE_GLOBALS, { globals: { foo: 'bar' } });514 await waitForEvents([Events.GLOBALS_UPDATED]);515 expect(mockChannel.emit).toHaveBeenCalledWith(Events.GLOBALS_UPDATED, {516 globals: { a: 'b', foo: 'bar' },517 initialGlobals: { a: 'b' },518 });519 });520 it('sets new globals on the store', async () => {521 document.location.search = '?id=component-one--a';522 const preview = await createAndRenderPreview();523 emitter.emit(Events.UPDATE_GLOBALS, { globals: { foo: 'bar' } });524 expect(preview.storyStore.globals.get()).toEqual({ a: 'b', foo: 'bar' });525 });526 it('passes new globals in context to renderToDOM', async () => {527 document.location.search = '?id=component-one--a';528 const preview = await createAndRenderPreview();529 mockChannel.emit.mockClear();530 projectAnnotations.renderToDOM.mockClear();531 emitter.emit(Events.UPDATE_GLOBALS, { globals: { foo: 'bar' } });532 await waitForRender();533 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(534 expect.objectContaining({535 forceRemount: false,536 storyContext: expect.objectContaining({537 globals: { a: 'b', foo: 'bar' },538 }),539 }),540 undefined // this is coming from view.prepareForStory, not super important541 );542 });543 it('emits STORY_RENDERED', async () => {544 document.location.search = '?id=component-one--a';545 await createAndRenderPreview();546 mockChannel.emit.mockClear();547 emitter.emit(Events.UPDATE_GLOBALS, { globals: { foo: 'bar' } });548 await waitForRender();549 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--a');550 });551 describe('in docs mode', () => {552 it('re-renders the docs container', async () => {553 document.location.search = '?id=component-one--a&viewMode=docs';554 await createAndRenderPreview();555 mockChannel.emit.mockClear();556 emitter.emit(Events.UPDATE_GLOBALS, { globals: { foo: 'bar' } });557 await waitForRender();558 expect(ReactDOM.render).toHaveBeenCalledTimes(2);559 });560 });561 });562 describe('onUpdateArgs', () => {563 it('emits STORY_ARGS_UPDATED', async () => {564 document.location.search = '?id=component-one--a';565 await createAndRenderPreview();566 emitter.emit(Events.UPDATE_STORY_ARGS, {567 storyId: 'component-one--a',568 updatedArgs: { new: 'arg' },569 });570 await waitForEvents([Events.STORY_ARGS_UPDATED]);571 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ARGS_UPDATED, {572 storyId: 'component-one--a',573 args: { foo: 'a', new: 'arg' },574 });575 });576 it('sets new args on the store', async () => {577 document.location.search = '?id=component-one--a';578 const preview = await createAndRenderPreview();579 emitter.emit(Events.UPDATE_STORY_ARGS, {580 storyId: 'component-one--a',581 updatedArgs: { new: 'arg' },582 });583 expect(preview.storyStore.args.get('component-one--a')).toEqual({584 foo: 'a',585 new: 'arg',586 });587 });588 it('passes new args in context to renderToDOM', async () => {589 document.location.search = '?id=component-one--a';590 await createAndRenderPreview();591 mockChannel.emit.mockClear();592 projectAnnotations.renderToDOM.mockClear();593 emitter.emit(Events.UPDATE_STORY_ARGS, {594 storyId: 'component-one--a',595 updatedArgs: { new: 'arg' },596 });597 await waitForRender();598 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(599 expect.objectContaining({600 forceRemount: false,601 storyContext: expect.objectContaining({602 initialArgs: { foo: 'a' },603 args: { foo: 'a', new: 'arg' },604 }),605 }),606 undefined // this is coming from view.prepareForStory, not super important607 );608 });609 it('emits STORY_RENDERED', async () => {610 document.location.search = '?id=component-one--a';611 await createAndRenderPreview();612 mockChannel.emit.mockClear();613 emitter.emit(Events.UPDATE_STORY_ARGS, {614 storyId: 'component-one--a',615 updatedArgs: { new: 'arg' },616 });617 await waitForRender();618 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--a');619 });620 describe('while story is still rendering', () => {621 it('runs loaders again', async () => {622 const [gate, openGate] = createGate();623 document.location.search = '?id=component-one--a';624 componentOneExports.default.loaders[0].mockImplementationOnce(async () => gate);625 await new PreviewWeb().initialize({ importFn, getProjectAnnotations });626 await waitForRenderPhase('loading');627 expect(componentOneExports.default.loaders[0]).toHaveBeenCalledWith(628 expect.objectContaining({629 args: { foo: 'a' },630 })631 );632 componentOneExports.default.loaders[0].mockClear();633 emitter.emit(Events.UPDATE_STORY_ARGS, {634 storyId: 'component-one--a',635 updatedArgs: { new: 'arg' },636 });637 await waitForRender();638 expect(componentOneExports.default.loaders[0]).toHaveBeenCalledWith(639 expect.objectContaining({640 args: { foo: 'a', new: 'arg' },641 })642 );643 // Story gets rendered with updated args644 expect(projectAnnotations.renderToDOM).toHaveBeenCalledTimes(1);645 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(646 expect.objectContaining({647 forceRemount: true, // Wasn't yet rendered so we need to force remount648 storyContext: expect.objectContaining({649 loaded: { l: 7 }, // This is the value returned by the *second* loader call650 args: { foo: 'a', new: 'arg' },651 }),652 }),653 undefined // this is coming from view.prepareForStory, not super important654 );655 // Now let the first loader call resolve656 mockChannel.emit.mockClear();657 projectAnnotations.renderToDOM.mockClear();658 openGate({ l: 8 });659 await waitForRender();660 // Now the first call comes through, but picks up the new args661 // Note this isn't a particularly realistic case (the second loader being quicker than the first)662 expect(projectAnnotations.renderToDOM).toHaveBeenCalledTimes(1);663 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(664 expect.objectContaining({665 storyContext: expect.objectContaining({666 loaded: { l: 8 },667 args: { foo: 'a', new: 'arg' },668 }),669 }),670 undefined // this is coming from view.prepareForStory, not super important671 );672 });673 it('renders a second time if renderToDOM is running', async () => {674 const [gate, openGate] = createGate();675 document.location.search = '?id=component-one--a';676 projectAnnotations.renderToDOM.mockImplementationOnce(async () => gate);677 await new PreviewWeb().initialize({ importFn, getProjectAnnotations });678 await waitForRenderPhase('rendering');679 emitter.emit(Events.UPDATE_STORY_ARGS, {680 storyId: 'component-one--a',681 updatedArgs: { new: 'arg' },682 });683 // Now let the renderToDOM call resolve684 openGate();685 await waitForRender();686 expect(projectAnnotations.renderToDOM).toHaveBeenCalledTimes(2);687 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(688 expect.objectContaining({689 forceRemount: true,690 storyContext: expect.objectContaining({691 loaded: { l: 7 },692 args: { foo: 'a' },693 }),694 }),695 undefined // this is coming from view.prepareForStory, not super important696 );697 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(698 expect.objectContaining({699 forceRemount: false,700 storyContext: expect.objectContaining({701 loaded: { l: 7 },702 args: { foo: 'a', new: 'arg' },703 }),704 }),705 undefined // this is coming from view.prepareForStory, not super important706 );707 });708 it('works if it is called directly from inside non async renderToDOM', async () => {709 document.location.search = '?id=component-one--a';710 projectAnnotations.renderToDOM.mockImplementationOnce(() => {711 emitter.emit(Events.UPDATE_STORY_ARGS, {712 storyId: 'component-one--a',713 updatedArgs: { new: 'arg' },714 });715 });716 await createAndRenderPreview();717 expect(projectAnnotations.renderToDOM).toHaveBeenCalledTimes(2);718 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(719 expect.objectContaining({720 forceRemount: true,721 storyContext: expect.objectContaining({722 loaded: { l: 7 },723 args: { foo: 'a' },724 }),725 }),726 undefined // this is coming from view.prepareForStory, not super important727 );728 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(729 expect.objectContaining({730 forceRemount: false,731 storyContext: expect.objectContaining({732 loaded: { l: 7 },733 args: { foo: 'a', new: 'arg' },734 }),735 }),736 undefined // this is coming from view.prepareForStory, not super important737 );738 });739 it('calls renderToDOM again if play function is running', async () => {740 const [gate, openGate] = createGate();741 componentOneExports.a.play.mockImplementationOnce(async () => gate);742 const renderToDOMCalled = new Promise((resolve) => {743 projectAnnotations.renderToDOM.mockImplementationOnce(() => {744 resolve(null);745 });746 });747 document.location.search = '?id=component-one--a';748 await new PreviewWeb().initialize({ importFn, getProjectAnnotations });749 await waitForRenderPhase('playing');750 await renderToDOMCalled;751 // Story gets rendered with original args752 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(753 expect.objectContaining({754 forceRemount: true,755 storyContext: expect.objectContaining({756 loaded: { l: 7 },757 args: { foo: 'a' },758 }),759 }),760 undefined // this is coming from view.prepareForStory, not super important761 );762 emitter.emit(Events.UPDATE_STORY_ARGS, {763 storyId: 'component-one--a',764 updatedArgs: { new: 'arg' },765 });766 // The second call should emit STORY_RENDERED767 await waitForRender();768 // Story gets rendered with updated args769 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(770 expect.objectContaining({771 forceRemount: false,772 storyContext: expect.objectContaining({773 loaded: { l: 7 },774 args: { foo: 'a', new: 'arg' },775 }),776 }),777 undefined // this is coming from view.prepareForStory, not super important778 );779 // Now let the playFunction call resolve780 openGate();781 });782 });783 describe('in docs mode, old inline render', () => {784 it('re-renders the docs container', async () => {785 document.location.search = '?id=component-one--a&viewMode=docs';786 await createAndRenderPreview();787 (ReactDOM.render as jest.MockedFunction<typeof ReactDOM.render>).mockClear();788 mockChannel.emit.mockClear();789 emitter.emit(Events.UPDATE_STORY_ARGS, {790 storyId: 'component-one--a',791 updatedArgs: { new: 'arg' },792 });793 await waitForRender();794 expect(ReactDOM.render).toHaveBeenCalledTimes(1);795 });796 });797 describe('in docs mode, modern inline render', () => {798 beforeEach(() => {799 global.FEATURES.modernInlineRender = true;800 });801 afterEach(() => {802 global.FEATURES.modernInlineRender = true;803 });804 it('does not re-render the docs container', async () => {805 document.location.search = '?id=component-one--a&viewMode=docs';806 await createAndRenderPreview();807 (ReactDOM.render as jest.MockedFunction<typeof ReactDOM.render>).mockClear();808 mockChannel.emit.mockClear();809 emitter.emit(Events.UPDATE_STORY_ARGS, {810 storyId: 'component-one--a',811 updatedArgs: { new: 'arg' },812 });813 await waitForEvents([Events.STORY_ARGS_UPDATED]);814 expect(ReactDOM.render).not.toHaveBeenCalled();815 });816 describe('when renderStoryToElement was called', () => {817 it('re-renders the story', async () => {818 document.location.search = '?id=component-one--a&viewMode=docs';819 const preview = await createAndRenderPreview();820 await waitForRender();821 mockChannel.emit.mockClear();822 const story = await preview.storyStore.loadStory({ storyId: 'component-one--a' });823 preview.renderStoryToElement(story, 'story-element' as any);824 await waitForRender();825 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(826 expect.objectContaining({827 storyContext: expect.objectContaining({828 args: { foo: 'a' },829 }),830 }),831 'story-element'832 );833 (ReactDOM.render as jest.MockedFunction<typeof ReactDOM.render>).mockClear();834 mockChannel.emit.mockClear();835 emitter.emit(Events.UPDATE_STORY_ARGS, {836 storyId: 'component-one--a',837 updatedArgs: { new: 'arg' },838 });839 await waitForRender();840 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(841 expect.objectContaining({842 storyContext: expect.objectContaining({843 args: { foo: 'a', new: 'arg' },844 }),845 }),846 'story-element'847 );848 });849 });850 });851 });852 describe('onResetArgs', () => {853 it('emits STORY_ARGS_UPDATED', async () => {854 document.location.search = '?id=component-one--a';855 await createAndRenderPreview();856 mockChannel.emit.mockClear();857 emitter.emit(Events.UPDATE_STORY_ARGS, {858 storyId: 'component-one--a',859 updatedArgs: { foo: 'new' },860 });861 await waitForEvents([Events.STORY_ARGS_UPDATED]);862 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ARGS_UPDATED, {863 storyId: 'component-one--a',864 args: { foo: 'new' },865 });866 mockChannel.emit.mockClear();867 emitter.emit(Events.RESET_STORY_ARGS, {868 storyId: 'component-one--a',869 argNames: ['foo'],870 });871 await waitForEvents([Events.STORY_ARGS_UPDATED]);872 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ARGS_UPDATED, {873 storyId: 'component-one--a',874 args: { foo: 'a' },875 });876 });877 it('resets a single arg', async () => {878 document.location.search = '?id=component-one--a';879 await createAndRenderPreview();880 mockChannel.emit.mockClear();881 emitter.emit(Events.UPDATE_STORY_ARGS, {882 storyId: 'component-one--a',883 updatedArgs: { foo: 'new', new: 'value' },884 });885 await waitForEvents([Events.STORY_ARGS_UPDATED]);886 mockChannel.emit.mockClear();887 emitter.emit(Events.RESET_STORY_ARGS, {888 storyId: 'component-one--a',889 argNames: ['foo'],890 });891 await waitForRender();892 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(893 expect.objectContaining({894 forceRemount: false,895 storyContext: expect.objectContaining({896 initialArgs: { foo: 'a' },897 args: { foo: 'a', new: 'value' },898 }),899 }),900 undefined // this is coming from view.prepareForStory, not super important901 );902 await waitForEvents([Events.STORY_ARGS_UPDATED]);903 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ARGS_UPDATED, {904 storyId: 'component-one--a',905 args: { foo: 'a', new: 'value' },906 });907 });908 it('resets all args', async () => {909 document.location.search = '?id=component-one--a';910 await createAndRenderPreview();911 emitter.emit(Events.UPDATE_STORY_ARGS, {912 storyId: 'component-one--a',913 updatedArgs: { foo: 'new', new: 'value' },914 });915 await waitForEvents([Events.STORY_ARGS_UPDATED]);916 mockChannel.emit.mockClear();917 emitter.emit(Events.RESET_STORY_ARGS, {918 storyId: 'component-one--a',919 });920 await waitForRender();921 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(922 expect.objectContaining({923 forceRemount: false,924 storyContext: expect.objectContaining({925 initialArgs: { foo: 'a' },926 args: { foo: 'a' },927 }),928 }),929 undefined // this is coming from view.prepareForStory, not super important930 );931 await waitForEvents([Events.STORY_ARGS_UPDATED]);932 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ARGS_UPDATED, {933 storyId: 'component-one--a',934 args: { foo: 'a' },935 });936 });937 });938 describe('on FORCE_RE_RENDER', () => {939 it('rerenders the story with the same args', async () => {940 document.location.search = '?id=component-one--a';941 await createAndRenderPreview();942 mockChannel.emit.mockClear();943 projectAnnotations.renderToDOM.mockClear();944 emitter.emit(Events.FORCE_RE_RENDER);945 await waitForRender();946 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(947 expect.objectContaining({ forceRemount: false }),948 undefined // this is coming from view.prepareForStory, not super important949 );950 });951 });952 describe('on FORCE_REMOUNT', () => {953 beforeEach(() => {954 jest.useFakeTimers();955 });956 afterEach(() => {957 jest.useRealTimers();958 });959 it('remounts the story with the same args', async () => {960 document.location.search = '?id=component-one--a';961 await createAndRenderPreview();962 mockChannel.emit.mockClear();963 projectAnnotations.renderToDOM.mockClear();964 emitter.emit(Events.FORCE_REMOUNT, { storyId: 'component-one--a' });965 await waitForRender();966 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(967 expect.objectContaining({ forceRemount: true }),968 undefined // this is coming from view.prepareForStory, not super important969 );970 });971 it('aborts render function for initial story', async () => {972 const [gate, openGate] = createGate();973 document.location.search = '?id=component-one--a';974 projectAnnotations.renderToDOM.mockImplementationOnce(async () => gate);975 await new PreviewWeb().initialize({ importFn, getProjectAnnotations });976 await waitForRenderPhase('rendering');977 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(978 expect.objectContaining({979 forceRemount: true,980 storyContext: expect.objectContaining({981 id: 'component-one--a',982 loaded: { l: 7 },983 }),984 }),985 undefined // this is coming from view.prepareForStory, not super important986 );987 mockChannel.emit.mockClear();988 emitter.emit(Events.FORCE_REMOUNT, { storyId: 'component-one--a' });989 await waitForSetCurrentStory();990 // Now let the renderToDOM call resolve991 openGate();992 await waitForRenderPhase('aborted');993 await waitForSetCurrentStory();994 await waitForRenderPhase('rendering');995 expect(projectAnnotations.renderToDOM).toHaveBeenCalledTimes(2);996 await waitForRenderPhase('playing');997 expect(componentOneExports.a.play).toHaveBeenCalledTimes(1);998 await waitForRenderPhase('completed');999 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--a');1000 await waitForQuiescence();1001 });1002 });1003 describe('onSetCurrentStory', () => {1004 beforeEach(() => {1005 jest.useFakeTimers();1006 });1007 afterEach(() => {1008 jest.useRealTimers();1009 });1010 it('updates URL', async () => {1011 document.location.search = '?id=component-one--a';1012 await createAndRenderPreview();1013 emitter.emit(Events.SET_CURRENT_STORY, {1014 storyId: 'component-one--b',1015 viewMode: 'story',1016 });1017 await waitForSetCurrentStory();1018 expect(history.replaceState).toHaveBeenCalledWith(1019 {},1020 '',1021 'pathname?id=component-one--b&viewMode=story'1022 );1023 });1024 it('emits CURRENT_STORY_WAS_SET', async () => {1025 document.location.search = '?id=component-one--a';1026 await createAndRenderPreview();1027 emitter.emit(Events.SET_CURRENT_STORY, {1028 storyId: 'component-one--b',1029 viewMode: 'story',1030 });1031 await waitForSetCurrentStory();1032 expect(mockChannel.emit).toHaveBeenCalledWith(Events.CURRENT_STORY_WAS_SET, {1033 storyId: 'component-one--b',1034 viewMode: 'story',1035 });1036 });1037 it('renders loading error if the story specified does not exist', async () => {1038 document.location.search = '?id=component-one--a';1039 const preview = await createAndRenderPreview();1040 emitter.emit(Events.SET_CURRENT_STORY, {1041 storyId: 'random',1042 viewMode: 'story',1043 });1044 await waitForSetCurrentStory();1045 await waitForEvents([Events.STORY_MISSING]);1046 expect(preview.view.showErrorDisplay).toHaveBeenCalled();1047 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_MISSING, 'random');1048 });1049 describe('if called before the preview is initialized', () => {1050 it('still renders the selected story, once ready', async () => {1051 document.location.search = '';1052 // We intentionally are *not* awaiting here1053 new PreviewWeb().initialize({ importFn, getProjectAnnotations });1054 emitter.emit(Events.SET_CURRENT_STORY, {1055 storyId: 'component-one--b',1056 viewMode: 'story',1057 });1058 await waitForEvents([Events.STORY_RENDERED]);1059 expect(mockChannel.emit).toHaveBeenCalledWith(Events.CURRENT_STORY_WAS_SET, {1060 storyId: 'component-one--b',1061 viewMode: 'story',1062 });1063 expect(history.replaceState).toHaveBeenCalledWith(1064 {},1065 '',1066 'pathname?id=component-one--b&viewMode=story'1067 );1068 expect(mockChannel.emit).not.toHaveBeenCalledWith(Events.STORY_MISSING, 'component-one--b');1069 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--b');1070 });1071 });1072 describe('if the selection is unchanged', () => {1073 it('emits STORY_UNCHANGED', async () => {1074 document.location.search = '?id=component-one--a';1075 await createAndRenderPreview();1076 emitter.emit(Events.SET_CURRENT_STORY, {1077 storyId: 'component-one--a',1078 viewMode: 'story',1079 });1080 await waitForSetCurrentStory();1081 await waitForEvents([Events.STORY_UNCHANGED]);1082 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_UNCHANGED, 'component-one--a');1083 });1084 it('does NOT call renderToDOM', async () => {1085 document.location.search = '?id=component-one--a';1086 await createAndRenderPreview();1087 projectAnnotations.renderToDOM.mockClear();1088 emitter.emit(Events.SET_CURRENT_STORY, {1089 storyId: 'component-one--a',1090 viewMode: 'story',1091 });1092 await waitForSetCurrentStory();1093 // The renderToDOM would have been async so we need to wait a tick.1094 await waitForQuiescence();1095 expect(projectAnnotations.renderToDOM).not.toHaveBeenCalled();1096 });1097 // For https://github.com/storybookjs/storybook/issues/172141098 it('does NOT render a second time if preparing', async () => {1099 document.location.search = '?id=component-one--a';1100 const [gate, openGate] = createGate();1101 const [importedGate, openImportedGate] = createGate();1102 importFn1103 .mockImplementationOnce(async (...args) => {1104 await gate;1105 return importFn(...args);1106 })1107 .mockImplementationOnce(async (...args) => {1108 // The second time we `import()` we open the "imported" gate1109 openImportedGate();1110 await gate;1111 return importFn(...args);1112 });1113 const preview = new PreviewWeb();1114 // We can't wait for the initialize function, as it waits for `renderSelection()`1115 // which prepares, but it does emit `CURRENT_STORY_WAS_SET` right before that1116 preview.initialize({ importFn, getProjectAnnotations });1117 await waitForEvents([Events.CURRENT_STORY_WAS_SET]);1118 mockChannel.emit.mockClear();1119 projectAnnotations.renderToDOM.mockClear();1120 emitter.emit(Events.SET_CURRENT_STORY, {1121 storyId: 'component-one--a',1122 viewMode: 'story',1123 });1124 await importedGate;1125 // We are blocking import so this won't render yet1126 expect(projectAnnotations.renderToDOM).not.toHaveBeenCalled();1127 mockChannel.emit.mockClear();1128 openGate();1129 await waitForRender();1130 // We should only render *once*1131 expect(projectAnnotations.renderToDOM).toHaveBeenCalledTimes(1);1132 // We should not show an error either1133 expect(preview.view.showErrorDisplay).not.toHaveBeenCalled();1134 });1135 });1136 describe('when changing story in story viewMode', () => {1137 it('updates URL', async () => {1138 document.location.search = '?id=component-one--a';1139 await createAndRenderPreview();1140 emitter.emit(Events.SET_CURRENT_STORY, {1141 storyId: 'component-one--b',1142 viewMode: 'story',1143 });1144 await waitForSetCurrentStory();1145 expect(history.replaceState).toHaveBeenCalledWith(1146 {},1147 '',1148 'pathname?id=component-one--b&viewMode=story'1149 );1150 });1151 it('renders preparing state', async () => {1152 document.location.search = '?id=component-one--a';1153 const preview = await createAndRenderPreview();1154 emitter.emit(Events.SET_CURRENT_STORY, {1155 storyId: 'component-one--b',1156 viewMode: 'story',1157 });1158 await waitForSetCurrentStory();1159 expect(preview.view.showPreparingStory).toHaveBeenCalled();1160 });1161 it('emits STORY_CHANGED', async () => {1162 document.location.search = '?id=component-one--a';1163 await createAndRenderPreview();1164 mockChannel.emit.mockClear();1165 emitter.emit(Events.SET_CURRENT_STORY, {1166 storyId: 'component-one--b',1167 viewMode: 'story',1168 });1169 await waitForSetCurrentStory();1170 await waitForEvents([Events.STORY_CHANGED]);1171 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_CHANGED, 'component-one--b');1172 });1173 it('emits STORY_PREPARED', async () => {1174 document.location.search = '?id=component-one--a';1175 await createAndRenderPreview();1176 mockChannel.emit.mockClear();1177 emitter.emit(Events.SET_CURRENT_STORY, {1178 storyId: 'component-one--b',1179 viewMode: 'story',1180 });1181 await waitForSetCurrentStory();1182 await waitForEvents([Events.STORY_PREPARED]);1183 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_PREPARED, {1184 id: 'component-one--b',1185 parameters: {1186 __isArgsStory: false,1187 docs: { container: expect.any(Function) },1188 fileName: './src/ComponentOne.stories.js',1189 },1190 initialArgs: { foo: 'b' },1191 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },1192 args: { foo: 'b' },1193 });1194 });1195 it('applies loaders with story context', async () => {1196 document.location.search = '?id=component-one--a';1197 await createAndRenderPreview();1198 mockChannel.emit.mockClear();1199 emitter.emit(Events.SET_CURRENT_STORY, {1200 storyId: 'component-one--b',1201 viewMode: 'story',1202 });1203 await waitForSetCurrentStory();1204 await waitForRender();1205 expect(componentOneExports.default.loaders[0]).toHaveBeenCalledWith(1206 expect.objectContaining({1207 id: 'component-one--b',1208 parameters: {1209 __isArgsStory: false,1210 docs: { container: expect.any(Function) },1211 fileName: './src/ComponentOne.stories.js',1212 },1213 initialArgs: { foo: 'b' },1214 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },1215 args: { foo: 'b' },1216 })1217 );1218 });1219 it('passes loaded context to renderToDOM', async () => {1220 document.location.search = '?id=component-one--a';1221 await createAndRenderPreview();1222 mockChannel.emit.mockClear();1223 emitter.emit(Events.SET_CURRENT_STORY, {1224 storyId: 'component-one--b',1225 viewMode: 'story',1226 });1227 await waitForSetCurrentStory();1228 await waitForRender();1229 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(1230 expect.objectContaining({1231 forceRemount: true,1232 storyContext: expect.objectContaining({1233 id: 'component-one--b',1234 parameters: {1235 __isArgsStory: false,1236 docs: { container: expect.any(Function) },1237 fileName: './src/ComponentOne.stories.js',1238 },1239 globals: { a: 'b' },1240 initialArgs: { foo: 'b' },1241 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },1242 args: { foo: 'b' },1243 loaded: { l: 7 },1244 }),1245 }),1246 undefined // this is coming from view.prepareForStory, not super important1247 );1248 });1249 it('renders exception if renderToDOM throws', async () => {1250 document.location.search = '?id=component-one--a';1251 const preview = await createAndRenderPreview();1252 const error = new Error('error');1253 projectAnnotations.renderToDOM.mockImplementationOnce(() => {1254 throw error;1255 });1256 mockChannel.emit.mockClear();1257 emitter.emit(Events.SET_CURRENT_STORY, {1258 storyId: 'component-one--b',1259 viewMode: 'story',1260 });1261 await waitForSetCurrentStory();1262 await waitForRender();1263 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);1264 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);1265 });1266 it('renders error if the story calls showError', async () => {1267 document.location.search = '?id=component-one--a';1268 const preview = await createAndRenderPreview();1269 const error = { title: 'title', description: 'description' };1270 projectAnnotations.renderToDOM.mockImplementationOnce((context) =>1271 context.showError(error)1272 );1273 mockChannel.emit.mockClear();1274 emitter.emit(Events.SET_CURRENT_STORY, {1275 storyId: 'component-one--b',1276 viewMode: 'story',1277 });1278 await waitForSetCurrentStory();1279 await waitForRender();1280 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ERRORED, error);1281 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith({1282 message: error.title,1283 stack: error.description,1284 });1285 });1286 it('renders exception if the story calls showException', async () => {1287 document.location.search = '?id=component-one--a';1288 const preview = await createAndRenderPreview();1289 const error = new Error('error');1290 projectAnnotations.renderToDOM.mockImplementationOnce((context) =>1291 context.showException(error)1292 );1293 mockChannel.emit.mockClear();1294 emitter.emit(Events.SET_CURRENT_STORY, {1295 storyId: 'component-one--b',1296 viewMode: 'story',1297 });1298 await waitForSetCurrentStory();1299 await waitForRender();1300 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);1301 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);1302 });1303 it('executes playFunction', async () => {1304 document.location.search = '?id=component-one--a';1305 await createAndRenderPreview();1306 mockChannel.emit.mockClear();1307 emitter.emit(Events.SET_CURRENT_STORY, {1308 storyId: 'component-one--b',1309 viewMode: 'story',1310 });1311 await waitForSetCurrentStory();1312 await waitForRender();1313 expect(componentOneExports.b.play).toHaveBeenCalled();1314 });1315 it('emits STORY_RENDERED', async () => {1316 document.location.search = '?id=component-one--a';1317 await createAndRenderPreview();1318 mockChannel.emit.mockClear();1319 emitter.emit(Events.SET_CURRENT_STORY, {1320 storyId: 'component-one--b',1321 viewMode: 'story',1322 });1323 await waitForSetCurrentStory();1324 await waitForRender();1325 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--b');1326 });1327 it('retains any arg changes', async () => {1328 document.location.search = '?id=component-one--a';1329 const preview = await createAndRenderPreview();1330 mockChannel.emit.mockClear();1331 emitter.emit(Events.UPDATE_STORY_ARGS, {1332 storyId: 'component-one--a',1333 updatedArgs: { foo: 'updated' },1334 });1335 await waitForRender();1336 expect(preview.storyStore.args.get('component-one--a')).toEqual({1337 foo: 'updated',1338 });1339 mockChannel.emit.mockClear();1340 emitter.emit(Events.SET_CURRENT_STORY, {1341 storyId: 'component-one--b',1342 viewMode: 'story',1343 });1344 await waitForSetCurrentStory();1345 await waitForRender();1346 expect(preview.storyStore.args.get('component-one--a')).toEqual({1347 foo: 'updated',1348 });1349 mockChannel.emit.mockClear();1350 emitter.emit(Events.SET_CURRENT_STORY, {1351 storyId: 'component-one--a',1352 viewMode: 'story',1353 });1354 await waitForSetCurrentStory();1355 await waitForRender();1356 expect(preview.storyStore.args.get('component-one--a')).toEqual({1357 foo: 'updated',1358 });1359 });1360 describe('while story is still rendering', () => {1361 it('stops initial story after loaders if running', async () => {1362 const [gate, openGate] = createGate();1363 componentOneExports.default.loaders[0].mockImplementationOnce(async () => gate);1364 document.location.search = '?id=component-one--a';1365 await new PreviewWeb().initialize({ importFn, getProjectAnnotations });1366 await waitForRenderPhase('loading');1367 emitter.emit(Events.SET_CURRENT_STORY, {1368 storyId: 'component-one--b',1369 viewMode: 'story',1370 });1371 await waitForSetCurrentStory();1372 await waitForRender();1373 // Now let the loader resolve1374 openGate({ l: 8 });1375 await waitForRender();1376 // Story gets rendered with updated args1377 expect(projectAnnotations.renderToDOM).toHaveBeenCalledTimes(1);1378 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(1379 expect.objectContaining({1380 forceRemount: true,1381 storyContext: expect.objectContaining({1382 id: 'component-one--b',1383 loaded: { l: 7 },1384 }),1385 }),1386 undefined // this is coming from view.prepareForStory, not super important1387 );1388 });1389 it('aborts render for initial story', async () => {1390 const [gate, openGate] = createGate();1391 document.location.search = '?id=component-one--a';1392 projectAnnotations.renderToDOM.mockImplementationOnce(async () => gate);1393 await new PreviewWeb().initialize({ importFn, getProjectAnnotations });1394 await waitForRenderPhase('rendering');1395 mockChannel.emit.mockClear();1396 emitter.emit(Events.SET_CURRENT_STORY, {1397 storyId: 'component-one--b',1398 viewMode: 'story',1399 });1400 await waitForSetCurrentStory();1401 // Now let the renderToDOM call resolve1402 openGate();1403 await waitForRenderPhase('aborted');1404 await waitForSetCurrentStory();1405 await waitForRenderPhase('rendering');1406 expect(projectAnnotations.renderToDOM).toHaveBeenCalledTimes(2);1407 await waitForRenderPhase('playing');1408 expect(componentOneExports.a.play).not.toHaveBeenCalled();1409 expect(componentOneExports.b.play).toHaveBeenCalled();1410 await waitForRenderPhase('completed');1411 expect(mockChannel.emit).not.toHaveBeenCalledWith(1412 Events.STORY_RENDERED,1413 'component-one--a'1414 );1415 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--b');1416 await waitForQuiescence();1417 });1418 it('aborts play function for initial story', async () => {1419 const [gate, openGate] = createGate();1420 componentOneExports.a.play.mockImplementationOnce(async () => gate);1421 document.location.search = '?id=component-one--a';1422 await new PreviewWeb().initialize({ importFn, getProjectAnnotations });1423 await waitForRenderPhase('playing');1424 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(1425 expect.objectContaining({1426 forceRemount: true,1427 storyContext: expect.objectContaining({1428 id: 'component-one--a',1429 loaded: { l: 7 },1430 }),1431 }),1432 undefined // this is coming from view.prepareForStory, not super important1433 );1434 mockChannel.emit.mockClear();1435 emitter.emit(Events.SET_CURRENT_STORY, {1436 storyId: 'component-one--b',1437 viewMode: 'story',1438 });1439 await waitForSetCurrentStory();1440 // Now let the playFunction call resolve1441 openGate();1442 await waitForRenderPhase('aborted');1443 await waitForSetCurrentStory();1444 await waitForRenderPhase('rendering');1445 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_CHANGED, 'component-one--b');1446 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(1447 expect.objectContaining({1448 forceRemount: true,1449 storyContext: expect.objectContaining({1450 id: 'component-one--b',1451 loaded: { l: 7 },1452 }),1453 }),1454 undefined // this is coming from view.prepareForStory, not super important1455 );1456 await waitForRenderPhase('playing');1457 await waitForRenderPhase('completed');1458 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--b');1459 // Final story rendered is not emitted for the first story1460 await waitForQuiescence();1461 expect(mockChannel.emit).not.toHaveBeenCalledWith(1462 Events.STORY_RENDERED,1463 'component-one--a'1464 );1465 });1466 it('reloads page if playFunction fails to abort in time', async () => {1467 const [gate] = createGate();1468 componentOneExports.a.play.mockImplementationOnce(async () => gate);1469 document.location.search = '?id=component-one--a';1470 await new PreviewWeb().initialize({ importFn, getProjectAnnotations });1471 await waitForRenderPhase('playing');1472 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(1473 expect.objectContaining({1474 forceRemount: true,1475 storyContext: expect.objectContaining({1476 id: 'component-one--a',1477 loaded: { l: 7 },1478 }),1479 }),1480 undefined // this is coming from view.prepareForStory, not super important1481 );1482 mockChannel.emit.mockClear();1483 emitter.emit(Events.SET_CURRENT_STORY, {1484 storyId: 'component-one--b',1485 viewMode: 'story',1486 });1487 // Wait three ticks without resolving the play function1488 await waitForSetCurrentStory();1489 await waitForSetCurrentStory();1490 await waitForSetCurrentStory();1491 expect(global.window.location.reload).toHaveBeenCalled();1492 expect(mockChannel.emit).not.toHaveBeenCalledWith(1493 Events.STORY_CHANGED,1494 'component-one--b'1495 );1496 expect(projectAnnotations.renderToDOM).not.toHaveBeenCalledWith(1497 expect.objectContaining({1498 storyContext: expect.objectContaining({ id: 'component-one--b' }),1499 }),1500 undefined1501 );1502 });1503 });1504 });1505 describe('when changing from story viewMode to docs', () => {1506 it('updates URL', async () => {1507 document.location.search = '?id=component-one--a';1508 await createAndRenderPreview();1509 emitter.emit(Events.SET_CURRENT_STORY, {1510 storyId: 'component-one--a',1511 viewMode: 'docs',1512 });1513 await waitForSetCurrentStory();1514 expect(history.replaceState).toHaveBeenCalledWith(1515 {},1516 '',1517 'pathname?id=component-one--a&viewMode=docs'1518 );1519 });1520 it('renders preparing state', async () => {1521 document.location.search = '?id=component-one--a';1522 const preview = await createAndRenderPreview();1523 emitter.emit(Events.SET_CURRENT_STORY, {1524 storyId: 'component-one--a',1525 viewMode: 'docs',1526 });1527 await waitForSetCurrentStory();1528 expect(preview.view.showPreparingDocs).toHaveBeenCalled();1529 });1530 it('emits STORY_CHANGED', async () => {1531 document.location.search = '?id=component-one--a';1532 await createAndRenderPreview();1533 mockChannel.emit.mockClear();1534 emitter.emit(Events.SET_CURRENT_STORY, {1535 storyId: 'component-one--a',1536 viewMode: 'docs',1537 });1538 await waitForSetCurrentStory();1539 await waitForEvents([Events.STORY_CHANGED]);1540 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_CHANGED, 'component-one--a');1541 });1542 it('calls view.prepareForDocs', async () => {1543 document.location.search = '?id=component-one--a';1544 const preview = await createAndRenderPreview();1545 mockChannel.emit.mockClear();1546 emitter.emit(Events.SET_CURRENT_STORY, {1547 storyId: 'component-one--a',1548 viewMode: 'docs',1549 });1550 await waitForSetCurrentStory();1551 await waitForRender();1552 expect(preview.view.prepareForDocs).toHaveBeenCalled();1553 });1554 it('render the docs container with the correct context', async () => {1555 document.location.search = '?id=component-one--a';1556 await createAndRenderPreview();1557 mockChannel.emit.mockClear();1558 emitter.emit(Events.SET_CURRENT_STORY, {1559 storyId: 'component-one--a',1560 viewMode: 'docs',1561 });1562 await waitForSetCurrentStory();1563 await waitForRender();1564 expect(ReactDOM.render).toHaveBeenCalledWith(1565 expect.objectContaining({1566 type: componentOneExports.default.parameters.docs.container,1567 props: expect.objectContaining({1568 context: expect.objectContaining({1569 id: 'component-one--a',1570 title: 'Component One',1571 name: 'A',1572 }),1573 }),1574 }),1575 'docs-element',1576 expect.any(Function)1577 );1578 });1579 it('emits DOCS_RENDERED', async () => {1580 document.location.search = '?id=component-one--a';1581 await createAndRenderPreview();1582 mockChannel.emit.mockClear();1583 emitter.emit(Events.SET_CURRENT_STORY, {1584 storyId: 'component-one--a',1585 viewMode: 'docs',1586 });1587 await waitForSetCurrentStory();1588 await waitForRender();1589 expect(mockChannel.emit).toHaveBeenCalledWith(Events.DOCS_RENDERED, 'component-one--a');1590 });1591 });1592 describe('when changing from docs viewMode to story', () => {1593 it('updates URL', async () => {1594 document.location.search = '?id=component-one--a&viewMode=docs';1595 await createAndRenderPreview();1596 emitter.emit(Events.SET_CURRENT_STORY, {1597 storyId: 'component-one--a',1598 viewMode: 'story',1599 });1600 await waitForSetCurrentStory();1601 expect(history.replaceState).toHaveBeenCalledWith(1602 {},1603 '',1604 'pathname?id=component-one--a&viewMode=story'1605 );1606 });1607 it('unmounts docs', async () => {1608 document.location.search = '?id=component-one--a&viewMode=docs';1609 await createAndRenderPreview();1610 mockChannel.emit.mockClear();1611 emitter.emit(Events.SET_CURRENT_STORY, {1612 storyId: 'component-one--a',1613 viewMode: 'story',1614 });1615 await waitForSetCurrentStory();1616 await waitForRender();1617 expect(ReactDOM.unmountComponentAtNode).toHaveBeenCalled();1618 });1619 // NOTE: I am not sure this entirely makes sense but this is the behaviour from 6.31620 it('emits STORY_CHANGED', async () => {1621 document.location.search = '?id=component-one--a&viewMode=docs';1622 await createAndRenderPreview();1623 mockChannel.emit.mockClear();1624 emitter.emit(Events.SET_CURRENT_STORY, {1625 storyId: 'component-one--a',1626 viewMode: 'story',1627 });1628 await waitForSetCurrentStory();1629 await waitForEvents([Events.STORY_CHANGED]);1630 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_CHANGED, 'component-one--a');1631 });1632 it('calls view.prepareForStory', async () => {1633 document.location.search = '?id=component-one--a&viewMode=docs';1634 const preview = await createAndRenderPreview();1635 mockChannel.emit.mockClear();1636 emitter.emit(Events.SET_CURRENT_STORY, {1637 storyId: 'component-one--a',1638 viewMode: 'story',1639 });1640 await waitForSetCurrentStory();1641 await waitForRender();1642 expect(preview.view.prepareForStory).toHaveBeenCalledWith(1643 expect.objectContaining({1644 id: 'component-one--a',1645 })1646 );1647 });1648 it('emits STORY_PREPARED', async () => {1649 document.location.search = '?id=component-one--a&viewMode=docs';1650 await createAndRenderPreview();1651 mockChannel.emit.mockClear();1652 emitter.emit(Events.SET_CURRENT_STORY, {1653 storyId: 'component-one--a',1654 viewMode: 'story',1655 });1656 await waitForSetCurrentStory();1657 await waitForEvents([Events.STORY_PREPARED]);1658 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_PREPARED, {1659 id: 'component-one--a',1660 parameters: {1661 __isArgsStory: false,1662 docs: { container: expect.any(Function) },1663 fileName: './src/ComponentOne.stories.js',1664 },1665 initialArgs: { foo: 'a' },1666 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },1667 args: { foo: 'a' },1668 });1669 });1670 it('applies loaders with story context', async () => {1671 document.location.search = '?id=component-one--a&viewMode=docs';1672 await createAndRenderPreview();1673 mockChannel.emit.mockClear();1674 emitter.emit(Events.SET_CURRENT_STORY, {1675 storyId: 'component-one--a',1676 viewMode: 'story',1677 });1678 await waitForSetCurrentStory();1679 await waitForRender();1680 expect(componentOneExports.default.loaders[0]).toHaveBeenCalledWith(1681 expect.objectContaining({1682 id: 'component-one--a',1683 parameters: {1684 __isArgsStory: false,1685 docs: { container: expect.any(Function) },1686 fileName: './src/ComponentOne.stories.js',1687 },1688 initialArgs: { foo: 'a' },1689 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },1690 args: { foo: 'a' },1691 })1692 );1693 });1694 it('passes loaded context to renderToDOM', async () => {1695 document.location.search = '?id=component-one--a&viewMode=docs';1696 await createAndRenderPreview();1697 mockChannel.emit.mockClear();1698 emitter.emit(Events.SET_CURRENT_STORY, {1699 storyId: 'component-one--a',1700 viewMode: 'story',1701 });1702 await waitForSetCurrentStory();1703 await waitForRender();1704 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(1705 expect.objectContaining({1706 forceRemount: true,1707 storyContext: expect.objectContaining({1708 id: 'component-one--a',1709 parameters: {1710 __isArgsStory: false,1711 docs: { container: expect.any(Function) },1712 fileName: './src/ComponentOne.stories.js',1713 },1714 globals: { a: 'b' },1715 initialArgs: { foo: 'a' },1716 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },1717 args: { foo: 'a' },1718 loaded: { l: 7 },1719 }),1720 }),1721 undefined // this is coming from view.prepareForStory, not super important1722 );1723 });1724 it('renders exception if renderToDOM throws', async () => {1725 document.location.search = '?id=component-one--a&viewMode=docs';1726 const preview = await createAndRenderPreview();1727 const error = new Error('error');1728 projectAnnotations.renderToDOM.mockImplementationOnce(() => {1729 throw error;1730 });1731 mockChannel.emit.mockClear();1732 emitter.emit(Events.SET_CURRENT_STORY, {1733 storyId: 'component-one--a',1734 viewMode: 'story',1735 });1736 await waitForSetCurrentStory();1737 await waitForRender();1738 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);1739 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);1740 });1741 it('renders error if the story calls showError', async () => {1742 const error = { title: 'title', description: 'description' };1743 projectAnnotations.renderToDOM.mockImplementationOnce((context) =>1744 context.showError(error)1745 );1746 document.location.search = '?id=component-one--a&viewMode=docs';1747 const preview = await createAndRenderPreview();1748 mockChannel.emit.mockClear();1749 emitter.emit(Events.SET_CURRENT_STORY, {1750 storyId: 'component-one--a',1751 viewMode: 'story',1752 });1753 await waitForSetCurrentStory();1754 await waitForRender();1755 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ERRORED, error);1756 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith({1757 message: error.title,1758 stack: error.description,1759 });1760 });1761 it('renders exception if the story calls showException', async () => {1762 const error = new Error('error');1763 projectAnnotations.renderToDOM.mockImplementationOnce((context) =>1764 context.showException(error)1765 );1766 document.location.search = '?id=component-one--a&viewMode=docs';1767 const preview = await createAndRenderPreview();1768 mockChannel.emit.mockClear();1769 emitter.emit(Events.SET_CURRENT_STORY, {1770 storyId: 'component-one--a',1771 viewMode: 'story',1772 });1773 await waitForSetCurrentStory();1774 await waitForRender();1775 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);1776 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);1777 });1778 it('executes playFunction', async () => {1779 document.location.search = '?id=component-one--a&viewMode=docs';1780 await createAndRenderPreview();1781 mockChannel.emit.mockClear();1782 emitter.emit(Events.SET_CURRENT_STORY, {1783 storyId: 'component-one--a',1784 viewMode: 'story',1785 });1786 await waitForSetCurrentStory();1787 await waitForRender();1788 expect(componentOneExports.a.play).toHaveBeenCalled();1789 });1790 it('emits STORY_RENDERED', async () => {1791 document.location.search = '?id=component-one--a&viewMode=docs';1792 await createAndRenderPreview();1793 mockChannel.emit.mockClear();1794 emitter.emit(Events.SET_CURRENT_STORY, {1795 storyId: 'component-one--a',1796 viewMode: 'story',1797 });1798 await waitForSetCurrentStory();1799 await waitForRender();1800 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--a');1801 });1802 });1803 });1804 describe('onStoriesChanged', () => {1805 describe('if stories.json endpoint 500s initially', () => {1806 it('recovers and renders the story', async () => {1807 document.location.search = '?id=component-one--a';1808 const err = new Error('sort error');1809 mockFetchResult = { status: 500, text: async () => err.toString() };1810 const preview = new PreviewWeb();1811 await expect(preview.initialize({ importFn, getProjectAnnotations })).rejects.toThrow(1812 'sort error'1813 );1814 expect(preview.view.showErrorDisplay).toHaveBeenCalled();1815 expect(mockChannel.emit).toHaveBeenCalledWith(Events.CONFIG_ERROR, expect.any(Error));1816 mockChannel.emit.mockClear();1817 mockFetchResult = { status: 200, json: mockStoryIndex, text: () => 'error text' };1818 preview.onStoryIndexChanged();1819 await waitForRender();1820 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--a');1821 });1822 it('sets story args from the URL', async () => {1823 document.location.search = '?id=component-one--a&args=foo:url';1824 const err = new Error('sort error');1825 mockFetchResult = { status: 500, text: async () => err.toString() };1826 const preview = new PreviewWeb();1827 await expect(preview.initialize({ importFn, getProjectAnnotations })).rejects.toThrow(1828 'sort error'1829 );1830 expect(preview.view.showErrorDisplay).toHaveBeenCalled();1831 expect(mockChannel.emit).toHaveBeenCalledWith(Events.CONFIG_ERROR, expect.any(Error));1832 mockChannel.emit.mockClear();1833 mockFetchResult = { status: 200, json: mockStoryIndex, text: () => 'error text' };1834 preview.onStoryIndexChanged();1835 await waitForRender();1836 expect(preview.storyStore.args.get('component-one--a')).toEqual({1837 foo: 'url',1838 });1839 });1840 });1841 describe('when the current story changes', () => {1842 const newComponentOneExports = merge({}, componentOneExports, {1843 a: { args: { foo: 'edited' } },1844 });1845 const newImportFn = jest.fn(async (path) => {1846 return path === './src/ComponentOne.stories.js'1847 ? newComponentOneExports1848 : componentTwoExports;1849 });1850 it('does not emit STORY_UNCHANGED', async () => {1851 document.location.search = '?id=component-one--a';1852 const preview = await createAndRenderPreview();1853 mockChannel.emit.mockClear();1854 preview.onStoriesChanged({ importFn: newImportFn });1855 await waitForRender();1856 expect(mockChannel.emit).not.toHaveBeenCalledWith(1857 Events.STORY_UNCHANGED,1858 'component-one--a'1859 );1860 });1861 it('does not emit STORY_CHANGED', async () => {1862 document.location.search = '?id=component-one--a';1863 const preview = await createAndRenderPreview();1864 mockChannel.emit.mockClear();1865 preview.onStoriesChanged({ importFn: newImportFn });1866 await waitForRender();1867 expect(mockChannel.emit).not.toHaveBeenCalledWith(Events.STORY_CHANGED, 'component-one--a');1868 });1869 it('emits STORY_PREPARED with new annotations', async () => {1870 document.location.search = '?id=component-one--a';1871 const preview = await createAndRenderPreview();1872 mockChannel.emit.mockClear();1873 preview.onStoriesChanged({ importFn: newImportFn });1874 await waitForRender();1875 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_PREPARED, {1876 id: 'component-one--a',1877 parameters: {1878 __isArgsStory: false,1879 docs: { container: expect.any(Function) },1880 fileName: './src/ComponentOne.stories.js',1881 },1882 initialArgs: { foo: 'edited' },1883 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },1884 args: { foo: 'edited' },1885 });1886 });1887 it('emits STORY_ARGS_UPDATED with new args', async () => {1888 document.location.search = '?id=component-one--a';1889 const preview = await createAndRenderPreview();1890 mockChannel.emit.mockClear();1891 preview.onStoriesChanged({ importFn: newImportFn });1892 await waitForRender();1893 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ARGS_UPDATED, {1894 storyId: 'component-one--a',1895 args: { foo: 'edited' },1896 });1897 });1898 it('applies loaders with story context', async () => {1899 document.location.search = '?id=component-one--a';1900 const preview = await createAndRenderPreview();1901 mockChannel.emit.mockClear();1902 componentOneExports.default.loaders[0].mockClear();1903 preview.onStoriesChanged({ importFn: newImportFn });1904 await waitForRender();1905 expect(componentOneExports.default.loaders[0]).toHaveBeenCalledWith(1906 expect.objectContaining({1907 id: 'component-one--a',1908 parameters: {1909 __isArgsStory: false,1910 docs: { container: expect.any(Function) },1911 fileName: './src/ComponentOne.stories.js',1912 },1913 initialArgs: { foo: 'edited' },1914 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },1915 args: { foo: 'edited' },1916 })1917 );1918 });1919 it('passes loaded context to renderToDOM', async () => {1920 document.location.search = '?id=component-one--a';1921 const preview = await createAndRenderPreview();1922 mockChannel.emit.mockClear();1923 projectAnnotations.renderToDOM.mockClear();1924 preview.onStoriesChanged({ importFn: newImportFn });1925 await waitForRender();1926 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(1927 expect.objectContaining({1928 forceRemount: true,1929 storyContext: expect.objectContaining({1930 id: 'component-one--a',1931 parameters: {1932 __isArgsStory: false,1933 docs: { container: expect.any(Function) },1934 fileName: './src/ComponentOne.stories.js',1935 },1936 globals: { a: 'b' },1937 initialArgs: { foo: 'edited' },1938 argTypes: { foo: { name: 'foo', type: { name: 'string' } } },1939 args: { foo: 'edited' },1940 loaded: { l: 7 },1941 }),1942 }),1943 undefined // this is coming from view.prepareForStory, not super important1944 );1945 });1946 it('retains the same delta to the args', async () => {1947 document.location.search = '?id=component-one--a';1948 const preview = await createAndRenderPreview();1949 mockChannel.emit.mockClear();1950 emitter.emit(Events.UPDATE_STORY_ARGS, {1951 storyId: 'component-one--a',1952 updatedArgs: { foo: 'updated' },1953 });1954 await waitForRender();1955 mockChannel.emit.mockClear();1956 projectAnnotations.renderToDOM.mockClear();1957 preview.onStoriesChanged({ importFn: newImportFn });1958 await waitForRender();1959 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(1960 expect.objectContaining({1961 forceRemount: true,1962 storyContext: expect.objectContaining({1963 id: 'component-one--a',1964 args: { foo: 'updated' },1965 }),1966 }),1967 undefined // this is coming from view.prepareForStory, not super important1968 );1969 });1970 it('renders exception if renderToDOM throws', async () => {1971 document.location.search = '?id=component-one--a';1972 const preview = await createAndRenderPreview();1973 const error = new Error('error');1974 projectAnnotations.renderToDOM.mockImplementationOnce(() => {1975 throw error;1976 });1977 mockChannel.emit.mockClear();1978 preview.onStoriesChanged({ importFn: newImportFn });1979 await waitForRender();1980 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);1981 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);1982 });1983 it('renders error if the story calls showError', async () => {1984 document.location.search = '?id=component-one--a';1985 const preview = await createAndRenderPreview();1986 const error = { title: 'title', description: 'description' };1987 projectAnnotations.renderToDOM.mockImplementationOnce((context) =>1988 context.showError(error)1989 );1990 mockChannel.emit.mockClear();1991 preview.onStoriesChanged({ importFn: newImportFn });1992 await waitForRender();1993 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ERRORED, error);1994 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith({1995 message: error.title,1996 stack: error.description,1997 });1998 });1999 it('renders exception if the story calls showException', async () => {2000 document.location.search = '?id=component-one--a';2001 const preview = await createAndRenderPreview();2002 const error = new Error('error');2003 projectAnnotations.renderToDOM.mockImplementationOnce((context) =>2004 context.showException(error)2005 );2006 mockChannel.emit.mockClear();2007 preview.onStoriesChanged({ importFn: newImportFn });2008 await waitForRender();2009 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_THREW_EXCEPTION, error);2010 expect(preview.view.showErrorDisplay).toHaveBeenCalledWith(error);2011 });2012 it('executes playFunction', async () => {2013 document.location.search = '?id=component-one--a';2014 const preview = await createAndRenderPreview();2015 mockChannel.emit.mockClear();2016 componentOneExports.a.play.mockClear();2017 preview.onStoriesChanged({ importFn: newImportFn });2018 await waitForRender();2019 expect(componentOneExports.a.play).toHaveBeenCalled();2020 });2021 it('emits STORY_RENDERED', async () => {2022 document.location.search = '?id=component-one--a';2023 const preview = await createAndRenderPreview();2024 mockChannel.emit.mockClear();2025 preview.onStoriesChanged({ importFn: newImportFn });2026 await waitForRender();2027 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--a');2028 });2029 });2030 describe('when the current story changes importPath', () => {2031 const newImportFn = jest.fn(async (path) => ({ ...componentOneExports }));2032 const newStoryIndex = {2033 v: 3,2034 stories: {2035 ...storyIndex.stories,2036 'component-one--a': {2037 ...storyIndex.stories['component-one--a'],2038 importPath: './src/ComponentOne-new.stories.js',2039 },2040 },2041 };2042 beforeEach(() => {2043 newImportFn.mockClear();2044 });2045 it('re-imports the component', async () => {2046 document.location.search = '?id=component-one--a';2047 const preview = await createAndRenderPreview();2048 mockChannel.emit.mockClear();2049 preview.onStoriesChanged({ importFn: newImportFn, storyIndex: newStoryIndex });2050 await waitForRender();2051 expect(newImportFn).toHaveBeenCalledWith('./src/ComponentOne-new.stories.js');2052 });2053 describe('if it was previously rendered', () => {2054 beforeEach(() => jest.useFakeTimers());2055 afterEach(() => jest.useRealTimers());2056 it('is reloaded when it is re-selected', async () => {2057 document.location.search = '?id=component-one--a';2058 const preview = await createAndRenderPreview();2059 mockChannel.emit.mockClear();2060 emitter.emit(Events.SET_CURRENT_STORY, {2061 storyId: 'component-one--b',2062 viewMode: 'story',2063 });2064 await waitForSetCurrentStory();2065 await waitForRender();2066 preview.onStoriesChanged({ importFn: newImportFn, storyIndex: newStoryIndex });2067 mockChannel.emit.mockClear();2068 emitter.emit(Events.SET_CURRENT_STORY, {2069 storyId: 'component-one--a',2070 viewMode: 'story',2071 });2072 await waitForSetCurrentStory();2073 await waitForRender();2074 expect(newImportFn).toHaveBeenCalledWith('./src/ComponentOne-new.stories.js');2075 });2076 });2077 });2078 describe('when the current story has not changed', () => {2079 const newComponentTwoExports = { ...componentTwoExports };2080 const newImportFn = jest.fn(async (path) => {2081 return path === './src/ComponentOne.stories.js'2082 ? componentOneExports2083 : newComponentTwoExports;2084 });2085 it('emits STORY_UNCHANGED', async () => {2086 document.location.search = '?id=component-one--a';2087 const preview = await createAndRenderPreview();2088 mockChannel.emit.mockClear();2089 preview.onStoriesChanged({ importFn: newImportFn });2090 await waitForEvents([Events.STORY_UNCHANGED]);2091 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_UNCHANGED, 'component-one--a');2092 expect(mockChannel.emit).not.toHaveBeenCalledWith(Events.STORY_CHANGED, 'component-one--a');2093 });2094 it('clears preparing state', async () => {2095 document.location.search = '?id=component-one--a';2096 const preview = await createAndRenderPreview();2097 (preview.view.showMain as jest.Mock).mockClear();2098 mockChannel.emit.mockClear();2099 preview.onStoriesChanged({ importFn: newImportFn });2100 await waitForEvents([Events.STORY_UNCHANGED]);2101 expect(preview.view.showMain).toHaveBeenCalled();2102 });2103 it('does not re-render the story', async () => {2104 document.location.search = '?id=component-one--a';2105 const preview = await createAndRenderPreview();2106 mockChannel.emit.mockClear();2107 projectAnnotations.renderToDOM.mockClear();2108 preview.onStoriesChanged({ importFn: newImportFn });2109 await waitForQuiescence();2110 expect(projectAnnotations.renderToDOM).not.toHaveBeenCalled();2111 expect(mockChannel.emit).not.toHaveBeenCalledWith(2112 Events.STORY_RENDERED,2113 'component-one--a'2114 );2115 });2116 });2117 describe('when another (not current) story changes', () => {2118 beforeEach(() => {2119 jest.useFakeTimers();2120 });2121 afterEach(() => {2122 jest.useRealTimers();2123 });2124 const newComponentOneExports = merge({}, componentOneExports, {2125 a: { args: { bar: 'edited' }, argTypes: { bar: { type: { name: 'string' } } } },2126 });2127 const newImportFn = jest.fn(async (path) => {2128 return path === './src/ComponentOne.stories.js'2129 ? newComponentOneExports2130 : componentTwoExports;2131 });2132 it('retains the same delta to the args', async () => {2133 // Start at Story A2134 document.location.search = '?id=component-one--a';2135 const preview = await createAndRenderPreview();2136 // Change A's args2137 mockChannel.emit.mockClear();2138 emitter.emit(Events.UPDATE_STORY_ARGS, {2139 storyId: 'component-one--a',2140 updatedArgs: { foo: 'updated' },2141 });2142 await waitForRender();2143 // Change to story B2144 mockChannel.emit.mockClear();2145 emitter.emit(Events.SET_CURRENT_STORY, {2146 storyId: 'component-one--b',2147 viewMode: 'story',2148 });2149 await waitForSetCurrentStory();2150 await waitForRender();2151 expect(preview.storyStore.args.get('component-one--a')).toEqual({2152 foo: 'updated',2153 });2154 // Update story A's args via HMR2155 mockChannel.emit.mockClear();2156 projectAnnotations.renderToDOM.mockClear();2157 preview.onStoriesChanged({ importFn: newImportFn });2158 await waitForRender();2159 // Change back to Story A2160 mockChannel.emit.mockClear();2161 emitter.emit(Events.SET_CURRENT_STORY, {2162 storyId: 'component-one--a',2163 viewMode: 'story',2164 });2165 await waitForSetCurrentStory();2166 await waitForRender();2167 expect(preview.storyStore.args.get('component-one--a')).toEqual({2168 foo: 'updated',2169 bar: 'edited',2170 });2171 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(2172 expect.objectContaining({2173 forceRemount: true,2174 storyContext: expect.objectContaining({2175 id: 'component-one--a',2176 args: { foo: 'updated', bar: 'edited' },2177 }),2178 }),2179 undefined // this is coming from view.prepareForStory, not super important2180 );2181 });2182 });2183 describe('if the story no longer exists', () => {2184 const { a, ...componentOneExportsWithoutA } = componentOneExports;2185 const newImportFn = jest.fn(async (path) => {2186 return path === './src/ComponentOne.stories.js'2187 ? componentOneExportsWithoutA2188 : componentTwoExports;2189 });2190 const newStoryIndex = {2191 v: 3,2192 stories: {2193 'component-one--b': storyIndex.stories['component-one--b'],2194 },2195 };2196 it('renders loading error', async () => {2197 document.location.search = '?id=component-one--a';2198 const preview = await createAndRenderPreview();2199 mockChannel.emit.mockClear();2200 preview.onStoriesChanged({ importFn: newImportFn, storyIndex: newStoryIndex });2201 await waitForEvents([Events.STORY_MISSING]);2202 expect(preview.view.showErrorDisplay).toHaveBeenCalled();2203 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_MISSING, 'component-one--a');2204 });2205 it('does not re-render the story', async () => {2206 document.location.search = '?id=component-one--a';2207 const preview = await createAndRenderPreview();2208 mockChannel.emit.mockClear();2209 projectAnnotations.renderToDOM.mockClear();2210 preview.onStoriesChanged({ importFn: newImportFn, storyIndex: newStoryIndex });2211 await waitForQuiescence();2212 expect(projectAnnotations.renderToDOM).not.toHaveBeenCalled();2213 expect(mockChannel.emit).not.toHaveBeenCalledWith(2214 Events.STORY_RENDERED,2215 'component-one--a'2216 );2217 });2218 it('re-renders the story if it is readded', async () => {2219 document.location.search = '?id=component-one--a';2220 const preview = await createAndRenderPreview();2221 mockChannel.emit.mockClear();2222 preview.onStoriesChanged({ importFn: newImportFn, storyIndex: newStoryIndex });2223 await waitForEvents([Events.STORY_MISSING]);2224 mockChannel.emit.mockClear();2225 preview.onStoriesChanged({ importFn, storyIndex });2226 await waitForRender();2227 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--a');2228 });2229 });2230 });2231 describe('onGetProjectAnnotationsChanged', () => {2232 describe('if initial getProjectAnnotations threw', () => {2233 it('recovers and renders the story', async () => {2234 document.location.search = '?id=component-one--a';2235 const err = new Error('meta error');2236 const preview = new PreviewWeb();2237 await expect(2238 preview.initialize({2239 importFn,2240 getProjectAnnotations: () => {2241 throw err;2242 },2243 })2244 ).rejects.toThrow(err);2245 preview.onGetProjectAnnotationsChanged({ getProjectAnnotations });2246 await waitForRender();2247 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_RENDERED, 'component-one--a');2248 });2249 it('sets globals from the URL', async () => {2250 document.location.search = '?id=*&globals=a:c';2251 const err = new Error('meta error');2252 const preview = new PreviewWeb();2253 await expect(2254 preview.initialize({2255 importFn,2256 getProjectAnnotations: () => {2257 throw err;2258 },2259 })2260 ).rejects.toThrow(err);2261 preview.onGetProjectAnnotationsChanged({ getProjectAnnotations });2262 await waitForRender();2263 expect(preview.storyStore.globals.get()).toEqual({ a: 'c' });2264 });2265 });2266 it('shows an error the new value throws', async () => {2267 document.location.search = '?id=component-one--a';2268 const preview = await createAndRenderPreview();2269 mockChannel.emit.mockClear();2270 const err = new Error('error getting meta');2271 await expect(2272 preview.onGetProjectAnnotationsChanged({2273 getProjectAnnotations: () => {2274 throw err;2275 },2276 })2277 ).rejects.toThrow(err);2278 expect(preview.view.showErrorDisplay).toHaveBeenCalled();2279 expect(mockChannel.emit).toHaveBeenCalledWith(Events.CONFIG_ERROR, err);2280 });2281 const newGlobalDecorator = jest.fn((s) => s());2282 const newGetProjectAnnotations = () => {2283 return {2284 ...projectAnnotations,2285 args: { global: 'added' },2286 globals: { a: 'edited' },2287 decorators: [newGlobalDecorator],2288 };2289 };2290 it('updates globals to their new values', async () => {2291 document.location.search = '?id=component-one--a';2292 const preview = await createAndRenderPreview();2293 mockChannel.emit.mockClear();2294 preview.onGetProjectAnnotationsChanged({ getProjectAnnotations: newGetProjectAnnotations });2295 await waitForRender();2296 expect(preview.storyStore.globals.get()).toEqual({ a: 'edited' });2297 });2298 it('emits SET_GLOBALS with new values', async () => {2299 document.location.search = '?id=component-one--a';2300 const preview = await createAndRenderPreview();2301 mockChannel.emit.mockClear();2302 preview.onGetProjectAnnotationsChanged({ getProjectAnnotations: newGetProjectAnnotations });2303 await waitForRender();2304 await waitForEvents([Events.SET_GLOBALS]);2305 expect(mockChannel.emit).toHaveBeenCalledWith(Events.SET_GLOBALS, {2306 globals: { a: 'edited' },2307 globalTypes: {},2308 });2309 });2310 it('updates args to their new values', async () => {2311 document.location.search = '?id=component-one--a';2312 const preview = await createAndRenderPreview();2313 mockChannel.emit.mockClear();2314 preview.onGetProjectAnnotationsChanged({ getProjectAnnotations: newGetProjectAnnotations });2315 await waitForRender();2316 expect(preview.storyStore.args.get('component-one--a')).toEqual({2317 foo: 'a',2318 global: 'added',2319 });2320 });2321 it('emits SET_STORY_ARGS with new values', async () => {2322 document.location.search = '?id=component-one--a';2323 const preview = await createAndRenderPreview();2324 mockChannel.emit.mockClear();2325 preview.onGetProjectAnnotationsChanged({ getProjectAnnotations: newGetProjectAnnotations });2326 await waitForRender();2327 expect(mockChannel.emit).toHaveBeenCalledWith(Events.STORY_ARGS_UPDATED, {2328 storyId: 'component-one--a',2329 args: { foo: 'a', global: 'added' },2330 });2331 });2332 it('rerenders the current story with new global meta-generated context', async () => {2333 document.location.search = '?id=component-one--a';2334 const preview = await createAndRenderPreview();2335 projectAnnotations.renderToDOM.mockClear();2336 mockChannel.emit.mockClear();2337 preview.onGetProjectAnnotationsChanged({ getProjectAnnotations: newGetProjectAnnotations });2338 await waitForRender();2339 expect(projectAnnotations.renderToDOM).toHaveBeenCalledWith(2340 expect.objectContaining({2341 storyContext: expect.objectContaining({2342 args: { foo: 'a', global: 'added' },2343 globals: { a: 'edited' },2344 }),2345 }),2346 undefined // this is coming from view.prepareForStory, not super important2347 );2348 });2349 });2350 describe('onKeydown', () => {2351 it('emits PREVIEW_KEYDOWN for regular elements', async () => {2352 document.location.search = '?id=component-one--a&viewMode=docs';2353 const preview = await createAndRenderPreview();2354 preview.onKeydown({2355 target: { tagName: 'div', getAttribute: jest.fn().mockReturnValue(null) },2356 } as any);2357 expect(mockChannel.emit).toHaveBeenCalledWith(2358 Events.PREVIEW_KEYDOWN,2359 expect.objectContaining({})2360 );2361 });2362 it('does not emit PREVIEW_KEYDOWN for input elements', async () => {2363 document.location.search = '?id=component-one--a&viewMode=docs';2364 const preview = await createAndRenderPreview();2365 preview.onKeydown({2366 target: { tagName: 'input', getAttribute: jest.fn().mockReturnValue(null) },2367 } as any);2368 expect(mockChannel.emit).not.toHaveBeenCalledWith(2369 Events.PREVIEW_KEYDOWN,2370 expect.objectContaining({})2371 );2372 });2373 });2374 describe('extract', () => {2375 // NOTE: if you are using storyStoreV6, and your `preview.js` throws, we do not currently2376 // detect it (as we do not wrap the import of `preview.js` in a `try/catch`). The net effect2377 // of that is that the `PreviewWeb`/`StoryStore` end up in an uninitalized state.2378 it('throws an error if the preview is uninitialized', async () => {2379 const preview = new PreviewWeb();2380 await expect(preview.extract()).rejects.toThrow(/Failed to initialize/);2381 });2382 it('throws an error if preview.js throws', async () => {2383 const err = new Error('meta error');2384 const preview = new PreviewWeb();2385 await expect(2386 preview.initialize({2387 importFn,2388 getProjectAnnotations: () => {2389 throw err;2390 },2391 })2392 ).rejects.toThrow(err);2393 await expect(preview.extract()).rejects.toThrow(err);2394 });2395 it('shows an error if the stories.json endpoint 500s', async () => {2396 const err = new Error('sort error');2397 mockFetchResult = { status: 500, text: async () => err.toString() };2398 const preview = new PreviewWeb();2399 await expect(preview.initialize({ importFn, getProjectAnnotations })).rejects.toThrow(2400 'sort error'2401 );2402 await expect(preview.extract()).rejects.toThrow('sort error');2403 });2404 it('waits for stories to be cached', async () => {2405 const [gate, openGate] = createGate();2406 const gatedImportFn = async (path) => {2407 await gate;2408 return importFn(path);2409 };2410 const preview = await createAndRenderPreview({ importFn: gatedImportFn });2411 let extracted = false;2412 preview.extract().then(() => {2413 extracted = true;2414 });2415 expect(extracted).toBe(false);2416 openGate();2417 await new Promise((r) => setTimeout(r, 0)); // Let the promise resolve2418 expect(extracted).toBe(true);2419 expect(await preview.extract()).toMatchInlineSnapshot(`2420 Object {2421 "component-one--a": Object {2422 "argTypes": Object {2423 "foo": Object {2424 "name": "foo",2425 "type": Object {2426 "name": "string",2427 },2428 },2429 },2430 "args": Object {2431 "foo": "a",2432 },2433 "component": undefined,2434 "componentId": "component-one",2435 "id": "component-one--a",2436 "initialArgs": Object {2437 "foo": "a",2438 },2439 "kind": "Component One",2440 "name": "A",2441 "parameters": Object {2442 "__isArgsStory": false,2443 "docs": Object {2444 "container": [MockFunction],2445 },2446 "fileName": "./src/ComponentOne.stories.js",2447 },2448 "story": "A",2449 "subcomponents": undefined,2450 "title": "Component One",2451 },2452 "component-one--b": Object {2453 "argTypes": Object {2454 "foo": Object {2455 "name": "foo",2456 "type": Object {2457 "name": "string",2458 },2459 },2460 },2461 "args": Object {2462 "foo": "b",2463 },2464 "component": undefined,2465 "componentId": "component-one",2466 "id": "component-one--b",2467 "initialArgs": Object {2468 "foo": "b",2469 },2470 "kind": "Component One",2471 "name": "B",2472 "parameters": Object {2473 "__isArgsStory": false,2474 "docs": Object {2475 "container": [MockFunction],2476 },2477 "fileName": "./src/ComponentOne.stories.js",2478 },2479 "story": "B",2480 "subcomponents": undefined,2481 "title": "Component One",2482 },2483 "component-two--c": Object {2484 "argTypes": Object {2485 "foo": Object {2486 "name": "foo",2487 "type": Object {2488 "name": "string",2489 },2490 },2491 },2492 "args": Object {2493 "foo": "c",2494 },2495 "component": undefined,2496 "componentId": "component-two",2497 "id": "component-two--c",2498 "initialArgs": Object {2499 "foo": "c",2500 },2501 "kind": "Component Two",2502 "name": "C",2503 "parameters": Object {2504 "__isArgsStory": false,2505 "fileName": "./src/ComponentTwo.stories.js",2506 },2507 "playFunction": undefined,2508 "story": "C",2509 "subcomponents": undefined,2510 "title": "Component Two",2511 },2512 }2513 `);2514 });2515 });...
WebView.ts
Source:WebView.ts
...50 default: // pass;51 }52 }53 // Get ready to render a story, returning the element to render to54 prepareForStory(story: Story<any>) {55 this.showStory();56 this.applyLayout(story.parameters.layout);57 document.documentElement.scrollTop = 0;58 document.documentElement.scrollLeft = 0;59 return this.storyRoot();60 }61 storyRoot(): HTMLElement {62 return document.getElementById('root');63 }64 prepareForDocs() {65 this.showMain();66 this.showDocs();67 this.applyLayout('fullscreen');68 return this.docsRoot();...
Using AI Code Generation
1import { prepareForStory } from './storybook-root';2import { prepareForStory } from './storybook-root';3import { prepareForStory } from './storybook-root';4import { prepareForStory } from './storybook-root';5import { configure } from '@storybook/react';6import { setOptions } from '@storybook/addon-options';7setOptions({
Using AI Code Generation
1import {prepareForStory} from 'storybook-root';2import {prepareForStory} from 'storybook-root';3import {prepareForStory} from 'storybook-root';4import {prepareForStory} from 'storybook-root';5import {prepareForStory} from 'storybook-root';6import {prepareForStory} from 'storybook-root';7import {prepareForStory} from 'storybook-root';8import {prepareForStory} from 'storybook-root';9import {prepareForStory} from 'storybook-root';10import {prepareForStory} from 'storybook-root';11import {prepareForStory} from 'storybook-root';12import {prepareForStory} from 'storybook-root';13import {prepareForStory} from 'storybook-root';14import {prepareForStory} from 'storybook-root';15import {prepareForStory} from 'storybook-root';16import {prepareForStory} from 'storybook-root';17import {prepareForStory} from 'storybook-root';18import {prepareForStory} from 'storybook-root';19import {prepareForStory} from 'storybook-root';
Using AI Code Generation
1import { prepareForStory } from 'storybook-root';2prepareForStory(story, context);3import { prepareForStory } from 'storybook-root';4prepareForStory(story, context);5import { prepareForStory } from 'storybook-root';6prepareForStory(story, context);7import { prepareForStory } from 'storybook-root';8prepareForStory(story, context);9import { prepareForStory } from 'storybook-root';10prepareForStory(story, context);11import { prepareForStory } from 'storybook-root';12prepareForStory(story, context);13import { prepareForStory } from 'storybook-root';14prepareForStory(story, context);15import { prepareForStory } from 'storybook-root';16prepareForStory(story, context);17import { prepareForStory } from 'storybook-root';18prepareForStory(story, context);19import { prepareForStory } from 'storybook-root';20prepareForStory(story, context);21import { prepareForStory } from 'storybook-root';22prepareForStory(story, context);23import { prepareForStory } from 'storybook-root';24prepareForStory(story, context);25import { prepareForStory } from 'storybook-root';26prepareForStory(story, context);27import { prepareForStory } from 'storybook-root';28prepareForStory(story, context);29import { prepareForStory } from 'storybook
Using AI Code Generation
1import {prepareForStory} from 'storybook-root-siblings';2prepareForStory();3import RootSiblings from 'storybook-root-siblings';4export const story = () => {5 return <RootSiblings>Test</RootSiblings>;6};7export default {8};9import RootSiblings from 'storybook-root-siblings';10const sibling = new RootSiblings(<Text>Hello!</Text>);11sibling.destroy();12sibling.update(<Text>Hi!</Text>);13const sibling = RootSiblings.current;14const sibling = RootSiblings.getSiblings()[0];15const sibling = new RootSiblings(<Text>Hello!</Text>, () => {16});17sibling.update(<Text>Hi!</Text>, () => {18});19sibling.destroy(() => {20});21### new RootSiblings(reactElement, callback)22### sibling.destroy(callback)23### sibling.update(reactElement, callback)24### RootSiblings.getSiblings()
Using AI Code Generation
1import { prepareForStory } from 'storybook-root';2import { MyComponent } from 'my-component';3prepareForStory(MyComponent, 'my-component', module);4import { MyComponent } from 'my-component';5export default {6};7const Template = (args) => <MyComponent {...args} />;8export const Default = Template.bind({});9Default.args = {10};11import { MyComponent } from 'my-component';12export default {13};14const Template = (args) => <MyComponent {...args} />;15export const Default = Template.bind({});16Default.args = {17};
Using AI Code Generation
1import { prepareForStory } from 'storybook-root';2prepareForStory('welcome', module);3export default {4};5import { prepareForStory } from 'storybook-root';6prepareForStory('welcome', module);7export default {8};9import { prepareForStory } from 'storybook-root';10prepareForStory('welcome', module);11export default {12};13import { prepareForStory } from 'storybook-root';14prepareForStory('welcome', module);15export default {16};17import { prepareForStory } from 'storybook-root';18prepareForStory('welcome', module);19export default {20};21import { prepareForStory } from 'storybook-root';22prepareForStory('welcome', module);23export default {24};25import { prepareForStory } from 'storybook-root';26prepareForStory('welcome', module);27export default {28};29import { prepareForStory } from 'storybook-root';30prepareForStory('welcome', module);31export default {32};33import { prepareForStory } from 'storybook-root';34prepareForStory('welcome', module);35export default {36};37import { prepareForStory } from 'storybook-root';38prepareForStory('welcome', module);39export default {40};41import { prepareForStory } from 'storybook-root';42prepareForStory('welcome', module);43export default {44};45import { prepareForStory } from 'storybook-root';46prepareForStory('welcome', module);47export default {48};49import { prepareForStory } from 'storybook-root';50prepareForStory('welcome', module);51export default {52};53import { prepareForStory } from 'storybook-root
Using AI Code Generation
1const rootCause = require('storybook-root-cause');2const { prepareForStory } = rootCause;3const storyName = 'some story name';4const storyPath = 'path to the story file';5const storybookUrl = 'url to the storybook';6const storybookPort = 6006;7const storybookPath = 'storybook path';8const storybookParameters = { 'some': 'parameters' };9const storybookKind = 'storybook kind';10const storybookStory = 'storybook story';11prepareForStory(storyName, storyPath, storybookUrl, storybookPort, storybookPath, storybookParameters, storybookKind, storybookStory);12const rootCause = require('storybook-root-cause');13const { prepareForStory } = rootCause;14const storyName = 'some story name';15const storyPath = 'path to the story file';16const storybookUrl = 'url to the storybook';17const storybookPort = 6006;18const storybookPath = 'storybook path';19const storybookParameters = { 'some': 'parameters' };20const storybookKind = 'storybook kind';21const storybookStory = 'storybook story';22prepareForStory(storyName, storyPath, storybookUrl, storybookPort, storybookPath, storybookParameters, storybookKind, storybookStory);23### `prepareForStory(storyName, storyPath, storybookUrl, storybookPort, storybookPath, storybookParameters, storybookKind, storybookStory)`24#### `getStoryUrl()`25#### `getStoryParameters()`26#### `getStoryKind()`27#### `getStoryName()`28#### `getStory()`
Using AI Code Generation
1const { prepareForStory } = require("storybook-root-cause/lib/cjs/prepareForStory");2const { story, storybookUrl } = prepareForStory("Button", "with text");3const { getStorybookUrl } = require("storybook-root-cause/lib/cjs/getStorybookUrl");4const storybookUrl = getStorybookUrl();5const { getStory } = require("storybook-root-cause/lib/cjs/getStory");6const story = getStory("Button", "with text");7const { getStorybookConfig } = require("storybook-root-cause/lib/cjs/getStorybookConfig");8const storybookConfig = getStorybookConfig();9const { prepareForStory } = require("storybook-root-cause/lib/esm/prepareForStory");10const { story, storybookUrl } = prepareForStory("Button", "with text");11const { getStorybookUrl } = require("storybook-root-cause/lib/esm/getStorybookUrl");12const storybookUrl = getStorybookUrl();13const { getStory } = require("storybook-root-cause/lib/esm/getStory");14const story = getStory("Button", "with text");15const { getStorybookConfig } = require("storybook-root-cause/lib/esm/getStorybookConfig");16const storybookConfig = getStorybookConfig();17const { prepareForStory } = require("storybook-root-cause/lib/umd/prepareForStory");18const { story, storybookUrl } = prepareForStory("Button", "with text");
Learn to execute automation testing from scratch with LambdaTest Learning Hub. Right from setting up the prerequisites to run your first automation test, to following best practices and diving deeper into advanced test scenarios. LambdaTest Learning Hubs compile a list of step-by-step guides to help you be proficient with different test automation frameworks i.e. Selenium, Cypress, TestNG etc.
You could also refer to video tutorials over LambdaTest YouTube channel to get step by step demonstration from industry experts.
Get 100 minutes of automation test minutes FREE!!