From b52715bc2e4122a229b5fb4faa450354bbf124ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Tue, 8 Oct 2024 22:49:42 +0200 Subject: [PATCH] implement identity too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- ...dentityAuthInjectorFetchMiddleware.test.ts | 17 +-- .../core-app-api/src/app/AppRouter.test.tsx | 11 +- .../components/AutoLogout/Autologout.test.tsx | 14 +- .../src/createPublicSignInApp.test.tsx | 7 +- packages/test-utils/report.api.md | 37 +++++- .../src/testUtils/apis/mockApis.test.tsx | 124 +++++++++++++++++- .../test-utils/src/testUtils/apis/mockApis.ts | 48 ++++++- .../CookieAuthRedirect.test.tsx | 8 +- .../UserListPicker/UserListPicker.test.tsx | 19 +-- .../useOwnedEntitiesCount.test.tsx | 45 +++---- .../src/hooks/useEntityListProvider.test.tsx | 24 ++-- .../src/hooks/useEntityOwnership.test.tsx | 21 +-- .../CatalogPage/DefaultCatalogPage.test.tsx | 23 +--- plugins/home/src/api/VisitsStorageApi.test.ts | 15 +-- .../home/src/api/VisitsWebStorageApi.test.ts | 14 +- .../MyGroupsSidebarItem.test.tsx | 63 ++++----- .../ListTasksPage/ListTaskPage.test.tsx | 13 +- .../columns/OwnerEntityColumn.test.tsx | 14 +- .../MyGroupsPicker/MyGroupsPicker.test.tsx | 31 ++--- plugins/signals/src/api/SignalsClient.test.ts | 11 +- plugins/techdocs/src/client.test.ts | 5 +- .../StorageApi/UserSettingsStorage.test.ts | 22 ++-- .../DefaultSettingsPage.test.tsx | 13 +- .../General/UserSettingsIdentityCard.test.tsx | 26 ++-- .../General/UserSettingsMenu.test.tsx | 7 +- .../General/UserSettingsProfileCard.test.tsx | 33 ++--- .../SettingsPage/SettingsPage.test.tsx | 19 +-- 27 files changed, 396 insertions(+), 288 deletions(-) diff --git a/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.test.ts b/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.test.ts index 1e29f2548a..f1daced2a1 100644 --- a/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.test.ts +++ b/packages/core-app-api/src/apis/implementations/FetchApi/IdentityAuthInjectorFetchMiddleware.test.ts @@ -15,8 +15,8 @@ */ import { ConfigReader } from '@backstage/config'; -import { IdentityApi } from '@backstage/core-plugin-api'; import { IdentityAuthInjectorFetchMiddleware } from './IdentityAuthInjectorFetchMiddleware'; +import { mockApis } from '@backstage/test-utils'; describe('IdentityAuthInjectorFetchMiddleware', () => { it('creates using defaults', async () => { @@ -58,10 +58,7 @@ describe('IdentityAuthInjectorFetchMiddleware', () => { }); it('injects the header only when a token is available', async () => { - const tokenFunction = jest.fn(); - const identityApi = { - getCredentials: tokenFunction, - } as unknown as IdentityApi; + const identityApi = mockApis.identity(); const middleware = new IdentityAuthInjectorFetchMiddleware( identityApi, @@ -73,27 +70,25 @@ describe('IdentityAuthInjectorFetchMiddleware', () => { const outer = middleware.apply(inner); // No token available - tokenFunction.mockResolvedValueOnce({ token: undefined }); + identityApi.getCredentials.mockResolvedValueOnce({ token: undefined }); await outer(new Request('https://example.com')); expect([...inner.mock.calls[0][0].headers.entries()]).toEqual([]); // Supply a token, header gets added - tokenFunction.mockResolvedValueOnce({ token: 'token' }); + identityApi.getCredentials.mockResolvedValueOnce({ token: 'token' }); await outer(new Request('https://example.com')); expect([...inner.mock.calls[1][0].headers.entries()]).toEqual([ ['authorization', 'Bearer token'], ]); // Token no longer available - tokenFunction.mockResolvedValueOnce({ token: undefined }); + identityApi.getCredentials.mockResolvedValueOnce({ token: undefined }); await outer(new Request('https://example.com')); expect([...inner.mock.calls[2][0].headers.entries()]).toEqual([]); }); it('does not overwrite an existing header with the same name', async () => { - const identityApi = { - getCredentials: () => ({ token: 'token' }), - } as unknown as IdentityApi; + const identityApi = mockApis.identity({ token: 'token' }); const middleware = new IdentityAuthInjectorFetchMiddleware( identityApi, diff --git a/packages/core-app-api/src/app/AppRouter.test.tsx b/packages/core-app-api/src/app/AppRouter.test.tsx index 9f3ab2ad89..b595f91794 100644 --- a/packages/core-app-api/src/app/AppRouter.test.tsx +++ b/packages/core-app-api/src/app/AppRouter.test.tsx @@ -18,7 +18,6 @@ import React from 'react'; import { AppComponents, configApiRef, - IdentityApi, identityApiRef, SignInPageProps, useApi, @@ -30,7 +29,7 @@ import { render, screen } from '@testing-library/react'; import { AppRouter } from './AppRouter'; import useAsync from 'react-use/esm/useAsync'; import { AppContextProvider } from './AppContext'; -import { TestApiProvider } from '@backstage/test-utils'; +import { mockApis, TestApiProvider } from '@backstage/test-utils'; import { ConfigReader } from '@backstage/config'; function UserRefDisplay() { @@ -78,13 +77,7 @@ describe('AppRouter', () => { const appIdentityProxy = new AppIdentityProxy(); const SignInPage = (props: SignInPageProps) => { - props.onSignInSuccess({ - getBackstageIdentity: async () => ({ - type: 'user', - userEntityRef: 'user:default/test', - ownershipEntityRefs: ['user:default/test'], - }), - } as IdentityApi); + props.onSignInSuccess(mockApis.identity()); return null; }; diff --git a/packages/core-components/src/components/AutoLogout/Autologout.test.tsx b/packages/core-components/src/components/AutoLogout/Autologout.test.tsx index 164ad716d2..5a555cd215 100644 --- a/packages/core-components/src/components/AutoLogout/Autologout.test.tsx +++ b/packages/core-components/src/components/AutoLogout/Autologout.test.tsx @@ -13,23 +13,23 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { createMocks } from 'react-idle-timer'; // eslint-disable-next-line no-restricted-imports import { MessageChannel } from 'worker_threads'; import { ApiProvider } from '@backstage/core-app-api'; import { identityApiRef } from '@backstage/core-plugin-api'; -import { TestApiRegistry, renderInTestApp } from '@backstage/test-utils'; +import { + TestApiRegistry, + renderInTestApp, + mockApis, +} from '@backstage/test-utils'; import React from 'react'; import { AutoLogout } from './AutoLogout'; import { cleanup } from '@testing-library/react'; -// Mock the signOut function of identityApiRef -const mockSignOut = jest.fn(); -const mockIdentityApi = { - signOut: mockSignOut, - getCredentials: jest.fn().mockReturnValue({ token: 'xxx' }), -}; +const mockIdentityApi = mockApis.identity({ token: 'xxx' }); const apis = TestApiRegistry.from([identityApiRef, mockIdentityApi]); describe('AutoLogout', () => { diff --git a/packages/frontend-defaults/src/createPublicSignInApp.test.tsx b/packages/frontend-defaults/src/createPublicSignInApp.test.tsx index ce0f8c4b2a..560abc602f 100644 --- a/packages/frontend-defaults/src/createPublicSignInApp.test.tsx +++ b/packages/frontend-defaults/src/createPublicSignInApp.test.tsx @@ -15,7 +15,6 @@ */ import { - IdentityApi, SignInPageBlueprint, createFrontendModule, } from '@backstage/frontend-plugin-api'; @@ -70,9 +69,9 @@ describe('createPublicSignInApp', () => { async () => ({ onSignInSuccess }) => { useEffect(() => { - onSignInSuccess({ - getCredentials: async () => ({ token: 'mock-token' }), - } as IdentityApi); + onSignInSuccess( + mockApis.identity({ token: 'mock-token' }), + ); }, [onSignInSuccess]); return
; }, diff --git a/packages/test-utils/report.api.md b/packages/test-utils/report.api.md index 6e614321b6..f570aacde4 100644 --- a/packages/test-utils/report.api.md +++ b/packages/test-utils/report.api.md @@ -100,7 +100,9 @@ export namespace mockApis { const // (undocumented) factory: () => ApiFactory; const // (undocumented) - mock: () => jest.Mocked; + mock: ( + partialImpl?: Partial | undefined, + ) => ApiMock; } export function config(options?: { data?: JsonObject }): ConfigApi; export namespace config { @@ -113,6 +115,35 @@ export namespace mockApis { ) => ApiFactory; const mock: (partialImpl?: Partial | undefined) => ApiMock; } + // (undocumented) + export function identity(options?: { + userEntityRef?: string; + ownershipEntityRefs?: string[]; + token?: string; + email?: string; + displayName?: string; + picture?: string; + }): jest.Mocked; + // (undocumented) + export namespace identity { + const // (undocumented) + factory: ( + options?: + | { + userEntityRef?: string | undefined; + ownershipEntityRefs?: string[] | undefined; + token?: string | undefined; + email?: string | undefined; + displayName?: string | undefined; + picture?: string | undefined; + } + | undefined, + ) => ApiFactory; + const // (undocumented) + mock: ( + partialImpl?: Partial | undefined, + ) => ApiMock; + } } // @public @deprecated @@ -331,4 +362,8 @@ export function wrapInTestApp( // src/testUtils/apis/mockApis.d.ts:45:5 - (ae-undocumented) Missing documentation for "analytics". // src/testUtils/apis/mockApis.d.ts:46:15 - (ae-undocumented) Missing documentation for "factory". // src/testUtils/apis/mockApis.d.ts:47:15 - (ae-undocumented) Missing documentation for "mock". +// src/testUtils/apis/mockApis.d.ts:98:5 - (ae-undocumented) Missing documentation for "identity". +// src/testUtils/apis/mockApis.d.ts:106:5 - (ae-undocumented) Missing documentation for "identity". +// src/testUtils/apis/mockApis.d.ts:107:15 - (ae-undocumented) Missing documentation for "factory". +// src/testUtils/apis/mockApis.d.ts:115:15 - (ae-undocumented) Missing documentation for "mock". ``` diff --git a/packages/test-utils/src/testUtils/apis/mockApis.test.tsx b/packages/test-utils/src/testUtils/apis/mockApis.test.tsx index ebbdb41884..f9286897b4 100644 --- a/packages/test-utils/src/testUtils/apis/mockApis.test.tsx +++ b/packages/test-utils/src/testUtils/apis/mockApis.test.tsx @@ -17,13 +17,49 @@ import { mockApis } from './mockApis'; describe('mockApis', () => { + describe('analytics', () => { + it('can create an instance and make assertions on it', () => { + const analytics = mockApis.analytics(); + expect( + analytics.captureEvent({ + action: 'a', + subject: 'b', + context: { pluginId: 'c', extension: 'd', routeRef: 'e' }, + }), + ).toBeUndefined(); + expect(analytics.captureEvent).toHaveBeenCalledTimes(1); + }); + + it('can create a mock and make assertions on it', async () => { + expect.assertions(3); + const analytics = mockApis.analytics.mock({ + captureEvent: event => { + expect(event).toEqual({ + action: 'a', + subject: 'b', + context: { pluginId: 'c', extension: 'd', routeRef: 'e' }, + }); + }, + }); + expect( + analytics.captureEvent({ + action: 'a', + subject: 'b', + context: { pluginId: 'c', extension: 'd', routeRef: 'e' }, + }), + ).toBeUndefined(); + expect(analytics.captureEvent).toHaveBeenCalledTimes(1); + }); + }); + describe('config', () => { const data = { backend: { baseUrl: 'http://test.com' } }; it('can create an instance', () => { const empty = mockApis.config(); - const notEmpty = mockApis.config({ data }); expect(empty.getOptional('backend.baseUrl')).toBeUndefined(); + + const notEmpty = mockApis.config({ data }); expect(notEmpty.getOptional('backend.baseUrl')).toEqual( 'http://test.com', ); @@ -35,4 +71,90 @@ describe('mockApis', () => { expect(mock.getString).toHaveBeenCalledTimes(1); }); }); + + describe('identity', () => { + it('can create an instance and make assertions on it', async () => { + const empty = mockApis.identity(); + await expect(empty.getBackstageIdentity()).resolves.toEqual({ + type: 'user', + userEntityRef: 'user:default/test', + ownershipEntityRefs: ['user:default/test'], + }); + await expect(empty.getCredentials()).resolves.toEqual({}); + await expect(empty.getProfileInfo()).resolves.toEqual({}); + await expect(empty.signOut()).resolves.toBeUndefined(); + expect(empty.getBackstageIdentity).toHaveBeenCalledTimes(1); + expect(empty.getCredentials).toHaveBeenCalledTimes(1); + expect(empty.getProfileInfo).toHaveBeenCalledTimes(1); + expect(empty.signOut).toHaveBeenCalledTimes(1); + + const notEmpty = mockApis.identity({ + userEntityRef: 'a', + ownershipEntityRefs: ['b'], + token: 'c', + email: 'd', + displayName: 'e', + picture: 'f', + }); + await expect(notEmpty.getBackstageIdentity()).resolves.toEqual({ + type: 'user', + userEntityRef: 'a', + ownershipEntityRefs: ['b'], + }); + await expect(notEmpty.getCredentials()).resolves.toEqual({ token: 'c' }); + await expect(notEmpty.getProfileInfo()).resolves.toEqual({ + email: 'd', + displayName: 'e', + picture: 'f', + }); + await expect(notEmpty.signOut()).resolves.toBeUndefined(); + expect(notEmpty.getBackstageIdentity).toHaveBeenCalledTimes(1); + expect(notEmpty.getCredentials).toHaveBeenCalledTimes(1); + expect(notEmpty.getProfileInfo).toHaveBeenCalledTimes(1); + expect(notEmpty.signOut).toHaveBeenCalledTimes(1); + }); + + it('can create a mock and make assertions on it', async () => { + const empty = mockApis.identity.mock(); + expect(empty.getBackstageIdentity()).toBeUndefined(); + expect(empty.getCredentials()).toBeUndefined(); + expect(empty.getProfileInfo()).toBeUndefined(); + expect(empty.signOut()).toBeUndefined(); + expect(empty.getBackstageIdentity).toHaveBeenCalledTimes(1); + expect(empty.getCredentials).toHaveBeenCalledTimes(1); + expect(empty.getProfileInfo).toHaveBeenCalledTimes(1); + expect(empty.signOut).toHaveBeenCalledTimes(1); + + const notEmpty = mockApis.identity.mock({ + getBackstageIdentity: async () => ({ + type: 'user', + userEntityRef: 'a', + ownershipEntityRefs: ['b'], + }), + getCredentials: async () => ({ token: 'c' }), + getProfileInfo: async () => ({ + email: 'd', + displayName: 'e', + picture: 'f', + }), + signOut: async () => undefined, + }); + await expect(notEmpty.getBackstageIdentity()).resolves.toEqual({ + type: 'user', + userEntityRef: 'a', + ownershipEntityRefs: ['b'], + }); + await expect(notEmpty.getCredentials()).resolves.toEqual({ token: 'c' }); + await expect(notEmpty.getProfileInfo()).resolves.toEqual({ + email: 'd', + displayName: 'e', + picture: 'f', + }); + await expect(notEmpty.signOut()).resolves.toBeUndefined(); + expect(notEmpty.getBackstageIdentity).toHaveBeenCalledTimes(1); + expect(notEmpty.getCredentials).toHaveBeenCalledTimes(1); + expect(notEmpty.getProfileInfo).toHaveBeenCalledTimes(1); + expect(notEmpty.signOut).toHaveBeenCalledTimes(1); + }); + }); }); diff --git a/packages/test-utils/src/testUtils/apis/mockApis.ts b/packages/test-utils/src/testUtils/apis/mockApis.ts index f0bd519eac..e47ee4ec3e 100644 --- a/packages/test-utils/src/testUtils/apis/mockApis.ts +++ b/packages/test-utils/src/testUtils/apis/mockApis.ts @@ -20,9 +20,11 @@ import { ApiFactory, ApiRef, ConfigApi, + IdentityApi, analyticsApiRef, configApiRef, createApiFactory, + identityApiRef, } from '@backstage/core-plugin-api'; import { JsonObject } from '@backstage/types'; import { ApiMock } from './ApiMock'; @@ -113,7 +115,7 @@ export namespace mockApis { } export namespace analytics { export const factory = simpleFactory(analyticsApiRef, analytics); - export const mock = analyticsMockSkeleton; + export const mock = simpleMock(analyticsApiRef, analyticsMockSkeleton); } /** @@ -180,4 +182,48 @@ export namespace mockApis { getOptionalStringArray: jest.fn(), })); } + + const identityMockSkeleton = (): jest.Mocked => ({ + getBackstageIdentity: jest.fn(), + getCredentials: jest.fn(), + getProfileInfo: jest.fn(), + signOut: jest.fn(), + }); + export function identity(options?: { + userEntityRef?: string; + ownershipEntityRefs?: string[]; + token?: string; + email?: string; + displayName?: string; + picture?: string; + }) { + const { + userEntityRef = 'user:default/test', + ownershipEntityRefs = ['user:default/test'], + token, + email, + displayName, + picture, + } = options ?? {}; + return simpleInstance( + identityApiRef, + { + async getBackstageIdentity() { + return { type: 'user', ownershipEntityRefs, userEntityRef }; + }, + async getCredentials() { + return { token }; + }, + async getProfileInfo() { + return { email, displayName, picture }; + }, + async signOut() {}, + }, + identityMockSkeleton, + ); + } + export namespace identity { + export const factory = simpleFactory(identityApiRef, identity); + export const mock = simpleMock(identityApiRef, identityMockSkeleton); + } } diff --git a/plugins/auth-react/src/components/CookieAuthRedirect/CookieAuthRedirect.test.tsx b/plugins/auth-react/src/components/CookieAuthRedirect/CookieAuthRedirect.test.tsx index 6c0141a9d1..18c6408407 100644 --- a/plugins/auth-react/src/components/CookieAuthRedirect/CookieAuthRedirect.test.tsx +++ b/plugins/auth-react/src/components/CookieAuthRedirect/CookieAuthRedirect.test.tsx @@ -16,12 +16,16 @@ import React from 'react'; import { screen, waitFor } from '@testing-library/react'; -import { TestApiProvider, renderInTestApp } from '@backstage/test-utils'; +import { + TestApiProvider, + renderInTestApp, + mockApis, +} from '@backstage/test-utils'; import { identityApiRef } from '@backstage/core-plugin-api'; import { CookieAuthRedirect } from './CookieAuthRedirect'; describe('CookieAuthRedirect', () => { - const identityApiMock = { getCredentials: jest.fn() }; + const identityApiMock = mockApis.identity.mock(); beforeEach(() => { jest.clearAllMocks(); diff --git a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx index 888940f30b..3f7c2034a0 100644 --- a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx +++ b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx @@ -36,13 +36,12 @@ import { catalogApiRef } from '../../api'; import { MockStorageApi, TestApiRegistry, + mockApis, renderInTestApp, } from '@backstage/test-utils'; import { ApiProvider } from '@backstage/core-app-api'; import { - ConfigApi, configApiRef, - IdentityApi, identityApiRef, storageApiRef, } from '@backstage/core-plugin-api'; @@ -61,15 +60,18 @@ const mockUser: UserEntity = { }, }; -const mockConfigApi = { - getOptionalString: () => 'Test Company', -} as Partial; +const ownershipEntityRefs = ['user:default/testuser']; + +const mockConfigApi = mockApis.config({ + data: { organization: { name: 'Test Company' } }, +}); const mockCatalogApi = catalogApiMock.mock(); -const mockIdentityApi = { - getBackstageIdentity: jest.fn(), -} as Partial>; +const mockIdentityApi = mockApis.identity({ + userEntityRef: ownershipEntityRefs[0], + ownershipEntityRefs, +}); const mockStarredEntitiesApi = new MockStarredEntitiesApi(); @@ -81,7 +83,6 @@ const apis = TestApiRegistry.from( [starredEntitiesApiRef, mockStarredEntitiesApi], ); -const ownershipEntityRefs = ['user:default/testuser']; describe('', () => { const mockQueryEntitiesImplementation: CatalogApi['queryEntities'] = async request => { diff --git a/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.test.tsx b/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.test.tsx index 4c620060fe..99b746c80c 100644 --- a/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.test.tsx +++ b/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.test.tsx @@ -23,11 +23,7 @@ import { useEntityList, } from '../../hooks'; import { catalogApiRef } from '../../api'; -import { - ApiRef, - IdentityApi, - identityApiRef, -} from '@backstage/core-plugin-api'; +import { ApiRef, identityApiRef } from '@backstage/core-plugin-api'; import { MemoryRouter } from 'react-router-dom'; import { useOwnedEntitiesCount } from './useOwnedEntitiesCount'; import { @@ -36,12 +32,13 @@ import { EntityUserFilter, } from '../../filters'; import { useMountEffect } from '@react-hookz/web'; +import { mockApis } from '@backstage/test-utils'; const mockCatalogApi = catalogApiMock.mock(); - -const mockGetBackstageIdentity: jest.MockedFn< - IdentityApi['getBackstageIdentity'] -> = jest.fn(); +const mockIdentityApi = mockApis.identity({ + ownershipEntityRefs: ['user:default/spiderman', 'user:group/a-group'], + userEntityRef: 'user:default/spiderman', +}); jest.mock('@backstage/core-plugin-api', () => { const actual = jest.requireActual('@backstage/core-plugin-api'); @@ -50,13 +47,9 @@ jest.mock('@backstage/core-plugin-api', () => { useApi: (ref: ApiRef) => { if (ref === catalogApiRef) { return mockCatalogApi; + } else if (ref === identityApiRef) { + return mockIdentityApi; } - if (ref === identityApiRef) { - return { - getBackstageIdentity: mockGetBackstageIdentity, - }; - } - return actual.useApi(ref); }, }; @@ -65,12 +58,6 @@ jest.mock('@backstage/core-plugin-api', () => { describe('useOwnedEntitiesCount', () => { beforeEach(() => { jest.clearAllMocks(); - - mockGetBackstageIdentity.mockResolvedValue({ - ownershipEntityRefs: ['user:default/spiderman', 'user:group/a-group'], - userEntityRef: 'user:default/spiderman', - type: 'user', - }); }); it(`shouldn't invoke queryEntities when filters are loading`, async () => { @@ -84,7 +71,9 @@ describe('useOwnedEntitiesCount', () => { wrapper: createWrapperWithInitialFilters({}), }); - await waitFor(() => expect(mockGetBackstageIdentity).toHaveBeenCalled()); + await waitFor(() => + expect(mockIdentityApi.getBackstageIdentity).toHaveBeenCalled(), + ); await expect( waitFor(() => expect(mockCatalogApi.queryEntities).toHaveBeenCalled()), @@ -114,7 +103,9 @@ describe('useOwnedEntitiesCount', () => { }), }); - await waitFor(() => expect(mockGetBackstageIdentity).toHaveBeenCalled()); + await waitFor(() => + expect(mockIdentityApi.getBackstageIdentity).toHaveBeenCalled(), + ); await waitFor(() => expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ @@ -151,7 +142,9 @@ describe('useOwnedEntitiesCount', () => { }), }); - await waitFor(() => expect(mockGetBackstageIdentity).toHaveBeenCalled()); + await waitFor(() => + expect(mockIdentityApi.getBackstageIdentity).toHaveBeenCalled(), + ); await expect( waitFor(() => expect(mockCatalogApi.queryEntities).toHaveBeenCalled()), @@ -185,7 +178,9 @@ describe('useOwnedEntitiesCount', () => { }), }); - await waitFor(() => expect(mockGetBackstageIdentity).toHaveBeenCalled()); + await waitFor(() => + expect(mockIdentityApi.getBackstageIdentity).toHaveBeenCalled(), + ); await waitFor(() => expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx index 0bda2d19ec..067ca33fa3 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx @@ -18,14 +18,16 @@ import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; import { Entity } from '@backstage/catalog-model'; import { alertApiRef, - ConfigApi, configApiRef, errorApiRef, - IdentityApi, identityApiRef, storageApiRef, } from '@backstage/core-plugin-api'; -import { MockStorageApi, TestApiProvider } from '@backstage/test-utils'; +import { + MockStorageApi, + TestApiProvider, + mockApis, +} from '@backstage/test-utils'; import { act, renderHook, waitFor } from '@testing-library/react'; import qs from 'qs'; import React, { PropsWithChildren } from 'react'; @@ -67,20 +69,14 @@ const entities: Entity[] = [ }, ]; -const mockConfigApi = { - getOptionalString: () => '', -} as Partial; +const mockConfigApi = mockApis.config(); const ownershipEntityRefs = ['user:default/guest']; -const mockIdentityApi: Partial = { - getBackstageIdentity: async () => ({ - type: 'user', - userEntityRef: 'user:default/guest', - ownershipEntityRefs, - }), - getCredentials: async () => ({ token: undefined }), -}; +const mockIdentityApi = mockApis.identity({ + userEntityRef: 'user:default/guest', + ownershipEntityRefs, +}); const mockCatalogApi = catalogApiMock.mock({ getEntities: jest.fn().mockResolvedValue({ items: entities }), queryEntities: jest.fn().mockResolvedValue({ diff --git a/plugins/catalog-react/src/hooks/useEntityOwnership.test.tsx b/plugins/catalog-react/src/hooks/useEntityOwnership.test.tsx index 5516a249ea..684580792d 100644 --- a/plugins/catalog-react/src/hooks/useEntityOwnership.test.tsx +++ b/plugins/catalog-react/src/hooks/useEntityOwnership.test.tsx @@ -15,20 +15,17 @@ */ import { ComponentEntity, RELATION_OWNED_BY } from '@backstage/catalog-model'; -import { IdentityApi, identityApiRef } from '@backstage/core-plugin-api'; -import { TestApiProvider } from '@backstage/test-utils'; +import { identityApiRef } from '@backstage/core-plugin-api'; +import { TestApiProvider, mockApis } from '@backstage/test-utils'; import { renderHook, waitFor } from '@testing-library/react'; import React from 'react'; import { useEntityOwnership } from './useEntityOwnership'; describe('useEntityOwnership', () => { - type MockIdentityApi = jest.Mocked>; - - const mockIdentityApi: MockIdentityApi = { - getBackstageIdentity: jest.fn(), - }; - - const identityApi = mockIdentityApi as unknown as IdentityApi; + const identityApi = mockApis.identity({ + userEntityRef: 'user:default/user1', + ownershipEntityRefs: ['user:default/user1', 'group:default/group1'], + }); const Wrapper = (props: { children?: React.ReactNode }) => ( @@ -64,12 +61,6 @@ describe('useEntityOwnership', () => { describe('useEntityOwnership', () => { it('matches ownership via ownership entity refs', async () => { - mockIdentityApi.getBackstageIdentity.mockResolvedValue({ - type: 'user', - userEntityRef: 'user:default/user1', - ownershipEntityRefs: ['user:default/user1', 'group:default/group1'], - }); - const { result } = renderHook(() => useEntityOwnership(), { wrapper: Wrapper, }); diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx index f5a16e3af3..a5ced77749 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx @@ -17,12 +17,7 @@ import { QueryEntitiesInitialRequest } from '@backstage/catalog-client'; import { RELATION_OWNED_BY } from '@backstage/catalog-model'; import { TableColumn, TableProps } from '@backstage/core-components'; -import { - IdentityApi, - identityApiRef, - ProfileInfo, - storageApiRef, -} from '@backstage/core-plugin-api'; +import { identityApiRef, storageApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef, entityRouteRef, @@ -34,6 +29,7 @@ import { MockPermissionApi, MockStorageApi, TestApiProvider, + mockApis, renderInTestApp, } from '@backstage/test-utils'; import DashboardIcon from '@material-ui/icons/Dashboard'; @@ -166,18 +162,11 @@ describe('DefaultCatalogPage', () => { }), }); - const testProfile: Partial = { + const identityApi = mockApis.identity({ + userEntityRef: 'user:default/guest', + ownershipEntityRefs: ['user:default/guest', 'group:default/tools'], displayName: 'Display Name', - }; - const identityApi: Partial = { - getBackstageIdentity: async () => ({ - type: 'user', - userEntityRef: 'user:default/guest', - ownershipEntityRefs: ['user:default/guest', 'group:default/tools'], - }), - getCredentials: async () => ({ token: undefined }), - getProfileInfo: async () => testProfile, - }; + }); const storageApi = MockStorageApi.create(); const renderWrapped = (children: React.ReactNode) => diff --git a/plugins/home/src/api/VisitsStorageApi.test.ts b/plugins/home/src/api/VisitsStorageApi.test.ts index 5e8bc2dbfc..206cf42689 100644 --- a/plugins/home/src/api/VisitsStorageApi.test.ts +++ b/plugins/home/src/api/VisitsStorageApi.test.ts @@ -14,9 +14,8 @@ * limitations under the License. */ -import { BackstageUserIdentity, IdentityApi } from '@backstage/core-plugin-api'; import { VisitsStorageApi } from './VisitsStorageApi'; -import { MockStorageApi } from '@backstage/test-utils'; +import { MockStorageApi, mockApis } from '@backstage/test-utils'; import { Visit, VisitsApi } from './VisitsApi'; describe('VisitsStorageApi.create', () => { @@ -26,13 +25,9 @@ describe('VisitsStorageApi.create', () => { () => Math.floor(Math.random() * 16).toString(16), // 0x0 to 0xf ) as `${string}-${string}-${string}-${string}-${string}`; - const mockIdentityApi: IdentityApi = { - signOut: jest.fn(), - getProfileInfo: jest.fn(), - getBackstageIdentity: async () => - ({ userEntityRef: 'user:default/guest' } as BackstageUserIdentity), - getCredentials: jest.fn(), - }; + const mockIdentityApi = mockApis.identity({ + userEntityRef: 'user:default/guest', + }); beforeEach(() => { window.crypto.randomUUID = mockRandomUUID; @@ -40,7 +35,7 @@ describe('VisitsStorageApi.create', () => { }); afterEach(() => { - jest.resetAllMocks(); + jest.clearAllMocks(); jest.useRealTimers(); window.localStorage.clear(); }); diff --git a/plugins/home/src/api/VisitsWebStorageApi.test.ts b/plugins/home/src/api/VisitsWebStorageApi.test.ts index 1cf1fa3129..8d8669e99e 100644 --- a/plugins/home/src/api/VisitsWebStorageApi.test.ts +++ b/plugins/home/src/api/VisitsWebStorageApi.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { BackstageUserIdentity, IdentityApi } from '@backstage/core-plugin-api'; +import { mockApis } from '@backstage/test-utils'; import { VisitsWebStorageApi } from './VisitsWebStorageApi'; describe('VisitsWebStorageApi.create()', () => { @@ -24,13 +24,9 @@ describe('VisitsWebStorageApi.create()', () => { () => Math.floor(Math.random() * 16).toString(16), // 0x0 to 0xf ) as `${string}-${string}-${string}-${string}-${string}`; - const mockIdentityApi: IdentityApi = { - signOut: jest.fn(), - getProfileInfo: jest.fn(), - getBackstageIdentity: async () => - ({ userEntityRef: 'user:default/guest' } as BackstageUserIdentity), - getCredentials: jest.fn(), - }; + const mockIdentityApi = mockApis.identity({ + userEntityRef: 'user:default/guest', + }); const mockErrorApi = { post: jest.fn(), error$: jest.fn() }; @@ -40,7 +36,7 @@ describe('VisitsWebStorageApi.create()', () => { afterEach(() => { window.localStorage.clear(); - jest.resetAllMocks(); + jest.clearAllMocks(); }); it('instantiates with only identitiyApi', () => { diff --git a/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx index 9eb9b3f70f..de46be98be 100644 --- a/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx +++ b/plugins/org/src/components/MyGroupsSidebarItem/MyGroupsSidebarItem.test.tsx @@ -14,11 +14,15 @@ * limitations under the License. */ -import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { + renderInTestApp, + TestApiProvider, + mockApis, +} from '@backstage/test-utils'; import React from 'react'; import { MyGroupsSidebarItem } from './MyGroupsSidebarItem'; import GroupIcon from '@material-ui/icons/People'; -import { IdentityApi, identityApiRef } from '@backstage/core-plugin-api'; +import { identityApiRef } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; @@ -26,13 +30,10 @@ import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; describe('MyGroupsSidebarItem Test', () => { describe('For guests or users with no groups', () => { it('MyGroupsSidebarItem should be empty', async () => { - const identityApi: Partial = { - getBackstageIdentity: async () => ({ - type: 'user', - userEntityRef: 'user:default/guest', - ownershipEntityRefs: ['user:default/guest'], - }), - }; + const identityApi = mockApis.identity({ + userEntityRef: 'user:default/guest', + ownershipEntityRefs: ['user:default/guest'], + }); const catalogApi = catalogApiMock(); const rendered = await renderInTestApp( { describe('For users that are members of a single group', () => { it('MyGroupsSidebarItem should display a single item that links to their group', async () => { - const identityApi: Partial = { - getBackstageIdentity: async () => ({ - type: 'user', - userEntityRef: 'user:default/nigel.manning', - ownershipEntityRefs: ['user:default/nigel.manning'], - }), - }; + const identityApi = mockApis.identity({ + userEntityRef: 'user:default/nigel.manning', + ownershipEntityRefs: ['user:default/nigel.manning'], + }); const catalogApi = catalogApiMock.mock({ getEntities: async () => ({ items: [ @@ -114,13 +112,10 @@ describe('MyGroupsSidebarItem Test', () => { describe('For users that are members of multiple groups', () => { it('MyGroupsSidebarItem should display a sub-menu with all their groups and a link to each group', async () => { - const identityApi: Partial = { - getBackstageIdentity: async () => ({ - type: 'user', - userEntityRef: 'user:default/nigel.manning', - ownershipEntityRefs: ['user:default/nigel.manning'], - }), - }; + const identityApi = mockApis.identity({ + userEntityRef: 'user:default/nigel.manning', + ownershipEntityRefs: ['user:default/nigel.manning'], + }); const catalogApi = catalogApiMock.mock({ getEntities: async () => ({ items: [ @@ -192,13 +187,10 @@ describe('MyGroupsSidebarItem Test', () => { describe('When an additional filter is not provided', () => { it('catalogApi.getEntities() should be called with the default filter', async () => { - const identityApi: Partial = { - getBackstageIdentity: async () => ({ - type: 'user', - userEntityRef: 'user:default/guest', - ownershipEntityRefs: ['user:default/guest'], - }), - }; + const identityApi = mockApis.identity({ + userEntityRef: 'user:default/guest', + ownershipEntityRefs: ['user:default/guest'], + }); const mockCatalogApi = catalogApiMock.mock(); await renderInTestApp( { describe('When an additional filter is provided', () => { it('catalogApi.getEntities() should be called with an additional filter item', async () => { - const identityApi: Partial = { - getBackstageIdentity: async () => ({ - type: 'user', - userEntityRef: 'user:default/guest', - ownershipEntityRefs: ['user:default/guest'], - }), - }; + const identityApi = mockApis.identity({ + userEntityRef: 'user:default/guest', + ownershipEntityRefs: ['user:default/guest'], + }); const mockCatalogApi = catalogApiMock.mock(); await renderInTestApp( ', () => { const catalogApi = catalogApiMock.mock(); - const identityApi = { - getBackstageIdentity: jest.fn(), - getProfileInfo: jest.fn(), - getCredentials: jest.fn(), - signOut: jest.fn(), - }; + const identityApi = mockApis.identity(); const scaffolderApiMock: jest.Mocked> = { scaffold: jest.fn(), diff --git a/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.test.tsx b/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.test.tsx index 35eb9ce0b7..bd5676ba31 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.test.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/columns/OwnerEntityColumn.test.tsx @@ -15,7 +15,11 @@ */ import { Entity } from '@backstage/catalog-model'; -import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { + renderInTestApp, + TestApiProvider, + mockApis, +} from '@backstage/test-utils'; import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; import React from 'react'; @@ -24,13 +28,7 @@ import { identityApiRef } from '@backstage/core-plugin-api'; describe('', () => { const catalogApi = catalogApiMock.mock(); - - const identityApi = { - getBackstageIdentity: jest.fn(), - getProfileInfo: jest.fn(), - getCredentials: jest.fn(), - signOut: jest.fn(), - }; + const identityApi = mockApis.identity(); it('should render the column with the user', async () => { const props = { diff --git a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx index d4a37d0752..57ce2d3023 100644 --- a/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/MyGroupsPicker/MyGroupsPicker.test.tsx @@ -18,7 +18,11 @@ import React from 'react'; import { waitFor } from '@testing-library/react'; import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; import { MyGroupsPicker } from './MyGroupsPicker'; -import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { + renderInTestApp, + TestApiProvider, + mockApis, +} from '@backstage/test-utils'; import { catalogApiRef, entityPresentationApiRef, @@ -26,7 +30,6 @@ import { import { Entity } from '@backstage/catalog-model'; import { ErrorApi, - IdentityApi, errorApiRef, identityApiRef, } from '@backstage/core-plugin-api'; @@ -34,25 +37,9 @@ import userEvent from '@testing-library/user-event'; import { ScaffolderRJSFFieldProps as FieldProps } from '@backstage/plugin-scaffolder-react'; import { DefaultEntityPresentationApi } from '@backstage/plugin-catalog'; -// Create a mock IdentityApi -const mockIdentityApi: IdentityApi = { - getProfileInfo: () => - Promise.resolve({ - displayName: 'Bob', - email: 'bob@example.com', - picture: 'https://example.com/picture.jpg', - }), - getBackstageIdentity: () => - Promise.resolve({ - id: 'Bob', - idToken: 'token', - type: 'user', - userEntityRef: 'user:default/bob', - ownershipEntityRefs: ['group:default/group1', 'group:default/group2'], - }), - getCredentials: () => Promise.resolve({ token: 'token' }), - signOut: () => Promise.resolve(), -}; +const mockIdentityApi = mockApis.identity({ + userEntityRef: 'user:default/bob', +}); describe('', () => { let entities: Entity[]; @@ -96,7 +83,7 @@ describe('', () => { }); afterEach(() => { - jest.resetAllMocks(); + jest.clearAllMocks(); }); it('should only return the groups a user is part of and not the groups a user is not part of', async () => { diff --git a/plugins/signals/src/api/SignalsClient.test.ts b/plugins/signals/src/api/SignalsClient.test.ts index 2e93cd69f1..057b1fb23f 100644 --- a/plugins/signals/src/api/SignalsClient.test.ts +++ b/plugins/signals/src/api/SignalsClient.test.ts @@ -14,16 +14,14 @@ * limitations under the License. */ -import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; +import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { mockApis } from '@backstage/test-utils'; import WS from 'jest-websocket-mock'; import { SignalClient } from './SignalClient'; describe('SignalsClient', () => { - const tokenFunction = jest.fn(); const baseUrlFunction = jest.fn(); - const identity = { - getCredentials: tokenFunction, - } as unknown as IdentityApi; + const identity = mockApis.identity({ token: '12345' }); const discoveryApi = { getBaseUrl: baseUrlFunction, } as unknown as DiscoveryApi; @@ -31,8 +29,7 @@ describe('SignalsClient', () => { let server: WS; beforeEach(async () => { - jest.resetAllMocks(); - tokenFunction.mockResolvedValue({ token: '12345' }); + jest.clearAllMocks(); baseUrlFunction.mockResolvedValue('http://localhost:1234'); server = new WS('ws://localhost:1234', { jsonProtocol: true }); }); diff --git a/plugins/techdocs/src/client.test.ts b/plugins/techdocs/src/client.test.ts index e2c68a7d73..8724e027f5 100644 --- a/plugins/techdocs/src/client.test.ts +++ b/plugins/techdocs/src/client.test.ts @@ -15,7 +15,6 @@ */ import { UrlPatternDiscovery } from '@backstage/core-app-api'; -import { IdentityApi } from '@backstage/core-plugin-api'; import { NotFoundError } from '@backstage/errors'; import { fetchEventSource } from '@microsoft/fetch-event-source'; import { mockApis, MockFetchApi } from '@backstage/test-utils'; @@ -36,9 +35,7 @@ describe('TechDocsStorageClient', () => { const mockBaseUrl = 'http://backstage:9191/api/techdocs'; const configApi = mockApis.config(); const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl); - const identityApi: jest.Mocked = { - getCredentials: jest.fn(), - } as unknown as jest.Mocked; + const identityApi = mockApis.identity(); const fetchApi = new MockFetchApi({ injectIdentityAuth: { identityApi } }); beforeEach(() => { diff --git a/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.test.ts b/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.test.ts index 6bb0035b83..e54ac36624 100644 --- a/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.test.ts +++ b/plugins/user-settings/src/apis/StorageApi/UserSettingsStorage.test.ts @@ -18,10 +18,13 @@ import { DiscoveryApi, ErrorApi, FetchApi, - IdentityApi, StorageApi, } from '@backstage/core-plugin-api'; -import { MockFetchApi, registerMswTestHooks } from '@backstage/test-utils'; +import { + MockFetchApi, + mockApis, + registerMswTestHooks, +} from '@backstage/test-utils'; import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { UserSettingsStorage } from './UserSettingsStorage'; @@ -35,13 +38,8 @@ describe('Persistent Storage API', () => { const mockDiscoveryApi = { getBaseUrl: async () => mockBaseUrl, }; - const mockIdentityApi: Partial = { - getCredentials: async () => ({ token: 'a-token' }), - }; - const mockIdentityApiFallback: Partial = { - // This API recreates the guest mode, where the WebStorage is used as fallback - getCredentials: async () => ({}), - }; + const mockIdentityApi = mockApis.identity({ token: 'a-token' }); + const mockIdentityApiFallback = mockApis.identity(); const createPersistentStorage = ( args?: Partial<{ @@ -55,7 +53,7 @@ describe('Persistent Storage API', () => { errorApi: mockErrorApi, fetchApi: new MockFetchApi(), discoveryApi: mockDiscoveryApi, - identityApi: mockIdentityApi as IdentityApi, + identityApi: mockIdentityApi, ...args, }); }; @@ -72,13 +70,13 @@ describe('Persistent Storage API', () => { errorApi: mockErrorApi, fetchApi: new MockFetchApi(), discoveryApi: mockDiscoveryApi, - identityApi: mockIdentityApiFallback as IdentityApi, + identityApi: mockIdentityApiFallback, ...args, }); }; afterEach(() => { - jest.resetAllMocks(); + jest.clearAllMocks(); }); it('should return undefined for values which are unset', async () => { diff --git a/plugins/user-settings/src/components/DefaultSettingsPage/DefaultSettingsPage.test.tsx b/plugins/user-settings/src/components/DefaultSettingsPage/DefaultSettingsPage.test.tsx index 14b7b7b48b..8a2e882aa8 100644 --- a/plugins/user-settings/src/components/DefaultSettingsPage/DefaultSettingsPage.test.tsx +++ b/plugins/user-settings/src/components/DefaultSettingsPage/DefaultSettingsPage.test.tsx @@ -20,16 +20,15 @@ import { DefaultSettingsPage } from './DefaultSettingsPage'; import { UserSettingsTab } from '../UserSettingsTab'; import { useOutlet } from 'react-router-dom'; import { SettingsLayout } from '../SettingsLayout'; -import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react'; +import { catalogApiRef } from '@backstage/plugin-catalog-react'; +import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useOutlet: jest.fn().mockReturnValue(undefined), })); -const catalogApiMock: jest.Mocked = { - getEntityByRef: jest.fn(), -} as any; +const catalogApi = catalogApiMock(); describe('', () => { beforeEach(() => { @@ -38,7 +37,7 @@ describe('', () => { it('should render the settings page with 3 tabs', async () => { const { container } = await renderInTestApp( - + , ); @@ -54,7 +53,7 @@ describe('', () => { ); const { container } = await renderInTestApp( - + , ); @@ -71,7 +70,7 @@ describe('', () => { ); const { container } = await renderInTestApp( - + , ); diff --git a/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx index b1a811d7fa..6fe0ee01d7 100644 --- a/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx @@ -14,32 +14,28 @@ * limitations under the License. */ -import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; +import { + renderInTestApp, + TestApiRegistry, + mockApis, +} from '@backstage/test-utils'; import { screen } from '@testing-library/react'; import React from 'react'; import { UserSettingsIdentityCard } from './UserSettingsIdentityCard'; import { ApiProvider } from '@backstage/core-app-api'; import { identityApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; +import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; const apiRegistry = TestApiRegistry.from( [ identityApiRef, - { - getProfileInfo: jest.fn(async () => ({})), - getBackstageIdentity: jest.fn(async () => ({ - type: 'user' as const, - userEntityRef: 'foo:bar/foobar', - ownershipEntityRefs: ['user:default/test-ownership'], - })), - }, - ], - [ - catalogApiRef, - { - getEntityByRef: jest.fn(), - }, + mockApis.identity({ + userEntityRef: 'foo:bar/foobar', + ownershipEntityRefs: ['user:default/test-ownership'], + }), ], + [catalogApiRef, catalogApiMock.mock()], ); describe('', () => { diff --git a/plugins/user-settings/src/components/General/UserSettingsMenu.test.tsx b/plugins/user-settings/src/components/General/UserSettingsMenu.test.tsx index c3b0aae4a7..0af33fac00 100644 --- a/plugins/user-settings/src/components/General/UserSettingsMenu.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsMenu.test.tsx @@ -18,6 +18,7 @@ import { MockErrorApi, TestApiProvider, renderInTestApp, + mockApis, } from '@backstage/test-utils'; import { errorApiRef, identityApiRef } from '@backstage/core-plugin-api'; import { fireEvent, waitFor, screen } from '@testing-library/react'; @@ -35,9 +36,9 @@ describe('', () => { }); it('handles errors that occur when signing out', async () => { - const failingIdentityApi = { - signOut: jest.fn().mockRejectedValue(new Error('Logout error')), - }; + const failingIdentityApi = mockApis.identity.mock({ + signOut: () => Promise.reject(new Error('Logout error')), + }); const mockErrorApi = new MockErrorApi({ collect: true }); await renderInTestApp( ({})), - getBackstageIdentity: jest.fn(async () => ({ - type: 'user' as const, - userEntityRef: 'foo:bar/foobar', - ownershipEntityRefs: ['user:default/test-ownership'], - })), - }, - ], + [identityApiRef, mockApis.identity()], [ catalogApiRef, - { - getEntityByRef: jest.fn(async () => { - return { + catalogApiMock({ + entities: [ + { apiVersion: 'backstage.io/v1beta1', kind: 'User', metadata: { - name: 'Guest', + name: 'test', annotations: {}, }, spec: { @@ -50,9 +45,9 @@ const apiRegistry = TestApiRegistry.from( picture: 'https://example.com/avatar.png', }, }, - }; - }), - }, + }, + ], + }), ], ); diff --git a/plugins/user-settings/src/components/SettingsPage/SettingsPage.test.tsx b/plugins/user-settings/src/components/SettingsPage/SettingsPage.test.tsx index acdb6c3f87..12431a44a2 100644 --- a/plugins/user-settings/src/components/SettingsPage/SettingsPage.test.tsx +++ b/plugins/user-settings/src/components/SettingsPage/SettingsPage.test.tsx @@ -22,20 +22,15 @@ import { useOutlet } from 'react-router-dom'; import { SettingsLayout } from '../SettingsLayout'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { - CatalogApi, - catalogApiRef, - entityRouteRef, -} from '@backstage/plugin-catalog-react'; +import { catalogApiRef, entityRouteRef } from '@backstage/plugin-catalog-react'; +import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; jest.mock('react-router-dom', () => ({ ...jest.requireActual('react-router-dom'), useOutlet: jest.fn().mockReturnValue(undefined), })); -const catalogApiMock: jest.Mocked = { - getEntityByRef: jest.fn(), -} as any; +const catalogApi = catalogApiMock(); describe('', () => { beforeEach(() => { @@ -44,7 +39,7 @@ describe('', () => { it('should render the default settings page with 3 tabs', async () => { const { container } = await renderInTestApp( - + , { @@ -64,7 +59,7 @@ describe('', () => { ); (useOutlet as jest.Mock).mockReturnValue(advancedTabRoute); const { container } = await renderInTestApp( - + , { @@ -85,7 +80,7 @@ describe('', () => { ); (useOutlet as jest.Mock).mockReturnValue(advancedTabRoute); const { container } = await renderInTestApp( - + , { @@ -115,7 +110,7 @@ describe('', () => { ); (useOutlet as jest.Mock).mockReturnValue(customLayout); const { container } = await renderInTestApp( - + , {