plugins: rtl 13 fixes for tests

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2023-10-13 13:11:15 +02:00
parent 0935b0d60f
commit 80a79cc1e7
27 changed files with 371 additions and 403 deletions
@@ -15,9 +15,10 @@
*/
import React from 'react';
import { render } from '@testing-library/react';
import { act, render } from '@testing-library/react';
import { BitriseBuildDetailsDialog } from './BitriseBuildDetailsDialog';
import { BitriseBuildResult } from '../../api/bitriseApi.model';
import userEvent from '@testing-library/user-event';
jest.mock('../BitriseArtifactsComponent', () => ({
BitriseArtifactsComponent: (_props: { build: string }) => <>VISIBLE</>,
@@ -48,7 +49,7 @@ describe('BitriseArtifactsComponent', () => {
expect(rendered.queryByText('VISIBLE')).not.toBeInTheDocument();
btn.click();
await act(() => userEvent.click(btn));
expect(rendered.getByText('VISIBLE')).toBeInTheDocument();
});
@@ -18,7 +18,7 @@ import { configApiRef, errorApiRef } from '@backstage/core-plugin-api';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { TestApiProvider, MockConfigApi } from '@backstage/test-utils';
import { TextField } from '@material-ui/core';
import { act, render, screen } from '@testing-library/react';
import { act, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { AnalyzeResult, catalogImportApiRef } from '../../api';
@@ -101,36 +101,36 @@ describe('<StepPrepareCreatePullRequest />', () => {
it('renders without exploding', async () => {
catalogApi.getEntities.mockReturnValue(Promise.resolve({ items: [] }));
await act(async () => {
render(
<StepPrepareCreatePullRequest
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={({ register }) => {
return (
<>
<TextField {...asInputRef(register('title'))} />
<TextField {...asInputRef(register('body'))} />
<TextField {...asInputRef(register('componentName'))} />
<TextField {...asInputRef(register('owner'))} />
</>
);
}}
/>,
{
wrapper: Wrapper,
},
);
render(
<StepPrepareCreatePullRequest
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={({ register }) => {
return (
<>
<TextField {...asInputRef(register('title'))} />
<TextField {...asInputRef(register('body'))} />
<TextField {...asInputRef(register('componentName'))} />
<TextField {...asInputRef(register('owner'))} />
</>
);
}}
/>,
{
wrapper: Wrapper,
},
);
const title = await screen.findByText('My title');
const description = await screen.findByText('body', {
selector: 'strong',
});
expect(title).toBeInTheDocument();
expect(title).toBeVisible();
expect(description).toBeInTheDocument();
expect(description).toBeVisible();
await act(async () => {});
const title = await screen.findByText('My title');
const description = await screen.findByText('body', {
selector: 'strong',
});
expect(title).toBeInTheDocument();
expect(title).toBeVisible();
expect(description).toBeInTheDocument();
expect(description).toBeVisible();
});
it('should submit created PR', async () => {
@@ -142,39 +142,38 @@ describe('<StepPrepareCreatePullRequest />', () => {
}),
);
await act(async () => {
render(
<StepPrepareCreatePullRequest
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={({ register }) => {
return (
<>
<TextField {...asInputRef(register('title'))} />
<TextField {...asInputRef(register('body'))} />
<TextField
{...asInputRef(register('componentName'))}
id="name"
label="name"
/>
<TextField
{...asInputRef(register('owner'))}
id="owner"
label="owner"
/>
</>
);
}}
/>,
{
wrapper: Wrapper,
},
);
render(
<StepPrepareCreatePullRequest
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={({ register }) => {
return (
<>
<TextField {...asInputRef(register('title'))} />
<TextField {...asInputRef(register('body'))} />
<TextField
{...asInputRef(register('componentName'))}
id="name"
label="name"
/>
<TextField
{...asInputRef(register('owner'))}
id="owner"
label="owner"
/>
</>
);
}}
/>,
{
wrapper: Wrapper,
},
);
await act(async () => {});
await userEvent.type(await screen.findByLabelText('name'), '-changed');
await userEvent.type(await screen.findByLabelText('owner'), '-changed');
await userEvent.click(screen.getByRole('button', { name: /Create PR/i }));
});
await userEvent.type(await screen.findByLabelText('name'), '-changed');
await userEvent.type(await screen.findByLabelText('owner'), '-changed');
await userEvent.click(screen.getByRole('button', { name: /Create PR/i }));
expect(catalogImportApi.submitPullRequest).toHaveBeenCalledTimes(1);
expect(catalogImportApi.submitPullRequest.mock.calls[0]).toMatchObject([
@@ -226,31 +225,30 @@ spec:
new Error('some error'),
);
await act(async () => {
render(
<StepPrepareCreatePullRequest
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={({ register }) => {
return (
<>
<TextField {...asInputRef(register('title'))} />
<TextField {...asInputRef(register('body'))} />
<TextField {...asInputRef(register('componentName'))} />
<TextField {...asInputRef(register('owner'))} />
</>
);
}}
/>,
{
wrapper: Wrapper,
},
);
render(
<StepPrepareCreatePullRequest
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={({ register }) => {
return (
<>
<TextField {...asInputRef(register('title'))} />
<TextField {...asInputRef(register('body'))} />
<TextField {...asInputRef(register('componentName'))} />
<TextField {...asInputRef(register('owner'))} />
</>
);
}}
/>,
{
wrapper: Wrapper,
},
);
await act(async () => {});
await userEvent.click(
await screen.findByRole('button', { name: /Create PR/i }),
);
});
await userEvent.click(
await screen.findByRole('button', { name: /Create PR/i }),
);
expect(screen.getByText('some error')).toBeInTheDocument();
expect(catalogImportApi.submitPullRequest).toHaveBeenCalledTimes(1);
@@ -273,30 +271,27 @@ spec:
}),
);
await act(async () => {
render(
<StepPrepareCreatePullRequest
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={renderFormFieldsFn}
/>,
{
wrapper: Wrapper,
},
);
render(
<StepPrepareCreatePullRequest
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={renderFormFieldsFn}
/>,
{
wrapper: Wrapper,
},
);
await act(async () => {});
await waitFor(() => {
expect(catalogApi.getEntities).toHaveBeenCalledTimes(1);
});
expect(catalogApi.getEntities).toHaveBeenCalledTimes(1);
expect(renderFormFieldsFn).toHaveBeenCalled();
expect(renderFormFieldsFn.mock.calls[0][0]).toMatchObject({
groups: [],
groupsLoading: true,
groups: ['my-group'],
groupsLoading: false,
});
expect(
renderFormFieldsFn.mock.calls[
renderFormFieldsFn.mock.calls.length - 1
][0],
).toMatchObject({ groups: ['my-group'], groupsLoading: false });
});
describe('generateEntities', () => {
@@ -73,7 +73,6 @@ describe('useImportState', () => {
describe('onAnalysis & onPrepare & onReview & onReset', () => {
it('should work', async () => {
const { result } = renderHook(() => useImportState());
await cleanup();
expect(result.current).toMatchObject({
activeFlow: 'unknown',
@@ -148,7 +147,6 @@ describe('useImportState', () => {
it('should work skipped', async () => {
const { result } = renderHook(() => useImportState());
await cleanup();
expect(result.current).toMatchObject({
activeFlow: 'unknown',
@@ -198,7 +196,6 @@ describe('useImportState', () => {
it('should ignore on invalid state', async () => {
const { result } = renderHook(() => useImportState());
await cleanup();
// state 'analyze'
act(() => {
@@ -264,7 +261,6 @@ describe('useImportState', () => {
describe('onGoBack', () => {
it('should work', async () => {
const { result } = renderHook(() => useImportState());
await cleanup();
expect(result.current.activeStepNumber).toBe(0);
expect(result.current.onGoBack).toBeUndefined();
@@ -303,7 +299,6 @@ describe('useImportState', () => {
it('should work for skipped', async () => {
const { result } = renderHook(() => useImportState());
await cleanup();
expect(result.current.activeStepNumber).toBe(0);
expect(result.current.onGoBack).toBeUndefined();
@@ -329,7 +324,6 @@ describe('useImportState', () => {
describe('should consider prepareNotRepeatable', () => {
it('as true', async () => {
const { result } = renderHook(() => useImportState());
await cleanup();
expect(result.current.onGoBack).toBeUndefined();
@@ -351,7 +345,6 @@ describe('useImportState', () => {
it('as false', async () => {
const { result } = renderHook(() => useImportState());
await cleanup();
expect(result.current.onGoBack).toBeUndefined();
@@ -179,9 +179,7 @@ describe('<EntityAutocompletePicker/>', () => {
</TestApiProvider>,
);
await waitFor(() =>
expect(updateFilters).toHaveBeenLastCalledWith({
options: undefined,
}),
expect(screen.getByTestId('options-picker-expand')).toBeInTheDocument(),
);
fireEvent.click(screen.getByTestId('options-picker-expand'));
@@ -74,9 +74,10 @@ describe('<EntityLifecyclePicker/>', () => {
</TestApiProvider>,
);
await waitFor(() => expect(catalogApi.getEntityFacets).toHaveBeenCalled());
expect(updateFilters).toHaveBeenLastCalledWith({
lifecycles: new EntityLifecycleFilter(['experimental']),
await waitFor(() => {
expect(updateFilters).toHaveBeenLastCalledWith({
lifecycles: new EntityLifecycleFilter(['experimental']),
});
});
});
@@ -119,9 +120,10 @@ describe('<EntityLifecyclePicker/>', () => {
</TestApiProvider>,
);
await waitFor(() => expect(catalogApi.getEntityFacets).toHaveBeenCalled());
expect(updateFilters).toHaveBeenLastCalledWith({
lifecycles: new EntityLifecycleFilter(['production']),
await waitFor(() => {
expect(updateFilters).toHaveBeenLastCalledWith({
lifecycles: new EntityLifecycleFilter(['production']),
});
});
fireEvent.click(screen.getByTestId('lifecycles-picker-expand'));
expect(screen.getByLabelText('production')).toBeChecked();
@@ -147,9 +149,10 @@ describe('<EntityLifecyclePicker/>', () => {
</TestApiProvider>,
);
await waitFor(() => expect(catalogApi.getEntityFacets).toHaveBeenCalled());
expect(updateFilters).toHaveBeenLastCalledWith({
lifecycles: new EntityLifecycleFilter(['experimental']),
await waitFor(() => {
expect(updateFilters).toHaveBeenLastCalledWith({
lifecycles: new EntityLifecycleFilter(['experimental']),
});
});
rendered.rerender(
@@ -210,9 +213,10 @@ describe('<EntityLifecyclePicker/>', () => {
</TestApiProvider>,
);
await waitFor(() => expect(catalogApi.getEntityFacets).toHaveBeenCalled());
expect(updateFilters).toHaveBeenLastCalledWith({
lifecycles: new EntityLifecycleFilter(['production']),
await waitFor(() => {
expect(updateFilters).toHaveBeenLastCalledWith({
lifecycles: new EntityLifecycleFilter(['production']),
});
});
});
});
@@ -113,9 +113,7 @@ describe('<EntityNamespacePicker/>', () => {
</TestApiProvider>,
);
await waitFor(() =>
expect(updateFilters).toHaveBeenLastCalledWith({
namespace: undefined,
}),
expect(screen.getByTestId('namespace-picker-expand')).toBeInTheDocument(),
);
fireEvent.click(screen.getByTestId('namespace-picker-expand'));
@@ -49,7 +49,7 @@ const apis = TestApiRegistry.from([catalogApiRef, catalogApi]);
describe('<EntityPeekAheadPopover/>', () => {
it('renders all owners', async () => {
renderInTestApp(
await renderInTestApp(
<ApiProvider apis={apis}>
<EntityPeekAheadPopover entityRef="component:default/service1">
<Button data-testid="popover1">s1</Button>
@@ -14,7 +14,13 @@
* limitations under the License.
*/
import { fireEvent, render, waitFor, screen } from '@testing-library/react';
import {
fireEvent,
render,
waitFor,
screen,
act,
} from '@testing-library/react';
import React from 'react';
import { MockEntityListContextProvider } from '../../testUtils/providers';
import { EntityTagFilter } from '../../filters';
@@ -127,9 +133,7 @@ describe('<EntityTagPicker/>', () => {
</TestApiProvider>,
);
await waitFor(() =>
expect(updateFilters).toHaveBeenLastCalledWith({
tags: undefined,
}),
expect(screen.getByTestId('tags-picker-expand')).toBeInTheDocument(),
);
fireEvent.click(screen.getByTestId('tags-picker-expand'));
@@ -218,13 +222,16 @@ describe('<EntityTagPicker/>', () => {
</TestApiProvider>,
);
await waitFor(() =>
expect(updateFilters).toHaveBeenLastCalledWith({
tags: new EntityTagFilter(['tag1']),
}),
expect(screen.getByTestId('tags-picker-expand')).toBeInTheDocument(),
);
fireEvent.click(screen.getByTestId('tags-picker-expand'));
fireEvent.click(screen.getByLabelText('tag2'));
expect(screen.getByLabelText('tag2')).toBeChecked();
await act(async () => {
fireEvent.click(screen.getByTestId('tags-picker-expand'));
});
await act(async () => {
fireEvent.click(screen.getByLabelText('tag2'));
});
expect(updateFilters).toHaveBeenLastCalledWith({
tags: new EntityTagFilter(['tag1', 'tag2']),
});
@@ -19,7 +19,7 @@ import {
AsyncEntityProvider,
EntityProvider,
} from '@backstage/plugin-catalog-react';
import { render, screen, waitFor } from '@testing-library/react';
import { render, screen } from '@testing-library/react';
import React, { useEffect } from 'react';
import { isKind } from './conditions';
import { EntitySwitch } from './EntitySwitch';
@@ -517,9 +517,7 @@ describe('EntitySwitch', () => {
</Wrapper>,
);
await waitFor(() => expect(shouldRender).toHaveBeenCalled());
expect(screen.getByText('C')).toBeInTheDocument();
await expect(screen.findByText('C')).resolves.toBeInTheDocument();
expect(screen.queryByText('A')).not.toBeInTheDocument();
expect(screen.queryByText('B')).not.toBeInTheDocument();
});
@@ -15,7 +15,7 @@
*/
import React from 'react';
import { render, act, waitFor } from '@testing-library/react';
import { render, waitFor } from '@testing-library/react';
import { Features } from './Features';
import { mockCalverProject } from '../test-helpers/test-helpers';
@@ -49,9 +49,7 @@ describe('Features', () => {
/>,
);
await act(async () => {
await waitFor(() => getByTestId(TEST_IDS.info.info));
});
await waitFor(() => getByTestId(TEST_IDS.info.info));
expect(getByTestId(TEST_IDS.info.info)).toMatchInlineSnapshot(`
<div
@@ -74,7 +74,7 @@ describe('useResponseSteps', () => {
"icon": "failure",
"message": <b>
Something went wrong
<WithStyles(ForwardRef(Typography))
aria-label="fire"
component="span"
@@ -108,7 +108,7 @@ describe('useResponseSteps', () => {
"icon": "failure",
"message": <b>
Something went wrong
<WithStyles(ForwardRef(Typography))
aria-label="fire"
component="span"
@@ -75,7 +75,7 @@ describe('<RecentWorkflowRunsCard />', () => {
});
const renderSubject = async (props: any = {}) => {
renderInTestApp(
await renderInTestApp(
<TestApiProvider
apis={[
[errorApiRef, mockErrorApi],
@@ -110,6 +110,8 @@ describe('<CreatePlaylistButton/>', () => {
act(() => {
fireEvent.click(rendered.getByRole('button'));
});
act(() => {
fireEvent.click(rendered.getByTestId('mock-playlist-edit-dialog'));
});
@@ -128,6 +130,8 @@ describe('<CreatePlaylistButton/>', () => {
act(() => {
fireEvent.click(rendered.getByRole('button'));
});
act(() => {
fireEvent.click(rendered.getByTestId('mock-playlist-edit-dialog'));
});
@@ -78,7 +78,9 @@ describe('<PlaylistEditDialog/>', () => {
},
},
);
});
act(() => {
fireEvent.input(
getByRole(
rendered.getByTestId('edit-dialog-description-input'),
@@ -90,17 +92,25 @@ describe('<PlaylistEditDialog/>', () => {
},
},
);
});
act(() => {
fireEvent.mouseDown(
getByRole(rendered.getByTestId('edit-dialog-owner-select'), 'button'),
);
});
act(() => {
fireEvent.click(rendered.getByText('test-owner'));
});
act(() => {
fireEvent.click(
getByRole(rendered.getByTestId('edit-dialog-public-option'), 'radio'),
);
});
act(() => {
fireEvent.click(rendered.getByTestId('edit-dialog-save-button'));
});
@@ -192,6 +192,8 @@ describe('PlaylistHeader', () => {
act(() => {
fireEvent.click(rendered.getByTestId('header-action-menu'));
});
act(() => {
fireEvent.click(
rendered
.getAllByTestId('header-action-item')
@@ -224,11 +226,15 @@ describe('PlaylistHeader', () => {
act(() => {
fireEvent.click(rendered.getByTestId('header-action-menu'));
});
act(() => {
fireEvent.click(
rendered
.getAllByTestId('header-action-item')
.find(e => e.innerHTML.includes('Delete Playlist'))!,
);
});
act(() => {
fireEvent.click(rendered.getByTestId('delete-playlist-dialog-button'));
});
@@ -109,8 +109,8 @@ describe('PlaylistPage', () => {
act(() => {
fireEvent.click(rendered.getByTestId('playlist-page-follow-button'));
testPlaylist.isFollowing = true;
});
testPlaylist.isFollowing = true;
await waitFor(() => {
expect(playlistApi.followPlaylist).toHaveBeenCalledWith('id1');
@@ -124,8 +124,8 @@ describe('PlaylistPage', () => {
act(() => {
fireEvent.click(rendered.getByTestId('playlist-page-follow-button'));
testPlaylist.isFollowing = false;
});
testPlaylist.isFollowing = false;
await waitFor(() => {
expect(playlistApi.unfollowPlaylist).toHaveBeenCalledWith('id1');
@@ -273,12 +273,12 @@ describe('Stepper', () => {
/>,
);
await act(async () => {
await fireEvent.click(getByRole('button', { name: 'Review' }));
expect(getByRole('progressbar')).toBeInTheDocument();
expect(getByRole('button', { name: 'Review' })).toBeDisabled();
act(() => {
fireEvent.click(getByRole('button', { name: 'Review' }));
});
expect(getByRole('progressbar')).toBeInTheDocument();
expect(getByRole('button', { name: 'Review' })).toBeDisabled();
});
it('should transform default error message', async () => {
@@ -20,6 +20,7 @@ import { TemplateCategoryPicker } from './TemplateCategoryPicker';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { alertApiRef } from '@backstage/core-plugin-api';
import { fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
jest.mock('@backstage/plugin-catalog-react', () => ({
useEntityTypeFilter: jest.fn(),
@@ -91,7 +92,7 @@ describe('TemplateCategoryPicker', () => {
);
const openButton = getByRole('button', { name: 'Open' });
openButton.click();
await userEvent.click(openButton);
expect(getByRole('checkbox', { name: 'Foo' })).toBeInTheDocument();
expect(getByRole('checkbox', { name: 'Bar' })).toBeInTheDocument();
@@ -22,6 +22,8 @@ import {
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
import { ApiProvider } from '@backstage/core-app-api';
import { rootRouteRef } from '../../routes';
import { userEvent } from '@testing-library/user-event';
import { act } from 'react-dom/test-utils';
const scaffolderApiMock: jest.Mocked<ScaffolderApi> = {
scaffold: jest.fn(),
@@ -261,7 +263,7 @@ describe('TemplatePage', () => {
expect(rendered.queryByText('nested prop b')).not.toBeInTheDocument();
expect(rendered.queryByText('number')).not.toBeInTheDocument();
objectChip.click();
await act(() => userEvent.click(objectChip));
expect(rendered.queryByText('nested prop a')).toBeInTheDocument();
expect(rendered.queryByText('string')).toBeInTheDocument();
@@ -323,7 +325,7 @@ describe('TemplatePage', () => {
expect(rendered.queryByText('nested prop b')).not.toBeInTheDocument();
expect(rendered.queryByText('nested object c')).not.toBeInTheDocument();
objectChip.click();
await act(() => userEvent.click(objectChip));
expect(rendered.queryByText('nested object a')).toBeInTheDocument();
expect(rendered.queryByText('nested prop b')).toBeInTheDocument();
@@ -331,7 +333,7 @@ describe('TemplatePage', () => {
const allObjectChips = rendered.getAllByText('object');
expect(allObjectChips.length).toBe(2);
allObjectChips[1].click();
await act(() => userEvent.click(allObjectChips[1]));
expect(rendered.queryByText('nested object a')).toBeInTheDocument();
expect(rendered.queryByText('nested prop b')).toBeInTheDocument();
@@ -374,7 +376,7 @@ describe('TemplatePage', () => {
expect(rendered.queryByText('No schema defined')).not.toBeInTheDocument();
objectChip.click();
await act(() => userEvent.click(objectChip));
expect(rendered.queryByText('No schema defined')).toBeInTheDocument();
});
@@ -471,7 +473,7 @@ describe('TemplatePage', () => {
expect(rendered.queryByText('nested object a')).not.toBeInTheDocument();
expect(rendered.queryByText('nested prop b')).not.toBeInTheDocument();
objectChip.click();
await act(() => userEvent.click(objectChip));
expect(rendered.queryByText('nested object a')).toBeInTheDocument();
expect(rendered.queryByText('nested prop b')).toBeInTheDocument();
@@ -16,7 +16,7 @@
import { entityRouteRef } from '@backstage/plugin-catalog-react';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { screen } from '@testing-library/react';
import { act, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React, { ReactNode, useEffect } from 'react';
import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react';
@@ -90,10 +90,10 @@ describe('DryRunResultsView', () => {
expect(screen.queryByText('Foo Message')).not.toBeInTheDocument();
expect(screen.queryByText('Foo Link')).not.toBeInTheDocument();
await userEvent.click(screen.getByText('Log'));
await act(() => userEvent.click(screen.getByText('Log')));
expect(screen.getByText('Foo Message')).toBeInTheDocument();
await userEvent.click(screen.getByText('Output'));
await act(() => userEvent.click(screen.getByText('Output')));
expect(screen.getByText('Foo Link')).toBeInTheDocument();
});
});
@@ -15,7 +15,7 @@
*/
import { MockConfigApi, TestApiProvider } from '@backstage/test-utils';
import { screen, render, waitFor, within } from '@testing-library/react';
import { screen, render, waitFor, within, act } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
@@ -364,7 +364,7 @@ describe('SearchFilter.Autocomplete', () => {
});
// Blur the field and only one tag should be shown with a +1.
input.blur();
await act(() => userEvent.tab());
expect(
screen.queryByRole('button', { name: values[0] }),
).not.toBeInTheDocument();
@@ -34,8 +34,12 @@ const apis = TestApiRegistry.from(
);
describe('Incidents', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.resetAllMocks();
jest.useRealTimers();
});
it('Renders an empty state when there are no incidents', async () => {
@@ -47,13 +51,10 @@ describe('Incidents', () => {
<Incidents readOnly={false} refreshIncidents={false} team="test" />
</ApiProvider>,
);
await waitFor(() => !screen.queryByTestId('progress'));
await waitFor(
() =>
expect(
screen.getByText('Nice! No incidents found!'),
).toBeInTheDocument(),
{ timeout: 2000 },
jest.advanceTimersByTime(2000);
await waitFor(() => expect(screen.queryByTestId('progress')).toBe(null));
await waitFor(() =>
expect(screen.getByText('Nice! No incidents found!')).toBeInTheDocument(),
);
});
@@ -66,15 +67,14 @@ describe('Incidents', () => {
<Incidents readOnly={false} team="test" refreshIncidents={false} />
</ApiProvider>,
);
await waitFor(() => !screen.queryByTestId('progress'));
await waitFor(
() =>
expect(
screen.getByText('user', {
exact: false,
}),
).toBeInTheDocument(),
{ timeout: 2000 },
jest.advanceTimersByTime(2000);
await waitFor(() => expect(screen.queryByTestId('progress')).toBe(null));
await waitFor(() =>
expect(
screen.getByText('user', {
exact: false,
}),
).toBeInTheDocument(),
);
expect(screen.getByText('test-incident')).toBeInTheDocument();
expect(screen.getByTitle('Acknowledged')).toBeInTheDocument();
@@ -93,15 +93,14 @@ describe('Incidents', () => {
<Incidents readOnly team="test" refreshIncidents={false} />
</ApiProvider>,
);
await waitFor(() => !screen.queryByTestId('progress'));
await waitFor(
() =>
expect(
screen.getByText('user', {
exact: false,
}),
).toBeInTheDocument(),
{ timeout: 2000 },
jest.advanceTimersByTime(2000);
await waitFor(() => expect(screen.queryByTestId('progress')).toBe(null));
await waitFor(() =>
expect(
screen.getByText('user', {
exact: false,
}),
).toBeInTheDocument(),
);
expect(screen.getByText('test-incident')).toBeInTheDocument();
expect(screen.getByLabelText('Status warning')).toBeInTheDocument();
@@ -127,15 +126,14 @@ describe('Incidents', () => {
<Incidents readOnly={false} team="test" refreshIncidents={false} />
</ApiProvider>,
);
await waitFor(() => !screen.queryByTestId('progress'));
await waitFor(
() =>
expect(
screen.getByText(
'Error encountered while fetching information. Error occurred',
),
).toBeInTheDocument(),
{ timeout: 2000 },
jest.advanceTimersByTime(2000);
await waitFor(() => expect(screen.queryByTestId('progress')).toBe(null));
await waitFor(() =>
expect(
screen.getByText(
'Error encountered while fetching information. Error occurred',
),
).toBeInTheDocument(),
);
});
});
@@ -19,7 +19,6 @@ import React, { ReactElement } from 'react';
// Shadow DOM support for the simple and complete DOM testing utilities
// https://github.com/testing-library/dom-testing-library/issues/742#issuecomment-674987855
import { screen } from 'testing-library__dom';
import { renderToStaticMarkup } from 'react-dom/server';
import { Route } from 'react-router-dom';
import { act, render } from '@testing-library/react';
@@ -39,6 +38,13 @@ import { catalogPlugin } from '@backstage/plugin-catalog';
import { searchApiRef } from '@backstage/plugin-search-react';
import { scmIntegrationsApiRef } from '@backstage/integration-react';
// Since React 18 react-dom/server eagerly uses TextEncoder, so lazy load and make it available globally first
if (!global.TextEncoder) {
global.TextEncoder = require('util').TextEncoder;
}
const { renderToStaticMarkup } =
require('react-dom/server') as typeof import('react-dom/server');
const techdocsApi = {
getTechDocsMetadata: jest.fn(),
getEntityMetadata: jest.fn(),
@@ -20,6 +20,8 @@ import {
TechDocsBuildLogs,
TechDocsBuildLogsDrawerContent,
} from './TechDocsBuildLogs';
import { userEvent } from '@testing-library/user-event';
import { act } from 'react-dom/test-utils';
// The <AutoSizer> inside <LogViewer> needs mocking to render in jsdom
jest.mock('react-virtualized-auto-sizer', () => ({
@@ -38,7 +40,7 @@ describe('<TechDocsBuildLogs />', () => {
it('should open drawer', async () => {
const rendered = await renderInTestApp(<TechDocsBuildLogs buildLog={[]} />);
rendered.getByText(/Show Build Logs/i).click();
await act(() => userEvent.click(rendered.getByText(/Show Build Logs/i)));
expect(rendered.getByText(/Build Details/i)).toBeInTheDocument();
});
});
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import React from 'react';
import { act } from '@testing-library/react';
import { scmIntegrationsApiRef } from '@backstage/integration-react';
import { entityRouteRef } from '@backstage/plugin-catalog-react';
@@ -150,31 +149,25 @@ describe('<TechDocsReaderPage />', () => {
});
it('should render a techdocs reader page with children', async () => {
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPage
entityRef={{
name: 'test-name',
namespace: 'test-namespace',
kind: 'test',
}}
>
techdocs reader page
</TechDocsReaderPage>
</Wrapper>,
{
mountedRoutes,
},
);
expect(
rendered.container.querySelector('header'),
).not.toBeInTheDocument();
expect(
rendered.container.querySelector('article'),
).not.toBeInTheDocument();
expect(rendered.getByText('techdocs reader page')).toBeInTheDocument();
});
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPage
entityRef={{
name: 'test-name',
namespace: 'test-namespace',
kind: 'test',
}}
>
techdocs reader page
</TechDocsReaderPage>
</Wrapper>,
{
mountedRoutes,
},
);
expect(rendered.container.querySelector('header')).not.toBeInTheDocument();
expect(rendered.container.querySelector('article')).not.toBeInTheDocument();
expect(rendered.getByText('techdocs reader page')).toBeInTheDocument();
});
it('should render techdocs reader page with addons', async () => {
@@ -183,56 +176,52 @@ describe('<TechDocsReaderPage />', () => {
const namespace = 'test-namespace';
const kind = 'test';
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<FlatRoutes>
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</Route>
</FlatRoutes>
</Wrapper>,
{
mountedRoutes,
routeEntries: ['/docs/test-namespace/test/test-name'],
},
);
const rendered = await renderInTestApp(
<Wrapper>
<FlatRoutes>
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</Route>
</FlatRoutes>
</Wrapper>,
{
mountedRoutes,
routeEntries: ['/docs/test-namespace/test/test-name'],
},
);
expect(
rendered.getByText(`PageMock: ${namespace}#${kind}#${name}`),
).toBeInTheDocument();
});
expect(
rendered.getByText(`PageMock: ${namespace}#${kind}#${name}`),
).toBeInTheDocument();
});
it('should render techdocs reader page with addons and page', async () => {
(Page as jest.Mock).mockImplementation(PageMock);
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<FlatRoutes>
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
<p>the page</p>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</Route>
</FlatRoutes>
</Wrapper>,
{
mountedRoutes,
routeEntries: ['/docs/test-namespace/test/test-name'],
},
);
const rendered = await renderInTestApp(
<Wrapper>
<FlatRoutes>
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
>
<p>the page</p>
<TechDocsAddons>
<ReportIssue />
</TechDocsAddons>
</Route>
</FlatRoutes>
</Wrapper>,
{
mountedRoutes,
routeEntries: ['/docs/test-namespace/test/test-name'],
},
);
expect(rendered.getByText('the page')).toBeInTheDocument();
});
expect(rendered.getByText('the page')).toBeInTheDocument();
});
});
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import React from 'react';
import { act, waitFor } from '@testing-library/react';
import { waitFor } from '@testing-library/react';
import { CompoundEntityRef } from '@backstage/catalog-model';
import {
@@ -90,18 +90,16 @@ describe('<TechDocsReaderPageContent />', () => {
useTechDocsReaderDom.mockReturnValue(document.createElement('html'));
useReaderState.mockReturnValue({ state: 'cached' });
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageContent withSearch={false} />
</Wrapper>,
);
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageContent withSearch={false} />
</Wrapper>,
);
await waitFor(() => {
expect(
rendered.getByTestId('techdocs-native-shadowroot'),
).toBeInTheDocument();
});
await waitFor(() => {
expect(
rendered.getByTestId('techdocs-native-shadowroot'),
).toBeInTheDocument();
});
});
@@ -110,21 +108,19 @@ describe('<TechDocsReaderPageContent />', () => {
useTechDocsReaderDom.mockReturnValue(document.createElement('html'));
useReaderState.mockReturnValue({ state: 'cached' });
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageContent withSearch={false} />
</Wrapper>,
);
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageContent withSearch={false} />
</Wrapper>,
);
await waitFor(() => {
expect(
rendered.queryByTestId('techdocs-native-shadowroot'),
).not.toBeInTheDocument();
expect(
rendered.getByText('ERROR 404: PAGE NOT FOUND'),
).toBeInTheDocument();
});
await waitFor(() => {
expect(
rendered.queryByTestId('techdocs-native-shadowroot'),
).not.toBeInTheDocument();
expect(
rendered.getByText('ERROR 404: PAGE NOT FOUND'),
).toBeInTheDocument();
});
});
@@ -134,21 +130,19 @@ describe('<TechDocsReaderPageContent />', () => {
useTechDocsReaderDom.mockReturnValue(undefined);
useReaderState.mockReturnValue({ state: 'CONTENT_NOT_FOUND' });
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageContent withSearch={false} />
</Wrapper>,
);
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageContent withSearch={false} />
</Wrapper>,
);
await waitFor(() => {
expect(
rendered.queryByTestId('techdocs-native-shadowroot'),
).not.toBeInTheDocument();
expect(
rendered.getByText('ERROR 404: Documentation not found'),
).toBeInTheDocument();
});
await waitFor(() => {
expect(
rendered.queryByTestId('techdocs-native-shadowroot'),
).not.toBeInTheDocument();
expect(
rendered.getByText('ERROR 404: Documentation not found'),
).toBeInTheDocument();
});
});
});
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import React from 'react';
import { act, waitFor } from '@testing-library/react';
import { CompoundEntityRef } from '@backstage/catalog-model';
import { entityRouteRef } from '@backstage/plugin-catalog-react';
@@ -80,112 +79,77 @@ describe('<TechDocsReaderPageHeader />', () => {
getEntityMetadata.mockResolvedValue(mockEntityMetadata);
getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata);
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
);
},
);
expect(rendered.container.innerHTML).toContain('header');
expect(rendered.container.innerHTML).toContain('header');
await waitFor(() => {
expect(rendered.getAllByText('test-site-name')).toHaveLength(2);
});
expect(rendered.getAllByText('test-site-name')).toHaveLength(2);
expect(rendered.getByText('test-site-desc')).toBeDefined();
expect(rendered.getByText('test-site-desc')).toBeDefined();
});
expect(
rendered.getByRole('link', { name: 'test:test-namespace/test-name' }),
).toHaveAttribute('href', '/catalog/test-namespace/test/test-name');
});
it('should render a techdocs page header even if metadata is not loaded', async () => {
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
);
},
);
expect(rendered.container.innerHTML).toContain('header');
});
expect(rendered.container.innerHTML).toContain('header');
});
it('should not render a techdocs page header if entity metadata is missing', async () => {
getEntityMetadata.mockResolvedValue(undefined);
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
);
},
);
await waitFor(() => {
expect(rendered.container.innerHTML).not.toContain('header');
});
});
expect(rendered.container.innerHTML).not.toContain('header');
});
it('should not render a techdocs page header if techdocs metadata is missing', async () => {
getTechDocsMetadata.mockResolvedValue(undefined);
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
);
},
);
await waitFor(() => {
expect(rendered.container.innerHTML).not.toContain('header');
});
});
});
it('should render a link back to the component page', async () => {
getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata);
await act(async () => {
const rendered = await renderInTestApp(
<Wrapper>
<TechDocsReaderPageHeader />
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name/*': entityRouteRef,
'/docs': rootRouteRef,
},
},
);
await waitFor(() => {
expect(
rendered.getByRole('link', { name: 'test:test-namespace/test-name' }),
).toHaveAttribute('href', '/catalog/test-namespace/test/test-name');
});
});
expect(rendered.container.innerHTML).not.toContain('header');
});
});