refactor: apply second round of suggestions
Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
@@ -3,41 +3,38 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AsyncState } from '@react-hookz/web';
|
||||
import { default as React_2 } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { UseAsyncActions } from '@react-hookz/web';
|
||||
|
||||
// @public
|
||||
export type CookieAuthRefreshOptions = {
|
||||
export function CookieAuthRefreshProvider(
|
||||
props: CookieAuthRefreshProviderProps,
|
||||
): JSX.Element;
|
||||
|
||||
// @public
|
||||
export type CookieAuthRefreshProviderProps = {
|
||||
pluginId: string;
|
||||
path?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export function CookieAuthRefreshProvider({
|
||||
children,
|
||||
...rest
|
||||
}: CookieAuthRefreshProviderProps):
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| Iterable<React_2.ReactNode>
|
||||
| React_2.JSX.Element
|
||||
| null
|
||||
| undefined;
|
||||
|
||||
// @public
|
||||
export type CookieAuthRefreshProviderProps = CookieAuthRefreshOptions & {
|
||||
options?: {
|
||||
path?: string;
|
||||
};
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
// @public
|
||||
export function useCookieAuthRefresh({
|
||||
pluginId,
|
||||
path,
|
||||
}: CookieAuthRefreshOptions): {
|
||||
state: AsyncState<any>;
|
||||
actions: UseAsyncActions<any, []>;
|
||||
export function useCookieAuthRefresh(params: {
|
||||
pluginId: string;
|
||||
options?: {
|
||||
path?: string;
|
||||
};
|
||||
}): {
|
||||
loading: boolean;
|
||||
error: Error | undefined;
|
||||
value:
|
||||
| {
|
||||
expiresAt: string;
|
||||
}
|
||||
| undefined;
|
||||
retry: (...args: unknown[]) => Promise<{
|
||||
expiresAt: string;
|
||||
}>;
|
||||
};
|
||||
```
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@material-ui/core": "^4.9.13",
|
||||
"@react-hookz/web": "^24.0.4",
|
||||
"@react-hookz/web": "^24.0.0",
|
||||
"@types/react": "^16.13.1 || ^17.0.0 || ^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
+20
-9
@@ -18,10 +18,19 @@ import React from 'react';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { CookieAuthRefreshProvider } from './CookieAuthRefreshProvider';
|
||||
import { TestApiProvider, renderInTestApp } from '@backstage/test-utils';
|
||||
import { discoveryApiRef, fetchApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
MockStorageApi,
|
||||
TestApiProvider,
|
||||
renderInTestApp,
|
||||
} from '@backstage/test-utils';
|
||||
import {
|
||||
discoveryApiRef,
|
||||
fetchApiRef,
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
describe('CookieAuthRefreshProvider', () => {
|
||||
const storageApiMock = MockStorageApi.create();
|
||||
const discoveryApiMock = {
|
||||
getBaseUrl: jest
|
||||
.fn()
|
||||
@@ -33,12 +42,6 @@ describe('CookieAuthRefreshProvider', () => {
|
||||
return new Date(Date.now() + tenMinutesInMilliseconds).toISOString();
|
||||
}
|
||||
|
||||
global.BroadcastChannel = jest.fn().mockImplementation(() => ({
|
||||
postMessage: jest.fn(),
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
}));
|
||||
|
||||
it('should render a progress bar', async () => {
|
||||
const fetchApiMock = {
|
||||
fetch: jest.fn().mockReturnValue(new Promise(() => {})),
|
||||
@@ -48,6 +51,7 @@ describe('CookieAuthRefreshProvider', () => {
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -57,7 +61,9 @@ describe('CookieAuthRefreshProvider', () => {
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('progress')).toBeInTheDocument();
|
||||
expect(screen.queryByText('Test Content')).not.toBeInTheDocument();
|
||||
|
||||
expect(screen.getByTestId('progress')).toBeVisible();
|
||||
});
|
||||
|
||||
it('should render an error panel', async () => {
|
||||
@@ -70,6 +76,7 @@ describe('CookieAuthRefreshProvider', () => {
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -100,6 +107,7 @@ describe('CookieAuthRefreshProvider', () => {
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -118,6 +126,8 @@ describe('CookieAuthRefreshProvider', () => {
|
||||
|
||||
expect(fetchApiMock.fetch).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(screen.queryByText('Test Content')).not.toBeInTheDocument();
|
||||
|
||||
expect(screen.getByText(error.message)).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByText('Retry'));
|
||||
@@ -143,6 +153,7 @@ describe('CookieAuthRefreshProvider', () => {
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
|
||||
+22
-15
@@ -18,13 +18,20 @@ import React, { ReactNode } from 'react';
|
||||
import { ErrorPanel } from '@backstage/core-components';
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
import { Button } from '@material-ui/core';
|
||||
import { useCookieAuthRefresh, CookieAuthRefreshOptions } from '../../hooks';
|
||||
import { useCookieAuthRefresh } from '../../hooks';
|
||||
|
||||
/**
|
||||
* @public
|
||||
* Props for the {@link CookieAuthRefreshProvider} component.
|
||||
*/
|
||||
export type CookieAuthRefreshProviderProps = CookieAuthRefreshOptions & {
|
||||
export type CookieAuthRefreshProviderProps = {
|
||||
// The plugin ID to used for discovering the API origin
|
||||
pluginId: string;
|
||||
// Options for configuring the refresh cookie endpoint
|
||||
options?: {
|
||||
// The path to used for calling the refresh cookie endpoint, default to '/cookie'
|
||||
path?: string;
|
||||
};
|
||||
// The children to render when the refresh is successful
|
||||
children: ReactNode;
|
||||
};
|
||||
@@ -33,28 +40,28 @@ export type CookieAuthRefreshProviderProps = CookieAuthRefreshOptions & {
|
||||
* @public
|
||||
* A provider that will refresh the cookie when it is about to expire.
|
||||
*/
|
||||
export function CookieAuthRefreshProvider({
|
||||
children,
|
||||
...rest
|
||||
}: CookieAuthRefreshProviderProps) {
|
||||
export function CookieAuthRefreshProvider(
|
||||
props: CookieAuthRefreshProviderProps,
|
||||
): JSX.Element {
|
||||
const { children, ...params } = props;
|
||||
const app = useApp();
|
||||
const { Progress } = app.getComponents();
|
||||
|
||||
const { state, actions } = useCookieAuthRefresh(rest);
|
||||
const { loading, error, retry } = useCookieAuthRefresh(params);
|
||||
|
||||
if (state.status === 'error' && state.error) {
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<ErrorPanel error={state.error}>
|
||||
<Button variant="outlined" onClick={actions.execute}>
|
||||
<ErrorPanel error={error}>
|
||||
<Button variant="outlined" onClick={retry}>
|
||||
Retry
|
||||
</Button>
|
||||
</ErrorPanel>
|
||||
);
|
||||
}
|
||||
|
||||
if (state.status === 'loading') {
|
||||
return <Progress />;
|
||||
}
|
||||
|
||||
return children;
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
useCookieAuthRefresh,
|
||||
type CookieAuthRefreshOptions,
|
||||
} from './useCookieAuthRefresh';
|
||||
export { useCookieAuthRefresh } from './useCookieAuthRefresh';
|
||||
|
||||
@@ -16,8 +16,12 @@
|
||||
|
||||
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 {
|
||||
fetchApiRef,
|
||||
discoveryApiRef,
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { MockStorageApi, TestApiProvider } from '@backstage/test-utils';
|
||||
import { useCookieAuthRefresh } from './useCookieAuthRefresh';
|
||||
|
||||
describe('useCookieAuthRefresh', () => {
|
||||
@@ -39,31 +43,11 @@ describe('useCookieAuthRefresh', () => {
|
||||
}),
|
||||
};
|
||||
|
||||
type Listener = (event: { data: any }) => void;
|
||||
|
||||
let listeners: Listener[];
|
||||
let channelMock: any;
|
||||
const storageApiMock = MockStorageApi.create();
|
||||
|
||||
beforeEach(() => {
|
||||
jest.useFakeTimers({ now });
|
||||
jest.clearAllMocks();
|
||||
listeners = [];
|
||||
channelMock = {
|
||||
postMessage: jest.fn((message: any) => {
|
||||
listeners.forEach(listener => listener({ data: message }));
|
||||
}),
|
||||
addEventListener: jest.fn((event: string, listener: Listener) => {
|
||||
if (event === 'message') {
|
||||
listeners.push(listener);
|
||||
}
|
||||
}),
|
||||
removeEventListener: jest.fn((event: string, listener: Listener) => {
|
||||
if (event === 'message') {
|
||||
listeners = listeners.filter(l => l !== listener);
|
||||
}
|
||||
}),
|
||||
};
|
||||
global.BroadcastChannel = jest.fn().mockImplementation(() => channelMock);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -83,6 +67,7 @@ describe('useCookieAuthRefresh', () => {
|
||||
fetch: jest.fn(),
|
||||
},
|
||||
],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -92,7 +77,7 @@ describe('useCookieAuthRefresh', () => {
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.current.state.status).toBe('loading');
|
||||
expect(result.current.loading).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return an error status when the refresh has failed', async () => {
|
||||
@@ -110,6 +95,7 @@ describe('useCookieAuthRefresh', () => {
|
||||
fetch: jest.fn().mockRejectedValue(error),
|
||||
},
|
||||
],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -119,9 +105,7 @@ describe('useCookieAuthRefresh', () => {
|
||||
},
|
||||
);
|
||||
|
||||
await waitFor(() => expect(result.current.state.status).toBe('error'));
|
||||
|
||||
expect(result.current.state.error).toStrictEqual(error);
|
||||
await waitFor(() => expect(result.current.error).toStrictEqual(error));
|
||||
});
|
||||
|
||||
it('should call the api to get the cookie and use it', async () => {
|
||||
@@ -132,6 +116,7 @@ describe('useCookieAuthRefresh', () => {
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -148,46 +133,18 @@ describe('useCookieAuthRefresh', () => {
|
||||
),
|
||||
);
|
||||
|
||||
expect(result.current.state.result).toMatchObject({ expiresAt });
|
||||
});
|
||||
|
||||
it('should send a message to other tabs when the cookie is refreshed', async () => {
|
||||
renderHook(() => useCookieAuthRefresh({ pluginId: 'techdocs' }), {
|
||||
wrapper: ({ children }) => (
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
{children}
|
||||
</TestApiProvider>
|
||||
),
|
||||
});
|
||||
|
||||
expect(global.BroadcastChannel).toHaveBeenCalledWith(
|
||||
'techdocs-auth-cookie-channel',
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(channelMock.postMessage).toHaveBeenCalledTimes(1),
|
||||
);
|
||||
|
||||
// posting the message to other tabs when the cookie is requested in the first time
|
||||
await waitFor(() =>
|
||||
expect(channelMock.postMessage).toHaveBeenCalledWith({
|
||||
action: 'COOKIE_REFRESHED',
|
||||
payload: { expiresAt },
|
||||
}),
|
||||
);
|
||||
expect(result.current.value).toMatchObject({ expiresAt });
|
||||
});
|
||||
|
||||
it('should cancel the refresh when a message is received from another tab', async () => {
|
||||
renderHook(() => useCookieAuthRefresh({ pluginId: 'techdocs' }), {
|
||||
const pluginId = 'techdocs';
|
||||
|
||||
renderHook(() => useCookieAuthRefresh({ pluginId }), {
|
||||
wrapper: ({ children }) => (
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -196,29 +153,25 @@ describe('useCookieAuthRefresh', () => {
|
||||
),
|
||||
});
|
||||
|
||||
await waitFor(() =>
|
||||
expect(channelMock.addEventListener).toHaveBeenCalledTimes(1),
|
||||
);
|
||||
|
||||
const twentyMinutesFromNowInMilliseconds =
|
||||
now + 2 * tenMinutesInMilliseconds;
|
||||
|
||||
// simulating other tab refreshing the cookie
|
||||
channelMock.postMessage({
|
||||
action: 'COOKIE_REFRESHED',
|
||||
payload: {
|
||||
expiresAt: new Date(twentyMinutesFromNowInMilliseconds).toISOString(),
|
||||
},
|
||||
});
|
||||
storageApiMock
|
||||
.forBucket(`${pluginId}-auth-cookie-storage`)
|
||||
.set(
|
||||
'expiresAt',
|
||||
new Date(twentyMinutesFromNowInMilliseconds).toISOString(),
|
||||
);
|
||||
|
||||
// advance the timers in 10 minutes to match the old expires at
|
||||
jest.advanceTimersByTime(tenMinutesInMilliseconds);
|
||||
|
||||
// should not call the api
|
||||
expect(fetchApiMock.fetch).toHaveBeenCalledTimes(1);
|
||||
await waitFor(() => expect(fetchApiMock.fetch).toHaveBeenCalledTimes(1));
|
||||
|
||||
// advance the timers in more 10 minutes to match the new expires at
|
||||
jest.advanceTimersByTime(tenMinutesInMilliseconds);
|
||||
jest.advanceTimersByTime(tenMinutesInMilliseconds - 1000);
|
||||
|
||||
// should call the api
|
||||
await waitFor(() => expect(fetchApiMock.fetch).toHaveBeenCalledTimes(2));
|
||||
@@ -232,6 +185,7 @@ describe('useCookieAuthRefresh', () => {
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -243,16 +197,10 @@ describe('useCookieAuthRefresh', () => {
|
||||
|
||||
await waitFor(() => expect(fetchApiMock.fetch).toHaveBeenCalledTimes(1));
|
||||
|
||||
expect(result.current.state.result).toMatchObject({ expiresAt });
|
||||
expect(result.current.value).toMatchObject({ expiresAt });
|
||||
|
||||
unmount();
|
||||
|
||||
expect(channelMock.removeEventListener).toHaveBeenCalledTimes(1);
|
||||
expect(channelMock.removeEventListener).toHaveBeenCalledWith(
|
||||
'message',
|
||||
expect.any(Function),
|
||||
);
|
||||
|
||||
// advance the timers to ensure that the refresh is not called
|
||||
jest.advanceTimersByTime(tenMinutesInMilliseconds);
|
||||
|
||||
@@ -268,6 +216,7 @@ describe('useCookieAuthRefresh', () => {
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -279,7 +228,7 @@ describe('useCookieAuthRefresh', () => {
|
||||
await waitFor(() => expect(fetchApiMock.fetch).toHaveBeenCalledTimes(1));
|
||||
|
||||
// advance the timers to the expiration date
|
||||
jest.advanceTimersByTime(tenMinutesInMilliseconds);
|
||||
jest.advanceTimersByTime(tenMinutesInMilliseconds - 1000);
|
||||
|
||||
// should call the api
|
||||
await waitFor(() => expect(fetchApiMock.fetch).toHaveBeenCalledTimes(2));
|
||||
|
||||
@@ -14,51 +14,38 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import { useEffect, useCallback } from 'react';
|
||||
import {
|
||||
discoveryApiRef,
|
||||
fetchApiRef,
|
||||
storageApiRef,
|
||||
useApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { useAsync, useMountEffect } from '@react-hookz/web';
|
||||
import { ResponseError } from '@backstage/errors';
|
||||
|
||||
type CookieAuthRefreshMessage = MessageEvent<{
|
||||
action: string;
|
||||
payload: {
|
||||
expiresAt: string;
|
||||
};
|
||||
}>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
* The options for the {@link useCookieAuthRefresh} hook.
|
||||
*/
|
||||
export type CookieAuthRefreshOptions = {
|
||||
// The plugin ID to used for discovering the API origin
|
||||
pluginId: string;
|
||||
// The path to used for calling the refresh cookie endpoint, default to '/cookie'
|
||||
path?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
* A hook that will refresh the cookie when it is about to expire.
|
||||
* @remarks
|
||||
* This hook expects a `BroadcastChannel` to be available in the global scope.
|
||||
*/
|
||||
export function useCookieAuthRefresh({
|
||||
pluginId,
|
||||
path = '/cookie',
|
||||
}: CookieAuthRefreshOptions) {
|
||||
export function useCookieAuthRefresh(params: {
|
||||
// The plugin ID to used for discovering the API origin
|
||||
pluginId: string;
|
||||
// Options for configuring the refresh cookie endpoint
|
||||
options?: {
|
||||
// The path to used for calling the refresh cookie endpoint, default to '/cookie'
|
||||
path?: string;
|
||||
};
|
||||
}) {
|
||||
const { pluginId, options: { path = '/cookie' } = {} } = params;
|
||||
|
||||
const fetchApi = useApi(fetchApiRef);
|
||||
const storageApi = useApi(storageApiRef);
|
||||
const discoveryApi = useApi(discoveryApiRef);
|
||||
|
||||
const [channel] = useState(
|
||||
() => new BroadcastChannel(`${pluginId}-auth-cookie-channel`),
|
||||
);
|
||||
const store = storageApi.forBucket(`${pluginId}-auth-cookie-storage`);
|
||||
|
||||
const [state, actions] = useAsync(async () => {
|
||||
const [state, actions] = useAsync<{ expiresAt: string }>(async () => {
|
||||
const apiOrigin = await discoveryApi.getBaseUrl(pluginId);
|
||||
const requestUrl = `${apiOrigin}${path}`;
|
||||
const response = await fetchApi.fetch(`${requestUrl}`, {
|
||||
@@ -86,28 +73,27 @@ export function useCookieAuthRefresh({
|
||||
useEffect(() => {
|
||||
if (!state.result) return () => {};
|
||||
|
||||
channel.postMessage({
|
||||
action: 'COOKIE_REFRESHED',
|
||||
payload: state.result,
|
||||
});
|
||||
store.set('expiresAt', state.result.expiresAt);
|
||||
|
||||
let cancel = refresh(state.result);
|
||||
|
||||
const handleMessage = (event: CookieAuthRefreshMessage): void => {
|
||||
const { action, payload } = event.data;
|
||||
if (action === 'COOKIE_REFRESHED') {
|
||||
cancel();
|
||||
cancel = refresh(payload);
|
||||
}
|
||||
};
|
||||
|
||||
channel.addEventListener('message', handleMessage);
|
||||
const observable = store.observe$<string>('expiresAt');
|
||||
const subscription = observable.subscribe(({ value }) => {
|
||||
if (!value) return;
|
||||
cancel();
|
||||
cancel = refresh({ expiresAt: value });
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancel();
|
||||
channel.removeEventListener('message', handleMessage);
|
||||
subscription.unsubscribe();
|
||||
};
|
||||
}, [state, refresh, channel]);
|
||||
}, [state, refresh, store]);
|
||||
|
||||
return { state, actions };
|
||||
return {
|
||||
loading: state.status === 'loading',
|
||||
error: state.error,
|
||||
value: state.result,
|
||||
retry: actions.execute,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,28 +14,3 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
type Listener = (event: { data: any }) => void;
|
||||
|
||||
global.BroadcastChannel = jest
|
||||
.fn()
|
||||
.mockImplementation((_channelName: string) => {
|
||||
let listeners: Listener[] = [];
|
||||
return {
|
||||
postMessage: jest.fn((message: any) => {
|
||||
// Simulate message event for all listeners
|
||||
listeners.forEach(listener => listener({ data: message }));
|
||||
}),
|
||||
addEventListener: jest.fn((event: string, listener: Listener) => {
|
||||
if (event === 'message') {
|
||||
listeners.push(listener);
|
||||
}
|
||||
}),
|
||||
removeEventListener: jest.fn((event: string, listener: Listener) => {
|
||||
if (event === 'message') {
|
||||
listeners = listeners.filter(l => l !== listener);
|
||||
}
|
||||
}),
|
||||
close: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -22,12 +22,17 @@ import { screen } from 'testing-library__dom';
|
||||
import { Route } from 'react-router-dom';
|
||||
import { act, render } from '@testing-library/react';
|
||||
|
||||
import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils';
|
||||
import {
|
||||
wrapInTestApp,
|
||||
TestApiProvider,
|
||||
MockStorageApi,
|
||||
} from '@backstage/test-utils';
|
||||
import { FlatRoutes } from '@backstage/core-app-api';
|
||||
import {
|
||||
ApiRef,
|
||||
discoveryApiRef,
|
||||
fetchApiRef,
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
import {
|
||||
@@ -73,6 +78,8 @@ const scmIntegrationsApi = {
|
||||
fromConfig: jest.fn().mockReturnValue({}),
|
||||
};
|
||||
|
||||
const storageApiMock = MockStorageApi.create();
|
||||
|
||||
const discoveryApi = {
|
||||
getBaseUrl: jest
|
||||
.fn()
|
||||
@@ -225,6 +232,7 @@ export class TechDocsAddonTester {
|
||||
build() {
|
||||
const apis: TechdocsAddonTesterApis<any[]> = [
|
||||
[fetchApiRef, fetchApi],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApi],
|
||||
[techdocsApiRef, techdocsApi],
|
||||
[techdocsStorageApiRef, techdocsStorageApi],
|
||||
|
||||
+5
-23
@@ -19,6 +19,7 @@ import { scmIntegrationsApiRef } from '@backstage/integration-react';
|
||||
import { entityRouteRef } from '@backstage/plugin-catalog-react';
|
||||
import {
|
||||
MockConfigApi,
|
||||
MockStorageApi,
|
||||
renderInTestApp,
|
||||
TestApiProvider,
|
||||
} from '@backstage/test-utils';
|
||||
@@ -38,6 +39,7 @@ import {
|
||||
configApiRef,
|
||||
discoveryApiRef,
|
||||
fetchApiRef,
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
const mockEntityMetadata = {
|
||||
@@ -80,6 +82,8 @@ const techdocsStorageApiMock: jest.Mocked<typeof techdocsStorageApiRef.T> = {
|
||||
syncEntityDocs: jest.fn(),
|
||||
};
|
||||
|
||||
const storageApiMock = MockStorageApi.create();
|
||||
|
||||
const discoveryApiMock = {
|
||||
getBaseUrl: jest
|
||||
.fn()
|
||||
@@ -117,6 +121,7 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => {
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[fetchApiRef, fetchApiMock],
|
||||
[storageApiRef, storageApiMock],
|
||||
[discoveryApiRef, discoveryApiMock],
|
||||
[scmIntegrationsApiRef, {}],
|
||||
[configApiRef, configApi],
|
||||
@@ -137,29 +142,6 @@ const mountedRoutes = {
|
||||
|
||||
describe('<TechDocsReaderPage />', () => {
|
||||
beforeEach(() => {
|
||||
type Listener = (event: { data: any }) => void;
|
||||
|
||||
global.BroadcastChannel = jest
|
||||
.fn()
|
||||
.mockImplementation((_channelName: string) => {
|
||||
let listeners: Listener[] = [];
|
||||
return {
|
||||
postMessage: jest.fn((message: any) => {
|
||||
listeners.forEach(listener => listener({ data: message }));
|
||||
}),
|
||||
addEventListener: jest.fn((event: string, listener: Listener) => {
|
||||
if (event === 'message') {
|
||||
listeners.push(listener);
|
||||
}
|
||||
}),
|
||||
removeEventListener: jest.fn((event: string, listener: Listener) => {
|
||||
if (event === 'message') {
|
||||
listeners = listeners.filter(l => l !== listener);
|
||||
}
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
getEntityMetadata.mockResolvedValue(mockEntityMetadata);
|
||||
getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata);
|
||||
getCookie.mockResolvedValue({
|
||||
|
||||
@@ -17,28 +17,3 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
Element.prototype.scrollIntoView = jest.fn();
|
||||
|
||||
type Listener = (event: { data: any }) => void;
|
||||
|
||||
global.BroadcastChannel = jest
|
||||
.fn()
|
||||
.mockImplementation((_channelName: string) => {
|
||||
let listeners: Listener[] = [];
|
||||
return {
|
||||
postMessage: jest.fn((message: any) => {
|
||||
// Simulate message event for all listeners
|
||||
listeners.forEach(listener => listener({ data: message }));
|
||||
}),
|
||||
addEventListener: jest.fn((event: string, listener: Listener) => {
|
||||
if (event === 'message') {
|
||||
listeners.push(listener);
|
||||
}
|
||||
}),
|
||||
removeEventListener: jest.fn((event: string, listener: Listener) => {
|
||||
if (event === 'message') {
|
||||
listeners = listeners.filter(l => l !== listener);
|
||||
}
|
||||
}),
|
||||
close: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
@@ -5120,7 +5120,7 @@ __metadata:
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/test-utils": "workspace:^"
|
||||
"@material-ui/core": ^4.9.13
|
||||
"@react-hookz/web": ^24.0.4
|
||||
"@react-hookz/web": ^24.0.0
|
||||
"@testing-library/jest-dom": ^6.0.0
|
||||
"@testing-library/react": ^14.0.0
|
||||
"@testing-library/user-event": ^14.0.0
|
||||
@@ -15728,7 +15728,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@react-hookz/web@npm:^24.0.0, @react-hookz/web@npm:^24.0.4":
|
||||
"@react-hookz/web@npm:^24.0.0":
|
||||
version: 24.0.4
|
||||
resolution: "@react-hookz/web@npm:24.0.4"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user