diff --git a/backend/src/Designer/RepositoryClient/Model/User.cs b/backend/src/Designer/RepositoryClient/Model/User.cs index e49d0175d7f..4b190519738 100644 --- a/backend/src/Designer/RepositoryClient/Model/User.cs +++ b/backend/src/Designer/RepositoryClient/Model/User.cs @@ -79,7 +79,7 @@ public User() /// /// Sets /// - [DataMember(Name = "UserType", EmitDefaultValue = false)] + [DataMember(Name = "userType", EmitDefaultValue = false)] public UserType UserType { get; set; } /// diff --git a/frontend/app-development/features/appPublish/components/deploy/DeployDropdown.test.tsx b/frontend/app-development/features/appPublish/components/deploy/DeployDropdown.test.tsx index cfcff42da1a..6f0ff1c2616 100644 --- a/frontend/app-development/features/appPublish/components/deploy/DeployDropdown.test.tsx +++ b/frontend/app-development/features/appPublish/components/deploy/DeployDropdown.test.tsx @@ -16,13 +16,17 @@ describe('DeployDropdown', () => { it('should open the confirmation dialog when clicking the delete button', async () => { await render(); - const deleteButton = screen.getByRole('button', { name: textMock('app_deploy_messages.btn_deploy_new_version') }); + const deleteButton = screen.getByRole('button', { + name: textMock('app_deploy_messages.btn_deploy_new_version'), + }); await act(() => user.click(deleteButton)); const dialog = screen.getByRole('dialog'); expect(dialog).toBeInTheDocument(); - const text = await screen.findByText(textMock('app_deploy_messages.deploy_confirmation_short', { selectedImageTag: '' })); + const text = await screen.findByText( + textMock('app_deploy_messages.deploy_confirmation_short', { selectedImageTag: '' }), + ); expect(text).toBeInTheDocument(); const confirmButton = screen.getByRole('button', { name: textMock('general.yes') }); @@ -35,38 +39,44 @@ describe('DeployDropdown', () => { it('should confirm and close the dialog when clicking the confirm button', async () => { await render(); - const deleteButton = screen.getByRole('button', { name: textMock('app_deploy_messages.btn_deploy_new_version') }); + const deleteButton = screen.getByRole('button', { + name: textMock('app_deploy_messages.btn_deploy_new_version'), + }); await act(() => user.click(deleteButton)); const confirmButton = screen.getByRole('button', { name: textMock('general.yes') }); await act(() => user.click(confirmButton)); - expect(startDeployMock).toBeCalledTimes(1); + expect(startDeployMock).toHaveBeenCalledTimes(1); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); it('should close the confirmation dialog when clicking the cancel button', async () => { await render(); - const deleteButton = screen.getByRole('button', { name: textMock('app_deploy_messages.btn_deploy_new_version') }); + const deleteButton = screen.getByRole('button', { + name: textMock('app_deploy_messages.btn_deploy_new_version'), + }); await act(() => user.click(deleteButton)); const cancelButton = screen.getByRole('button', { name: textMock('general.cancel') }); await act(() => user.click(cancelButton)); - expect(startDeployMock).toBeCalledTimes(0); + expect(startDeployMock).toHaveBeenCalledTimes(0); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); it('should close when clicking outside the popover', async () => { await render(); - const deleteButton = screen.getByRole('button', { name: textMock('app_deploy_messages.btn_deploy_new_version') }); + const deleteButton = screen.getByRole('button', { + name: textMock('app_deploy_messages.btn_deploy_new_version'), + }); await act(() => user.click(deleteButton)); await act(() => user.click(document.body)); - expect(startDeployMock).toBeCalledTimes(0); + expect(startDeployMock).toHaveBeenCalledTimes(0); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); }); @@ -83,7 +93,7 @@ const render = async (props: Partial = {}) => { setSelectedImageTag: jest.fn(), selectedImageTag: '', startDeploy: startDeployMock, - ...props + ...props, }; return rtlRender(); diff --git a/frontend/app-development/features/dataModelling/DataModelling.test.tsx b/frontend/app-development/features/dataModelling/DataModelling.test.tsx index 2086fa96c92..016362fdaca 100644 --- a/frontend/app-development/features/dataModelling/DataModelling.test.tsx +++ b/frontend/app-development/features/dataModelling/DataModelling.test.tsx @@ -35,22 +35,12 @@ const org = 'org'; const app = 'app'; const user = userEvent.setup(); -// Mocks: -const getDatamodel = jest.fn().mockImplementation(() => Promise.resolve({})); -const getDatamodelsJson = jest.fn().mockImplementation(() => Promise.resolve([])); -const getDatamodelsXsd = jest.fn().mockImplementation(() => Promise.resolve([])); -const generateModels = jest.fn().mockImplementation(() => Promise.resolve()); - const render = ( queries: Partial = {}, queryClient: QueryClient = createQueryClientMock(), ) => { const allQueries: ServicesContextProps = { ...queriesMock, - getDatamodel, - getDatamodelsJson, - getDatamodelsXsd, - generateModels, ...queries, }; @@ -66,8 +56,8 @@ describe('DataModelling', () => { it('fetches models on mount', () => { render(); - expect(getDatamodelsJson).toHaveBeenCalledTimes(1); - expect(getDatamodelsXsd).toHaveBeenCalledTimes(1); + expect(queriesMock.getDatamodelsJson).toHaveBeenCalledTimes(1); + expect(queriesMock.getDatamodelsXsd).toHaveBeenCalledTimes(1); }); it('shows start dialog when no models are present and intro page is closed', () => { @@ -90,8 +80,10 @@ describe('DataModelling', () => { }); it('does not show start dialog when there are models present', async () => { - getDatamodelsJson.mockImplementation(() => Promise.resolve([jsonMetadata1Mock])); - render(); + const getDatamodelsJson = jest + .fn() + .mockImplementation(() => Promise.resolve([jsonMetadata1Mock])); + render({ getDatamodelsJson }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('general.loading'))); expect( screen.queryByRole('heading', { name: textMock('app_data_modelling.landing_dialog_header') }), @@ -100,14 +92,19 @@ describe('DataModelling', () => { it('shows schema errors panel first when "generate model" button is clicked and returns errors', async () => { const queryClient = createQueryClientMock(); - generateModels.mockImplementation(() => - Promise.reject( - createApiErrorMock(400, 'DM_01', ['custom error message', 'another custom error message']), - ), - ); + const generateModels = jest + .fn() + .mockImplementation(() => + Promise.reject( + createApiErrorMock(400, 'DM_01', [ + 'custom error message', + 'another custom error message', + ]), + ), + ); queryClient.setQueryData([QueryKey.DatamodelsJson, org, app], [jsonMetadata1Mock]); queryClient.setQueryData([QueryKey.DatamodelsXsd, org, app], []); - render({}, queryClient); + render({ generateModels }, queryClient); const errorsPanel = screen.queryByText(textMock('api_errors.DM_01')); expect(errorsPanel).not.toBeInTheDocument(); @@ -121,14 +118,19 @@ describe('DataModelling', () => { it('closes schemaErrorsPanel when "close" button is clicked', async () => { const queryClient = createQueryClientMock(); - generateModels.mockImplementation(() => - Promise.reject( - createApiErrorMock(400, 'DM_01', ['custom error message', 'another custom error message']), - ), - ); + const generateModels = jest + .fn() + .mockImplementation(() => + Promise.reject( + createApiErrorMock(400, 'DM_01', [ + 'custom error message', + 'another custom error message', + ]), + ), + ); queryClient.setQueryData([QueryKey.DatamodelsJson, org, app], [jsonMetadata1Mock]); queryClient.setQueryData([QueryKey.DatamodelsXsd, org, app], []); - render({}, queryClient); + render({ generateModels }, queryClient); const generateModelButton = screen.getByRole('button', { name: textMock('schema_editor.generate_model_files'), diff --git a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/LandingPagePanel.test.tsx b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/LandingPagePanel.test.tsx index 5dec418a4b6..7545e2dcac0 100644 --- a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/LandingPagePanel.test.tsx +++ b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/LandingPagePanel.test.tsx @@ -17,20 +17,30 @@ describe('LandingPagePanel', () => { it('renders component', async () => { render(); - expect(screen.getByRole('heading', { name: textMock('app_data_modelling.landing_dialog_header') })).toBeInTheDocument(); - expect(screen.getByText(textMock('app_data_modelling.landing_dialog_paragraph'))).toBeInTheDocument(); + expect( + screen.getByRole('heading', { name: textMock('app_data_modelling.landing_dialog_header') }), + ).toBeInTheDocument(); + expect( + screen.getByText(textMock('app_data_modelling.landing_dialog_paragraph')), + ).toBeInTheDocument(); expect(screen.getByTestId(testids.fileSelectorInput)).toBeInTheDocument(); - expect(screen.getByRole('button', { name: textMock('app_data_modelling.landing_dialog_upload') })).toBeInTheDocument(); - expect(screen.getByRole('button', { name: textMock('app_data_modelling.landing_dialog_create') })).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: textMock('app_data_modelling.landing_dialog_upload') }), + ).toBeInTheDocument(); + expect( + screen.getByRole('button', { name: textMock('app_data_modelling.landing_dialog_create') }), + ).toBeInTheDocument(); }); it('opens create dialog when clicking create button', async () => { render(); - const button = screen.getByRole('button', { name: textMock('app_data_modelling.landing_dialog_create') }); + const button = screen.getByRole('button', { + name: textMock('app_data_modelling.landing_dialog_create'), + }); await act(() => user.click(button)); - expect(landingPagePropsMock.openCreateNew).toBeCalledTimes(1); + expect(landingPagePropsMock.openCreateNew).toHaveBeenCalledTimes(1); }); }); diff --git a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.test.tsx b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.test.tsx index 6fef2e09c3b..8f0e8b07542 100644 --- a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.test.tsx +++ b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/SelectedSchemaEditor.test.tsx @@ -36,8 +36,7 @@ jest.useFakeTimers({ advanceTimers: true }); describe('SelectedSchemaEditor', () => { it('Displays loading spinner while loading', () => { - const getDatamodel = jest.fn().mockImplementation(() => Promise.resolve({})); - render({ getDatamodel }); + render(); expect(screen.getByTitle(textMock('general.loading'))).toBeInTheDocument(); }); @@ -50,8 +49,7 @@ describe('SelectedSchemaEditor', () => { }); it('Renders SchemaEditorApp when finished loading', async () => { - const getDatamodel = jest.fn().mockImplementation(() => Promise.resolve({})); - render({ getDatamodel }); + render(); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('general.loading'))); expect(screen.getByTestId(schemaEditorTestId)).toBeInTheDocument(); }); diff --git a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/TopToolbar.test.tsx b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/TopToolbar.test.tsx index b6acabf2cd3..0c06394feea 100644 --- a/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/TopToolbar.test.tsx +++ b/frontend/app-development/features/dataModelling/SchemaEditorWithToolbar/TopToolbar/TopToolbar.test.tsx @@ -12,6 +12,7 @@ import { convertMetadataToOption } from '../../../../utils/metadataUtils'; import { buildJsonSchema } from '@altinn/schema-model'; import { renderWithMockStore } from '../../../../test/mocks'; import { useQueryClient } from '@tanstack/react-query'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; const user = userEvent.setup(); @@ -44,7 +45,6 @@ const defaultProps: TopToolbarProps = { }; const org = 'org'; const app = 'app'; -const generateModels = jest.fn().mockImplementation(() => Promise.resolve()); const modelPath = jsonMetadata1Mock.repositoryRelativeUrl; const renderToolbar = ( @@ -60,10 +60,7 @@ const renderToolbar = ( return ; }; - return renderWithMockStore( - {}, - { generateModels, ...servicesContextProps }, - )(); + return renderWithMockStore({}, { ...servicesContextProps })(); }; // Mocks: @@ -93,7 +90,7 @@ describe('TopToolbar', () => { const generateButton = screen.getByRole('button', { name: generateText }); expect(generateButton).toBeDefined(); await act(() => user.click(generateButton)); - expect(generateModels).toHaveBeenCalledTimes(1); + expect(queriesMock.generateModels).toHaveBeenCalledTimes(1); }); it('Does not show any error by default', () => { diff --git a/frontend/app-development/features/overview/components/AppEnvironments.test.tsx b/frontend/app-development/features/overview/components/AppEnvironments.test.tsx index f0392563b71..5ba72ba89fe 100644 --- a/frontend/app-development/features/overview/components/AppEnvironments.test.tsx +++ b/frontend/app-development/features/overview/components/AppEnvironments.test.tsx @@ -3,8 +3,8 @@ import { screen, waitForElementToBeRemoved } from '@testing-library/react'; import { AppEnvironments } from './AppEnvironments'; import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants'; import { renderWithProviders } from '../../../test/testUtils'; -import { queriesMock } from 'app-development/test/mocks'; import { textMock } from '../../../../testing/mocks/i18nMock'; +import { appDeployment, deployEnvironment } from 'app-shared/mocks/mocks'; // Test data const org = 'org'; @@ -13,19 +13,13 @@ const app = 'app'; const render = (queries = {}) => { return renderWithProviders(, { startUrl: `${APP_DEVELOPMENT_BASENAME}/${org}/${app}`, - queries: { - ...queriesMock, - ...queries, - }, + queries, }); }; describe('AppEnvironments', () => { it('shows loading spinner when loading required data', () => { - render({ - getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([])), - getOrgList: jest.fn().mockImplementation(() => Promise.resolve([])), - }); + render(); expect(screen.getByText(textMock('general.loading'))).toBeInTheDocument(); }); @@ -42,10 +36,7 @@ describe('AppEnvironments', () => { }); it('shows no environments message when organization has no environment', async () => { - render({ - getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([])), - getOrgList: jest.fn().mockImplementation(() => Promise.resolve({ orgs: [] })), - }); + render(); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('general.loading'))); @@ -58,26 +49,17 @@ describe('AppEnvironments', () => { it('shows statuses when organization has environments', async () => { const envName = 'tt02'; - const envType = 'test'; render({ getDeployments: jest.fn().mockImplementation(() => Promise.resolve({ results: [ { - tagName: '1', + ...appDeployment, envName, - deployedInEnv: false, build: { - id: '14381045', - status: 'completed', - result: 'succeeded', - started: '2023-10-03T09:57:31.238Z', + ...appDeployment.build, finished: '2023-10-03T09:57:41.29Z', }, - created: '2023-10-03T11:57:31.072013+02:00', - createdBy: 'test', - app, - org, }, ], }), @@ -86,13 +68,8 @@ describe('AppEnvironments', () => { getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([ { - appsUrl: 'http://host.docker.internal:6161', - platformUrl: 'http://host.docker.internal:6161', - hostname: 'host.docker.internal:6161', - appPrefix: 'apps', - platformPrefix: 'platform', + ...deployEnvironment, name: envName, - type: envType, }, ]), ), diff --git a/frontend/app-development/features/overview/components/AppLogs.test.tsx b/frontend/app-development/features/overview/components/AppLogs.test.tsx index 407d0c5b01d..2914dbd1615 100644 --- a/frontend/app-development/features/overview/components/AppLogs.test.tsx +++ b/frontend/app-development/features/overview/components/AppLogs.test.tsx @@ -3,8 +3,8 @@ import { screen, waitForElementToBeRemoved } from '@testing-library/react'; import { AppLogs } from './AppLogs'; import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants'; import { renderWithProviders } from '../../../test/testUtils'; -import { queriesMock } from 'app-development/test/mocks'; import { textMock } from '../../../../testing/mocks/i18nMock'; +import { appDeployment, deployEnvironment } from 'app-shared/mocks/mocks'; // Test data const org = 'ttd'; @@ -13,18 +13,13 @@ const app = 'test-ttd'; const render = (queries = {}) => { return renderWithProviders(, { startUrl: `${APP_DEVELOPMENT_BASENAME}/${org}/${app}`, - queries: { - ...queriesMock, - ...queries, - }, + queries, }); }; describe('AppLogs', () => { it('shows loading spinner when loading required data', () => { - render({ - getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([])), - }); + render(); expect(screen.getByText(textMock('general.loading'))).toBeInTheDocument(); }); @@ -45,36 +40,14 @@ describe('AppLogs', () => { Promise.resolve({ results: [ { + ...appDeployment, tagName: '2', envName: 'production', - deployedInEnv: true, - build: { - id: '14381045', - status: 'completed', - result: 'succeeded', - started: '2023-10-03T09:57:31.238Z', - finished: '2023-10-03T09:57:41.29Z', - }, - created: '2023-10-03T11:57:31.072013+02:00', - createdBy: 'test', - app, - org, }, { + ...appDeployment, tagName: '1', envName: 'tt02', - deployedInEnv: true, - build: { - id: '14381045', - status: 'completed', - result: 'succeeded', - started: '2023-10-03T09:57:31.238Z', - finished: '2023-10-03T09:57:41.29Z', - }, - created: '2023-10-03T11:57:31.072013+02:00', - createdBy: 'test', - app, - org, }, ], }), @@ -82,41 +55,15 @@ describe('AppLogs', () => { getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([ { - appsUrl: 'http://host.docker.internal:6161', - platformUrl: 'http://host.docker.internal:6161', - hostname: 'host.docker.internal:6161', - appPrefix: 'apps', - platformPrefix: 'platform', + ...deployEnvironment, name: 'production', type: 'production', }, { - appsUrl: 'http://host.docker.internal:6161', - platformUrl: 'http://host.docker.internal:6161', - hostname: 'host.docker.internal:6161', - appPrefix: 'apps', - platformPrefix: 'platform', + ...deployEnvironment, name: 'tt02', type: 'test', }, - { - appsUrl: 'http://host.docker.internal:6161', - platformUrl: 'http://host.docker.internal:6161', - hostname: 'host.docker.internal:6161', - appPrefix: 'apps', - platformPrefix: 'platform', - name: 'at21', - type: 'test', - }, - { - appsUrl: 'http://host.docker.internal:6161', - platformUrl: 'http://host.docker.internal:6161', - hostname: 'host.docker.internal:6161', - appPrefix: 'apps', - platformPrefix: 'platform', - name: 'at22', - type: 'test', - }, ]), ), }); @@ -146,62 +93,14 @@ describe('AppLogs', () => { Promise.resolve({ results: [ { - tagName: '2', - envName: 'production', - deployedInEnv: true, + ...appDeployment, build: { - id: '14381045', - status: 'completed', result: '', - started: '2023-10-03T09:57:31.238Z', - finished: '2023-10-03T09:57:41.29Z', - }, - created: '2023-10-03T11:57:31.072013+02:00', - createdBy: 'test', - app, - org, - }, - { - tagName: '1', - envName: 'tt02', - deployedInEnv: true, - build: { - id: '14381045', - status: 'completed', - result: 'succeeded', - started: '2023-10-03T09:57:31.238Z', - finished: null, }, - created: '2023-10-03T11:57:31.072013+02:00', - createdBy: 'test', - app, - org, }, ], }), ), - getEnvironments: jest.fn().mockImplementation(() => - Promise.resolve([ - { - appsUrl: 'http://host.docker.internal:6161', - platformUrl: 'http://host.docker.internal:6161', - hostname: 'host.docker.internal:6161', - appPrefix: 'apps', - platformPrefix: 'platform', - name: 'production', - type: 'production', - }, - { - appsUrl: 'http://host.docker.internal:6161', - platformUrl: 'http://host.docker.internal:6161', - hostname: 'host.docker.internal:6161', - appPrefix: 'apps', - platformPrefix: 'platform', - name: 'tt02', - type: 'test', - }, - ]), - ), }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('general.loading'))); diff --git a/frontend/app-development/features/overview/components/AppStatus.test.tsx b/frontend/app-development/features/overview/components/AppStatus.test.tsx index f2f72f10448..79285f81577 100644 --- a/frontend/app-development/features/overview/components/AppStatus.test.tsx +++ b/frontend/app-development/features/overview/components/AppStatus.test.tsx @@ -3,8 +3,8 @@ import { screen, waitForElementToBeRemoved } from '@testing-library/react'; import { AppStatus } from './AppStatus'; import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants'; import { renderWithProviders } from '../../../test/testUtils'; -import { queriesMock } from 'app-development/test/mocks'; import { textMock } from '../../../../testing/mocks/i18nMock'; +import { appDeployment } from 'app-shared/mocks/mocks'; // Test data const org = 'ttd'; @@ -15,10 +15,7 @@ const envTypeTest = 'test'; const render = (queries = {}, envName = envNameTest, envType = envTypeTest) => { return renderWithProviders(, { startUrl: `${APP_DEVELOPMENT_BASENAME}/${org}/${app}`, - queries: { - ...queriesMock, - ...queries, - }, + queries, }); }; @@ -48,20 +45,8 @@ describe('AppStatus', () => { Promise.resolve({ results: [ { - tagName: '1', - envName: envNameProduction, + ...appDeployment, deployedInEnv: true, - build: { - id: '14381045', - status: 'completed', - result: 'succeeded', - started: '2023-10-03T09:57:31.238Z', - finished: '2023-10-03T09:57:41.29Z', - }, - created: '2023-10-03T11:57:31.072013+02:00', - createdBy: 'test', - app, - org, }, ], }), @@ -84,20 +69,9 @@ describe('AppStatus', () => { Promise.resolve({ results: [ { - tagName: '1', + ...appDeployment, envName: envNameTest, deployedInEnv: true, - build: { - id: '14381045', - status: 'completed', - result: 'succeeded', - started: '2023-10-03T09:57:31.238Z', - finished: '2023-10-03T09:57:41.29Z', - }, - created: '2023-10-03T11:57:31.072013+02:00', - createdBy: 'test', - app, - org, }, ], }), @@ -112,13 +86,7 @@ describe('AppStatus', () => { }); it('shows no app alert when application not deployed', async () => { - render({ - getDeployments: jest.fn().mockImplementation(() => - Promise.resolve({ - results: [], - }), - ), - }); + render(); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('general.loading'))); @@ -133,20 +101,12 @@ describe('AppStatus', () => { Promise.resolve({ results: [ { - tagName: '1', + ...appDeployment, envName: envNameTest, - deployedInEnv: false, build: { - id: '14381045', - status: 'completed', - result: 'succeeded', - started: '2023-10-03T09:57:31.238Z', + ...appDeployment.build, finished: '2023-10-03T09:57:41.29Z', }, - created: '2023-10-03T11:57:31.072013+02:00', - createdBy: 'test', - app, - org, }, ], }), diff --git a/frontend/app-development/features/overview/components/Documentation.test.tsx b/frontend/app-development/features/overview/components/Documentation.test.tsx index f62cd7b4b91..d980b5d60b5 100644 --- a/frontend/app-development/features/overview/components/Documentation.test.tsx +++ b/frontend/app-development/features/overview/components/Documentation.test.tsx @@ -4,7 +4,6 @@ import { Documentation } from './Documentation'; import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants'; import { renderWithProviders } from '../../../test/testUtils'; import { textMock } from '../../../../testing/mocks/i18nMock'; -import { queriesMock } from 'app-development/test/mocks'; // Test data const org = 'my-org'; @@ -14,9 +13,6 @@ describe('Documentation', () => { it('renders component', async () => { renderWithProviders(, { startUrl: `${APP_DEVELOPMENT_BASENAME}/${org}/${app}`, - queries: { - ...queriesMock, - }, }); expect( diff --git a/frontend/app-development/features/overview/components/Overview.test.tsx b/frontend/app-development/features/overview/components/Overview.test.tsx index 168c2e3153a..b96200c2d4a 100644 --- a/frontend/app-development/features/overview/components/Overview.test.tsx +++ b/frontend/app-development/features/overview/components/Overview.test.tsx @@ -3,39 +3,26 @@ import { screen } from '@testing-library/react'; import { Overview } from './Overview'; import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants'; import { renderWithProviders } from '../../../test/testUtils'; -import { queriesMock } from 'app-development/test/mocks'; import { textMock } from '../../../../testing/mocks/i18nMock'; -import { privateRepositoryMock, repositoryMock } from '../../../test/repositoryMock'; +import { repository } from 'app-shared/mocks/mocks'; // Test data const org = 'org'; const app = 'app'; const title = 'test'; -// Mocking console.error due to Tanstack Query removing custom logger between V4 and v5 see issue: #11692 -const realConsole = console; - describe('Overview', () => { - beforeEach(() => { - global.console = { - ...console, - error: jest.fn(), - }; - }); afterEach(() => { - global.console = realConsole; jest.clearAllMocks(); }); it('renders component', async () => { render({ - getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([])), getOrgList: jest.fn().mockImplementation(() => Promise.resolve({ orgs: [org] })), getAppConfig: jest.fn().mockImplementation(() => Promise.resolve({ serviceName: title, }), ), - getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve(repositoryMock)), }); expect(await screen.findByRole('heading', { name: title })).toBeInTheDocument(); @@ -66,30 +53,15 @@ describe('Overview', () => { }, }), ), - getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve(repositoryMock)), - getDeployments: jest.fn().mockImplementation(() => + getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve({ - results: [ - { - tagName: '1', - envName: 'test', - deployedInEnv: true, - build: { - id: '1', - status: 'completed', - result: 'succeeded', - started: '2023-10-03T09:57:31.238Z', - finished: null, - }, - created: '2023-10-03T11:57:31.072013+02:00', - createdBy: 'test', - app, - org, - }, - ], + ...repository, + owner: { + ...repository.owner, + login: org, + }, }), ), - getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([{}])), }); expect( await screen.findByRole('heading', { name: textMock('overview.activity') }), @@ -98,7 +70,15 @@ describe('Overview', () => { it('should not display AppLogs if environments do not exist for repo owned by org', async () => { render({ - getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve(repositoryMock)), + getRepoMetadata: jest.fn().mockImplementation(() => + Promise.resolve({ + ...repository, + owner: { + ...repository.owner, + login: org, + }, + }), + ), getOrgList: jest.fn().mockImplementation(() => Promise.resolve({ orgs: { @@ -108,7 +88,6 @@ describe('Overview', () => { }, }), ), - getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([])), }); expect(await screen.findByText(textMock('app_publish.no_env_title'))).toBeInTheDocument(); expect( @@ -117,14 +96,7 @@ describe('Overview', () => { }); it('should display RepoOwnedByPersonInfo if repo is not owned by an org', async () => { - render({ - getOrgList: jest.fn().mockImplementation(() => - Promise.resolve({ - orgs: {}, - }), - ), - getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve(privateRepositoryMock)), - }); + render(); expect(await screen.findByText(textMock('app_publish.private_app_owner'))).toBeInTheDocument(); }); }); @@ -132,9 +104,6 @@ describe('Overview', () => { const render = (queries = {}) => { return renderWithProviders(, { startUrl: `${APP_DEVELOPMENT_BASENAME}/${org}/${app}`, - queries: { - ...queriesMock, - ...queries, - }, + queries, }); }; diff --git a/frontend/app-development/features/textEditor/TextEditor.test.tsx b/frontend/app-development/features/textEditor/TextEditor.test.tsx index f7484a071f0..aa1242afc12 100644 --- a/frontend/app-development/features/textEditor/TextEditor.test.tsx +++ b/frontend/app-development/features/textEditor/TextEditor.test.tsx @@ -7,6 +7,7 @@ import { textMock } from '../../../testing/mocks/i18nMock'; import { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import userEvent from '@testing-library/user-event'; import * as testids from '../../../testing/testids'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; // Test data const org = 'test-org'; @@ -15,19 +16,17 @@ const testTextResourceKey = 'test-key'; const testTextResourceValue = 'test-value'; const languages = ['nb', 'en']; -const queriesMock: Partial = { - getTextResources: jest.fn().mockImplementation(() => - Promise.resolve({ - resources: [ - { - id: testTextResourceKey, - value: testTextResourceValue, - }, - ], - }) - ), - getTextLanguages: jest.fn().mockImplementation(() => Promise.resolve(languages)), -}; +const getTextResources = jest.fn().mockImplementation(() => + Promise.resolve({ + resources: [ + { + id: testTextResourceKey, + value: testTextResourceValue, + }, + ], + }), +); +const getTextLanguages = jest.fn().mockImplementation(() => Promise.resolve(languages)); const mockSetSearchParams = jest.fn(); jest.mock('react-router-dom', () => ({ @@ -60,37 +59,33 @@ describe('TextEditor', () => { it('adds new text resource when clicking add button', async () => { const user = userEvent.setup(); - const upsertTextResources = jest.fn().mockImplementation(() => Promise.resolve()); - - await render({ upsertTextResources }); + await render(); const addButton = screen.getByRole('button', { name: textMock('text_editor.new_text') }); await act(() => user.click(addButton)); - expect(upsertTextResources).toBeCalledTimes(2); + expect(queriesMock.upsertTextResources).toHaveBeenCalledTimes(2); }); it('updates text resource when editing text', async () => { const user = userEvent.setup(); - const upsertTextResources = jest.fn().mockImplementation(() => Promise.resolve()); - - await render({ upsertTextResources }); + await render(); const textarea = screen.getByRole('textbox', { name: 'nb translation' }); await act(() => user.clear(textarea)); await act(() => user.type(textarea, 'test')); await act(() => user.tab()); - expect(upsertTextResources).toBeCalledWith(org, app, 'nb', { [testTextResourceKey]: 'test' }); + expect(queriesMock.upsertTextResources).toHaveBeenCalledWith(org, app, 'nb', { + [testTextResourceKey]: 'test', + }); }); it('updates text id when editing text id', async () => { const user = userEvent.setup(); - const updateTextId = jest.fn().mockImplementation(() => Promise.resolve()); - - await render({ updateTextId }); + await render(); const editButton = screen.getByRole('button', { name: 'toggle-textkey-edit' }); await act(() => editButton.click()); @@ -100,15 +95,15 @@ describe('TextEditor', () => { await act(() => user.type(textarea, 'test')); await act(() => user.tab()); - expect(updateTextId).toBeCalledWith(org, app, [{ newId: 'test', oldId: testTextResourceKey }]); + expect(queriesMock.updateTextId).toHaveBeenCalledWith(org, app, [ + { newId: 'test', oldId: testTextResourceKey }, + ]); }); it('deletes text id when clicking delete button', async () => { const user = userEvent.setup(); - const updateTextId = jest.fn().mockImplementation(() => Promise.resolve()); - - await render({ updateTextId }); + await render(); const deleteButton = screen.getByRole('button', { name: textMock('schema_editor.delete') }); await act(() => deleteButton.click()); @@ -118,15 +113,15 @@ describe('TextEditor', () => { }); await act(() => user.click(confirmButton)); - expect(updateTextId).toBeCalledWith(org, app, [{ oldId: testTextResourceKey }]); + expect(queriesMock.updateTextId).toHaveBeenCalledWith(org, app, [ + { oldId: testTextResourceKey }, + ]); }); it('adds new language when clicking add button', async () => { const user = userEvent.setup(); - const addLanguageCode = jest.fn().mockImplementation(() => Promise.resolve()); - - await render({ addLanguageCode }); + await render(); const addBtn = screen.getByRole('button', { name: /legg til/i, @@ -140,7 +135,7 @@ describe('TextEditor', () => { expect(addBtn).not.toBeDisabled(); await act(() => user.click(addBtn)); - expect(addLanguageCode).toBeCalledWith(org, app, 'se', { + expect(queriesMock.addLanguageCode).toHaveBeenCalledWith(org, app, 'se', { language: 'se', resources: [{ id: testTextResourceKey, value: '' }], }); @@ -149,9 +144,7 @@ describe('TextEditor', () => { it('deletes a language when clicking delete button', async () => { const user = userEvent.setup(); - const deleteLanguageCode = jest.fn().mockImplementation(() => Promise.resolve()); - - await render({ deleteLanguageCode }); + await render(); const deleteButton = screen.getByTestId(testids.deleteButton('en')); await act(() => user.click(deleteButton)); @@ -161,7 +154,7 @@ describe('TextEditor', () => { }); await act(() => user.click(confirmButton)); - expect(deleteLanguageCode).toBeCalledWith(org, app, 'en'); + expect(queriesMock.deleteLanguageCode).toHaveBeenCalledWith(org, app, 'en'); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); @@ -177,7 +170,8 @@ describe('TextEditor', () => { const render = async (queries: Partial = {}) => { const view = renderWithProviders(, { queries: { - ...queriesMock, + getTextResources, + getTextLanguages, ...queries, }, startUrl: `${APP_DEVELOPMENT_BASENAME}/${org}/${app}`, diff --git a/frontend/app-development/hooks/mutations/useBpmnMutation.ts b/frontend/app-development/hooks/mutations/useBpmnMutation.ts index 95bb6b2ddc8..2059634fdb3 100644 --- a/frontend/app-development/hooks/mutations/useBpmnMutation.ts +++ b/frontend/app-development/hooks/mutations/useBpmnMutation.ts @@ -1,5 +1,5 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { updateBpmnXml } from 'app-shared/api/queries'; +import { updateBpmnXml } from 'app-shared/api/mutations'; import { QueryKey } from 'app-shared/types/QueryKey'; type UseBpmnMutationPayload = { diff --git a/frontend/app-development/hooks/mutations/useResetRepositoryMutation.test.ts b/frontend/app-development/hooks/mutations/useResetRepositoryMutation.test.ts index 0de0601b572..5cfa8dac575 100644 --- a/frontend/app-development/hooks/mutations/useResetRepositoryMutation.test.ts +++ b/frontend/app-development/hooks/mutations/useResetRepositoryMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, renderHookWithMockStore } from '../../test/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../test/mocks'; import { useResetRepositoryMutation } from './useResetRepositoryMutation'; // Test data: diff --git a/frontend/app-development/hooks/queries/useAppDeploymentsQuery.ts b/frontend/app-development/hooks/queries/useAppDeploymentsQuery.ts index 0d568db1554..535aa3568e3 100644 --- a/frontend/app-development/hooks/queries/useAppDeploymentsQuery.ts +++ b/frontend/app-development/hooks/queries/useAppDeploymentsQuery.ts @@ -13,7 +13,7 @@ export const useAppDeploymentsQuery = ( const { getDeployments } = useServicesContext(); return useQuery({ queryKey: [QueryKey.AppDeployments, owner, app], - queryFn: () => getDeployments(owner, app).then((res) => res.results), + queryFn: () => getDeployments(owner, app).then((res) => res?.results || []), refetchInterval: DEPLOYMENTS_REFETCH_INTERVAL, meta, }); diff --git a/frontend/app-development/hooks/queries/useAppLibVersionQuery.ts b/frontend/app-development/hooks/queries/useAppLibVersionQuery.ts index 744b09bd3c3..ae91b27ed95 100644 --- a/frontend/app-development/hooks/queries/useAppLibVersionQuery.ts +++ b/frontend/app-development/hooks/queries/useAppLibVersionQuery.ts @@ -1,5 +1,6 @@ import { useQuery } from '@tanstack/react-query'; import { getAppLibVersion } from 'app-shared/api/queries'; +import { AppLibVersion } from 'app-shared/types/AppLibVersion'; import { QueryKey } from 'app-shared/types/QueryKey'; /** @@ -11,7 +12,7 @@ import { QueryKey } from 'app-shared/types/QueryKey'; * @returns UseQueryResult with the version */ export const useAppLibVersionQuery = (org: string, repo: string) => { - return useQuery<{ version: string }>({ + return useQuery({ queryKey: [QueryKey.AppLibVersion, org, repo], queryFn: () => getAppLibVersion(org, repo), }); diff --git a/frontend/app-development/hooks/queries/useSchemaQuery.test.ts b/frontend/app-development/hooks/queries/useSchemaQuery.test.ts index 742f5c2bca0..1297d20f377 100644 --- a/frontend/app-development/hooks/queries/useSchemaQuery.test.ts +++ b/frontend/app-development/hooks/queries/useSchemaQuery.test.ts @@ -2,13 +2,12 @@ import { waitFor } from '@testing-library/react'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { useSchemaQuery } from './useSchemaQuery'; import { renderHookWithMockStore } from '../../test/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; // Test data: const jsonModelPathWithSlash = '/App/models/model.schema.json'; const xsdModelPath = 'App/models/model.xsd'; const xsdModelPathWithSlash = '/' + xsdModelPath; -const getDatamodel = jest.fn().mockImplementation(() => Promise.resolve({})); -const addXsdFromRepo = jest.fn().mockImplementation(() => Promise.resolve({})); const org = 'org'; const app = 'app'; @@ -16,26 +15,30 @@ describe('useSchemaQuery', () => { afterEach(jest.clearAllMocks); it('Calls getDatamodel with correct arguments when Json Schema', async () => { - const { renderHookResult: { result } } = renderHookWithMockStore( + const { + renderHookResult: { result }, + } = renderHookWithMockStore( + {}, {}, - { getDatamodel, addXsdFromRepo }, createQueryClientMock(), )(() => useSchemaQuery(jsonModelPathWithSlash)); await waitFor(() => expect(result.current.isSuccess).toBe(true)); - expect(getDatamodel).toHaveBeenCalledTimes(1); - expect(getDatamodel).toHaveBeenCalledWith(org, app, jsonModelPathWithSlash); - expect(addXsdFromRepo).not.toHaveBeenCalled(); + expect(queriesMock.getDatamodel).toHaveBeenCalledTimes(1); + expect(queriesMock.getDatamodel).toHaveBeenCalledWith(org, app, jsonModelPathWithSlash); + expect(queriesMock.addXsdFromRepo).not.toHaveBeenCalled(); }); it('Calls addXsdFromRepo with correct arguments when XSD', async () => { - const { renderHookResult: { result } } = renderHookWithMockStore( + const { + renderHookResult: { result }, + } = renderHookWithMockStore( + {}, {}, - { getDatamodel, addXsdFromRepo }, createQueryClientMock(), )(() => useSchemaQuery(xsdModelPathWithSlash)); await waitFor(() => expect(result.current.isSuccess).toBe(true)); - expect(addXsdFromRepo).toHaveBeenCalledTimes(1); - expect(addXsdFromRepo).toHaveBeenCalledWith(org, app, xsdModelPath); - expect(getDatamodel).not.toHaveBeenCalled(); + expect(queriesMock.addXsdFromRepo).toHaveBeenCalledTimes(1); + expect(queriesMock.addXsdFromRepo).toHaveBeenCalledWith(org, app, xsdModelPath); + expect(queriesMock.getDatamodel).not.toHaveBeenCalled(); }); }); diff --git a/frontend/app-development/layout/App.test.tsx b/frontend/app-development/layout/App.test.tsx index 11da8bf7a4f..292b9f133b3 100644 --- a/frontend/app-development/layout/App.test.tsx +++ b/frontend/app-development/layout/App.test.tsx @@ -6,17 +6,13 @@ import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants'; import { renderWithProviders } from '../test/testUtils'; import * as testids from '../../testing/testids'; import { textMock } from '../../testing/mocks/i18nMock'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; jest.mock('../../language/src/nb.json', jest.fn()); jest.mock('../../language/src/en.json', jest.fn()); -// Mocking console.error due to Tanstack Query removing custom logger between V4 and v5 see issue: #11692 -const realConsole = console; const render = async (remainingMinutes: number = 40) => { renderWithProviders(, { startUrl: `${APP_DEVELOPMENT_BASENAME}/my-org/my-app`, - queries: { ...queriesMock }, preloadedState: { userState: { session: { @@ -28,14 +24,7 @@ const render = async (remainingMinutes: number = 40) => { }; describe('App', () => { - beforeEach(() => { - global.console = { - ...console, - error: jest.fn(), - }; - }); afterEach(() => { - global.console = realConsole; jest.clearAllMocks(); }); diff --git a/frontend/app-development/layout/PageHeader.tsx b/frontend/app-development/layout/PageHeader.tsx index 7cad574e891..ce168959922 100644 --- a/frontend/app-development/layout/PageHeader.tsx +++ b/frontend/app-development/layout/PageHeader.tsx @@ -7,7 +7,7 @@ import { AltinnButtonActionItem } from 'app-shared/components/altinnHeader/types import { GiteaHeader } from 'app-shared/components/GiteaHeader'; import { SettingsModalButton } from './SettingsModalButton'; import { TopBarMenu } from 'app-shared/enums/TopBarMenu'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { PackagesRouter } from 'app-shared/navigation/PackagesRouter'; type SubMenuContentProps = { diff --git a/frontend/app-development/layout/PageLayout.test.tsx b/frontend/app-development/layout/PageLayout.test.tsx index 28f8bd77e70..1ea410fa0ef 100644 --- a/frontend/app-development/layout/PageLayout.test.tsx +++ b/frontend/app-development/layout/PageLayout.test.tsx @@ -4,37 +4,13 @@ import { screen, waitForElementToBeRemoved } from '@testing-library/react'; import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants'; import { renderWithProviders } from '../test/testUtils'; import { textMock } from '../../testing/mocks/i18nMock'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; -import { RepoStatus } from 'app-shared/types/RepoStatus'; -import { User } from 'app-shared/types/User'; import { RoutePaths } from 'app-development/enums/RoutePaths'; -import { privateRepositoryMock, repositoryMock } from '../test/repositoryMock'; +import { repoStatus } from 'app-shared/mocks/mocks'; const mockOrg: string = 'org'; const mockApp: string = 'app'; -const getRepoStatus = jest.fn().mockImplementation(() => Promise.resolve({})); -const getUser = jest.fn().mockImplementation(() => Promise.resolve({})); -const getRepoMetadata = jest.fn().mockImplementation(() => Promise.resolve(repositoryMock)); -const getOrgList = jest.fn().mockImplementation(() => Promise.resolve({ orgs: [] })); - -const mockRepoStatus: RepoStatus = { - aheadBy: 0, - behindBy: 0, - contentStatus: [], - hasMergeConflict: false, - repositoryStatus: 'Ok', -}; - -const mockUser: User = { - avatar_url: 'test', - email: 'test@test.com', - full_name: 'Mock Tester', - id: 1, - login: 'MT1', -}; - jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useParams: () => ({ @@ -43,18 +19,8 @@ jest.mock('react-router-dom', () => ({ }), })); -// Mocking console.error due to Tanstack Query removing custom logger between V4 and v5 see issue: #11692 -const realConsole = console; - describe('PageLayout', () => { - beforeEach(() => { - global.console = { - ...console, - error: jest.fn(), - }; - }); afterEach(() => { - global.console = realConsole; jest.clearAllMocks(); }); @@ -77,7 +43,7 @@ describe('PageLayout', () => { it('renders "MergeConflictWarning" when repoStatus has merge conflict', async () => { render({ - getRepoStatus: () => Promise.resolve({ ...mockRepoStatus, hasMergeConflict: true }), + getRepoStatus: () => Promise.resolve({ ...repoStatus, hasMergeConflict: true }), }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('general.loading'))); @@ -99,9 +65,7 @@ describe('PageLayout', () => { }); it('renders header with no publish button when repoOwner is a private person', async () => { - await resolveAndWaitForSpinnerToDisappear({ - getRepoMetadata: () => Promise.resolve(privateRepositoryMock), - }); + await resolveAndWaitForSpinnerToDisappear(); expect(screen.getByRole('button', { name: textMock('top_menu.preview') })).toBeInTheDocument(); @@ -112,25 +76,13 @@ describe('PageLayout', () => { }); const resolveAndWaitForSpinnerToDisappear = async (queries: Partial = {}) => { - getRepoStatus.mockImplementation(() => Promise.resolve(mockRepoStatus)); - getUser.mockImplementation(() => Promise.resolve(mockUser)); - render(queries); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('general.loading'))); }; const render = async (queries: Partial = {}) => { - const allQueries: ServicesContextProps = { - ...queriesMock, - getRepoStatus, - getUser, - getOrgList, - getRepoMetadata, - ...queries, - }; - renderWithProviders(, { startUrl: `${APP_DEVELOPMENT_BASENAME}/my-org/my-app/${RoutePaths.Overview}`, - queries: allQueries, + queries, }); }; diff --git a/frontend/app-development/layout/SettingsModalButton/SettingsModal/SettingsModal.test.tsx b/frontend/app-development/layout/SettingsModalButton/SettingsModal/SettingsModal.test.tsx index c054f5d70e2..57753d51fc4 100644 --- a/frontend/app-development/layout/SettingsModalButton/SettingsModal/SettingsModal.test.tsx +++ b/frontend/app-development/layout/SettingsModalButton/SettingsModal/SettingsModal.test.tsx @@ -11,7 +11,6 @@ import { textMock } from '../../../../testing/mocks/i18nMock'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { QueryClient, UseMutationResult } from '@tanstack/react-query'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { AppConfig } from 'app-shared/types/AppConfig'; import { useAppConfigMutation } from 'app-development/hooks/mutations'; import { MemoryRouter } from 'react-router-dom'; @@ -27,9 +26,6 @@ jest.mock('react-router-dom', () => ({ }), })); -// Mocking console.error due to Tanstack Query removing custom logger between V4 and v5 see issue: #11692 -const realConsole = console; - jest.mock('../../../hooks/mutations/useAppConfigMutation'); const updateAppConfigMutation = jest.fn(); const mockUpdateAppConfigMutation = useAppConfigMutation as jest.MockedFunction< @@ -41,14 +37,7 @@ mockUpdateAppConfigMutation.mockReturnValue({ describe('SettingsModal', () => { const user = userEvent.setup(); - beforeEach(() => { - global.console = { - ...console, - error: jest.fn(), - }; - }); afterEach(() => { - global.console = realConsole; jest.clearAllMocks(); }); @@ -255,13 +244,9 @@ const render = ( queries: Partial = {}, queryClient: QueryClient = createQueryClientMock(), ) => { - const allQueries: ServicesContextProps = { - ...queriesMock, - ...queries, - }; return rtlRender( - + , diff --git a/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/AccessControlTab/AccessControlTab.test.tsx b/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/AccessControlTab/AccessControlTab.test.tsx index 0689e9ff5e1..fad6454ae40 100644 --- a/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/AccessControlTab/AccessControlTab.test.tsx +++ b/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/AccessControlTab/AccessControlTab.test.tsx @@ -8,7 +8,6 @@ import { import { AccessControlTab, AccessControlTabProps } from './AccessControlTab'; import { textMock } from '../../../../../../../testing/mocks/i18nMock'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { QueryClient, UseMutationResult } from '@tanstack/react-query'; import userEvent from '@testing-library/user-event'; @@ -138,7 +137,6 @@ const render = ( queryClient: QueryClient = createQueryClientMock(), ) => { const allQueries: ServicesContextProps = { - ...queriesMock, getAppMetadata, ...queries, }; diff --git a/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/LocalChangesTab/LocalChangesTab.test.tsx b/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/LocalChangesTab/LocalChangesTab.test.tsx index 842ee1355dd..62088bb795c 100644 --- a/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/LocalChangesTab/LocalChangesTab.test.tsx +++ b/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/LocalChangesTab/LocalChangesTab.test.tsx @@ -3,7 +3,6 @@ import { act, render as rtlRender, screen } from '@testing-library/react'; import { LocalChangesTab, LocalChangesTabProps } from './LocalChangesTab'; import { textMock } from '../../../../../../../testing/mocks/i18nMock'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { QueryClient, UseMutationResult } from '@tanstack/react-query'; import userEvent from '@testing-library/user-event'; @@ -137,13 +136,8 @@ const render = ( queryClient: QueryClient = createQueryClientMock(), props: LocalChangesTabProps, ) => { - const allQueries: ServicesContextProps = { - ...queriesMock, - ...queries, - }; - return rtlRender( - + , ); diff --git a/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/SetupTab/SetupTab.test.tsx b/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/SetupTab/SetupTab.test.tsx index b0897e2c578..91c41218c10 100644 --- a/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/SetupTab/SetupTab.test.tsx +++ b/frontend/app-development/layout/SettingsModalButton/SettingsModal/components/Tabs/SetupTab/SetupTab.test.tsx @@ -14,22 +14,13 @@ const mockApp: string = 'testApp'; const getAppMetadata = jest.fn().mockImplementation(() => Promise.resolve({})); -// Mocking console.error due to Tanstack Query removing custom logger between V4 and v5 see issue: #11692 -const realConsole = console; const defaultProps: SetupTabProps = { org: mockOrg, app: mockApp, }; describe('SetupTab Component', () => { - beforeEach(() => { - global.console = { - ...console, - error: jest.fn(), - }; - }); afterEach(() => { - global.console = realConsole; jest.clearAllMocks(); }); diff --git a/frontend/app-development/layout/SettingsModalButton/SettingsModal/mocks/repositoryMock.ts b/frontend/app-development/layout/SettingsModalButton/SettingsModal/mocks/repositoryMock.ts index 4e5b8fba92a..313c4eb1e5d 100644 --- a/frontend/app-development/layout/SettingsModalButton/SettingsModal/mocks/repositoryMock.ts +++ b/frontend/app-development/layout/SettingsModalButton/SettingsModal/mocks/repositoryMock.ts @@ -1,41 +1,15 @@ +import { repository } from 'app-shared/mocks/mocks'; import { Repository } from 'app-shared/types/Repository'; export const mockRepository1: Repository = { - clone_url: '', + ...repository, created_at: '2023-09-20T10:40:00Z', - default_branch: '', - description: '', - empty: false, - fork: false, - forks_count: 0, - full_name: '', - html_url: '', - id: 123, - is_cloned_to_local: true, - mirror: false, name: 'CoolService', - open_issues_count: 0, owner: { - avatar_url: '', - email: '', + ...repository.owner, full_name: 'Mons Monsen', - id: 234, login: 'Mons', - UserType: 2, }, - permissions: { - admin: true, - pull: true, - push: true, - }, - private: false, - repositoryCreatedStatus: 0, - size: 0, - ssh_url: '', - stars_count: 1337, - updated_at: '', - watchers_count: 0, - website: '', }; export const mockRepository2: Repository = { diff --git a/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.test.tsx b/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.test.tsx index c3c2a60c261..4f18f8ac2ad 100644 --- a/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.test.tsx +++ b/frontend/app-development/layout/SettingsModalButton/SettingsModalButton.test.tsx @@ -12,12 +12,6 @@ import { MemoryRouter } from 'react-router-dom'; const mockApp: string = 'app'; const mockOrg: string = 'org'; -const getAppPolicy = jest.fn().mockImplementation(() => Promise.resolve({})); -const getAppConfig = jest.fn().mockImplementation(() => Promise.resolve({})); -const getRepoMetadata = jest.fn().mockImplementation(() => Promise.resolve({})); -const getRepoInitialCommit = jest.fn().mockImplementation(() => Promise.resolve({})); -const getAppMetadata = jest.fn().mockImplementation(() => Promise.resolve({})); - const defaultProps: SettingsModalButtonProps = { org: mockOrg, app: mockApp, @@ -85,11 +79,6 @@ const render = ( ) => { const allQueries: ServicesContextProps = { ...queriesMock, - getAppPolicy, - getAppConfig, - getRepoMetadata, - getRepoInitialCommit, - getAppMetadata, ...queries, }; return rtlRender( diff --git a/frontend/app-development/test/mocks.tsx b/frontend/app-development/test/mocks.tsx index 3939bcdfb40..b4d3e839495 100644 --- a/frontend/app-development/test/mocks.tsx +++ b/frontend/app-development/test/mocks.tsx @@ -8,49 +8,69 @@ import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contex import { BrowserRouter } from 'react-router-dom'; import { PreviewConnectionContextProvider } from 'app-shared/providers/PreviewConnectionContext'; -import { queriesMock as allQueriesMock } from 'app-shared/mocks/queriesMock'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; import { rootStateMock } from './rootStateMock'; import { QueryClient } from '@tanstack/react-query'; import { queryClientConfigMock } from 'app-shared/mocks/queryClientMock'; export const textLanguagesMock = ['nb', 'nn', 'en']; -export const queriesMock: ServicesContextProps = { - ...allQueriesMock, -}; - export const renderWithMockStore = - (state: Partial = {}, queries: Partial = {}, queryClient?: QueryClient) => + ( + state: Partial = {}, + queries: Partial = {}, + queryClient?: QueryClient, + ) => (component: ReactNode) => { const store = configureStore()({ ...rootStateMock, ...state }); const renderResult = render( - + {component} - - ); - const rerender = (rerenderedComponent) => renderResult.rerender( - - - - {rerenderedComponent} - - - + , ); + const rerender = (rerenderedComponent) => + renderResult.rerender( + + + + {rerenderedComponent} + + + , + ); return { renderResult: { ...renderResult, rerender }, store }; }; export const renderHookWithMockStore = - (state: Partial = {}, queries: Partial = {}, queryClient?: QueryClient) => + ( + state: Partial = {}, + queries: Partial = {}, + queryClient?: QueryClient, + ) => (hook: () => any) => { const store = configureStore()({ ...rootStateMock, ...state }); const renderHookResult = renderHook(hook, { wrapper: ({ children }) => ( - + {children} diff --git a/frontend/app-development/test/repositoryMock.ts b/frontend/app-development/test/repositoryMock.ts deleted file mode 100644 index 360903d0564..00000000000 --- a/frontend/app-development/test/repositoryMock.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { Repository } from 'app-shared/types/Repository'; - -export const repositoryMock: Repository = { - clone_url: '', - created_at: '', - default_branch: 'test', - description: '', - empty: false, - fork: false, - forks_count: 0, - full_name: 'app', - html_url: '', - id: 0, - is_cloned_to_local: false, - mirror: false, - name: 'test', - open_issues_count: 0, - owner: { - avatar_url: '', - email: '', - full_name: 'Testdepartementet', - id: 1, - login: 'org', - UserType: 0, - }, - permissions: { - admin: true, - pull: true, - push: true, - }, - private: false, - repositoryCreatedStatus: 0, - size: 1, - ssh_url: '', - stars_count: 0, - updated_at: '', - watchers_count: 0, - website: '', -}; - -export const privateRepositoryMock: Repository = { - clone_url: '', - created_at: '', - default_branch: 'test', - description: '', - empty: false, - fork: false, - forks_count: 0, - full_name: 'privateApp', - html_url: '', - id: 1, - is_cloned_to_local: false, - mirror: false, - name: 'privateApp', - open_issues_count: 0, - owner: { - avatar_url: '', - email: 'tester@tester.test', - full_name: 'Tester Testersen', - id: 3, - login: 'tester', - UserType: 0, - }, - permissions: { - admin: true, - pull: true, - push: true, - }, - private: false, - repositoryCreatedStatus: 0, - size: 1, - ssh_url: '', - stars_count: 0, - updated_at: '', - watchers_count: 0, - website: '', -}; diff --git a/frontend/app-development/test/rootStateMock.ts b/frontend/app-development/test/rootStateMock.ts index 35263d8350d..80bf985e16a 100644 --- a/frontend/app-development/test/rootStateMock.ts +++ b/frontend/app-development/test/rootStateMock.ts @@ -1,7 +1,7 @@ -import { repositoryMock } from './repositoryMock'; import { mockDeployments } from './appDeploymentsMock'; import type { RootState } from '../store'; import { applicationMetadataMock } from './applicationMetadataMock'; +import { repository } from 'app-shared/mocks/mocks'; export const rootStateMock: RootState = { applicationMetadataState: { @@ -9,7 +9,7 @@ export const rootStateMock: RootState = { error: null, }, serviceInformation: { - repositoryInfo: repositoryMock, + repositoryInfo: repository, error: null, initialCommit: null, serviceDescriptionObj: { diff --git a/frontend/app-development/test/testUtils.tsx b/frontend/app-development/test/testUtils.tsx index 97cf1a6383a..9562f6e7c73 100644 --- a/frontend/app-development/test/testUtils.tsx +++ b/frontend/app-development/test/testUtils.tsx @@ -10,6 +10,7 @@ import { APP_DEVELOPMENT_BASENAME } from 'app-shared/constants'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { queryClientConfigMock } from 'app-shared/mocks/queryClientMock'; import { QueryClient } from '@tanstack/react-query'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; interface ExtendedRenderOptions extends Omit { preloadedState?: PreloadedState; @@ -35,6 +36,7 @@ export const renderWithProviders = ( { + return !isNaN(Date.parse(dateStr)); +}; + /** * Formats a datestring of the format YYYY-MM-DDTHH:MM:SSZ to the * following date and time format: DD.MM.YYYY HH:MM, where the time @@ -8,6 +12,7 @@ * @returns the formated date and time string */ export const formatDateToDateAndTimeString = (dateString: string): string => { + if (!isDateValid(dateString)) return ''; return new Intl.DateTimeFormat('no-NB', { year: 'numeric', month: '2-digit', diff --git a/frontend/app-preview/src/components/AppPreviewSubMenu.test.tsx b/frontend/app-preview/src/components/AppPreviewSubMenu.test.tsx index 1698c74c4ce..de1dcd09428 100644 --- a/frontend/app-preview/src/components/AppPreviewSubMenu.test.tsx +++ b/frontend/app-preview/src/components/AppPreviewSubMenu.test.tsx @@ -1,8 +1,6 @@ import React from 'react'; -import { - queryClientMock, - renderWithMockStore -} from '../../../../frontend/packages/ux-editor/src/testing/mocks'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; +import { renderWithMockStore } from '../../../../frontend/packages/ux-editor/src/testing/mocks'; import { layoutSetsMock } from '../../../../frontend/packages/ux-editor/src/testing/layoutMock'; import { AppPreviewSubMenuProps, AppPreviewSubMenu } from './AppPreviewSubMenu'; import { LayoutSets } from 'app-shared/types/api/LayoutSetsResponse'; diff --git a/frontend/app-preview/src/views/LandingPage.test.tsx b/frontend/app-preview/src/views/LandingPage.test.tsx index c55f4dc86ac..c3f9961eb6a 100644 --- a/frontend/app-preview/src/views/LandingPage.test.tsx +++ b/frontend/app-preview/src/views/LandingPage.test.tsx @@ -5,20 +5,7 @@ import { LandingPage } from './LandingPage'; import { renderWithMockStore } from '../../../../frontend/packages/ux-editor/src/testing/mocks'; import { textMock } from '../../../testing/mocks/i18nMock'; -// Mocking console.error due to Tanstack Query removing custom logger between V4 and v5 see issue: #11692 -const realConsole = console; - describe('LandingPage', () => { - beforeEach(() => { - global.console = { - ...console, - error: jest.fn(), - }; - }); - afterEach(() => { - global.console = realConsole; - }); - it('should render an iframe', () => { const { renderResult } = renderWithMockStore()(); diff --git a/frontend/dashboard/app/App.test.tsx b/frontend/dashboard/app/App.test.tsx index 2a7fc1c78d8..efc767794ae 100644 --- a/frontend/dashboard/app/App.test.tsx +++ b/frontend/dashboard/app/App.test.tsx @@ -10,12 +10,11 @@ const renderWithMockServices = (services: Partial = {}) => render( - + , ); }; describe('App', () => { - test('should display spinner while loading', () => { renderWithMockServices(); expect(screen.getByText(textMock('general.loading'))).toBeInTheDocument(); @@ -27,7 +26,7 @@ describe('App', () => { await screen.findByRole('heading', { level: 1, name: 'Feil oppstod ved innlasting av brukerdata', - }) + }), ).toBeInTheDocument(); }); @@ -38,14 +37,13 @@ describe('App', () => { await screen.findByRole('heading', { level: 1, name: 'Feil oppstod ved innlasting av organisasjoner', - }) + }), ); }); test('should display dashboard page if successfully loading data', async () => { renderWithMockServices(); await waitForElementToBeRemoved(screen.queryByText(textMock('general.loading'))); - await waitForElementToBeRemoved(screen.queryByText(textMock('general.loading'))); expect(screen.getByRole('heading', { level: 2, name: textMock('dashboard.favourites') })); expect(screen.getByRole('heading', { level: 2, name: textMock('dashboard.my_apps') })); expect(screen.getByRole('heading', { level: 2, name: textMock('dashboard.resources') })); diff --git a/frontend/dashboard/components/DataModelsRepoList/DatamodelsRepoList.tsx b/frontend/dashboard/components/DataModelsRepoList/DatamodelsRepoList.tsx index ff9981623db..f640ad548f0 100644 --- a/frontend/dashboard/components/DataModelsRepoList/DatamodelsRepoList.tsx +++ b/frontend/dashboard/components/DataModelsRepoList/DatamodelsRepoList.tsx @@ -4,23 +4,19 @@ import { getReposLabel } from '../../utils/repoUtils'; import { getUidFilter } from '../../utils/filterUtils'; import { useAugmentReposWithStarred } from '../../hooks/useAugmentReposWithStarred'; import { useTranslation } from 'react-i18next'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { Organization } from 'app-shared/types/Organization'; import { useSearchReposQuery } from 'dashboard/hooks/queries/useSearchReposQuery'; -import { IRepository } from 'app-shared/types/global'; import { useSelectedContext } from 'dashboard/hooks/useSelectedContext'; import { Heading } from '@digdir/design-system-react'; +import { useStarredReposQuery } from 'dashboard/hooks/queries'; type DataModelsReposListProps = { user: User; organizations: Organization[]; - starredRepos: IRepository[]; }; -export const DatamodelsReposList = ({ - user, - organizations, - starredRepos, -}: DataModelsReposListProps) => { +export const DatamodelsReposList = ({ user, organizations }: DataModelsReposListProps) => { + const { data: starredRepos = [], isPending: areStarredReposPending } = useStarredReposQuery(); const selectedContext = useSelectedContext(); const { t } = useTranslation(); @@ -50,7 +46,11 @@ export const DatamodelsReposList = ({ {getReposLabel({ selectedContext, orgs: organizations, t, isDatamodelsRepo: true })} - + ); }; diff --git a/frontend/dashboard/components/MakeCopyModal/MakeCopyModal.test.tsx b/frontend/dashboard/components/MakeCopyModal/MakeCopyModal.test.tsx index 73eb2d5ae17..295714f0e64 100644 --- a/frontend/dashboard/components/MakeCopyModal/MakeCopyModal.test.tsx +++ b/frontend/dashboard/components/MakeCopyModal/MakeCopyModal.test.tsx @@ -5,6 +5,7 @@ import { MakeCopyModal } from './MakeCopyModal'; import { MockServicesContextWrapper } from 'dashboard/dashboardTestUtils'; import { textMock } from '../../../testing/mocks/i18nMock'; import { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; const user = userEvent.setup(); const org = 'org'; @@ -31,8 +32,7 @@ describe('MakeCopyModal', () => { }); test('should not show error message when clicking confirm and name is added', async () => { - const copyAppMock = jest.fn(() => Promise.resolve()); - renderWithMockServices({ copyApp: copyAppMock }); + renderWithMockServices(); await act(() => user.type(screen.getByRole('textbox'), 'new-repo-name')); await act(() => @@ -44,8 +44,8 @@ describe('MakeCopyModal', () => { ); expect(screen.queryByText(textMock('dashboard.field_cannot_be_empty'))).not.toBeInTheDocument(); - expect(copyAppMock).toHaveBeenCalledTimes(1); - expect(copyAppMock).toHaveBeenCalledWith('org', 'app', 'new-repo-name'); + expect(queriesMock.copyApp).toHaveBeenCalledTimes(1); + expect(queriesMock.copyApp).toHaveBeenCalledWith('org', 'app', 'new-repo-name'); }); test('should show error message when clicking confirm without adding name', async () => { diff --git a/frontend/dashboard/components/OrgRepoList/OrgReposList.tsx b/frontend/dashboard/components/OrgRepoList/OrgReposList.tsx index fceab3ea1c2..7d93ad1ae4e 100644 --- a/frontend/dashboard/components/OrgRepoList/OrgReposList.tsx +++ b/frontend/dashboard/components/OrgRepoList/OrgReposList.tsx @@ -2,22 +2,22 @@ import React from 'react'; import { RepoList } from '../RepoList'; import { getReposLabel } from '../../utils/repoUtils'; import { getUidFilter } from '../../utils/filterUtils'; -import { useAugmentReposWithStarred } from '../../hooks/useAugmentReposWithStarred'; import { useTranslation } from 'react-i18next'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { Organization } from 'app-shared/types/Organization'; -import { IRepository } from 'app-shared/types/global'; import { useReposSearch } from 'dashboard/hooks/useReposSearch'; import { useSelectedContext } from 'dashboard/hooks/useSelectedContext'; import { Heading } from '@digdir/design-system-react'; import { DATAGRID_DEFAULT_PAGE_SIZE } from 'dashboard/constants'; +import { useAugmentReposWithStarred } from 'dashboard/hooks/useAugmentReposWithStarred'; +import { useStarredReposQuery } from 'dashboard/hooks/queries'; type OrgReposListProps = { user: User; organizations: Organization[]; - starredRepos: IRepository[]; }; -export const OrgReposList = ({ user, organizations, starredRepos }: OrgReposListProps) => { +export const OrgReposList = ({ user, organizations }: OrgReposListProps) => { + const { data: starredRepos = [], isPending: areStarredReposPending } = useStarredReposQuery(); const selectedContext = useSelectedContext(); const { t } = useTranslation(); const uid = getUidFilter({ selectedContext, userId: user.id, organizations }); @@ -44,7 +44,7 @@ export const OrgReposList = ({ user, organizations, starredRepos }: OrgReposList !repo.name.endsWith('-datamodels'))} - isLoading={isLoadingSearchResults} + isLoading={isLoadingSearchResults || areStarredReposPending} onPageSizeChange={setPageSize} isServerSort={true} rowCount={searchResults?.totalCount ?? 0} diff --git a/frontend/dashboard/components/RepoList/RepoList.test.tsx b/frontend/dashboard/components/RepoList/RepoList.test.tsx index 2b2e01e60cb..45ea5cbf978 100644 --- a/frontend/dashboard/components/RepoList/RepoList.test.tsx +++ b/frontend/dashboard/components/RepoList/RepoList.test.tsx @@ -2,26 +2,40 @@ import React from 'react'; import { act, screen, render, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MockServicesContextWrapper } from '../../dashboardTestUtils'; -import { starredRepoMock } from '../../data-mocks/starredRepoMock'; import { IRepoListProps, RepoList } from './RepoList'; -import { IRepository } from 'app-shared/types/global'; import { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import { textMock } from '../../../testing/mocks/i18nMock'; +import { repository } from 'app-shared/mocks/mocks'; const user = userEvent.setup(); const renderWithMockServices = ( componentProps: IRepoListProps, - services?: Partial + services?: Partial, ) => { render( - - + + , ); }; describe('RepoList', () => { + test('should display spinner while loading starred repositories', () => { + renderWithMockServices({ + isLoading: true, + rowCount: 5, + }); + expect(screen.getByRole('progressbar')).toBeInTheDocument(); + }); + + test('should display no repos when repos are empty', async () => { + renderWithMockServices({ + isLoading: false, + rowCount: 5, + }); + expect(await screen.findByText(textMock('dashboard.no_repos_result'))).toBeInTheDocument(); + }); test('should not call onSortModelChange when clicking sort button and isServerSort is false', async () => { const handleSortMock = jest.fn(); @@ -56,14 +70,20 @@ describe('RepoList', () => { }); }); - test("Should render GridActionsCellItem", () => { - renderWithMockServices({ + test('Should render GridActionsCellItem', async () => { + renderWithMockServices({ + repos: [ + { + ...repository, + hasStarred: true, + }, + ], isLoading: false, isServerSort: true, rowCount: 5, }); - const gridActionsCellItem = within(screen.getByRole('menuitem', - { name: textMock('dashboard.unstar') })).getByRole('img'); + const unstar = await screen.findByRole('menuitem', { name: textMock('dashboard.unstar') }); + const gridActionsCellItem = within(unstar).getByRole('img'); expect(gridActionsCellItem).toBeInTheDocument(); }); }); diff --git a/frontend/dashboard/components/RepoList/RepoList.tsx b/frontend/dashboard/components/RepoList/RepoList.tsx index 838d4ad656e..9121b7afff0 100644 --- a/frontend/dashboard/components/RepoList/RepoList.tsx +++ b/frontend/dashboard/components/RepoList/RepoList.tsx @@ -10,7 +10,7 @@ import type { } from '@mui/x-data-grid'; import { DataGrid, GridActionsCellItem, GridOverlay } from '@mui/x-data-grid'; import cn from 'classnames'; -import type { IRepository } from 'app-shared/types/global'; +import type { RepositoryWithStarred } from 'dashboard/utils/repoUtils/repoUtils'; import { MakeCopyModal } from '../MakeCopyModal'; import { getRepoEditUrl } from '../../utils/urlUtils'; import { useTranslation } from 'react-i18next'; @@ -20,7 +20,7 @@ import { DATAGRID_PAGE_SIZE_TYPE, } from '../../constants'; import classes from './RepoList.module.css'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { useSetStarredRepoMutation } from '../../hooks/mutations'; import { useUnsetStarredRepoMutation } from '../../hooks/mutations'; import { @@ -33,7 +33,7 @@ import { export interface IRepoListProps { isLoading: boolean; - repos?: IRepository[]; + repos?: RepositoryWithStarred[]; isServerSort?: boolean; pageSize?: DATAGRID_PAGE_SIZE_TYPE; rowCount: number; @@ -49,7 +49,7 @@ const defaultPageSizeOptions = DATAGRID_PAGE_SIZE_OPTIONS; const isRowSelectable = () => false; -const defaultArray: IRepository[] = []; +const defaultArray: RepositoryWithStarred[] = []; const gridStyleOverride = { border: 'none', @@ -122,9 +122,10 @@ export const RepoList = ({ headerClassName: classes.columnHeader, width: 50, getActions: (params: GridRowParams) => { - const repo = params.row as IRepository; + const repo = params.row as RepositoryWithStarred; + const handleToggleFav = () => { - if (repo.user_has_starred) { + if (repo.hasStarred) { unsetStarredRepo(repo); } else { setStarredRepo(repo); @@ -136,9 +137,9 @@ export const RepoList = ({ key={repo.id} id={`fav-repo-${repo.id}`} onClick={handleToggleFav} - label={repo.user_has_starred ? t('dashboard.unstar') : t('dashboard.star')} + label={repo.hasStarred ? t('dashboard.unstar') : t('dashboard.star')} icon={ - repo.user_has_starred ? ( + repo.hasStarred ? ( ) : ( diff --git a/frontend/dashboard/components/ResourcesRepoList/ResourcesRepoList.test.tsx b/frontend/dashboard/components/ResourcesRepoList/ResourcesRepoList.test.tsx index 0a83273ea31..6a897070e7a 100644 --- a/frontend/dashboard/components/ResourcesRepoList/ResourcesRepoList.test.tsx +++ b/frontend/dashboard/components/ResourcesRepoList/ResourcesRepoList.test.tsx @@ -5,7 +5,7 @@ import { ResourcesRepoList } from './ResourcesRepoList'; import { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import { textMock } from '../../../testing/mocks/i18nMock'; import { useParams } from 'react-router-dom'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { MockServicesContextWrapper } from 'dashboard/dashboardTestUtils'; const originalWindowLocation = window.location; diff --git a/frontend/dashboard/components/ResourcesRepoList/ResourcesRepoList.tsx b/frontend/dashboard/components/ResourcesRepoList/ResourcesRepoList.tsx index 28187d0066d..985d18747a7 100644 --- a/frontend/dashboard/components/ResourcesRepoList/ResourcesRepoList.tsx +++ b/frontend/dashboard/components/ResourcesRepoList/ResourcesRepoList.tsx @@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next'; import { StudioSpinner } from '@studio/components'; import { Alert, Heading, Link } from '@digdir/design-system-react'; import { useSearchReposQuery } from 'dashboard/hooks/queries'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { getUidFilter } from 'dashboard/utils/filterUtils'; type ResourcesRepoListProps = { diff --git a/frontend/dashboard/components/SearchResultReposList/SearchResultReposList.tsx b/frontend/dashboard/components/SearchResultReposList/SearchResultReposList.tsx index fbdd7e9514f..f630b325df8 100644 --- a/frontend/dashboard/components/SearchResultReposList/SearchResultReposList.tsx +++ b/frontend/dashboard/components/SearchResultReposList/SearchResultReposList.tsx @@ -3,13 +3,13 @@ import { useAugmentReposWithStarred } from '../../hooks/useAugmentReposWithStarr import { RepoList } from '../RepoList'; import { useTranslation } from 'react-i18next'; import { useReposSearch } from 'dashboard/hooks/useReposSearch'; -import { IRepository } from 'app-shared/types/global'; +import { useStarredReposQuery } from 'dashboard/hooks/queries'; type SearchResultReposList = { - starredRepos: IRepository[]; searchValue: string; }; -export const SearchResultReposList = ({ starredRepos, searchValue }: SearchResultReposList) => { +export const SearchResultReposList = ({ searchValue }: SearchResultReposList) => { + const { data: starredRepos = [], isPending: areStarredReposPending } = useStarredReposQuery(); const { t } = useTranslation(); const { searchResults, @@ -31,7 +31,7 @@ export const SearchResultReposList = ({ starredRepos, searchValue }: SearchResul

