react-dom#unmountComponentAtNode TypeScript Examples

The following examples show how to use react-dom#unmountComponentAtNode. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: usePortal.tsx    From react-amap with MIT License 6 votes vote down vote up
usePortal = () => {
  const [container] = React.useState<HTMLDivElement>(() => {
    const el = document.createElement('div');
    return el;
  });
  const [portal, setPortal] = useState<State>({
    render: () => null,
    remove: () => null,
  });

  const ReactCreatePortal = React.useCallback<(elmm: HTMLDivElement) => State>((elmm) => {
    const Portal: State['render'] = ({ children }) => {
      if (!children) return null;
      return createPortal(children, elmm);
    };
    const remove: State['remove'] = (elm) => {
      elm && unmountComponentAtNode(elm);
    };
    return { render: Portal, remove };
  }, []);

  useEffect(() => {
    if (container) portal.remove();
    const newPortal = ReactCreatePortal(container);
    setPortal(newPortal);
    return () => {
      newPortal.remove(container);
    };
  }, [container]);

  return { Portal: portal.render, container };
}
Example #2
Source File: cohort_vis_renderer.tsx    From kibana_cohort with Apache License 2.0 6 votes vote down vote up
selfChangingVisRenderer: () => ExpressionRenderDefinition<CohortVisRenderValue> = () => ({
  name: 'cohort_vis',
  reuseDomNode: true,
  render: async (domNode, config, handlers) => {
    handlers.onDestroy(() => {
      unmountComponentAtNode(domNode);
    });

    render(<CohortComponent renderComplete={handlers.done} {...config} />, domNode);
  },
})
Example #3
Source File: InfoWindow.tsx    From react-bmapgl with MIT License 6 votes vote down vote up
destroy() {
        if (this.content) {
            unmountComponentAtNode(this.content);
        }
        if (this.infoWindow) {
            this.map.closeInfoWindow();
            // @ts-ignore
            this.instance = this.infoWindow = undefined;
        }
    }
