Best JavaScript code snippet using storybook-root
instrumenter.test.ts
Source:instrumenter.test.ts
1/* eslint-disable no-underscore-dangle */2import { addons, mockChannel } from '@storybook/addons';3import {4 FORCE_REMOUNT,5 SET_CURRENT_STORY,6 STORY_RENDER_PHASE_CHANGED,7} from '@storybook/core-events';8import global from 'global';9import { EVENTS, Instrumenter } from './instrumenter';10import type { Options } from './types';11const callSpy = jest.fn();12const syncSpy = jest.fn();13const forceRemountSpy = jest.fn();14addons.setChannel(mockChannel());15addons.getChannel().on(EVENTS.CALL, callSpy);16addons.getChannel().on(EVENTS.SYNC, syncSpy);17addons.getChannel().on(FORCE_REMOUNT, forceRemountSpy);18class HTMLElement {19 constructor(props: any) {20 Object.assign(this, props);21 }22}23delete global.window.location;24global.window.location = { reload: jest.fn() };25global.window.HTMLElement = HTMLElement;26const storyId = 'kind--story';27global.window.__STORYBOOK_PREVIEW__ = { urlStore: { selection: { storyId } } };28const setRenderPhase = (newPhase: string) =>29 addons.getChannel().emit(STORY_RENDER_PHASE_CHANGED, { newPhase, storyId });30let instrumenter: Instrumenter;31const instrument = <TObj extends Record<string, any>>(obj: TObj, options: Options = {}) =>32 instrumenter.instrument(obj, options);33beforeEach(() => {34 jest.useRealTimers();35 callSpy.mockClear();36 syncSpy.mockClear();37 forceRemountSpy.mockClear();38 instrumenter = new Instrumenter();39 setRenderPhase('loading');40});41afterEach(() => {42 addons.getChannel().emit(SET_CURRENT_STORY); // trigger a cleanup43});44describe('Instrumenter', () => {45 it('patches object methods', () => {46 const fn = () => {};47 const result = instrument({ fn });48 expect(result).toStrictEqual({ fn: expect.any(Function) });49 expect(result.fn.name).toBe('fn');50 expect(result.fn.__originalFn__).toBe(fn);51 });52 it('patches nested methods', () => {53 const fn1: any = () => {};54 const fn2: any = () => {};55 const result = instrument({ foo: { fn1, bar: { fn2 } } });56 expect(result).toStrictEqual({57 foo: {58 fn1: expect.any(Function),59 bar: { fn2: expect.any(Function) },60 },61 });62 expect(result.foo.fn1.__originalFn__).toBe(fn1);63 expect(result.foo.bar.fn2.__originalFn__).toBe(fn2);64 });65 it('does not patch already patched functions', () => {66 const fn: any = () => {};67 const result = instrument(instrument({ fn }));68 expect(result.fn.__originalFn__).toBe(fn);69 expect(result.fn.__originalFn__.__originalFn__).not.toBeDefined();70 });71 it('does not traverse into arrays', () => {72 const fn1: any = () => {};73 const fn2: any = () => {};74 const result = instrument({ arr: [fn1, { fn2 }] });75 expect(result).toStrictEqual({ arr: [fn1, { fn2 }] });76 expect(result.arr[0].__originalFn__).not.toBeDefined();77 expect(result.arr[1].fn2.__originalFn__).not.toBeDefined();78 });79 it('patches function properties on functions', () => {80 const fn1: any = () => {};81 fn1.fn2 = () => {};82 const result = instrument({ fn1 });83 expect(result.fn1).toEqual(expect.any(Function));84 expect(result.fn1.fn2).toEqual(expect.any(Function));85 expect(result.fn1.__originalFn__).toBe(fn1);86 expect(result.fn1.fn2.__originalFn__).toBe(fn1.fn2);87 });88 it('patched functions call the original function when invoked', () => {89 const { fn } = instrument({ fn: jest.fn() });90 const obj = {};91 fn('foo', obj);92 expect(fn.__originalFn__).toHaveBeenCalledWith('foo', obj);93 });94 it('emits a "call" event every time a patched function is invoked', () => {95 const { fn } = instrument({ fn: (...args: any) => {} });96 fn('foo', 'bar');97 fn('baz');98 expect(callSpy).toHaveBeenCalledWith(99 expect.objectContaining({100 id: 'kind--story [0] fn',101 args: ['foo', 'bar'],102 })103 );104 expect(callSpy).toHaveBeenCalledWith(105 expect.objectContaining({106 id: 'kind--story [1] fn',107 args: ['baz'],108 })109 );110 });111 it('provides metadata about the call in the event', () => {112 const { obj } = instrument({ obj: { fn: () => {} } });113 obj.fn();114 expect(callSpy).toHaveBeenCalledWith(115 expect.objectContaining({116 path: ['obj'],117 method: 'fn',118 interceptable: false,119 status: 'done',120 parentId: undefined,121 })122 );123 });124 it('maps event args which originate from an earlier call to a call ref', () => {125 const { fn1, fn2 } = instrument({126 fn1: (arg: any) => arg,127 fn2: (arg: any) => {},128 });129 fn2(fn1({}));130 expect(callSpy).toHaveBeenLastCalledWith(131 expect.objectContaining({132 method: 'fn2',133 args: [{ __callId__: callSpy.mock.calls[0][0].id, retain: false }],134 })135 );136 });137 it('does not map primitive event args which originate from an earlier call', () => {138 const { fn1, fn2 } = instrument({139 fn1: (...args: any) => args[0],140 fn2: (...args: any) => {},141 });142 fn2(143 fn1(undefined),144 fn1(null),145 fn1(true),146 fn1('foo'),147 fn1(1),148 fn1(BigInt(1)), // eslint-disable-line no-undef149 fn1({}),150 fn1([]),151 fn1(() => {}),152 fn1(Symbol('hi')),153 fn1(new Error('Oops'))154 );155 expect(callSpy).toHaveBeenLastCalledWith(156 expect.objectContaining({157 method: 'fn2',158 args: [159 /* call 0 */ undefined,160 /* call 1 */ null,161 /* call 2 */ true,162 /* call 3 */ 'foo',163 /* call 4 */ 1,164 /* call 5 */ BigInt(1), // eslint-disable-line no-undef165 { __callId__: callSpy.mock.calls[6][0].id, retain: false },166 { __callId__: callSpy.mock.calls[7][0].id, retain: false },167 { __callId__: callSpy.mock.calls[8][0].id, retain: false },168 { __callId__: callSpy.mock.calls[9][0].id, retain: false },169 { __callId__: callSpy.mock.calls[10][0].id, retain: false },170 ],171 })172 );173 });174 it('maps HTML Elements in event args to an element ref', () => {175 const { fn } = instrument({ fn: (...args: any) => {} });176 fn(new HTMLElement({ prefix: '', localName: 'div', id: 'root', classList: [] }));177 expect(callSpy).toHaveBeenLastCalledWith(178 expect.objectContaining({179 args: [{ __element__: { prefix: '', localName: 'div', id: 'root', classNames: [] } }],180 })181 );182 });183 it('tracks the parent call id for calls inside callbacks', () => {184 const fn = (callback?: Function) => callback && callback();185 const { fn1, fn2, fn3, fn4, fn5 } = instrument({ fn1: fn, fn2: fn, fn3: fn, fn4: fn, fn5: fn });186 fn1(() => {187 fn2(() => fn3());188 fn4();189 });190 fn5();191 expect(callSpy).toHaveBeenCalledWith(192 expect.objectContaining({ id: 'kind--story [0] fn1', parentId: undefined })193 );194 expect(callSpy).toHaveBeenCalledWith(195 expect.objectContaining({196 id: 'kind--story [0] fn1 [0] fn2',197 parentId: 'kind--story [0] fn1',198 })199 );200 expect(callSpy).toHaveBeenCalledWith(201 expect.objectContaining({202 id: 'kind--story [0] fn1 [0] fn2 [0] fn3',203 parentId: 'kind--story [0] fn1 [0] fn2',204 })205 );206 expect(callSpy).toHaveBeenCalledWith(207 expect.objectContaining({208 id: 'kind--story [0] fn1 [1] fn4',209 parentId: 'kind--story [0] fn1',210 })211 );212 expect(callSpy).toHaveBeenCalledWith(213 expect.objectContaining({ id: 'kind--story [1] fn5', parentId: undefined })214 );215 });216 it('tracks the parent call id for async callbacks', async () => {217 const fn = (callback?: Function) => Promise.resolve(callback && callback());218 const { fn1, fn2, fn3 } = instrument({ fn1: fn, fn2: fn, fn3: fn });219 await fn1(() => fn2());220 await fn3();221 expect(callSpy).toHaveBeenCalledWith(222 expect.objectContaining({ id: 'kind--story [0] fn1', parentId: undefined })223 );224 expect(callSpy).toHaveBeenCalledWith(225 expect.objectContaining({226 id: 'kind--story [0] fn1 [0] fn2',227 parentId: 'kind--story [0] fn1',228 })229 );230 expect(callSpy).toHaveBeenCalledWith(231 expect.objectContaining({ id: 'kind--story [1] fn3', parentId: undefined })232 );233 });234 it('instruments the call result to support chaining', () => {235 const { fn1 } = instrument({236 fn1: () => ({237 fn2: () => {},238 }),239 });240 fn1().fn2();241 expect(callSpy).toHaveBeenLastCalledWith(242 expect.objectContaining({243 method: 'fn2',244 path: [{ __callId__: callSpy.mock.calls[0][0].id }],245 })246 );247 });248 it('emits a "sync" event with debounce after a patched function is invoked', () => {249 const { fn } = instrument({ fn: (...args: any) => {} }, { intercept: true });250 jest.useFakeTimers();251 syncSpy.mockClear();252 fn('foo');253 fn('bar');254 jest.runAllTimers();255 expect(syncSpy).toHaveBeenCalledTimes(1);256 });257 it('sends a folded log with the "sync" event', () => {258 const { fn } = instrument({ fn: (...args: any) => ({ fn2: () => {} }) }, { intercept: true });259 jest.useFakeTimers();260 fn('foo', fn('bar')).fn2();261 fn('baz');262 jest.runAllTimers();263 expect(syncSpy).toHaveBeenCalledWith(264 expect.objectContaining({265 logItems: [266 { callId: 'kind--story [2] fn2', status: 'done' },267 { callId: 'kind--story [3] fn', status: 'done' },268 ],269 })270 );271 });272 it('catches thrown errors and returns the error', () => {273 const { fn } = instrument({274 fn: () => {275 throw new Error('Boom!');276 },277 });278 expect(fn()).toEqual(new Error('Boom!'));279 expect(() => setRenderPhase('played')).toThrow(new Error('Boom!'));280 });281 it('forwards nested exceptions', () => {282 const { fn1, fn2 } = instrument({283 fn1: (...args: any) => {}, // doesn't forward args284 fn2: () => {285 throw new Error('Boom!');286 },287 });288 expect(fn1(fn2())).toEqual(new Error('Boom!'));289 expect(() => setRenderPhase('played')).toThrow(new Error('Boom!'));290 });291 it("re-throws anything that isn't an error", () => {292 const { fn } = instrument({293 fn: () => {294 throw 'Boom!'; // eslint-disable-line no-throw-literal295 },296 });297 expect(fn).toThrow('Boom!');298 expect(callSpy).not.toHaveBeenCalled();299 });300 it('does not affect intercepted methods', () => {301 const { fn } = instrument({ fn: jest.fn() }, { intercept: true });302 fn('foo');303 expect(fn.__originalFn__).toHaveBeenCalledWith('foo');304 });305 it('clears state when switching stories', () => {306 addons.getChannel().emit(SET_CURRENT_STORY); // initialization307 instrumenter.state = {308 'kind--story': {309 isDebugging: false,310 cursor: 123,311 calls: [{ id: 'kind--story [0] fn' }],312 shadowCalls: [{ id: 'kind--story [0] fn' }, { id: 'kind--story [1] fn' }],313 callRefsByResult: new Map([[{}, 'ref']]),314 chainedCallIds: new Set(['kind--story [0] fn']),315 parentCall: { id: 'kind--story [0] fn' },316 playUntil: 'kind--story [1] fn',317 resolvers: { ref: () => {} },318 syncTimeout: 123,319 forwardedException: new Error('Oops'),320 },321 } as any;322 addons.getChannel().emit(SET_CURRENT_STORY);323 expect(instrumenter.state).toStrictEqual({});324 });325 describe('with intercept: true', () => {326 const options = { intercept: true };327 it('emits a call event with error data when the function throws', () => {328 const { fn } = instrument(329 {330 fn: () => {331 throw new Error('Boom!');332 },333 },334 options335 );336 expect(fn).toThrow();337 expect(callSpy).toHaveBeenCalledWith(338 expect.objectContaining({339 id: 'kind--story [0] fn',340 exception: {341 name: 'Error',342 message: 'Boom!',343 stack: expect.stringContaining('Error: Boom!'),344 },345 })346 );347 });348 it('catches thrown errors and throws an ignoredException instead', () => {349 const { fn } = instrument(350 {351 fn: () => {352 throw new Error('Boom!');353 },354 },355 options356 );357 expect(fn).toThrow('ignoredException');358 });359 it('catches forwarded exceptions and throws an ignoredException instead', () => {360 const { fn1, fn2 } = instrument(361 {362 fn1: (_: any) => {},363 fn2: () => {364 throw new Error('Boom!');365 },366 },367 options368 );369 expect(() => fn1(fn2())).toThrow('ignoredException');370 });371 });372 describe('while debugging', () => {373 afterEach(() => {374 addons.getChannel().emit(EVENTS.END, { storyId });375 });376 it('remounts on the "start" event', async () => {377 addons.getChannel().emit(EVENTS.START, { storyId });378 expect(forceRemountSpy).toHaveBeenCalled();379 });380 it('defers calls to intercepted functions', () => {381 const { fn } = instrument({ fn: jest.fn() }, { intercept: true });382 addons.getChannel().emit(EVENTS.START, { storyId });383 expect(fn()).toEqual(expect.any(Promise));384 expect(fn.__originalFn__).not.toHaveBeenCalled();385 });386 it('does not defer calls to non-intercepted functions', () => {387 const { fn } = instrument({ fn: jest.fn(() => 'ok') });388 addons.getChannel().emit(EVENTS.START, { storyId });389 expect(fn()).toBe('ok');390 expect(fn.__originalFn__).toHaveBeenCalled();391 });392 it('does not defer calls to intercepted functions that are chained upon', () => {393 const { fn1 } = instrument(394 { fn1: jest.fn(() => ({ fn2: jest.fn() as any })) },395 { intercept: true }396 );397 fn1().fn2();398 addons.getChannel().emit(EVENTS.START, { storyId });399 const res1 = fn1();400 expect(res1.fn2()).toEqual(expect.any(Promise));401 expect(fn1.__originalFn__).toHaveBeenCalledTimes(2);402 expect(res1.fn2.__originalFn__).not.toHaveBeenCalled();403 });404 it.skip('starts debugging at the first non-nested interceptable call', () => {405 const { fn } = instrument({ fn: jest.fn((...args: any) => args) }, { intercept: true });406 fn(fn(), fn()); // setup the dependencies407 addons.getChannel().emit(EVENTS.START, { storyId });408 const a = fn('a');409 const b = fn('b');410 const c = fn(a, b);411 expect(a).toEqual(['a']);412 expect(b).toEqual(['b']);413 expect(c).toEqual(expect.any(Promise));414 });415 it('steps through each interceptable function on "next"', async () => {416 const fn = jest.fn();417 const { fn: instrumentedFn } = instrument({ fn }, { intercept: true });418 const mockedInstrumentedFn = jest.fn(instrumentedFn);419 const play = async () => {420 await mockedInstrumentedFn();421 await mockedInstrumentedFn();422 await mockedInstrumentedFn();423 };424 await play();425 fn.mockClear();426 mockedInstrumentedFn.mockClear();427 addons.getChannel().emit(EVENTS.START, { storyId });428 const p = play();429 expect(mockedInstrumentedFn).toHaveBeenCalledTimes(1);430 expect(fn).toHaveBeenCalledTimes(0);431 addons.getChannel().emit(EVENTS.NEXT, { storyId });432 await new Promise((resolve) => setTimeout(resolve, 0));433 expect(mockedInstrumentedFn).toHaveBeenCalledTimes(2);434 expect(fn).toHaveBeenCalledTimes(1);435 addons.getChannel().emit(EVENTS.END, { storyId });436 await new Promise((resolve) => setTimeout(resolve, 0));437 expect(mockedInstrumentedFn).toHaveBeenCalledTimes(3);438 expect(fn).toHaveBeenCalledTimes(3);439 await p;440 });441 });...
Using AI Code Generation
1import { forceRemountSpy } from 'storybook-root-cause';2forceRemountSpy();3forceRemountSpy();4forceRemountSpy();5forceRemountSpy();6forceRemountSpy();7forceRemountSpy();8forceRemountSpy();9forceRemountSpy();10forceRemountSpy();11forceRemountSpy();12forceRemountSpy();13forceRemountSpy();14forceRemountSpy();15forceRemountSpy();16forceRemountSpy();17forceRemountSpy();18forceRemountSpy();19forceRemountSpy();20forceRemountSpy();21forceRemountSpy();22forceRemountSpy();23forceRemountSpy();24forceRemountSpy();
Using AI Code Generation
1import { forceRemountSpy } from 'storybook-root-provider';2import { forceRemountSpy } from 'storybook-root-provider';3import { forceRemountSpy } from 'storybook-root-provider';4import { forceRemountSpy } from 'storybook-root-provider';5import { forceRemountSpy } from 'storybook-root-provider';6import { forceRemountSpy } from 'storybook-root-provider';7import { forceRemountSpy } from 'storybook-root-provider';8import { forceRemountSpy } from 'storybook-root-provider';9import { forceRemountSpy } from 'storybook-root-provider';10import { forceRemountSpy } from 'storybook-root-provider';11import { forceRemountSpy } from
Using AI Code Generation
1import { forceRemountSpy } from 'storybook-root-provider';2forceRemountSpy();3forceRemount();4import { forceRemountSpy } from 'storybook-root-provider';5forceRemountSpy();6forceRemount();7import { forceRemountSpy } from 'storybook-root-provider';8forceRemountSpy();9forceRemount();10import { forceRemountSpy } from 'storybook-root-provider';11forceRemountSpy();12forceRemount();13import { forceRemountSpy } from 'storybook-root-provider';14forceRemountSpy();15forceRemount();16import { forceRemountSpy } from 'storybook-root-provider';17forceRemountSpy();18forceRemount();19import { forceRemountSpy } from 'storybook-root-provider';20forceRemountSpy();21forceRemount();22import
Using AI Code Generation
1import { render } from '@testing-library/react';2import { forceRemountSpy } from 'storybook-root-cause';3import { Button } from './button';4describe('Button', () => {5 it('should render successfully', () => {6 const { baseElement } = render(<Button />);7 expect(baseElement).toBeTruthy();8 });9 it('should render successfully', () => {10 const { baseElement } = render(<Button />);11 expect(baseElement).toBeTruthy();12 });13});
Using AI Code Generation
1import {forceRemountSpy} from 'storybook-root-provider';2forceRemountSpy();3import {forceRemountSpy} from 'storybook-root-provider';4forceRemountSpy();5import {forceRemountSpy} from 'storybook-root-provider';6forceRemountSpy();7import {forceRemountSpy} from 'storybook-root-provider';8forceRemountSpy();9import {forceRemountSpy} from 'storybook-root-provider';10forceRemountSpy();11import {forceRemountSpy} from 'storybook-root-provider';12forceRemountSpy();13import {forceRemountSpy} from 'storybook-root-provider';14forceRemountSpy();15import {forceRemountSpy} from 'storybook-root-provider';16forceRemountSpy();17import {forceRemountSpy} from 'storybook-root-provider';18forceRemountSpy();19import {forceRemountSpy} from 'storybook-root-provider
Using AI Code Generation
1import { forceRemountSpy } from 'storybook-root-cause';2import { render } from '@testing-library/react';3import App from './App';4describe('App', () => {5 it('should render', () => {6 const { container } = render(<App />);7 expect(container).toBeTruthy();8 });9 it('should force remount', () => {10 forceRemountSpy();11 });12});13import React from 'react';14import { useForceRemount } from 'storybook-root-cause';15export default function App() {16 useForceRemount();17 return <div>App</div>;18}19import { addDecorator, addParameters } from '@storybook/react';20import { withRootCause } from 'storybook-root-cause';21addDecorator(withRootCause);22addParameters({23 rootCause: {24 },25});26module.exports = {27 stories: ['../src/**/*.stories.@(js|jsx|ts|tsx)'],28};29{30 "dependencies": {31 },32 "scripts": {33 },34 "eslintConfig": {35 },36 "browserslist": {
Using AI Code Generation
1import { forceRemountSpy } from 'storybook-root'2forceRemountSpy()3import { forceRemountSpy } from 'storybook-root'4describe('MyComponent', () => {5 it('should render', () => {6 const { getByText } = render(<MyComponent />)7 expect(getByText('Some text')).toBeInTheDocument()8 })9 it('should render something else', () => {10 forceRemountSpy()11 const { getByText } = render(<MyComponent />)12 expect(getByText('Some other text')).toBeInTheDocument()13 })14})15import { forceRemountSpy } from 'storybook-root'16afterEach(() => {17 forceRemountSpy()18})19describe('MyComponent', () => {20 it('should render', () => {21 const { getByText } = render(<MyComponent />)22 expect(getByText('Some text')).toBeInTheDocument()23 })24 it('should render something else', () => {25 const { getByText } = render(<MyComponent />)26 expect(getByText('Some other text')).toBeInTheDocument()27 })28})29import { forceRemountSpy } from 'storybook-root'30afterEach(() => {31 forceRemountSpy()32})33module.exports = {34 globals: {35 'ts-jest': {36 },37 },38}
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!!