frontend-test-utils: remove render method

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2024-08-26 17:35:01 +02:00
parent e760f84a59
commit e6e488cf20
13 changed files with 180 additions and 468 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-test-utils': minor
---
**BREAKING**: The deprecated `.render()` method has been removed from the extension tester.
@@ -42,32 +42,36 @@ import { renderInTestApp as renderInOldTestApp } from '@backstage/test-utils';
describe('BackwardsCompatProvider', () => {
it('should convert the app context', () => {
// TODO(Rugvip): Replace with the new renderInTestApp once it's available, and have some plugins
createExtensionTester(
createExtension({
attachTo: { id: 'ignored', input: 'ignored' },
output: [coreExtensionData.reactElement],
factory() {
function Component() {
const app = useApp();
return (
<div data-testid="ctx">
plugins:
{app
.getPlugins()
.map(p => p.getId())
.join(', ')}
{'\n'}
components: {Object.keys(app.getComponents()).join(', ')}
{'\n'}
icons: {Object.keys(app.getSystemIcons()).join(', ')}
</div>
);
}
renderInNewTestApp(
createExtensionTester(
createExtension({
attachTo: { id: 'ignored', input: 'ignored' },
output: [coreExtensionData.reactElement],
factory() {
function Component() {
const app = useApp();
return (
<div data-testid="ctx">
plugins:
{app
.getPlugins()
.map(p => p.getId())
.join(', ')}
{'\n'}
components: {Object.keys(app.getComponents()).join(', ')}
{'\n'}
icons: {Object.keys(app.getSystemIcons()).join(', ')}
</div>
);
}
return [coreExtensionData.reactElement(compatWrapper(<Component />))];
},
}),
).render();
return [
coreExtensionData.reactElement(compatWrapper(<Component />)),
];
},
}),
).reactElement(),
);
expect(screen.getByTestId('ctx').textContent).toMatchInlineSnapshot(`
"plugins:
@@ -16,14 +16,15 @@
import React from 'react';
import { AppRootWrapperBlueprint } from './AppRootWrapperBlueprint';
import { createExtensionTester } from '@backstage/frontend-test-utils';
import { PageBlueprint } from './PageBlueprint';
import { waitFor } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import {
coreExtensionData,
createExtension,
createExtensionInput,
createFrontendPlugin,
} from '../wiring';
import { createSpecializedApp } from '@backstage/frontend-app-api';
import { MockConfigApi } from '@backstage/test-utils';
describe('AppRootWrapperBlueprint', () => {
it('should return an extension with sensible defaults', () => {
@@ -64,24 +65,22 @@ describe('AppRootWrapperBlueprint', () => {
},
});
const { getByText } = createExtensionTester(
PageBlueprint.make({
params: {
defaultPath: '/',
loader: async () => <div />,
},
}),
)
.add(extension)
.render();
const app = createSpecializedApp({
features: [
createFrontendPlugin({
id: 'test',
extensions: [extension],
}),
],
});
await waitFor(() => expect(getByText('Hello')).toBeInTheDocument());
render(app.createRoot());
await waitFor(() => expect(screen.getByText('Hello')).toBeInTheDocument());
});
it('should render the complex component wrapper', async () => {
const extension = AppRootWrapperBlueprint.makeWithOverrides({
namespace: 'ns',
name: 'test',
config: {
schema: {
name: z => z.string(),
@@ -104,28 +103,39 @@ describe('AppRootWrapperBlueprint', () => {
},
});
const { getByText, getByTestId } = createExtensionTester(
PageBlueprint.make({
params: {
defaultPath: '/',
loader: async () => <div>Hi</div>,
const app = createSpecializedApp({
features: [
createFrontendPlugin({
id: 'test',
extensions: [
extension,
createExtension({
name: 'test-child',
attachTo: { id: 'app-root-wrapper:test', input: 'children' },
output: [coreExtensionData.reactElement],
factory: () => [
coreExtensionData.reactElement(<div>Its Me</div>),
],
}),
],
}),
],
config: new MockConfigApi({
app: {
extensions: [
{
'app-root-wrapper:test': { config: { name: 'Robin' } },
},
],
},
}),
)
.add(extension, { config: { name: 'Robin' } })
.add(
createExtension({
attachTo: { id: 'app-root-wrapper:ns/test', input: 'children' },
output: [coreExtensionData.reactElement],
factory: () => [coreExtensionData.reactElement(<div>Its Me</div>)],
}),
)
.render();
});
render(app.createRoot());
await waitFor(() => {
expect(getByText('Hi')).toBeInTheDocument();
expect(getByTestId('Robin-1')).toBeInTheDocument();
expect(getByText('Its Me')).toBeInTheDocument();
expect(screen.getByTestId('Robin-1')).toBeInTheDocument();
expect(screen.getByText('Its Me')).toBeInTheDocument();
});
});
});
@@ -103,7 +103,7 @@ describe('PageBlueprint', () => {
expect(tester.get(coreExtensionData.routeRef)).toBe(mockRouteRef);
const { getByTestId } = tester.render();
const { getByTestId } = renderInTestApp(tester.reactElement());
await waitFor(() => expect(getByTestId('test')).toBeInTheDocument());
});
@@ -23,14 +23,12 @@ import {
} from '@backstage/test-utils';
import { ExtensionBoundary } from './ExtensionBoundary';
import { coreExtensionData, createExtension } from '../wiring';
import {
analyticsApiRef,
createApiFactory,
useAnalytics,
} from '@backstage/core-plugin-api';
import { analyticsApiRef, useAnalytics } from '@backstage/core-plugin-api';
import { createRouteRef } from '../routing';
import { createExtensionTester } from '@backstage/frontend-test-utils';
import { ApiBlueprint } from '../blueprints';
import {
createExtensionTester,
renderInTestApp,
} from '@backstage/frontend-test-utils';
const wrapInBoundaryExtension = (element?: JSX.Element) => {
const routeRef = createRouteRef();
@@ -60,7 +58,11 @@ describe('ExtensionBoundary', () => {
const TextComponent = () => {
return <p>{text}</p>;
};
createExtensionTester(wrapInBoundaryExtension(<TextComponent />)).render();
renderInTestApp(
createExtensionTester(
wrapInBoundaryExtension(<TextComponent />),
).reactElement(),
);
await waitFor(() => expect(screen.getByText(text)).toBeInTheDocument());
});
@@ -70,9 +72,11 @@ describe('ExtensionBoundary', () => {
throw new Error(errorMsg);
};
const { error } = await withLogCollector(['error'], async () => {
createExtensionTester(
wrapInBoundaryExtension(<ErrorComponent />),
).render();
renderInTestApp(
createExtensionTester(
wrapInBoundaryExtension(<ErrorComponent />),
).reactElement(),
);
await waitFor(() =>
expect(screen.getByText(errorMsg)).toBeInTheDocument(),
);
@@ -99,13 +103,13 @@ describe('ExtensionBoundary', () => {
return null;
};
createExtensionTester(
wrapInBoundaryExtension(
<TestApiProvider apis={[[analyticsApiRef, analyticsApiMock]]}>
<AnalyticsComponent />
</TestApiProvider>,
),
).render();
renderInTestApp(
<TestApiProvider apis={[[analyticsApiRef, analyticsApiMock]]}>
{createExtensionTester(
wrapInBoundaryExtension(<AnalyticsComponent />),
).reactElement()}
</TestApiProvider>,
);
await waitFor(() => {
const event = analyticsApiMock
@@ -122,8 +126,9 @@ describe('ExtensionBoundary', () => {
});
});
// TODO(Rugvip): It's annoying to test the inverse of this currently, because the extension tester overrides the subject to always output a path
it('should emit analytics events if routable', async () => {
// TODO(Rugvip): Need a way to be able to override APIs in the app to be able to test this properly
// eslint-disable-next-line jest/no-disabled-tests
it.skip('should emit analytics events if routable', async () => {
const Emitter = () => {
const analytics = useAnalytics();
useEffect(() => {
@@ -134,16 +139,12 @@ describe('ExtensionBoundary', () => {
const analyticsApiMock = new MockAnalyticsApi();
await act(async () => {
createExtensionTester(wrapInBoundaryExtension(<Emitter />))
.add(
ApiBlueprint.make({
namespace: analyticsApiRef.id,
params: {
factory: createApiFactory(analyticsApiRef, analyticsApiMock),
},
}),
)
.render();
renderInTestApp(
createExtensionTester(
wrapInBoundaryExtension(<Emitter />),
).reactElement(),
// { apis: [[analyticsApiRef, analyticsApiMock]] },
);
});
expect(analyticsApiMock.getEvents()).toEqual([
@@ -17,7 +17,10 @@
import React from 'react';
import { coreExtensionData } from './coreExtensionData';
import { createExtensionBlueprint } from './createExtensionBlueprint';
import { createExtensionTester } from '@backstage/frontend-test-utils';
import {
createExtensionTester,
renderInTestApp,
} from '@backstage/frontend-test-utils';
import {
ExtensionDataValue,
createExtensionDataRef,
@@ -80,7 +83,9 @@ describe('createExtensionBlueprint', () => {
version: 'v2',
});
const { container } = createExtensionTester(extension).render();
const { container } = renderInTestApp(
createExtensionTester(extension).reactElement(),
);
expect(container.querySelector('h1')).toHaveTextContent('Hello, world!');
});
@@ -120,7 +125,9 @@ describe('createExtensionBlueprint', () => {
version: 'v2',
});
const { container } = createExtensionTester(extension).render();
const { container } = renderInTestApp(
createExtensionTester(extension).reactElement(),
);
expect(container.querySelector('h1')).toHaveTextContent('Hello, world!');
});
@@ -145,7 +152,9 @@ describe('createExtensionBlueprint', () => {
expect(extension).toBeDefined();
const { container } = createExtensionTester(extension).render();
const { container } = renderInTestApp(
createExtensionTester(extension).reactElement(),
);
expect(container.querySelector('h1')).toHaveTextContent('Hello, world!');
});
@@ -216,13 +225,15 @@ describe('createExtensionBlueprint', () => {
expect.assertions(4);
createExtensionTester(extension, {
config: {
something: 'something new!',
text: 'Hello, world!',
defaulted: 'lolz',
},
}).render();
renderInTestApp(
createExtensionTester(extension, {
config: {
something: 'something new!',
text: 'Hello, world!',
defaulted: 'lolz',
},
}).reactElement(),
);
});
it('should not allow overlapping config keys', () => {
@@ -291,12 +302,14 @@ describe('createExtensionBlueprint', () => {
expect.assertions(2);
createExtensionTester(extension, {
config: {
something: 'something new!',
defaulted: 'lolz',
},
}).render();
renderInTestApp(
createExtensionTester(extension, {
config: {
something: 'something new!',
defaulted: 'lolz',
},
}).reactElement(),
);
});
it('should allow getting inputs properly', () => {
@@ -84,8 +84,6 @@ export class ExtensionTester<UOutput extends AnyExtensionDataRef> {
): ExtensionQuery<UQueryExtensionOutput>;
// (undocumented)
reactElement(): JSX.Element;
// @deprecated (undocumented)
render(options?: { config?: JsonObject }): RenderResult;
}
// @public
@@ -14,22 +14,13 @@
* limitations under the License.
*/
import React, { useCallback } from 'react';
import { Link } from 'react-router-dom';
import { fireEvent, screen, waitFor } from '@testing-library/react';
import React from 'react';
import {
ApiBlueprint,
analyticsApiRef,
configApiRef,
coreExtensionData,
createApiFactory,
createExtension,
createExtensionDataRef,
createExtensionInput,
useAnalytics,
useApi,
} from '@backstage/frontend-plugin-api';
import { MockAnalyticsApi } from '../apis';
import { createExtensionTester } from './createExtensionTester';
const stringDataRef = createExtensionDataRef<string>().with({
@@ -37,206 +28,6 @@ const stringDataRef = createExtensionDataRef<string>().with({
});
describe('createExtensionTester', () => {
const defaultDefinition = {
namespace: 'test',
attachTo: { id: 'ignored', input: 'ignored' },
output: [coreExtensionData.reactElement],
factory: () => [coreExtensionData.reactElement(<div>test</div>)],
};
it('should render a simple extension', async () => {
const extension = createExtension(defaultDefinition);
const tester = createExtensionTester(extension);
tester.render();
await expect(screen.findByText('test')).resolves.toBeInTheDocument();
});
it('should render an extension even if disabled by default', async () => {
const extension = createExtension({
...defaultDefinition,
disabled: true,
});
const tester = createExtensionTester(extension);
tester.render();
await expect(screen.findByText('test')).resolves.toBeInTheDocument();
});
it("should fail to render an extension that doesn't output a react element", async () => {
const extension = createExtension({
...defaultDefinition,
output: [coreExtensionData.routePath],
factory: () => [coreExtensionData.routePath('/foo')],
});
const tester = createExtensionTester(extension);
expect(() => tester.render()).toThrowErrorMatchingInlineSnapshot(
`"Failed to instantiate extension 'app/routes', extension 'test' could not be attached because its output data ('core.routing.path') does not match what the input 'routes' requires ('core.routing.path', 'core.reactElement')"`,
);
});
it('should render multiple extensions', async () => {
const indexPageExtension = createExtension({
...defaultDefinition,
factory: () => [
coreExtensionData.reactElement(
<div>
Index page <Link to="/details">See details</Link>
</div>,
),
],
});
const detailsPageExtension = createExtension({
...defaultDefinition,
name: 'details',
attachTo: { id: 'app/routes', input: 'routes' },
output: [coreExtensionData.routePath, coreExtensionData.reactElement],
factory: () => [
coreExtensionData.routePath('/details'),
coreExtensionData.reactElement(<div>Details page</div>),
],
});
const tester = createExtensionTester(indexPageExtension);
tester.add(detailsPageExtension);
tester.render();
await expect(screen.findByText('Index page')).resolves.toBeInTheDocument();
fireEvent.click(screen.getByRole('link', { name: 'See details' }));
await expect(
screen.findByText('Details page'),
).resolves.toBeInTheDocument();
});
it('should accepts a custom config', async () => {
const indexPageExtension = createExtension({
...defaultDefinition,
config: {
schema: {
title: z => z.string().optional(),
},
},
factory: ({ config }) => {
const Component = () => {
const configApi = useApi(configApiRef);
const appTitle = configApi.getOptionalString('app.title');
return (
<div>
<h2>{appTitle ?? 'Backstage app'}</h2>
<h3>{config.title ?? 'Index page'}</h3>
<Link to="/details">See details</Link>
</div>
);
};
return [coreExtensionData.reactElement(<Component />)];
},
});
const detailsPageExtension = createExtension({
...defaultDefinition,
name: 'details',
attachTo: { id: 'app/routes', input: 'routes' },
config: {
schema: {
title: z => z.string().optional(),
},
},
output: [coreExtensionData.routePath, coreExtensionData.reactElement],
factory: ({ config }) => [
coreExtensionData.routePath('/details'),
coreExtensionData.reactElement(
<div>{config.title ?? 'Details page'}</div>,
),
],
});
const tester = createExtensionTester(indexPageExtension, {
config: { title: 'Custom index' },
});
tester.add(detailsPageExtension, {
config: { title: 'Custom details' },
});
tester.render({
config: {
app: {
title: 'Custom app',
},
},
});
await expect(
screen.findByRole('heading', { name: 'Custom app' }),
).resolves.toBeInTheDocument();
await expect(
screen.findByRole('heading', { name: 'Custom index' }),
).resolves.toBeInTheDocument();
fireEvent.click(screen.getByRole('link', { name: 'See details' }));
await expect(
screen.findByText('Custom details'),
).resolves.toBeInTheDocument();
});
it('should capture click events in analytics', async () => {
// Mocking the analytics api implementation
const analyticsApiMock = new MockAnalyticsApi();
const analyticsApiOverride = ApiBlueprint.make({
params: {
factory: createApiFactory({
api: analyticsApiRef,
deps: {},
factory: () => analyticsApiMock,
}),
},
});
const indexPageExtension = createExtension({
...defaultDefinition,
factory: () => {
const Component = () => {
const analyticsApi = useAnalytics();
const handleClick = useCallback(() => {
analyticsApi.captureEvent('click', 'See details');
}, [analyticsApi]);
return (
<div>
Index Page
<button onClick={handleClick}>See details</button>
</div>
);
};
return [coreExtensionData.reactElement(<Component />)];
},
});
const tester = createExtensionTester(indexPageExtension);
// Overriding the analytics api extension
tester.add(analyticsApiOverride);
tester.render();
fireEvent.click(await screen.findByRole('button', { name: 'See details' }));
await waitFor(() =>
expect(analyticsApiMock.getEvents()).toEqual(
expect.arrayContaining([
expect.objectContaining({
action: 'click',
subject: 'See details',
}),
]),
),
);
});
it('should return the correct dataRef when called', () => {
const extension = createExtension({
namespace: 'test',
@@ -14,10 +14,6 @@
* limitations under the License.
*/
import React from 'react';
import { MemoryRouter, Link } from 'react-router-dom';
import { RenderResult, render } from '@testing-library/react';
import { createSpecializedApp } from '@backstage/frontend-app-api';
import {
AnyExtensionDataRef,
AppNode,
@@ -25,23 +21,13 @@ import {
Extension,
ExtensionDataRef,
ExtensionDefinition,
IconComponent,
NavItemBlueprint,
RouteRef,
RouterBlueprint,
coreExtensionData,
createExtension,
createExtensionInput,
createExtensionOverrides,
useRouteRef,
} from '@backstage/frontend-plugin-api';
import { Config, ConfigReader } from '@backstage/config';
import { JsonArray, JsonObject, JsonValue } from '@backstage/types';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { resolveExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { toInternalExtensionDefinition } from '../../../frontend-plugin-api/src/wiring/createExtension';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { resolveAppTree } from '../../../frontend-app-api/src/tree/resolveAppTree';
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
import { resolveAppNodeSpecs } from '../../../frontend-app-api/src/tree/resolveAppNodeSpecs';
@@ -51,56 +37,6 @@ import { instantiateAppNodeTree } from '../../../frontend-app-api/src/tree/insta
import { readAppExtensionsConfig } from '../../../frontend-app-api/src/tree/readAppExtensionsConfig';
import { TestApiRegistry } from '@backstage/test-utils';
const NavItem = (props: {
routeRef: RouteRef<undefined>;
title: string;
icon: IconComponent;
}) => {
const { routeRef, title, icon: Icon } = props;
const link = useRouteRef(routeRef);
if (!link) {
return null;
}
return (
<li>
<Link to={link()}>
<Icon /> {title}
</Link>
</li>
);
};
const TestAppNavExtension = createExtension({
namespace: 'app',
name: 'nav',
attachTo: { id: 'app/layout', input: 'nav' },
inputs: {
items: createExtensionInput([NavItemBlueprint.dataRefs.target]),
},
output: [coreExtensionData.reactElement],
factory({ inputs }) {
return [
coreExtensionData.reactElement(
<nav>
<ul>
{inputs.items.map((item, index) => {
const target = item.get(NavItemBlueprint.dataRefs.target);
return (
<NavItem
key={index}
icon={target.icon}
title={target.title}
routeRef={target.routeRef}
/>
);
})}
</ul>
</nav>,
),
];
},
});
/** @public */
export class ExtensionQuery<UOutput extends AnyExtensionDataRef> {
#node: AppNode;
@@ -236,70 +172,6 @@ export class ExtensionTester<UOutput extends AnyExtensionDataRef> {
return element;
}
/**
* @deprecated Switch to using `renderInTestApp` directly and using `.reactElement()` or `.get(...)` to get the component you w
*/
render(options?: { config?: JsonObject }): RenderResult {
const { config = {} } = options ?? {};
const [subject] = this.#extensions;
if (!subject) {
throw new Error(
'No subject found. At least one extension should be added to the tester.',
);
}
const subjectInternal = toInternalExtensionDefinition(subject.definition);
let subjectOverride;
// attaching to app/routes to render as index route
if (subjectInternal.version === 'v1') {
throw new Error('The extension tester does not support v1 extensions');
} else if (subjectInternal.version === 'v2') {
subjectOverride = createExtension({
...subjectInternal,
attachTo: { id: 'app/routes', input: 'routes' },
output: subjectInternal.output.find(
ref => ref.id === coreExtensionData.routePath.id,
)
? subjectInternal.output
: [...subjectInternal.output, coreExtensionData.routePath],
factory: params => {
const parentOutput = Array.from(
subjectInternal.factory(params as any),
).filter(val => val.id !== coreExtensionData.routePath.id);
return [...parentOutput, coreExtensionData.routePath('/')];
},
});
(subjectOverride as any).configSchema = subjectInternal.configSchema;
} else {
throw new Error('Unsupported extension version');
}
const app = createSpecializedApp({
features: [
createExtensionOverrides({
extensions: [
subjectOverride,
...this.#extensions.slice(1).map(extension => extension.definition),
TestAppNavExtension,
RouterBlueprint.make({
namespace: 'test',
params: {
Component: ({ children }) => (
<MemoryRouter>{children}</MemoryRouter>
),
},
}),
],
}),
],
config: this.#getConfig(config),
});
return render(app.createRoot());
}
#resolveTree() {
if (this.#tree) {
return this.#tree;
@@ -20,7 +20,10 @@ import {
createExtension,
createExtensionInput,
} from '@backstage/frontend-plugin-api';
import { createExtensionTester } from '@backstage/frontend-test-utils';
import {
createExtensionTester,
renderInTestApp,
} from '@backstage/frontend-test-utils';
import { waitFor, screen } from '@testing-library/react';
import { Entity } from '@backstage/catalog-model';
@@ -163,9 +166,11 @@ describe('EntityCardBlueprint', () => {
},
});
createExtensionTester(extension, { config: { mock: 'mock test config' } })
.add(mockExtension)
.render();
renderInTestApp(
createExtensionTester(extension, { config: { mock: 'mock test config' } })
.add(mockExtension)
.reactElement(),
);
await waitFor(() => {
expect(screen.getByTestId('test')).toBeInTheDocument();
@@ -15,7 +15,10 @@
*/
import React from 'react';
import { EntityContentBlueprint } from './EntityContentBlueprint';
import { createExtensionTester } from '@backstage/frontend-test-utils';
import {
createExtensionTester,
renderInTestApp,
} from '@backstage/frontend-test-utils';
import {
coreExtensionData,
createExtension,
@@ -211,9 +214,11 @@ describe('EntityContentBlueprint', () => {
},
});
createExtensionTester(extension, { config: { mock: 'mock test config' } })
.add(mockExtension)
.render();
renderInTestApp(
createExtensionTester(extension, { config: { mock: 'mock test config' } })
.add(mockExtension)
.reactElement(),
);
await waitFor(() => {
expect(screen.getByTestId('test')).toBeInTheDocument();
@@ -20,7 +20,10 @@ import {
createExtension,
createExtensionInput,
} from '@backstage/frontend-plugin-api';
import { createExtensionTester } from '@backstage/frontend-test-utils';
import {
createExtensionTester,
renderInTestApp,
} from '@backstage/frontend-test-utils';
import { waitFor, screen } from '@testing-library/react';
describe('CatalogFilterBlueprint', () => {
@@ -89,9 +92,11 @@ describe('CatalogFilterBlueprint', () => {
},
});
createExtensionTester(extension, { config: { test: 'mock test config' } })
.add(mockExtension)
.render();
renderInTestApp(
createExtensionTester(extension, { config: { test: 'mock test config' } })
.add(mockExtension)
.reactElement(),
);
await waitFor(() => {
expect(screen.getByTestId('test')).toBeInTheDocument();
@@ -16,7 +16,10 @@
import React from 'react';
import { SearchResultListItemBlueprint } from './SearchResultListItemBlueprint';
import { createExtensionTester } from '@backstage/frontend-test-utils';
import {
createExtensionTester,
renderInTestApp,
} from '@backstage/frontend-test-utils';
import {
PageBlueprint,
createExtensionInput,
@@ -107,17 +110,17 @@ describe('SearchResultListItemBlueprint', () => {
});
await expect(
createExtensionTester(mockSearchPage)
.add(extension)
.render()
.findByText('noTrack: false'),
renderInTestApp(
createExtensionTester(mockSearchPage).add(extension).reactElement(),
).findByText('noTrack: false'),
).resolves.toBeInTheDocument();
await expect(
createExtensionTester(mockSearchPage)
.add(extension, { config: { noTrack: true } })
.render()
.findByText('noTrack: true'),
renderInTestApp(
createExtensionTester(mockSearchPage)
.add(extension, { config: { noTrack: true } })
.reactElement(),
).findByText('noTrack: true'),
).resolves.toBeInTheDocument();
});
});