From 3afafea87b7143944b2279768291297c325ee075 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 9 Oct 2024 16:33:39 +0200 Subject: [PATCH] implement discovery too MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- ...ginProtocolResolverFetchMiddleware.test.ts | 22 ++++---- .../core-app-api/src/app/AppManager.test.tsx | 6 +- .../ProxiedSignInPage.test.tsx | 5 +- packages/test-utils/report.api.md | 55 +++++++++++++------ .../src/testUtils/apis/mockApis.test.tsx | 28 ++++++++++ .../test-utils/src/testUtils/apis/mockApis.ts | 30 ++++++++-- .../CookieAuthRefreshProvider.test.tsx | 8 +-- .../useCookieAuthRefresh.test.tsx | 10 +--- .../PodExecTerminal/PodExecTerminal.test.tsx | 9 ++- .../Pods/PodDrawer/PodDrawer.test.tsx | 12 ++-- plugins/signals/src/api/SignalsClient.test.ts | 11 ++-- .../TechDocsReaderPage.test.tsx | 8 +-- 12 files changed, 129 insertions(+), 75 deletions(-) diff --git a/packages/core-app-api/src/apis/implementations/FetchApi/PluginProtocolResolverFetchMiddleware.test.ts b/packages/core-app-api/src/apis/implementations/FetchApi/PluginProtocolResolverFetchMiddleware.test.ts index 3b4b7136bb..7a9ebdc4f9 100644 --- a/packages/core-app-api/src/apis/implementations/FetchApi/PluginProtocolResolverFetchMiddleware.test.ts +++ b/packages/core-app-api/src/apis/implementations/FetchApi/PluginProtocolResolverFetchMiddleware.test.ts @@ -14,15 +14,14 @@ * limitations under the License. */ -import { DiscoveryApi } from '@backstage/core-plugin-api'; +import { mockApis } from '@backstage/test-utils'; import { PluginProtocolResolverFetchMiddleware } from './PluginProtocolResolverFetchMiddleware'; describe('PluginProtocolResolverFetchMiddleware', () => { it.each([['https://passthrough.com/a']])( 'passes through regular URLs, %p', async url => { - const resolve = jest.fn(); - const discoveryApi = { getBaseUrl: resolve } as unknown as DiscoveryApi; + const discoveryApi = mockApis.discovery(); const middleware = new PluginProtocolResolverFetchMiddleware( discoveryApi, ); @@ -31,7 +30,7 @@ describe('PluginProtocolResolverFetchMiddleware', () => { await outer(url); expect(inner.mock.calls[0][0]).toBe(url); - expect(resolve).not.toHaveBeenCalled(); + expect(discoveryApi.getBaseUrl).not.toHaveBeenCalled(); }, ); @@ -76,30 +75,29 @@ describe('PluginProtocolResolverFetchMiddleware', () => { ])( 'resolves backstage URLs, %p', async (original, host, resolved, result) => { - const resolve = jest.fn(); - const discoveryApi = { getBaseUrl: resolve } as unknown as DiscoveryApi; + const discoveryApi = mockApis.discovery.mock({ + getBaseUrl: async () => resolved, + }); const middleware = new PluginProtocolResolverFetchMiddleware( discoveryApi, ); const inner = jest.fn(); const outer = middleware.apply(inner); - resolve.mockResolvedValueOnce(resolved); await outer(original); expect(inner.mock.calls[0][0]).toBe(result); - expect(resolve).toHaveBeenLastCalledWith(host); + expect(discoveryApi.getBaseUrl).toHaveBeenLastCalledWith(host); }, ); it('properly supports transferring request bodies too', async () => { - const resolve = jest.fn(); - const discoveryApi = { getBaseUrl: resolve } as unknown as DiscoveryApi; + const discoveryApi = mockApis.discovery.mock({ + getBaseUrl: async () => 'https://elsewhere.com', + }); const middleware = new PluginProtocolResolverFetchMiddleware(discoveryApi); const inner = jest.fn(); const outer = middleware.apply(inner); - resolve.mockResolvedValue('https://elsewhere.com'); - await outer('plugin://a', { method: 'POST', body: '123', diff --git a/packages/core-app-api/src/app/AppManager.test.tsx b/packages/core-app-api/src/app/AppManager.test.tsx index 1b7d2d28cb..d995edffdf 100644 --- a/packages/core-app-api/src/app/AppManager.test.tsx +++ b/packages/core-app-api/src/app/AppManager.test.tsx @@ -878,9 +878,9 @@ describe('Integration Test', () => { }), }), }; - const discoveryApiMock = { - getBaseUrl: jest.fn().mockResolvedValue('http://localhost:7007/app'), - }; + const discoveryApiMock = mockApis.discovery.mock({ + getBaseUrl: async () => 'http://localhost:7007/app', + }); const app = new AppManager({ icons, diff --git a/packages/core-components/src/layout/ProxiedSignInPage/ProxiedSignInPage.test.tsx b/packages/core-components/src/layout/ProxiedSignInPage/ProxiedSignInPage.test.tsx index e4e65b613f..ad7e650d24 100644 --- a/packages/core-components/src/layout/ProxiedSignInPage/ProxiedSignInPage.test.tsx +++ b/packages/core-components/src/layout/ProxiedSignInPage/ProxiedSignInPage.test.tsx @@ -20,6 +20,7 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { TestApiProvider, + mockApis, registerMswTestHooks, wrapInTestApp, } from '@backstage/test-utils'; @@ -37,9 +38,7 @@ describe('ProxiedSignInPage', () => { apis={[ [ discoveryApiRef, - { - getBaseUrl: async () => 'http://example.com/api/auth', - }, + mockApis.discovery({ baseUrl: 'http://example.com' }), ], ]} > diff --git a/packages/test-utils/report.api.md b/packages/test-utils/report.api.md index 167494d4f6..cc09727d87 100644 --- a/packages/test-utils/report.api.md +++ b/packages/test-utils/report.api.md @@ -115,6 +115,25 @@ export namespace mockApis { const mock: (partialImpl?: Partial | undefined) => ApiMock; } // (undocumented) + export function discovery(options?: { baseUrl?: string }): jest.Mocked<{ + getBaseUrl(pluginId: string): Promise; + }>; + // (undocumented) + export namespace discovery { + const // (undocumented) + factory: ( + options?: + | { + baseUrl?: string | undefined; + } + | undefined, + ) => ApiFactory; + const // (undocumented) + mock: ( + partialImpl?: Partial | undefined, + ) => ApiMock; + } + // (undocumented) export function identity(options?: { userEntityRef?: string; ownershipEntityRefs?: string[]; @@ -421,20 +440,24 @@ export function wrapInTestApp( // src/testUtils/apis/mockApis.d.ts:48:5 - (ae-undocumented) Missing documentation for "analytics". // src/testUtils/apis/mockApis.d.ts:49:15 - (ae-undocumented) Missing documentation for "factory". // src/testUtils/apis/mockApis.d.ts:50:15 - (ae-undocumented) Missing documentation for "mock". -// src/testUtils/apis/mockApis.d.ts:101:5 - (ae-undocumented) Missing documentation for "identity". -// src/testUtils/apis/mockApis.d.ts:109:5 - (ae-undocumented) Missing documentation for "identity". -// src/testUtils/apis/mockApis.d.ts:110:15 - (ae-undocumented) Missing documentation for "factory". -// src/testUtils/apis/mockApis.d.ts:118:15 - (ae-undocumented) Missing documentation for "mock". -// src/testUtils/apis/mockApis.d.ts:120:5 - (ae-undocumented) Missing documentation for "permission". -// src/testUtils/apis/mockApis.d.ts:123:5 - (ae-undocumented) Missing documentation for "permission". -// src/testUtils/apis/mockApis.d.ts:124:15 - (ae-undocumented) Missing documentation for "factory". -// src/testUtils/apis/mockApis.d.ts:127:15 - (ae-undocumented) Missing documentation for "mock". -// src/testUtils/apis/mockApis.d.ts:129:5 - (ae-undocumented) Missing documentation for "storage". -// src/testUtils/apis/mockApis.d.ts:132:5 - (ae-undocumented) Missing documentation for "storage". -// src/testUtils/apis/mockApis.d.ts:133:15 - (ae-undocumented) Missing documentation for "factory". -// src/testUtils/apis/mockApis.d.ts:136:15 - (ae-undocumented) Missing documentation for "mock". -// src/testUtils/apis/mockApis.d.ts:138:5 - (ae-undocumented) Missing documentation for "translation". -// src/testUtils/apis/mockApis.d.ts:139:5 - (ae-undocumented) Missing documentation for "translation". -// src/testUtils/apis/mockApis.d.ts:140:15 - (ae-undocumented) Missing documentation for "factory". -// src/testUtils/apis/mockApis.d.ts:141:15 - (ae-undocumented) Missing documentation for "mock". +// src/testUtils/apis/mockApis.d.ts:101:5 - (ae-undocumented) Missing documentation for "discovery". +// src/testUtils/apis/mockApis.d.ts:106:5 - (ae-undocumented) Missing documentation for "discovery". +// src/testUtils/apis/mockApis.d.ts:107:15 - (ae-undocumented) Missing documentation for "factory". +// src/testUtils/apis/mockApis.d.ts:110:15 - (ae-undocumented) Missing documentation for "mock". +// src/testUtils/apis/mockApis.d.ts:112:5 - (ae-undocumented) Missing documentation for "identity". +// src/testUtils/apis/mockApis.d.ts:120:5 - (ae-undocumented) Missing documentation for "identity". +// src/testUtils/apis/mockApis.d.ts:121:15 - (ae-undocumented) Missing documentation for "factory". +// src/testUtils/apis/mockApis.d.ts:129:15 - (ae-undocumented) Missing documentation for "mock". +// src/testUtils/apis/mockApis.d.ts:131:5 - (ae-undocumented) Missing documentation for "permission". +// src/testUtils/apis/mockApis.d.ts:134:5 - (ae-undocumented) Missing documentation for "permission". +// src/testUtils/apis/mockApis.d.ts:135:15 - (ae-undocumented) Missing documentation for "factory". +// src/testUtils/apis/mockApis.d.ts:138:15 - (ae-undocumented) Missing documentation for "mock". +// src/testUtils/apis/mockApis.d.ts:140:5 - (ae-undocumented) Missing documentation for "storage". +// src/testUtils/apis/mockApis.d.ts:143:5 - (ae-undocumented) Missing documentation for "storage". +// src/testUtils/apis/mockApis.d.ts:144:15 - (ae-undocumented) Missing documentation for "factory". +// src/testUtils/apis/mockApis.d.ts:147:15 - (ae-undocumented) Missing documentation for "mock". +// src/testUtils/apis/mockApis.d.ts:149:5 - (ae-undocumented) Missing documentation for "translation". +// src/testUtils/apis/mockApis.d.ts:150:5 - (ae-undocumented) Missing documentation for "translation". +// src/testUtils/apis/mockApis.d.ts:151:15 - (ae-undocumented) Missing documentation for "factory". +// src/testUtils/apis/mockApis.d.ts:152: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 662567066c..af0bbaee72 100644 --- a/packages/test-utils/src/testUtils/apis/mockApis.test.tsx +++ b/packages/test-utils/src/testUtils/apis/mockApis.test.tsx @@ -79,6 +79,34 @@ describe('mockApis', () => { }); }); + describe('discovery', () => { + it('can create an instance and make assertions on it', async () => { + const empty = mockApis.discovery(); + await expect(empty.getBaseUrl('catalog')).resolves.toBe( + 'http://example.com/api/catalog', + ); + expect(empty.getBaseUrl).toHaveBeenCalledTimes(1); + + const notEmpty = mockApis.discovery({ baseUrl: 'https://other.net' }); + await expect(notEmpty.getBaseUrl('catalog')).resolves.toBe( + 'https://other.net/api/catalog', + ); + expect(notEmpty.getBaseUrl).toHaveBeenCalledTimes(1); + }); + + it('can create a mock and make assertions on it', async () => { + const empty = mockApis.discovery.mock(); + expect(empty.getBaseUrl('catalog')).toBeUndefined(); + expect(empty.getBaseUrl).toHaveBeenCalledTimes(1); + + const notEmpty = mockApis.discovery.mock({ + getBaseUrl: async () => 'replaced', + }); + await expect(notEmpty.getBaseUrl('catalog')).resolves.toBe('replaced'); + expect(notEmpty.getBaseUrl).toHaveBeenCalledTimes(1); + }); + }); + describe('identity', () => { it('can create an instance and make assertions on it', async () => { const empty = mockApis.identity(); diff --git a/packages/test-utils/src/testUtils/apis/mockApis.ts b/packages/test-utils/src/testUtils/apis/mockApis.ts index cfc174c5ad..1adf172893 100644 --- a/packages/test-utils/src/testUtils/apis/mockApis.ts +++ b/packages/test-utils/src/testUtils/apis/mockApis.ts @@ -20,14 +20,20 @@ import { ApiFactory, ApiRef, ConfigApi, + DiscoveryApi, IdentityApi, StorageApi, analyticsApiRef, configApiRef, createApiFactory, + discoveryApiRef, identityApiRef, storageApiRef, } from '@backstage/core-plugin-api'; +import { + TranslationApi, + translationApiRef, +} from '@backstage/core-plugin-api/alpha'; import { AuthorizeResult, EvaluatePermissionRequest, @@ -40,10 +46,6 @@ import { JsonObject } from '@backstage/types'; import { ApiMock } from './ApiMock'; import { MockPermissionApi } from './PermissionApi'; import { MockStorageApi } from './StorageApi'; -import { - TranslationApi, - translationApiRef, -} from '@backstage/core-plugin-api/alpha'; import { MockTranslationApi } from './TranslationApi'; /** @internal */ @@ -200,6 +202,26 @@ export namespace mockApis { })); } + const discoveryMockSkeleton = (): jest.Mocked => ({ + getBaseUrl: jest.fn(), + }); + export function discovery(options?: { baseUrl?: string }) { + const baseUrl = options?.baseUrl ?? 'http://example.com'; + return simpleInstance( + discoveryApiRef, + { + async getBaseUrl(pluginId: string) { + return `${baseUrl}/api/${pluginId}`; + }, + }, + discoveryMockSkeleton, + ); + } + export namespace discovery { + export const factory = simpleFactory(discoveryApiRef, discovery); + export const mock = simpleMock(discoveryApiRef, discoveryMockSkeleton); + } + const identityMockSkeleton = (): jest.Mocked => ({ getBackstageIdentity: jest.fn(), getCredentials: jest.fn(), diff --git a/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.test.tsx b/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.test.tsx index 6ff9d8a1c5..8b07abb163 100644 --- a/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.test.tsx +++ b/plugins/auth-react/src/components/CookieAuthRefreshProvider/CookieAuthRefreshProvider.test.tsx @@ -30,11 +30,7 @@ import { } from '@backstage/core-plugin-api'; describe('CookieAuthRefreshProvider', () => { - const discoveryApiMock = { - getBaseUrl: jest - .fn() - .mockResolvedValue('http://localhost:7000/api/techdocs'), - }; + const discoveryApiMock = mockApis.discovery(); function getExpiresAtInFuture() { const tenMinutesInMilliseconds = 10 * 60 * 1000; @@ -118,7 +114,7 @@ describe('CookieAuthRefreshProvider', () => { await waitFor(() => expect(fetchApiMock.fetch).toHaveBeenCalledWith( - 'http://localhost:7000/api/techdocs/.backstage/auth/v1/cookie', + 'http://example.com/api/techdocs/.backstage/auth/v1/cookie', { credentials: 'include' }, ), ); diff --git a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx index 422f392e93..6b59dbcb9e 100644 --- a/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx +++ b/plugins/auth-react/src/hooks/useCookieAuthRefresh/useCookieAuthRefresh.test.tsx @@ -17,15 +17,11 @@ import React from 'react'; import { renderHook, waitFor } from '@testing-library/react'; import { fetchApiRef, discoveryApiRef } from '@backstage/core-plugin-api'; -import { TestApiProvider } from '@backstage/test-utils'; +import { TestApiProvider, mockApis } from '@backstage/test-utils'; import { useCookieAuthRefresh } from './useCookieAuthRefresh'; describe('useCookieAuthRefresh', () => { - const discoveryApiMock = { - getBaseUrl: jest - .fn() - .mockResolvedValue('http://localhost:7000/api/techdocs'), - }; + const discoveryApiMock = mockApis.discovery(); const now = 1710316886171; const tenMinutesInMilliseconds = 10 * 60 * 1000; @@ -269,7 +265,7 @@ describe('useCookieAuthRefresh', () => { await waitFor(() => expect(fetchApiMock.fetch).toHaveBeenCalledWith( - 'http://localhost:7000/api/techdocs/.backstage/auth/v1/cookie', + 'http://example.com/api/techdocs/.backstage/auth/v1/cookie', { credentials: 'include' }, ), ); diff --git a/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminal.test.tsx b/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminal.test.tsx index 4aa6112436..0e56c9599a 100644 --- a/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminal.test.tsx +++ b/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminal.test.tsx @@ -14,8 +14,9 @@ * limitations under the License. */ -import { DiscoveryApi, discoveryApiRef } from '@backstage/core-plugin-api'; +import { discoveryApiRef } from '@backstage/core-plugin-api'; import { + mockApis, renderInTestApp, TestApiProvider, textContentMatcher, @@ -37,9 +38,7 @@ describe('PodExecTerminal', () => { const podName = 'pod1'; const podNamespace = 'podNamespace'; - const mockDiscoveryApi: Partial = { - getBaseUrl: () => Promise.resolve('http://localhost'), - }; + const mockDiscoveryApi = mockApis.discovery(); it('Should render an XTerm web terminal', async () => { await renderInTestApp( @@ -62,7 +61,7 @@ describe('PodExecTerminal', () => { it('Should connect to WebSocket server & render response', async () => { const server = new WS( - 'ws://localhost/proxy/api/v1/namespaces/podNamespace/pods/pod1/exec?container=container2&stdin=true&stdout=true&stderr=true&tty=true&command=%2Fbin%2Fsh', + 'ws://example.com/api/kubernetes/proxy/api/v1/namespaces/podNamespace/pods/pod1/exec?container=container2&stdin=true&stdout=true&stderr=true&tty=true&command=%2Fbin%2Fsh', ); await renderInTestApp( diff --git a/plugins/kubernetes-react/src/components/Pods/PodDrawer/PodDrawer.test.tsx b/plugins/kubernetes-react/src/components/Pods/PodDrawer/PodDrawer.test.tsx index 066fa0f874..4718f8f29f 100644 --- a/plugins/kubernetes-react/src/components/Pods/PodDrawer/PodDrawer.test.tsx +++ b/plugins/kubernetes-react/src/components/Pods/PodDrawer/PodDrawer.test.tsx @@ -17,19 +17,21 @@ import React from 'react'; import { screen } from '@testing-library/react'; -import { TestApiProvider, renderInTestApp } from '@backstage/test-utils'; +import { + TestApiProvider, + mockApis, + renderInTestApp, +} from '@backstage/test-utils'; import '@testing-library/jest-dom'; import { PodDrawer } from './PodDrawer'; -import { DiscoveryApi, discoveryApiRef } from '@backstage/core-plugin-api'; +import { discoveryApiRef } from '@backstage/core-plugin-api'; jest.mock('../../../hooks/useIsPodExecTerminalSupported'); describe('PodDrawer', () => { it('Should show title and container names', async () => { - const mockDiscoveryApi: Partial = { - getBaseUrl: () => Promise.resolve('http://localhost'), - }; + const mockDiscoveryApi = mockApis.discovery(); await renderInTestApp( diff --git a/plugins/signals/src/api/SignalsClient.test.ts b/plugins/signals/src/api/SignalsClient.test.ts index 057b1fb23f..b6944cfdc1 100644 --- a/plugins/signals/src/api/SignalsClient.test.ts +++ b/plugins/signals/src/api/SignalsClient.test.ts @@ -14,24 +14,21 @@ * limitations under the License. */ -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 baseUrlFunction = jest.fn(); const identity = mockApis.identity({ token: '12345' }); - const discoveryApi = { - getBaseUrl: baseUrlFunction, - } as unknown as DiscoveryApi; + const discoveryApi = mockApis.discovery({ baseUrl: 'http://localhost:1234' }); let server: WS; beforeEach(async () => { jest.clearAllMocks(); - baseUrlFunction.mockResolvedValue('http://localhost:1234'); - server = new WS('ws://localhost:1234', { jsonProtocol: true }); + server = new WS('ws://localhost:1234/api/signals', { + jsonProtocol: true, + }); }); afterEach(() => { diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.test.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.test.tsx index 5d08355ae4..afb57e22ba 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.test.tsx @@ -81,12 +81,6 @@ const techdocsStorageApiMock: jest.Mocked = { syncEntityDocs: jest.fn(), }; -const discoveryApiMock = { - getBaseUrl: jest - .fn() - .mockResolvedValue('https://localhost:7000/api/techdocs'), -}; - const fetchApiMock = { fetch: jest.fn().mockResolvedValue({ ok: true, @@ -116,7 +110,7 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => {