refactor: use broadcast channel again

Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
Camila Belo
2024-03-18 16:54:35 +01:00
parent cfc0bb345d
commit 13c680c165
5 changed files with 66 additions and 51 deletions
@@ -16,12 +16,8 @@
import React from 'react';
import { renderHook, waitFor } from '@testing-library/react';
import {
fetchApiRef,
discoveryApiRef,
storageApiRef,
} from '@backstage/core-plugin-api';
import { MockStorageApi, TestApiProvider } from '@backstage/test-utils';
import { fetchApiRef, discoveryApiRef } from '@backstage/core-plugin-api';
import { TestApiProvider } from '@backstage/test-utils';
import { useCookieAuthRefresh } from './useCookieAuthRefresh';
describe('useCookieAuthRefresh', () => {
@@ -43,8 +39,6 @@ describe('useCookieAuthRefresh', () => {
}),
};
const storageApiMock = MockStorageApi.create();
beforeEach(() => {
jest.useFakeTimers({ now });
jest.clearAllMocks();
@@ -69,7 +63,6 @@ describe('useCookieAuthRefresh', () => {
fetch: jest.fn().mockRejectedValue(error),
},
],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApiMock],
]}
>
@@ -100,7 +93,6 @@ describe('useCookieAuthRefresh', () => {
.mockReturnValue(new Promise(() => {})),
},
],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApiMock],
]}
>
@@ -153,7 +145,6 @@ describe('useCookieAuthRefresh', () => {
.mockReturnValue(new Promise(() => {})),
},
],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApiMock],
]}
>
@@ -208,7 +199,6 @@ describe('useCookieAuthRefresh', () => {
fetch: jest.fn().mockRejectedValue(error),
},
],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApiMock],
]}
>
@@ -235,7 +225,6 @@ describe('useCookieAuthRefresh', () => {
<TestApiProvider
apis={[
[fetchApiRef, fetchApiMock],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApiMock],
]}
>
@@ -268,7 +257,6 @@ describe('useCookieAuthRefresh', () => {
<TestApiProvider
apis={[
[fetchApiRef, fetchApiMock],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApiMock],
]}
>
@@ -281,12 +269,14 @@ describe('useCookieAuthRefresh', () => {
now + 2 * tenMinutesInMilliseconds;
// simulating other tab refreshing the cookie
storageApiMock
.forBucket(`${pluginId}-auth-cookie-storage`)
.set(
'expiresAt',
new Date(twentyMinutesFromNowInMilliseconds).toISOString(),
);
new global.BroadcastChannel(
`${pluginId}-auth-cookie-expires-at`,
).postMessage({
action: 'COOKIE_REFRESH_SUCCESS',
payload: {
expiresAt: new Date(twentyMinutesFromNowInMilliseconds).toISOString(),
},
});
// advance the timers in 10 minutes to match the old expires at
jest.advanceTimersByTime(tenMinutesInMilliseconds);
@@ -309,7 +299,6 @@ describe('useCookieAuthRefresh', () => {
<TestApiProvider
apis={[
[fetchApiRef, fetchApiMock],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApiMock],
]}
>
@@ -345,7 +334,6 @@ describe('useCookieAuthRefresh', () => {
<TestApiProvider
apis={[
[fetchApiRef, fetchApiMock],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApiMock],
]}
>
@@ -14,11 +14,10 @@
* limitations under the License.
*/
import { useEffect, useCallback } from 'react';
import { useEffect, useCallback, useMemo } from 'react';
import {
discoveryApiRef,
fetchApiRef,
storageApiRef,
useApi,
} from '@backstage/core-plugin-api';
import { useAsync, useMountEffect } from '@react-hookz/web';
@@ -40,10 +39,13 @@ export function useCookieAuthRefresh(options: {
| { status: 'success'; data: { expiresAt: string } } {
const { pluginId, path = '/cookie' } = options ?? {};
const fetchApi = useApi(fetchApiRef);
const storageApi = useApi(storageApiRef);
const discoveryApi = useApi(discoveryApiRef);
const store = storageApi.forBucket(`${pluginId}-auth-cookie-storage`);
const channel = useMemo(() => {
return 'BroadcastChannel' in window
? new BroadcastChannel(`${pluginId}-auth-cookie-expires-at`)
: null;
}, [pluginId]);
const [state, actions] = useAsync<{ expiresAt: string }>(async () => {
const apiOrigin = await discoveryApi.getBaseUrl(pluginId);
@@ -84,21 +86,26 @@ export function useCookieAuthRefresh(options: {
if (state.status !== 'success' || !state.result) {
return () => {};
}
const expiresAt = state.result.expiresAt;
store.set('expiresAt', expiresAt);
let cancel = refresh({ expiresAt });
const subscription = store
.observe$<string>('expiresAt')
.subscribe(({ value }) => {
if (!value) return;
channel?.postMessage({
action: 'COOKIE_REFRESH_SUCCESS',
payload: state.result,
});
let cancel = refresh(state.result);
const listener = (
event: MessageEvent<{ action: string; payload: { expiresAt: string } }>,
) => {
const { action, payload } = event.data;
if (action === 'COOKIE_REFRESH_SUCCESS') {
cancel();
cancel = refresh({ expiresAt: value });
});
cancel = refresh(payload);
}
};
channel?.addEventListener('message', listener);
return () => {
cancel();
subscription.unsubscribe();
channel?.removeEventListener('message', listener);
};
}, [state, refresh, store]);
}, [state, refresh, channel]);
// Initialising
if (state.status === 'not-executed') {
+33
View File
@@ -14,3 +14,36 @@
* limitations under the License.
*/
import '@testing-library/jest-dom';
global.BroadcastChannel = jest
.fn()
.mockImplementation((_channelName: string) => {
const listeners: ((event: { data: any }) => void)[] = [];
return {
addEventListener: (
type: string,
listener: (event: { data: any }) => void,
) => {
if (type === 'message') {
listeners.push(listener);
}
},
removeEventListener: (
type: string,
listener: (event: { data: any }) => void,
) => {
if (type === 'message') {
const index = listeners.indexOf(listener);
if (index !== -1) {
listeners.splice(index, 1);
}
}
},
postMessage: (message: any) => {
listeners.forEach(listener => {
listener({ data: message });
});
},
};
});
@@ -22,17 +22,12 @@ import { screen } from 'testing-library__dom';
import { Route } from 'react-router-dom';
import { act, render } from '@testing-library/react';
import {
wrapInTestApp,
TestApiProvider,
MockStorageApi,
} from '@backstage/test-utils';
import { wrapInTestApp, TestApiProvider } from '@backstage/test-utils';
import { FlatRoutes } from '@backstage/core-app-api';
import {
ApiRef,
discoveryApiRef,
fetchApiRef,
storageApiRef,
} from '@backstage/core-plugin-api';
import {
@@ -78,8 +73,6 @@ const scmIntegrationsApi = {
fromConfig: jest.fn().mockReturnValue({}),
};
const storageApiMock = MockStorageApi.create();
const discoveryApi = {
getBaseUrl: jest
.fn()
@@ -232,7 +225,6 @@ export class TechDocsAddonTester {
build() {
const apis: TechdocsAddonTesterApis<any[]> = [
[fetchApiRef, fetchApi],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApi],
[techdocsApiRef, techdocsApi],
[techdocsStorageApiRef, techdocsStorageApi],
@@ -19,7 +19,6 @@ import { scmIntegrationsApiRef } from '@backstage/integration-react';
import { entityRouteRef } from '@backstage/plugin-catalog-react';
import {
MockConfigApi,
MockStorageApi,
renderInTestApp,
TestApiProvider,
} from '@backstage/test-utils';
@@ -39,7 +38,6 @@ import {
configApiRef,
discoveryApiRef,
fetchApiRef,
storageApiRef,
} from '@backstage/core-plugin-api';
const mockEntityMetadata = {
@@ -82,8 +80,6 @@ const techdocsStorageApiMock: jest.Mocked<typeof techdocsStorageApiRef.T> = {
syncEntityDocs: jest.fn(),
};
const storageApiMock = MockStorageApi.create();
const discoveryApiMock = {
getBaseUrl: jest
.fn()
@@ -121,7 +117,6 @@ const Wrapper = ({ children }: { children: React.ReactNode }) => {
<TestApiProvider
apis={[
[fetchApiRef, fetchApiMock],
[storageApiRef, storageApiMock],
[discoveryApiRef, discoveryApiMock],
[scmIntegrationsApiRef, {}],
[configApiRef, configApi],