import { act, renderHook } from '@testing-library/react'; import { AddedComponentsRegistry } from './registry/AddedComponentsRegistry'; import { AddedFunctionsRegistry } from './registry/AddedFunctionsRegistry'; import { AddedLinksRegistry } from './registry/AddedLinksRegistry'; import { ExposedComponentsRegistry } from './registry/ExposedComponentsRegistry'; import { PluginExtensionRegistries } from './registry/types'; import { useLoadAppPlugins } from './useLoadAppPlugins'; import { createUsePluginExtensions } from './usePluginExtensions'; jest.mock('./useLoadAppPlugins'); describe('usePluginExtensions()', () => { let registries: PluginExtensionRegistries; const pluginId = 'myorg-extensions-app'; const extensionPointId = `${pluginId}/extension-point/v1`; beforeEach(() => { registries = { addedComponentsRegistry: new AddedComponentsRegistry(), addedLinksRegistry: new AddedLinksRegistry(), exposedComponentsRegistry: new ExposedComponentsRegistry(), addedFunctionsRegistry: new AddedFunctionsRegistry(), }; jest.mocked(useLoadAppPlugins).mockReturnValue({ isLoading: false }); }); it('should return an empty array if there are no extensions registered for the extension point', () => { const usePluginExtensions = createUsePluginExtensions(registries); const { result } = renderHook(() => usePluginExtensions({ extensionPointId: 'foo/bar/v1', }) ); expect(result.current.extensions).toEqual([]); }); it('should return the plugin link extensions from the registry', () => { registries.addedLinksRegistry.register({ pluginId, configs: [ { targets: extensionPointId, title: '1', description: '1', path: `/a/${pluginId}/2`, }, { targets: extensionPointId, title: '2', description: '2', path: `/a/${pluginId}/2`, }, ], }); const usePluginExtensions = createUsePluginExtensions(registries); const { result } = renderHook(() => usePluginExtensions({ extensionPointId })); expect(result.current.extensions.length).toBe(2); expect(result.current.extensions[0].title).toBe('1'); expect(result.current.extensions[1].title).toBe('2'); }); it('should return the plugin component extensions from the registry', () => { const componentExtensionPointId = `${pluginId}/component/v1`; registries.addedLinksRegistry.register({ pluginId, configs: [ { targets: extensionPointId, title: '1', description: '1', path: `/a/${pluginId}/2`, }, { targets: extensionPointId, title: '2', description: '2', path: `/a/${pluginId}/2`, }, ], }); registries.addedComponentsRegistry.register({ pluginId, configs: [ { targets: componentExtensionPointId, title: 'Component 1', description: '1', component: () =>
Hello World1
, }, { targets: componentExtensionPointId, title: 'Component 2', description: '2', component: () =>
Hello World2
, }, ], }); const usePluginExtensions = createUsePluginExtensions(registries); const { result } = renderHook(() => usePluginExtensions({ extensionPointId: componentExtensionPointId })); expect(result.current.extensions.length).toBe(2); expect(result.current.extensions[0].title).toBe('Component 1'); expect(result.current.extensions[1].title).toBe('Component 2'); }); it('should dynamically update the extensions registered for a certain extension point', () => { const usePluginExtensions = createUsePluginExtensions(registries); let { result, rerender } = renderHook(() => usePluginExtensions({ extensionPointId })); // No extensions yet expect(result.current.extensions.length).toBe(0); // Add extensions to the registry act(() => { registries.addedLinksRegistry.register({ pluginId, configs: [ { targets: extensionPointId, title: '1', description: '1', path: `/a/${pluginId}/2`, }, { targets: extensionPointId, title: '2', description: '2', path: `/a/${pluginId}/2`, }, ], }); }); // Check if the hook returns the new extensions rerender(); expect(result.current.extensions.length).toBe(2); expect(result.current.extensions[0].title).toBe('1'); expect(result.current.extensions[1].title).toBe('2'); }); it('should only render the hook once', () => { const addedComponentsRegistrySpy = jest.spyOn(registries.addedComponentsRegistry, 'asObservable'); const addedLinksRegistrySpy = jest.spyOn(registries.addedLinksRegistry, 'asObservable'); const usePluginExtensions = createUsePluginExtensions(registries); renderHook(() => usePluginExtensions({ extensionPointId })); expect(addedComponentsRegistrySpy).toHaveBeenCalledTimes(1); expect(addedLinksRegistrySpy).toHaveBeenCalledTimes(1); }); it('should return the same extensions object if the context object is the same', async () => { const usePluginExtensions = createUsePluginExtensions(registries); // Add extensions to the registry act(() => { registries.addedLinksRegistry.register({ pluginId, configs: [ { targets: extensionPointId, title: '1', description: '1', path: `/a/${pluginId}/2`, }, { targets: extensionPointId, title: '2', description: '2', path: `/a/${pluginId}/2`, }, ], }); }); // Check if it returns the same extensions object in case nothing changes const context = {}; const { rerender, result } = renderHook(usePluginExtensions, { initialProps: { extensionPointId, context, }, }); const firstResult = result.current; rerender({ context, extensionPointId }); const secondResult = result.current; expect(firstResult.extensions).toBe(secondResult.extensions); }); it('should return a new extensions object if the context object is different', () => { const usePluginExtensions = createUsePluginExtensions(registries); // Add extensions to the registry act(() => { registries.addedLinksRegistry.register({ pluginId, configs: [ { targets: extensionPointId, title: '1', description: '1', path: `/a/${pluginId}/2`, }, { targets: extensionPointId, title: '2', description: '2', path: `/a/${pluginId}/2`, }, ], }); }); // Check if it returns a different extensions object in case the context object changes const firstResults = renderHook(() => usePluginExtensions({ extensionPointId, context: {} })); const secondResults = renderHook(() => usePluginExtensions({ extensionPointId, context: {} })); expect(firstResults.result.current.extensions === secondResults.result.current.extensions).toBe(false); }); it('should return a new extensions object if the registry changes but the context object is the same', () => { const context = {}; const usePluginExtensions = createUsePluginExtensions(registries); // Add the first extension act(() => { registries.addedLinksRegistry.register({ pluginId, configs: [ { targets: extensionPointId, title: '1', description: '1', path: `/a/${pluginId}/2`, }, ], }); }); const { result, rerender } = renderHook(() => usePluginExtensions({ extensionPointId, context })); const firstExtensions = result.current.extensions; // Add the second extension act(() => { registries.addedLinksRegistry.register({ pluginId, configs: [ { targets: extensionPointId, // extensionPointId: 'plugins/foo/bar/zed', // A different extension point (to be sure that it's also returning a new object when the actual extension point doesn't change) title: '2', description: '2', path: `/a/${pluginId}/2`, }, ], }); }); rerender(); const secondExtensions = result.current.extensions; expect(firstExtensions === secondExtensions).toBe(false); }); });