From 08cedc7c222b269b26c49566eed49a9c06506b3e Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Thu, 21 Sep 2023 13:38:34 +0800 Subject: [PATCH 1/3] add unit tests for core WorkspacesService module Signed-off-by: Yulong Ruan --- src/core/public/core_system.test.mocks.ts | 9 +++ src/core/public/core_system.test.ts | 25 ++++++ src/core/public/core_system.ts | 1 + src/core/public/mocks.ts | 2 +- .../public/plugins/plugins_service.test.ts | 2 +- .../workspace/workspaces_service.mock.ts | 13 ++- .../workspace/workspaces_service.test.ts | 80 +++++++++++++++++++ .../public/workspace/workspaces_service.ts | 6 +- 8 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 src/core/public/workspace/workspaces_service.test.ts diff --git a/src/core/public/core_system.test.mocks.ts b/src/core/public/core_system.test.mocks.ts index da09de341bc4..4025361e150d 100644 --- a/src/core/public/core_system.test.mocks.ts +++ b/src/core/public/core_system.test.mocks.ts @@ -42,6 +42,7 @@ import { docLinksServiceMock } from './doc_links/doc_links_service.mock'; import { renderingServiceMock } from './rendering/rendering_service.mock'; import { contextServiceMock } from './context/context_service.mock'; import { integrationsServiceMock } from './integrations/integrations_service.mock'; +import { workspacesServiceMock } from './workspace/workspaces_service.mock'; import { coreAppMock } from './core_app/core_app.mock'; export const MockInjectedMetadataService = injectedMetadataServiceMock.create(); @@ -145,3 +146,11 @@ export const CoreAppConstructor = jest.fn().mockImplementation(() => MockCoreApp jest.doMock('./core_app', () => ({ CoreApp: CoreAppConstructor, })); + +export const MockWorkspacesService = workspacesServiceMock.create(); +export const WorkspacesServiceConstructor = jest + .fn() + .mockImplementation(() => MockWorkspacesService); +jest.doMock('./workspace', () => ({ + WorkspacesService: WorkspacesServiceConstructor, +})); diff --git a/src/core/public/core_system.test.ts b/src/core/public/core_system.test.ts index b3acd696bc3d..81dfa97b9afa 100644 --- a/src/core/public/core_system.test.ts +++ b/src/core/public/core_system.test.ts @@ -55,6 +55,8 @@ import { MockIntegrationsService, CoreAppConstructor, MockCoreApp, + WorkspacesServiceConstructor, + MockWorkspacesService, } from './core_system.test.mocks'; import { CoreSystem } from './core_system'; @@ -99,6 +101,7 @@ describe('constructor', () => { expect(RenderingServiceConstructor).toHaveBeenCalledTimes(1); expect(IntegrationsServiceConstructor).toHaveBeenCalledTimes(1); expect(CoreAppConstructor).toHaveBeenCalledTimes(1); + expect(WorkspacesServiceConstructor).toHaveBeenCalledTimes(1); }); it('passes injectedMetadata param to InjectedMetadataService', () => { @@ -223,6 +226,11 @@ describe('#setup()', () => { expect(MockIntegrationsService.setup).toHaveBeenCalledTimes(1); }); + it('calls workspaces#setup()', async () => { + await setupCore(); + expect(MockWorkspacesService.setup).toHaveBeenCalledTimes(1); + }); + it('calls coreApp#setup()', async () => { await setupCore(); expect(MockCoreApp.setup).toHaveBeenCalledTimes(1); @@ -310,6 +318,15 @@ describe('#start()', () => { expect(MockIntegrationsService.start).toHaveBeenCalledTimes(1); }); + it('calls workspaces#start()', async () => { + await startCore(); + expect(MockWorkspacesService.start).toHaveBeenCalledTimes(1); + expect(MockWorkspacesService.start).toHaveBeenCalledWith({ + application: expect.any(Object), + http: expect.any(Object), + }); + }); + it('calls coreApp#start()', async () => { await startCore(); expect(MockCoreApp.start).toHaveBeenCalledTimes(1); @@ -364,6 +381,14 @@ describe('#stop()', () => { expect(MockIntegrationsService.stop).toHaveBeenCalled(); }); + it('calls workspaces.stop()', () => { + const coreSystem = createCoreSystem(); + + expect(MockWorkspacesService.stop).not.toHaveBeenCalled(); + coreSystem.stop(); + expect(MockWorkspacesService.stop).toHaveBeenCalled(); + }); + it('calls coreApp.stop()', () => { const coreSystem = createCoreSystem(); diff --git a/src/core/public/core_system.ts b/src/core/public/core_system.ts index d7de8b4595d5..0a64e0f4fa9a 100644 --- a/src/core/public/core_system.ts +++ b/src/core/public/core_system.ts @@ -311,6 +311,7 @@ export class CoreSystem { this.chrome.stop(); this.i18n.stop(); this.application.stop(); + this.workspaces.stop(); this.rootDomElement.textContent = ''; } } diff --git a/src/core/public/mocks.ts b/src/core/public/mocks.ts index 722070d5a9ea..3acc71424b91 100644 --- a/src/core/public/mocks.ts +++ b/src/core/public/mocks.ts @@ -87,7 +87,7 @@ function createCoreSetupMock({ getInjectedVar: injectedMetadataServiceMock.createSetupContract().getInjectedVar, getBranding: injectedMetadataServiceMock.createSetupContract().getBranding, }, - workspaces: workspacesServiceMock.createSetupContractMock(), + workspaces: workspacesServiceMock.createSetupContract(), }; return mock; diff --git a/src/core/public/plugins/plugins_service.test.ts b/src/core/public/plugins/plugins_service.test.ts index 7e8c96c1f9d0..f26626ed1ca3 100644 --- a/src/core/public/plugins/plugins_service.test.ts +++ b/src/core/public/plugins/plugins_service.test.ts @@ -109,7 +109,7 @@ describe('PluginsService', () => { injectedMetadata: injectedMetadataServiceMock.createStartContract(), notifications: notificationServiceMock.createSetupContract(), uiSettings: uiSettingsServiceMock.createSetupContract(), - workspaces: workspacesServiceMock.createSetupContractMock(), + workspaces: workspacesServiceMock.createSetupContract(), }; mockSetupContext = { ...mockSetupDeps, diff --git a/src/core/public/workspace/workspaces_service.mock.ts b/src/core/public/workspace/workspaces_service.mock.ts index 3c35315aa850..3b1408b03045 100644 --- a/src/core/public/workspace/workspaces_service.mock.ts +++ b/src/core/public/workspace/workspaces_service.mock.ts @@ -4,6 +4,9 @@ */ import { BehaviorSubject } from 'rxjs'; +import type { PublicMethodsOf } from '@osd/utility-types'; + +import { WorkspacesService } from './workspaces_service'; import { WorkspaceAttribute } from '..'; const currentWorkspaceId$ = new BehaviorSubject(''); @@ -30,7 +33,15 @@ const createWorkspacesStartContractMock = () => ({ renderWorkspaceMenu: jest.fn(), }); +export type WorkspacesServiceContract = PublicMethodsOf; +const createMock = (): jest.Mocked => ({ + setup: jest.fn().mockReturnValue(createWorkspacesSetupContractMock()), + start: jest.fn().mockReturnValue(createWorkspacesStartContractMock()), + stop: jest.fn(), +}); + export const workspacesServiceMock = { - createSetupContractMock: createWorkspacesSetupContractMock, + create: createMock, + createSetupContract: createWorkspacesSetupContractMock, createStartContract: createWorkspacesStartContractMock, }; diff --git a/src/core/public/workspace/workspaces_service.test.ts b/src/core/public/workspace/workspaces_service.test.ts new file mode 100644 index 000000000000..8fcd4e4322db --- /dev/null +++ b/src/core/public/workspace/workspaces_service.test.ts @@ -0,0 +1,80 @@ +import { httpServiceMock } from '../http/http_service.mock'; +import { applicationServiceMock } from '../application/application_service.mock'; +import { WorkspacesService, WorkspacesSetup, WorkspacesStart } from './workspaces_service'; + +describe('WorkspacesService', () => { + let workspaces: WorkspacesService; + let workspacesSetup: WorkspacesSetup; + let workspacesStart: WorkspacesStart; + + beforeEach(() => { + workspaces = new WorkspacesService(); + workspacesSetup = workspaces.setup(); + workspacesStart = workspaces.start({ + http: httpServiceMock.createStartContract(), + application: applicationServiceMock.createInternalStartContract(), + }); + }); + + afterEach(() => { + workspaces.stop(); + }); + + it('workspace initialized$ state is false by default', () => { + expect(workspacesStart.initialized$.value).toBe(false); + }); + + it('workspace is not enabled by default', () => { + expect(workspacesStart.workspaceEnabled$.value).toBe(false); + }); + + it('currentWorkspace is not set by default', () => { + expect(workspacesStart.currentWorkspace$.value).toBe(null); + expect(workspacesStart.currentWorkspaceId$.value).toBe(''); + }); + + it('workspaceList$ is empty by default', () => { + expect(workspacesStart.workspaceList$.value.length).toBe(0); + }); + + it('should call menu render function', () => { + const renderFn = jest.fn(); + workspacesSetup.registerWorkspaceMenuRender(renderFn); + workspacesStart.renderWorkspaceMenu(); + expect(renderFn).toHaveBeenCalled(); + }); + + it('should return null if NO menu render function was registered', () => { + expect(workspacesStart.renderWorkspaceMenu()).toBe(null); + }); + + it('the current workspace should also updated after changing current workspace id', () => { + expect(workspacesStart.currentWorkspace$.value).toBe(null); + + workspacesStart.initialized$.next(true); + workspacesStart.workspaceList$.next([ + { id: 'workspace-1', name: 'workspace 1' }, + { id: 'workspace-2', name: 'workspace 2' }, + ]); + workspacesStart.currentWorkspaceId$.next('workspace-1'); + + expect(workspacesStart.currentWorkspace$.value).toEqual({ + id: 'workspace-1', + name: 'workspace 1', + }); + + workspacesStart.currentWorkspaceId$.next(''); + expect(workspacesStart.currentWorkspace$.value).toEqual(null); + }); + + it('should return error if the specified workspace id cannot be found', () => { + expect(workspacesStart.currentWorkspace$.hasError).toBe(false); + workspacesStart.initialized$.next(true); + workspacesStart.workspaceList$.next([ + { id: 'workspace-1', name: 'workspace 1' }, + { id: 'workspace-2', name: 'workspace 2' }, + ]); + workspacesStart.currentWorkspaceId$.next('workspace-3'); + expect(workspacesStart.currentWorkspace$.hasError).toBe(true); + }); +}); diff --git a/src/core/public/workspace/workspaces_service.ts b/src/core/public/workspace/workspaces_service.ts index 39519ccdddbe..b5c8ff4cdba1 100644 --- a/src/core/public/workspace/workspaces_service.ts +++ b/src/core/public/workspace/workspaces_service.ts @@ -8,7 +8,7 @@ import { isEqual } from 'lodash'; import { CoreService, WorkspaceAttribute } from '../../types'; import { InternalApplicationStart } from '../application'; -import { HttpSetup } from '../http'; +import { HttpStart } from '../http'; type WorkspaceMenuRenderFn = ({ basePath, @@ -16,7 +16,7 @@ type WorkspaceMenuRenderFn = ({ observables, }: { getUrlForApp: InternalApplicationStart['getUrlForApp']; - basePath: HttpSetup['basePath']; + basePath: HttpStart['basePath']; observables: WorkspaceObservables; }) => JSX.Element | null; @@ -99,7 +99,7 @@ export class WorkspacesService implements CoreService Date: Thu, 21 Sep 2023 13:48:26 +0800 Subject: [PATCH 2/3] add test for WorkspacesService.stop() function Signed-off-by: Yulong Ruan --- src/core/public/workspace/workspaces_service.test.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/core/public/workspace/workspaces_service.test.ts b/src/core/public/workspace/workspaces_service.test.ts index 8fcd4e4322db..724af4ee9855 100644 --- a/src/core/public/workspace/workspaces_service.test.ts +++ b/src/core/public/workspace/workspaces_service.test.ts @@ -77,4 +77,13 @@ describe('WorkspacesService', () => { workspacesStart.currentWorkspaceId$.next('workspace-3'); expect(workspacesStart.currentWorkspace$.hasError).toBe(true); }); + + it('should stop all observables when workspace service stopped', () => { + workspaces.stop(); + expect(workspacesStart.currentWorkspaceId$.isStopped).toBe(true); + expect(workspacesStart.currentWorkspace$.isStopped).toBe(true); + expect(workspacesStart.workspaceList$.isStopped).toBe(true); + expect(workspacesStart.workspaceEnabled$.isStopped).toBe(true); + expect(workspacesStart.initialized$.isStopped).toBe(true); + }); }); From d7e2691e569f2691e16a22850b974073a4524909 Mon Sep 17 00:00:00 2001 From: Yulong Ruan Date: Fri, 22 Sep 2023 10:43:04 +0800 Subject: [PATCH 3/3] add license header Signed-off-by: Yulong Ruan --- src/core/public/workspace/workspaces_service.test.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/core/public/workspace/workspaces_service.test.ts b/src/core/public/workspace/workspaces_service.test.ts index 724af4ee9855..b2d84152da83 100644 --- a/src/core/public/workspace/workspaces_service.test.ts +++ b/src/core/public/workspace/workspaces_service.test.ts @@ -1,3 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + import { httpServiceMock } from '../http/http_service.mock'; import { applicationServiceMock } from '../application/application_service.mock'; import { WorkspacesService, WorkspacesSetup, WorkspacesStart } from './workspaces_service';