Example #4
Source File: index.test.tsx    From erda-ui with GNU Affero General Public License v3.0 6 votes vote down vote up
describe('BackToTop', () => {
  Element.prototype.scrollTo = function (opt?: ScrollToOptions | number) {
    if (typeof opt !== 'number') {
      const { top, left } = opt as ScrollToOptions;
      this.scrollTop = top || 0;
      this.scrollLeft = left || 0;
    }
  };
  it('should back to top', async () => {
    document.body.innerHTML = '<div id="main" style="height: 400px; overflow-y: auto"></div>';
    const div = document.getElementById('main') as HTMLDivElement;
    const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((x: number, y: number) => {
      div.scrollLeft = x;
      div.scrollTop = y;
    });
    act(() => {
      render(
        <div id="child" style={{ height: '1000px' }}>
          <BackToTop />
        </div>,
        div,
      );
    });
    window.scrollTo(0, 500);
    expect(div.scrollTop).toBe(500);
    act(() => {
      div.dispatchEvent(new Event('scroll'));
    });
    const button = document.querySelector('.scroll-top-btn') as Element;
    act(() => {
      button.dispatchEvent(new MouseEvent('click', { bubbles: true }));
    });
    expect(div.scrollTop).toBe(0);
    scrollToSpy.mockRestore();
    unmountComponentAtNode(div);
  });
});
Example #5
Source File: ImageExportDialog.tsx    From excalidraw with MIT License 6 votes vote down vote up
renderPreview = (
  content: HTMLCanvasElement | Error,
  previewNode: HTMLDivElement,
) => {
  unmountComponentAtNode(previewNode);
  previewNode.innerHTML = "";
  if (content instanceof HTMLCanvasElement) {
    previewNode.appendChild(content);
  } else {
    render(<ErrorCanvasPreview />, previewNode);
  }
}
Example #6
Source File: BuildingHeader.test.tsx    From zapquaker with MIT License 5 votes vote down vote up
afterEach(() => {
  // cleanup on exiting
  unmountComponentAtNode(container);
  container.remove();
});
Example #7
Source File: index.test.tsx    From react-canvas-confetti with MIT License 5 votes vote down vote up
describe('ReactCanvasConfetti', () => {
  beforeEach(() => {
    container = document.createElement('div');
    document.body.appendChild(container);
  });

  afterEach(() => {
    unmountComponentAtNode(container);
    container.remove();
    createSpy.mockClear();
  });

  test('Should create canvas dom node', () => {
    render(<ReactCanvasConfetti {...props} />, container);

    const canvas = container.querySelector('canvas');

    expect(canvas).not.toBe(null);
    expect(canvas!.height).toEqual(props.height);
    expect(canvas!.width).toEqual(props.width);
    expect(canvas!.style.opacity).toEqual(props.style.opacity);
    expect(canvas!.classList.contains(props.className)).toBeTruthy();
  });

  test('Should set up ref to confetti instance', () => {
    expect(createSpy).toHaveBeenCalledTimes(0);

    render(<ReactCanvasConfetti refConfetti={setRefConfetti} />, container);

    expect(createSpy).toHaveBeenCalledTimes(1);
    expect(typeof refConfetti === 'function').toBeTruthy();
    expect(typeof refConfetti!.reset === 'function').toBeTruthy();
  });

  test('Should be called with correct arguments', () => {
    expect(createSpy).toHaveBeenCalledTimes(0);

    render(<ReactCanvasConfetti refConfetti={setRefConfetti} />, container);
    render(<ReactCanvasConfetti {...props} fire={{}} angle={45} />, container);
    render(<ReactCanvasConfetti {...props} fire={{}} angle={45} spread={180} />, container);
    render(<ReactCanvasConfetti {...props} reset={{}} />, container);

    const refConfettiMock = ((refConfetti as unknown) as jest.Mock).mock;

    expect(refConfetti).toHaveBeenCalledTimes(2);
    expect(refConfettiMock.calls[0][0]).toEqual({ angle: 45 });
    expect(refConfettiMock.calls[1][0]).toEqual({ angle: 45, spread: 180 });
    expect(refConfetti!.reset).toHaveBeenCalledTimes(1);
    expect(props.onFire).toHaveBeenCalledTimes(2);
    expect(props.onReset).toHaveBeenCalledTimes(1);
    expect(createSpy).toHaveBeenCalledTimes(1);
  });
});
Example #8
Source File: form.spec.tsx    From react-bare-forms with MIT License 5 votes vote down vote up
afterEach(() => {
   unmountComponentAtNode(container);
   container.remove();
   container = null;
});
Example #9
Source File: FileField.spec.tsx    From react-bare-forms with MIT License 5 votes vote down vote up
afterEach(() => {
    // cleanup on exiting
    unmountComponentAtNode(container);
    container.remove();
    container = null;
});
Example #10
Source File: CustomOverlayDom.ts    From react-bmapgl with MIT License 5 votes vote down vote up
CustomOverlayDom.prototype.destroy = function(){
    unmountComponentAtNode(this._div);
}
Example #11
Source File: ContextMenu.tsx    From excalidraw-embed with MIT License 5 votes vote down vote up
handleClose = () => {
  unmountComponentAtNode(getContextMenuNode());
}
Example #12
Source File: ContextMenu.tsx    From excalidraw with MIT License 5 votes vote down vote up
handleClose = (container: HTMLElement) => {
  const contextMenuNode = contextMenuNodeByContainer.get(container);
  if (contextMenuNode) {
    unmountComponentAtNode(contextMenuNode);
    contextMenuNode.remove();
    contextMenuNodeByContainer.delete(container);
  }
}
Example #13
Source File: index.tsx    From reskript with MIT License 5 votes vote down vote up
unmount = async () => {
    unmountComponentAtNode(document.getElementById('root')!);
}
Example #14
Source File: ens.test.tsx    From safe-airdrop with MIT License 5 votes vote down vote up
afterEach(() => {
  jest.clearAllMocks();
  if (container) {
    unmountComponentAtNode(container);
    container.remove();
    container = null;
  }
});
Example #15
Source File: CrystalBall.test.tsx    From ds2020_mauricio with GNU General Public License v3.0 5 votes vote down vote up
afterEach(() => {
  // cleanup on exiting
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});
Example #16
Source File: App.test.tsx    From ds2020_mauricio with GNU General Public License v3.0 5 votes vote down vote up
afterEach(() => {
  // cleanup on exiting
  unmountComponentAtNode(container);
  container.remove();
  container = null;
});
Example #17
Source File: LevelRange.test.tsx    From zapquaker with MIT License 5 votes vote down vote up
afterEach(() => {
  // cleanup on exiting
  unmountComponentAtNode(container);
  container.remove();
});
Example #18
Source File: BuildingCard.test.tsx    From zapquaker with MIT License 5 votes vote down vote up
afterEach(() => {
  // cleanup on exiting
  unmountComponentAtNode(container);
  container.remove();
});
Example #19
Source File: WalletProvider-test.tsx    From wallet-adapter with Apache License 2.0 4 votes vote down vote up
describe('WalletProvider', () => {
    let container: HTMLDivElement | null;
    let ref: React.RefObject<TestRefType>;
    let fooWalletAdapter: MockWalletAdapter;
    let barWalletAdapter: MockWalletAdapter;
    let bazWalletAdapter: MockWalletAdapter;
    let adapters: Adapter[];

    function renderTest(props: Omit<WalletProviderProps, 'children' | 'wallets'>) {
        act(() => {
            render(
                <WalletProvider {...props} wallets={adapters}>
                    <TestComponent ref={ref} />
                </WalletProvider>,
                container
            );
        });
    }

    abstract class MockWalletAdapter extends BaseWalletAdapter {
        connectionPromise: null | Promise<void> = null;
        disconnectionPromise: null | Promise<void> = null;
        connectedValue = false;
        get connected() {
            return this.connectedValue;
        }
        readyStateValue: WalletReadyState = WalletReadyState.Installed;
        get readyState() {
            return this.readyStateValue;
        }
        connecting = false;
        connect = jest.fn(async () => {
            this.connecting = true;
            if (this.connectionPromise) {
                await this.connectionPromise;
            }
            this.connecting = false;
            this.connectedValue = true;
            this.emit('connect', this.publicKey!);
        });
        disconnect = jest.fn(async () => {
            this.connecting = false;
            if (this.disconnectionPromise) {
                await this.disconnectionPromise;
            }
            this.connectedValue = false;
            this.emit('disconnect');
        });
        sendTransaction = jest.fn();
    }
    class FooWalletAdapter extends MockWalletAdapter {
        name = 'FooWallet' as WalletName<'FooWallet'>;
        url = 'https://foowallet.com';
        icon = 'foo.png';
        publicKey = new PublicKey('Foo11111111111111111111111111111111111111111');
    }
    class BarWalletAdapter extends MockWalletAdapter {
        name = 'BarWallet' as WalletName<'BarWallet'>;
        url = 'https://barwallet.com';
        icon = 'bar.png';
        publicKey = new PublicKey('Bar11111111111111111111111111111111111111111');
    }
    class BazWalletAdapter extends MockWalletAdapter {
        name = 'BazWallet' as WalletName<'BazWallet'>;
        url = 'https://bazwallet.com';
        icon = 'baz.png';
        publicKey = new PublicKey('Baz11111111111111111111111111111111111111111');
    }

    beforeEach(() => {
        localStorage.clear();
        jest.resetAllMocks();
        container = document.createElement('div');
        document.body.appendChild(container);
        ref = createRef();
        fooWalletAdapter = new FooWalletAdapter();
        barWalletAdapter = new BarWalletAdapter();
        bazWalletAdapter = new BazWalletAdapter();
        adapters = [fooWalletAdapter, barWalletAdapter, bazWalletAdapter];
    });
    afterEach(() => {
        if (container) {
            unmountComponentAtNode(container);
            container.remove();
            container = null;
        }
    });
    describe('given a selected wallet', () => {
        beforeEach(async () => {
            fooWalletAdapter.readyStateValue = WalletReadyState.NotDetected;
            renderTest({});
            await act(async () => {
                ref.current?.getWalletContextState().select('FooWallet' as WalletName<'FooWallet'>);
                await Promise.resolve(); // Flush all promises in effects after calling `select()`.
            });
            expect(ref.current?.getWalletContextState().wallet?.readyState).toBe(WalletReadyState.NotDetected);
        });
        describe('that then becomes ready', () => {
            beforeEach(() => {
                act(() => {
                    fooWalletAdapter.readyStateValue = WalletReadyState.Installed;
                    fooWalletAdapter.emit('readyStateChange', WalletReadyState.Installed);
                });
            });
            it('sets `ready` to true', () => {
                expect(ref.current?.getWalletContextState().wallet?.readyState).toBe(WalletReadyState.Installed);
            });
        });
        describe('when the wallet disconnects of its own accord', () => {
            beforeEach(() => {
                act(() => {
                    fooWalletAdapter.disconnect();
                });
            });
            it('should clear the stored wallet name', () => {
                expect(localStorage.removeItem).toHaveBeenCalled();
            });
            it('updates state tracking variables appropriately', () => {
                expect(ref.current?.getWalletContextState()).toMatchObject({
                    wallet: null,
                    connected: false,
                    connecting: false,
                    publicKey: null,
                });
            });
        });
        describe('when the wallet disconnects as a consequence of the window unloading', () => {
            beforeEach(() => {
                act(() => {
                    window.dispatchEvent(new Event('beforeunload'));
                    fooWalletAdapter.disconnect();
                });
            });
            it('should not clear the stored wallet name', () => {
                expect(localStorage.removeItem).not.toHaveBeenCalled();
            });
        });
    });
    describe('when there exists no stored wallet name', () => {
        beforeEach(() => {
            (localStorage.getItem as jest.Mock).mockReturnValue(null);
        });
        it('loads no wallet into state', () => {
            renderTest({});
            expect(ref.current?.getWalletContextState().wallet).toBeNull();
        });
        it('loads no public key into state', () => {
            renderTest({});
            expect(ref.current?.getWalletContextState().publicKey).toBeNull();
        });
    });
    describe('when there exists a stored wallet name', () => {
        beforeEach(() => {
            (localStorage.getItem as jest.Mock).mockReturnValue(JSON.stringify('FooWallet'));
        });
        it('loads the corresponding adapter into state', () => {
            renderTest({});
            expect(ref.current?.getWalletContextState().wallet?.adapter).toBeInstanceOf(FooWalletAdapter);
        });
        it('loads the corresponding public key into state', () => {
            renderTest({});
            expect(ref.current?.getWalletContextState().publicKey).toBe(fooWalletAdapter.publicKey);
        });
        it('sets state tracking variables to defaults', () => {
            renderTest({});
            expect(ref.current?.getWalletContextState()).toMatchObject({
                connected: false,
                connecting: false,
            });
        });
        describe('and auto connect is disabled', () => {
            const props = { autoConnect: false };
            beforeEach(() => {
                renderTest(props);
            });
            it('`autoConnect` is `false` on state', () => {
                expect(ref.current?.getWalletContextState().autoConnect).toBe(false);
            });
            it('does not call `connect` on the adapter', () => {
                expect(fooWalletAdapter.connect).not.toHaveBeenCalled();
            });
        });
        describe('and auto connect is enabled', () => {
            const props = { autoConnect: true };
            beforeEach(() => {
                fooWalletAdapter.readyStateValue = WalletReadyState.NotDetected;
                renderTest(props);
            });
            it('`autoConnect` is `true` on state', () => {
                expect(ref.current?.getWalletContextState().autoConnect).toBe(true);
            });
            describe('before the adapter is ready', () => {
                it('does not call `connect` on the adapter', () => {
                    expect(fooWalletAdapter.connect).not.toHaveBeenCalled();
                });
                describe('once the adapter becomes ready', () => {
                    beforeEach(async () => {
                        await act(async () => {
                            fooWalletAdapter.readyStateValue = WalletReadyState.Installed;
                            fooWalletAdapter.emit('readyStateChange', WalletReadyState.Installed);
                            await Promise.resolve(); // Flush all promises in effects after calling `select()`.
                        });
                    });
                    it('calls `connect` on the adapter', () => {
                        expect(fooWalletAdapter.connect).toHaveBeenCalledTimes(1);
                    });
                });
            });
        });
    });
    describe('custom error handler', () => {
        const errorToEmit = new WalletError();
        let handleError: (error: WalletError) => void;
        beforeEach(async () => {
            handleError = jest.fn();
            renderTest({ onError: handleError });
            await act(async () => {
                ref.current?.getWalletContextState().select('FooWallet' as WalletName<'FooWallet'>);
                await Promise.resolve(); // Flush all promises in effects after calling `select()`.
            });
        });
        it('gets called in response to adapter errors', () => {
            act(() => {
                fooWalletAdapter.emit('error', errorToEmit);
            });
            expect(handleError).toBeCalledWith(errorToEmit);
        });
        it('does not get called if the window is unloading', () => {
            const errorToEmit = new WalletError();
            act(() => {
                window.dispatchEvent(new Event('beforeunload'));
                fooWalletAdapter.emit('error', errorToEmit);
            });
            expect(handleError).not.toBeCalled();
        });
    });
    describe('connect()', () => {
        describe('given a wallet that is not ready', () => {
            beforeEach(async () => {
                window.open = jest.fn();
                fooWalletAdapter.readyStateValue = WalletReadyState.NotDetected;
                renderTest({});
                act(() => {
                    ref.current?.getWalletContextState().select('FooWallet' as WalletName<'FooWallet'>);
                });
                expect(ref.current?.getWalletContextState().wallet?.readyState).toBe(WalletReadyState.NotDetected);
                act(() => {
                    expect(ref.current?.getWalletContextState().connect).rejects.toThrow();
                });
            });
            it('clears out the state', () => {
                expect(ref.current?.getWalletContextState()).toMatchObject({
                    wallet: null,
                    connected: false,
                    connecting: false,
                    publicKey: null,
                });
            });
            it("opens the wallet's URL in a new window", () => {
                expect(window.open).toBeCalledWith('https://foowallet.com', '_blank');
            });
            it('throws a `WalletNotReady` error', () => {
                act(() => {
                    expect(ref.current?.getWalletContextState().connect()).rejects.toThrow(new WalletNotReadyError());
                });
            });
        });
        describe('given a wallet that is ready', () => {
            let commitConnection: () => void;
            beforeEach(async () => {
                renderTest({});
                await act(async () => {
                    ref.current?.getWalletContextState().select('FooWallet' as WalletName<'FooWallet'>);
                    await Promise.resolve(); // Flush all promises in effects after calling `select()`.
                });
                fooWalletAdapter.connectionPromise = new Promise<void>((resolve) => {
                    commitConnection = resolve;
                });
                act(() => {
                    ref.current?.getWalletContextState().connect();
                });
            });
            it('calls connect on the adapter', () => {
                expect(fooWalletAdapter.connect).toHaveBeenCalled();
            });
            it('updates state tracking variables appropriately', () => {
                expect(ref.current?.getWalletContextState()).toMatchObject({
                    connected: false,
                    connecting: true,
                });
            });
            describe('once connected', () => {
                beforeEach(() => {
                    act(() => {
                        commitConnection();
                    });
                });
                it('updates state tracking variables appropriately', () => {
                    expect(ref.current?.getWalletContextState()).toMatchObject({
                        connected: true,
                        connecting: false,
                    });
                });
            });
        });
    });
    describe('disconnect()', () => {
        describe('when there is already a wallet connected', () => {
            let commitDisconnection: () => void;
            beforeEach(async () => {
                window.open = jest.fn();
                renderTest({});
                await act(async () => {
                    ref.current?.getWalletContextState().select('FooWallet' as WalletName<'FooWallet'>);
                    await Promise.resolve(); // Flush all promises in effects after calling `select()`.
                });
                act(() => {
                    ref.current?.getWalletContextState().connect();
                });
                fooWalletAdapter.disconnectionPromise = new Promise<void>((resolve) => {
                    commitDisconnection = resolve;
                });
                act(() => {
                    ref.current?.getWalletContextState().disconnect();
                });
            });
            it('updates state tracking variables appropriately', () => {
                expect(ref.current?.getWalletContextState()).toMatchObject({
                    connected: true,
                });
            });
            describe('once disconnected', () => {
                beforeEach(() => {
                    act(() => {
                        commitDisconnection();
                    });
                });
                it('should clear the stored wallet name', () => {
                    expect(localStorage.removeItem).toHaveBeenCalled();
                });
                it('clears out the state', () => {
                    expect(ref.current?.getWalletContextState()).toMatchObject({
                        wallet: null,
                        connected: false,
                        connecting: false,
                        publicKey: null,
                    });
                });
            });
        });
    });
    describe('select()', () => {
        describe('when there is no wallet connected', () => {
            describe('and you select a wallet', () => {
                beforeEach(async () => {
                    renderTest({});
                    await act(async () => {
                        ref.current?.getWalletContextState().select('FooWallet' as WalletName<'FooWallet'>);
                        await Promise.resolve(); // Flush all promises in effects after calling `select()`.
                    });
                });
                it('sets the state tracking variables', () => {
                    expect(ref.current?.getWalletContextState()).toMatchObject({
                        wallet: { adapter: fooWalletAdapter, readyState: fooWalletAdapter.readyState },
                        connected: false,
                        connecting: false,
                        publicKey: fooWalletAdapter.publicKey,
                    });
                });
            });
        });
        describe('when there is already a wallet selected', () => {
            let commitFooWalletDisconnection: () => void;
            beforeEach(async () => {
                fooWalletAdapter.disconnectionPromise = new Promise<void>((resolve) => {
                    commitFooWalletDisconnection = resolve;
                });
                renderTest({});
                await act(async () => {
                    ref.current?.getWalletContextState().select('FooWallet' as WalletName<'FooWallet'>);
                    await Promise.resolve(); // Flush all promises in effects after calling `select()`.
                });
            });
            describe('and you select a different wallet', () => {
                beforeEach(async () => {
                    await act(async () => {
                        ref.current?.getWalletContextState().select('BarWallet' as WalletName<'BarWallet'>);
                        await Promise.resolve(); // Flush all promises in effects after calling `select()`.
                    });
                });
                it('should disconnect the old wallet', () => {
                    expect(fooWalletAdapter.disconnect).toHaveBeenCalled();
                });
                it('the adapter of the new wallet should be set in state', () => {
                    expect(ref.current?.getWalletContextState().wallet?.adapter).toBe(barWalletAdapter);
                });
                /**
                 * Regression test: a race condition in the wallet name setter could result in the
                 * wallet reverting back to an old value, depending on the cadence of the previous
                 * wallets' disconnect operation.
                 */
                describe('then change your mind before the first one has disconnected', () => {
                    beforeEach(async () => {
                        await act(async () => {
                            ref.current?.getWalletContextState().select('BazWallet' as WalletName<'BazWallet'>);
                            await Promise.resolve(); // Flush all promises in effects after calling `select()`.
                        });
                        act(() => {
                            commitFooWalletDisconnection();
                        });
                    });
                    it('the wallet you selected last should be set in state', () => {
                        expect(ref.current?.getWalletContextState().wallet?.adapter).toBe(bazWalletAdapter);
                    });
                });
            });
        });
    });
});
Example #20
Source File: useLocalStorage-test.tsx    From wallet-adapter with Apache License 2.0 4 votes vote down vote up
describe('useLocalStorage', () => {
    let container: HTMLDivElement | null;
    let ref: React.RefObject<TestRefType>;
    function renderTest() {
        act(() => {
            render(<TestComponent ref={ref} />, container);
        });
    }
    beforeEach(() => {
        localStorage.clear();
        jest.resetAllMocks();
        container = document.createElement('div');
        document.body.appendChild(container);
        ref = createRef();
    });
    afterEach(() => {
        if (container) {
            unmountComponentAtNode(container);
            container.remove();
            container = null;
        }
    });
    describe('getting the persisted value', () => {
        describe('when local storage has a value for the storage key', () => {
            const PERSISTED_VALUE = 'value';
            beforeEach(() => {
                (localStorage.getItem as jest.Mock).mockImplementation((storageKey) => {
                    if (storageKey !== STORAGE_KEY) {
                        return null;
                    }
                    return JSON.stringify(PERSISTED_VALUE);
                });
                expect(renderTest).not.toThrow();
            });
            it('returns that value', () => {
                expect(ref.current?.getPersistedValue()).toBe(PERSISTED_VALUE);
            });
        });
        describe('when local storage has no value for the storage key', () => {
            const PERSISTED_VALUE = 'value';
            beforeEach(() => {
                (localStorage.getItem as jest.Mock).mockReturnValue(null);
                expect(renderTest).not.toThrow();
            });
            it('returns the default value', () => {
                expect(ref.current?.getPersistedValue()).toBe(DEFAULT_VALUE);
            });
        });
        describe('when merely accessing local storage results in a fatal error', () => {
            let restoreOldLocalStorage: () => void;
            beforeEach(() => {
                restoreOldLocalStorage = configureLocalStorageToFatalOnAccess();
                expect(renderTest).not.toThrow();
            });
            afterEach(() => {
                restoreOldLocalStorage();
            });
            it('renders with the default value', () => {
                expect(ref.current?.getPersistedValue()).toBe(DEFAULT_VALUE);
            });
        });
        describe('when local storage fatals on read', () => {
            beforeEach(() => {
                (localStorage.getItem as jest.Mock).mockImplementation(() => {
                    throw new Error('Local storage derped');
                });
                expect(renderTest).not.toThrow();
            });
            it('renders with the default value', () => {
                expect(ref.current?.getPersistedValue()).toBe(DEFAULT_VALUE);
            });
        });
        describe('when local storage does not exist', () => {
            let cachedLocalStorage: Storage;
            beforeEach(() => {
                cachedLocalStorage = localStorage;
                // @ts-ignore
                delete global.localStorage;
                expect(renderTest).not.toThrow();
            });
            afterEach(() => {
                global.localStorage = cachedLocalStorage;
            });
            it('renders with the default value', () => {
                expect(ref.current?.getPersistedValue()).toBe(DEFAULT_VALUE);
            });
        });
        describe('when local storage contains invalid JSON', () => {
            beforeEach(() => {
                (localStorage.getItem as jest.Mock).mockReturnValue('' /* <- not valid JSON! */);
                expect(renderTest).not.toThrow();
            });
            it('renders with the default value', () => {
                expect(ref.current?.getPersistedValue()).toBe(DEFAULT_VALUE);
            });
        });
    });
    describe('setting the persisted value', () => {
        describe('when setting to a non-null value', () => {
            const NEW_VALUE = 'new value';
            beforeEach(() => {
                expect(renderTest).not.toThrow();
            });
            it('sets that value in local storage', () => {
                act(() => {
                    ref.current?.persistValue(NEW_VALUE);
                });
                expect(localStorage.setItem).toHaveBeenCalledWith(STORAGE_KEY, JSON.stringify(NEW_VALUE));
            });
            it('re-renders the component with the new value', () => {
                act(() => {
                    ref.current?.persistValue(NEW_VALUE);
                });
                expect(ref.current?.getPersistedValue()).toBe(NEW_VALUE);
            });
            describe('many times in a row', () => {
                it('sets the new value in local storage once', () => {
                    act(() => {
                        ref.current?.persistValue(NEW_VALUE);
                        ref.current?.persistValue(NEW_VALUE);
                    });
                    expect(window.localStorage.setItem).toHaveBeenCalledTimes(1);
                    expect(window.localStorage.setItem).toHaveBeenCalledWith(STORAGE_KEY, JSON.stringify(NEW_VALUE));
                });
            });
            describe('multiple times ending with the current value', () => {
                it("does not call local storage's setter", () => {
                    act(() => {
                        ref.current?.persistValue(NEW_VALUE);
                        ref.current?.persistValue(DEFAULT_VALUE);
                    });
                    expect(window.localStorage.setItem).toHaveBeenCalledTimes(0);
                });
            });
        });
        describe('when setting to `null`', () => {
            beforeEach(() => {
                expect(renderTest).not.toThrow();
            });
            it('removes the key from local storage', () => {
                act(() => {
                    ref.current?.persistValue(null);
                });
                expect(localStorage.removeItem).toHaveBeenCalledWith(STORAGE_KEY);
            });
            it('re-renders the component with `null`', () => {
                act(() => {
                    ref.current?.persistValue(null);
                });
                expect(ref.current?.getPersistedValue()).toBe(null);
            });
        });
        describe('when merely accessing local storage results in a fatal error', () => {
            const NEW_VALUE = 'new value';
            let restoreOldLocalStorage: () => void;
            beforeEach(() => {
                restoreOldLocalStorage = configureLocalStorageToFatalOnAccess();
                expect(renderTest).not.toThrow();
            });
            afterEach(() => {
                restoreOldLocalStorage();
            });
            it('re-renders the component with the new value', () => {
                act(() => {
                    ref.current?.persistValue(NEW_VALUE);
                });
                expect(ref.current?.getPersistedValue()).toBe(NEW_VALUE);
            });
        });
        describe('when local storage fatals on write', () => {
            const NEW_VALUE = 'new value';
            beforeEach(() => {
                (localStorage.setItem as jest.Mock).mockImplementation(() => {
                    throw new Error('Local storage derped');
                });
                expect(renderTest).not.toThrow();
            });
            it('re-renders the component with the new value', () => {
                act(() => {
                    ref.current?.persistValue(NEW_VALUE);
                });
                expect(ref.current?.getPersistedValue()).toBe(NEW_VALUE);
            });
        });
        describe('when local storage does not exist', () => {
            let cachedLocalStorage: Storage;
            beforeEach(() => {
                cachedLocalStorage = localStorage;
                // @ts-ignore
                delete global.localStorage;
                expect(renderTest).not.toThrow();
            });
            afterEach(() => {
                global.localStorage = cachedLocalStorage;
            });
            describe('when setting to a non-null value', () => {
                const NEW_VALUE = 'new value';
                it('re-renders the component with the new value', () => {
                    act(() => {
                        ref.current?.persistValue(NEW_VALUE);
                    });
                    expect(ref.current?.getPersistedValue()).toBe(NEW_VALUE);
                });
            });
            describe('when setting to `null`', () => {
                it('re-renders the component with `null`', () => {
                    act(() => {
                        ref.current?.persistValue(null);
                    });
                    expect(ref.current?.getPersistedValue()).toBe(null);
                });
            });
        });
    });
});
Example #21
Source File: Modals.test.tsx    From jmix-frontend with Apache License 2.0 4 votes vote down vote up
describe('Modals component and API', () => {

  describe('Modals component', () => {

    it('Modals renders without crashing', () => {
      TestRenderer.act(() => {
        TestRenderer.create(testedJsx)
      })
    })
  })

  describe('Modals API', () => {

    beforeEach(() => {
      container = document.createElement("div");
      document.body.appendChild(container);
    });

    afterEach(() => {
      unmountComponentAtNode(container);
      container.remove();
    });

    it('open method', () => {
      act(() => {
        render(testedJsx, container);
      });
      act(() => {
        modals.open({
          content: <span id="testedModal"> tested Modal</span>
        });
      });
      const testedModalContent = document.getElementById("testedModal");
      expect(testedModalContent).not.toBeNull();
    });

    it('dispose method, which open method returned', () => {
      let disposeOpenedModal: any;
      let testedModalContent: HTMLElement | null = null;
      act(() => {
        render(testedJsx, container);
      });
      act(() => {
        disposeOpenedModal = modals.open({
          content: <span id="testedModal"> tested Modal</span>
        });
      });
      testedModalContent = document.getElementById("testedModal");
      expect(testedModalContent).not.toBeNull();

      act(() => {
        disposeOpenedModal();
      });
      testedModalContent = document.getElementById("testedModal");
      expect(testedModalContent).toBeNull();
    });

    it('close method', () => {
      const firstModalOptions = {
        content: <span id="firstModal">first tested Modal</span>
      }
      const secondModalOptions = {
        content: <span id="secondModal">second tested Modal</span>
      }
      let firstTestedModalContent: HTMLElement | null = null;
      let secondTestedModalContent: HTMLElement | null = null;

      act(() => {
        render(testedJsx, container);
      });

      act(() => {
        modals.open(firstModalOptions);
        modals.open(secondModalOptions);
      });
      firstTestedModalContent = document.getElementById("firstModal");
      secondTestedModalContent = document.getElementById("secondModal");
      expect(firstTestedModalContent).not.toBeNull();
      expect(secondTestedModalContent).not.toBeNull();

      act(() => {
        modals.close(firstModalOptions);
      });
      firstTestedModalContent = document.getElementById("firstModal");
      secondTestedModalContent = document.getElementById("secondModal");
      expect(firstTestedModalContent).toBeNull();
      expect(secondTestedModalContent).not.toBeNull();

      act(() => {
        modals.close(secondModalOptions);
      });
      firstTestedModalContent = document.getElementById("firstModal");
      secondTestedModalContent = document.getElementById("secondModal");
      expect(firstTestedModalContent).toBeNull();
      expect(secondTestedModalContent).toBeNull();
    });

    it('closeAll method', () => {
      const firstModalOptions = {
        content: <span id="firstModal">first tested Modal</span>
      }
      const secondModalOptions = {
        content: <span id="secondModal">second tested Modal</span>
      }
      let firstTestedModalContent: HTMLElement | null = null;
      let secondTestedModalContent: HTMLElement | null = null;

      act(() => {
        render(testedJsx, container);
      });

      act(() => {
        modals.open(firstModalOptions);
        modals.open(secondModalOptions);
      });
      firstTestedModalContent = document.getElementById("firstModal");
      secondTestedModalContent = document.getElementById("secondModal");
      expect(firstTestedModalContent).not.toBeNull();
      expect(secondTestedModalContent).not.toBeNull();

      act(() => {
        modals.closeAll();
      });
      firstTestedModalContent = document.getElementById("firstModal");
      secondTestedModalContent = document.getElementById("secondModal");
      expect(firstTestedModalContent).toBeNull();
      expect(secondTestedModalContent).toBeNull();
    });

  })
})
Example #22
Source File: index.test.tsx    From urx with MIT License 4 votes vote down vote up
describe('components from system', () => {
  let container: any = null

  beforeEach(() => {
    // setup a DOM element as a render target
    container = document.createElement('div')
    document.body.appendChild(container)
  })

  afterEach(() => {
    // cleanup on exiting
    unmountComponentAtNode(container)
    container.remove()
    container = null
  })

  const simpleSystem = () =>
    system(() => {
      const prop = stream<number>()
      const depot = statefulStream(10)
      connect(prop, depot)

      return { prop, depot }
    })

  describe('prop mapping', () => {
    let Component: any
    let Child: any

    beforeEach(() => {
      const e = simpleSystem()

      const { Component: Comp, useEmitterValue } = systemToComponent(e, {
        optional: { prop: 'prop' },
      })

      Child = () => {
        const value = useEmitterValue('depot')
        return <div>{value}</div>
      }
      Component = Comp
    })

    it('maps a property to the component', () => {
      render(
        <Component>
          <Child />
        </Component>,
        container
      )
      expect(container.textContent).toBe('10')
    })

    it('pipes the prop to depot and to the output', () => {
      act(() => {
        render(
          <Component prop={20}>
            <Child />
          </Component>,
          container
        )
      })
      expect(container.textContent).toBe('20')
    })
  })

  it('supports passing root', () => {
    const e = simpleSystem()

    const Root: FC = () => {
      const value = useEmitterValue('depot')
      return <div>{value}</div>
    }

    const { Component: Comp, useEmitterValue } = systemToComponent(
      e,
      {
        optional: { prop: 'prop' },
      },
      Root
    )

    act(() => {
      render(<Comp prop={20} />, container)
    })

    expect(container.textContent).toBe('20')
  })

  it('supports root component with props', () => {
    const e = simpleSystem()

    const Root: FC<{ rootProp: number }> = ({ rootProp }) => {
      const value = useEmitterValue('depot')
      return (
        <div>
          {rootProp} - {value}
        </div>
      )
    }

    const { Component: Comp, useEmitterValue } = systemToComponent(
      e,
      {
        optional: { prop: 'prop' },
      },
      Root
    )

    act(() => {
      render(<Comp prop={20} rootProp={10} />, container)
    })

    expect(container.textContent).toBe('10 - 20')
  })

  it('exposes streams as methods', () => {
    const e = system(() => {
      const meth = statefulStream(20)
      return { meth }
    })

    const Root: FC = () => {
      const value = useEmitterValue('meth')
      return <div>{value}</div>
    }

    const { Component: Comp, useEmitterValue } = systemToComponent(
      e,
      {
        methods: { meth: 'meth' },
      },
      Root
    )

    const ref = createRef<RefHandle<typeof Comp>>()

    act(() => {
      render(<Comp ref={ref} />, container)
      ref.current!.meth(30)
    })

    expect(container.textContent).toBe('30')
  })

  it('exposes changes in streams as events', () => {
    const e = system(() => {
      const meth = statefulStream(20)
      const methCalledDouble = statefulStreamFromEmitter(
        pipe(
          duc(meth),
          map(value => value * 2)
        ),
        20
      )

      return { meth, methCalledDouble }
    })

    const Root: FC = () => {
      const value = useEmitterValue('meth')
      return <div>{value}</div>
    }

    const { Component: Comp, useEmitterValue } = systemToComponent(
      e,
      {
        methods: { meth: 'meth' },
        events: { methCalledDouble: 'methCalledDouble' },
      },
      Root
    )

    const ref = createRef<RefHandle<typeof Comp>>()
    const sub = jest.fn()

    act(() => {
      render(<Comp ref={ref} methCalledDouble={sub} />, container)
      ref.current!.meth(30)
    })

    expect(container.textContent).toBe('30')
    expect(sub).toHaveBeenCalledWith(60)
  })

  it('works with function properties', () => {
    const e = system(() => {
      const prop = statefulStream(() => 20 as number)
      return { prop }
    })

    const Root: FC = () => {
      const prop = useEmitterValue('prop')
      return <div>{prop()}</div>
    }

    const { Component: Comp, useEmitterValue } = systemToComponent(
      e,
      {
        required: { prop: 'prop' },
      },
      Root
    )

    act(() => {
      render(<Comp prop={() => 50} />, container)
    })

    expect(container.textContent).toBe('50')
  })

  it('accepts undefined event handlers', () => {
    const e = system(() => {
      const prop = statefulStream(20)
      return { prop }
    })

    const Root: FC = () => {
      return <div>Foo</div>
    }

    const { Component: Comp } = systemToComponent(
      e,
      {
        events: { prop: 'prop' },
      },
      Root
    )

    act(() => {
      expect(() => {
        render(<Comp prop={undefined} />, container)
      }).not.toThrow()
    })
  })

  it('emits changes caused by prop changes', () => {
    const e = system(() => {
      const prop = statefulStream(20)
      const doubleProp = streamFromEmitter(
        pipe(
          prop,
          map(value => value * 2)
        )
      )
      return { prop, doubleProp }
    })

    const Root: FC = () => {
      const value = useEmitterValue('prop')
      return <div>{value}</div>
    }

    const { Component: Comp, useEmitterValue } = systemToComponent(
      e,
      {
        required: { prop: 'prop' },
        events: { doubleProp: 'doubleProp' },
      },
      Root
    )

    const sub = jest.fn()

    act(() => {
      render(<Comp prop={20} doubleProp={sub} />, container)
    })

    expect(container.textContent).toBe('20')
    expect(sub).toHaveBeenCalledWith(40)
    expect(sub).toHaveBeenCalledTimes(1)
  })

  it('skips setup glitches', () => {
    const e = system(() => {
      const prop = statefulStream(2)
      const prop2 = statefulStream(3)
      const propsReady = stream<boolean>()
      const combinedProp = streamFromEmitter(
        pipe(
          combineLatest(prop, prop2, propsReady),
          filter(([, , ready]) => ready),
          map(([p, p2]) => p * p2)
        )
      )

      return { prop, prop2, combinedProp, propsReady }
    })

    const Root: FC = () => {
      const value = useEmitterValue('prop')
      return <div>{value}</div>
    }

    const { Component: Comp, useEmitterValue } = systemToComponent(
      e,
      {
        required: { prop: 'prop', prop2: 'prop2' },
        events: { combinedProp: 'combinedProp' },
      },
      Root
    )

    const sub = jest.fn()

    act(() => {
      render(<Comp prop={3} prop2={4} combinedProp={sub} />, container)
    })

    expect(sub).toHaveBeenCalledWith(12)
    expect(sub).toHaveBeenCalledTimes(1)

    act(() => {
      render(<Comp prop={5} prop2={6} combinedProp={sub} />, container)
    })

    expect(sub).toHaveBeenCalledWith(30)
    expect(sub).toHaveBeenCalledTimes(2)
  })
})