rtl 13 fixes for tests with type issues
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -353,9 +353,7 @@ describe('v1 consumer', () => {
|
||||
initialProps: {
|
||||
routeRef: routeRef1 as AnyRouteRef,
|
||||
},
|
||||
wrapper: ({
|
||||
children,
|
||||
}: React.PropsWithChildren<{ routeRef: AnyRouteRef }>) => (
|
||||
wrapper: ({ children }) => (
|
||||
<RoutingProvider
|
||||
routePaths={
|
||||
new Map<RouteRef<any>, string>([
|
||||
|
||||
@@ -385,9 +385,7 @@ describe('v1 consumer', () => {
|
||||
initialProps: {
|
||||
routeRef: routeRef1 as AnyRouteRef,
|
||||
},
|
||||
wrapper: ({
|
||||
children,
|
||||
}: React.PropsWithChildren<{ routeRef: AnyRouteRef }>) => (
|
||||
wrapper: ({ children }) => (
|
||||
<RoutingProvider
|
||||
routePaths={
|
||||
new Map<RouteRef<any>, string>([
|
||||
|
||||
@@ -316,23 +316,23 @@ describe('useElementFilter', () => {
|
||||
</MockComponent>
|
||||
);
|
||||
|
||||
const { result } = renderHook(
|
||||
props =>
|
||||
useElementFilter(props.tree, elements =>
|
||||
elements
|
||||
.selectByComponentData({
|
||||
key: WRAPPING_COMPONENT_KEY,
|
||||
withStrictError: 'Could not find component',
|
||||
})
|
||||
.findComponentData({ key: INNER_COMPONENT_KEY }),
|
||||
),
|
||||
{
|
||||
initialProps: { tree },
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
|
||||
expect(result.error?.message).toEqual('Could not find component');
|
||||
expect(() =>
|
||||
renderHook(
|
||||
props =>
|
||||
useElementFilter(props.tree, elements =>
|
||||
elements
|
||||
.selectByComponentData({
|
||||
key: WRAPPING_COMPONENT_KEY,
|
||||
withStrictError: 'Could not find component',
|
||||
})
|
||||
.findComponentData({ key: INNER_COMPONENT_KEY }),
|
||||
),
|
||||
{
|
||||
initialProps: { tree },
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
),
|
||||
).toThrow('Could not find component');
|
||||
});
|
||||
|
||||
it('should support fragments and text node iteration', () => {
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
TestApiProvider,
|
||||
withLogCollector,
|
||||
} from '@backstage/test-utils';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { createTranslationRef, TranslationRef } from './TranslationRef';
|
||||
import { useTranslationRef } from './useTranslationRef';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
@@ -91,14 +91,11 @@ describe('useTranslationRef', () => {
|
||||
],
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useTranslationRef(plainRef),
|
||||
{
|
||||
wrapper: makeWrapper(translationApi),
|
||||
},
|
||||
);
|
||||
const { result } = renderHook(() => useTranslationRef(plainRef), {
|
||||
wrapper: makeWrapper(translationApi),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
const { t } = result.current;
|
||||
|
||||
@@ -123,12 +120,9 @@ describe('useTranslationRef', () => {
|
||||
],
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useTranslationRef(plainRef),
|
||||
{
|
||||
wrapper: makeWrapper(translationApi),
|
||||
},
|
||||
);
|
||||
const { result } = renderHook(() => useTranslationRef(plainRef), {
|
||||
wrapper: makeWrapper(translationApi),
|
||||
});
|
||||
|
||||
const { t } = result.current;
|
||||
|
||||
@@ -137,7 +131,7 @@ describe('useTranslationRef', () => {
|
||||
|
||||
languageApi.setLanguage('de');
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
const { t: t2 } = result.current;
|
||||
|
||||
@@ -165,14 +159,11 @@ describe('useTranslationRef', () => {
|
||||
languageApi,
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useTranslationRef(resourceRef),
|
||||
{
|
||||
wrapper: makeWrapper(translationApi),
|
||||
},
|
||||
);
|
||||
const { result } = renderHook(() => useTranslationRef(resourceRef), {
|
||||
wrapper: makeWrapper(translationApi),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
const { t } = result.current;
|
||||
|
||||
@@ -212,7 +203,7 @@ describe('useTranslationRef', () => {
|
||||
});
|
||||
|
||||
const { error } = await withLogCollector(['error'], async () => {
|
||||
await rendered2.waitForNextUpdate();
|
||||
await act(rendered2.rerender);
|
||||
});
|
||||
|
||||
const msg =
|
||||
|
||||
+29
-29
@@ -21,7 +21,7 @@ import {
|
||||
RELATION_PART_OF,
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { filter, keyBy } from 'lodash';
|
||||
import { useEntityRelationGraph as useEntityRelationGraphMocked } from './useEntityRelationGraph';
|
||||
import { useEntityRelationNodesAndEdges } from './useEntityRelationNodesAndEdges';
|
||||
@@ -163,7 +163,7 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
});
|
||||
|
||||
test('should generate unidirectional graph with merged relations', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useEntityRelationNodesAndEdges({
|
||||
rootEntityRefs: ['b:d/c'],
|
||||
unidirectional: true,
|
||||
@@ -171,9 +171,9 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
await waitForValueToChange(
|
||||
() => result.current.nodes && result.current.edges,
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current.nodes && result.current.edges).toBeDefined();
|
||||
});
|
||||
|
||||
const { nodes, edges, loading, error } = result.current;
|
||||
|
||||
@@ -236,7 +236,7 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
});
|
||||
|
||||
test('should generate unidirectional graph', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useEntityRelationNodesAndEdges({
|
||||
rootEntityRefs: ['b:d/c'],
|
||||
unidirectional: true,
|
||||
@@ -244,9 +244,9 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
await waitForValueToChange(
|
||||
() => result.current.nodes && result.current.edges,
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current.nodes && result.current.edges).toBeDefined();
|
||||
});
|
||||
|
||||
const { nodes, edges, loading, error } = result.current;
|
||||
|
||||
@@ -309,7 +309,7 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
});
|
||||
|
||||
test('should generate bidirectional graph with merged relations', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useEntityRelationNodesAndEdges({
|
||||
rootEntityRefs: ['b:d/c'],
|
||||
unidirectional: false,
|
||||
@@ -317,9 +317,9 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
await waitForValueToChange(
|
||||
() => result.current.nodes && result.current.edges,
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current.nodes && result.current.edges).toBeDefined();
|
||||
});
|
||||
|
||||
const { nodes, edges, loading, error } = result.current;
|
||||
|
||||
@@ -412,7 +412,7 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
});
|
||||
|
||||
test('should generate bidirectional graph with all relations', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useEntityRelationNodesAndEdges({
|
||||
rootEntityRefs: ['b:d/c'],
|
||||
unidirectional: false,
|
||||
@@ -420,9 +420,9 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
}),
|
||||
);
|
||||
|
||||
await waitForValueToChange(
|
||||
() => result.current.nodes && result.current.edges,
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current.nodes && result.current.edges).toBeDefined();
|
||||
});
|
||||
|
||||
const { nodes, edges, loading, error } = result.current;
|
||||
|
||||
@@ -515,15 +515,15 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
});
|
||||
|
||||
test('should generate graph with multiple root nodes', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useEntityRelationNodesAndEdges({
|
||||
rootEntityRefs: ['b:d/c', 'b:d/c2'],
|
||||
}),
|
||||
);
|
||||
|
||||
await waitForValueToChange(
|
||||
() => result.current.nodes && result.current.edges,
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current.nodes && result.current.edges).toBeDefined();
|
||||
});
|
||||
|
||||
const { nodes, edges, loading, error } = result.current;
|
||||
|
||||
@@ -586,16 +586,16 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
});
|
||||
|
||||
test('should filter by relation', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useEntityRelationNodesAndEdges({
|
||||
rootEntityRefs: ['b:d/c'],
|
||||
relations: [RELATION_OWNER_OF],
|
||||
}),
|
||||
);
|
||||
|
||||
await waitForValueToChange(
|
||||
() => result.current.nodes && result.current.edges,
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current.nodes && result.current.edges).toBeDefined();
|
||||
});
|
||||
|
||||
const { nodes, edges, loading, error } = result.current;
|
||||
|
||||
@@ -646,16 +646,16 @@ describe('useEntityRelationNodesAndEdges', () => {
|
||||
});
|
||||
|
||||
test('should filter by kind', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useEntityRelationNodesAndEdges({
|
||||
rootEntityRefs: ['b:d/c'],
|
||||
kinds: ['b'],
|
||||
}),
|
||||
);
|
||||
|
||||
await waitForValueToChange(
|
||||
() => result.current.nodes && result.current.edges,
|
||||
);
|
||||
await waitFor(() => {
|
||||
expect(result.current.nodes && result.current.edges).toBeDefined();
|
||||
});
|
||||
|
||||
const { nodes, edges, loading, error } = result.current;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useApi as useApiMocked } from '@backstage/core-plugin-api';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { useEntityStore } from './useEntityStore';
|
||||
|
||||
jest.mock('@backstage/core-plugin-api');
|
||||
@@ -65,7 +65,7 @@ describe('useEntityStore', () => {
|
||||
|
||||
catalogApi.getEntityByRef.mockResolvedValue(entity);
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEntityStore());
|
||||
const { result } = renderHook(() => useEntityStore());
|
||||
|
||||
act(() => {
|
||||
result.current.requestEntities(['kind:namespace/name']);
|
||||
@@ -85,7 +85,7 @@ describe('useEntityStore', () => {
|
||||
const err = new Error('Hello World');
|
||||
catalogApi.getEntityByRef.mockRejectedValue(err);
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEntityStore());
|
||||
const { result } = renderHook(() => useEntityStore());
|
||||
|
||||
act(() => {
|
||||
result.current.requestEntities(['kind:namespace/name']);
|
||||
@@ -134,7 +134,7 @@ describe('useEntityStore', () => {
|
||||
|
||||
catalogApi.getEntityByRef.mockResolvedValue(entity1);
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEntityStore());
|
||||
const { result } = renderHook(() => useEntityStore());
|
||||
|
||||
act(() => {
|
||||
result.current.requestEntities(['kind:namespace/name1']);
|
||||
@@ -189,7 +189,7 @@ describe('useEntityStore', () => {
|
||||
|
||||
catalogApi.getEntityByRef.mockResolvedValue(entity1);
|
||||
|
||||
const { result, waitFor } = renderHook(() => useEntityStore());
|
||||
const { result } = renderHook(() => useEntityStore());
|
||||
|
||||
act(() => {
|
||||
result.current.requestEntities(['kind:namespace/name1']);
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useFacetsEntities } from './useFacetsEntities';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
|
||||
@@ -50,9 +50,7 @@ describe('useFacetsEntities', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() =>
|
||||
useFacetsEntities({ enabled: true }),
|
||||
);
|
||||
const { result } = renderHook(() => useFacetsEntities({ enabled: true }));
|
||||
|
||||
result.current[1]({ text: '' });
|
||||
await waitFor(() => {
|
||||
@@ -91,9 +89,7 @@ describe('useFacetsEntities', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() =>
|
||||
useFacetsEntities({ enabled: true }),
|
||||
);
|
||||
const { result } = renderHook(() => useFacetsEntities({ enabled: true }));
|
||||
|
||||
result.current[1]({ text: '' });
|
||||
await waitFor(() => {
|
||||
@@ -147,9 +143,7 @@ describe('useFacetsEntities', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() =>
|
||||
useFacetsEntities({ enabled: true }),
|
||||
);
|
||||
const { result } = renderHook(() => useFacetsEntities({ enabled: true }));
|
||||
|
||||
result.current[1]({ text: '' }, { limit: 2 });
|
||||
await waitFor(() => {
|
||||
@@ -261,9 +255,7 @@ describe('useFacetsEntities', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() =>
|
||||
useFacetsEntities({ enabled: true }),
|
||||
);
|
||||
const { result } = renderHook(() => useFacetsEntities({ enabled: true }));
|
||||
|
||||
result.current[1]({ text: 'der ' });
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { useQueryEntities } from './useQueryEntities';
|
||||
|
||||
@@ -62,7 +62,7 @@ describe('useQueryEntities', () => {
|
||||
totalItems: 2,
|
||||
});
|
||||
|
||||
const { result, waitFor } = renderHook(() => useQueryEntities());
|
||||
const { result } = renderHook(() => useQueryEntities());
|
||||
const [, fetch] = result.current!;
|
||||
fetch({ text: 'text' });
|
||||
|
||||
|
||||
+20
-47
@@ -17,12 +17,9 @@
|
||||
import { CatalogApi, Location } from '@backstage/catalog-client';
|
||||
import { Entity, ANNOTATION_ORIGIN_LOCATION } from '@backstage/catalog-model';
|
||||
import { catalogApiRef } from '../../api';
|
||||
import { act, renderHook, RenderHookResult } from '@testing-library/react';
|
||||
import React, { ReactNode } from 'react';
|
||||
import {
|
||||
UseUnregisterEntityDialogState,
|
||||
useUnregisterEntityDialogState,
|
||||
} from './useUnregisterEntityDialogState';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { useUnregisterEntityDialogState } from './useUnregisterEntityDialogState';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
function defer<T>(): { promise: Promise<T>; resolve: (value: T) => void } {
|
||||
@@ -81,26 +78,20 @@ describe('useUnregisterEntityDialogState', () => {
|
||||
});
|
||||
|
||||
it('goes through the happy unregister path', async () => {
|
||||
let rendered: RenderHookResult<
|
||||
{ children?: ReactNode },
|
||||
UseUnregisterEntityDialogState
|
||||
>;
|
||||
act(() => {
|
||||
rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
const rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
expect(rendered!.result.current).toEqual({ type: 'loading' });
|
||||
expect(rendered.result.current).toEqual({ type: 'loading' });
|
||||
|
||||
resolveLocation({ type: 'url', target: 'https://example.com', id: 'x' });
|
||||
resolveColocatedEntities([entity]);
|
||||
|
||||
await act(async () => {
|
||||
await rendered!.waitForNextUpdate();
|
||||
await rendered.rerender();
|
||||
});
|
||||
|
||||
expect(rendered!.result.current).toEqual({
|
||||
expect(rendered.result.current).toEqual({
|
||||
type: 'unregister',
|
||||
location: 'url:https://example.com',
|
||||
colocatedEntities: [{ kind: 'Component', namespace: 'ns', name: 'n' }],
|
||||
@@ -113,23 +104,17 @@ describe('useUnregisterEntityDialogState', () => {
|
||||
entity.metadata.annotations![ANNOTATION_ORIGIN_LOCATION] =
|
||||
'bootstrap:bootstrap';
|
||||
|
||||
let rendered: RenderHookResult<
|
||||
{ children?: ReactNode },
|
||||
UseUnregisterEntityDialogState
|
||||
>;
|
||||
act(() => {
|
||||
rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
const rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
resolveLocation({ type: 'bootstrap', target: 'bootstrap', id: 'x' });
|
||||
resolveColocatedEntities([]);
|
||||
await act(async () => {
|
||||
await rendered!.waitForNextUpdate();
|
||||
await rendered.rerender();
|
||||
});
|
||||
|
||||
expect(rendered!.result.current).toEqual({
|
||||
expect(rendered.result.current).toEqual({
|
||||
type: 'bootstrap',
|
||||
location: 'bootstrap:bootstrap',
|
||||
deleteEntity: expect.any(Function),
|
||||
@@ -139,46 +124,34 @@ describe('useUnregisterEntityDialogState', () => {
|
||||
it('chooses only-delete when there was no location annotation', async () => {
|
||||
delete entity.metadata.annotations![ANNOTATION_ORIGIN_LOCATION];
|
||||
|
||||
let rendered: RenderHookResult<
|
||||
{ children?: ReactNode },
|
||||
UseUnregisterEntityDialogState
|
||||
>;
|
||||
act(() => {
|
||||
rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
const rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
resolveLocation(undefined);
|
||||
resolveColocatedEntities([]);
|
||||
await act(async () => {
|
||||
await rendered!.waitForNextUpdate();
|
||||
await rendered.rerender();
|
||||
});
|
||||
|
||||
expect(rendered!.result.current).toEqual({
|
||||
expect(rendered.result.current).toEqual({
|
||||
type: 'only-delete',
|
||||
deleteEntity: expect.any(Function),
|
||||
});
|
||||
});
|
||||
|
||||
it('chooses only-delete when the location could not be found', async () => {
|
||||
let rendered: RenderHookResult<
|
||||
{ children?: ReactNode },
|
||||
UseUnregisterEntityDialogState
|
||||
>;
|
||||
act(() => {
|
||||
rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
const rendered = renderHook(() => useUnregisterEntityDialogState(entity), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
resolveLocation(undefined);
|
||||
resolveColocatedEntities([]);
|
||||
await act(async () => {
|
||||
await rendered!.waitForNextUpdate();
|
||||
await rendered.rerender();
|
||||
});
|
||||
|
||||
expect(rendered!.result.current).toEqual({
|
||||
expect(rendered.result.current).toEqual({
|
||||
type: 'only-delete',
|
||||
deleteEntity: expect.any(Function),
|
||||
});
|
||||
|
||||
@@ -31,13 +31,13 @@ const entity = { metadata: { name: 'my-entity' }, kind: 'MyKind' } as Entity;
|
||||
|
||||
describe('useEntity', () => {
|
||||
it('should throw if no entity is provided', async () => {
|
||||
const { result } = renderHook(() => useEntity(), {
|
||||
wrapper: ({ children }: React.PropsWithChildren<{}>) => (
|
||||
<EntityProvider children={children} />
|
||||
),
|
||||
});
|
||||
|
||||
expect(result.error?.message).toMatch(/entity has not been loaded/);
|
||||
expect(() =>
|
||||
renderHook(() => useEntity(), {
|
||||
wrapper: ({ children }: React.PropsWithChildren<{}>) => (
|
||||
<EntityProvider children={children} />
|
||||
),
|
||||
}),
|
||||
).toThrow(/entity has not been loaded/);
|
||||
});
|
||||
|
||||
it('should provide an entity', async () => {
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { MockStorageApi, TestApiProvider } from '@backstage/test-utils';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import qs from 'qs';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
@@ -119,10 +119,14 @@ describe('<EntityListProvider />', () => {
|
||||
});
|
||||
|
||||
it('resolves backend filters', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(() => useEntityList(), {
|
||||
const { result } = renderHook(() => useEntityList(), {
|
||||
wrapper,
|
||||
});
|
||||
await waitForValueToChange(() => result.current.backendEntities);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.backendEntities.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
expect(result.current.backendEntities.length).toBe(2);
|
||||
expect(mockCatalogApi.getEntities).toHaveBeenCalledWith({
|
||||
filter: { kind: 'component' },
|
||||
@@ -130,13 +134,16 @@ describe('<EntityListProvider />', () => {
|
||||
});
|
||||
|
||||
it('resolves frontend filters', async () => {
|
||||
const { result, waitFor } = renderHook(() => useEntityList(), {
|
||||
const { result } = renderHook(() => useEntityList(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
userFilter: 'all',
|
||||
},
|
||||
});
|
||||
await waitFor(() => !!result.current.entities.length);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.backendEntities.length).toBeGreaterThan(0);
|
||||
});
|
||||
expect(result.current.backendEntities.length).toBe(2);
|
||||
|
||||
act(() =>
|
||||
@@ -160,13 +167,15 @@ describe('<EntityListProvider />', () => {
|
||||
const query = qs.stringify({
|
||||
filters: { kind: 'component', type: 'service' },
|
||||
});
|
||||
const { result, waitFor } = renderHook(() => useEntityList(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
location: `/catalog?${query}`,
|
||||
},
|
||||
const { result } = renderHook(() => useEntityList(), {
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({ location: `/catalog?${query}`, children }),
|
||||
});
|
||||
await act(async () => {});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.queryParameters).toBeTruthy();
|
||||
});
|
||||
await act(() => waitFor(() => !!result.current.queryParameters));
|
||||
expect(result.current.queryParameters).toEqual({
|
||||
kind: 'component',
|
||||
type: 'service',
|
||||
@@ -174,7 +183,7 @@ describe('<EntityListProvider />', () => {
|
||||
});
|
||||
|
||||
it('does not fetch when only frontend filters change', async () => {
|
||||
const { result, waitFor } = renderHook(() => useEntityList(), {
|
||||
const { result } = renderHook(() => useEntityList(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
@@ -200,32 +209,34 @@ describe('<EntityListProvider />', () => {
|
||||
});
|
||||
|
||||
it('debounces multiple filter changes', async () => {
|
||||
const { result, waitForNextUpdate, waitForValueToChange } = renderHook(
|
||||
() => useEntityList(),
|
||||
{
|
||||
wrapper,
|
||||
},
|
||||
);
|
||||
await waitForValueToChange(() => result.current.backendEntities);
|
||||
const { result } = renderHook(() => useEntityList(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.backendEntities.length).toBeGreaterThan(0);
|
||||
});
|
||||
expect(result.current.backendEntities.length).toBe(2);
|
||||
expect(mockCatalogApi.getEntities).toHaveBeenCalledTimes(1);
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.updateFilters({ kind: new EntityKindFilter('component') });
|
||||
result.current.updateFilters({ type: new EntityTypeFilter('service') });
|
||||
});
|
||||
await waitForNextUpdate();
|
||||
expect(mockCatalogApi.getEntities).toHaveBeenCalledTimes(2);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockCatalogApi.getEntities).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an error on catalogApi failure', async () => {
|
||||
const { result, waitForValueToChange, waitFor } = renderHook(
|
||||
() => useEntityList(),
|
||||
{
|
||||
wrapper,
|
||||
},
|
||||
);
|
||||
await waitForValueToChange(() => result.current.backendEntities);
|
||||
const { result } = renderHook(() => useEntityList(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.backendEntities.length).toBeGreaterThan(0);
|
||||
});
|
||||
expect(result.current.backendEntities.length).toBe(2);
|
||||
|
||||
mockCatalogApi.getEntities = jest.fn().mockRejectedValue('error');
|
||||
|
||||
@@ -18,7 +18,7 @@ import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { ComponentEntity, RELATION_OWNED_BY } from '@backstage/catalog-model';
|
||||
import { IdentityApi, identityApiRef } from '@backstage/core-plugin-api';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { catalogApiRef } from '../api';
|
||||
import { useEntityOwnership } from './useEntityOwnership';
|
||||
@@ -83,19 +83,15 @@ describe('useEntityOwnership', () => {
|
||||
});
|
||||
mockCatalogApi.getEntityByRef.mockResolvedValue(undefined);
|
||||
|
||||
const { result, waitForValueToChange } = renderHook(
|
||||
() => useEntityOwnership(),
|
||||
{
|
||||
wrapper: Wrapper,
|
||||
},
|
||||
);
|
||||
const { result } = renderHook(() => useEntityOwnership(), {
|
||||
wrapper: Wrapper,
|
||||
});
|
||||
|
||||
expect(result.current.loading).toBe(true);
|
||||
expect(result.current.isOwnedEntity(ownedEntity)).toBe(false);
|
||||
|
||||
await waitForValueToChange(() => result.current.loading);
|
||||
await waitFor(() => expect(result.current.loading).toBe(false));
|
||||
|
||||
expect(result.current.loading).toBe(false);
|
||||
expect(result.current.isOwnedEntity(ownedEntity)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { WrapperComponent, renderHook } from '@testing-library/react';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import React, { ComponentType, PropsWithChildren } from 'react';
|
||||
import { catalogApiRef } from '../api';
|
||||
import { useRelatedEntities } from './useRelatedEntities';
|
||||
|
||||
@@ -50,7 +50,7 @@ describe('useRelatedEntities', () => {
|
||||
getEntitiesByRefs: jest.fn(),
|
||||
};
|
||||
|
||||
const wrapper: WrapperComponent<PropsWithChildren<{}>> = ({ children }) => {
|
||||
const wrapper: ComponentType<PropsWithChildren<{}>> = ({ children }) => {
|
||||
return (
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApi]]}>
|
||||
{children}
|
||||
@@ -70,7 +70,9 @@ describe('useRelatedEntities', () => {
|
||||
|
||||
expect(rendered.result.current).toEqual({ loading: true });
|
||||
|
||||
await rendered.waitForValueToChange(() => rendered.result.current.loading);
|
||||
await waitFor(() => {
|
||||
expect(rendered.result.current.loading).toBe(false);
|
||||
});
|
||||
|
||||
expect(catalogApi.getEntitiesByRefs).toHaveBeenCalledWith({
|
||||
entityRefs: ['group:default/the-owners-1', 'group:default/the-owners-2'],
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import {
|
||||
starredEntitiesApiRef,
|
||||
@@ -56,12 +56,11 @@ describe('useStarredEntities', () => {
|
||||
});
|
||||
|
||||
it('should return an empty set', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useStarredEntities(),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => useStarredEntities(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.starredEntities.size).toBe(0);
|
||||
});
|
||||
@@ -72,12 +71,11 @@ describe('useStarredEntities', () => {
|
||||
mockApi.toggleStarred(id);
|
||||
}
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useStarredEntities(),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => useStarredEntities(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
for (const item of expectedIds) {
|
||||
expect(result.current.starredEntities.has(item)).toBeTruthy();
|
||||
@@ -85,12 +83,11 @@ describe('useStarredEntities', () => {
|
||||
});
|
||||
|
||||
it('should listen to changes when the storage is set elsewhere', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useStarredEntities(),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => useStarredEntities(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.starredEntities.size).toBe(0);
|
||||
expect(result.current.isStarredEntity(mockEntity)).toBeFalsy();
|
||||
@@ -99,42 +96,37 @@ describe('useStarredEntities', () => {
|
||||
// catch when the hook re-renders with the latest data
|
||||
setTimeout(() => result.current.toggleStarredEntity(mockEntity), 1);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.starredEntities.size).toBe(1);
|
||||
});
|
||||
|
||||
expect(result.current.starredEntities.size).toBe(1);
|
||||
expect(result.current.isStarredEntity(mockEntity)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should write new entries to the local store when adding a toggling entity', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useStarredEntities(),
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
act(() => {
|
||||
result.current.toggleStarredEntity(mockEntity);
|
||||
const { result } = renderHook(() => useStarredEntities(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {
|
||||
result.current.toggleStarredEntity(mockEntity);
|
||||
});
|
||||
|
||||
expect(result.current.isStarredEntity(mockEntity)).toBeTruthy();
|
||||
expect(result.current.isStarredEntity(secondMockEntity)).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should remove an existing entity when toggling entries', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useStarredEntities(),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => useStarredEntities(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.toggleStarredEntity(mockEntity);
|
||||
result.current.toggleStarredEntity(secondMockEntity);
|
||||
result.current.toggleStarredEntity(mockEntity);
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(result.current.isStarredEntity(mockEntity)).toBeFalsy();
|
||||
expect(result.current.isStarredEntity(secondMockEntity)).toBeTruthy();
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
stringifyEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import useObservable from 'react-use/lib/useObservable';
|
||||
import { starredEntitiesApiRef } from '../apis';
|
||||
|
||||
@@ -45,7 +45,7 @@ export function useStarredEntities(): {
|
||||
const starredEntitiesApi = useApi(starredEntitiesApiRef);
|
||||
|
||||
const starredEntities = useObservable(
|
||||
starredEntitiesApi.starredEntitie$(),
|
||||
useMemo(() => starredEntitiesApi.starredEntitie$(), [starredEntitiesApi]),
|
||||
new Set<string>(),
|
||||
);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { Entity, CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import Observable from 'zen-observable';
|
||||
import { StarredEntitiesApi, starredEntitiesApiRef } from '../apis';
|
||||
@@ -83,18 +83,16 @@ describe('useStarredEntity', () => {
|
||||
);
|
||||
mockStarredEntitiesApi.toggleStarred.mockResolvedValue();
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useStarredEntity(entityOrRef),
|
||||
{
|
||||
wrapper,
|
||||
},
|
||||
);
|
||||
const { result } = renderHook(() => useStarredEntity(entityOrRef), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
// the initial value will always be false because the observable triggers async
|
||||
expect(result.current.isStarredEntity).toBe(false);
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(result.current.isStarredEntity).toBe(true);
|
||||
await waitFor(() => {
|
||||
expect(result.current.isStarredEntity).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+2
-4
@@ -14,7 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
import {
|
||||
mockCalverProject,
|
||||
@@ -48,10 +47,9 @@ describe('useCreateReleaseCandidate', () => {
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => result.current.run());
|
||||
await result.current.run();
|
||||
});
|
||||
|
||||
expect(result.error).toEqual(undefined);
|
||||
expect(result.current.responseSteps).toHaveLength(6);
|
||||
});
|
||||
|
||||
@@ -67,7 +65,7 @@ describe('useCreateReleaseCandidate', () => {
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => result.current.run());
|
||||
await result.current.run();
|
||||
});
|
||||
|
||||
expect(result.current.responseSteps).toHaveLength(7);
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
import {
|
||||
mockBumpedTag,
|
||||
@@ -50,10 +49,9 @@ describe('patch', () => {
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => result.current.run(mockSelectedPatchCommit));
|
||||
await result.current.run(mockSelectedPatchCommit);
|
||||
});
|
||||
|
||||
expect(result.error).toEqual(undefined);
|
||||
expect(result.current.responseSteps).toHaveLength(18);
|
||||
});
|
||||
|
||||
@@ -69,10 +67,9 @@ describe('patch', () => {
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => result.current.run(mockSelectedPatchCommit));
|
||||
await result.current.run(mockSelectedPatchCommit);
|
||||
});
|
||||
|
||||
expect(result.error).toEqual(undefined);
|
||||
expect(result.current.responseSteps).toHaveLength(19);
|
||||
expect(result.current).toMatchInlineSnapshot(`
|
||||
{
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
|
||||
import { renderHook, act } from '@testing-library/react';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
import {
|
||||
mockCalverProject,
|
||||
mockReleaseCandidateCalver,
|
||||
@@ -50,10 +48,9 @@ describe('usePromoteRc', () => {
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => result.current.run());
|
||||
await result.current.run();
|
||||
});
|
||||
|
||||
expect(result.error).toEqual(undefined);
|
||||
expect(result.current.responseSteps).toHaveLength(4);
|
||||
});
|
||||
|
||||
@@ -67,7 +64,7 @@ describe('usePromoteRc', () => {
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
await waitFor(() => result.current.run());
|
||||
await result.current.run();
|
||||
});
|
||||
|
||||
expect(result.current.responseSteps).toHaveLength(5);
|
||||
|
||||
+2
-6
@@ -165,12 +165,8 @@ describe('useConsumerGroupOffsets', () => {
|
||||
lifecycle: 'development',
|
||||
},
|
||||
};
|
||||
const { result } = subject();
|
||||
expect(() => result.current).toThrow();
|
||||
expect(result.error).toStrictEqual(
|
||||
new Error(
|
||||
`Failed to parse kafka consumer group annotation: got "dev/another,consumer"`,
|
||||
),
|
||||
expect(() => subject()).toThrow(
|
||||
`Failed to parse kafka consumer group annotation: got "dev/another,consumer"`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
+16
-15
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { when } from 'jest-when';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import {
|
||||
@@ -96,27 +96,28 @@ describe('useConsumerGroupOffsets', () => {
|
||||
.mockResolvedValue(consumerGroupOffsets);
|
||||
when(mockKafkaDashboardApi.getDashboardUrl).mockReturnValue({});
|
||||
|
||||
const { result, waitForNextUpdate } = subject();
|
||||
await waitForNextUpdate();
|
||||
const [tableProps] = result.current;
|
||||
const { result } = subject();
|
||||
|
||||
expect(tableProps.consumerGroupsTopics).toStrictEqual([
|
||||
{
|
||||
clusterId: 'prod',
|
||||
consumerGroup: consumerGroupOffsets.consumerId,
|
||||
dashboardUrl: undefined,
|
||||
topics: consumerGroupOffsets.offsets,
|
||||
},
|
||||
]);
|
||||
await waitFor(() => {
|
||||
expect(result.current[0].consumerGroupsTopics).toStrictEqual([
|
||||
{
|
||||
clusterId: 'prod',
|
||||
consumerGroup: consumerGroupOffsets.consumerId,
|
||||
dashboardUrl: undefined,
|
||||
topics: consumerGroupOffsets.offsets,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('posts an error to the error api', async () => {
|
||||
const error = new Error('error!');
|
||||
mockKafkaApi.getConsumerGroupOffsets.mockRejectedValueOnce(error);
|
||||
|
||||
const { waitForNextUpdate } = subject();
|
||||
await waitForNextUpdate();
|
||||
subject();
|
||||
|
||||
expect(mockErrorApi.post).toHaveBeenCalledWith(error);
|
||||
await waitFor(() => {
|
||||
expect(mockErrorApi.post).toHaveBeenCalledWith(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
import { useEvents } from './useEvents';
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
@@ -49,7 +49,7 @@ describe('Events', () => {
|
||||
mockGetEventsByInvolvedObjectName.mockResolvedValue(response),
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useEvents({
|
||||
involvedObjectName: 'some-objecgt',
|
||||
namespace: 'some-namespace',
|
||||
@@ -59,7 +59,9 @@ describe('Events', () => {
|
||||
|
||||
expect(result.current.loading).toEqual(true);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toEqual(false);
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { useCustomResources } from './useCustomResources';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { CustomResourceMatcher } from '@backstage/plugin-kubernetes-common';
|
||||
import { generateAuth } from './auth';
|
||||
@@ -97,13 +97,13 @@ describe('useCustomResources', () => {
|
||||
getCustomObjectsByEntity:
|
||||
mockGetCustomObjectsByEntity.mockResolvedValue(mockResponse),
|
||||
});
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useCustomResources(entity, customResourceMatchers),
|
||||
);
|
||||
|
||||
expect(result.current.loading).toEqual(true);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -117,20 +117,20 @@ describe('useCustomResources', () => {
|
||||
getCustomObjectsByEntity:
|
||||
mockGetCustomObjectsByEntity.mockResolvedValue(mockResponse),
|
||||
});
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useCustomResources(entity, customResourceMatchers, 100),
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
expect(result.current.error).toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expectMocksCalledCorrectly(2);
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.kubernetesObjects).toStrictEqual(mockResponse);
|
||||
|
||||
expectMocksCalledCorrectly(2);
|
||||
});
|
||||
it('should return error when getObjectsByEntity throws', async () => {
|
||||
mockGenerateAuth.mockResolvedValue(entityWithAuthToken.auth);
|
||||
@@ -139,11 +139,11 @@ describe('useCustomResources', () => {
|
||||
message: 'some error',
|
||||
}),
|
||||
});
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useCustomResources(entity, customResourceMatchers),
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBe('some error');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -162,17 +162,19 @@ describe('useCustomResources', () => {
|
||||
mockGetCustomObjectsByEntity.mockResolvedValue(mockResponse),
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useCustomResources(entity, customResourceMatchers, 100),
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBe('generateAuth failed');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.kubernetesObjects).toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.error).toBeUndefined();
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -186,17 +188,19 @@ describe('useCustomResources', () => {
|
||||
.mockResolvedValue(mockResponse),
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useCustomResources(entity, customResourceMatchers, 100),
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBe('failed to fetch');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.kubernetesObjects).toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.error).toBeUndefined();
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -212,17 +216,19 @@ describe('useCustomResources', () => {
|
||||
mockGetCustomObjectsByEntity.mockResolvedValue(mockResponse),
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useCustomResources(entity, customResourceMatchers, 100),
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.kubernetesObjects).not.toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.error).toBeDefined();
|
||||
});
|
||||
|
||||
expect(result.current.error).toBe('generateAuth failed');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -236,17 +242,19 @@ describe('useCustomResources', () => {
|
||||
.mockRejectedValue({ message: 'failed to fetch' }),
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useCustomResources(entity, customResourceMatchers, 100),
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.kubernetesObjects).not.toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.error).toBeDefined();
|
||||
});
|
||||
|
||||
expect(result.current.error).toBe('failed to fetch');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { renderHook, waitFor } from '@testing-library/react';
|
||||
|
||||
import { useIsPodExecTerminalSupported } from './useIsPodExecTerminalSupported';
|
||||
|
||||
@@ -68,15 +68,14 @@ describe('useIsClusterShellEnabled', () => {
|
||||
async ({ testClusters, returnValue }) => {
|
||||
clusters = testClusters;
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useIsPodExecTerminalSupported(),
|
||||
);
|
||||
const { result } = renderHook(() => useIsPodExecTerminalSupported());
|
||||
|
||||
expect(result.current.loading).toEqual(true);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toEqual(false);
|
||||
});
|
||||
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.value).toBe(returnValue);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { useKubernetesObjects } from './useKubernetesObjects';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { generateAuth } from './auth';
|
||||
|
||||
@@ -87,13 +87,13 @@ describe('useKubernetesObjects', () => {
|
||||
getObjectsByEntity:
|
||||
mockGetObjectsByEntity.mockResolvedValue(mockResponse),
|
||||
});
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useKubernetesObjects(entity),
|
||||
);
|
||||
const { result } = renderHook(() => useKubernetesObjects(entity));
|
||||
|
||||
expect(result.current.loading).toEqual(true);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.loading).toEqual(false);
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -107,14 +107,13 @@ describe('useKubernetesObjects', () => {
|
||||
getObjectsByEntity:
|
||||
mockGetObjectsByEntity.mockResolvedValue(mockResponse),
|
||||
});
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useKubernetesObjects(entity, 100),
|
||||
);
|
||||
const { result } = renderHook(() => useKubernetesObjects(entity, 100));
|
||||
|
||||
await waitForNextUpdate();
|
||||
expect(result.current.error).toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expectMocksCalledCorrectly(2);
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -129,11 +128,9 @@ describe('useKubernetesObjects', () => {
|
||||
message: 'some error',
|
||||
}),
|
||||
});
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useKubernetesObjects(entity),
|
||||
);
|
||||
const { result } = renderHook(() => useKubernetesObjects(entity));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBe('some error');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -152,17 +149,17 @@ describe('useKubernetesObjects', () => {
|
||||
mockGetObjectsByEntity.mockResolvedValue(mockResponse),
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useKubernetesObjects(entity, 100),
|
||||
);
|
||||
const { result } = renderHook(() => useKubernetesObjects(entity, 100));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBe('generateAuth failed');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.kubernetesObjects).toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.error).toBeUndefined();
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -176,17 +173,17 @@ describe('useKubernetesObjects', () => {
|
||||
.mockResolvedValue(mockResponse),
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useKubernetesObjects(entity, 100),
|
||||
);
|
||||
const { result } = renderHook(() => useKubernetesObjects(entity, 100));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBe('failed to fetch');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.kubernetesObjects).toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.error).toBeUndefined();
|
||||
});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -202,17 +199,17 @@ describe('useKubernetesObjects', () => {
|
||||
mockGetObjectsByEntity.mockResolvedValue(mockResponse),
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useKubernetesObjects(entity, 100),
|
||||
);
|
||||
const { result } = renderHook(() => useKubernetesObjects(entity, 100));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.kubernetesObjects).not.toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.error).toBeDefined();
|
||||
});
|
||||
|
||||
expect(result.current.error).toBe('generateAuth failed');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
@@ -226,17 +223,17 @@ describe('useKubernetesObjects', () => {
|
||||
.mockRejectedValue({ message: 'failed to fetch' }),
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
useKubernetesObjects(entity, 100),
|
||||
);
|
||||
const { result } = renderHook(() => useKubernetesObjects(entity, 100));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.error).toBeUndefined();
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.kubernetesObjects).not.toBeUndefined();
|
||||
|
||||
await waitForNextUpdate();
|
||||
await waitFor(() => {
|
||||
expect(result.current.error).toBeDefined();
|
||||
});
|
||||
|
||||
expect(result.current.error).toBe('failed to fetch');
|
||||
expect(result.current.loading).toEqual(false);
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { WebsiteListResponse } from '@backstage/plugin-lighthouse-common';
|
||||
import { lighthouseApiRef } from '../api';
|
||||
@@ -77,8 +77,8 @@ describe('useWebsiteForEntity', () => {
|
||||
});
|
||||
|
||||
it('returns the lighthouse information for the website url in annotations', async () => {
|
||||
const { result, waitForNextUpdate } = subject();
|
||||
await waitForNextUpdate();
|
||||
const { result } = subject();
|
||||
await act(async () => {});
|
||||
expect(result.current?.value).toBe(website);
|
||||
});
|
||||
|
||||
@@ -92,8 +92,8 @@ describe('useWebsiteForEntity', () => {
|
||||
});
|
||||
|
||||
it('posts the error to the error api and returns the error to the caller', async () => {
|
||||
const { result, waitForNextUpdate } = subject();
|
||||
await waitForNextUpdate();
|
||||
const { result } = subject();
|
||||
await act(async () => {});
|
||||
expect(result.current?.error).toBe(error);
|
||||
expect(mockErrorApi.post).toHaveBeenCalledWith(error);
|
||||
});
|
||||
@@ -109,8 +109,8 @@ describe('useWebsiteForEntity', () => {
|
||||
});
|
||||
|
||||
it('does not post the error to the error api and returns the error to the caller', async () => {
|
||||
const { result, waitForNextUpdate } = subject();
|
||||
await waitForNextUpdate();
|
||||
const { result } = subject();
|
||||
await act(async () => {});
|
||||
expect(result.current?.error).toBe(error);
|
||||
expect(mockErrorApi.post).not.toHaveBeenCalledWith(error);
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import { CompoundEntityRef, Entity } from '@backstage/catalog-model';
|
||||
import { useGetEntities } from './useGetEntities';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { getEntityRelations } from '@backstage/plugin-catalog-react';
|
||||
|
||||
const givenParentGroup = 'team.squad1';
|
||||
@@ -66,14 +66,11 @@ describe('useGetEntities', () => {
|
||||
|
||||
describe('given aggregated relationsType', () => {
|
||||
const whenHookIsCalledWith = async (_entity: Entity) => {
|
||||
const hook = renderHook(
|
||||
({ entity }) => useGetEntities(entity, 'aggregated'),
|
||||
{
|
||||
initialProps: { entity: _entity },
|
||||
},
|
||||
);
|
||||
renderHook(({ entity }) => useGetEntities(entity, 'aggregated'), {
|
||||
initialProps: { entity: _entity },
|
||||
});
|
||||
|
||||
await hook.waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -207,14 +204,11 @@ describe('useGetEntities', () => {
|
||||
|
||||
describe('given direct relationsType', () => {
|
||||
const whenHookIsCalledWith = async (_entity: Entity) => {
|
||||
const hook = renderHook(
|
||||
({ entity }) => useGetEntities(entity, 'direct'),
|
||||
{
|
||||
initialProps: { entity: _entity },
|
||||
},
|
||||
);
|
||||
renderHook(({ entity }) => useGetEntities(entity, 'direct'), {
|
||||
initialProps: { entity: _entity },
|
||||
});
|
||||
|
||||
await hook.waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
};
|
||||
|
||||
it('given group entity should return directly owned entities', async () => {
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { Playlist } from '@backstage/plugin-playlist-common';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import qs from 'qs';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
@@ -114,23 +114,22 @@ describe('<PlaylistListProvider />', () => {
|
||||
});
|
||||
|
||||
it('resolves backend filters', async () => {
|
||||
const { result, waitForValueToChange } = renderHook(
|
||||
() => usePlaylistList(),
|
||||
{
|
||||
wrapper,
|
||||
},
|
||||
);
|
||||
await waitForValueToChange(() => result.current.backendPlaylists);
|
||||
expect(result.current.backendPlaylists.length).toBe(2);
|
||||
const { result } = renderHook(() => usePlaylistList(), {
|
||||
wrapper,
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(result.current.backendPlaylists.length).toBe(2);
|
||||
});
|
||||
expect(mockPlaylistApi.getAllPlaylists).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('resolves frontend filters', async () => {
|
||||
const { result, waitFor } = renderHook(() => usePlaylistList(), {
|
||||
const { result } = renderHook(() => usePlaylistList(), {
|
||||
wrapper,
|
||||
});
|
||||
await waitFor(() => !!result.current.playlists.length);
|
||||
expect(result.current.backendPlaylists.length).toBe(2);
|
||||
await waitFor(() => {
|
||||
expect(result.current.backendPlaylists.length).toBe(2);
|
||||
});
|
||||
|
||||
act(() =>
|
||||
result.current.updateFilters({
|
||||
@@ -152,13 +151,15 @@ describe('<PlaylistListProvider />', () => {
|
||||
const query = qs.stringify({
|
||||
filters: { personal: 'all', owners: ['user:default/guest'] },
|
||||
});
|
||||
const { result, waitFor } = renderHook(() => usePlaylistList(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
location: `/playlist?${query}`,
|
||||
},
|
||||
const { result } = renderHook(() => usePlaylistList(), {
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({ location: `/playlist?${query}`, children }),
|
||||
});
|
||||
await act(async () => {});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.queryParameters).toBeTruthy();
|
||||
});
|
||||
await waitFor(() => !!result.current.queryParameters);
|
||||
expect(result.current.queryParameters).toEqual({
|
||||
personal: 'all',
|
||||
owners: ['user:default/guest'],
|
||||
@@ -166,7 +167,7 @@ describe('<PlaylistListProvider />', () => {
|
||||
});
|
||||
|
||||
it('does not fetch when only frontend filters change', async () => {
|
||||
const { result, waitFor } = renderHook(() => usePlaylistList(), {
|
||||
const { result } = renderHook(() => usePlaylistList(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
@@ -191,7 +192,7 @@ describe('<PlaylistListProvider />', () => {
|
||||
});
|
||||
|
||||
it('applies custom sorting', async () => {
|
||||
const { result, waitFor } = renderHook(() => usePlaylistList(), {
|
||||
const { result } = renderHook(() => usePlaylistList(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
@@ -216,7 +217,7 @@ describe('<PlaylistListProvider />', () => {
|
||||
|
||||
it('returns an error on playlistApi failure', async () => {
|
||||
mockPlaylistApi.getAllPlaylists = jest.fn().mockRejectedValue('error');
|
||||
const { result, waitFor } = renderHook(() => usePlaylistList(), {
|
||||
const { result } = renderHook(() => usePlaylistList(), {
|
||||
wrapper,
|
||||
});
|
||||
await waitFor(() => {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
import React from 'react';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import { MockConfigApi, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
|
||||
import { searchApiRef } from '../../api';
|
||||
import { SearchContextProvider, useSearch } from '../../context';
|
||||
@@ -67,7 +67,7 @@ describe('SearchFilter.hooks', () => {
|
||||
it('should set non-empty string value', async () => {
|
||||
const expectedFilter = 'someField';
|
||||
const expectedValue = 'someValue';
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
useDefaultFilterValue(expectedFilter, expectedValue);
|
||||
return useSearch();
|
||||
@@ -77,7 +77,7 @@ describe('SearchFilter.hooks', () => {
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.filters[expectedFilter]).toEqual(expectedValue);
|
||||
});
|
||||
@@ -85,7 +85,7 @@ describe('SearchFilter.hooks', () => {
|
||||
it('should set non-empty array value', async () => {
|
||||
const expectedFilter = 'someField';
|
||||
const expectedValue = ['someValue', 'anotherValue'];
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
useDefaultFilterValue(expectedFilter, expectedValue);
|
||||
return useSearch();
|
||||
@@ -95,7 +95,7 @@ describe('SearchFilter.hooks', () => {
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.filters[expectedFilter]).toEqual(expectedValue);
|
||||
});
|
||||
@@ -103,24 +103,25 @@ describe('SearchFilter.hooks', () => {
|
||||
it('should not set undefined value', async () => {
|
||||
const expectedFilter = 'someField';
|
||||
const expectedValue = 'notEmpty';
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
useDefaultFilterValue(expectedFilter, undefined);
|
||||
return useSearch();
|
||||
},
|
||||
{
|
||||
wrapper,
|
||||
initialProps: {
|
||||
overrides: {
|
||||
filters: {
|
||||
[expectedFilter]: expectedValue,
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
overrides: {
|
||||
filters: {
|
||||
[expectedFilter]: expectedValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.filters[expectedFilter]).toEqual(expectedValue);
|
||||
});
|
||||
@@ -128,24 +129,25 @@ describe('SearchFilter.hooks', () => {
|
||||
it('should not set null value', async () => {
|
||||
const expectedFilter = 'someField';
|
||||
const expectedValue = 'notEmpty';
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
useDefaultFilterValue(expectedFilter, null);
|
||||
return useSearch();
|
||||
},
|
||||
{
|
||||
wrapper,
|
||||
initialProps: {
|
||||
overrides: {
|
||||
filters: {
|
||||
[expectedFilter]: expectedValue,
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
overrides: {
|
||||
filters: {
|
||||
[expectedFilter]: expectedValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.filters[expectedFilter]).toEqual(expectedValue);
|
||||
});
|
||||
@@ -153,24 +155,25 @@ describe('SearchFilter.hooks', () => {
|
||||
it('should not set empty string value', async () => {
|
||||
const expectedFilter = 'someField';
|
||||
const expectedValue = 'notEmpty';
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
useDefaultFilterValue(expectedFilter, '');
|
||||
return useSearch();
|
||||
},
|
||||
{
|
||||
wrapper,
|
||||
initialProps: {
|
||||
overrides: {
|
||||
filters: {
|
||||
[expectedFilter]: expectedValue,
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
overrides: {
|
||||
filters: {
|
||||
[expectedFilter]: expectedValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.filters[expectedFilter]).toEqual(expectedValue);
|
||||
});
|
||||
@@ -178,24 +181,25 @@ describe('SearchFilter.hooks', () => {
|
||||
it('should not set empty array value', async () => {
|
||||
const expectedFilter = 'someField';
|
||||
const expectedValue = ['not', 'empty'];
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
useDefaultFilterValue(expectedFilter, []);
|
||||
return useSearch();
|
||||
},
|
||||
{
|
||||
wrapper,
|
||||
initialProps: {
|
||||
overrides: {
|
||||
filters: {
|
||||
[expectedFilter]: expectedValue,
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
overrides: {
|
||||
filters: {
|
||||
[expectedFilter]: expectedValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.filters[expectedFilter]).toEqual(expectedValue);
|
||||
});
|
||||
@@ -203,24 +207,25 @@ describe('SearchFilter.hooks', () => {
|
||||
it('should not affect unrelated filters', async () => {
|
||||
const expectedFilter = 'someField';
|
||||
const expectedValue = 'someValue';
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
const { result } = renderHook(
|
||||
() => {
|
||||
useDefaultFilterValue(expectedFilter, expectedValue);
|
||||
return useSearch();
|
||||
},
|
||||
{
|
||||
wrapper,
|
||||
initialProps: {
|
||||
overrides: {
|
||||
filters: {
|
||||
unrelatedField: 'unrelatedValue',
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
overrides: {
|
||||
filters: {
|
||||
unrelatedField: 'unrelatedValue',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.filters.unrelatedField).toEqual('unrelatedValue');
|
||||
});
|
||||
@@ -240,14 +245,15 @@ describe('SearchFilter.hooks', () => {
|
||||
it('should return resolved values of provided async function', async () => {
|
||||
const expectedValues = ['value1', 'value2'];
|
||||
const asyncFn = () => Promise.resolve(expectedValues);
|
||||
const { result, waitForNextUpdate } = renderHook(() =>
|
||||
const { result } = renderHook(() =>
|
||||
useAsyncFilterValues(asyncFn, '', undefined, 1000),
|
||||
);
|
||||
|
||||
expect(result.current.loading).toEqual(true);
|
||||
|
||||
jest.runAllTimers();
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
|
||||
expect(result.current.loading).toEqual(false);
|
||||
expect(result.current.value).toEqual(expectedValues);
|
||||
@@ -261,11 +267,15 @@ describe('SearchFilter.hooks', () => {
|
||||
expect(asyncFn).not.toHaveBeenCalled();
|
||||
|
||||
// Advance timers by 600ms
|
||||
jest.advanceTimersByTime(600);
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(600);
|
||||
});
|
||||
expect(asyncFn).not.toHaveBeenCalled();
|
||||
|
||||
// Another 600ms to exceed the 1000ms debounce
|
||||
jest.advanceTimersByTime(600);
|
||||
await act(async () => {
|
||||
jest.advanceTimersByTime(600);
|
||||
});
|
||||
expect(asyncFn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -273,21 +283,23 @@ describe('SearchFilter.hooks', () => {
|
||||
const asyncFn = jest
|
||||
.fn()
|
||||
.mockImplementation((x: string) => Promise.resolve([x]));
|
||||
const { rerender, waitForNextUpdate } = renderHook(
|
||||
const { rerender } = renderHook(
|
||||
(props: { inputValue: string } = { inputValue: '' }) =>
|
||||
useAsyncFilterValues(asyncFn, props.inputValue, undefined, 1000),
|
||||
);
|
||||
|
||||
expect(asyncFn).not.toHaveBeenCalled();
|
||||
jest.runAllTimers();
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(asyncFn).toHaveBeenCalledTimes(1);
|
||||
expect(asyncFn).toHaveBeenCalledWith('');
|
||||
|
||||
// Re-render with different input value.
|
||||
rerender({ inputValue: 'somethingElse' });
|
||||
jest.runAllTimers();
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(asyncFn).toHaveBeenCalledTimes(2);
|
||||
expect(asyncFn).toHaveBeenLastCalledWith('somethingElse');
|
||||
});
|
||||
@@ -295,15 +307,16 @@ describe('SearchFilter.hooks', () => {
|
||||
it('should not call provided method more than once when re-rendered with same input', async () => {
|
||||
const expectedValues = ['value1', 'value2'];
|
||||
const asyncFn = jest.fn().mockResolvedValue(expectedValues);
|
||||
const { rerender, waitForNextUpdate } = renderHook(
|
||||
const { rerender } = renderHook(
|
||||
(props: { inputValue: string } = { inputValue: '' }) =>
|
||||
useAsyncFilterValues(asyncFn, props.inputValue, undefined, 1000),
|
||||
);
|
||||
|
||||
expect(asyncFn).not.toHaveBeenCalled();
|
||||
|
||||
jest.runAllTimers();
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {
|
||||
jest.runAllTimers();
|
||||
});
|
||||
expect(asyncFn).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Re-render multiple times with the same input.
|
||||
|
||||
@@ -15,8 +15,13 @@
|
||||
*/
|
||||
|
||||
import { configApiRef } from '@backstage/core-plugin-api';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import {
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
act,
|
||||
renderHook,
|
||||
} from '@testing-library/react';
|
||||
import { MockConfigApi, TestApiProvider } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import {
|
||||
@@ -70,10 +75,8 @@ describe('SearchContext', () => {
|
||||
});
|
||||
|
||||
it('Throws error when no context is set', () => {
|
||||
const { result } = renderHook(() => useSearch());
|
||||
|
||||
expect(result.error).toEqual(
|
||||
Error('useSearch must be used within a SearchContextProvider'),
|
||||
expect(() => renderHook(() => useSearch())).toThrow(
|
||||
'useSearch must be used within a SearchContextProvider',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -82,28 +85,22 @@ describe('SearchContext', () => {
|
||||
|
||||
expect(hook.result.current).toEqual(false);
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useSearchContextCheck(),
|
||||
{
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
},
|
||||
},
|
||||
);
|
||||
const { result } = renderHook(() => useSearchContextCheck(), {
|
||||
wrapper: ({ children }) => wrapper({ children, initialState }),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current).toEqual(true);
|
||||
});
|
||||
|
||||
describe('Uses initial state values', () => {
|
||||
it('Uses default initial state values', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -117,34 +114,32 @@ describe('SearchContext', () => {
|
||||
});
|
||||
|
||||
it('Uses provided initial state values', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) => wrapper({ children, initialState }),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current).toEqual(expect.objectContaining(initialState));
|
||||
});
|
||||
|
||||
it('Uses page limit provided via config api', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
config: {
|
||||
search: {
|
||||
query: {
|
||||
pageLimit: 100,
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
initialState,
|
||||
config: {
|
||||
search: {
|
||||
query: {
|
||||
pageLimit: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current).toEqual(
|
||||
expect.objectContaining({ ...initialState, pageLimit: 100 }),
|
||||
@@ -154,131 +149,122 @@ describe('SearchContext', () => {
|
||||
|
||||
describe('Resets cursor', () => {
|
||||
it('When term is cleared', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState: {
|
||||
...initialState,
|
||||
term: 'first term',
|
||||
pageCursor: 'SOMEPAGE',
|
||||
},
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
initialState: {
|
||||
...initialState,
|
||||
term: 'first term',
|
||||
pageCursor: 'SOMEPAGE',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.term).toEqual('first term');
|
||||
expect(result.current.pageCursor).toEqual('SOMEPAGE');
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.setTerm('');
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(result.current.pageCursor).toBeUndefined();
|
||||
});
|
||||
|
||||
it('When term is set (and different from previous)', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState: {
|
||||
...initialState,
|
||||
term: 'first term',
|
||||
pageCursor: 'SOMEPAGE',
|
||||
},
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
initialState: {
|
||||
...initialState,
|
||||
term: 'first term',
|
||||
pageCursor: 'SOMEPAGE',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.term).toEqual('first term');
|
||||
expect(result.current.pageCursor).toEqual('SOMEPAGE');
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.setTerm('second term');
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(result.current.pageCursor).toBeUndefined();
|
||||
});
|
||||
|
||||
it('When filters are cleared', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState: {
|
||||
...initialState,
|
||||
term: 'first term',
|
||||
filters: { foo: 'bar' },
|
||||
pageCursor: 'SOMEPAGE',
|
||||
},
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
initialState: {
|
||||
...initialState,
|
||||
term: 'first term',
|
||||
filters: { foo: 'bar' },
|
||||
pageCursor: 'SOMEPAGE',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.filters).toEqual({ foo: 'bar' });
|
||||
expect(result.current.pageCursor).toEqual('SOMEPAGE');
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.setFilters({});
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(result.current.pageCursor).toBeUndefined();
|
||||
});
|
||||
|
||||
it('When filters are set (and different from previous)', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState: {
|
||||
...initialState,
|
||||
term: 'first term',
|
||||
filters: { foo: 'bar' },
|
||||
pageCursor: 'SOMEPAGE',
|
||||
},
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
initialState: {
|
||||
...initialState,
|
||||
term: 'first term',
|
||||
filters: { foo: 'bar' },
|
||||
pageCursor: 'SOMEPAGE',
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.filters).toEqual({ foo: 'bar' });
|
||||
expect(result.current.pageCursor).toEqual('SOMEPAGE');
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.setFilters({ foo: 'test' });
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(result.current.pageCursor).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Performs search (and sets results)', () => {
|
||||
it('When term is set', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) => wrapper({ children, initialState }),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
const term = 'term';
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.setTerm(term);
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(searchApiMock.query).toHaveBeenLastCalledWith({
|
||||
term,
|
||||
types: ['*'],
|
||||
@@ -287,23 +273,18 @@ describe('SearchContext', () => {
|
||||
});
|
||||
|
||||
it('When types is set', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) => wrapper({ children, initialState }),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
const types = ['type'];
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.setTypes(types);
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(searchApiMock.query).toHaveBeenLastCalledWith({
|
||||
types,
|
||||
term: '',
|
||||
@@ -312,23 +293,18 @@ describe('SearchContext', () => {
|
||||
});
|
||||
|
||||
it('When filters are set', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) => wrapper({ children, initialState }),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
const filters = { filter: 'filter' };
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.setFilters(filters);
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(searchApiMock.query).toHaveBeenLastCalledWith({
|
||||
filters,
|
||||
term: '',
|
||||
@@ -337,23 +313,18 @@ describe('SearchContext', () => {
|
||||
});
|
||||
|
||||
it('When page limit is set', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) => wrapper({ children, initialState }),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
const pageLimit = 30;
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.setPageLimit(pageLimit);
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(searchApiMock.query).toHaveBeenLastCalledWith({
|
||||
pageLimit,
|
||||
term: '',
|
||||
@@ -363,23 +334,18 @@ describe('SearchContext', () => {
|
||||
});
|
||||
|
||||
it('When page cursor is set', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) => wrapper({ children, initialState }),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
const pageCursor = 'SOMEPAGE';
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.setPageCursor(pageCursor);
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(searchApiMock.query).toHaveBeenLastCalledWith({
|
||||
pageCursor,
|
||||
term: '',
|
||||
@@ -394,24 +360,19 @@ describe('SearchContext', () => {
|
||||
nextPageCursor: 'NEXT',
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) => wrapper({ children, initialState }),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.fetchNextPage).toBeDefined();
|
||||
expect(result.current.fetchPreviousPage).toBeUndefined();
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.fetchNextPage!();
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(searchApiMock.query).toHaveBeenLastCalledWith({
|
||||
term: '',
|
||||
types: ['*'],
|
||||
@@ -426,24 +387,19 @@ describe('SearchContext', () => {
|
||||
previousPageCursor: 'PREVIOUS',
|
||||
});
|
||||
|
||||
const { result, waitForNextUpdate } = renderHook(() => useSearch(), {
|
||||
wrapper,
|
||||
initialProps: {
|
||||
initialState,
|
||||
},
|
||||
const { result } = renderHook(() => useSearch(), {
|
||||
wrapper: ({ children }) => wrapper({ children, initialState }),
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.fetchNextPage).toBeUndefined();
|
||||
expect(result.current.fetchPreviousPage).toBeDefined();
|
||||
|
||||
act(() => {
|
||||
await act(async () => {
|
||||
result.current.fetchPreviousPage!();
|
||||
});
|
||||
|
||||
await waitForNextUpdate();
|
||||
|
||||
expect(searchApiMock.query).toHaveBeenLastCalledWith({
|
||||
term: '',
|
||||
types: ['*'],
|
||||
|
||||
@@ -19,7 +19,11 @@ import { renderHook, act } from '@testing-library/react';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { MockAnalyticsApi, TestApiProvider } from '@backstage/test-utils';
|
||||
import {
|
||||
MockAnalyticsApi,
|
||||
MockConfigApi,
|
||||
TestApiProvider,
|
||||
} from '@backstage/test-utils';
|
||||
import { Entity, CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import {
|
||||
analyticsApiRef,
|
||||
@@ -30,6 +34,7 @@ import {
|
||||
import { techdocsApiRef } from './api';
|
||||
import { useTechDocsReaderPage, TechDocsReaderPageProvider } from './context';
|
||||
import { TechDocsMetadata } from './types';
|
||||
import { JsonObject } from '@backstage/config';
|
||||
|
||||
const mockShadowRoot = () => {
|
||||
const div = document.createElement('div');
|
||||
@@ -60,10 +65,6 @@ const techdocsApiMock = {
|
||||
getTechDocsMetadata: jest.fn().mockResolvedValue(mockTechDocsMetadata),
|
||||
};
|
||||
|
||||
const configApiMock = {
|
||||
getOptionalBoolean: jest.fn().mockReturnValue(undefined),
|
||||
};
|
||||
|
||||
const analyticsApiMock = new MockAnalyticsApi();
|
||||
|
||||
const wrapper = ({
|
||||
@@ -72,16 +73,18 @@ const wrapper = ({
|
||||
name: mockEntityMetadata.metadata.name,
|
||||
namespace: mockEntityMetadata.metadata.namespace!!,
|
||||
},
|
||||
config,
|
||||
children,
|
||||
}: {
|
||||
entityRef?: CompoundEntityRef;
|
||||
config?: JsonObject;
|
||||
children: React.ReactNode;
|
||||
}) => (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[analyticsApiRef, analyticsApiMock],
|
||||
[configApiRef, configApiMock],
|
||||
[configApiRef, new MockConfigApi(config ?? {})],
|
||||
[techdocsApiRef, techdocsApiMock],
|
||||
]}
|
||||
>
|
||||
@@ -98,47 +101,32 @@ describe('useTechDocsReaderPage', () => {
|
||||
});
|
||||
|
||||
it('should set title', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useTechDocsReaderPage(),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => useTechDocsReaderPage(), { wrapper });
|
||||
|
||||
expect(result.current.title).toBe('');
|
||||
|
||||
act(() => result.current.setTitle('test site title'));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => result.current.setTitle('test site title'));
|
||||
|
||||
expect(result.current.title).toBe('test site title');
|
||||
});
|
||||
|
||||
it('should set subtitle', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useTechDocsReaderPage(),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => useTechDocsReaderPage(), { wrapper });
|
||||
|
||||
expect(result.current.subtitle).toBe('');
|
||||
|
||||
act(() => result.current.setSubtitle('test site subtitle'));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => result.current.setSubtitle('test site subtitle'));
|
||||
|
||||
expect(result.current.subtitle).toBe('test site subtitle');
|
||||
});
|
||||
|
||||
it('should set shadow root', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useTechDocsReaderPage(),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => useTechDocsReaderPage(), { wrapper });
|
||||
|
||||
// mock shadowroot
|
||||
const shadowRoot = mockShadowRoot();
|
||||
|
||||
act(() => result.current.setShadowRoot(shadowRoot));
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => result.current.setShadowRoot(shadowRoot));
|
||||
|
||||
expect(result.current.shadowRoot?.innerHTML).toBe(
|
||||
'<h1>Shadow DOM Mock</h1>',
|
||||
@@ -152,30 +140,35 @@ describe('useTechDocsReaderPage', () => {
|
||||
namespace: mockEntityMetadata.metadata.namespace?.toLocaleLowerCase(),
|
||||
};
|
||||
const { result } = renderHook(() => useTechDocsReaderPage(), { wrapper });
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.entityRef).toStrictEqual(lowercaseEntityRef);
|
||||
});
|
||||
|
||||
it('entityRef is not modified when legacyUseCaseSensitiveTripletPaths is true', async () => {
|
||||
configApiMock.getOptionalBoolean.mockReturnValueOnce(true);
|
||||
const caseSensitiveEntityRef = {
|
||||
kind: mockEntityMetadata.kind,
|
||||
name: mockEntityMetadata.metadata.name,
|
||||
namespace: mockEntityMetadata.metadata.namespace!!,
|
||||
};
|
||||
|
||||
const { result } = renderHook(() => useTechDocsReaderPage(), { wrapper });
|
||||
const { result } = renderHook(() => useTechDocsReaderPage(), {
|
||||
wrapper: ({ children }) =>
|
||||
wrapper({
|
||||
children,
|
||||
config: { techdocs: { legacyUseCaseSensitiveTripletPaths: true } },
|
||||
}),
|
||||
});
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.entityRef).toStrictEqual(caseSensitiveEntityRef);
|
||||
});
|
||||
|
||||
it('entityRef provided as analytics context', async () => {
|
||||
const { waitForNextUpdate } = renderHook(
|
||||
() => useAnalytics().captureEvent('action', 'subject'),
|
||||
{ wrapper },
|
||||
);
|
||||
|
||||
await waitForNextUpdate();
|
||||
renderHook(() => useAnalytics().captureEvent('action', 'subject'), {
|
||||
wrapper,
|
||||
});
|
||||
await act(async () => {});
|
||||
|
||||
expect(analyticsApiMock.getEvents()[0]).toMatchObject({
|
||||
action: 'action',
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
|
||||
@@ -95,12 +95,9 @@ describe('context', () => {
|
||||
});
|
||||
|
||||
it('should return expected entity values', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useEntityMetadata(),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => useEntityMetadata(), { wrapper });
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.value).toBeDefined();
|
||||
expect(result.current.error).toBeUndefined();
|
||||
@@ -116,12 +113,9 @@ describe('context', () => {
|
||||
});
|
||||
|
||||
it('should return expected techdocs metadata values', async () => {
|
||||
const { result, waitForNextUpdate } = renderHook(
|
||||
() => useTechDocsMetadata(),
|
||||
{ wrapper },
|
||||
);
|
||||
const { result } = renderHook(() => useTechDocsMetadata(), { wrapper });
|
||||
|
||||
await waitForNextUpdate();
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current.value).toBeDefined();
|
||||
expect(result.current.error).toBeUndefined();
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { NotFoundError } from '@backstage/errors';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
import { act, renderHook } from '@testing-library/react';
|
||||
import { act, renderHook, waitFor } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { techdocsStorageApiRef } from '../../api';
|
||||
import {
|
||||
@@ -284,47 +284,45 @@ describe('useReaderState', () => {
|
||||
return 'cached';
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
const { result, waitForValueToChange } = await renderHook(
|
||||
() => useReaderState('Component', 'default', 'backstage', '/example'),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
const { result } = renderHook(
|
||||
() => useReaderState('Component', 'default', 'backstage', '/example'),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await act(async () => {});
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('should reload initially missing content', async () => {
|
||||
@@ -336,6 +334,7 @@ describe('useReaderState', () => {
|
||||
});
|
||||
techdocsStorageApi.syncEntityDocs.mockImplementation(
|
||||
async (_, logHandler) => {
|
||||
await 'a tick';
|
||||
logHandler?.call(this, 'Line 1');
|
||||
logHandler?.call(this, 'Line 2');
|
||||
await new Promise(resolve => setTimeout(resolve, 1100));
|
||||
@@ -343,75 +342,73 @@ describe('useReaderState', () => {
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
const { result, waitForValueToChange } = await renderHook(
|
||||
() => useReaderState('Component', 'default', 'backstage', '/example'),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
const { result } = renderHook(
|
||||
() => useReaderState('Component', 'default', 'backstage', '/example'),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await waitForValueToChange(() => result.current.state, {
|
||||
timeout: 2000,
|
||||
});
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'INITIAL_BUILD',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: 'NotFoundError: Page Not Found',
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: ['Line 1', 'Line 2'],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledTimes(2);
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledTimes(1);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.state).toBe('INITIAL_BUILD'), {
|
||||
timeout: 2000,
|
||||
});
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'INITIAL_BUILD',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: 'NotFoundError: Page Not Found',
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: ['Line 1', 'Line 2'],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.state).toBe('CHECKING'));
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.state).toBe('CONTENT_FRESH'));
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledTimes(2);
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledTimes(1);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle stale content', async () => {
|
||||
@@ -423,6 +420,7 @@ describe('useReaderState', () => {
|
||||
});
|
||||
techdocsStorageApi.syncEntityDocs.mockImplementation(
|
||||
async (_, logHandler) => {
|
||||
await 'a tick';
|
||||
logHandler?.call(this, 'Line 1');
|
||||
logHandler?.call(this, 'Line 2');
|
||||
await new Promise(resolve => setTimeout(resolve, 1100));
|
||||
@@ -430,101 +428,105 @@ describe('useReaderState', () => {
|
||||
},
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
const { result, waitForValueToChange } = await renderHook(
|
||||
() => useReaderState('Component', 'default', 'backstage', '/example'),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
const { result } = renderHook(
|
||||
() => useReaderState('Component', 'default', 'backstage', '/example'),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the content is returned but the sync is in progress
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: ['Line 1', 'Line 2'],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the sync takes longer than 1 seconds so the refreshing state starts
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_STALE_REFRESHING',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: ['Line 1', 'Line 2'],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the content is updated but not yet displayed
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_STALE_READY',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: ['Line 1', 'Line 2'],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// reload the content
|
||||
result.current.contentReload();
|
||||
|
||||
// the new content refresh is triggered
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the new content is loaded
|
||||
await waitForValueToChange(() => result.current.state, {
|
||||
timeout: 2000,
|
||||
});
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my new content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledTimes(2);
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the content is returned but the sync is in progress
|
||||
await waitFor(() => expect(result.current.state).toBe('CONTENT_FRESH'));
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: ['Line 1', 'Line 2'],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the sync takes longer than 1 seconds so the refreshing state starts
|
||||
await waitFor(() =>
|
||||
expect(result.current.state).toBe('CONTENT_STALE_REFRESHING'),
|
||||
);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_STALE_REFRESHING',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: ['Line 1', 'Line 2'],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the content is updated but not yet displayed
|
||||
await waitFor(() =>
|
||||
expect(result.current.state).toBe('CONTENT_STALE_READY'),
|
||||
);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_STALE_READY',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: ['Line 1', 'Line 2'],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// reload the content
|
||||
await act(async () => {
|
||||
result.current.contentReload();
|
||||
});
|
||||
|
||||
// the new content refresh is triggered
|
||||
await waitFor(() => expect(result.current.state).toBe('CHECKING'));
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the new content is loaded
|
||||
await waitFor(() => expect(result.current.state).toBe('CONTENT_FRESH'), {
|
||||
timeout: 2000,
|
||||
});
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my new content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledTimes(2);
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle navigation', async () => {
|
||||
@@ -537,93 +539,93 @@ describe('useReaderState', () => {
|
||||
.mockRejectedValueOnce(new NotFoundError('Some error description'));
|
||||
techdocsStorageApi.syncEntityDocs.mockResolvedValue('cached');
|
||||
|
||||
await act(async () => {
|
||||
const { result, waitForValueToChange, rerender } = await renderHook(
|
||||
({ path }: { path: string }) =>
|
||||
useReaderState('Component', 'default', 'backstage', path),
|
||||
{ initialProps: { path: '/example' }, wrapper: Wrapper as any },
|
||||
);
|
||||
const { result, rerender } = renderHook(
|
||||
({ path }: { path: string }) =>
|
||||
useReaderState('Component', 'default', 'backstage', path),
|
||||
{ initialProps: { path: '/example' }, wrapper: Wrapper as any },
|
||||
);
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// show the content
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// navigate
|
||||
rerender({ path: '/new' });
|
||||
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await waitForValueToChange(() => result.current.state, {
|
||||
timeout: 2000,
|
||||
});
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/new',
|
||||
content: 'my new content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// navigate
|
||||
rerender({ path: '/missing' });
|
||||
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_NOT_FOUND',
|
||||
path: '/missing',
|
||||
content: undefined,
|
||||
contentErrorMessage: 'NotFoundError: Some error description',
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/new',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// show the content
|
||||
await waitFor(() => expect(result.current.state).toBe('CONTENT_FRESH'));
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// navigate
|
||||
rerender({ path: '/new' });
|
||||
|
||||
await waitFor(() => expect(result.current.state).toBe('CHECKING'));
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: 'my content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
await waitFor(() => expect(result.current.state).toBe('CONTENT_FRESH'), {
|
||||
timeout: 2000,
|
||||
});
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_FRESH',
|
||||
path: '/new',
|
||||
content: 'my new content',
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// navigate
|
||||
rerender({ path: '/missing' });
|
||||
|
||||
await waitFor(() =>
|
||||
expect(result.current.state).toBe('CONTENT_NOT_FOUND'),
|
||||
);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_NOT_FOUND',
|
||||
path: '/missing',
|
||||
content: undefined,
|
||||
contentErrorMessage: 'NotFoundError: Some error description',
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/new',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle content error', async () => {
|
||||
@@ -632,47 +634,47 @@ describe('useReaderState', () => {
|
||||
);
|
||||
techdocsStorageApi.syncEntityDocs.mockResolvedValue('cached');
|
||||
|
||||
await act(async () => {
|
||||
const { result, waitForValueToChange } = await renderHook(
|
||||
() => useReaderState('Component', 'default', 'backstage', '/example'),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
const { result } = renderHook(
|
||||
() => useReaderState('Component', 'default', 'backstage', '/example'),
|
||||
{ wrapper: Wrapper },
|
||||
);
|
||||
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the content loading threw an error
|
||||
await waitForValueToChange(() => result.current.state);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_NOT_FOUND',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: 'NotFoundError: Some error description',
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CHECKING',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: undefined,
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
// the content loading threw an error
|
||||
await waitFor(() =>
|
||||
expect(result.current.state).toBe('CONTENT_NOT_FOUND'),
|
||||
);
|
||||
expect(result.current).toEqual({
|
||||
state: 'CONTENT_NOT_FOUND',
|
||||
path: '/example',
|
||||
content: undefined,
|
||||
contentErrorMessage: 'NotFoundError: Some error description',
|
||||
syncErrorMessage: undefined,
|
||||
buildLog: [],
|
||||
contentReload: expect.any(Function),
|
||||
});
|
||||
|
||||
expect(techdocsStorageApi.getEntityDocs).toHaveBeenCalledWith(
|
||||
{ kind: 'Component', namespace: 'default', name: 'backstage' },
|
||||
'/example',
|
||||
);
|
||||
expect(techdocsStorageApi.syncEntityDocs).toHaveBeenCalledWith(
|
||||
{
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'backstage',
|
||||
},
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user