{t('dashboard.search_result')}

{ const queries: ServicesContextProviderProps = { ...queriesMock, - getUser: () => Promise.resolve({ avatar_url: null, email: '', full_name: '', id: null, login: null }), - logout: () => Promise.resolve(), - getOrganizations: () => Promise.resolve([]), - addRepo: (repoToAdd: AddRepoParams) => Promise.resolve({} as IRepository), - copyApp: () => Promise.resolve(), - getStarredRepos: () => Promise.resolve([] as IRepository[]), - searchRepos: (params: SearchRepoFilterParams) => Promise.resolve({} as SearchRepositoryResponse), - setStarredRepo: () => Promise.resolve([]), - unsetStarredRepo: () => Promise.resolve(), ...customServices, client, - clientConfig: queryClientConfigMock + clientConfig: queryClientConfigMock, }; return ( - - {children} - + {children} ); }; diff --git a/frontend/dashboard/data-mocks/searchedReposMock.ts b/frontend/dashboard/data-mocks/searchedReposMock.ts deleted file mode 100644 index 94e427647b0..00000000000 --- a/frontend/dashboard/data-mocks/searchedReposMock.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { SearchRepositoryResponse } from 'app-shared/types/api'; - -export const searchedRepos: SearchRepositoryResponse = { - data: [ - { - clone_url: 'http://dummy-url/repos/mocked-user/mocked-repo.git', - description: '', - full_name: 'Mocked Name', - html_url: 'http://dummy-url/repos/unit/testing', - id: 36, - name: 'Mocking Username', - owner: { - avatar_url: 'https://secure.gravatar.com/avatar/c60d5958ff3bb331294e89d51bc022dd?d=identicon', - full_name: '', - login: 'mocked-testing', - }, - updated_at: '2023-01-31T21:03:23Z', - is_cloned_to_local: false, - user_has_starred: false, - }, - ], - ok: true, - totalCount: 1, - totalPages: 1, -}; diff --git a/frontend/dashboard/data-mocks/starredRepoMock.ts b/frontend/dashboard/data-mocks/starredRepoMock.ts deleted file mode 100644 index b7f62951e82..00000000000 --- a/frontend/dashboard/data-mocks/starredRepoMock.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { IRepository } from 'app-shared/types/global'; - -export const starredRepoMock: IRepository = { - clone_url: 'http://dummy-url/repos/mocked-user/mocked-repo.git', - description: '', - full_name: 'Mocked Name', - html_url: 'http://dummy-url/repos/unit/testing', - id: 36, - name: 'Mocking Username', - owner: { - avatar_url: 'https://secure.gravatar.com/avatar/c60d5958ff3bb331294e89d51bc022dd?d=identicon', - full_name: '', - login: 'mocked-testing', - }, - updated_at: '2023-01-31T21:03:23Z', - is_cloned_to_local: false, - user_has_starred: true, -}; diff --git a/frontend/dashboard/hooks/mutations/useSetStarredRepoMutation.ts b/frontend/dashboard/hooks/mutations/useSetStarredRepoMutation.ts index 0756ed1ed53..6175fb57107 100644 --- a/frontend/dashboard/hooks/mutations/useSetStarredRepoMutation.ts +++ b/frontend/dashboard/hooks/mutations/useSetStarredRepoMutation.ts @@ -1,13 +1,13 @@ import { useServicesContext } from 'app-shared/contexts/ServicesContext'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { IRepository } from 'app-shared/types/global'; +import { Repository } from 'app-shared/types/Repository'; import { QueryKey } from 'app-shared/types/QueryKey'; export const useSetStarredRepoMutation = () => { const { setStarredRepo } = useServicesContext(); const queryClient = useQueryClient(); return useMutation({ - mutationFn: (repo: IRepository) => setStarredRepo(repo), + mutationFn: (repo: Repository) => setStarredRepo(repo.owner.login, repo.name), onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.StarredRepos] }), }); }; diff --git a/frontend/dashboard/hooks/mutations/useUnsetStarredRepoMutation.ts b/frontend/dashboard/hooks/mutations/useUnsetStarredRepoMutation.ts index 3e19c3b8747..997201fd197 100644 --- a/frontend/dashboard/hooks/mutations/useUnsetStarredRepoMutation.ts +++ b/frontend/dashboard/hooks/mutations/useUnsetStarredRepoMutation.ts @@ -1,13 +1,13 @@ import { useServicesContext } from 'app-shared/contexts/ServicesContext'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { IRepository } from 'app-shared/types/global'; +import { Repository } from 'app-shared/types/Repository'; import { QueryKey } from 'app-shared/types/QueryKey'; export const useUnsetStarredRepoMutation = () => { const { unsetStarredRepo } = useServicesContext(); const queryClient = useQueryClient(); return useMutation({ - mutationFn: (repo: IRepository) => unsetStarredRepo(repo), + mutationFn: (repo: Repository) => unsetStarredRepo(repo.owner.login, repo.name), onSuccess: () => queryClient.invalidateQueries({ queryKey: [QueryKey.StarredRepos] }), }); }; diff --git a/frontend/dashboard/hooks/queries/useStarredReposQuery.ts b/frontend/dashboard/hooks/queries/useStarredReposQuery.ts index 1c4273d550f..39cc791ce1b 100644 --- a/frontend/dashboard/hooks/queries/useStarredReposQuery.ts +++ b/frontend/dashboard/hooks/queries/useStarredReposQuery.ts @@ -1,13 +1,13 @@ import { useServicesContext } from 'app-shared/contexts/ServicesContext'; import { useQuery, UseQueryResult } from '@tanstack/react-query'; import { QueryKey } from 'app-shared/types/QueryKey'; -import { IRepository } from 'app-shared/types/global'; +import { RepositoryWithStarred } from 'dashboard/utils/repoUtils/repoUtils'; -export const useStarredReposQuery = (): UseQueryResult => { +export const useStarredReposQuery = (): UseQueryResult => { const { getStarredRepos } = useServicesContext(); return useQuery({ queryKey: [QueryKey.StarredRepos], queryFn: () => - getStarredRepos().then((data) => data.map((repo) => ({ ...repo, user_has_starred: true }))), + getStarredRepos().then((data) => data.map((repo) => ({ ...repo, hasStarred: true }))), }); }; diff --git a/frontend/dashboard/hooks/useAugmentReposWithStarred/useAugmentReposWithStarred.ts b/frontend/dashboard/hooks/useAugmentReposWithStarred/useAugmentReposWithStarred.ts index cd31a1da370..a6561f4269f 100644 --- a/frontend/dashboard/hooks/useAugmentReposWithStarred/useAugmentReposWithStarred.ts +++ b/frontend/dashboard/hooks/useAugmentReposWithStarred/useAugmentReposWithStarred.ts @@ -1,12 +1,12 @@ import { useMemo } from 'react'; import type { MergeReposProps } from '../../utils/repoUtils'; import { mergeRepos } from '../../utils/repoUtils'; -import type { IRepository } from 'app-shared/types/global'; +import { RepositoryWithStarred } from 'dashboard/utils/repoUtils/repoUtils'; export const useAugmentReposWithStarred = ({ repos, starredRepos, -}: MergeReposProps): IRepository[] => { +}: MergeReposProps): RepositoryWithStarred[] => { return useMemo(() => { return mergeRepos({ repos, starredRepos }); }, [repos, starredRepos]); diff --git a/frontend/dashboard/pages/CreateService/CreateService.test.tsx b/frontend/dashboard/pages/CreateService/CreateService.test.tsx index 499ff3d8f95..6e14e39026f 100644 --- a/frontend/dashboard/pages/CreateService/CreateService.test.tsx +++ b/frontend/dashboard/pages/CreateService/CreateService.test.tsx @@ -3,43 +3,19 @@ import { act, render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { MockServicesContextWrapper } from '../../dashboardTestUtils'; import { CreateService } from './CreateService'; -import { User } from 'app-shared/types/User'; -import { IGiteaOrganisation, IRepository } from 'app-shared/types/global'; +import { User } from 'app-shared/types/Repository'; +import { Organization } from 'app-shared/types/Organization'; import { textMock } from '../../../testing/mocks/i18nMock'; import { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; +import { repository, user as userMock } from 'app-shared/mocks/mocks'; -const orgMock: IGiteaOrganisation = { +const orgMock: Organization = { avatar_url: '', id: 1, username: 'unit-test', full_name: 'unit-test', }; -const repositoryMock: IRepository = { - clone_url: '', - description: '', - full_name: 'test', - html_url: '', - id: 0, - is_cloned_to_local: false, - user_has_starred: false, - name: 'test', - owner: { - avatar_url: '', - full_name: '', - login: '', - }, - updated_at: '', -}; - -const userMock: User = { - id: 1, - avatar_url: '', - email: 'tester@tester.test', - full_name: 'Tester Testersen', - login: 'tester', -}; - jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useNavigate: jest.fn(), @@ -47,7 +23,7 @@ jest.mock('react-router-dom', () => ({ const renderWithMockServices = ( services?: Partial, - organizations?: IGiteaOrganisation[], + organizations?: Organization[], user?: User, ) => { render( @@ -61,6 +37,7 @@ const renderWithMockServices = ( email: '', full_name: '', login: '', + userType: 0, } } /> @@ -200,12 +177,19 @@ describe('CreateService', () => { addRepo: () => new Promise((resolve) => setTimeout(() => { - resolve(repositoryMock); + resolve({ + ...repository, + full_name: 'test', + name: 'test', + }); }, 2), ), }, [orgMock], - userMock, + { + ...userMock, + login: 'tester', + }, ); const createBtn: HTMLElement = screen.getByRole('button', { @@ -252,10 +236,13 @@ describe('CreateService', () => { renderWithMockServices( { - addRepo: () => Promise.resolve(repositoryMock), + addRepo: () => Promise.resolve(repository), }, [orgMock], - userMock, + { + ...userMock, + login: 'tester', + }, ); const createBtn: HTMLElement = screen.getByRole('button', { diff --git a/frontend/dashboard/pages/CreateService/CreateService.tsx b/frontend/dashboard/pages/CreateService/CreateService.tsx index a23999ff77d..f705516babf 100644 --- a/frontend/dashboard/pages/CreateService/CreateService.tsx +++ b/frontend/dashboard/pages/CreateService/CreateService.tsx @@ -6,7 +6,7 @@ import classes from './CreateService.module.css'; import { Button } from '@digdir/design-system-react'; import { useTranslation } from 'react-i18next'; import { Organization } from 'app-shared/types/Organization'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { useAddRepoMutation } from 'dashboard/hooks/mutations/useAddRepoMutation'; import { DatamodelFormat } from 'app-shared/types/DatamodelFormat'; import { SelectedContextType } from 'app-shared/navigation/main-header/Header'; diff --git a/frontend/dashboard/pages/Dashboard/Dashboard.test.tsx b/frontend/dashboard/pages/Dashboard/Dashboard.test.tsx index daa1f297c5c..de12de9bf07 100644 --- a/frontend/dashboard/pages/Dashboard/Dashboard.test.tsx +++ b/frontend/dashboard/pages/Dashboard/Dashboard.test.tsx @@ -1,50 +1,49 @@ import React from 'react'; -import { render, screen, waitForElementToBeRemoved } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import { MockServicesContextWrapper } from '../../dashboardTestUtils'; import { Dashboard } from './Dashboard'; import { textMock } from '../../../testing/mocks/i18nMock'; -import { User } from 'app-shared/types/User'; -import { starredRepoMock } from '../../data-mocks/starredRepoMock'; -import { searchedRepos } from '../../data-mocks/searchedReposMock'; +import { Repository, User } from 'app-shared/types/Repository'; import { SelectedContextType } from 'app-shared/navigation/main-header/Header'; import { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; +import { repository, searchRepositoryResponse } from 'app-shared/mocks/mocks'; +import { SearchRepositoryResponse } from 'app-shared/types/api'; const renderWithMockServices = (services?: Partial) => { render( - + , ); }; -jest.mock("react-router-dom", () => ({ - ...jest.requireActual("react-router-dom"), +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), useParams: () => ({ - selectedContext: SelectedContextType.Self + selectedContext: SelectedContextType.Self, }), })); describe('Dashboard', () => { - - test('should display spinner while loading starred repositories', () => { - renderWithMockServices(); - expect(screen.getByText(textMock('general.loading'))).toBeInTheDocument(); - }); - - test('should display no favorites when starred repos is empty', async () => { - renderWithMockServices({ getStarredRepos: () => Promise.resolve([]) }); - expect(await screen.findByText(textMock('dashboard.no_repos_result'))).toBeInTheDocument(); - }); - test('should display favorite list with one item', async () => { - renderWithMockServices({ getStarredRepos: () => Promise.resolve([starredRepoMock]) }); - await waitForElementToBeRemoved(() => screen.queryByText(textMock('general.loading'))); - expect(await screen.findAllByRole('menuitem', { name: textMock('dashboard.unstar') })).toHaveLength(1); + renderWithMockServices({ + getStarredRepos: () => Promise.resolve([repository]), + }); + expect( + await screen.findAllByRole('menuitem', { name: textMock('dashboard.unstar') }), + ).toHaveLength(1); }); test('should display list of my application', async () => { - renderWithMockServices({ searchRepos: () => Promise.resolve(searchedRepos) }); - await waitForElementToBeRemoved(() => screen.queryByText(textMock('general.loading'))); - expect(await screen.findAllByRole('menuitem', { name: textMock('dashboard.star') })).toHaveLength(1); + renderWithMockServices({ + searchRepos: () => + Promise.resolve({ + ...searchRepositoryResponse, + data: [repository], + }), + }); + expect( + await screen.findAllByRole('menuitem', { name: textMock('dashboard.star') }), + ).toHaveLength(1); }); }); diff --git a/frontend/dashboard/pages/Dashboard/Dashboard.tsx b/frontend/dashboard/pages/Dashboard/Dashboard.tsx index 680f57a441c..1ec4de146d8 100644 --- a/frontend/dashboard/pages/Dashboard/Dashboard.tsx +++ b/frontend/dashboard/pages/Dashboard/Dashboard.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; import classes from './Dashboard.module.css'; -import { StudioPageSpinner } from '@studio/components'; import cn from 'classnames'; import type { ChangeEvent, KeyboardEvent } from 'react'; import { SearchField } from '@altinn/altinn-design-system'; @@ -15,9 +14,8 @@ import { Footer } from '../../components/Footer'; import { Link } from 'react-router-dom'; import { useDebounce } from 'react-use'; import { useTranslation } from 'react-i18next'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { Organization } from 'app-shared/types/Organization'; -import { useStarredReposQuery } from '../../hooks/queries'; import { useSelectedContext } from 'dashboard/hooks/useSelectedContext'; import * as testids from '../../../testing/testids'; import { ResourcesRepoList } from 'dashboard/components/ResourcesRepoList/ResourcesRepoList'; @@ -32,7 +30,6 @@ type DashboardProps = { export const Dashboard = ({ user, organizations, disableDebounce }: DashboardProps) => { const { t } = useTranslation(); const selectedContext = useSelectedContext(); - const { data: starredRepos = [], isPending: areStarredReposPending } = useStarredReposQuery(); const [searchText, setSearchText] = useState(''); const [isNewLinkFocused, setIsNewLinkFocused] = useState(false); const [debouncedSearchText, setDebouncedSearchText] = useState(''); @@ -46,10 +43,6 @@ export const Dashboard = ({ user, organizations, disableDebounce }: DashboardPro const handleNewLinkFocus = () => setIsNewLinkFocused(true); const handleNewLinkFocusOut = () => setIsNewLinkFocused(false); - if (areStarredReposPending) { - return ; - } - return ( <> @@ -94,22 +87,14 @@ export const Dashboard = ({ user, organizations, disableDebounce }: DashboardPro {debouncedSearchText ? ( - + ) : ( <>
- +
- + {selectedContext !== SelectedContextType.All && selectedContext !== SelectedContextType.Self && ( diff --git a/frontend/dashboard/utils/repoUtils/repoUtils.test.ts b/frontend/dashboard/utils/repoUtils/repoUtils.test.ts index 8facd34acb3..5469bcffc80 100644 --- a/frontend/dashboard/utils/repoUtils/repoUtils.test.ts +++ b/frontend/dashboard/utils/repoUtils/repoUtils.test.ts @@ -1,5 +1,5 @@ import { SelectedContextType } from 'app-shared/navigation/main-header/Header'; -import { getReposLabel, mergeRepos, validateRepoName } from './repoUtils'; +import { getReposLabel, validateRepoName } from './repoUtils'; import { textMock } from '../../../testing/mocks/i18nMock'; import { Organization } from 'app-shared/types/Organization'; @@ -56,88 +56,6 @@ describe('getReposLabel', () => { }); }); -describe('mergeRepos', () => { - const repoData = { - name: 'repo', - full_name: 'repo_fullname', - owner: { - avatar_url: 'url', - login: 'login', - full_name: 'full_name', - }, - description: 'description', - is_cloned_to_local: false, - updated_at: 'today', - html_url: 'html_url', - clone_url: 'clone_url', - user_has_starred: false, - }; - - it('should set user_has_starred to true when ids for repos in both lists match', () => { - const result = mergeRepos({ - repos: [ - { - ...repoData, - id: 1, - }, - { - ...repoData, - id: 2, - }, - ], - starredRepos: [ - { - ...repoData, - id: 2, - user_has_starred: true, - }, - ], - }); - - expect(result).toEqual([ - { - ...repoData, - id: 1, - user_has_starred: false, - }, - { - ...repoData, - id: 2, - user_has_starred: true, - }, - ]); - }); - - it('should return empty array when no repos are passed', () => { - const result = mergeRepos({ - repos: undefined, - starredRepos: undefined, - }); - - expect(result).toEqual([]); - }); - - it('should return original repos array when starred repos are undefined', () => { - const repos = [ - { - ...repoData, - id: 1, - }, - { - ...repoData, - id: 2, - }, - ]; - - const result = mergeRepos({ - repos, - starredRepos: undefined, - }); - - expect(result).toEqual(repos); - }); -}); - describe('validateRepoName', () => { it('should return true when repo name does not contain invalid characters', () => { expect(validateRepoName('repo')).toBe(true); diff --git a/frontend/dashboard/utils/repoUtils/repoUtils.ts b/frontend/dashboard/utils/repoUtils/repoUtils.ts index d9a45eae233..b8fc3f1a570 100644 --- a/frontend/dashboard/utils/repoUtils/repoUtils.ts +++ b/frontend/dashboard/utils/repoUtils/repoUtils.ts @@ -1,5 +1,5 @@ import { SelectedContextType } from 'app-shared/navigation/main-header/Header'; -import type { IRepository } from 'app-shared/types/global'; +import { Repository } from 'app-shared/types/Repository'; import { Organization } from 'app-shared/types/Organization'; import i18next from 'i18next'; @@ -12,10 +12,12 @@ type GetReposLabel = { }; export type MergeReposProps = { - repos?: IRepository[]; - starredRepos: IRepository[]; + repos?: Repository[]; + starredRepos: Repository[]; }; +export type RepositoryWithStarred = Repository & { hasStarred?: boolean }; + type TranslationMapKey = SelectedContextType | 'named_org' | 'org'; type TranslationMap = Record; const appsTranslationMap: TranslationMap = { @@ -64,7 +66,7 @@ export const getReposLabel = ({ : t(concatenatedTranslationMap.org); }; -export const mergeRepos = ({ repos, starredRepos }: MergeReposProps) => { +export const mergeRepos = ({ repos, starredRepos }: MergeReposProps): RepositoryWithStarred[] => { if (!repos) { return []; } @@ -76,7 +78,7 @@ export const mergeRepos = ({ repos, starredRepos }: MergeReposProps) => { return repos.map((repo) => { return { ...repo, - user_has_starred: !!starredRepos.find((starredRepo) => starredRepo.id === repo.id), + hasStarred: !!starredRepos.find((starredRepo) => starredRepo.id === repo.id), }; }); }; diff --git a/frontend/packages/schema-model/src/lib/mutations/ui-schema-reducers.test.ts b/frontend/packages/schema-model/src/lib/mutations/ui-schema-reducers.test.ts index 0aa1831eb34..5f85dd97bad 100644 --- a/frontend/packages/schema-model/src/lib/mutations/ui-schema-reducers.test.ts +++ b/frontend/packages/schema-model/src/lib/mutations/ui-schema-reducers.test.ts @@ -238,7 +238,7 @@ describe('ui-schema-reducers', () => { it('Calls the callback function with the new pointer', () => { result = setPropertyName(createNewModelMock(), args); - expect(callback).toBeCalledTimes(1); + expect(callback).toHaveBeenCalledTimes(1); expect(callback).toHaveBeenCalledWith(expectedPointer); }); }); diff --git a/frontend/packages/shared/src/api/mutations.ts b/frontend/packages/shared/src/api/mutations.ts index 48b8cfae630..a894d546cbb 100644 --- a/frontend/packages/shared/src/api/mutations.ts +++ b/frontend/packages/shared/src/api/mutations.ts @@ -30,6 +30,7 @@ import { appMetadataPath, serviceConfigPath, importResourceFromAltinn2Path, + processEditorPath, } from 'app-shared/api/paths'; import { AddLanguagePayload } from 'app-shared/types/api/AddLanguagePayload'; import { AddRepoParams } from 'app-shared/types/api'; @@ -39,7 +40,7 @@ import { CreateReleasePayload } from 'app-shared/types/api/CreateReleasePayload' import { CreateRepoCommitPayload } from 'app-shared/types/api/CreateRepoCommitPayload'; import { ExternalFormLayout } from 'app-shared/types/api/FormLayoutsResponse'; import { LayoutSetConfig, LayoutSets } from 'app-shared/types/api/LayoutSetsResponse'; -import { ILayoutSettings, IRepository, ITextResourcesObjectFormat } from 'app-shared/types/global'; +import { ILayoutSettings, ITextResourcesObjectFormat } from 'app-shared/types/global'; import { RuleConfig } from 'app-shared/types/RuleConfig'; import { UpdateTextIdPayload } from 'app-shared/types/api/UpdateTextIdPayload'; import { buildQueryParams } from 'app-shared/utils/urlUtils'; @@ -49,6 +50,7 @@ import type { Policy } from '@altinn/policy-editor'; import type { NewResource, Resource } from 'app-shared/types/ResourceAdm'; import { ApplicationMetadata } from 'app-shared/types/ApplicationMetadata'; import { AppConfig } from 'app-shared/types/AppConfig'; +import { Repository } from 'app-shared/types/Repository'; const headers = { Accept: 'application/json', @@ -58,13 +60,13 @@ const headers = { export const addAppAttachmentMetadata = (org: string, app: string, payload: ApplicationAttachmentMetadata) => post(appMetadataAttachmentPath(org, app), payload); export const addLanguageCode = (org: string, app: string, language: string, payload: AddLanguagePayload) => post(textResourcesPath(org, app, language), payload); export const addLayoutSet = (org: string, app: string, payload: LayoutSetConfig) => put(layoutSetsPath(org, app), payload); -export const addRepo = (repoToAdd: AddRepoParams) => post(`${createRepoPath()}${buildQueryParams(repoToAdd)}`); +export const addRepo = (repoToAdd: AddRepoParams) => post(`${createRepoPath()}${buildQueryParams(repoToAdd)}`); export const addXsdFromRepo = (org: string, app: string, modelPath: string) => post(datamodelAddXsdFromRepoPath(org, app, modelPath)); +export const commitAndPushChanges = (org: string, app: string, payload: CreateRepoCommitPayload) => post(repoCommitPushPath(org, app), payload, { headers }); +export const configureLayoutSet = (org: string, app: string, layoutSetName: string) => post(layoutSetPath(org, app, layoutSetName)); export const copyApp = (org: string, app: string, repoName: string) => post(copyAppPath(org, app, repoName)); export const createDatamodel = (org: string, app: string, payload: CreateDatamodelPayload) => post(createDatamodelPath(org, app), payload); export const createDeployment = (org: string, app: string, payload: CreateDeploymentPayload) => post(deploymentsPath(org, app), payload); -export const commitAndPushChanges = (org: string, app: string, payload: CreateRepoCommitPayload) => post(repoCommitPushPath(org, app), payload, { headers }); -export const configureLayoutSet = (org: string, app: string, layoutSetName: string) => post(layoutSetPath(org, app, layoutSetName)); export const createRelease = (org: string, app: string, payload: CreateReleasePayload) => post(releasesPath(org, app), payload); export const createRepoCommit = (org: string, app: string, payload: CreateRepoCommitPayload) => post(repoCommitPath(org, app), payload, { headers }); export const deleteAppAttachmentMetadata = (org: string, app: string, id: string) => del(appMetadataAttachmentPath(org, app), { headers, data: id }); @@ -79,20 +81,28 @@ export const saveDatamodel = (org: string, app: string, modelPath: string, paylo export const saveFormLayout = (org: string, app: string, layoutName: string, layoutSetName: string, payload: ExternalFormLayout) => post(formLayoutPath(org, app, layoutName, layoutSetName), payload); export const saveFormLayoutSettings = (org: string, app: string, layoutSetName: string, payload: ILayoutSettings) => post(layoutSettingsPath(org, app, layoutSetName), payload); export const saveRuleConfig = (org: string, app: string, layoutSetName: string, payload: RuleConfig) => post(ruleConfigPath(org, app, layoutSetName), payload); -export const setStarredRepo = (repo: IRepository) => put(userStarredRepoPath(repo.owner.login, repo.name), {}); -export const unsetStarredRepo = (repo: IRepository) => del(userStarredRepoPath(repo.owner.login, repo.name)); +export const setStarredRepo = (org: string, app: string) => put(userStarredRepoPath(org, app), {}); +export const unsetStarredRepo = (org: string, app: string) => del(userStarredRepoPath(org, app)); export const updateAppAttachmentMetadata = (org: string, app: string, payload: ApplicationAttachmentMetadata) => put(appMetadataAttachmentPath(org, app), payload); export const updateFormLayoutName = (org: string, app: string, oldName: string, newName: string, layoutSetName: string) => post(formLayoutNamePath(org, app, oldName, layoutSetName), JSON.stringify(newName), { headers: { 'Content-Type': 'application/json' } }); export const updateTextId = (org: string, app: string, payload: UpdateTextIdPayload) => put(textResourceIdsPath(org, app), payload); export const updateTranslationByLangCode = (org: string, app: string, language, payload) => post(textResourcesPath(org, app, language), payload); -export const upsertTextResources = (org: string, app: string, language: string, payload: ITextResourcesObjectFormat) => put(textResourcesPath(org, app, language), payload); export const updateAppPolicy = (org: string, app: string, payload: Policy) => put(appPolicyPath(org, app), payload); export const updateAppMetadata = (org: string, app: string, payload: ApplicationMetadata) => put(appMetadataPath(org, app), payload); export const updateAppConfig = (org: string, app: string, payload: AppConfig) => post(serviceConfigPath(org, app), payload); +export const upsertTextResources = (org: string, app: string, language: string, payload: ITextResourcesObjectFormat) => put(textResourcesPath(org, app, language), payload); // Resourceadm -export const updatePolicy = (org: string, repo: string, id: string, payload: Policy) => put(resourcePolicyPath(org, repo, id), payload); export const createResource = (org: string, payload: NewResource) => post(resourceCreatePath(org), payload); -export const updateResource = (org: string, repo: string, payload: Resource) => put(resourceEditPath(org, repo), payload); -export const publishResource = (org: string, repo: string, id: string, env: string) => post(publishResourcePath(org, repo, id, env), { headers: { 'Content-Type': 'application/json' } }); export const importResourceFromAltinn2 = (org: string, environment: string, serviceCode: string, serviceEdition: string) => post(importResourceFromAltinn2Path(org, environment, serviceCode, serviceEdition)); +export const publishResource = (org: string, repo: string, id: string, env: string) => post(publishResourcePath(org, repo, id, env), { headers: { 'Content-Type': 'application/json' } }); +export const updatePolicy = (org: string, repo: string, id: string, payload: Policy) => put(resourcePolicyPath(org, repo, id), payload); +export const updateResource = (org: string, repo: string, payload: Resource) => put(resourceEditPath(org, repo), payload); + +// ProcessEditor +export const updateBpmnXml = (org: string, app: string, bpmnXml: string) => + put(processEditorPath(org, app), bpmnXml, { + headers: { + 'Content-Type': 'application/xml', + }, + }); diff --git a/frontend/packages/shared/src/api/queries.ts b/frontend/packages/shared/src/api/queries.ts index d11f9a933a6..1e6c3acae93 100644 --- a/frontend/packages/shared/src/api/queries.ts +++ b/frontend/packages/shared/src/api/queries.ts @@ -1,4 +1,4 @@ -import { get, put } from 'app-shared/utils/networking'; +import { get } from 'app-shared/utils/networking'; import { altinn2LinkServicesPath, appLibVersionPath, @@ -49,13 +49,13 @@ import { DatamodelMetadataJson, DatamodelMetadataXsd } from 'app-shared/types/Da import { DeployEnvironment } from 'app-shared/types/DeployEnvironment'; import { FormLayoutsResponse } from 'app-shared/types/api/FormLayoutsResponse'; import { LayoutSets } from 'app-shared/types/api/LayoutSetsResponse'; -import { ILayoutSettings, IRepository, ITextResourcesWithLanguage, IFrontEndSettings } from 'app-shared/types/global'; +import { ILayoutSettings, ITextResourcesWithLanguage, IFrontEndSettings } from 'app-shared/types/global'; import { Organization } from 'app-shared/types/Organization'; import { OrgsState } from 'app-shared/types/OrgsState'; import { RepoStatus } from 'app-shared/types/RepoStatus'; import { Repository } from 'app-shared/types/Repository'; import { RuleConfig } from 'app-shared/types/RuleConfig'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { WidgetSettingsResponse } from 'app-shared/types/widgetTypes'; import { buildQueryParams } from 'app-shared/utils/urlUtils'; import { componentSchemaUrl, expressionSchemaUrl, layoutSchemaUrl, newsListUrl, numberFormatSchemaUrl, orgsListUrl } from '../cdn-paths'; @@ -67,9 +67,12 @@ import type { Commit } from 'app-shared/types/Commit'; import type { ApplicationMetadata } from 'app-shared/types/ApplicationMetadata'; import { Altinn2LinkService } from 'app-shared/types/Altinn2LinkService'; import { NewsList } from 'app-shared/types/api/NewsList'; +import { AppLibVersion } from 'app-shared/types/AppLibVersion'; export const getAppReleases = (owner: string, app: string) => get(releasesPath(owner, app, 'Descending')); export const getBranchStatus = (owner: string, app: string, branch: string) => get(branchStatusPath(owner, app, branch)); +export const getComponentSchema = (component: string) => get(componentSchemaUrl(component)); +export const getComponentsCommonDefsSchema = () => get(componentSchemaUrl('common-defs')); export const getDatamodel = (owner: string, app: string, modelPath: string) => get(datamodelPath(owner, app, modelPath)); export const getDatamodelMetadata = (owner: string, app: string) => get(datamodelMetadataPath(owner, app)); export const getDatamodelsJson = (owner: string, app: string) => get(datamodelsPath(owner, app)); @@ -77,12 +80,16 @@ export const getDatamodelsXsd = (owner: string, app: string) => get get(deployPermissionsPath(owner, app)); export const getDeployments = (owner: string, app: string) => get(deploymentsPath(owner, app, 'Descending')); export const getEnvironments = () => get(envConfigPath()); +export const getExpressionSchema = () => get(expressionSchemaUrl()); export const getFormLayoutSettings = (owner: string, app: string, layoutSetName: string) => get(layoutSettingsPath(owner, app, layoutSetName)); export const getFormLayouts = (owner: string, app: string, layoutSetName: string) => get(formLayoutsPath(owner, app, layoutSetName)); export const getFrontEndSettings = (owner: string, app: string) => get(frontEndSettingsPath(owner, app)); -export const getLayoutSets = (owner: string, app: string) => get(layoutSetsPath(owner, app)); export const getInstanceIdForPreview = (owner: string, app: string) => get(instanceIdForPreviewPath(owner, app)); +export const getLayoutSchema = () => get(layoutSchemaUrl()); +export const getLayoutSets = (owner: string, app: string) => get(layoutSetsPath(owner, app)); export const getNewsList = (language: 'nb' | 'en') => get(newsListUrl(language)); +export const getNumberFormatSchema = () => get(numberFormatSchemaUrl()); +export const getOptionListIds = (owner: string, app: string) => get(optionListIdsPath(owner, app)); export const getOrgList = () => get(orgsListUrl()); export const getOrganizations = () => get(orgsListPath()); export const getRepoInitialCommit = (owner: string, app: string) => get(repoInitialCommitPath(owner, app)); @@ -90,20 +97,13 @@ export const getRepoMetadata = (owner: string, app: string) => get(r export const getRepoPull = (owner: string, app: string) => get(repoPullPath(owner, app)); export const getRepoStatus = (owner: string, app: string) => get(repoStatusPath(owner, app)); export const getRuleConfig = (owner: string, app: string, layoutSetName: string) => get(ruleConfigPath(owner, app, layoutSetName)); -export const getRuleModel = (owner: string, app: string, layoutSetName: string) => get(ruleHandlerPath(owner, app, layoutSetName)); -export const getStarredRepos = () => get(userStarredListPath()); +export const getRuleModel = (owner: string, app: string, layoutSetName: string) => get(ruleHandlerPath(owner, app, layoutSetName)); +export const getStarredRepos = () => get(userStarredListPath()); export const getTextLanguages = (owner: string, app: string): Promise => get(textLanguagesPath(owner, app)); export const getTextResources = (owner: string, app: string, lang: string) => get(textResourcesPath(owner, app, lang)); export const getUser = () => get(userCurrentPath()); export const getWidgetSettings = (owner: string, app: string) => get(widgetSettingsPath(owner, app)); export const searchRepos = (filter: SearchRepoFilterParams) => get(`${repoSearchPath()}${buildQueryParams(filter)}`); -export const getOptionListIds = (owner: string, app: string) => get(optionListIdsPath(owner, app)); - -export const getExpressionSchema = () => get(expressionSchemaUrl()); -export const getLayoutSchema = () => get(layoutSchemaUrl()); -export const getNumberFormatSchema = () => get(numberFormatSchemaUrl()); -export const getComponentSchema = (component: string) => get(componentSchemaUrl(component)); -export const getComponentsCommonDefsSchema = () => get(componentSchemaUrl('common-defs')); // Settings modal export const getAppConfig = (org: string, app: string) => get(serviceConfigPath(org, app)); @@ -111,22 +111,16 @@ export const getAppPolicy = (org: string, app: string) => get(appPolicyP export const getAppMetadata = (org: string, app: string) => get(appMetadataPath(org, app)); // Resourceadm +export const getAltinn2LinkServices = (org: string, environment: string) => get(altinn2LinkServicesPath(org, environment)); export const getPolicyActions = (org: string, repo: string) => get(resourceActionsPath(org, repo)); export const getPolicy = (org: string, repo: string, id: string) => get(resourcePolicyPath(org, repo, id)); export const getPolicySubjects = (org: string, repo: string) => get(resourceSubjectsPath(org, repo)); -export const getResourcePublishStatus = (org: string, repo: string, id: string) => get(resourcePublishStatusPath(org, repo, id)); -export const getResourceList = (org: string) => get(resourceListPath(org)); export const getResource = (org: string, repo: string, id: string) => get(resourceSinglePath(org, repo, id)); +export const getResourceList = (org: string) => get(resourceListPath(org)); +export const getResourcePublishStatus = (org: string, repo: string, id: string) => get(resourcePublishStatusPath(org, repo, id)); export const getValidatePolicy = (org: string, repo: string, id: string) => get(resourceValidatePolicyPath(org, repo, id)); export const getValidateResource = (org: string, repo: string, id: string) => get(resourceValidateResourcePath(org, repo, id)); -export const getAltinn2LinkServices = (org: string, environment: string) => get(altinn2LinkServicesPath(org, environment)); // ProcessEditor -export const getBpmnFile = (org: string, app: string) => get(processEditorPath(org, app)); -export const updateBpmnXml = (org: string, app: string, bpmnXml: string) => - put(processEditorPath(org, app), bpmnXml, { - headers: { - 'Content-Type': 'application/xml', - }, - }); -export const getAppLibVersion = (org: string, app: string) => get(appLibVersionPath(org, app)); +export const getAppLibVersion = (org: string, app: string) => get(appLibVersionPath(org, app)); +export const getBpmnFile = (org: string, app: string) => get(processEditorPath(org, app)); diff --git a/frontend/packages/shared/src/components/AltinnConfirmDialog.test.tsx b/frontend/packages/shared/src/components/AltinnConfirmDialog.test.tsx index e9d84d863b0..66866ed7725 100644 --- a/frontend/packages/shared/src/components/AltinnConfirmDialog.test.tsx +++ b/frontend/packages/shared/src/components/AltinnConfirmDialog.test.tsx @@ -44,8 +44,8 @@ describe('AltinnConfirmDialog', () => { const confirmButton = screen.getByRole('button', { name: confirmTextMock }); await act(() => user.click(confirmButton)); - expect(onConfirmMock).toBeCalledTimes(1); - expect(onCloseMock).toBeCalledTimes(1); + expect(onConfirmMock).toHaveBeenCalledTimes(1); + expect(onCloseMock).toHaveBeenCalledTimes(1); }); it('should call onClose when clicking the cancel button', async () => { @@ -79,6 +79,6 @@ const render = async (props: Partial = {}) => { return rtlRender(

{descriptionTextMock}

-
+ , ); }; diff --git a/frontend/packages/shared/src/components/AltinnHeaderProfile/AltinnHeaderProfile.test.tsx b/frontend/packages/shared/src/components/AltinnHeaderProfile/AltinnHeaderProfile.test.tsx index 83b3e0d9fb6..66ef3ff7827 100644 --- a/frontend/packages/shared/src/components/AltinnHeaderProfile/AltinnHeaderProfile.test.tsx +++ b/frontend/packages/shared/src/components/AltinnHeaderProfile/AltinnHeaderProfile.test.tsx @@ -18,6 +18,7 @@ describe('AltinnHeaderProfile', () => { full_name: 'Test User', id: 1, login: 'test-user', + userType: 0, }, }); expect(screen.queryByText('test-user')).not.toBeInTheDocument(); @@ -26,7 +27,11 @@ describe('AltinnHeaderProfile', () => { it('should render users name and name of org the user represents', () => { render({ org: 'test-org' }); - expect(screen.getByText(textMock('shared.header_user_for_org', { user: 'test-user', org: 'Test Org' }))).toBeInTheDocument(); + expect( + screen.getByText( + textMock('shared.header_user_for_org', { user: 'test-user', org: 'Test Org' }), + ), + ).toBeInTheDocument(); }); }); @@ -47,7 +52,7 @@ export const render = (props?: Partial) => { login: 'test-org', email: 'test-email', id: 1, - UserType: 1, + userType: 1, }, updated_at: 'never', created_at: 'now', @@ -76,6 +81,7 @@ export const render = (props?: Partial) => { full_name: undefined, id: 1, login: 'test-user', + userType: 0, }, }; diff --git a/frontend/packages/shared/src/components/AltinnHeaderProfile/AltinnHeaderProfile.tsx b/frontend/packages/shared/src/components/AltinnHeaderProfile/AltinnHeaderProfile.tsx index e941e42e2af..2ce6440dc37 100644 --- a/frontend/packages/shared/src/components/AltinnHeaderProfile/AltinnHeaderProfile.tsx +++ b/frontend/packages/shared/src/components/AltinnHeaderProfile/AltinnHeaderProfile.tsx @@ -1,6 +1,6 @@ import { ProfileMenu } from 'app-shared/navigation/main-header/ProfileMenu'; import { Repository } from 'app-shared/types/Repository'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import React, { ReactNode } from 'react'; import classes from './AltinnHeaderProfile.module.css'; import { useUserNameAndOrg } from './hooks/useUserNameAndOrg'; diff --git a/frontend/packages/shared/src/components/AltinnHeaderProfile/hooks/useUserNameAndOrg.test.ts b/frontend/packages/shared/src/components/AltinnHeaderProfile/hooks/useUserNameAndOrg.test.ts index 93b66b5f9e7..61081433a7c 100644 --- a/frontend/packages/shared/src/components/AltinnHeaderProfile/hooks/useUserNameAndOrg.test.ts +++ b/frontend/packages/shared/src/components/AltinnHeaderProfile/hooks/useUserNameAndOrg.test.ts @@ -1,6 +1,6 @@ import { textMock } from '../../../../../../testing/mocks/i18nMock'; import { useUserNameAndOrg } from './useUserNameAndOrg'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { Repository } from 'app-shared/types/Repository'; const mockOrg: string = 'org'; @@ -11,6 +11,7 @@ const mockUser: User = { id: 1, login: 'testUser', full_name: 'Test User', + userType: 0, }; const mockRepository: Repository = { @@ -34,7 +35,7 @@ const mockRepository: Repository = { full_name: 'Name Test', id: 0, login: '', - UserType: 0, + userType: 0, }, permissions: { admin: true, diff --git a/frontend/packages/shared/src/components/AltinnHeaderProfile/hooks/useUserNameAndOrg.ts b/frontend/packages/shared/src/components/AltinnHeaderProfile/hooks/useUserNameAndOrg.ts index c2e17f0362b..38f3f508fcc 100644 --- a/frontend/packages/shared/src/components/AltinnHeaderProfile/hooks/useUserNameAndOrg.ts +++ b/frontend/packages/shared/src/components/AltinnHeaderProfile/hooks/useUserNameAndOrg.ts @@ -1,5 +1,5 @@ import { Repository } from 'app-shared/types/Repository'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { useTranslation } from 'react-i18next'; /** diff --git a/frontend/packages/shared/src/components/GiteaHeader/VersionControlButtons/VersionControlButtons.test.tsx b/frontend/packages/shared/src/components/GiteaHeader/VersionControlButtons/VersionControlButtons.test.tsx index 803d0209c84..15abf1375da 100644 --- a/frontend/packages/shared/src/components/GiteaHeader/VersionControlButtons/VersionControlButtons.test.tsx +++ b/frontend/packages/shared/src/components/GiteaHeader/VersionControlButtons/VersionControlButtons.test.tsx @@ -6,9 +6,10 @@ import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contex import { textMock } from '../../../../../../testing/mocks/i18nMock'; import userEvent from '@testing-library/user-event'; import type { RepoStatus } from 'app-shared/types/RepoStatus'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { queryClientMock } from 'app-shared/mocks/queryClientMock'; import * as testids from '../../../../../../testing/testids'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { repoStatus } from 'app-shared/mocks/mocks'; const user = userEvent.setup(); const org = 'test-org'; @@ -29,37 +30,29 @@ jest.mock('react-router-dom', () => ({ * for instance the `renderWithProviders` method. */ const okRepoStatus: RepoStatus = { - aheadBy: 0, - behindBy: 0, - contentStatus: [], - hasMergeConflict: false, + ...repoStatus, repositoryStatus: 'Ok', }; const aheadRepoStatus: RepoStatus = { + ...repoStatus, aheadBy: 1, - behindBy: 0, - contentStatus: [], - hasMergeConflict: false, repositoryStatus: 'Ok', }; const mergeConflictRepoStatus: RepoStatus = { + ...repoStatus, aheadBy: 1, behindBy: 1, - contentStatus: [], hasMergeConflict: true, repositoryStatus: 'CheckoutConflict', }; -const getRepoMetadata = jest.fn().mockImplementation(() => Promise.resolve({})); const getRepoStatus = jest.fn().mockImplementation(() => Promise.resolve(okRepoStatus)); const getRepoPull = jest.fn().mockImplementation(() => Promise.resolve(okRepoStatus)); const commitAndPushChanges = jest.fn().mockImplementation(() => Promise.resolve(okRepoStatus)); -const defaultQueries: ServicesContextProps = { - ...queriesMock, - getRepoMetadata, +const defaultQueries: Partial = { getRepoStatus, getRepoPull, commitAndPushChanges, @@ -76,7 +69,7 @@ describe('Shared > Version Control > VersionControlHeader', () => { it('should render header when type is not defined', async () => { render(); - await waitFor(() => expect(getRepoMetadata).toHaveBeenCalledTimes(1)); + await waitFor(() => expect(queriesMock.getRepoMetadata).toHaveBeenCalledTimes(1)); expect(await screen.findByTestId(testids.versionControlHeader)).not.toBeNull(); }); @@ -84,7 +77,7 @@ describe('Shared > Version Control > VersionControlHeader', () => { render(); const fetchButton = screen.getByRole('button', { name: textMock('sync_header.fetch_changes') }); await act(() => user.click(fetchButton)); - await waitFor(() => expect(getRepoMetadata).toHaveBeenCalledTimes(1)); + await waitFor(() => expect(queriesMock.getRepoMetadata).toHaveBeenCalledTimes(1)); await waitFor(() => expect(getRepoStatus).toHaveBeenCalledTimes(1)); await waitFor(() => expect(getRepoPull).toHaveBeenCalledTimes(1)); }); @@ -103,7 +96,7 @@ describe('Shared > Version Control > VersionControlHeader', () => { await act(() => user.click(shareButton)); await waitFor(() => expect(mockGetRepoStatus).toHaveBeenCalledTimes(1)); expect( - await screen.findByText(textMock('sync_header.describe_and_validate')) + await screen.findByText(textMock('sync_header.describe_and_validate')), ).toBeInTheDocument(); }); @@ -134,7 +127,7 @@ describe('Shared > Version Control > VersionControlHeader', () => { await act(() => user.click(shareButton)); await waitFor(() => expect(mockGetRepoStatus).toHaveBeenCalledTimes(1)); expect( - await screen.findByText(textMock('sync_header.describe_and_validate')) + await screen.findByText(textMock('sync_header.describe_and_validate')), ).toBeInTheDocument(); }); @@ -153,8 +146,8 @@ describe('Shared > Version Control > VersionControlHeader', () => { await waitFor(() => expect(mockGetRepoStatus).toHaveBeenCalledTimes(1)); await act(() => user.click( - screen.getByRole('button', { name: textMock('sync_header.describe_and_validate_btnText') }) - ) + screen.getByRole('button', { name: textMock('sync_header.describe_and_validate_btnText') }), + ), ); await waitFor(() => expect(commitAndPushChanges).toHaveBeenCalledTimes(1)); }); @@ -177,8 +170,8 @@ describe('Shared > Version Control > VersionControlHeader', () => { await waitFor(() => expect(mockGetRepoStatus).toHaveBeenCalledTimes(1)); await act(() => user.click( - screen.getByRole('button', { name: textMock('sync_header.describe_and_validate_btnText') }) - ) + screen.getByRole('button', { name: textMock('sync_header.describe_and_validate_btnText') }), + ), ); await waitFor(() => expect(mockCommitAndPushChanges).toHaveBeenCalledTimes(1)); await waitFor(() => expect(mockConsoleError).toHaveBeenCalledTimes(1)); @@ -207,22 +200,27 @@ describe('Shared > Version Control > VersionControlHeader', () => { await waitFor(() => expect(mockGetRepoStatus).toHaveBeenCalledTimes(1)); await act(() => user.click( - screen.getByRole('button', { name: textMock('sync_header.describe_and_validate_btnText') }) - ) + screen.getByRole('button', { name: textMock('sync_header.describe_and_validate_btnText') }), + ), ); expect(mockConsoleError).toHaveBeenCalled(); expect( - await screen.findByText(textMock('sync_header.merge_conflict_occured')) + await screen.findByText(textMock('sync_header.merge_conflict_occured')), ).toBeInTheDocument(); }); }); const render = ( props: Partial = {}, - queries: Partial = {} + queries: Partial = {}, ) => renderRtl( - - - + + + , ); diff --git a/frontend/packages/shared/src/components/InputActionWrapper/InputActionWrapper.test.tsx b/frontend/packages/shared/src/components/InputActionWrapper/InputActionWrapper.test.tsx index 4c2b307f7e4..e5cb6b1a0a3 100644 --- a/frontend/packages/shared/src/components/InputActionWrapper/InputActionWrapper.test.tsx +++ b/frontend/packages/shared/src/components/InputActionWrapper/InputActionWrapper.test.tsx @@ -59,21 +59,21 @@ describe('InputActionWrapper', () => { render(); const saveButton = screen.getByLabelText('general.save'); await act(() => user.click(saveButton)); - expect(mockProps.onSaveClick).toBeCalledTimes(1); + expect(mockProps.onSaveClick).toHaveBeenCalledTimes(1); }); it('triggers delete click on delete button click', async () => { render(); const deleteButton = screen.getByLabelText('general.delete'); await act(() => user.click(deleteButton)); - expect(mockProps.onDeleteClick).toBeCalledTimes(1); + expect(mockProps.onDeleteClick).toHaveBeenCalledTimes(1); }); it('check that handleActionClick is called when edit button is clicked', async () => { render(); const editButton = screen.getByLabelText('general.edit'); await act(() => user.click(editButton)); - expect(mockProps.onEditClick).toBeCalledTimes(1); + expect(mockProps.onEditClick).toHaveBeenCalledTimes(1); }); it('check that handleHover is called when onMouseOver is called ', async () => { diff --git a/frontend/packages/shared/src/components/altinnHeader/AltinnHeader.test.tsx b/frontend/packages/shared/src/components/altinnHeader/AltinnHeader.test.tsx index a847701cfb7..16d8d04e964 100644 --- a/frontend/packages/shared/src/components/altinnHeader/AltinnHeader.test.tsx +++ b/frontend/packages/shared/src/components/altinnHeader/AltinnHeader.test.tsx @@ -111,6 +111,7 @@ const render = (props: Partial = {}) => { full_name: 'Test Testesen', id: 1, login: 'username', + userType: 0, }, repository: { clone_url: 'clone_url', @@ -126,7 +127,7 @@ const render = (props: Partial = {}) => { login: 'test-org', email: 'test-email', id: 1, - UserType: 1, + userType: 1, }, updated_at: 'never', created_at: 'now', diff --git a/frontend/packages/shared/src/components/altinnHeader/AltinnHeader.tsx b/frontend/packages/shared/src/components/altinnHeader/AltinnHeader.tsx index af9ae5d85ec..ac89184f6be 100644 --- a/frontend/packages/shared/src/components/altinnHeader/AltinnHeader.tsx +++ b/frontend/packages/shared/src/components/altinnHeader/AltinnHeader.tsx @@ -5,7 +5,7 @@ import { AltinnSubMenu } from '../altinnSubHeader'; import { AltinnHeaderMenu } from '../altinnHeaderMenu'; import { AltinnHeaderButton } from '../altinnHeaderButtons/AltinnHeaderButton'; import { AltinnHeaderProfile } from '../AltinnHeaderProfile'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import classnames from 'classnames'; import { AltinnButtonActionItem, AltinnHeaderVariant } from './types'; import { Repository } from 'app-shared/types/Repository'; diff --git a/frontend/packages/shared/src/contexts/ServicesContext.test.tsx b/frontend/packages/shared/src/contexts/ServicesContext.test.tsx index 8739d4c0316..639e8decae9 100644 --- a/frontend/packages/shared/src/contexts/ServicesContext.test.tsx +++ b/frontend/packages/shared/src/contexts/ServicesContext.test.tsx @@ -27,7 +27,7 @@ const wrapper = ({ queries = {}, }: { children: React.JSX.Element; - queries: Partial; + queries?: Partial; }) => { const allQueries: ServicesContextProps = { ...queriesMock, @@ -40,7 +40,6 @@ describe('ServicesContext', () => { it('logs the user out after displaying a toast for a given time when the api says unauthorized', async () => { const mockConsoleError = jest.spyOn(console, 'error').mockImplementation(); jest.spyOn(global, 'setTimeout'); - const logout = jest.fn().mockImplementation(() => Promise.resolve()); renderHook( () => useQuery({ @@ -50,14 +49,14 @@ describe('ServicesContext', () => { }), { wrapper: ({ children }) => { - return wrapper({ children, queries: { logout } }); + return wrapper({ children }); }, }, ); expect(await screen.findByText(textMock('api_errors.Unauthorized'))).toBeInTheDocument(); jest.runAllTimers(); await waitFor(() => { - expect(logout).toHaveBeenCalledTimes(1); + expect(queriesMock.logout).toHaveBeenCalledTimes(1); }); expect(mockConsoleError).toHaveBeenCalled(); diff --git a/frontend/packages/shared/src/hooks/mutations/useRepoCommitAndPushMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useRepoCommitAndPushMutation.test.ts index 1e22dd3bdc5..569274c0311 100644 --- a/frontend/packages/shared/src/hooks/mutations/useRepoCommitAndPushMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useRepoCommitAndPushMutation.test.ts @@ -1,5 +1,6 @@ import { CreateRepoCommitPayload } from 'app-shared/types/api'; -import { queriesMock, renderHookWithMockStore } from 'app-development/test/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from 'app-development/test/mocks'; import { useRepoCommitAndPushMutation } from './useRepoCommitAndPushMutation'; // Test data: diff --git a/frontend/packages/shared/src/hooks/mutations/useUpsertTextResourcesMutation.test.ts b/frontend/packages/shared/src/hooks/mutations/useUpsertTextResourcesMutation.test.ts index 0482a5c9089..2a4afedac88 100644 --- a/frontend/packages/shared/src/hooks/mutations/useUpsertTextResourcesMutation.test.ts +++ b/frontend/packages/shared/src/hooks/mutations/useUpsertTextResourcesMutation.test.ts @@ -1,7 +1,11 @@ -import { queriesMock, renderHookWithMockStore } from '../../../../ux-editor/src/testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../../../ux-editor/src/testing/mocks'; import { waitFor } from '@testing-library/react'; import { useTextResourcesQuery } from '../../../../../app-development/hooks/queries'; -import { UpsertTextResourcesMutationArgs, useUpsertTextResourcesMutation } from './useUpsertTextResourcesMutation'; +import { + UpsertTextResourcesMutationArgs, + useUpsertTextResourcesMutation, +} from './useUpsertTextResourcesMutation'; import { ITextResource } from 'app-shared/types/global'; // Test data: @@ -18,12 +22,16 @@ describe('useUpsertTextResourcesMutation', () => { const { result: upsertTextResources } = await renderUpsertTextResourcesMutation(); await upsertTextResources.current.mutateAsync(args); expect(queriesMock.upsertTextResources).toHaveBeenCalledTimes(1); - expect(queriesMock.upsertTextResources).toHaveBeenCalledWith(org, app, language, { [textId]: textValue }); + expect(queriesMock.upsertTextResources).toHaveBeenCalledWith(org, app, language, { + [textId]: textValue, + }); }); }); const renderUpsertTextResourcesMutation = async () => { - const { result: texts } = renderHookWithMockStore()(() => useTextResourcesQuery(org, app)).renderHookResult; + const { result: texts } = renderHookWithMockStore()(() => + useTextResourcesQuery(org, app), + ).renderHookResult; await waitFor(() => expect(texts.current.isSuccess).toBe(true)); return renderHookWithMockStore()(() => useUpsertTextResourcesMutation(org, app)).renderHookResult; -} +}; diff --git a/frontend/packages/shared/src/hooks/queries/useTextResourcesQuery.test.ts b/frontend/packages/shared/src/hooks/queries/useTextResourcesQuery.test.ts index 630c10de156..1f75ede4724 100644 --- a/frontend/packages/shared/src/hooks/queries/useTextResourcesQuery.test.ts +++ b/frontend/packages/shared/src/hooks/queries/useTextResourcesQuery.test.ts @@ -1,4 +1,8 @@ -import { queriesMock, renderHookWithMockStore, textLanguagesMock } from '../../../../ux-editor/src/testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { + renderHookWithMockStore, + textLanguagesMock, +} from '../../../../ux-editor/src/testing/mocks'; import { useTextResourcesQuery } from './useTextResourcesQuery'; import { waitFor } from '@testing-library/react'; @@ -8,10 +12,16 @@ const app = 'app'; describe('useTextResourcesQuery', () => { it('Calls getTextResources for each language', async () => { - const { result: resourcesResult } = renderHookWithMockStore()(() => useTextResourcesQuery(org, app)).renderHookResult; + const getTextLanguages = jest.fn().mockImplementation(() => Promise.resolve(textLanguagesMock)); + const { result: resourcesResult } = renderHookWithMockStore( + {}, + { + getTextLanguages, + }, + )(() => useTextResourcesQuery(org, app)).renderHookResult; await waitFor(() => expect(resourcesResult.current.isSuccess).toBe(true)); - expect(queriesMock.getTextLanguages).toHaveBeenCalledTimes(1); - expect(queriesMock.getTextLanguages).toHaveBeenCalledWith(org, app); + expect(getTextLanguages).toHaveBeenCalledTimes(1); + expect(getTextLanguages).toHaveBeenCalledWith(org, app); expect(queriesMock.getTextResources).toHaveBeenCalledTimes(textLanguagesMock.length); textLanguagesMock.forEach((language) => { expect(queriesMock.getTextResources).toHaveBeenCalledWith(org, app, language); diff --git a/frontend/packages/shared/src/hooks/queries/useUserQuery.ts b/frontend/packages/shared/src/hooks/queries/useUserQuery.ts index 878f7259753..ff467684fe1 100644 --- a/frontend/packages/shared/src/hooks/queries/useUserQuery.ts +++ b/frontend/packages/shared/src/hooks/queries/useUserQuery.ts @@ -1,6 +1,6 @@ import { useQuery, UseQueryResult } from '@tanstack/react-query'; import { useServicesContext } from 'app-shared/contexts/ServicesContext'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { QueryKey } from 'app-shared/types/QueryKey'; export const useUserQuery = (): UseQueryResult => { diff --git a/frontend/packages/shared/src/mocks/mocks.ts b/frontend/packages/shared/src/mocks/mocks.ts new file mode 100644 index 00000000000..12908a76405 --- /dev/null +++ b/frontend/packages/shared/src/mocks/mocks.ts @@ -0,0 +1,222 @@ +import { + AppDeploymentsResponse, + AppReleasesResponse, + CreateRepoCommitPayload, + DatamodelMetadataResponse, + SearchRepositoryResponse, +} from 'app-shared/types/api'; +import { LayoutSets } from 'app-shared/types/api/LayoutSetsResponse'; +import { NewsList } from 'app-shared/types/api/NewsList'; +import { BranchStatus } from 'app-shared/types/BranchStatus'; +import { Commit } from 'app-shared/types/Commit'; +import { OrgsState } from 'app-shared/types/OrgsState'; +import { RepoStatus } from 'app-shared/types/RepoStatus'; +import { RuleConfig } from 'app-shared/types/RuleConfig'; +import { ITextResourcesWithLanguage } from 'app-shared/types/global'; +import { User } from 'app-shared/types/Repository'; +import { AppConfig } from 'app-shared/types/AppConfig'; +import { Policy } from '@altinn/policy-editor'; +import { ApplicationMetadata } from 'app-shared/types/ApplicationMetadata'; +import { Resource, ResourceVersionStatus, Validation } from 'app-shared/types/ResourceAdm'; +import { AppLibVersion } from 'app-shared/types/AppLibVersion'; +import { BuildResult, BuildStatus } from 'app-shared/types/Build'; +import { AppDeployment } from 'app-shared/types/AppDeployment'; +import { DeployEnvironment } from 'app-shared/types/DeployEnvironment'; +import { Organization } from 'app-shared/types/Organization'; +import { Repository } from 'app-shared/types/Repository'; + +export const appReleasesResponse: AppReleasesResponse = { + results: [], +}; + +export const branchStatus: BranchStatus = { + commit: { + author: '', + committer: '', + id: '', + }, + name: '', +}; + +export const datamodelMetadataResponse: DatamodelMetadataResponse = { + elements: {}, +}; + +export const appDeploymentsResponse: AppDeploymentsResponse = { + results: [], +}; + +export const appDeployment: AppDeployment = { + id: '', + tagName: '', + app: '', + org: '', + envName: '', + deployedInEnv: false, + createdBy: '', + created: '', + build: { + id: '', + status: BuildStatus.completed, + result: BuildResult.succeeded, + started: '', + finished: '', + }, +}; + +export const deployEnvironment: DeployEnvironment = { + appsUrl: '', + platformUrl: '', + hostname: '', + appPrefix: '', + platformPrefix: '', + name: '', + type: '', +}; + +export const layoutSets: LayoutSets = { + sets: [], +}; + +export const newsList: NewsList = { + news: [], +}; + +export const orgsState: OrgsState = { + orgs: {}, +}; + +export const commit: Commit = { + message: '', + author: { name: '', email: '', when: new Date(null) }, + comitter: { name: '', email: '', when: new Date(null) }, + sha: '', + messageShort: '', + encoding: '', +}; + +export const repoStatus: RepoStatus = { + aheadBy: 0, + behindBy: 0, + contentStatus: [], + hasMergeConflict: false, + repositoryStatus: '', +}; + +export const ruleConfig: RuleConfig = { + data: { + ruleConnection: {}, + conditionalRendering: {}, + }, +}; + +export const textResourcesWithLanguage: ITextResourcesWithLanguage = { + language: '', + resources: [], +}; + +export const user: User = { + avatar_url: '', + email: '', + full_name: '', + id: 1, + login: '', + userType: 0, +}; + +export const appConfig: AppConfig = { + repositoryName: '', + serviceName: '', + serviceId: '', + serviceDescription: '', +}; + +export const policy: Policy = { + rules: [], + requiredAuthenticationLevelEndUser: '0', + requiredAuthenticationLevelOrg: '', +}; + +export const applicationMetadata: ApplicationMetadata = { + id: '', + org: '', +}; + +export const resourceVersionStatus: ResourceVersionStatus = { + publishedVersions: [], +}; + +export const validation: Validation = { + status: 200, + errors: [], +}; + +export const appLibVersion: AppLibVersion = { + version: '', +}; + +export const repository: Repository = { + clone_url: '', + created_at: '', + default_branch: '', + description: '', + empty: false, + fork: false, + forks_count: 0, + full_name: '', + html_url: '', + id: 1, + is_cloned_to_local: false, + mirror: false, + name: '', + open_issues_count: 0, + owner: { + avatar_url: '', + email: '', + full_name: '', + id: 1, + login: '', + userType: 0, + }, + permissions: { + admin: false, + pull: false, + push: false, + }, + private: false, + repositoryCreatedStatus: 0, + size: 0, + ssh_url: '', + stars_count: 0, + updated_at: '', + watchers_count: 0, + website: '', +}; + +export const createRepoCommitPayload: CreateRepoCommitPayload = { + message: '', + org: '', + repository: '', +}; + +export const organization: Organization = { + avatar_url: '', + id: 1, + username: '', +}; + +export const resource: Resource = { + identifier: '', + title: { + nb: '', + nn: '', + en: '', + }, +}; + +export const searchRepositoryResponse: SearchRepositoryResponse = { + data: [], + ok: false, + totalCount: 0, + totalPages: 0, +}; diff --git a/frontend/packages/shared/src/mocks/queriesMock.ts b/frontend/packages/shared/src/mocks/queriesMock.ts index 16619877477..3600441b7cf 100644 --- a/frontend/packages/shared/src/mocks/queriesMock.ts +++ b/frontend/packages/shared/src/mocks/queriesMock.ts @@ -1,94 +1,201 @@ import { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; -import inputComponentSchema from '../../../ux-editor/src/testing/schemas/json/component/Input.schema.v1.json'; -import commonDefsComponentSchema from '../../../ux-editor/src/testing/schemas/json/component/Input.schema.v1.json'; +import { Altinn2LinkService } from 'app-shared/types/Altinn2LinkService'; +import { AppConfig } from 'app-shared/types/AppConfig'; +import { AppLibVersion } from 'app-shared/types/AppLibVersion'; +import { ApplicationMetadata } from 'app-shared/types/ApplicationMetadata'; +import { BranchStatus } from 'app-shared/types/BranchStatus'; +import { Commit } from 'app-shared/types/Commit'; +import { DatamodelMetadataJson, DatamodelMetadataXsd } from 'app-shared/types/DatamodelMetadata'; +import { DeployEnvironment } from 'app-shared/types/DeployEnvironment'; +import { JsonSchema } from 'app-shared/types/JsonSchema'; +import { Organization } from 'app-shared/types/Organization'; +import { OrgsState } from 'app-shared/types/OrgsState'; +import { RepoStatus } from 'app-shared/types/RepoStatus'; +import { Repository } from 'app-shared/types/Repository'; +import { + Resource, + ResourceListItem, + ResourceVersionStatus, + Validation, +} from 'app-shared/types/ResourceAdm'; +import { RuleConfig } from 'app-shared/types/RuleConfig'; +import { User } from 'app-shared/types/Repository'; +import { + AppDeploymentsResponse, + AppReleasesResponse, + CreateRepoCommitPayload, + DatamodelMetadataResponse, + FormLayoutsResponse, + SearchRepositoryResponse, +} from 'app-shared/types/api'; +import { LayoutSets } from 'app-shared/types/api/LayoutSetsResponse'; +import { NewsList } from 'app-shared/types/api/NewsList'; +import { + IFrontEndSettings, + ILayoutSettings, + ITextResourcesObjectFormat, + ITextResourcesWithLanguage, +} from 'app-shared/types/global'; +import { WidgetSettingsResponse } from 'app-shared/types/widgetTypes'; +import { Policy, PolicyAction, PolicySubject } from 'packages/policy-editor'; +import { + appConfig, + appDeploymentsResponse, + appLibVersion, + appReleasesResponse, + applicationMetadata, + branchStatus, + commit, + createRepoCommitPayload, + datamodelMetadataResponse, + layoutSets, + newsList, + orgsState, + policy, + repoStatus, + repository, + resource, + resourceVersionStatus, + ruleConfig, + searchRepositoryResponse, + textResourcesWithLanguage, + user, + validation, +} from './mocks'; + export const queriesMock: ServicesContextProps = { - addAppAttachmentMetadata: jest.fn(), - addLanguageCode: jest.fn(), - addLayoutSet: jest.fn(), - addRepo: jest.fn(), - addXsdFromRepo: jest.fn(), - commitAndPushChanges: jest.fn(), - configureLayoutSet: jest.fn(), - copyApp: jest.fn(), - createDatamodel: jest.fn(), - createDeployment: jest.fn(), - createRelease: jest.fn(), - createRepoCommit: jest.fn(), - createResource: jest.fn(), - deleteAppAttachmentMetadata: jest.fn(), - deleteDatamodel: jest.fn(), - deleteFormLayout: jest.fn(), - deleteLanguageCode: jest.fn(), - generateModels: jest.fn(), - getAppReleases: jest.fn(), - getBranchStatus: jest.fn(), - getComponentSchema: jest.fn().mockResolvedValue(inputComponentSchema), - getComponentsCommonDefsSchema: jest.fn().mockResolvedValue(commonDefsComponentSchema), - getDatamodel: jest.fn(), - getDatamodelMetadata: jest.fn(), - getDatamodelsJson: jest.fn(), - getDatamodelsXsd: jest.fn(), - getDeployPermissions: jest.fn(), - getDeployments: jest.fn(), - getEnvironments: jest.fn(), - getExpressionSchema: jest.fn(), - getFormLayoutSettings: jest.fn(), - getFormLayouts: jest.fn(), - getFrontEndSettings: jest.fn(), - getInstanceIdForPreview: jest.fn(), - getLayoutSchema: jest.fn(), - getLayoutSets: jest.fn(), - getNewsList: jest.fn(), - getNumberFormatSchema: jest.fn(), - getOptionListIds: jest.fn(), - getOrgList: jest.fn(), - getOrganizations: jest.fn(), - getPolicy: jest.fn(), - getPolicyActions: jest.fn(), - getPolicySubjects: jest.fn(), - getRepoMetadata: jest.fn(), - getRepoPull: jest.fn(), - getRepoStatus: jest.fn(), - getResource: jest.fn(), - getResourceList: jest.fn(), - getResourcePublishStatus: jest.fn(), - getRuleConfig: jest.fn(), - getRuleModel: jest.fn(), - getStarredRepos: jest.fn(), - getTextLanguages: jest.fn(), - getTextResources: jest.fn(), - getUser: jest.fn(), - getValidatePolicy: jest.fn(), - getValidateResource: jest.fn(), - getWidgetSettings: jest.fn(), - logout: jest.fn(), - pushRepoChanges: jest.fn(), - resetRepoChanges: jest.fn(), - saveDatamodel: jest.fn(), - saveFormLayout: jest.fn(), - saveFormLayoutSettings: jest.fn(), - saveRuleConfig: jest.fn(), - searchRepos: jest.fn(), - setStarredRepo: jest.fn(), - unsetStarredRepo: jest.fn(), - updateAppAttachmentMetadata: jest.fn(), - updateFormLayoutName: jest.fn(), - updatePolicy: jest.fn(), - updateResource: jest.fn(), - updateTextId: jest.fn(), - updateTranslationByLangCode: jest.fn(), - upsertTextResources: jest.fn(), - getBpmnFile: jest.fn(), - updateBpmnXml: jest.fn(), - getAppPolicy: jest.fn(), - updateAppPolicy: jest.fn(), - getAppConfig: jest.fn(), - getAppMetadata: jest.fn(), - updateAppMetadata: jest.fn(), - updateAppConfig: jest.fn(), - getRepoInitialCommit: jest.fn(), - publishResource: jest.fn(), - getAltinn2LinkServices: jest.fn(), - importResourceFromAltinn2: jest.fn(), - getAppLibVersion: jest.fn(), + // Queries + getAppReleases: jest + .fn() + .mockImplementation(() => Promise.resolve(appReleasesResponse)), + getBranchStatus: jest.fn().mockImplementation(() => Promise.resolve(branchStatus)), + getComponentSchema: jest.fn().mockImplementation(() => Promise.resolve([])), + getComponentsCommonDefsSchema: jest.fn().mockImplementation(() => Promise.resolve([])), + getDatamodel: jest.fn().mockImplementation(() => Promise.resolve({})), + getDatamodelMetadata: jest + .fn() + .mockImplementation(() => + Promise.resolve(datamodelMetadataResponse), + ), + getDatamodelsJson: jest + .fn() + .mockImplementation(() => Promise.resolve([])), + getDatamodelsXsd: jest.fn().mockImplementation(() => Promise.resolve([])), + getDeployPermissions: jest.fn().mockImplementation(() => Promise.resolve([])), + getDeployments: jest + .fn() + .mockImplementation(() => Promise.resolve(appDeploymentsResponse)), + getEnvironments: jest.fn().mockImplementation(() => Promise.resolve([])), + getExpressionSchema: jest.fn().mockImplementation(() => Promise.resolve([])), + getFormLayoutSettings: jest.fn().mockImplementation(() => Promise.resolve({})), + getFormLayouts: jest.fn().mockImplementation(() => Promise.resolve({})), + getFrontEndSettings: jest.fn().mockImplementation(() => Promise.resolve({})), + getInstanceIdForPreview: jest.fn().mockImplementation(() => Promise.resolve('')), + getLayoutSchema: jest.fn().mockImplementation(() => Promise.resolve([])), + getLayoutSets: jest.fn().mockImplementation(() => Promise.resolve(layoutSets)), + getNewsList: jest.fn().mockImplementation(() => Promise.resolve(newsList)), + getNumberFormatSchema: jest.fn().mockImplementation(() => Promise.resolve([])), + getOptionListIds: jest.fn().mockImplementation(() => Promise.resolve([])), + getOrgList: jest.fn().mockImplementation(() => Promise.resolve(orgsState)), + getOrganizations: jest.fn().mockImplementation(() => Promise.resolve([])), + getRepoInitialCommit: jest.fn().mockImplementation(() => Promise.resolve(commit)), + getRepoMetadata: jest.fn().mockImplementation(() => Promise.resolve(repository)), + getRepoPull: jest.fn().mockImplementation(() => Promise.resolve(repoStatus)), + getRepoStatus: jest.fn().mockImplementation(() => Promise.resolve(repoStatus)), + getRuleConfig: jest.fn().mockImplementation(() => Promise.resolve(ruleConfig)), + getRuleModel: jest.fn().mockImplementation(() => Promise.resolve('')), + getStarredRepos: jest.fn().mockImplementation(() => Promise.resolve([])), + getTextLanguages: jest.fn().mockImplementation(() => Promise.resolve([])), + getTextResources: jest + .fn() + .mockImplementation(() => + Promise.resolve(textResourcesWithLanguage), + ), + getUser: jest.fn().mockImplementation(() => Promise.resolve(user)), + getWidgetSettings: jest + .fn() + .mockImplementation(() => Promise.resolve({})), + searchRepos: jest + .fn() + .mockImplementation(() => Promise.resolve(searchRepositoryResponse)), + + // Queries - Settings modal + getAppConfig: jest.fn().mockImplementation(() => Promise.resolve(appConfig)), + getAppPolicy: jest.fn().mockImplementation(() => Promise.resolve(policy)), + getAppMetadata: jest + .fn() + .mockImplementation(() => Promise.resolve(applicationMetadata)), + + // Queries - Resourceadm + getAltinn2LinkServices: jest + .fn() + .mockImplementation(() => Promise.resolve([])), + getPolicyActions: jest.fn().mockImplementation(() => Promise.resolve([])), + getPolicy: jest.fn().mockImplementation(() => Promise.resolve(policy)), + getPolicySubjects: jest.fn().mockImplementation(() => Promise.resolve([])), + getResource: jest.fn().mockImplementation(() => Promise.resolve(resource)), + getResourceList: jest.fn().mockImplementation(() => Promise.resolve([])), + getResourcePublishStatus: jest + .fn() + .mockImplementation(() => Promise.resolve(resourceVersionStatus)), + getValidatePolicy: jest.fn().mockImplementation(() => Promise.resolve(validation)), + getValidateResource: jest.fn().mockImplementation(() => Promise.resolve(validation)), + + // Queries - PrgetBpmnFile + getAppLibVersion: jest + .fn() + .mockImplementation(() => Promise.resolve(appLibVersion)), + getBpmnFile: jest.fn().mockImplementation(() => Promise.resolve('')), + + // Mutations + addAppAttachmentMetadata: jest.fn().mockImplementation(() => Promise.resolve()), + addLanguageCode: jest.fn().mockImplementation(() => Promise.resolve()), + addLayoutSet: jest.fn().mockImplementation(() => Promise.resolve()), + addRepo: jest.fn().mockImplementation(() => Promise.resolve(repository)), + addXsdFromRepo: jest.fn().mockImplementation(() => Promise.resolve({})), + commitAndPushChanges: jest + .fn() + .mockImplementation(() => Promise.resolve(createRepoCommitPayload)), + configureLayoutSet: jest.fn().mockImplementation(() => Promise.resolve(layoutSets)), + copyApp: jest.fn().mockImplementation(() => Promise.resolve()), + createDatamodel: jest.fn().mockImplementation(() => Promise.resolve({})), + createDeployment: jest.fn().mockImplementation(() => Promise.resolve()), + createRelease: jest.fn().mockImplementation(() => Promise.resolve()), + createRepoCommit: jest + .fn() + .mockImplementation(() => Promise.resolve(createRepoCommitPayload)), + deleteAppAttachmentMetadata: jest.fn().mockImplementation(() => Promise.resolve()), + deleteDatamodel: jest.fn().mockImplementation(() => Promise.resolve()), + deleteFormLayout: jest.fn().mockImplementation(() => Promise.resolve()), + deleteLanguageCode: jest.fn().mockImplementation(() => Promise.resolve()), + generateModels: jest.fn().mockImplementation(() => Promise.resolve()), + logout: jest.fn().mockImplementation(() => Promise.resolve()), + pushRepoChanges: jest.fn().mockImplementation(() => Promise.resolve()), + resetRepoChanges: jest.fn().mockImplementation(() => Promise.resolve()), + saveDatamodel: jest.fn().mockImplementation(() => Promise.resolve()), + saveFormLayout: jest.fn().mockImplementation(() => Promise.resolve()), + saveFormLayoutSettings: jest.fn().mockImplementation(() => Promise.resolve({})), + saveRuleConfig: jest.fn().mockImplementation(() => Promise.resolve(ruleConfig)), + setStarredRepo: jest.fn().mockImplementation(() => Promise.resolve()), + unsetStarredRepo: jest.fn().mockImplementation(() => Promise.resolve()), + updateAppAttachmentMetadata: jest.fn().mockImplementation(() => Promise.resolve()), + updateFormLayoutName: jest.fn().mockImplementation(() => Promise.resolve()), + updateTextId: jest.fn().mockImplementation(() => Promise.resolve()), + updateTranslationByLangCode: jest.fn().mockImplementation(() => Promise.resolve()), + updateAppPolicy: jest.fn().mockImplementation(() => Promise.resolve()), + updateAppMetadata: jest.fn().mockImplementation(() => Promise.resolve()), + updateAppConfig: jest.fn().mockImplementation(() => Promise.resolve()), + upsertTextResources: jest + .fn() + .mockImplementation(() => Promise.resolve({})), + + // Mutations - Resourceadm + createResource: jest.fn().mockImplementation(() => Promise.resolve()), + importResourceFromAltinn2: jest.fn().mockImplementation(() => Promise.resolve(null)), + publishResource: jest.fn().mockImplementation(() => Promise.resolve()), + updatePolicy: jest.fn().mockImplementation(() => Promise.resolve()), + updateResource: jest.fn().mockImplementation(() => Promise.resolve()), + + // Mutations - ProcessEditor + updateBpmnXml: jest.fn().mockImplementation(() => Promise.resolve()), }; diff --git a/frontend/packages/shared/src/navigation/main-header/Header.test.tsx b/frontend/packages/shared/src/navigation/main-header/Header.test.tsx index 8d00814d7f5..87f51744d1b 100644 --- a/frontend/packages/shared/src/navigation/main-header/Header.test.tsx +++ b/frontend/packages/shared/src/navigation/main-header/Header.test.tsx @@ -1,18 +1,24 @@ import React from 'react'; import { render as rtlRender, screen } from '@testing-library/react'; -import Router from "react-router-dom"; +import Router from 'react-router-dom'; -import { getOrgNameByUsername, Header, HeaderContext, SelectedContextType } from './Header'; +import { + getOrgNameByUsername, + Header, + HeaderContext, + IHeaderContext, + SelectedContextType, +} from './Header'; const orgUsername = 'username1'; const orgFullName = 'Organization 1'; -jest.mock("react-router-dom", () => ({ - ...jest.requireActual("react-router-dom"), +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), useParams: jest.fn(), useNavigate: jest.fn(), - useLocation: jest.fn() + useLocation: jest.fn(), })); describe('Header', () => { @@ -45,13 +51,13 @@ describe('Header', () => { ...orgProps, id: 1, full_name: 'full_name 1', - username: 'username1' + username: 'username1', }, { ...orgProps, id: 2, full_name: 'full_name 2', - username: 'username2' + username: 'username2', }, ]; @@ -85,8 +91,10 @@ describe('Header', () => { const render = ({ selectedContext = SelectedContextType.Self, -}: {selectedContext: string | SelectedContextType}) => { - jest.spyOn(Router, 'useParams').mockReturnValue({ selectedContext }) +}: { + selectedContext: string | SelectedContextType; +}) => { + jest.spyOn(Router, 'useParams').mockReturnValue({ selectedContext }); const orgProps = { avatar_url: 'avatar_url', @@ -98,18 +106,21 @@ const render = ({ full_name: orgFullName, }; - const headerContextValue = { + const headerContextValue: IHeaderContext = { selectableOrgs: [{ ...orgProps }], user: { full_name: 'John Smith', avatar_url: 'avatar_url', login: 'login', + email: '', + id: 0, + userType: 0, }, }; return rtlRender(
- + , ); }; diff --git a/frontend/packages/shared/src/navigation/main-header/Header.tsx b/frontend/packages/shared/src/navigation/main-header/Header.tsx index f267de8e5b0..42ede8016dd 100644 --- a/frontend/packages/shared/src/navigation/main-header/Header.tsx +++ b/frontend/packages/shared/src/navigation/main-header/Header.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { AppBar, Grid, Toolbar, Typography } from '@mui/material'; -import type { IGiteaOrganisation, IUser } from '../../types/global'; +import type { Organization } from '../../types/Organization'; +import type { User } from '../../types/Repository'; import AltinnStudioLogo from './AltinnStudioLogo'; import { HeaderMenu } from './HeaderMenu'; import classes from './Header.module.css'; @@ -12,8 +13,8 @@ export enum SelectedContextType { } export interface IHeaderContext { - selectableOrgs?: IGiteaOrganisation[]; - user: IUser; + selectableOrgs?: Organization[]; + user: User; } export const HeaderContext = React.createContext({ @@ -21,12 +22,12 @@ export const HeaderContext = React.createContext({ user: undefined, }); -export const getOrgNameByUsername = (username: string, orgs: IGiteaOrganisation[]) => { +export const getOrgNameByUsername = (username: string, orgs: Organization[]) => { const org = orgs?.find((o) => o.username === username); return org?.full_name || org?.username; }; -export const getOrgUsernameByUsername = (username: string, orgs: IGiteaOrganisation[]) => { +export const getOrgUsernameByUsername = (username: string, orgs: Organization[]) => { const org = orgs?.find((o) => o.username === username); return org?.username; }; diff --git a/frontend/packages/shared/src/navigation/main-header/HeaderMenu.test.tsx b/frontend/packages/shared/src/navigation/main-header/HeaderMenu.test.tsx index 946f4990fb7..877eea19e7e 100644 --- a/frontend/packages/shared/src/navigation/main-header/HeaderMenu.test.tsx +++ b/frontend/packages/shared/src/navigation/main-header/HeaderMenu.test.tsx @@ -1,6 +1,6 @@ import React from 'react'; import * as networking from '../../utils/networking'; -import { HeaderContext, SelectedContextType } from './Header'; +import { HeaderContext, IHeaderContext, SelectedContextType } from './Header'; import type { HeaderMenuProps } from './HeaderMenu'; import { HeaderMenu } from './HeaderMenu'; import { act, render as rtlRender, screen } from '@testing-library/react'; @@ -13,11 +13,11 @@ jest.mock('../../utils/networking', () => ({ })); const mockedNavigate = jest.fn(); -jest.mock("react-router-dom", () => ({ - ...jest.requireActual("react-router-dom"), +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), useNavigate: () => mockedNavigate, useLocation: () => ({ - search: '' + search: '', }), })); @@ -99,7 +99,7 @@ const openMenu = async () => { const render = (props: Partial = {}) => { const handleSetSelectedContext = jest.fn(); - const headerContextValue = { + const headerContextValue: IHeaderContext = { selectableOrgs: [ { avatar_url: 'avatar_url', @@ -115,6 +115,9 @@ const render = (props: Partial = {}) => { full_name: 'John Smith', avatar_url: 'avatar_url', login: 'login', + email: '', + id: 0, + userType: 0, }, }; const allProps = { @@ -127,7 +130,7 @@ const render = (props: Partial = {}) => { rendered: rtlRender( - + , ), handleSetSelectedContext, }; diff --git a/frontend/packages/shared/src/navigation/main-header/ProfileMenu/ProfileMenu.tsx b/frontend/packages/shared/src/navigation/main-header/ProfileMenu/ProfileMenu.tsx index dfc58095a46..85e87e75283 100644 --- a/frontend/packages/shared/src/navigation/main-header/ProfileMenu/ProfileMenu.tsx +++ b/frontend/packages/shared/src/navigation/main-header/ProfileMenu/ProfileMenu.tsx @@ -5,7 +5,7 @@ import { altinnDocsUrl } from 'app-shared/ext-urls'; import { post } from '../../../utils/networking'; import { repositoryPath, userLogoutAfterPath, userLogoutPath } from '../../../api/paths'; import { useTranslation } from 'react-i18next'; -import { User } from 'app-shared/types/User'; +import { User } from 'app-shared/types/Repository'; import { useStudioUrlParams } from 'app-shared/hooks/useStudioUrlParams'; import { Button } from '@digdir/design-system-react'; import * as testids from '../../../../../../testing/testids'; diff --git a/frontend/packages/shared/src/types/AppLibVersion.ts b/frontend/packages/shared/src/types/AppLibVersion.ts new file mode 100644 index 00000000000..41755d1e97a --- /dev/null +++ b/frontend/packages/shared/src/types/AppLibVersion.ts @@ -0,0 +1,3 @@ +export interface AppLibVersion { + version: string; +} diff --git a/frontend/packages/shared/src/types/Repository.ts b/frontend/packages/shared/src/types/Repository.ts index af02dca3ee6..c0fc1994858 100644 --- a/frontend/packages/shared/src/types/Repository.ts +++ b/frontend/packages/shared/src/types/Repository.ts @@ -13,7 +13,7 @@ export interface Repository { mirror: boolean; name: string; open_issues_count: number; - owner: Owner; + owner: User; permissions: Permissions; private: boolean; repositoryCreatedStatus: number; @@ -25,13 +25,13 @@ export interface Repository { website: string; } -interface Owner { +export interface User { avatar_url: string; email: string; full_name: string; id: number; login: string; - UserType: number; + userType: number; } interface Permissions { diff --git a/frontend/packages/shared/src/types/User.ts b/frontend/packages/shared/src/types/User.ts deleted file mode 100644 index 3cf3f3bdb4a..00000000000 --- a/frontend/packages/shared/src/types/User.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type User = { - avatar_url: string; - email: string; - full_name: string; - id: number; - login: string; -}; \ No newline at end of file diff --git a/frontend/packages/shared/src/types/api/SearchRepositoryResponse.ts b/frontend/packages/shared/src/types/api/SearchRepositoryResponse.ts index 3b0b0e7af8f..de147f55250 100644 --- a/frontend/packages/shared/src/types/api/SearchRepositoryResponse.ts +++ b/frontend/packages/shared/src/types/api/SearchRepositoryResponse.ts @@ -1,7 +1,7 @@ -import { IRepository } from 'app-shared/types/global'; +import { Repository } from 'app-shared/types/Repository'; export interface SearchRepositoryResponse { - data: IRepository[]; + data: Repository[]; ok: boolean; totalCount: number; totalPages: number; diff --git a/frontend/packages/shared/src/types/global.ts b/frontend/packages/shared/src/types/global.ts index 87de827b36e..adf1089d678 100644 --- a/frontend/packages/shared/src/types/global.ts +++ b/frontend/packages/shared/src/types/global.ts @@ -20,41 +20,12 @@ export interface IComponentsSettings { excludeFromPdf?: string[]; } -export interface IRepository { - name: string; - full_name: string; - owner: IUser; - description: string; - is_cloned_to_local: boolean; - updated_at: string; - html_url: string; - clone_url: string; - id: number; - user_has_starred: boolean; -} - export enum RepositoryType { App = 'App', Datamodels = 'Datamodels', Unknown = 'Unknown', } -export interface IUser { - avatar_url: string; - login: string; - full_name: string; -} - -export interface IGiteaOrganisation { - avatar_url: string; - description?: string; - id: number; - location?: string; - username: string; - website?: string; - full_name?: string; -} - export interface IContentStatus { filePath: string; fileStatus: string; diff --git a/frontend/packages/text-editor/src/RightMenu.test.tsx b/frontend/packages/text-editor/src/RightMenu.test.tsx index a255e5bdb32..c8e02b666ce 100644 --- a/frontend/packages/text-editor/src/RightMenu.test.tsx +++ b/frontend/packages/text-editor/src/RightMenu.test.tsx @@ -28,8 +28,10 @@ describe('RightMenu', () => { const render = async () => { rtlRender(); - await waitFor(() => { expect(screen.getByRole('combobox')).toBeInTheDocument(); }); - } + await waitFor(() => { + expect(screen.getByRole('combobox')).toBeInTheDocument(); + }); + }; describe('Delete confirmation dialog', () => { it('should open the confirmation dialog when clicking the delete button', async () => { @@ -41,10 +43,14 @@ describe('RightMenu', () => { const dialog = screen.getByRole('dialog'); expect(dialog).toBeInTheDocument(); - const text = await screen.findByText(textMock('schema_editor.language_display_confirm_delete')); + const text = await screen.findByText( + textMock('schema_editor.language_display_confirm_delete'), + ); expect(text).toBeInTheDocument(); - const confirmButton = screen.getByRole('button', { name: textMock('schema_editor.language_confirm_deletion') }); + const confirmButton = screen.getByRole('button', { + name: textMock('schema_editor.language_confirm_deletion'), + }); expect(confirmButton).toBeInTheDocument(); const cancelButton = screen.getByRole('button', { name: textMock('general.cancel') }); @@ -57,10 +63,12 @@ describe('RightMenu', () => { const deleteButton = screen.getByTestId(testids.deleteButton('en')); await act(() => user.click(deleteButton)); - const confirmButton = screen.getByRole('button', { name: textMock('schema_editor.language_confirm_deletion') }); + const confirmButton = screen.getByRole('button', { + name: textMock('schema_editor.language_confirm_deletion'), + }); await act(() => user.click(confirmButton)); - expect(defaultProps.deleteLanguage).toBeCalledWith('en'); + expect(defaultProps.deleteLanguage).toHaveBeenCalledWith('en'); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); @@ -73,7 +81,7 @@ describe('RightMenu', () => { const cancelButton = screen.getByRole('button', { name: textMock('general.cancel') }); await act(() => user.click(cancelButton)); - expect(defaultProps.deleteLanguage).toBeCalledTimes(0); + expect(defaultProps.deleteLanguage).toHaveBeenCalledTimes(0); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); @@ -85,7 +93,7 @@ describe('RightMenu', () => { await act(() => user.click(document.body)); - expect(defaultProps.deleteLanguage).toBeCalledTimes(0); + expect(defaultProps.deleteLanguage).toHaveBeenCalledTimes(0); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); }); diff --git a/frontend/packages/text-editor/src/TextRow.test.tsx b/frontend/packages/text-editor/src/TextRow.test.tsx index 71d78af3098..ed4cf5fa259 100644 --- a/frontend/packages/text-editor/src/TextRow.test.tsx +++ b/frontend/packages/text-editor/src/TextRow.test.tsx @@ -33,7 +33,7 @@ describe('TextRow', () => { - + , ); }; @@ -147,7 +147,7 @@ describe('TextRow', () => { }); await act(() => user.click(confirmButton)); - expect(removeEntry).toBeCalledWith({ textId: 'key1' }); + expect(removeEntry).toHaveBeenCalledWith({ textId: 'key1' }); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); @@ -162,7 +162,7 @@ describe('TextRow', () => { const cancelButton = screen.getByRole('button', { name: textMock('general.cancel') }); await act(() => user.click(cancelButton)); - expect(removeEntry).toBeCalledTimes(0); + expect(removeEntry).toHaveBeenCalledTimes(0); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); @@ -176,7 +176,7 @@ describe('TextRow', () => { await act(() => user.click(document.body)); - expect(removeEntry).toBeCalledTimes(0); + expect(removeEntry).toHaveBeenCalledTimes(0); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); }); diff --git a/frontend/packages/ux-editor/src/App.test.tsx b/frontend/packages/ux-editor/src/App.test.tsx index b48964bf1e0..c8db7804b36 100644 --- a/frontend/packages/ux-editor/src/App.test.tsx +++ b/frontend/packages/ux-editor/src/App.test.tsx @@ -1,18 +1,25 @@ import React from 'react'; import { screen, waitFor } from '@testing-library/react'; -import { renderWithMockStore } from './testing/mocks'; +import { formLayoutSettingsMock, renderWithMockStore } from './testing/mocks'; import { App } from './App'; import { textMock } from '../../../testing/mocks/i18nMock'; import { typedLocalStorage } from 'app-shared/utils/webStorage'; import { ServicesContextProps } from 'app-shared/contexts/ServicesContext'; import { appStateMock } from './testing/stateMocks'; import { AppContextProps } from './AppContext'; +import ruleHandlerMock from './testing/ruleHandlerMock'; +import { layoutSetsMock } from './testing/layoutMock'; const { selectedLayoutSet } = appStateMock.formDesigner.layout; const removeSelectedLayoutSetMock = jest.fn(); const render = (selectedLayoutSetForRender: string) => { const queries: Partial = { getInstanceIdForPreview: jest.fn().mockImplementation(() => Promise.resolve('test')), + getRuleModel: jest.fn().mockImplementation(() => Promise.resolve(ruleHandlerMock)), + getLayoutSets: jest.fn().mockImplementation(() => Promise.resolve(layoutSetsMock)), + getFormLayoutSettings: jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)), }; const appContextProps: Partial = { selectedLayoutSet: selectedLayoutSetForRender, diff --git a/frontend/packages/ux-editor/src/components/Elements/LayoutSetsContainer.test.tsx b/frontend/packages/ux-editor/src/components/Elements/LayoutSetsContainer.test.tsx index 0d5d42f318b..7d656f6cb24 100644 --- a/frontend/packages/ux-editor/src/components/Elements/LayoutSetsContainer.test.tsx +++ b/frontend/packages/ux-editor/src/components/Elements/LayoutSetsContainer.test.tsx @@ -2,7 +2,8 @@ import React from 'react'; import { act, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { LayoutSetsContainer } from './LayoutSetsContainer'; -import { queryClientMock, renderWithMockStore } from '../../testing/mocks'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; +import { renderWithMockStore } from '../../testing/mocks'; import { layoutSetsMock } from '../../testing/layoutMock'; import { AppContextProps } from '../../AppContext'; import { appStateMock } from '../../testing/stateMocks'; @@ -24,12 +25,8 @@ describe('LayoutSetsContainer', () => { it('renders component', async () => { render(); - expect( - await screen.findByRole('option', { name: layoutSetName1 }), - ).toBeInTheDocument(); - expect( - await screen.findByRole('option', { name: layoutSetName2 }), - ).toBeInTheDocument(); + expect(await screen.findByRole('option', { name: layoutSetName1 })).toBeInTheDocument(); + expect(await screen.findByRole('option', { name: layoutSetName2 })).toBeInTheDocument(); }); it('NativeSelect should be rendered', async () => { @@ -43,7 +40,6 @@ describe('LayoutSetsContainer', () => { await act(() => user.selectOptions(screen.getByRole('combobox'), layoutSetName2)); expect(setSelectedLayoutSetMock).toHaveBeenCalledTimes(1); }); - }); const render = () => { diff --git a/frontend/packages/ux-editor/src/components/FormComponent/FormComponent.test.tsx b/frontend/packages/ux-editor/src/components/FormComponent/FormComponent.test.tsx index 9dd631d3d39..2d6154fedd7 100644 --- a/frontend/packages/ux-editor/src/components/FormComponent/FormComponent.test.tsx +++ b/frontend/packages/ux-editor/src/components/FormComponent/FormComponent.test.tsx @@ -5,7 +5,11 @@ import { HTML5Backend } from 'react-dnd-html5-backend'; import { DndProvider } from 'react-dnd'; import type { IFormComponentProps } from './FormComponent'; import { FormComponent } from './FormComponent'; -import { renderHookWithMockStore, renderWithMockStore } from '../../testing/mocks'; +import { + renderHookWithMockStore, + renderWithMockStore, + textLanguagesMock, +} from '../../testing/mocks'; import { component1IdMock, component1Mock } from '../../testing/layoutMock'; import { textMock } from '../../../../../testing/mocks/i18nMock'; import { useTextResourcesQuery } from 'app-shared/hooks/queries/useTextResourcesQuery'; @@ -52,8 +56,8 @@ describe('FormComponent', () => { const component = screen.getByRole('listitem'); await act(() => user.click(component)); - expect(handleSaveMock).toBeCalledTimes(1); - expect(handleEditMock).toBeCalledTimes(1); + expect(handleSaveMock).toHaveBeenCalledTimes(1); + expect(handleEditMock).toHaveBeenCalledTimes(1); }); describe('Delete confirmation dialog', () => { @@ -91,7 +95,7 @@ describe('FormComponent', () => { }); await act(() => user.click(confirmButton)); - expect(mockDeleteFormComponent).toBeCalledWith(component1IdMock); + expect(mockDeleteFormComponent).toHaveBeenCalledWith(component1IdMock); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); @@ -104,7 +108,7 @@ describe('FormComponent', () => { const cancelButton = screen.getByRole('button', { name: textMock('general.cancel') }); await act(() => user.click(cancelButton)); - expect(mockDeleteFormComponent).toBeCalledTimes(0); + expect(mockDeleteFormComponent).toHaveBeenCalledTimes(0); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); @@ -119,7 +123,7 @@ describe('FormComponent', () => { }); await act(() => user.click(confirmButton)); - expect(mockDeleteFormComponent).toBeCalledTimes(1); + expect(mockDeleteFormComponent).toHaveBeenCalledTimes(1); expect(handleDiscardMock).toHaveBeenCalledTimes(1); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); @@ -132,7 +136,7 @@ describe('FormComponent', () => { await act(() => user.click(document.body)); - expect(mockDeleteFormComponent).toBeCalledTimes(0); + expect(mockDeleteFormComponent).toHaveBeenCalledTimes(0); await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument()); }); }); @@ -200,7 +204,10 @@ const waitForData = async () => { const { result: texts } = renderHookWithMockStore( {}, { - getTextResources: () => Promise.resolve({ language: 'nb', resources: nbTextResources }), + getTextResources: jest + .fn() + .mockImplementation(() => Promise.resolve({ language: 'nb', resources: nbTextResources })), + getTextLanguages: jest.fn().mockImplementation(() => Promise.resolve(textLanguagesMock)), }, )(() => useTextResourcesQuery(org, app)).renderHookResult; await waitFor(() => expect(texts.current.isSuccess).toBe(true)); diff --git a/frontend/packages/ux-editor/src/components/Preview/Preview.test.tsx b/frontend/packages/ux-editor/src/components/Preview/Preview.test.tsx index 9f6a142f8b7..253c30adb2c 100644 --- a/frontend/packages/ux-editor/src/components/Preview/Preview.test.tsx +++ b/frontend/packages/ux-editor/src/components/Preview/Preview.test.tsx @@ -1,7 +1,8 @@ import React, { createRef } from 'react'; import { Preview } from './Preview'; import { act, screen } from '@testing-library/react'; -import { queryClientMock, renderWithMockStore } from '../../testing/mocks'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; +import { renderWithMockStore } from '../../testing/mocks'; import type { IAppState } from '../../types/global'; import { textMock } from '../../../../../testing/mocks/i18nMock'; import userEvent from '@testing-library/user-event'; @@ -9,14 +10,14 @@ import userEvent from '@testing-library/user-event'; describe('Preview', () => { it('Renders an iframe with the ref from AppContext', () => { const previewIframeRef = createRef(); - renderWithMockStore({}, {}, queryClientMock, { previewIframeRef })(); + renderWithMockStore({}, {}, queryClientMock, { previewIframeRef })(); expect(screen.getByTitle(textMock('ux_editor.preview'))).toBe(previewIframeRef.current); }); it('should be able to toggle between mobile and desktop view', async () => { const user = userEvent.setup(); const previewIframeRef = createRef(); - renderWithMockStore({}, {}, queryClientMock, { previewIframeRef })(); + renderWithMockStore({}, {}, queryClientMock, { previewIframeRef })(); const switchButton = screen.getByRole('checkbox', { name: textMock('ux_editor.mobilePreview'), @@ -30,15 +31,15 @@ describe('Preview', () => { it('should render a message when no page is selected', () => { const mockedLayout = { layout: { selectedLayout: undefined } } as IAppState['formDesigner']; - renderWithMockStore({ formDesigner: mockedLayout }, {}, queryClientMock)(); + renderWithMockStore({ formDesigner: mockedLayout }, {}, queryClientMock)(); expect(screen.getByText(textMock('ux_editor.no_components_selected'))).toBeInTheDocument(); }); it('Renders the information alert with preview being limited', () => { const previewIframeRef = createRef(); - renderWithMockStore({}, {}, queryClientMock, { previewIframeRef })(); + renderWithMockStore({}, {}, queryClientMock, { previewIframeRef })(); const previewLimitationsAlert = screen.getByText(textMock('preview.limitations_info')); expect(previewLimitationsAlert).toBeInTheDocument(); }); -}); \ No newline at end of file +}); diff --git a/frontend/packages/ux-editor/src/components/TextResource.test.tsx b/frontend/packages/ux-editor/src/components/TextResource.test.tsx index 97a2173a6ad..941da9224aa 100644 --- a/frontend/packages/ux-editor/src/components/TextResource.test.tsx +++ b/frontend/packages/ux-editor/src/components/TextResource.test.tsx @@ -1,12 +1,9 @@ import React from 'react'; import userEvent from '@testing-library/user-event'; import type { ITextResource, ITextResourcesWithLanguage } from 'app-shared/types/global'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; import { TextResource, TextResourceProps } from './TextResource'; -import { - queryClientMock, - renderHookWithMockStore, - renderWithMockStore, -} from '../testing/mocks'; +import { renderHookWithMockStore, renderWithMockStore, textLanguagesMock } from '../testing/mocks'; import { useLayoutSchemaQuery } from '../hooks/queries/useLayoutSchemaQuery'; import { act, screen, waitFor } from '@testing-library/react'; import { textMock } from '../../../../testing/mocks/i18nMock'; @@ -156,7 +153,11 @@ describe('TextResource', () => { it('Calls handleIdChange when selection in search section is changed', async () => { await renderAndOpenSearchSection(); - await act(() => user.click(screen.getByRole('combobox', { name: textMock('ux_editor.search_text_resources_label') }))); + await act(() => + user.click( + screen.getByRole('combobox', { name: textMock('ux_editor.search_text_resources_label') }), + ), + ); await act(() => user.click(screen.getByRole('option', { name: textResources[1].id }))); expect(handleIdChange).toHaveBeenCalledTimes(1); expect(handleIdChange).toHaveBeenCalledWith(textResources[1].id); @@ -164,15 +165,25 @@ describe('TextResource', () => { it('Calls handleIdChange with undefined when "none" is selected', async () => { await renderAndOpenSearchSection(); - await act(() => user.click(screen.getByRole('combobox', { name: textMock('ux_editor.search_text_resources_label') }))); - await act(() => user.click(screen.getByRole('option', { name: textMock('ux_editor.search_text_resources_none') }))); + await act(() => + user.click( + screen.getByRole('combobox', { name: textMock('ux_editor.search_text_resources_label') }), + ), + ); + await act(() => + user.click( + screen.getByRole('option', { name: textMock('ux_editor.search_text_resources_none') }), + ), + ); expect(handleIdChange).toHaveBeenCalledTimes(1); expect(handleIdChange).toHaveBeenCalledWith(undefined); }); it('Closes search section when close button is clicked', async () => { await renderAndOpenSearchSection(); - await act(() => user.click(screen.getByLabelText(textMock('ux_editor.search_text_resources_close')))); + await act(() => + user.click(screen.getByLabelText(textMock('ux_editor.search_text_resources_close'))), + ); expect(screen.queryByRole('combobox')).not.toBeInTheDocument(); }); @@ -180,15 +191,23 @@ describe('TextResource', () => { await render({ textResourceId: 'test', handleRemoveTextResource: jest.fn() }); await act(() => user.click(screen.getByRole('button', { name: textMock('general.delete') }))); expect(screen.getByRole('dialog')).toBeInTheDocument(); - expect(screen.getByText(textMock('ux_editor.text_resource_bindings.delete_confirm'))).toBeInTheDocument(); + expect( + screen.getByText(textMock('ux_editor.text_resource_bindings.delete_confirm')), + ).toBeInTheDocument(); }); it('Calls handleRemoveTextResourceBinding is called when confirm delete button is clicked', async () => { const handleRemoveTextResource = jest.fn(); await render({ handleRemoveTextResource, textResourceId: 'test' }); await act(() => user.click(screen.getByRole('button', { name: textMock('general.delete') }))); - await act(() => user.click(screen.getByRole('button', { name: textMock('ux_editor.text_resource_bindings.delete_confirm') }))); - expect(handleRemoveTextResource).toBeCalledTimes(1); + await act(() => + user.click( + screen.getByRole('button', { + name: textMock('ux_editor.text_resource_bindings.delete_confirm'), + }), + ), + ); + expect(handleRemoveTextResource).toHaveBeenCalledTimes(1); }); it('Does not call handleRemoveTextResourceBinding is called when cancel delete button is clicked', async () => { @@ -196,7 +215,7 @@ describe('TextResource', () => { await render({ handleRemoveTextResource, textResourceId: 'test' }); await act(() => user.click(screen.getByRole('button', { name: textMock('general.delete') }))); await act(() => user.click(screen.getByRole('button', { name: textMock('general.cancel') }))); - expect(handleRemoveTextResource).not.toBeCalled(); + expect(handleRemoveTextResource).not.toHaveBeenCalled(); }); it('Renders delete button as disabled when no handleRemoveTextResource is given', async () => { @@ -227,13 +246,20 @@ const renderAndOpenSearchSection = async () => { }; const waitForData = async (resources: ITextResource[]) => { - const { result } = renderHookWithMockStore({}, { - getTextResources: jest.fn().mockImplementation(() => Promise.resolve({ - language: DEFAULT_LANGUAGE, - resources, - })), - })(() => useTextResourcesQuery(org, app)).renderHookResult; - const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()).renderHookResult.result; + const { result } = renderHookWithMockStore( + {}, + { + getTextResources: jest.fn().mockImplementation(() => + Promise.resolve({ + language: DEFAULT_LANGUAGE, + resources, + }), + ), + getTextLanguages: jest.fn().mockImplementation(() => Promise.resolve(textLanguagesMock)), + }, + )(() => useTextResourcesQuery(org, app)).renderHookResult; + const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()) + .renderHookResult.result; await waitFor(() => expect(result.current.isSuccess).toBe(true)); await waitFor(() => expect(layoutSchemaResult.current[0].isSuccess).toBe(true)); }; diff --git a/frontend/packages/ux-editor/src/components/TextResourceEdit.test.tsx b/frontend/packages/ux-editor/src/components/TextResourceEdit.test.tsx index 835887d55bc..9e2c8b9dcca 100644 --- a/frontend/packages/ux-editor/src/components/TextResourceEdit.test.tsx +++ b/frontend/packages/ux-editor/src/components/TextResourceEdit.test.tsx @@ -4,12 +4,9 @@ import type { ITextResourcesState } from '../features/appData/textResources/text import type { ITextResources, ITextResourcesWithLanguage } from 'app-shared/types/global'; import userEvent from '@testing-library/user-event'; import { TextResourceEdit } from './TextResourceEdit'; -import { - renderHookWithMockStore, - renderWithMockStore, - queriesMock, - queryClientMock, -} from '../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; +import { renderHookWithMockStore, renderWithMockStore, textLanguagesMock } from '../testing/mocks'; import { appDataMock, textResourcesMock } from '../testing/stateMocks'; import { act, fireEvent, screen, waitFor } from '@testing-library/react'; import { mockUseTranslation } from '../../../../testing/mocks/i18nMock'; @@ -178,7 +175,7 @@ const render = async ( const { result } = renderHookWithMockStore( { appData }, { - getTextLanguages: () => Promise.resolve(['nb', 'nn', 'en']), + getTextLanguages: jest.fn().mockImplementation(() => Promise.resolve(textLanguagesMock)), getTextResources: (_o, _a, lang) => Promise.resolve({ language: lang, diff --git a/frontend/packages/ux-editor/src/components/config/EditFormComponent.test.tsx b/frontend/packages/ux-editor/src/components/config/EditFormComponent.test.tsx index 044e788e02a..219427a066d 100644 --- a/frontend/packages/ux-editor/src/components/config/EditFormComponent.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/EditFormComponent.test.tsx @@ -8,6 +8,7 @@ import { useLayoutSchemaQuery } from '../../hooks/queries/useLayoutSchemaQuery'; import { mockUseTranslation } from '../../../../../testing/mocks/i18nMock'; import { ComponentType } from 'app-shared/types/ComponentType'; import { useDatamodelMetadataQuery } from '../../hooks/queries/useDatamodelMetadataQuery'; +import { DatamodelMetadataResponse } from 'app-shared/types/api'; const user = userEvent.setup(); @@ -37,7 +38,7 @@ jest.mock('./componentSpecificContent/Image/ImageComponent', () => ({ })); const getDatamodelMetadata = () => - Promise.resolve({ + Promise.resolve({ elements: { testModel: { id: 'testModel', diff --git a/frontend/packages/ux-editor/src/components/config/EditFormContainer.test.tsx b/frontend/packages/ux-editor/src/components/config/EditFormContainer.test.tsx index 745728443d2..6cafdfa8438 100644 --- a/frontend/packages/ux-editor/src/components/config/EditFormContainer.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/EditFormContainer.test.tsx @@ -5,10 +5,16 @@ import type { IEditFormContainerProps } from './EditFormContainer'; import { EditFormContainer } from './EditFormContainer'; import { useFormLayoutsQuery } from '../../hooks/queries/useFormLayoutsQuery'; import { useFormLayoutSettingsQuery } from '../../hooks/queries/useFormLayoutSettingsQuery'; -import { renderHookWithMockStore, renderWithMockStore } from '../../testing/mocks'; +import { + formLayoutSettingsMock, + renderHookWithMockStore, + renderWithMockStore, +} from '../../testing/mocks'; import { useLayoutSchemaQuery } from '../../hooks/queries/useLayoutSchemaQuery'; -import { container1IdMock, layoutMock } from '../../testing/layoutMock'; +import { container1IdMock, externalLayoutsMock, layoutMock } from '../../testing/layoutMock'; import { textMock } from '../../../../../testing/mocks/i18nMock'; +import { FormLayoutsResponse } from 'app-shared/types/api'; +import { ILayoutSettings } from 'app-shared/types/global'; const user = userEvent.setup(); @@ -80,12 +86,20 @@ describe('EditFormContainer', () => { }); const waitForData = async () => { - const formLayoutsResult = renderHookWithMockStore()(() => - useFormLayoutsQuery(org, app, selectedLayoutSet), - ).renderHookResult.result; - const settingsResult = renderHookWithMockStore()(() => - useFormLayoutSettingsQuery(org, app, selectedLayoutSet), - ).renderHookResult.result; + const getFormLayouts = jest + .fn() + .mockImplementation(() => Promise.resolve(externalLayoutsMock)); + const getFormLayoutSettings = jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)); + const formLayoutsResult = renderHookWithMockStore( + {}, + { getFormLayouts }, + )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; + const settingsResult = renderHookWithMockStore( + {}, + { getFormLayoutSettings }, + )(() => useFormLayoutSettingsQuery(org, app, selectedLayoutSet)).renderHookResult.result; const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()) .renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); diff --git a/frontend/packages/ux-editor/src/components/config/SelectDataModelComponent.test.tsx b/frontend/packages/ux-editor/src/components/config/SelectDataModelComponent.test.tsx index 48964a9383f..9a36b57fa03 100644 --- a/frontend/packages/ux-editor/src/components/config/SelectDataModelComponent.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/SelectDataModelComponent.test.tsx @@ -8,9 +8,10 @@ import { SelectDataModelComponent } from './SelectDataModelComponent'; import { textMock } from '../../../../../testing/mocks/i18nMock'; import { useDatamodelMetadataQuery } from '../../hooks/queries/useDatamodelMetadataQuery'; import userEvent from '@testing-library/user-event'; +import { DatamodelMetadataResponse } from 'app-shared/types/api'; const getDatamodelMetadata = () => - Promise.resolve({ + Promise.resolve({ elements: { testModel: { id: 'testModel', diff --git a/frontend/packages/ux-editor/src/components/config/componentSpecificContent/Panel/PanelComponent.test.tsx b/frontend/packages/ux-editor/src/components/config/componentSpecificContent/Panel/PanelComponent.test.tsx index 3db0bb6aaba..989b28f8cfd 100644 --- a/frontend/packages/ux-editor/src/components/config/componentSpecificContent/Panel/PanelComponent.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/componentSpecificContent/Panel/PanelComponent.test.tsx @@ -3,7 +3,11 @@ import { act, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { PanelComponent } from './PanelComponent'; import { FormComponent } from '../../../../types/FormComponent'; -import { renderHookWithMockStore, renderWithMockStore } from '../../../../testing/mocks'; +import { + formLayoutSettingsMock, + renderHookWithMockStore, + renderWithMockStore, +} from '../../../../testing/mocks'; import { useLayoutSchemaQuery } from '../../../../hooks/queries/useLayoutSchemaQuery'; import { ComponentType } from 'app-shared/types/ComponentType'; import { useFormLayoutsQuery } from '../../../../hooks/queries/useFormLayoutsQuery'; @@ -30,12 +34,16 @@ const mockHandleComponentChange = jest.fn(); const user = userEvent.setup(); const waitForData = async () => { + const getFormLayoutSettings = jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)); const formLayoutsResult = renderHookWithMockStore()(() => useFormLayoutsQuery(org, app, selectedLayoutSet), ).renderHookResult.result; - const settingsResult = renderHookWithMockStore()(() => - useFormLayoutSettingsQuery(org, app, selectedLayoutSet), - ).renderHookResult.result; + const settingsResult = renderHookWithMockStore( + {}, + { getFormLayoutSettings }, + )(() => useFormLayoutSettingsQuery(org, app, selectedLayoutSet)).renderHookResult.result; const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()) .renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditCodeList.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditCodeList.test.tsx index 192518f4491..4c6b09eb2cb 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditCodeList.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditCodeList.test.tsx @@ -2,19 +2,33 @@ import React from 'react'; import { EditCodeList } from './EditCodeList'; import { screen, waitFor, act } from '@testing-library/react'; import { ComponentType } from 'app-shared/types/ComponentType'; -import { renderWithMockStore, renderHookWithMockStore } from '../../../testing/mocks'; +import { + renderWithMockStore, + renderHookWithMockStore, + optionListIdsMock, +} from '../../../testing/mocks'; import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; import userEvent from '@testing-library/user-event'; describe('EditCodeList', () => { it('should render the component', async () => { - await render(); + await render({ + queries: { + getOptionListIds: jest + .fn() + .mockImplementation(() => Promise.resolve(optionListIdsMock)), + }, + }); expect(await screen.findByText('Bytt til egendefinert kodeliste')).toBeInTheDocument(); }); it('should render the component when optionListIds is undefined', async () => { await render({ - queries: { getOptionListIds: jest.fn().mockImplementation(() => Promise.resolve(undefined)) }, + queries: { + getOptionListIds: jest + .fn() + .mockImplementation(() => Promise.resolve(optionListIdsMock)), + }, }); expect(await screen.findByText('Bytt til egendefinert kodeliste')).toBeInTheDocument(); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditDataModelBindings.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditDataModelBindings.test.tsx index a21827ec6ec..97e32beba13 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditDataModelBindings.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditDataModelBindings.test.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { act, screen, waitFor, within } from '@testing-library/react'; +import { act, screen } from '@testing-library/react'; import { renderWithMockStore } from '../../../testing/mocks'; import { appDataMock, textResourcesMock } from '../../../testing/stateMocks'; import { IAppDataState } from '../../../features/appData/appDataReducers'; @@ -7,56 +7,52 @@ import { EditDataModelBindings } from './EditDataModelBindings'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; import { ComponentType } from 'app-shared/types/ComponentType'; import userEvent from '@testing-library/user-event'; +import { DatamodelMetadataResponse } from 'app-shared/types/api'; const user = userEvent.setup(); -const getDatamodelMetadata = () => - Promise.resolve({ - elements: { - testModel: { - id: 'testModel', - type: 'ComplexType', - dataBindingName: 'testModel', - displayString: 'testModel', - isReadOnly: false, - isTagContent: false, - jsonSchemaPointer: '#/definitions/testModel', - maxOccurs: 1, - minOccurs: 1, - name: 'testModel', - parentElement: null, - restrictions: [], - texts: [], - xmlSchemaXPath: '/testModel', - xPath: '/testModel', - }, - 'testModel.field1': { - id: 'testModel.field1', - type: 'SimpleType', - dataBindingName: 'testModel.field1', - displayString: 'testModel.field1', - isReadOnly: false, - isTagContent: false, - jsonSchemaPointer: '#/definitions/testModel/properteis/field1', - maxOccurs: 1, - minOccurs: 1, - name: 'testModel/field1', - parentElement: null, - restrictions: [], - texts: [], - xmlSchemaXPath: '/testModel/field1', - xPath: '/testModel/field1', - }, +const datamodelMetadata: DatamodelMetadataResponse = { + elements: { + testModel: { + id: 'testModel', + type: 'ComplexType', + dataBindingName: 'testModel', + displayString: 'testModel', + isReadOnly: false, + isTagContent: false, + jsonSchemaPointer: '#/definitions/testModel', + maxOccurs: 1, + minOccurs: 1, + name: 'testModel', + parentElement: null, + restrictions: [], + texts: [], + xmlSchemaXPath: '/testModel', + xPath: '/testModel', }, - }); + 'testModel.field1': { + id: 'testModel.field1', + type: 'SimpleType', + dataBindingName: 'testModel.field1', + displayString: 'testModel.field1', + isReadOnly: false, + isTagContent: false, + jsonSchemaPointer: '#/definitions/testModel/properteis/field1', + maxOccurs: 1, + minOccurs: 1, + name: 'testModel/field1', + parentElement: null, + restrictions: [], + texts: [], + xmlSchemaXPath: '/testModel/field1', + xPath: '/testModel/field1', + }, + }, +}; -const render = async ({ - dataModelBindings = {}, - handleComponentChange = jest.fn(), - handleDataModelChange = jest.fn(), - setSelectedOption = jest.fn(), - onEditClick = jest.fn(), -} = {}) => { +const getDatamodelMetadata = () => Promise.resolve(datamodelMetadata); + +const render = async ({ dataModelBindings = {}, handleComponentChange = jest.fn() } = {}) => { const appData: IAppDataState = { ...appDataMock, textResources: { @@ -67,8 +63,6 @@ const render = async ({ return renderWithMockStore( { appData }, { getDatamodelMetadata }, - handleDataModelChange(), - setSelectedOption(), )( { it('should toggle select on link icon click', async () => { await render(); expect(screen.queryByRole('combobox')).not.toBeInTheDocument(); - const linkIcon = screen.getByText(/ux_editor.modal_properties_data_model_link/i); + const linkIcon = screen.getByText(textMock('ux_editor.modal_properties_data_model_link')); await act(() => user.click(linkIcon)); expect(screen.getByRole('combobox')).toBeInTheDocument(); }); - it('check that handleDataModelChange is called', async () => { - const handleDataModelChange = jest.fn(); - const dataModelSelectVisible = jest.fn(); - await render({ handleDataModelChange }); + it('check that handleComponentChange is called', async () => { + const handleComponentChange = jest.fn(); + await render({ handleComponentChange }); const linkIcon = screen.getByText(textMock('ux_editor.modal_properties_data_model_link')); await act(() => user.click(linkIcon)); - dataModelSelectVisible(true); - const select = screen.getByRole('combobox'); - const option = within(select).getByText(''); + const option = screen.getByText('testModel'); await act(() => user.click(option)); - expect(handleDataModelChange).toHaveBeenCalled(); + expect(handleComponentChange).toHaveBeenCalledWith({ + dataModelBindings: { simpleBinding: 'testModel' }, + id: 'someComponentId', + itemType: 'COMPONENT', + required: true, + textResourceBindings: { title: 'ServiceName' }, + timeStamp: undefined, + type: 'Input', + }); }); it('should render save icon', async () => { @@ -173,77 +172,60 @@ describe('EditDataModelBindings', () => { const saveButton = await screen.findByRole('button', { name: /general.save/i }); await act(() => user.click(saveButton)); - expect(screen.getByText(textMock('ux_editor.modal_properties_data_model_link'))).toBeInTheDocument(); + expect( + screen.getByText(textMock('ux_editor.modal_properties_data_model_link')), + ).toBeInTheDocument(); }); - it('should call handleDataModelChange and update setSelectedOption on delete button click', async () => { - const handleDataModelChange = jest.fn(); - const setSelectedOption = String(''); - - await render({ handleDataModelChange }); + it('deletes existing data model link', async () => { + const handleComponentChange = jest.fn(); + const dataModelBindingKey = 'testModel.field1'; - const linkIcon = screen.getByText(/ux_editor.modal_properties_data_model_link/i); - await act(() => user.click(linkIcon)); - - expect(await screen.findByText('testModel.field1')).toBeInTheDocument(); - const deleteButton = await screen.findByRole('button', { name: /general.delete/i }); - await waitFor(async () => { - await userEvent.click(deleteButton); + await render({ + handleComponentChange, + dataModelBindings: { simpleBinding: dataModelBindingKey }, }); - expect(handleDataModelChange).toBeCalled; - expect(typeof setSelectedOption).toEqual('string'); - }); - it('should call handleDataModelChange and setSelectedOption on data model change', async () => { - const handleDataModelChange = jest.fn(); - const setSelectedOption = jest.fn(); + const datamodelText = screen.getByText(dataModelBindingKey); + expect(datamodelText).toBeInTheDocument(); - await render({ handleDataModelChange, setSelectedOption }); + await act(() => user.hover(datamodelText)); - const linkIcon = screen.getByText(/ux_editor.modal_properties_data_model_link/i); - await act(() => user.click(linkIcon)); - - expect( - await screen.findByText(textMock('ux_editor.modal_properties_data_model_helper')), - ).toBeInTheDocument(); - expect(await screen.findByText('testModel.field1')).toBeInTheDocument(); + const editIcon = screen.getByRole('button', { name: textMock('general.edit') }); + await act(() => user.click(editIcon)); - expect(handleDataModelChange).toBeCalled; - expect(setSelectedOption).toBeCalled; + expect(await screen.findByText(dataModelBindingKey)).toBeInTheDocument(); + const deleteButton = await screen.findByRole('button', { name: /general.delete/i }); + await act(() => user.click(deleteButton)); + expect(handleComponentChange).toHaveBeenCalledWith({ + dataModelBindings: { simpleBinding: '' }, + id: 'someComponentId', + itemType: 'COMPONENT', + required: false, + textResourceBindings: { title: 'ServiceName' }, + timeStamp: undefined, + type: 'Input', + }); }); - it('should render LinkedDataModelContainer component when an option is selected', async () => { - const handleDataModelChange = jest.fn(); - const setSelectedOption = jest.fn(); - - await render({ handleDataModelChange, setSelectedOption }); - - const linkIcon = screen.getByText(/ux_editor.modal_properties_data_model_link/i); - await act(() => user.click(linkIcon)); - - expect( - await screen.findByText(textMock('ux_editor.modal_properties_data_model_helper')), - ).toBeInTheDocument(); - expect(await screen.findByText('testModel.field1')).toBeInTheDocument(); - - expect(handleDataModelChange).toBeCalled; - expect(setSelectedOption).toBeCalled; + it('shows edit form', async () => { + const dataModelBindingKey = 'testModel.field1'; + await render({ + dataModelBindings: { simpleBinding: dataModelBindingKey }, + }); - const linkedDataModelContainer = await screen.findByText('testModel.field1'); - expect(linkedDataModelContainer).toBeInTheDocument(); - }); + const datamodelText = screen.getByText(dataModelBindingKey); + expect(datamodelText).toBeInTheDocument(); - it('check that onEditClick is called', async () => { - const onEditClick = jest.fn(); - await render({ onEditClick }); + await act(() => user.hover(datamodelText)); - const linkIcon = screen.getByText(textMock('ux_editor.modal_properties_data_model_link')); - await act(() => user.click(linkIcon)); - const editIcon = screen.getByRole('button', { name: /Edit/i }); + const editIcon = screen.getByRole('button', { name: textMock('general.edit') }); await act(() => user.click(editIcon)); - expect(editIcon).toBeInTheDocument(); - expect(onEditClick).toHaveBeenCalled; + expect( + screen.getByText(textMock('ux_editor.modal_properties_data_model_helper')), + ).toBeInTheDocument(); + expect(screen.getByRole('combobox').getAttribute('value')).toEqual(dataModelBindingKey); }); it('show right data model when switching component', async () => { diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditTextResourceBinding.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditTextResourceBinding.test.tsx index 696463fc5e0..75d5c8dc6ee 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditTextResourceBinding.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditTextResourceBinding.test.tsx @@ -2,7 +2,11 @@ import React from 'react'; import { EditTextResourceBinding, EditTextResourceBindingProps } from './EditTextResourceBinding'; import { act, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { renderHookWithMockStore, renderWithMockStore } from '../../../testing/mocks'; +import { + renderHookWithMockStore, + renderWithMockStore, + textLanguagesMock, +} from '../../../testing/mocks'; import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; import type { ITextResource } from 'app-shared/types/global'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; @@ -31,8 +35,8 @@ describe('EditTextResourceBindings component', () => { const textResources: ITextResource[] = [ { id: 'test-text', - value: 'This is a test' - } + value: 'This is a test', + }, ]; test('that it renders', async () => { @@ -45,14 +49,20 @@ describe('EditTextResourceBindings component', () => { test('that handleComponentChange is called when adding a new text', async () => { const handleComponentChange = jest.fn(); - await renderEditTextResourceBindingsComponent({ handleComponentChange, textKey: 'does-not-exist' }); + await renderEditTextResourceBindingsComponent({ + handleComponentChange, + textKey: 'does-not-exist', + }); await act(() => user.click(screen.getByLabelText(textMock('general.add')))); - expect(handleComponentChange).toBeCalledTimes(1); + expect(handleComponentChange).toHaveBeenCalledTimes(1); }); test('that handleComponentChange is called when choosing existing text', async () => { const handleComponentChange = jest.fn(); - await renderEditTextResourceBindingsComponent({ handleComponentChange, textKey: 'does-not-exist' }); + await renderEditTextResourceBindingsComponent({ + handleComponentChange, + textKey: 'does-not-exist', + }); // Click search button await act(() => user.click(screen.getByLabelText(textMock('general.search')))); @@ -65,39 +75,53 @@ describe('EditTextResourceBindings component', () => { // Select text from available options await act(() => user.click(screen.getByRole('option', { name: textResources[0].id }))); - expect(handleComponentChange).toBeCalledTimes(1); - expect(handleComponentChange).toBeCalledWith({ + expect(handleComponentChange).toHaveBeenCalledTimes(1); + expect(handleComponentChange).toHaveBeenCalledWith({ ...mockComponent, textResourceBindings: { ...mockComponent.textResourceBindings, - 'does-not-exist': 'test-text' - } + 'does-not-exist': 'test-text', + }, }); }); test('That handleComponentChange and removeTextResourceBinding are called when confirm delete textResourceBinding button is clicked', async () => { const handleComponentChange = jest.fn(); const removeTextResourceBinding = jest.fn(); - await renderEditTextResourceBindingsComponent({ handleComponentChange, removeTextResourceBinding }); + await renderEditTextResourceBindingsComponent({ + handleComponentChange, + removeTextResourceBinding, + }); await act(() => user.click(screen.getByRole('button', { name: textMock('general.delete') }))); - await act(() => user.click(screen.getByRole('button', { name: textMock('ux_editor.text_resource_bindings.delete_confirm') }))); - expect(handleComponentChange).toBeCalledTimes(1); - expect(handleComponentChange).toBeCalledWith({ + await act(() => + user.click( + screen.getByRole('button', { + name: textMock('ux_editor.text_resource_bindings.delete_confirm'), + }), + ), + ); + expect(handleComponentChange).toHaveBeenCalledTimes(1); + expect(handleComponentChange).toHaveBeenCalledWith({ ...mockComponent, textResourceBindings: {}, }); - expect(removeTextResourceBinding).toBeCalledTimes(1); + expect(removeTextResourceBinding).toHaveBeenCalledTimes(1); }); const waitForData = async () => { - const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()).renderHookResult.result; - const { result } = renderHookWithMockStore({}, { - getTextLanguages: () => Promise.resolve(['nb', 'nn', 'en']), - getTextResources: (_o, _a, lang) => Promise.resolve({ - language: lang, - resources: textResources - }), - })(() => useTextResourcesQuery(org, app)).renderHookResult; + const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()) + .renderHookResult.result; + const { result } = renderHookWithMockStore( + {}, + { + getTextLanguages: jest.fn().mockImplementation(() => Promise.resolve(textLanguagesMock)), + getTextResources: (_o, _a, lang) => + Promise.resolve({ + language: lang, + resources: textResources, + }), + }, + )(() => useTextResourcesQuery(org, app)).renderHookResult; await waitFor(() => expect(layoutSchemaResult.current[0].isSuccess).toBe(true)); await waitFor(() => expect(result.current.isSuccess).toBe(true)); }; @@ -111,12 +135,14 @@ describe('EditTextResourceBindings component', () => { }: Partial) => { await waitForData(); - return renderWithMockStore()(); + return renderWithMockStore()( + , + ); }; }); diff --git a/frontend/packages/ux-editor/src/components/config/editModal/EditTextResourceBindings.test.tsx b/frontend/packages/ux-editor/src/components/config/editModal/EditTextResourceBindings.test.tsx index 9e64180e6ac..8ec41768305 100644 --- a/frontend/packages/ux-editor/src/components/config/editModal/EditTextResourceBindings.test.tsx +++ b/frontend/packages/ux-editor/src/components/config/editModal/EditTextResourceBindings.test.tsx @@ -4,7 +4,11 @@ import { EditTextResourceBindingsProps, } from './EditTextResourceBindings'; import { act, screen, waitFor } from '@testing-library/react'; -import { renderHookWithMockStore, renderWithMockStore } from '../../../testing/mocks'; +import { + renderHookWithMockStore, + renderWithMockStore, + textLanguagesMock, +} from '../../../testing/mocks'; import { useLayoutSchemaQuery } from '../../../hooks/queries/useLayoutSchemaQuery'; import type { ITextResource } from 'app-shared/types/global'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; @@ -39,15 +43,21 @@ describe('EditTextResourceBindings component', () => { test('that it renders with expected text resource binding keys', async () => { const textResourceBindingKeys = ['title', 'description', 'help']; await renderEditTextResourceBindingsComponent({ textResourceBindingKeys }); - const label = screen.getByText(textMock(`ux_editor.modal_properties_textResourceBindings_test`)); + const label = screen.getByText( + textMock(`ux_editor.modal_properties_textResourceBindings_test`), + ); const text = screen.getByText('This is a test'); expect(label).toBeInTheDocument(); expect(text).toBeInTheDocument(); }); test('that it renders no text resource bindings if none are added', async () => { - await renderEditTextResourceBindingsComponent({ component: { ...mockComponent, textResourceBindings: {} } }); - const titleLabel = screen.queryByText(textMock(`ux_editor.modal_properties_textResourceBindings_title`)); + await renderEditTextResourceBindingsComponent({ + component: { ...mockComponent, textResourceBindings: {} }, + }); + const titleLabel = screen.queryByText( + textMock(`ux_editor.modal_properties_textResourceBindings_title`), + ); expect(titleLabel).not.toBeInTheDocument(); const searchTextButton = screen.queryByRole('button', { name: textMock('general.search') }); expect(searchTextButton).not.toBeInTheDocument(); @@ -56,14 +66,18 @@ describe('EditTextResourceBindings component', () => { test('that it renders the combobox for selecting text resource binding keys to add', async () => { const textResourceBindingKeys = ['title', 'description', 'help']; await renderEditTextResourceBindingsComponent({ textResourceBindingKeys }); - const selectTextResourcesCombobox = screen.getByRole('combobox', { name: textMock('ux_editor.text_resource_bindings.add_label') }); + const selectTextResourcesCombobox = screen.getByRole('combobox', { + name: textMock('ux_editor.text_resource_bindings.add_label'), + }); expect(selectTextResourcesCombobox).toBeInTheDocument(); }); test('that the combobox for selecting text resource binding keys only contains keys that are not already added', async () => { const textResourceBindingKeys = ['title', 'description', 'help']; await renderEditTextResourceBindingsComponent({ textResourceBindingKeys }); - const selectTextResourcesCombobox = screen.getByRole('combobox', { name: textMock('ux_editor.text_resource_bindings.add_label') }); + const selectTextResourcesCombobox = screen.getByRole('combobox', { + name: textMock('ux_editor.text_resource_bindings.add_label'), + }); await act(() => userEvent.click(selectTextResourcesCombobox)); // eslint-disable-line testing-library/no-unnecessary-act let options = screen.getAllByRole('option'); @@ -78,24 +92,27 @@ describe('EditTextResourceBindings component', () => { test('that it does not render the combobox for selecting text resource binding keys when all available keys are added', async () => { const textResourceBindingKeys = ['test']; await renderEditTextResourceBindingsComponent({ textResourceBindingKeys }); - const selectTextResourcesCombobox = screen.queryByRole('combobox', { name: textMock('ux_editor.text_resource_bindings.add_label') }); + const selectTextResourcesCombobox = screen.queryByRole('combobox', { + name: textMock('ux_editor.text_resource_bindings.add_label'), + }); const addTextResourceButton = screen.queryByRole('button', { name: textMock('general.add') }); expect(selectTextResourcesCombobox).not.toBeInTheDocument(); expect(addTextResourceButton).not.toBeInTheDocument(); }); const waitForData = async () => { - const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()).renderHookResult.result; + const layoutSchemaResult = renderHookWithMockStore()(() => useLayoutSchemaQuery()) + .renderHookResult.result; const { result } = renderHookWithMockStore( {}, { - getTextLanguages: () => Promise.resolve(['nb', 'nn', 'en']), + getTextLanguages: jest.fn().mockImplementation(() => Promise.resolve(textLanguagesMock)), getTextResources: (_o, _a, lang) => Promise.resolve({ language: lang, resources: textResources, }), - } + }, )(() => useTextResourcesQuery(org, app)).renderHookResult; await waitFor(() => expect(layoutSchemaResult.current[0].isSuccess).toBe(true)); await waitFor(() => expect(result.current.isSuccess).toBe(true)); @@ -113,7 +130,7 @@ describe('EditTextResourceBindings component', () => { component={component} handleComponentChange={handleComponentChange} textResourceBindingKeys={textResourceBindingKeys} - /> + />, ); }; }); diff --git a/frontend/packages/ux-editor/src/containers/DesignView/DesignView.test.tsx b/frontend/packages/ux-editor/src/containers/DesignView/DesignView.test.tsx index d943f56620d..83102c5eff7 100644 --- a/frontend/packages/ux-editor/src/containers/DesignView/DesignView.test.tsx +++ b/frontend/packages/ux-editor/src/containers/DesignView/DesignView.test.tsx @@ -7,7 +7,7 @@ import { FormContextProvider } from '../FormContext'; import { DragAndDrop } from 'app-shared/components/dragAndDrop'; import { BASE_CONTAINER_ID } from 'app-shared/constants'; import userEvent from '@testing-library/user-event'; -import { queriesMock } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; import { typedLocalStorage } from 'app-shared/utils/webStorage'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { QueryKey } from 'app-shared/types/QueryKey'; @@ -33,18 +33,8 @@ jest.mock('react-router-dom', () => ({ }, })); -// Mocking console.error due to Tanstack Query removing custom logger between V4 and v5 see issue: #11692 -const realConsole = console; - describe('DesignView', () => { - beforeEach(() => { - global.console = { - ...console, - error: jest.fn(), - }; - }); afterEach(() => { - global.console = realConsole; jest.clearAllMocks(); }); diff --git a/frontend/packages/ux-editor/src/containers/DesignView/PageAccordion/NavigationMenu/NavigationMenu.test.tsx b/frontend/packages/ux-editor/src/containers/DesignView/PageAccordion/NavigationMenu/NavigationMenu.test.tsx index 81b7aa385a2..6a5fc03ba75 100644 --- a/frontend/packages/ux-editor/src/containers/DesignView/PageAccordion/NavigationMenu/NavigationMenu.test.tsx +++ b/frontend/packages/ux-editor/src/containers/DesignView/PageAccordion/NavigationMenu/NavigationMenu.test.tsx @@ -3,8 +3,9 @@ import { act, screen, waitFor } from '@testing-library/react'; import { NavigationMenu, NavigationMenuProps } from './NavigationMenu'; import userEvent from '@testing-library/user-event'; import { textMock } from '../../../../../../../testing/mocks/i18nMock'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; import { - queriesMock, + formLayoutSettingsMock, renderHookWithMockStore, renderWithMockStore, } from '../../../../testing/mocks'; @@ -116,9 +117,14 @@ describe('NavigationMenu', () => { }); const waitForData = async () => { - const settingsResult = renderHookWithMockStore()(() => - useFormLayoutSettingsQuery(mockOrg, mockApp, mockSelectedLayoutSet), - ).renderHookResult.result; + const getFormLayoutSettings = jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)); + const settingsResult = renderHookWithMockStore( + {}, + { getFormLayoutSettings }, + )(() => useFormLayoutSettingsQuery(mockOrg, mockApp, mockSelectedLayoutSet)).renderHookResult + .result; await waitFor(() => expect(settingsResult.current.isSuccess).toBe(true)); }; diff --git a/frontend/packages/ux-editor/src/containers/DesignView/PageAccordion/PageAccordion.test.tsx b/frontend/packages/ux-editor/src/containers/DesignView/PageAccordion/PageAccordion.test.tsx index de5b64389b5..a678dcd603f 100644 --- a/frontend/packages/ux-editor/src/containers/DesignView/PageAccordion/PageAccordion.test.tsx +++ b/frontend/packages/ux-editor/src/containers/DesignView/PageAccordion/PageAccordion.test.tsx @@ -5,7 +5,11 @@ import userEvent from '@testing-library/user-event'; import { textMock } from '../../../../../../testing/mocks/i18nMock'; import { formDesignerMock } from '../../../testing/stateMocks'; import { useFormLayoutSettingsQuery } from '../../../hooks/queries/useFormLayoutSettingsQuery'; -import { renderHookWithMockStore, renderWithMockStore } from '../../../testing/mocks'; +import { + formLayoutSettingsMock, + renderHookWithMockStore, + renderWithMockStore, +} from '../../../testing/mocks'; import { layout2NameMock } from '../../../testing/layoutMock'; const mockOrg = 'org'; @@ -96,9 +100,14 @@ describe('PageAccordion', () => { }); const waitForData = async () => { - const settingsResult = renderHookWithMockStore()(() => - useFormLayoutSettingsQuery(mockOrg, mockApp, mockSelectedLayoutSet), - ).renderHookResult.result; + const getFormLayoutSettings = jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)); + const settingsResult = renderHookWithMockStore( + {}, + { getFormLayoutSettings }, + )(() => useFormLayoutSettingsQuery(mockOrg, mockApp, mockSelectedLayoutSet)).renderHookResult + .result; await waitFor(() => expect(settingsResult.current.isSuccess).toBe(true)); }; diff --git a/frontend/packages/ux-editor/src/containers/DesignView/ReceiptContent/ReceiptContent.test.tsx b/frontend/packages/ux-editor/src/containers/DesignView/ReceiptContent/ReceiptContent.test.tsx index 4f8c7ba2f07..f13060352de 100644 --- a/frontend/packages/ux-editor/src/containers/DesignView/ReceiptContent/ReceiptContent.test.tsx +++ b/frontend/packages/ux-editor/src/containers/DesignView/ReceiptContent/ReceiptContent.test.tsx @@ -11,7 +11,11 @@ import { layout2NameMock, } from '../../../testing/layoutMock'; import { IInternalLayout } from '../../../types/global'; -import { renderHookWithMockStore, renderWithMockStore } from '../../../testing/mocks'; +import { + formLayoutSettingsMock, + renderHookWithMockStore, + renderWithMockStore, +} from '../../../testing/mocks'; import { useFormLayoutSettingsQuery } from '../../../hooks/queries/useFormLayoutSettingsQuery'; import { useFormLayoutsQuery } from '../../../hooks/queries/useFormLayoutsQuery'; import { DragAndDrop } from 'app-shared/components/dragAndDrop'; @@ -107,13 +111,17 @@ describe('ReceiptContent', () => { }); const waitForData = async () => { + const getFormLayoutSettings = jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)); const formLayoutsResult = renderHookWithMockStore()(() => useFormLayoutsQuery(mockOrg, mockApp, mockSelectedLayoutSet), ).renderHookResult.result; - - const settingsResult = renderHookWithMockStore()(() => - useFormLayoutSettingsQuery(mockOrg, mockApp, mockSelectedLayoutSet), - ).renderHookResult.result; + const settingsResult = renderHookWithMockStore( + {}, + { getFormLayoutSettings }, + )(() => useFormLayoutSettingsQuery(mockOrg, mockApp, mockSelectedLayoutSet)).renderHookResult + .result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); await waitFor(() => expect(settingsResult.current.isSuccess).toBe(true)); diff --git a/frontend/packages/ux-editor/src/containers/FormDesigner.test.tsx b/frontend/packages/ux-editor/src/containers/FormDesigner.test.tsx index 72378ef4b41..dece3900eb4 100644 --- a/frontend/packages/ux-editor/src/containers/FormDesigner.test.tsx +++ b/frontend/packages/ux-editor/src/containers/FormDesigner.test.tsx @@ -1,9 +1,14 @@ import React from 'react'; import { screen, waitFor } from '@testing-library/react'; -import { renderHookWithMockStore, renderWithMockStore } from '../testing/mocks'; +import { + formLayoutSettingsMock, + renderHookWithMockStore, + renderWithMockStore, +} from '../testing/mocks'; import { FormDesigner } from './FormDesigner'; import { textMock } from '../../../../testing/mocks/i18nMock'; import { useWidgetsQuery } from '../hooks/queries/useWidgetsQuery'; +import ruleHandlerMock from '../testing/ruleHandlerMock'; // Test data: const org = 'org'; @@ -11,19 +16,22 @@ const app = 'app'; const render = () => { const queries = { - getInstanceIdForPreview: jest.fn().mockImplementation(() => Promise.resolve('test')) + getFormLayoutSettings: jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)), + getRuleModel: jest.fn().mockImplementation(() => Promise.resolve(ruleHandlerMock)), + getInstanceIdForPreview: jest.fn().mockImplementation(() => Promise.resolve('test')), }; const props = { selectedLayout: 'test-layout', selectedLayoutSet: 'test-layout-set', }; - return renderWithMockStore({}, queries)( - - ); + return renderWithMockStore({}, queries)(); }; const waitForData = async () => { - const widgetsResult = renderHookWithMockStore()(() => useWidgetsQuery(org, app)).renderHookResult.result; + const widgetsResult = renderHookWithMockStore()(() => useWidgetsQuery(org, app)).renderHookResult + .result; await waitFor(() => expect(widgetsResult.current.isSuccess).toBe(true)); }; @@ -36,6 +44,8 @@ describe('FormDesigner', () => { it('should render the component', async () => { await waitForData(); render(); - await waitFor(() => expect(screen.queryByText(textMock('general.loading'))).not.toBeInTheDocument()); + await waitFor(() => + expect(screen.queryByText(textMock('general.loading'))).not.toBeInTheDocument(), + ); }); }); diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useAddAppAttachmentMetadataMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useAddAppAttachmentMetadataMutation.test.ts index a1419905daa..8b003093384 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useAddAppAttachmentMetadataMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useAddAppAttachmentMetadataMutation.test.ts @@ -1,5 +1,6 @@ import { useAddAppAttachmentMetadataMutation } from './useAddAppAttachmentMetadataMutation'; -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { ApplicationAttachmentMetadata } from 'app-shared/types/ApplicationAttachmentMetadata'; // Test data: @@ -11,22 +12,18 @@ const metadata: ApplicationAttachmentMetadata = { maxCount: 3, minCount: 1, maxSize: 16, - fileType: 'jpg' + fileType: 'jpg', }; describe('useAddAppAttachmentMetadataMutation', () => { it('Calls addAppAttachmentMetadata with correct arguments and payload', async () => { - const metadataResult = renderHookWithMockStore()(() => useAddAppAttachmentMetadataMutation(org, app)) - .renderHookResult - .result; + const metadataResult = renderHookWithMockStore()(() => + useAddAppAttachmentMetadataMutation(org, app), + ).renderHookResult.result; await metadataResult.current.mutateAsync(metadata); expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledTimes(1); - expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledWith( - org, - app, - metadata - ); + expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledWith(org, app, metadata); }); }); diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useAddFormContainerMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useAddFormContainerMutation.test.ts index 55f8bd1e35f..e99b176cf9c 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useAddFormContainerMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useAddFormContainerMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { waitFor } from '@testing-library/react'; import { diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useAddItemToLayoutMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useAddItemToLayoutMutation.test.ts index 930b9ac5e21..6c6c1335af3 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useAddItemToLayoutMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useAddItemToLayoutMutation.test.ts @@ -1,4 +1,6 @@ -import { queryClientMock, queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { appStateMock, formDesignerMock } from '../../testing/stateMocks'; import { waitFor } from '@testing-library/react'; import { AddFormItemMutationArgs, useAddItemToLayoutMutation } from './useAddItemToLayoutMutation'; @@ -28,8 +30,8 @@ const appStateMockCopy = (layoutSetName: string): Partial => ({ layout: { ...formDesignerMock.layout, selectedLayoutSet: layoutSetName, - } - } + }, + }, }); const applicationAttachmentMetaDataMock: ApplicationAttachmentMetadata = { @@ -42,7 +44,6 @@ const applicationAttachmentMetaDataMock: ApplicationAttachmentMetadata = { }; describe('useAddItemToLayoutMutation', () => { - afterEach(jest.clearAllMocks); it('Returns ID of new item', async () => { @@ -77,27 +78,33 @@ describe('useAddItemToLayoutMutation', () => { const { result } = renderAddItemToLayoutMutation('test-layout-set-2'); result.current.mutate({ ...defaultArgs, componentType: ComponentType.FileUpload }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); - expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledWith(org, app, { ...applicationAttachmentMetaDataMock, taskId: 'Task_2' }); + expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledWith(org, app, { + ...applicationAttachmentMetaDataMock, + taskId: 'Task_2', + }); }); it('Adds Task_1 to attachment metadata when component type is fileUpload and selectedLayoutSet is undefined', async () => { const { result } = renderAddItemToLayoutMutation(undefined); result.current.mutate({ ...defaultArgs, componentType: ComponentType.FileUpload }); await waitFor(() => expect(result.current.isSuccess).toBe(true)); - expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledWith(org, app, { ...applicationAttachmentMetaDataMock, taskId: 'Task_1' }); + expect(queriesMock.addAppAttachmentMetadata).toHaveBeenCalledWith(org, app, { + ...applicationAttachmentMetaDataMock, + taskId: 'Task_1', + }); }); }); const renderAddItemToLayoutMutation = (layoutSetName?: string) => { queryClientMock.setQueryData( [QueryKey.FormLayouts, org, app, layoutSetName], - convertExternalLayoutsToInternalFormat(externalLayoutsMock).convertedLayouts + convertExternalLayoutsToInternalFormat(externalLayoutsMock).convertedLayouts, ); queryClientMock.setQueryData( [QueryKey.LayoutSets, org, app], - layoutSetName ? layoutSetsMock : null + layoutSetName ? layoutSetsMock : null, ); - return renderHookWithMockStore(appStateMockCopy(layoutSetName))( - () => useAddItemToLayoutMutation(org, app, layoutSetName) + return renderHookWithMockStore(appStateMockCopy(layoutSetName))(() => + useAddItemToLayoutMutation(org, app, layoutSetName), ).renderHookResult; -} +}; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useAddLayoutMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useAddLayoutMutation.test.ts index fa6e6b17cbb..36663c7aef1 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useAddLayoutMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useAddLayoutMutation.test.ts @@ -1,9 +1,13 @@ -import { queriesMock, formLayoutSettingsMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { formLayoutSettingsMock, renderHookWithMockStore } from '../../testing/mocks'; import { AddLayoutMutationArgs, useAddLayoutMutation } from './useAddLayoutMutation'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { waitFor } from '@testing-library/react'; import { useFormLayoutSettingsQuery } from '../queries/useFormLayoutSettingsQuery'; import { ComponentType } from 'app-shared/types/ComponentType'; +import { externalLayoutsMock } from '../../testing/layoutMock'; +import { FormLayoutsResponse } from 'app-shared/types/api'; +import { ILayoutSettings } from 'app-shared/types/global'; // Test data: const org = 'org'; @@ -72,12 +76,20 @@ describe('useAddLayoutMutation', () => { }); const renderAndWaitForData = async () => { - const formLayoutsResult = renderHookWithMockStore()(() => - useFormLayoutsQuery(org, app, selectedLayoutSet), - ).renderHookResult.result; - const settingsResult = renderHookWithMockStore()(() => - useFormLayoutSettingsQuery(org, app, selectedLayoutSet), - ).renderHookResult.result; + const getFormLayouts = jest + .fn() + .mockImplementation(() => Promise.resolve(externalLayoutsMock)); + const getFormLayoutSettings = jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)); + const formLayoutsResult = renderHookWithMockStore( + {}, + { getFormLayouts }, + )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; + const settingsResult = renderHookWithMockStore( + {}, + { getFormLayoutSettings }, + )(() => useFormLayoutSettingsQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); await waitFor(() => expect(settingsResult.current.isSuccess).toBe(true)); }; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useAddWidgetMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useAddWidgetMutation.test.ts index 2acdddba6c5..e9d083d4fc8 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useAddWidgetMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useAddWidgetMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { waitFor } from '@testing-library/react'; import { AddWidgetMutationArgs, useAddWidgetMutation } from './useAddWidgetMutation'; @@ -21,7 +22,7 @@ const widget: IWidget = { components: [], texts, displayName, -} +}; const defaultArgs: AddWidgetMutationArgs = { widget, position: 0 }; describe('useAddWidgetMutation', () => { @@ -37,14 +38,21 @@ describe('useAddWidgetMutation', () => { const { result } = await renderAddWidgetMutation(); await result.current.mutateAsync(defaultArgs); expect(queriesMock.upsertTextResources).toHaveBeenCalledTimes(texts.length); - expect(queriesMock.upsertTextResources).toHaveBeenCalledWith(org, app, language, { [textId]: textValue }); + expect(queriesMock.upsertTextResources).toHaveBeenCalledWith(org, app, language, { + [textId]: textValue, + }); }); }); const renderAddWidgetMutation = async () => { - const { result: formLayouts } = renderHookWithMockStore()(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult; + const { result: formLayouts } = renderHookWithMockStore()(() => + useFormLayoutsQuery(org, app, selectedLayoutSet), + ).renderHookResult; await waitFor(() => expect(formLayouts.current.isSuccess).toBe(true)); - const { result: texts } = renderHookWithMockStore()(() => useTextResourcesQuery(org, app)).renderHookResult; + const { result: texts } = renderHookWithMockStore()(() => + useTextResourcesQuery(org, app), + ).renderHookResult; await waitFor(() => expect(texts.current.isSuccess).toBe(true)); - return renderHookWithMockStore()(() => useAddWidgetMutation(org, app, selectedLayoutSet)).renderHookResult; -} + return renderHookWithMockStore()(() => useAddWidgetMutation(org, app, selectedLayoutSet)) + .renderHookResult; +}; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useConfigureLayoutSetMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useConfigureLayoutSetMutation.test.ts index eb907050471..cc263f88869 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useConfigureLayoutSetMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useConfigureLayoutSetMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useConfigureLayoutSetMutation } from './useConfigureLayoutSetMutation'; import { waitFor } from '@testing-library/react'; @@ -8,14 +9,14 @@ const app = 'app'; const layoutSetName = 'first-layout-set-name'; describe('useConfigureLayoutSetMutation', () => { - it('Calls configureLayoutSet with correct arguments and payload', async () => { - const configureLayoutSetResult = renderHookWithMockStore()(() => useConfigureLayoutSetMutation(org, app)) - .renderHookResult - .result; - await configureLayoutSetResult.current.mutateAsync({ layoutSetName }); - await waitFor(() => expect(configureLayoutSetResult.current.isSuccess).toBe(true)); - - expect(queriesMock.configureLayoutSet).toHaveBeenCalledTimes(1); - expect(queriesMock.configureLayoutSet).toHaveBeenCalledWith(org, app, layoutSetName); - }); -}); \ No newline at end of file + it('Calls configureLayoutSet with correct arguments and payload', async () => { + const configureLayoutSetResult = renderHookWithMockStore()(() => + useConfigureLayoutSetMutation(org, app), + ).renderHookResult.result; + await configureLayoutSetResult.current.mutateAsync({ layoutSetName }); + await waitFor(() => expect(configureLayoutSetResult.current.isSuccess).toBe(true)); + + expect(queriesMock.configureLayoutSet).toHaveBeenCalledTimes(1); + expect(queriesMock.configureLayoutSet).toHaveBeenCalledWith(org, app, layoutSetName); + }); +}); diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useDeleteAppAttachmentMetadataMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useDeleteAppAttachmentMetadataMutation.test.ts index 302004030f0..8eecc126f60 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useDeleteAppAttachmentMetadataMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useDeleteAppAttachmentMetadataMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useDeleteAppAttachmentMetadataMutation } from './useDeleteAppAttachmentMetadataMutation'; // Test data: @@ -8,9 +9,9 @@ const id = 'test'; describe('useDeleteAppAttachmentMetadataMutation', () => { it('Calls deleteAppAttachmentMetadata with correct arguments and payload', async () => { - const metadataResult = renderHookWithMockStore()(() => useDeleteAppAttachmentMetadataMutation(org, app)) - .renderHookResult - .result; + const metadataResult = renderHookWithMockStore()(() => + useDeleteAppAttachmentMetadataMutation(org, app), + ).renderHookResult.result; await metadataResult.current.mutateAsync(id); expect(queriesMock.deleteAppAttachmentMetadata).toHaveBeenCalledTimes(1); expect(queriesMock.deleteAppAttachmentMetadata).toHaveBeenCalledWith(org, app, id); diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useDeleteFormComponentMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useDeleteFormComponentMutation.test.ts index c29d7257e36..684f1675368 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useDeleteFormComponentMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useDeleteFormComponentMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { waitFor } from '@testing-library/react'; import { useDeleteFormComponentMutation } from './useDeleteFormComponentMutation'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; @@ -21,17 +22,19 @@ describe('useDeleteFormComponentMutation', () => { selectedLayoutSet, expect.objectContaining({ data: expect.objectContaining({ - layout: expect.not.arrayContaining([ - expect.objectContaining({ id: component2IdMock }) - ]) - }) - }) + layout: expect.not.arrayContaining([expect.objectContaining({ id: component2IdMock })]), + }), + }), ); }); }); const renderDeleteFormComponentsMutation = async () => { - const formLayoutsResult = renderHookWithMockStore()(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; + const formLayoutsResult = renderHookWithMockStore()(() => + useFormLayoutsQuery(org, app, selectedLayoutSet), + ).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); - return renderHookWithMockStore()(() => useDeleteFormComponentMutation(org, app, selectedLayoutSet)).renderHookResult; -} + return renderHookWithMockStore()(() => + useDeleteFormComponentMutation(org, app, selectedLayoutSet), + ).renderHookResult; +}; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useDeleteFormContainerMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useDeleteFormContainerMutation.test.ts index 6be0443f953..c21bdfc27f0 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useDeleteFormContainerMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useDeleteFormContainerMutation.test.ts @@ -1,8 +1,10 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { waitFor } from '@testing-library/react'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { useDeleteFormContainerMutation } from './useDeleteFormContainerMutation'; -import { container1IdMock, layout1NameMock } from '../../testing/layoutMock'; +import { container1IdMock, externalLayoutsMock, layout1NameMock } from '../../testing/layoutMock'; +import { FormLayoutsResponse } from 'app-shared/types/api'; // Test data: const org = 'org'; @@ -22,18 +24,23 @@ describe('useDeleteFormContainerMutation', () => { selectedLayoutSet, expect.objectContaining({ data: expect.objectContaining({ - layout: expect.not.arrayContaining([ - expect.objectContaining({ id }) - ]) - }) - }) + layout: expect.not.arrayContaining([expect.objectContaining({ id })]), + }), + }), ); }); }); - const renderDeleteFormContainerMutation = async () => { - const formLayoutsResult = renderHookWithMockStore()(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; + const getFormLayouts = jest + .fn() + .mockImplementation(() => Promise.resolve(externalLayoutsMock)); + const formLayoutsResult = renderHookWithMockStore( + {}, + { getFormLayouts }, + )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); - return renderHookWithMockStore()(() => useDeleteFormContainerMutation(org, app, selectedLayoutSet)).renderHookResult; -} + return renderHookWithMockStore()(() => + useDeleteFormContainerMutation(org, app, selectedLayoutSet), + ).renderHookResult; +}; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useDeleteLayoutMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useDeleteLayoutMutation.test.ts index 7e8f804a674..32b84fe2546 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useDeleteLayoutMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useDeleteLayoutMutation.test.ts @@ -1,8 +1,10 @@ -import { queriesMock, formLayoutSettingsMock, renderHookWithMockStore, queryClientMock } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; +import { formLayoutSettingsMock, renderHookWithMockStore } from '../../testing/mocks'; import { useDeleteLayoutMutation } from './useDeleteLayoutMutation'; import { externalLayoutsMock, layout2NameMock } from '../../testing/layoutMock'; -import { QueryKey } from "app-shared/types/QueryKey"; -import { convertExternalLayoutsToInternalFormat } from "../../utils/formLayoutsUtils"; +import { QueryKey } from 'app-shared/types/QueryKey'; +import { convertExternalLayoutsToInternalFormat } from '../../utils/formLayoutsUtils'; // Test data: const org = 'org'; @@ -17,20 +19,40 @@ describe('useDeleteLayoutMutation', () => { const { result } = renderDeleteLayoutMutation(); await result.current.mutateAsync(layoutName); expect(queriesMock.deleteFormLayout).toHaveBeenCalledTimes(1); - expect(queriesMock.deleteFormLayout).toHaveBeenCalledWith(org, app, layoutName, selectedLayoutSet); + expect(queriesMock.deleteFormLayout).toHaveBeenCalledWith( + org, + app, + layoutName, + selectedLayoutSet, + ); }); it('Calls deleteFormLayout with the name of the receipt layout when deleting custom receipt', async () => { const { result } = renderDeleteLayoutMutation(); await result.current.mutateAsync(formLayoutSettingsMock.receiptLayoutName); expect(queriesMock.deleteFormLayout).toHaveBeenCalledTimes(1); - expect(queriesMock.deleteFormLayout).toHaveBeenCalledWith(org, app, formLayoutSettingsMock.receiptLayoutName, selectedLayoutSet); - expect(queriesMock.saveFormLayoutSettings).toHaveBeenCalledWith(org, app, selectedLayoutSet, { ...formLayoutSettingsMock, receiptLayoutName: undefined }); + expect(queriesMock.deleteFormLayout).toHaveBeenCalledWith( + org, + app, + formLayoutSettingsMock.receiptLayoutName, + selectedLayoutSet, + ); + expect(queriesMock.saveFormLayoutSettings).toHaveBeenCalledWith(org, app, selectedLayoutSet, { + ...formLayoutSettingsMock, + receiptLayoutName: undefined, + }); }); }); const renderDeleteLayoutMutation = () => { - queryClientMock.setQueryData([QueryKey.FormLayouts, org, app, selectedLayoutSet], convertExternalLayoutsToInternalFormat(externalLayoutsMock).convertedLayouts); - queryClientMock.setQueryData([QueryKey.FormLayoutSettings, org, app, selectedLayoutSet], formLayoutSettingsMock); - return renderHookWithMockStore()(() => useDeleteLayoutMutation(org, app, selectedLayoutSet)).renderHookResult; -} + queryClientMock.setQueryData( + [QueryKey.FormLayouts, org, app, selectedLayoutSet], + convertExternalLayoutsToInternalFormat(externalLayoutsMock).convertedLayouts, + ); + queryClientMock.setQueryData( + [QueryKey.FormLayoutSettings, org, app, selectedLayoutSet], + formLayoutSettingsMock, + ); + return renderHookWithMockStore()(() => useDeleteLayoutMutation(org, app, selectedLayoutSet)) + .renderHookResult; +}; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useFormLayoutMutation.test.tsx b/frontend/packages/ux-editor/src/hooks/mutations/useFormLayoutMutation.test.tsx index d2975d2936b..4702ca3a6de 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useFormLayoutMutation.test.tsx +++ b/frontend/packages/ux-editor/src/hooks/mutations/useFormLayoutMutation.test.tsx @@ -1,4 +1,6 @@ -import { queriesMock, queryClientMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useFormLayoutMutation } from './useFormLayoutMutation'; import { IInternalLayout } from '../../types/global'; import { ComponentType } from 'app-shared/types/ComponentType'; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useFormLayoutSettingsMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useFormLayoutSettingsMutation.test.ts index 6a4e055cf9b..5e72e54af57 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useFormLayoutSettingsMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useFormLayoutSettingsMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, formLayoutSettingsMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { formLayoutSettingsMock, renderHookWithMockStore } from '../../testing/mocks'; import { useFormLayoutSettingsMutation } from './useFormLayoutSettingsMutation'; import { waitFor } from '@testing-library/react'; @@ -9,9 +10,9 @@ const selectedLayoutSet = 'test-layout-set'; describe('useFormLayoutSettingsMutation', () => { it('Calls saveFormLayoutSettings with correct arguments and payload', async () => { - const settingsResult = renderHookWithMockStore()(() => useFormLayoutSettingsMutation(org, app, selectedLayoutSet)) - .renderHookResult - .result; + const settingsResult = renderHookWithMockStore()(() => + useFormLayoutSettingsMutation(org, app, selectedLayoutSet), + ).renderHookResult.result; settingsResult.current.mutate(formLayoutSettingsMock); await waitFor(() => expect(settingsResult.current.isSuccess).toBe(true)); @@ -21,7 +22,7 @@ describe('useFormLayoutSettingsMutation', () => { org, app, selectedLayoutSet, - formLayoutSettingsMock + formLayoutSettingsMock, ); }); }); diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useRuleConfigMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useRuleConfigMutation.test.ts index 543da2094a1..33662aa8584 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useRuleConfigMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useRuleConfigMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useRuleConfigMutation } from './useRuleConfigMutation'; import { RuleConfig } from 'app-shared/types/RuleConfig'; @@ -12,11 +13,11 @@ const newRuleConfig: RuleConfig = { ruleConnection1: { selectedFunction: 'selectedFunction1', inputParams: {}, - outParams: {} - } + outParams: {}, + }, }, - conditionalRendering: {} - } + conditionalRendering: {}, + }, }; describe('useRuleConfigMutation', () => { @@ -26,8 +27,15 @@ describe('useRuleConfigMutation', () => { const { result } = await render(); await result.current.mutateAsync(newRuleConfig); expect(queriesMock.saveRuleConfig).toHaveBeenCalledTimes(1); - expect(queriesMock.saveRuleConfig).toHaveBeenCalledWith(org, app, selectedLayoutSet, newRuleConfig); + expect(queriesMock.saveRuleConfig).toHaveBeenCalledWith( + org, + app, + selectedLayoutSet, + newRuleConfig, + ); }); }); -const render = async () => renderHookWithMockStore()(() => useRuleConfigMutation(org, app, selectedLayoutSet)).renderHookResult; +const render = async () => + renderHookWithMockStore()(() => useRuleConfigMutation(org, app, selectedLayoutSet)) + .renderHookResult; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateAppAttachmentMetadataMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateAppAttachmentMetadataMutation.test.ts index 105adf4f831..a2889843856 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateAppAttachmentMetadataMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateAppAttachmentMetadataMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useUpdateAppAttachmentMetadataMutation } from './useUpdateAppAttachmentMetadataMutation'; import { ApplicationAttachmentMetadata } from 'app-shared/types/ApplicationAttachmentMetadata'; @@ -8,9 +9,9 @@ const app = 'app'; describe('useUpdateAppAttachmentMetadataMutation', () => { it('Calls updateAppAttachmentMetadata with correct arguments and payload', async () => { - const metadataResult = renderHookWithMockStore()(() => useUpdateAppAttachmentMetadataMutation(org, app)) - .renderHookResult - .result; + const metadataResult = renderHookWithMockStore()(() => + useUpdateAppAttachmentMetadataMutation(org, app), + ).renderHookResult.result; const metadata: ApplicationAttachmentMetadata = { id: 'test', @@ -18,15 +19,11 @@ describe('useUpdateAppAttachmentMetadataMutation', () => { maxCount: 3, minCount: 1, maxSize: 16, - fileType: 'jpg' + fileType: 'jpg', }; await metadataResult.current.mutateAsync(metadata); expect(queriesMock.updateAppAttachmentMetadata).toHaveBeenCalledTimes(1); - expect(queriesMock.updateAppAttachmentMetadata).toHaveBeenCalledWith( - org, - app, - metadata - ); + expect(queriesMock.updateAppAttachmentMetadata).toHaveBeenCalledWith(org, app, metadata); }); }); diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormComponentMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormComponentMutation.test.ts index 93f74bb69e3..f68513d7647 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormComponentMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormComponentMutation.test.ts @@ -1,11 +1,17 @@ -import { queriesMock, queryClientMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { ComponentType } from 'app-shared/types/ComponentType'; -import { UpdateFormComponentMutationArgs, useUpdateFormComponentMutation } from './useUpdateFormComponentMutation'; +import { + UpdateFormComponentMutationArgs, + useUpdateFormComponentMutation, +} from './useUpdateFormComponentMutation'; import { component1IdMock, externalLayoutsMock, layout1NameMock } from '../../testing/layoutMock'; import type { FormCheckboxesComponent, FormComponent, - FormFileUploaderComponent, FormRadioButtonsComponent, + FormFileUploaderComponent, + FormRadioButtonsComponent, } from '../../types/FormComponent'; import { IDataModelBindings } from '../../types/global'; import { QueryKey } from 'app-shared/types/QueryKey'; @@ -25,7 +31,7 @@ const updatedComponent: FormComponent = { itemType: 'COMPONENT', type: ComponentType.TextArea, dataModelBindings, -} +}; const defaultArgs: UpdateFormComponentMutationArgs = { id, updatedComponent }; describe('useUpdateFormComponentMutation', () => { @@ -34,9 +40,9 @@ describe('useUpdateFormComponentMutation', () => { it('Saves layout with updated component', async () => { renderAndWaitForData(); - const updateFormComponentResult = renderHookWithMockStore()(() => useUpdateFormComponentMutation(org, app, selectedLayoutName, selectedLayoutSet)) - .renderHookResult - .result; + const updateFormComponentResult = renderHookWithMockStore()(() => + useUpdateFormComponentMutation(org, app, selectedLayoutName, selectedLayoutSet), + ).renderHookResult.result; await updateFormComponentResult.current.mutateAsync(defaultArgs); @@ -53,18 +59,18 @@ describe('useUpdateFormComponentMutation', () => { id, type, dataModelBindings, - } - ]) - }) - }) + }, + ]), + }), + }), ); }); it('Does not run attachment metadata queries if the component type is not fileUpload', async () => { renderAndWaitForData(); - const updateFormComponentResult = renderHookWithMockStore()(() => useUpdateFormComponentMutation(org, app, selectedLayoutName, selectedLayoutSet)) - .renderHookResult - .result; + const updateFormComponentResult = renderHookWithMockStore()(() => + useUpdateFormComponentMutation(org, app, selectedLayoutName, selectedLayoutSet), + ).renderHookResult.result; await updateFormComponentResult.current.mutateAsync(defaultArgs); expect(queriesMock.addAppAttachmentMetadata).not.toHaveBeenCalled(); expect(queriesMock.deleteAppAttachmentMetadata).not.toHaveBeenCalled(); @@ -73,9 +79,9 @@ describe('useUpdateFormComponentMutation', () => { it('Updates attachment metadata queries if the component type is fileUpload', async () => { renderAndWaitForData(); - const updateFormComponentResult = renderHookWithMockStore()(() => useUpdateFormComponentMutation(org, app, selectedLayoutName, selectedLayoutSet)) - .renderHookResult - .result; + const updateFormComponentResult = renderHookWithMockStore()(() => + useUpdateFormComponentMutation(org, app, selectedLayoutName, selectedLayoutSet), + ).renderHookResult.result; const newComponent: FormFileUploaderComponent = { ...updatedComponent, description: 'test', @@ -89,16 +95,16 @@ describe('useUpdateFormComponentMutation', () => { const args: UpdateFormComponentMutationArgs = { ...defaultArgs, updatedComponent: newComponent, - } + }; await updateFormComponentResult.current.mutateAsync(args); expect(queriesMock.updateAppAttachmentMetadata).toHaveBeenCalledTimes(1); }); it('Does not keep original optionsId and options props from component when updating RadioButtons and CheckBoxes', async () => { renderAndWaitForData(); - const updateFormComponentResult = renderHookWithMockStore()(() => useUpdateFormComponentMutation(org, app, selectedLayoutName, selectedLayoutSet)) - .renderHookResult - .result; + const updateFormComponentResult = renderHookWithMockStore()(() => + useUpdateFormComponentMutation(org, app, selectedLayoutName, selectedLayoutSet), + ).renderHookResult.result; for (const componentType of [ComponentType.RadioButtons, ComponentType.Checkboxes]) { for (const optionKind of ['options', 'optionsId']) { @@ -112,7 +118,7 @@ describe('useUpdateFormComponentMutation', () => { const args: UpdateFormComponentMutationArgs = { ...defaultArgs, updatedComponent: newComponent, - } + }; await updateFormComponentResult.current.mutateAsync(args); expect(queriesMock.saveFormLayout).toHaveBeenCalledWith( org, @@ -127,25 +133,20 @@ describe('useUpdateFormComponentMutation', () => { type: componentType, dataModelBindings, ...optionsProp, - } - ]) - }) - }) + }, + ]), + }), + }), ); } } - - }); }); const renderAndWaitForData = () => { queryClientMock.setQueryData( [QueryKey.FormLayouts, org, app, selectedLayoutSet], - convertExternalLayoutsToInternalFormat(externalLayoutsMock).convertedLayouts - ); - queryClientMock.setQueryData( - [QueryKey.RuleConfig, org, app, selectedLayoutSet], - ruleConfigMock + convertExternalLayoutsToInternalFormat(externalLayoutsMock).convertedLayouts, ); + queryClientMock.setQueryData([QueryKey.RuleConfig, org, app, selectedLayoutSet], ruleConfigMock); }; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormComponentOrderMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormComponentOrderMutation.test.ts index 8a4c8469046..8687ea713bf 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormComponentOrderMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormComponentOrderMutation.test.ts @@ -1,4 +1,5 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { waitFor } from '@testing-library/react'; import { useUpdateFormComponentOrderMutation } from './useUpdateFormComponentOrderMutation'; @@ -7,9 +8,11 @@ import { component1IdMock, component2IdMock, container1IdMock, + externalLayoutsMock, layout1NameMock, - layoutMock + layoutMock, } from '../../testing/layoutMock'; +import { FormLayoutsResponse } from 'app-shared/types/api'; // Test data: const org = 'org'; @@ -22,13 +25,13 @@ describe('useUpdateFormComponentOrderMutation', () => { it('Calls updateFormComponentOrder with correct arguments and payload', async () => { await renderAndWaitForData(); - const componentOrderResult = renderHookWithMockStore()(() => useUpdateFormComponentOrderMutation(org, app, selectedLayoutSet)) - .renderHookResult - .result; + const componentOrderResult = renderHookWithMockStore()(() => + useUpdateFormComponentOrderMutation(org, app, selectedLayoutSet), + ).renderHookResult.result; const newOrder: IFormLayoutOrder = { ...layoutMock.order, - [container1IdMock]: [component2IdMock, component1IdMock] + [container1IdMock]: [component2IdMock, component1IdMock], }; await componentOrderResult.current.mutateAsync(newOrder); @@ -44,14 +47,20 @@ describe('useUpdateFormComponentOrderMutation', () => { expect.objectContaining({ id: container1IdMock }), expect.objectContaining({ id: component2IdMock }), expect.objectContaining({ id: component1IdMock }), - ] - }) - }) + ], + }), + }), ); }); }); const renderAndWaitForData = async () => { - const formLayoutsResult = renderHookWithMockStore()(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; + const getFormLayouts = jest + .fn() + .mockImplementation(() => Promise.resolve(externalLayoutsMock)); + const formLayoutsResult = renderHookWithMockStore( + {}, + { getFormLayouts }, + )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); -} +}; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormContainerMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormContainerMutation.test.ts index 9cbb0aa7f8c..079c628edb6 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormContainerMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateFormContainerMutation.test.ts @@ -1,10 +1,22 @@ import { waitFor } from '@testing-library/react'; -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { useRuleConfigQuery } from '../queries/useRuleConfigQuery'; -import { UpdateFormContainerMutationArgs, useUpdateFormContainerMutation } from './useUpdateFormContainerMutation'; +import { + UpdateFormContainerMutationArgs, + useUpdateFormContainerMutation, +} from './useUpdateFormContainerMutation'; import { FormContainer } from '../../types/FormContainer'; -import { container1IdMock, layout1Mock, layout1NameMock } from '../../testing/layoutMock'; +import { + container1IdMock, + externalLayoutsMock, + layout1Mock, + layout1NameMock, +} from '../../testing/layoutMock'; +import { ruleConfig as ruleConfigMock } from '../../testing/ruleConfigMock'; +import { FormLayoutsResponse } from 'app-shared/types/api'; +import { RuleConfig } from 'app-shared/types/RuleConfig'; // Test data: const org = 'org'; @@ -24,9 +36,9 @@ describe('useUpdateFormContainerMutation', () => { it('Saves layouts with new container and updates rule config', async () => { await renderAndWaitForData(); - const updateFormContainerResult = renderHookWithMockStore()(() => useUpdateFormContainerMutation(org, app, selectedLayoutName, selectedLayoutSet)) - .renderHookResult - .result; + const updateFormContainerResult = renderHookWithMockStore()(() => + useUpdateFormContainerMutation(org, app, selectedLayoutName, selectedLayoutSet), + ).renderHookResult.result; await updateFormContainerResult.current.mutateAsync(mutationArgs); @@ -43,17 +55,29 @@ describe('useUpdateFormContainerMutation', () => { ...layout1Mock.data.layout[0], id: updatedContainer.id, maxCount, - } - ]) - }) - }) + }, + ]), + }), + }), ); }); }); const renderAndWaitForData = async () => { - const formLayoutsResult = renderHookWithMockStore()(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; - const ruleConfigResult = renderHookWithMockStore()(() => useRuleConfigQuery(org, app, selectedLayoutSet)).renderHookResult.result; + const getFormLayouts = jest + .fn() + .mockImplementation(() => Promise.resolve(externalLayoutsMock)); + const getRuleConfig = jest + .fn() + .mockImplementation(() => Promise.resolve(ruleConfigMock)); + const formLayoutsResult = renderHookWithMockStore( + {}, + { getFormLayouts }, + )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; + const ruleConfigResult = renderHookWithMockStore( + {}, + { getRuleConfig }, + )(() => useRuleConfigQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); await waitFor(() => expect(ruleConfigResult.current.isSuccess).toBe(true)); -} +}; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateLayoutNameMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateLayoutNameMutation.test.ts index a2bfbe2faba..2810551b0c5 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateLayoutNameMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateLayoutNameMutation.test.ts @@ -1,8 +1,12 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { formLayoutSettingsMock, renderHookWithMockStore } from '../../testing/mocks'; import { useFormLayoutsQuery } from '../queries/useFormLayoutsQuery'; import { useFormLayoutSettingsQuery } from '../queries/useFormLayoutSettingsQuery'; import { waitFor } from '@testing-library/react'; -import { UpdateLayoutNameMutationArgs, useUpdateLayoutNameMutation } from './useUpdateLayoutNameMutation'; +import { + UpdateLayoutNameMutationArgs, + useUpdateLayoutNameMutation, +} from './useUpdateLayoutNameMutation'; import { layout1NameMock } from '../../testing/layoutMock'; // Test data: @@ -19,20 +23,34 @@ describe('useUpdateLayoutNameMutation', () => { it('Updates layout name', async () => { await renderAndWaitForData(); - const updateLayoutNameResult = renderHookWithMockStore()(() => useUpdateLayoutNameMutation(org, app, selectedLayoutSet)) - .renderHookResult - .result; + const updateLayoutNameResult = renderHookWithMockStore()(() => + useUpdateLayoutNameMutation(org, app, selectedLayoutSet), + ).renderHookResult.result; await updateLayoutNameResult.current.mutateAsync(args); expect(queriesMock.updateFormLayoutName).toHaveBeenCalledTimes(1); - expect(queriesMock.updateFormLayoutName).toHaveBeenCalledWith(org, app, oldName, newName, selectedLayoutSet); + expect(queriesMock.updateFormLayoutName).toHaveBeenCalledWith( + org, + app, + oldName, + newName, + selectedLayoutSet, + ); }); }); const renderAndWaitForData = async () => { - const formLayoutsResult = renderHookWithMockStore()(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; - const settingsResult = renderHookWithMockStore()(() => useFormLayoutSettingsQuery(org, app, selectedLayoutSet)).renderHookResult.result; + const getFormLayoutSettings = jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)); + const formLayoutsResult = renderHookWithMockStore()(() => + useFormLayoutsQuery(org, app, selectedLayoutSet), + ).renderHookResult.result; + const settingsResult = renderHookWithMockStore( + {}, + { getFormLayoutSettings }, + )(() => useFormLayoutSettingsQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); await waitFor(() => expect(settingsResult.current.isSuccess).toBe(true)); -} +}; diff --git a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateLayoutOrderMutation.test.ts b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateLayoutOrderMutation.test.ts index 2556f89abc8..2196fb5cee1 100644 --- a/frontend/packages/ux-editor/src/hooks/mutations/useUpdateLayoutOrderMutation.test.ts +++ b/frontend/packages/ux-editor/src/hooks/mutations/useUpdateLayoutOrderMutation.test.ts @@ -1,7 +1,11 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { formLayoutSettingsMock, renderHookWithMockStore } from '../../testing/mocks'; import { useFormLayoutSettingsQuery } from '../queries/useFormLayoutSettingsQuery'; import { waitFor } from '@testing-library/react'; -import { UpdateLayoutOrderMutationArgs, useUpdateLayoutOrderMutation } from './useUpdateLayoutOrderMutation'; +import { + UpdateLayoutOrderMutationArgs, + useUpdateLayoutOrderMutation, +} from './useUpdateLayoutOrderMutation'; import { layout1NameMock, layout2NameMock } from '../../testing/layoutMock'; // Test data: @@ -15,13 +19,13 @@ describe('useUpdateLayoutOrderMutation', () => { it('Moves layout down when direction is set to "down"', async () => { await renderAndWaitForData(); - const updateLayoutOrderResult = renderHookWithMockStore()(() => useUpdateLayoutOrderMutation(org, app, selectedLayoutSet)) - .renderHookResult - .result; + const updateLayoutOrderResult = renderHookWithMockStore()(() => + useUpdateLayoutOrderMutation(org, app, selectedLayoutSet), + ).renderHookResult.result; const args: UpdateLayoutOrderMutationArgs = { layoutName: layout1NameMock, - direction: 'down' + direction: 'down', }; await updateLayoutOrderResult.current.mutateAsync(args); @@ -32,22 +36,22 @@ describe('useUpdateLayoutOrderMutation', () => { selectedLayoutSet, expect.objectContaining({ pages: expect.objectContaining({ - order: [layout2NameMock, layout1NameMock] - }) - }) + order: [layout2NameMock, layout1NameMock], + }), + }), ); }); it('Moves layout up when direction is set to "up"', async () => { await renderAndWaitForData(); - const updateLayoutOrderResult = renderHookWithMockStore()(() => useUpdateLayoutOrderMutation(org, app, selectedLayoutSet)) - .renderHookResult - .result; + const updateLayoutOrderResult = renderHookWithMockStore()(() => + useUpdateLayoutOrderMutation(org, app, selectedLayoutSet), + ).renderHookResult.result; const args: UpdateLayoutOrderMutationArgs = { layoutName: layout2NameMock, - direction: 'up' + direction: 'up', }; await updateLayoutOrderResult.current.mutateAsync(args); @@ -58,14 +62,20 @@ describe('useUpdateLayoutOrderMutation', () => { selectedLayoutSet, expect.objectContaining({ pages: expect.objectContaining({ - order: [layout2NameMock, layout1NameMock] - }) - }) + order: [layout2NameMock, layout1NameMock], + }), + }), ); }); }); const renderAndWaitForData = async () => { - const settingsResult = renderHookWithMockStore()(() => useFormLayoutSettingsQuery(org, app, selectedLayoutSet)).renderHookResult.result; + const getFormLayoutSettings = jest + .fn() + .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)); + const settingsResult = renderHookWithMockStore( + {}, + { getFormLayoutSettings }, + )(() => useFormLayoutSettingsQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(settingsResult.current.isSuccess).toBe(true)); }; diff --git a/frontend/packages/ux-editor/src/hooks/queries/useFrontEndSettingsQuery.test.ts b/frontend/packages/ux-editor/src/hooks/queries/useFrontEndSettingsQuery.test.ts index 9bc0c01c029..5ad5a989945 100644 --- a/frontend/packages/ux-editor/src/hooks/queries/useFrontEndSettingsQuery.test.ts +++ b/frontend/packages/ux-editor/src/hooks/queries/useFrontEndSettingsQuery.test.ts @@ -1,5 +1,6 @@ import { waitFor } from '@testing-library/react'; -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { useFrontEndSettingsQuery } from './useFrontEndSettingsQuery'; // Test data: diff --git a/frontend/packages/ux-editor/src/hooks/queries/useRuleModelQuery.test.ts b/frontend/packages/ux-editor/src/hooks/queries/useRuleModelQuery.test.ts index 2220921a2bf..4a34bf5d49b 100644 --- a/frontend/packages/ux-editor/src/hooks/queries/useRuleModelQuery.test.ts +++ b/frontend/packages/ux-editor/src/hooks/queries/useRuleModelQuery.test.ts @@ -1,7 +1,7 @@ -import { queriesMock, renderHookWithMockStore } from '../../testing/mocks'; +import { renderHookWithMockStore } from '../../testing/mocks'; import { waitFor } from '@testing-library/react'; import { useRuleModelQuery, WindowWithRuleModel } from './useRuleModelQuery'; -import { +import ruleHandlerMock, { condition1Input1Label, condition1Input1Name, condition1Name, @@ -30,18 +30,20 @@ const org = 'org'; const app = 'app'; const selectedLayoutSet = 'test-layout-set'; +const getRuleModel = jest.fn().mockImplementation(() => Promise.resolve(ruleHandlerMock)); + describe('useRuleModelQuery', () => { afterAll(() => { delete global.window; }); it('Calls getRuleModel with correct parameters', async () => { - await renderAndWaitForSuccess(); - expect(queriesMock.getRuleModel).toHaveBeenCalledTimes(1); - expect(queriesMock.getRuleModel).toHaveBeenCalledWith(org, app, selectedLayoutSet); + await renderAndWaitForSuccess({ getRuleModel }); + expect(getRuleModel).toHaveBeenCalledTimes(1); + expect(getRuleModel).toHaveBeenCalledWith(org, app, selectedLayoutSet); }); it('Parses file correctly and returns an array of rules and conditions', async () => { - const { result } = await renderAndWaitForSuccess(); + const { result } = await renderAndWaitForSuccess({ getRuleModel }); expect(result.current.data).toEqual([ { name: rule1Name, @@ -80,7 +82,7 @@ describe('useRuleModelQuery', () => { it('sets all ruleModel related objects to "undefined" in window object if ruleHandler does not exist in repo', async () => { const globalWindowWithRuleModel = global.window as WindowWithRuleModel; - await renderAndWaitForSuccess(); + await renderAndWaitForSuccess({ getRuleModel }); expect(globalWindowWithRuleModel.ruleHandlerObject).toBeDefined(); expect(globalWindowWithRuleModel.ruleHandlerHelper).toBeDefined(); expect(globalWindowWithRuleModel.conditionalRuleHandlerObject).toBeDefined(); diff --git a/frontend/packages/ux-editor/src/hooks/useFormLayoutsSelector.test.ts b/frontend/packages/ux-editor/src/hooks/useFormLayoutsSelector.test.ts index 757a7024a85..15f031fd3d5 100644 --- a/frontend/packages/ux-editor/src/hooks/useFormLayoutsSelector.test.ts +++ b/frontend/packages/ux-editor/src/hooks/useFormLayoutsSelector.test.ts @@ -18,9 +18,11 @@ const selectedLayoutName = 'Side1'; const selectedLayoutSet = 'test-layout-set'; const render = async (callback: () => IFormLayouts | IInternalLayout | IInternalLayoutWithName) => { - const formLayoutsResult = renderHookWithMockStore()(() => - useFormLayoutsQuery(org, app, selectedLayoutSet), - ).renderHookResult.result; + const getFormLayouts = jest.fn().mockImplementation(() => Promise.resolve(externalLayoutsMock)); + const formLayoutsResult = renderHookWithMockStore( + {}, + { getFormLayouts }, + )(() => useFormLayoutsQuery(org, app, selectedLayoutSet)).renderHookResult.result; await waitFor(() => expect(formLayoutsResult.current.isSuccess).toBe(true)); return renderHookWithMockStore()(() => callback()).renderHookResult; diff --git a/frontend/packages/ux-editor/src/hooks/useValidateComponent.test.ts b/frontend/packages/ux-editor/src/hooks/useValidateComponent.test.ts index e0b531884d3..378f5d0b256 100644 --- a/frontend/packages/ux-editor/src/hooks/useValidateComponent.test.ts +++ b/frontend/packages/ux-editor/src/hooks/useValidateComponent.test.ts @@ -1,6 +1,10 @@ import { ComponentType } from 'app-shared/types/ComponentType'; import { ErrorCode, useValidateComponent } from './useValidateComponent'; -import { FormCheckboxesComponent, FormComponent, FormRadioButtonsComponent } from '../types/FormComponent'; +import { + FormCheckboxesComponent, + FormComponent, + FormRadioButtonsComponent, +} from '../types/FormComponent'; import { optionListIdsMock, renderHookWithMockStore } from '../testing/mocks'; describe('useValidateComponent', () => { @@ -139,4 +143,12 @@ describe('useValidateComponent', () => { }); }); -const render = (component: FormComponent) => renderHookWithMockStore()(() => useValidateComponent(component)).renderHookResult.result.current; +const render = (component: FormComponent) => { + const queries = { + getOptionListIds: jest + .fn() + .mockImplementation(() => Promise.resolve(optionListIdsMock)), + }; + return renderHookWithMockStore({}, queries)(() => useValidateComponent(component)) + .renderHookResult.result.current; +}; diff --git a/frontend/packages/ux-editor/src/testing/mocks.tsx b/frontend/packages/ux-editor/src/testing/mocks.tsx index 7f450e983e5..ab4fb227d47 100644 --- a/frontend/packages/ux-editor/src/testing/mocks.tsx +++ b/frontend/packages/ux-editor/src/testing/mocks.tsx @@ -7,23 +7,14 @@ import { render, renderHook } from '@testing-library/react'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { ILayoutSettings } from 'app-shared/types/global'; import { BrowserRouter } from 'react-router-dom'; -import ruleHandlerMock from './ruleHandlerMock'; import { PreviewConnectionContextProvider } from 'app-shared/providers/PreviewConnectionContext'; -import { ruleConfig as ruleConfigMock } from './ruleConfigMock'; -import { - externalLayoutsMock, - layout1NameMock, - layout2NameMock, - layoutSetsMock, -} from './layoutMock'; +import { layout1NameMock, layout2NameMock } from './layoutMock'; import { appStateMock } from './stateMocks'; -import { queriesMock as allQueriesMock } from 'app-shared/mocks/queriesMock'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; import { QueryClient } from '@tanstack/react-query'; -import expressionSchema from './schemas/json/layout/expression.schema.v1.json'; -import numberFormatSchema from './schemas/json/layout/number-format.schema.v1.json'; -import layoutSchema from './schemas/json/layout/layout.schema.v1.json'; import { AppContext, AppContextProps } from '../AppContext'; import { appContextMock } from './appContextMock'; +import { queryClientMock } from 'app-shared/mocks/queryClientMock'; export const formLayoutSettingsMock: ILayoutSettings = { pages: { @@ -36,43 +27,6 @@ export const textLanguagesMock = ['nb', 'nn', 'en']; export const optionListIdsMock: string[] = ['test-1', 'test-2']; -export const queriesMock: ServicesContextProps = { - ...allQueriesMock, - addAppAttachmentMetadata: jest.fn().mockImplementation(() => Promise.resolve({})), - addLayoutSet: jest.fn(), - configureLayoutSet: jest.fn().mockImplementation(() => Promise.resolve({})), - deleteAppAttachmentMetadata: jest.fn().mockImplementation(() => Promise.resolve({})), - deleteFormLayout: jest.fn().mockImplementation(() => Promise.resolve({})), - getDatamodelMetadata: jest.fn().mockImplementation(() => Promise.resolve({ elements: {} })), - getExpressionSchema: jest.fn().mockImplementation(() => Promise.resolve(expressionSchema)), - getFormLayoutSettings: jest - .fn() - .mockImplementation(() => Promise.resolve(formLayoutSettingsMock)), - getFormLayouts: jest.fn().mockImplementation(() => Promise.resolve(externalLayoutsMock)), - getFrontEndSettings: jest.fn().mockImplementation(() => Promise.resolve({})), - getInstanceIdForPreview: jest.fn(), - getLayoutSchema: jest.fn().mockImplementation(() => Promise.resolve(layoutSchema)), - getLayoutSets: jest.fn().mockImplementation(() => Promise.resolve(layoutSetsMock)), - getNumberFormatSchema: jest.fn().mockImplementation(() => Promise.resolve(numberFormatSchema)), - getOptionListIds: jest.fn().mockImplementation(() => Promise.resolve(optionListIdsMock)), - getRuleConfig: jest.fn().mockImplementation(() => Promise.resolve(ruleConfigMock)), - getRuleModel: jest.fn().mockImplementation(() => Promise.resolve(ruleHandlerMock)), - getTextLanguages: jest.fn().mockImplementation(() => Promise.resolve(textLanguagesMock)), - getTextResources: jest.fn().mockImplementation(() => Promise.resolve([])), - getWidgetSettings: jest.fn().mockImplementation(() => Promise.resolve({})), - saveFormLayout: jest.fn().mockImplementation(() => Promise.resolve({})), - saveFormLayoutSettings: jest.fn().mockImplementation(() => Promise.resolve({})), - updateAppAttachmentMetadata: jest.fn().mockImplementation(() => Promise.resolve({})), - updateFormLayoutName: jest.fn().mockImplementation(() => Promise.resolve({})), - upsertTextResources: jest.fn().mockImplementation(() => Promise.resolve()), -}; - -export const queryClientMock = new QueryClient({ - defaultOptions: { - queries: { staleTime: Infinity }, - }, -}); - type WrapperArgs = { appContextProps: Partial; queries: Partial; diff --git a/frontend/resourceadm/components/ImportResourceModal/ImportResourceModal.test.tsx b/frontend/resourceadm/components/ImportResourceModal/ImportResourceModal.test.tsx index eea7ef23265..5d2b2c710f3 100644 --- a/frontend/resourceadm/components/ImportResourceModal/ImportResourceModal.test.tsx +++ b/frontend/resourceadm/components/ImportResourceModal/ImportResourceModal.test.tsx @@ -6,7 +6,6 @@ import { textMock } from '../../../testing/mocks/i18nMock'; import { act } from 'react-dom/test-utils'; import { MemoryRouter } from 'react-router-dom'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; import { Altinn2LinkService } from 'app-shared/types/Altinn2LinkService'; @@ -118,8 +117,7 @@ describe('ImportResourceModal', () => { }); const render = (props: Partial = {}) => { - const allQueries: ServicesContextProps = { - ...queriesMock, + const allQueries: Partial = { getAltinn2LinkServices, importResourceFromAltinn2, }; diff --git a/frontend/resourceadm/components/ImportResourceModal/ServiceContent/ServiceContent.test.tsx b/frontend/resourceadm/components/ImportResourceModal/ServiceContent/ServiceContent.test.tsx index 537aea58b1c..35d9ddf48f3 100644 --- a/frontend/resourceadm/components/ImportResourceModal/ServiceContent/ServiceContent.test.tsx +++ b/frontend/resourceadm/components/ImportResourceModal/ServiceContent/ServiceContent.test.tsx @@ -17,8 +17,6 @@ import { queriesMock } from 'app-shared/mocks/queriesMock'; const mockSelectedContext: string = 'selectedContext'; const mockEnv: string = 'env1'; -const getAltinn2LinkServices = jest.fn().mockImplementation(() => Promise.resolve({})); - const mockAltinn2LinkService: Altinn2LinkService = { externalServiceCode: 'code1', externalServiceEditionCode: 'edition1', @@ -48,7 +46,7 @@ describe('ServiceContent', () => { it('fetches getAltinn2LinkServices on mount', () => { render(); - expect(getAltinn2LinkServices).toHaveBeenCalledTimes(1); + expect(queriesMock.getAltinn2LinkServices).toHaveBeenCalledTimes(1); }); it('shows an error message if an error occured on the "getAltinn2LinkServices" query', async () => { @@ -70,12 +68,7 @@ describe('ServiceContent', () => { }); it('renders empty list state correctly', async () => { - render( - {}, - { - getAltinn2LinkServices: () => Promise.resolve([]), - }, - ); + render(); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.import_resource_spinner')), @@ -116,9 +109,11 @@ describe('ServiceContent', () => { }); const resolveAndWaitForSpinnerToDisappear = async (props: Partial = {}) => { - getAltinn2LinkServices.mockImplementation(() => Promise.resolve(mockAltinn2LinkServices)); + const getAltinn2LinkServices = jest + .fn() + .mockImplementation(() => Promise.resolve(mockAltinn2LinkServices)); - render(props); + render(props, { getAltinn2LinkServices }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.import_resource_spinner')), ); @@ -131,7 +126,6 @@ const render = ( ) => { const allQueries: ServicesContextProps = { ...queriesMock, - getAltinn2LinkServices, ...queries, }; diff --git a/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.test.tsx b/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.test.tsx index 77879000143..977585a3e1d 100644 --- a/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.test.tsx +++ b/frontend/resourceadm/pages/DeployResourcePage/DeployResourcePage.test.tsx @@ -8,27 +8,19 @@ import { import { DeployResourcePage, DeployResourcePageProps } from './DeployResourcePage'; import { textMock } from '../../../testing/mocks/i18nMock'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { MemoryRouter } from 'react-router-dom'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { QueryClient, UseMutationResult } from '@tanstack/react-query'; import { usePublishResourceMutation } from 'resourceadm/hooks/mutations'; import { RepoStatus } from 'app-shared/types/RepoStatus'; -import { ResourceVersionStatus, Version, Validation } from 'app-shared/types/ResourceAdm'; +import { Validation } from 'app-shared/types/ResourceAdm'; import userEvent from '@testing-library/user-event'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; const mockResourceId: string = 'r1'; const mockSelectedContext: string = 'test'; const mockId: string = 'page-content-deploy'; -const mockRepoStatus: RepoStatus = { - aheadBy: 0, - behindBy: 0, - contentStatus: [], - hasMergeConflict: false, - repositoryStatus: 'Ok', -}; - const mockRepoStatusAhead: RepoStatus = { aheadBy: 1, behindBy: 0, @@ -37,18 +29,6 @@ const mockRepoStatusAhead: RepoStatus = { repositoryStatus: 'Ok', }; -const mockVersionTT02: Version = { version: null, environment: 'tt02' }; -const mockVersionPROD: Version = { version: null, environment: 'prod' }; -const mockVersionAT22: Version = { version: null, environment: 'at22' }; -const mockVersionAT23: Version = { version: null, environment: 'at23' }; - -const mockPublishStatus: ResourceVersionStatus = { - policyVersion: null, - resourceVersion: '1', - publishedVersions: [mockVersionTT02, mockVersionPROD, mockVersionAT22, mockVersionAT23], -}; - -const mockValidatePolicyData1: Validation = { status: 200, errors: [] }; const mockValidatePolicyData2: Validation = { status: 400, errors: ['rule1.policyerror.missingsubject'], @@ -58,18 +38,12 @@ const mockValidatePolicyData3: Validation = { errors: ['policyerror.missingpolicy'], }; -const mockValidateResourceData1: Validation = { status: 200, errors: [] }; const mockValidateResourceData2: Validation = { status: 400, errors: ['resource.title'] }; const mockResourceVersionText: string = '2'; const mockNavigateToPageWithError = jest.fn(); const mockOnSaveVersion = jest.fn(); -const getRepoStatus = jest.fn().mockImplementation(() => Promise.resolve({})); -const getResourcePublishStatus = jest.fn().mockImplementation(() => Promise.resolve({})); -const getValidatePolicy = jest.fn().mockImplementation(() => Promise.resolve({})); -const getValidateResource = jest.fn().mockImplementation(() => Promise.resolve({})); - jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useParams: () => ({ @@ -105,22 +79,22 @@ describe('DeployResourcePage', () => { it('fetches repo status data on mount', () => { render(); - expect(getRepoStatus).toHaveBeenCalledTimes(1); + expect(queriesMock.getRepoStatus).toHaveBeenCalledTimes(1); }); it('fetches resource publish status data on mount', () => { render(); - expect(getResourcePublishStatus).toHaveBeenCalledTimes(1); + expect(queriesMock.getResourcePublishStatus).toHaveBeenCalledTimes(1); }); it('fetches validates policy on mount', () => { render(); - expect(getValidatePolicy).toHaveBeenCalledTimes(1); + expect(queriesMock.getValidatePolicy).toHaveBeenCalledTimes(1); }); it('fetches validates resource on mount', () => { render(); - expect(getValidateResource).toHaveBeenCalledTimes(1); + expect(queriesMock.getValidateResource).toHaveBeenCalledTimes(1); }); it.each(['getResourcePublishStatus', 'getValidatePolicy', 'getValidateResource'])( @@ -302,7 +276,7 @@ describe('DeployResourcePage', () => { it('disables the deploy buttons when there is validate resource error', async () => { await resolveAndWaitForSpinnerToDisappear({ - getValidateResource: () => Promise.resolve(mockValidateResourceData2), + getValidateResource: () => Promise.resolve(mockValidateResourceData2), }); const tt02 = textMock('resourceadm.deploy_test_env'); @@ -321,7 +295,7 @@ describe('DeployResourcePage', () => { it('disables the deploy buttons when there is validate policy error', async () => { await resolveAndWaitForSpinnerToDisappear({ - getValidatePolicy: () => Promise.resolve(mockValidatePolicyData2), + getValidatePolicy: () => Promise.resolve(mockValidatePolicyData2), }); const tt02 = textMock('resourceadm.deploy_test_env'); const prod = textMock('resourceadm.deploy_prod_env'); @@ -393,12 +367,12 @@ const resolveAndWaitForSpinnerToDisappear = async ( queries: Partial = {}, props: Partial = {}, ) => { - getRepoStatus.mockImplementation(() => Promise.resolve(mockRepoStatus)); - getResourcePublishStatus.mockImplementation(() => Promise.resolve(mockPublishStatus)); - getValidatePolicy.mockImplementation(() => Promise.resolve(mockValidatePolicyData1)); - getValidateResource.mockImplementation(() => Promise.resolve(mockValidateResourceData1)); - - render(queries, props); + render( + { + ...queries, + }, + props, + ); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.deploy_spinner')), ); @@ -409,17 +383,12 @@ const render = ( props: Partial = {}, queryClient: QueryClient = createQueryClientMock(), ) => { - const allQueries: ServicesContextProps = { - ...queriesMock, - getRepoStatus, - getResourcePublishStatus, - getValidatePolicy, - getValidateResource, + const allQueries = { ...queries, }; return rtlRender( - + , diff --git a/frontend/resourceadm/pages/PolicyEditorPage/PolicyEditorPage.test.tsx b/frontend/resourceadm/pages/PolicyEditorPage/PolicyEditorPage.test.tsx index 1c34bad8383..0053c50bfc0 100644 --- a/frontend/resourceadm/pages/PolicyEditorPage/PolicyEditorPage.test.tsx +++ b/frontend/resourceadm/pages/PolicyEditorPage/PolicyEditorPage.test.tsx @@ -3,7 +3,6 @@ import { render as rtlRender, screen, waitForElementToBeRemoved } from '@testing import { PolicyEditorPage, PolicyEditorPageProps } from './PolicyEditorPage'; import { textMock } from '../../../testing/mocks/i18nMock'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { MemoryRouter } from 'react-router-dom'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { QueryClient } from '@tanstack/react-query'; @@ -89,21 +88,21 @@ describe('PolicyEditorPage', () => { screen.queryByRole('heading', { name: textMock('policy_editor.rules'), level: 2 }), ).not.toBeInTheDocument(); - getPolicy.mockImplementation(() => Promise.resolve(mockPolicy)); + getPolicy.mockImplementation(() => Promise.resolve(mockPolicy)); expect(screen.getByTitle(textMock('resourceadm.policy_editor_spinner'))).toBeInTheDocument(); expect( screen.queryByRole('heading', { name: textMock('policy_editor.rules'), level: 2 }), ).not.toBeInTheDocument(); - getPolicyActions.mockImplementation(() => Promise.resolve(mockActions)); + getPolicyActions.mockImplementation(() => Promise.resolve(mockActions)); expect(screen.getByTitle(textMock('resourceadm.policy_editor_spinner'))).toBeInTheDocument(); expect( screen.queryByRole('heading', { name: textMock('policy_editor.rules'), level: 2 }), ).not.toBeInTheDocument(); - getPolicySubjects.mockImplementation(() => Promise.resolve(mockSubjects)); + getPolicySubjects.mockImplementation(() => Promise.resolve(mockSubjects)); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.policy_editor_spinner')), @@ -119,7 +118,6 @@ const render = ( queryClient: QueryClient = createQueryClientMock(), ) => { const allQueries: ServicesContextProps = { - ...queriesMock, getPolicy, getPolicyActions, getPolicySubjects, diff --git a/frontend/resourceadm/pages/ResourceDashboardPage/ResourceDashboardPage.test.tsx b/frontend/resourceadm/pages/ResourceDashboardPage/ResourceDashboardPage.test.tsx index 4ceec5cbec7..b427c4fc214 100644 --- a/frontend/resourceadm/pages/ResourceDashboardPage/ResourceDashboardPage.test.tsx +++ b/frontend/resourceadm/pages/ResourceDashboardPage/ResourceDashboardPage.test.tsx @@ -6,10 +6,12 @@ import { act } from 'react-dom/test-utils'; import { textMock } from '../../../testing/mocks/i18nMock'; import { ResourceListItem } from 'app-shared/types/ResourceAdm'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { MemoryRouter } from 'react-router-dom'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { QueryClient } from '@tanstack/react-query'; +import { queriesMock } from 'app-shared/mocks/queriesMock'; +import { Organization } from 'app-shared/types/Organization'; +import { organization } from 'app-shared/mocks/mocks'; const mockResourceListItem1: ResourceListItem = { title: { nb: 'resource 1', nn: '', en: '' }, @@ -54,9 +56,6 @@ const mockResourceList: ResourceListItem[] = [ mockResourceListItem5, ]; -const getResourceList = jest.fn().mockImplementation(() => Promise.resolve({})); -const getOrganizations = jest.fn().mockImplementation(() => Promise.resolve([])); - jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useParams: () => ({ @@ -64,40 +63,26 @@ jest.mock('react-router-dom', () => ({ }), })); -// Mocking console.error due to Tanstack Query removing custom logger between V4 and v5 see issue: #11692 -const realConsole = console; - describe('ResourceDashBoardPage', () => { - beforeEach(() => { - global.console = { - ...console, - error: jest.fn(), - }; - }); afterEach(() => { - global.console = realConsole; jest.clearAllMocks(); }); it('fetches resource list on mount', () => { render(); - expect(getResourceList).toHaveBeenCalledTimes(1); + expect(queriesMock.getResourceList).toHaveBeenCalledTimes(1); }); it('shows correct organization header', async () => { - getOrganizations.mockImplementation(() => - Promise.resolve([ + const getOrganizations = jest.fn().mockImplementation(() => + Promise.resolve([ { - avatar_url: 'http://studio.localhost/repos/avatars/5d076e5c3d34cb8bb08e54a4bb7e223e', - description: 'Internt organisasjon for test av løsning', + ...organization, full_name: 'Testdepartementet', - id: 3, - location: '', username: 'ttd', - website: '', }, ]), ); - render(); + render({ getOrganizations }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.dashboard_spinner')), ); @@ -121,8 +106,10 @@ describe('ResourceDashBoardPage', () => { }); it('does not show the spinner when the resource list is present', async () => { - getResourceList.mockImplementation(() => Promise.resolve(mockResourceList)); - render(); + const getResourceList = jest + .fn() + .mockImplementation(() => Promise.resolve(mockResourceList)); + render({ getResourceList }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.dashboard_spinner')), ); @@ -138,8 +125,10 @@ describe('ResourceDashBoardPage', () => { it('opens the import resource from altinn 2 modal on click', async () => { const user = userEvent.setup(); - getResourceList.mockImplementation(() => Promise.resolve(mockResourceList)); - render(); + const getResourceList = jest + .fn() + .mockImplementation(() => Promise.resolve(mockResourceList)); + render({ getResourceList }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.dashboard_spinner')), ); @@ -165,8 +154,10 @@ describe('ResourceDashBoardPage', () => { it('opens the create new resource modal on click', async () => { const user = userEvent.setup(); - getResourceList.mockImplementation(() => Promise.resolve(mockResourceList)); - render(); + const getResourceList = jest + .fn() + .mockImplementation(() => Promise.resolve(mockResourceList)); + render({ getResourceList }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.dashboard_spinner')), ); @@ -192,8 +183,10 @@ describe('ResourceDashBoardPage', () => { it('filters the resource list when the search value changes', async () => { const user = userEvent.setup(); - getResourceList.mockImplementation(() => Promise.resolve(mockResourceList)); - render(); + const getResourceList = jest + .fn() + .mockImplementation(() => Promise.resolve(mockResourceList)); + render({ getResourceList }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.dashboard_spinner')), ); @@ -209,8 +202,10 @@ describe('ResourceDashBoardPage', () => { }); it('does not display the error message when the list is not empty', async () => { - getResourceList.mockImplementation(() => Promise.resolve(mockResourceList)); - render(); + const getResourceList = jest + .fn() + .mockImplementation(() => Promise.resolve(mockResourceList)); + render({ getResourceList }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.dashboard_spinner')), ); @@ -222,8 +217,10 @@ describe('ResourceDashBoardPage', () => { it('displays empty list message when the list is empty', async () => { const user = userEvent.setup(); - getResourceList.mockImplementation(() => Promise.resolve(mockResourceList)); - render(); + const getResourceList = jest + .fn() + .mockImplementation(() => Promise.resolve(mockResourceList)); + render({ getResourceList }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.dashboard_spinner')), ); @@ -247,15 +244,9 @@ const render = ( queries: Partial = {}, queryClient: QueryClient = createQueryClientMock(), ) => { - const allQueries: ServicesContextProps = { - ...queriesMock, - getResourceList, - getOrganizations, - ...queries, - }; return rtlRender( - + , diff --git a/frontend/resourceadm/pages/ResourcePage/ResourcePage.test.tsx b/frontend/resourceadm/pages/ResourcePage/ResourcePage.test.tsx index fdc90c999c7..6dce44b6c8c 100644 --- a/frontend/resourceadm/pages/ResourcePage/ResourcePage.test.tsx +++ b/frontend/resourceadm/pages/ResourcePage/ResourcePage.test.tsx @@ -6,13 +6,10 @@ import { act } from 'react-dom/test-utils'; import { textMock } from '../../../testing/mocks/i18nMock'; import { Resource } from 'app-shared/types/ResourceAdm'; import { createQueryClientMock } from 'app-shared/mocks/queryClientMock'; -import { queriesMock } from 'app-shared/mocks/queriesMock'; import { MemoryRouter } from 'react-router-dom'; import { ServicesContextProps, ServicesContextProvider } from 'app-shared/contexts/ServicesContext'; import { QueryClient } from '@tanstack/react-query'; - -// Mocking console.error due to Tanstack Query removing custom logger between V4 and v5 see issue: #11692 -const realConsole = console; +import { queriesMock } from 'app-shared/mocks/queriesMock'; const mockResource1: Resource = { identifier: 'r1', @@ -33,11 +30,6 @@ const mockResource2: Resource = { const mockSelectedContext: string = 'test'; -const getValidatePolicy = jest.fn().mockImplementation(() => Promise.resolve({})); -const getValidateResource = jest.fn().mockImplementation(() => Promise.resolve({})); -const getResource = jest.fn().mockImplementation(() => Promise.resolve({})); -const updateResource = jest.fn().mockImplementation(() => Promise.resolve({})); - jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useParams: () => ({ @@ -48,30 +40,23 @@ jest.mock('react-router-dom', () => ({ })); describe('ResourcePage', () => { - beforeEach(() => { - global.console = { - ...console, - error: jest.fn(), - }; - }); afterEach(() => { - global.console = realConsole; jest.clearAllMocks(); }); it('fetches validate policy on mount', () => { render(); - expect(getValidatePolicy).toHaveBeenCalledTimes(1); + expect(queriesMock.getValidatePolicy).toHaveBeenCalledTimes(1); }); it('fetches validate resource on mount', () => { render(); - expect(getValidateResource).toHaveBeenCalledTimes(1); + expect(queriesMock.getValidateResource).toHaveBeenCalledTimes(1); }); it('fetches resource on mount', () => { render(); - expect(getResource).toHaveBeenCalledTimes(1); + expect(queriesMock.getResource).toHaveBeenCalledTimes(1); }); it('displays left navigation bar on mount', () => { @@ -100,9 +85,11 @@ describe('ResourcePage', () => { }); it('displays migrate tab in left navigation bar when resource reference is present in resource', async () => { - getResource.mockImplementation(() => Promise.resolve(mockResource1)); + const getResource = jest + .fn() + .mockImplementation(() => Promise.resolve(mockResource1)); - render(); + render({ getResource }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.about_resource_spinner')), ); @@ -113,9 +100,11 @@ describe('ResourcePage', () => { }); it('does not display migrate tab in left navigation bar when resource reference is not in resource', async () => { - getResource.mockImplementation(() => Promise.resolve(mockResource2)); + const getResource = jest + .fn() + .mockImplementation(() => Promise.resolve(mockResource2)); - render(); + render({ getResource }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.about_resource_spinner')), ); @@ -127,9 +116,12 @@ describe('ResourcePage', () => { it('opens navigation modal when resource has errors', async () => { const user = userEvent.setup(); - getResource.mockImplementation(() => Promise.resolve(mockResource2)); + const getResource = jest + .fn() + .mockImplementation(() => Promise.resolve(mockResource2)); + const getValidateResource = jest.fn().mockImplementation(() => Promise.reject(null)); - render(); + render({ getResource, getValidateResource }); await waitForElementToBeRemoved(() => screen.queryByTitle(textMock('resourceadm.about_resource_spinner')), ); @@ -159,17 +151,9 @@ const render = ( queries: Partial = {}, queryClient: QueryClient = createQueryClientMock(), ) => { - const allQueries: ServicesContextProps = { - ...queriesMock, - getValidatePolicy, - getValidateResource, - getResource, - updateResource, - ...queries, - }; return rtlRender( - + ,