From d6e2dec831641f67497c284d6891fc90b4caaa8b Mon Sep 17 00:00:00 2001 From: Dmitry Kalinin Date: Mon, 27 Oct 2025 10:02:31 +0100 Subject: [PATCH 1/2] Added tests for workspace toolbar --- .../workspace-users-management.test.tsx | 6 - ...workspace-users-toolbar.component.test.tsx | 199 ++++++++++++++++++ 2 files changed, 199 insertions(+), 6 deletions(-) delete mode 100644 web_ui/src/pages/user-management/workspaces/workspace-users-management.test.tsx create mode 100644 web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.test.tsx diff --git a/web_ui/src/pages/user-management/workspaces/workspace-users-management.test.tsx b/web_ui/src/pages/user-management/workspaces/workspace-users-management.test.tsx deleted file mode 100644 index a70605dbfc..0000000000 --- a/web_ui/src/pages/user-management/workspaces/workspace-users-management.test.tsx +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright (C) 2022-2025 Intel Corporation -// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE - -describe('Workspace user management tests', () => { - test.todo('tbd'); -}); diff --git a/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.test.tsx b/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.test.tsx new file mode 100644 index 0000000000..1e17eaf25e --- /dev/null +++ b/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.test.tsx @@ -0,0 +1,199 @@ +// Copyright (C) 2022-2025 Intel Corporation +// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE + +import { RESOURCE_TYPE, USER_ROLE } from '@geti/core/src/users/users.interface'; +import { createInMemoryApiWorkspacesService } from '@geti/core/src/workspaces/services/in-memory-api-workspaces-service'; +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; + +import { getMockedWorkspace } from '../../../test-utils/mocked-items-factory/mocked-workspace'; +import { providersRender } from '../../../test-utils/required-providers-render'; +import { WorkspaceUsersToolbar } from './workspace-users-toolbar.component'; + +const mockUseParams = jest.fn(); +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useParams: (...args: unknown[]) => mockUseParams(...args), +})); + +jest.mock('../../../hooks/use-organization-identifier/use-organization-identifier.hook', () => ({ + useOrganizationIdentifier: () => ({ organizationId: 'org-123' }), +})); + +const mockUseActiveUser = jest.fn(); +const mockUseUsers = jest.fn(); +jest.mock('@geti/core/src/users/hook/use-users.hook', () => ({ + ...jest.requireActual('@geti/core/src/users/hook/use-users.hook'), + useActiveUser: (...args: unknown[]) => mockUseActiveUser(...args), + useUsers: (...args: unknown[]) => mockUseUsers(...args), +})); + +const mockIsOrganizationAdmin = jest.fn(); +jest.mock('@geti/core/src/users/user-role-utils', () => ({ + isOrganizationAdmin: (...args: unknown[]) => mockIsOrganizationAdmin(...args), +})); + +const workspaces = [ + getMockedWorkspace({ id: 'workspace-1', name: 'Workspace 1' }), + getMockedWorkspace({ id: 'workspace-2', name: 'Workspace 2' }), +]; + +describe('WorkspaceUsersToolbar', () => { + beforeEach(() => { + jest.clearAllMocks(); + mockUseParams.mockReturnValue({ organizationId: 'org-123', workspaceId: 'workspace-1' }); + const adminUser = { + roles: [ + { + role: USER_ROLE.WORKSPACE_ADMIN, + resourceType: RESOURCE_TYPE.WORKSPACE, + resourceId: 'workspace-1', + }, + { + role: USER_ROLE.ORGANIZATION_ADMIN, + resourceType: RESOURCE_TYPE.ORGANIZATION, + resourceId: 'org-123', + }, + ], + }; + + mockUseUsers.mockReturnValue({ + useActiveUser: (...args: unknown[]) => mockUseActiveUser(...args), + }); + mockUseActiveUser.mockReturnValue({ data: adminUser }); + mockIsOrganizationAdmin.mockReturnValue(true); + }); + + it('renders a tab for each workspace and highlights the selected workspace', async () => { + const workspacesService = createInMemoryApiWorkspacesService(); + workspacesService.getWorkspaces = jest.fn().mockResolvedValue(workspaces); + providersRender( + , + { + featureFlags: { FEATURE_FLAG_WORKSPACE_ACTIONS: false }, + services: { workspacesService }, + } + ); + + const tabs = await screen.findAllByRole('tab'); + expect(tabs).toHaveLength(workspaces.length); + + workspaces.forEach((workspace) => { + expect(screen.getByRole('tab', { name: workspace.name })).toBeInTheDocument(); + }); + + expect(screen.getByRole('tab', { name: workspaces[0].name })).toHaveAttribute('aria-selected', 'true'); + expect(screen.getByRole('tab', { name: workspaces[1].name })).not.toHaveAttribute('aria-selected', 'true'); + }); + + it('hides workspace actions when the feature flag is disabled', async () => { + const workspacesService = createInMemoryApiWorkspacesService(); + workspacesService.getWorkspaces = jest.fn().mockResolvedValue(workspaces); + providersRender( + , + { + featureFlags: { FEATURE_FLAG_WORKSPACE_ACTIONS: false }, + services: { workspacesService }, + } + ); + + await screen.findAllByRole('tab'); + + expect(screen.queryByRole('button', { name: 'Create workspace' })).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument(); + }); + + it('shows workspace actions when flag and permissions allow it', async () => { + const workspacesService = createInMemoryApiWorkspacesService(); + workspacesService.getWorkspaces = jest.fn().mockResolvedValue(workspaces); + mockUseParams.mockReturnValue({ organizationId: 'org-123', workspaceId: workspaces[0].id }); + + providersRender( + , + { + featureFlags: { FEATURE_FLAG_WORKSPACE_ACTIONS: true }, + services: { workspacesService }, + } + ); + + await screen.findAllByRole('tab'); + + await waitFor(() => { + expect(screen.getByRole('button', { name: /open menu/i })).toBeVisible(); + }); + + expect(screen.getByRole('button', { name: 'Create workspace' })).toBeVisible(); + }); + + it('renders fallback when permissions fail even with the feature flag enabled', async () => { + const workspacesService = createInMemoryApiWorkspacesService(); + workspacesService.getWorkspaces = jest.fn().mockResolvedValue(workspaces); + const nonAdminUser = { + roles: [ + { + role: 'WORKSPACE_CONTRIBUTOR', + resourceType: 'WORKSPACE', + resourceId: 'workspace-1', + }, + ], + }; + + mockUseActiveUser.mockReturnValue({ data: nonAdminUser }); + mockUseUsers.mockReturnValue({ + useActiveUser: (...args: unknown[]) => mockUseActiveUser(...args), + }); + mockIsOrganizationAdmin.mockReturnValue(false); + + providersRender( + , + { + featureFlags: { FEATURE_FLAG_WORKSPACE_ACTIONS: true }, + services: { workspacesService }, + } + ); + + await screen.findAllByRole('tab'); + + expect(screen.queryByRole('button', { name: /open menu/i })).not.toBeInTheDocument(); + }); + + it('opens the create workspace dialog when the action button is pressed', async () => { + const workspacesService = createInMemoryApiWorkspacesService(); + workspacesService.getWorkspaces = jest.fn().mockResolvedValue(workspaces); + + providersRender( + , + { + featureFlags: { FEATURE_FLAG_WORKSPACE_ACTIONS: true }, + services: { workspacesService }, + } + ); + + await screen.findAllByRole('tab'); + + const createButton = await screen.findByRole('button', { name: 'Create workspace' }); + await userEvent.click(createButton); + + expect(await screen.findByText('Create a new workspace')).toBeInTheDocument(); + }); +}); From 93b44ae9a5563d832e0d8c0a19e0cfe7987f7540 Mon Sep 17 00:00:00 2001 From: Dmitry Kalinin Date: Wed, 29 Oct 2025 15:27:04 +0100 Subject: [PATCH 2/2] Reworked tests --- ...workspace-users-toolbar.component.test.tsx | 80 ++++++------------- 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.test.tsx b/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.test.tsx index 1e17eaf25e..b4e8e8bb21 100644 --- a/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.test.tsx +++ b/web_ui/src/pages/user-management/workspaces/workspace-users-toolbar.component.test.tsx @@ -1,11 +1,13 @@ // Copyright (C) 2022-2025 Intel Corporation // LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE -import { RESOURCE_TYPE, USER_ROLE } from '@geti/core/src/users/users.interface'; +import { createInMemoryUsersService } from '@geti/core/src/users/services/in-memory-users-service'; +import { User } from '@geti/core/src/users/users.interface'; import { createInMemoryApiWorkspacesService } from '@geti/core/src/workspaces/services/in-memory-api-workspaces-service'; import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; +import { getMockedAdminUser, getMockedContributorUser } from '../../../test-utils/mocked-items-factory/mocked-users'; import { getMockedWorkspace } from '../../../test-utils/mocked-items-factory/mocked-workspace'; import { providersRender } from '../../../test-utils/required-providers-render'; import { WorkspaceUsersToolbar } from './workspace-users-toolbar.component'; @@ -20,48 +22,27 @@ jest.mock('../../../hooks/use-organization-identifier/use-organization-identifie useOrganizationIdentifier: () => ({ organizationId: 'org-123' }), })); -const mockUseActiveUser = jest.fn(); -const mockUseUsers = jest.fn(); -jest.mock('@geti/core/src/users/hook/use-users.hook', () => ({ - ...jest.requireActual('@geti/core/src/users/hook/use-users.hook'), - useActiveUser: (...args: unknown[]) => mockUseActiveUser(...args), - useUsers: (...args: unknown[]) => mockUseUsers(...args), -})); - -const mockIsOrganizationAdmin = jest.fn(); -jest.mock('@geti/core/src/users/user-role-utils', () => ({ - isOrganizationAdmin: (...args: unknown[]) => mockIsOrganizationAdmin(...args), -})); - const workspaces = [ getMockedWorkspace({ id: 'workspace-1', name: 'Workspace 1' }), getMockedWorkspace({ id: 'workspace-2', name: 'Workspace 2' }), ]; +const organizationId = 'org-123'; + +const createUsersServiceWithActiveUser = (activeUser: User) => { + const usersService = createInMemoryUsersService(); + usersService.getActiveUser = jest.fn().mockResolvedValue(activeUser); + + return usersService; +}; + describe('WorkspaceUsersToolbar', () => { beforeEach(() => { + mockUseParams.mockReturnValue({ organizationId, workspaceId: workspaces[0].id }); + }); + + afterEach(() => { jest.clearAllMocks(); - mockUseParams.mockReturnValue({ organizationId: 'org-123', workspaceId: 'workspace-1' }); - const adminUser = { - roles: [ - { - role: USER_ROLE.WORKSPACE_ADMIN, - resourceType: RESOURCE_TYPE.WORKSPACE, - resourceId: 'workspace-1', - }, - { - role: USER_ROLE.ORGANIZATION_ADMIN, - resourceType: RESOURCE_TYPE.ORGANIZATION, - resourceId: 'org-123', - }, - ], - }; - - mockUseUsers.mockReturnValue({ - useActiveUser: (...args: unknown[]) => mockUseActiveUser(...args), - }); - mockUseActiveUser.mockReturnValue({ data: adminUser }); - mockIsOrganizationAdmin.mockReturnValue(true); }); it('renders a tab for each workspace and highlights the selected workspace', async () => { @@ -114,7 +95,9 @@ describe('WorkspaceUsersToolbar', () => { it('shows workspace actions when flag and permissions allow it', async () => { const workspacesService = createInMemoryApiWorkspacesService(); workspacesService.getWorkspaces = jest.fn().mockResolvedValue(workspaces); - mockUseParams.mockReturnValue({ organizationId: 'org-123', workspaceId: workspaces[0].id }); + mockUseParams.mockReturnValue({ organizationId, workspaceId: workspaces[0].id }); + const adminUser = getMockedAdminUser({}, workspaces[0].id, true, organizationId); + const usersService = createUsersServiceWithActiveUser(adminUser); providersRender( { />, { featureFlags: { FEATURE_FLAG_WORKSPACE_ACTIONS: true }, - services: { workspacesService }, + services: { workspacesService, usersService }, } ); @@ -140,21 +123,8 @@ describe('WorkspaceUsersToolbar', () => { it('renders fallback when permissions fail even with the feature flag enabled', async () => { const workspacesService = createInMemoryApiWorkspacesService(); workspacesService.getWorkspaces = jest.fn().mockResolvedValue(workspaces); - const nonAdminUser = { - roles: [ - { - role: 'WORKSPACE_CONTRIBUTOR', - resourceType: 'WORKSPACE', - resourceId: 'workspace-1', - }, - ], - }; - - mockUseActiveUser.mockReturnValue({ data: nonAdminUser }); - mockUseUsers.mockReturnValue({ - useActiveUser: (...args: unknown[]) => mockUseActiveUser(...args), - }); - mockIsOrganizationAdmin.mockReturnValue(false); + const nonAdminUser = getMockedContributorUser({}, workspaces[0].id); + const usersService = createUsersServiceWithActiveUser(nonAdminUser); providersRender( { />, { featureFlags: { FEATURE_FLAG_WORKSPACE_ACTIONS: true }, - services: { workspacesService }, + services: { workspacesService, usersService }, } ); @@ -176,6 +146,8 @@ describe('WorkspaceUsersToolbar', () => { it('opens the create workspace dialog when the action button is pressed', async () => { const workspacesService = createInMemoryApiWorkspacesService(); workspacesService.getWorkspaces = jest.fn().mockResolvedValue(workspaces); + const adminUser = getMockedAdminUser({}, workspaces[0].id, true, organizationId); + const usersService = createUsersServiceWithActiveUser(adminUser); providersRender( { />, { featureFlags: { FEATURE_FLAG_WORKSPACE_ACTIONS: true }, - services: { workspacesService }, + services: { workspacesService, usersService }, } );