react-router#Routes TypeScript Examples
The following examples show how to use
react-router#Routes.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Router.tsx From backstage with Apache License 2.0 | 6 votes |
Router = () => {
const { entity } = useEntity();
if (!isGithubActionsAvailable(entity)) {
return (
<MissingAnnotationEmptyState annotation={GITHUB_ACTIONS_ANNOTATION} />
);
}
return (
<Routes>
<Route path="/" element={<WorkflowRunsTable entity={entity} />} />
<Route
path={`${buildRouteRef.path}`}
element={<WorkflowRunDetails entity={entity} />}
/>
)
</Routes>
);
}
Example #2
Source File: App.tsx From Riakuto-StartingReact-ja3.1 with Apache License 2.0 | 6 votes |
App: VFC = () => (
<>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/:orgCode/members" element={<Members />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</>
)
Example #3
Source File: App.tsx From Riakuto-StartingReact-ja3.1 with Apache License 2.0 | 6 votes |
App: VFC = () => {
const { hash, pathname } = useLocation();
useEffect(() => {
if (!hash) window.scrollTo(0, 0);
}, [hash, pathname]);
return (
<div className="container">
<Routes>
<Route path="/" element={<Home />} />
<Route path="characters" element={<Characters />}>
<Route path="" element={<AllCharacters />} />
<Route path=":schoolCode" element={<SchoolCharacters />} />
</Route>
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</div>
);
}
Example #4
Source File: SettingsPage.tsx From lightning-terminal with MIT License | 6 votes |
SettingsPage: React.FC = () => {
const { Wrapper } = Styled;
return (
<Wrapper>
<Routes>
<Route path="/" element={<GeneralSettings />} />
<Route path="unit" element={<UnitSettings />} />
<Route path="balance" element={<BalanceSettings />} />
<Route path="explorers" element={<ExplorerSettings />} />
</Routes>
</Wrapper>
);
}
Example #5
Source File: App.tsx From axios-source-analysis with MIT License | 6 votes |
function App() {
debugger
return (
<Routes>
{/* 注意,这里不是LayoutRoute,因为LayoutRoute只允许element和children,而这里有path */}
<Route path='/' element={<Layout />}>
{
routeConfigs.map(({ path, Element }) => <Route key={path} path={`${path}${path === '*' ? '': '/*'}`} element={<Element />} />)
}
</Route>
</Routes>
)
}
Example #6
Source File: Router.tsx From backstage with Apache License 2.0 | 6 votes |
Router = ({ entity }: { entity: Entity }) => {
return (
<Routes>
<Route
path="/"
element={
<SentryIssuesWidget
entity={entity}
statsFor="24h"
tableOptions={{
padding: 'dense',
paging: true,
search: false,
pageSize: 5,
}}
/>
}
/>
)
</Routes>
);
}
Example #7
Source File: Router.tsx From backstage with Apache License 2.0 | 6 votes |
Router = (props: RouterProps) => {
const { groups, components = {}, defaultPreviewTemplate } = props;
const { TemplateCardComponent, TaskPageComponent } = components;
const outlet = useOutlet();
const TaskPageElement = TaskPageComponent ?? TaskPage;
const customFieldExtensions = useElementFilter(outlet, elements =>
elements
.selectByComponentData({
key: FIELD_EXTENSION_WRAPPER_KEY,
})
.findComponentData<FieldExtensionOptions>({
key: FIELD_EXTENSION_KEY,
}),
);
const fieldExtensions = [
...customFieldExtensions,
...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter(
({ name }) =>
!customFieldExtensions.some(
customFieldExtension => customFieldExtension.name === name,
),
),
];
return (
<Routes>
<Route
element={
<ScaffolderPage
groups={groups}
TemplateCardComponent={TemplateCardComponent}
contextMenu={props.contextMenu}
/>
}
/>
<Route
path={selectedTemplateRouteRef.path}
element={
<SecretsContextProvider>
<TemplatePage customFieldExtensions={fieldExtensions} />
</SecretsContextProvider>
}
/>
<Route
path={scaffolderListTaskRouteRef.path}
element={<ListTasksPage />}
/>
<Route path={scaffolderTaskRouteRef.path} element={<TaskPageElement />} />
<Route path={actionsRouteRef.path} element={<ActionsPage />} />
<Route
path={editRouteRef.path}
element={
<SecretsContextProvider>
<TemplateEditorPage
defaultPreviewTemplate={defaultPreviewTemplate}
customFieldExtensions={fieldExtensions}
/>
</SecretsContextProvider>
}
/>
<Route path="preview" element={<Navigate to="../edit" />} />
</Routes>
);
}
Example #8
Source File: Router.tsx From backstage with Apache License 2.0 | 6 votes |
Router = (_props: Props) => {
const { entity } = useEntity();
if (!isPluginApplicableToEntity(entity)) {
<MissingAnnotationEmptyState annotation={ROLLBAR_ANNOTATION} />;
}
return (
<Routes>
<Route path="/" element={<EntityPageRollbar />} />
</Routes>
);
}
Example #9
Source File: Router.tsx From backstage with Apache License 2.0 | 6 votes |
Router = (_props: Props) => {
const { entity } = useEntity();
if (!isPluginApplicableToEntity(entity)) {
return (
<MissingAnnotationEmptyState
annotation={KAFKA_CONSUMER_GROUP_ANNOTATION}
/>
);
}
return (
<Routes>
<Route path="/" element={<KafkaTopicsForConsumer />} />
</Routes>
);
}
Example #10
Source File: Router.tsx From backstage with Apache License 2.0 | 6 votes |
Router = (_props: Props) => {
const { entity } = useEntity();
if (!isJenkinsAvailable(entity)) {
return <MissingAnnotationEmptyState annotation={JENKINS_ANNOTATION} />;
}
return (
<Routes>
<Route path="/" element={<CITable />} />
<Route path={`/${buildRouteRef.path}`} element={<DetailedViewPage />} />
</Routes>
);
}
Example #11
Source File: Router.tsx From backstage with Apache License 2.0 | 6 votes |
Router = () => {
const { entity } = useEntity();
if (!isCloudbuildAvailable(entity)) {
// TODO(shmidt-i): move warning to a separate standardized component
return <MissingAnnotationEmptyState annotation={CLOUDBUILD_ANNOTATION} />;
}
return (
<Routes>
<Route path="/" element={<WorkflowRunsTable entity={entity} />} />
<Route
path={`${buildRouteRef.path}`}
element={<WorkflowRunDetails entity={entity} />}
/>
</Routes>
);
}
Example #12
Source File: Router.tsx From backstage with Apache License 2.0 | 6 votes |
Router = () => {
const { entity } = useEntity();
if (!isCircleCIAvailable(entity)) {
return <MissingAnnotationEmptyState annotation={CIRCLECI_ANNOTATION} />;
}
return (
<Routes>
<Route path="/" element={<BuildsPage />} />
<Route
path={`${circleCIBuildRouteRef.path}`}
element={<BuildWithStepsPage />}
/>
</Routes>
);
}
Example #13
Source File: Button.test.tsx From backstage with Apache License 2.0 | 6 votes |
describe('<Button />', () => {
it('navigates using react-router', async () => {
const testString = 'This is test string';
const buttonLabel = 'Navigate!';
const { getByText } = render(
wrapInTestApp(
<Routes>
<Route path="/test" element={<p>{testString}</p>} />
<Button to="/test">{buttonLabel}</Button>
</Routes>,
),
);
expect(() => getByText(testString)).toThrow();
await act(async () => {
fireEvent.click(getByText(buttonLabel));
});
expect(getByText(testString)).toBeInTheDocument();
});
});
Example #14
Source File: PlaygroundLayout.tsx From atlas with GNU General Public License v3.0 | 5 votes |
PlaygroundLayout = () => {
const [isMemberDropdownActive, setIsMemberDropdownActive] = useState(false)
const { activeMembership, activeAccountId, activeMemberId, extensionConnected, signIn } = useUser()
const isLoggedIn = activeAccountId && !!activeMemberId && !!extensionConnected
const { url: memberAvatarUrl, isLoadingAsset: memberAvatarLoading } = useMemberAvatar(activeMembership)
return (
<ActiveUserProvider>
<TopbarBase
fullLogoNode={
<LogoWrapper>
<SvgJoystreamLogoShort />
<Text variant="h500" margin={{ left: 2 }}>
Playground
</Text>
</LogoWrapper>
}
logoLinkUrl={absoluteRoutes.playground.index()}
>
<ButtonContainer>
<Button variant="secondary" to={absoluteRoutes.viewer.index()}>
Go to viewer
</Button>
<Button variant="secondary" to={absoluteRoutes.studio.index()}>
Go to studio
</Button>
{isLoggedIn ? (
<Avatar
size="small"
assetUrl={memberAvatarUrl}
loading={memberAvatarLoading}
onClick={() => setIsMemberDropdownActive(true)}
/>
) : (
<Button onClick={signIn}>Sign in</Button>
)}
</ButtonContainer>
</TopbarBase>
<MemberDropdown isActive={isMemberDropdownActive} closeDropdown={() => setIsMemberDropdownActive(false)} />
<ConfirmationModalProvider>
<Container>
<NavContainer>
{playgroundRoutes.map((route) => (
<Link key={route.path} to={`/playground/${route.path}`}>
{route.name}
</Link>
))}
</NavContainer>
<ContentContainer>
<Routes>
{playgroundRoutes.map((route) => (
<Route key={route.path} path={route.path} element={route.element} />
))}
</Routes>
</ContentContainer>
</Container>
<ConnectionStatusManager />
</ConfirmationModalProvider>
</ActiveUserProvider>
)
}
Example #15
Source File: TabbedLayout.test.tsx From backstage with Apache License 2.0 | 5 votes |
describe('TabbedLayout', () => {
it('renders simplest case', async () => {
const { getByText } = await renderInTestApp(
<TabbedLayout>
<TabbedLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</TabbedLayout.Route>
</TabbedLayout>,
);
expect(getByText('tabbed-test-title')).toBeInTheDocument();
expect(getByText('tabbed-test-content')).toBeInTheDocument();
});
it('throws if any other component is a child of TabbedLayout', async () => {
const { error } = await withLogCollector(async () => {
await expect(
renderInTestApp(
<TabbedLayout>
<TabbedLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</TabbedLayout.Route>
<div>This will cause app to throw</div>
</TabbedLayout>,
),
).rejects.toThrow(/Child of TabbedLayout must be an TabbedLayout.Route/);
});
expect(error).toEqual([
expect.stringMatching(
/Child of TabbedLayout must be an TabbedLayout.Route/,
),
expect.stringMatching(
/The above error occurred in the <TabbedLayout> component/,
),
]);
});
it('navigates when user clicks different tab', async () => {
const { getByText, queryByText, queryAllByRole } = await renderInTestApp(
<Routes>
<Route
path="/*"
element={
<TabbedLayout>
<TabbedLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</TabbedLayout.Route>
<TabbedLayout.Route
path="/some-other-path"
title="tabbed-test-title-2"
>
<div>tabbed-test-content-2</div>
</TabbedLayout.Route>
</TabbedLayout>
}
/>
</Routes>,
);
const secondTab = queryAllByRole('tab')[1];
act(() => {
fireEvent.click(secondTab);
});
expect(getByText('tabbed-test-title')).toBeInTheDocument();
expect(queryByText('tabbed-test-content')).not.toBeInTheDocument();
expect(getByText('tabbed-test-title-2')).toBeInTheDocument();
expect(queryByText('tabbed-test-content-2')).toBeInTheDocument();
});
});
Example #16
Source File: Router.tsx From backstage with Apache License 2.0 | 5 votes |
Router = (props: PropsWithChildren<NextRouterProps>) => {
const { components: { TemplateCardComponent } = {} } = props;
const outlet = useOutlet() || props.children;
const customFieldExtensions = useElementFilter(outlet, elements =>
elements
.selectByComponentData({
key: FIELD_EXTENSION_WRAPPER_KEY,
})
.findComponentData<FieldExtensionOptions>({
key: FIELD_EXTENSION_KEY,
}),
);
const fieldExtensions = [
...customFieldExtensions,
...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter(
({ name }) =>
!customFieldExtensions.some(
customFieldExtension => customFieldExtension.name === name,
),
),
];
return (
<Routes>
<Route
element={
<TemplateListPage
TemplateCardComponent={TemplateCardComponent}
groups={props.groups}
/>
}
/>
<Route
path={selectedTemplateRouteRef.path}
element={
<SecretsContextProvider>
<TemplateWizardPage customFieldExtensions={fieldExtensions} />
</SecretsContextProvider>
}
/>
</Routes>
);
}
Example #17
Source File: TabbedLayout.stories.tsx From backstage with Apache License 2.0 | 5 votes |
Wrapper = ({ children }: PropsWithChildren<{}>) => (
<MemoryRouter>
<Routes>
<Route path="/*" element={<>{children}</>} />
</Routes>
</MemoryRouter>
)
Example #18
Source File: app.tsx From flood with GNU General Public License v3.0 | 5 votes |
FloodApp: FC = observer(() => {
useEffect(() => {
UIStore.registerDependency([
{
id: 'notifications',
message: {id: 'dependency.loading.notifications'},
},
]);
UIStore.registerDependency([
{
id: 'torrent-taxonomy',
message: {id: 'dependency.loading.torrent.taxonomy'},
},
]);
UIStore.registerDependency([
{
id: 'transfer-data',
message: {id: 'dependency.loading.transfer.rate.details'},
},
{
id: 'transfer-history',
message: {id: 'dependency.loading.transfer.history'},
},
]);
UIStore.registerDependency([
{
id: 'torrent-list',
message: {id: 'dependency.loading.torrent.list'},
},
]);
}, []);
const isSystemPreferDark = useMedia('(prefers-color-scheme: dark)');
useEffect(() => {
ConfigStore.setSystemPreferDark(isSystemPreferDark);
}, [isSystemPreferDark]);
// max-width here must sync with CSS
const isSmallScreen = useMedia('(max-width: 720px)');
useEffect(() => {
ConfigStore.setSmallScreen(isSmallScreen);
}, [isSmallScreen]);
return (
<Suspense fallback={<LoadingOverlay />}>
<AsyncIntlProvider>
<BrowserRouter basename={stringUtil.withoutTrailingSlash(ConfigStore.baseURI)}>
<AppWrapper className={ConfigStore.isPreferDark ? 'dark' : undefined}>
<Routes>
<Route path="/login" element={<Login />} />
<Route path="/overview" element={<Overview />} />
<Route path="/register" element={<Register />} />
</Routes>
</AppWrapper>
</BrowserRouter>
</AsyncIntlProvider>
</Suspense>
);
})
Example #19
Source File: EntityLayout.test.tsx From backstage with Apache License 2.0 | 4 votes |
describe('EntityLayout', () => {
it('renders simplest case', async () => {
const rendered = await renderInTestApp(
<ApiProvider apis={mockApis}>
<EntityProvider entity={mockEntity}>
<EntityLayout>
<EntityLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</EntityLayout.Route>
</EntityLayout>
</EntityProvider>
</ApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
expect(rendered.getByText('my-entity')).toBeInTheDocument();
expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument();
expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument();
});
it('renders the entity title if defined', async () => {
const mockEntityWithTitle = {
kind: 'MyKind',
metadata: {
name: 'my-entity',
title: 'My Entity',
},
} as Entity;
const rendered = await renderInTestApp(
<ApiProvider apis={mockApis}>
<EntityProvider entity={mockEntityWithTitle}>
<EntityLayout>
<EntityLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</EntityLayout.Route>
</EntityLayout>
</EntityProvider>
</ApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
expect(rendered.getByText('My Entity')).toBeInTheDocument();
expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument();
expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument();
});
it('renders default error message when entity is not found', async () => {
const rendered = await renderInTestApp(
<ApiProvider apis={mockApis}>
<AsyncEntityProvider loading={false}>
<EntityLayout>
<EntityLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</EntityLayout.Route>
</EntityLayout>
</AsyncEntityProvider>
</ApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
expect(rendered.getByText('Warning: Entity not found')).toBeInTheDocument();
expect(rendered.queryByText('my-entity')).not.toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-title')).not.toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument();
});
it('renders custom message when entity is not found', async () => {
const rendered = await renderInTestApp(
<ApiProvider apis={mockApis}>
<AsyncEntityProvider loading={false}>
<EntityLayout
NotFoundComponent={<div>Oppps.. Your entity was not found</div>}
>
<EntityLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</EntityLayout.Route>
</EntityLayout>
</AsyncEntityProvider>
</ApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
expect(
rendered.getByText('Oppps.. Your entity was not found'),
).toBeInTheDocument();
expect(rendered.queryByText('my-entity')).not.toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-title')).not.toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument();
});
it('navigates when user clicks different tab', async () => {
const rendered = await renderInTestApp(
<Routes>
<Route
path="/*"
element={
<ApiProvider apis={mockApis}>
<EntityProvider entity={mockEntity}>
<EntityLayout>
<EntityLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</EntityLayout.Route>
<EntityLayout.Route
path="/some-other-path"
title="tabbed-test-title-2"
>
<div>tabbed-test-content-2</div>
</EntityLayout.Route>
</EntityLayout>
</EntityProvider>
</ApiProvider>
}
/>
</Routes>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
const secondTab = rendered.queryAllByRole('tab')[1];
act(() => {
fireEvent.click(secondTab);
});
expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument();
expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument();
});
it('should conditionally render tabs', async () => {
const shouldRenderTab = (e: Entity) => e.metadata.name === 'my-entity';
const shouldNotRenderTab = (e: Entity) => e.metadata.name === 'some-entity';
const rendered = await renderInTestApp(
<ApiProvider apis={mockApis}>
<EntityProvider entity={mockEntity}>
<EntityLayout>
<EntityLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</EntityLayout.Route>
<EntityLayout.Route
path="/some-other-path"
title="tabbed-test-title-2"
if={shouldNotRenderTab}
>
<div>tabbed-test-content-2</div>
</EntityLayout.Route>
<EntityLayout.Route
path="/some-other-other-path"
title="tabbed-test-title-3"
if={shouldRenderTab}
>
<div>tabbed-test-content-3</div>
</EntityLayout.Route>
</EntityLayout>
</EntityProvider>
</ApiProvider>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
expect(rendered.queryByText('tabbed-test-title')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-title-2')).not.toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-title-3')).toBeInTheDocument();
});
});
Example #20
Source File: appWrappers.tsx From backstage with Apache License 2.0 | 4 votes |
/**
* Creates a Wrapper component that wraps a component inside a Backstage test app,
* providing a mocked theme and app context, along with mocked APIs.
*
* @param options - Additional options for the rendering.
* @public
*/
export function createTestAppWrapper(
options: TestAppOptions = {},
): (props: { children: ReactNode }) => JSX.Element {
const { routeEntries = ['/'] } = options;
const boundRoutes = new Map<ExternalRouteRef, RouteRef>();
const app = createSpecializedApp({
apis: mockApis,
defaultApis,
// Bit of a hack to make sure that the default config loader isn't used
// as that would force every single test to wait for config loading.
configLoader: false as unknown as undefined,
components: {
Progress,
BootErrorPage,
NotFoundErrorPage,
ErrorBoundaryFallback,
Router: ({ children }) => (
<MemoryRouter initialEntries={routeEntries} children={children} />
),
},
icons: mockIcons,
plugins: [],
themes: [
{
id: 'light',
title: 'Test App Theme',
variant: 'light',
Provider: ({ children }) => (
<ThemeProvider theme={lightTheme}>
<CssBaseline>{children}</CssBaseline>
</ThemeProvider>
),
},
],
bindRoutes: ({ bind }) => {
for (const [externalRef, absoluteRef] of boundRoutes) {
bind(
{ ref: externalRef },
{
ref: absoluteRef,
},
);
}
},
});
const routeElements = Object.entries(options.mountedRoutes ?? {}).map(
([path, routeRef]) => {
const Page = () => <div>Mounted at {path}</div>;
// Allow external route refs to be bound to paths as well, for convenience.
// We work around it by creating and binding an absolute ref to the external one.
if (isExternalRouteRef(routeRef)) {
const absoluteRef = createRouteRef({ id: 'id' });
boundRoutes.set(routeRef, absoluteRef);
attachComponentData(Page, 'core.mountPoint', absoluteRef);
} else {
attachComponentData(Page, 'core.mountPoint', routeRef);
}
return <Route key={path} path={path} element={<Page />} />;
},
);
const AppProvider = app.getProvider();
const AppRouter = app.getRouter();
const TestAppWrapper = ({ children }: { children: ReactNode }) => (
<AppProvider>
<AppRouter>
<NoRender>{routeElements}</NoRender>
{/* The path of * here is needed to be set as a catch all, so it will render the wrapper element
* and work with nested routes if they exist too */}
<Routes>
<Route path="/*" element={<>{children}</>} />
</Routes>
</AppRouter>
</AppProvider>
);
return TestAppWrapper;
}
Example #21
Source File: appWrappers.test.tsx From backstage with Apache License 2.0 | 4 votes |
describe('wrapInTestApp', () => {
it('should provide routing and warn about missing act()', async () => {
const { error } = await withLogCollector(['error'], async () => {
const rendered = render(
wrapInTestApp(
<Routes>
<Route path="/route1" element={<p>Route 1</p>} />
<Route path="/route2" element={<p>Route 2</p>} />
</Routes>,
{ routeEntries: ['/route2'] },
),
);
expect(rendered.getByText('Route 2')).toBeInTheDocument();
// Wait for async actions to trigger the act() warnings that we assert below
await Promise.resolve();
});
expect(
error.some(e =>
e.includes(
'Warning: An update to %s inside a test was not wrapped in act(...)',
),
),
).toBeTruthy();
});
it('should render a component in a test app without warning about missing act()', async () => {
const { error } = await withLogCollector(['error'], async () => {
const Foo = () => {
return <p>foo</p>;
};
const rendered = await renderInTestApp(Foo);
expect(rendered.getByText('foo')).toBeInTheDocument();
});
expect(error).toEqual([]);
});
it('should render a node in a test app', async () => {
const Foo = () => {
return <p>foo</p>;
};
const rendered = await renderInTestApp(<Foo />);
expect(rendered.getByText('foo')).toBeInTheDocument();
});
it('should provide mock API implementations', async () => {
const A = () => {
const errorApi = useApi(errorApiRef);
errorApi.post(new Error('NOPE'));
return null;
};
const { error } = await withLogCollector(['error'], async () => {
await expect(renderInTestApp(A)).rejects.toThrow('NOPE');
});
expect(error).toEqual([
expect.stringMatching(
/^Error: Uncaught \[Error: MockErrorApi received unexpected error, Error: NOPE\]/,
),
expect.stringMatching(/^The above error occurred in the <A> component:/),
]);
});
it('should allow custom API implementations', async () => {
const mockErrorApi = new MockErrorApi({ collect: true });
const A = () => {
const errorApi = useApi(errorApiRef);
useEffect(() => {
errorApi.post(new Error('NOPE'));
}, [errorApi]);
return <p>foo</p>;
};
const rendered = await renderInTestApp(
<TestApiProvider apis={[[errorApiRef, mockErrorApi]]}>
<A />
</TestApiProvider>,
);
expect(rendered.getByText('foo')).toBeInTheDocument();
expect(mockErrorApi.getErrors()).toEqual([{ error: new Error('NOPE') }]);
});
it('should allow route refs to be mounted on specific paths', async () => {
const aRouteRef = createRouteRef({ id: 'A' });
const bRouteRef = createRouteRef({ id: 'B', params: ['name'] });
const subRouteRef = createSubRouteRef({
id: 'S',
parent: bRouteRef,
path: '/:page',
});
const externalRouteRef = createExternalRouteRef({
id: 'E',
params: ['name'],
});
const MyComponent = () => {
const a = useRouteRef(aRouteRef);
const b = useRouteRef(bRouteRef);
const s = useRouteRef(subRouteRef);
const e = useRouteRef(externalRouteRef);
return (
<div>
<div>Link A: {a()}</div>
<div>Link B: {b({ name: 'x' })}</div>
<div>Link S: {s({ name: 'y', page: 'p' })}</div>
<div>Link E: {e({ name: 'z' })}</div>
</div>
);
};
const rendered = await renderInTestApp(<MyComponent />, {
mountedRoutes: {
'/my-a-path': aRouteRef,
'/my-b-path/:name': bRouteRef,
'/my-e-path/:name': externalRouteRef,
},
});
expect(rendered.getByText('Link A: /my-a-path')).toBeInTheDocument();
expect(rendered.getByText('Link B: /my-b-path/x')).toBeInTheDocument();
expect(rendered.getByText('Link S: /my-b-path/y/p')).toBeInTheDocument();
expect(rendered.getByText('Link E: /my-e-path/z')).toBeInTheDocument();
});
it('should not make route mounting elements visible during tests', async () => {
const routeRef = createRouteRef({ id: 'foo' });
const rendered = await renderInTestApp(<span>foo</span>, {
mountedRoutes: { '/foo': routeRef },
});
const [root] = rendered.baseElement.children;
expect(root).toBeInTheDocument();
expect(root.children.length).toBe(1);
expect(root.children[0].textContent).toBe('foo');
});
it('should support rerenders', async () => {
const MyComponent = () => {
const app = useApp();
const { Progress } = app.getComponents();
return <Progress />;
};
const rendered = await renderInTestApp(<MyComponent />);
expect(rendered.getByTestId('progress')).toBeInTheDocument();
rendered.rerender(<MyComponent />);
expect(rendered.getByTestId('progress')).toBeInTheDocument();
});
});
Example #22
Source File: RoutedTabs.test.tsx From backstage with Apache License 2.0 | 4 votes |
describe('RoutedTabs', () => {
it('renders simplest case', async () => {
const rendered = await renderInTestApp(
<RoutedTabs routes={[testRoute1]} />,
);
expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument();
expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument();
});
it('navigates when user clicks different tab', async () => {
const rendered = await renderInTestApp(
<Routes>
<Route
path="/*"
element={<RoutedTabs routes={[testRoute1, testRoute2, testRoute3]} />}
/>
</Routes>,
);
const secondTab = rendered.queryAllByRole('tab')[1];
act(() => {
fireEvent.click(secondTab);
});
expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument();
expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument();
const thirdTab = rendered.queryAllByRole('tab')[2];
act(() => {
fireEvent.click(thirdTab);
});
expect(rendered.getByText('tabbed-test-title-3')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content-3')).toBeInTheDocument();
});
describe('correctly delegates nested links', () => {
const renderRoute = (route: string) =>
renderInTestApp(
<Routes>
<Route
path="/*"
element={
<RoutedTabs
routes={[
testRoute1,
{
...testRoute2,
children: (
<div>
tabbed-test-content-2
<Routes>
<Route
path="/nested"
element={<div>tabbed-test-nested-content-2</div>}
/>
</Routes>
</div>
),
},
]}
/>
}
/>
</Routes>,
{ routeEntries: [route] },
);
it('works for nested content', async () => {
const rendered = await renderRoute('/some-other-path/nested');
expect(
rendered.queryByText('tabbed-test-content'),
).not.toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument();
expect(
rendered.queryByText('tabbed-test-nested-content-2'),
).toBeInTheDocument();
});
it('works for non-nested content', async () => {
const rendered = await renderRoute('/some-other-path/');
expect(
rendered.queryByText('tabbed-test-content'),
).not.toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument();
expect(
rendered.queryByText('tabbed-test-nested-content-2'),
).not.toBeInTheDocument();
});
});
it('shows only one tab contents at a time', async () => {
const rendered = await renderInTestApp(
<RoutedTabs routes={[testRoute1, testRoute2]} />,
{ routeEntries: ['/some-other-path'] },
);
expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content')).not.toBeInTheDocument();
expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument();
});
it('redirects to the top level when no route is matching the url', async () => {
const rendered = await renderInTestApp(
<RoutedTabs routes={[testRoute1, testRoute2]} />,
{ routeEntries: ['/non-existing-path'] },
);
expect(rendered.getByText('tabbed-test-title')).toBeInTheDocument();
expect(rendered.getByText('tabbed-test-content')).toBeInTheDocument();
expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument();
expect(
rendered.queryByText('tabbed-test-content-2'),
).not.toBeInTheDocument();
});
});
Example #23
Source File: Link.test.tsx From backstage with Apache License 2.0 | 4 votes |
describe('<Link />', () => {
it('navigates using react-router', async () => {
const testString = 'This is test string';
const linkText = 'Navigate!';
const { getByText } = render(
wrapInTestApp(
<Routes>
<Link to="/test">{linkText}</Link>
<Route path="/test" element={<p>{testString}</p>} />
</Routes>,
),
);
expect(() => getByText(testString)).toThrow();
fireEvent.click(getByText(linkText));
await waitFor(() => {
expect(getByText(testString)).toBeInTheDocument();
});
});
it('captures click using analytics api', async () => {
const linkText = 'Navigate!';
const analyticsApi = new MockAnalyticsApi();
const customOnClick = jest.fn();
const { getByText } = render(
wrapInTestApp(
<TestApiProvider apis={[[analyticsApiRef, analyticsApi]]}>
<Link to="/test" onClick={customOnClick}>
{linkText}
</Link>
</TestApiProvider>,
),
);
fireEvent.click(getByText(linkText));
// Analytics event should have been fired.
await waitFor(() => {
expect(analyticsApi.getEvents()[0]).toMatchObject({
action: 'click',
subject: linkText,
attributes: {
to: '/test',
},
});
// Custom onClick handler should have still been fired too.
expect(customOnClick).toHaveBeenCalled();
});
});
it('does not capture click when noTrack is set', async () => {
const linkText = 'Navigate!';
const analyticsApi = new MockAnalyticsApi();
const customOnClick = jest.fn();
const { getByText } = render(
wrapInTestApp(
<TestApiProvider apis={[[analyticsApiRef, analyticsApi]]}>
<Link to="/test" onClick={customOnClick} noTrack>
{linkText}
</Link>
</TestApiProvider>,
),
);
fireEvent.click(getByText(linkText));
// Analytics event should have been fired.
await waitFor(() => {
// Custom onClick handler should have been fired.
expect(customOnClick).toHaveBeenCalled();
// But there should be no analytics event.
expect(analyticsApi.getEvents()).toHaveLength(0);
});
});
describe('isExternalUri', () => {
it.each([
[true, 'http://'],
[true, 'https://'],
[true, 'https://some-host'],
[true, 'https://some-host/path#fragment'],
[true, 'https://some-host/path?param1=value'],
[true, 'slack://'],
[true, 'mailto:[email protected]'],
[true, 'ms-help://'],
[true, 'ms.help://'],
[true, 'ms+help://'],
[false, '//'],
[false, '123://'],
[false, 'abc&xzy://'],
[false, 'http'],
[false, 'path/to'],
[false, 'path/to/something#fragment'],
[false, 'path/to/something?param1=value'],
[false, '/path/to/something'],
[false, '/path/to/something#fragment'],
])('should be %p when %p', (expected, uri) => {
expect(isExternalUri(uri)).toBe(expected);
});
});
});
Example #24
Source File: StudioLayout.tsx From atlas with GNU General Public License v3.0 | 4 votes |
StudioLayout = () => {
const location = useLocation()
const displayedLocation = useVideoWorkspaceRouting()
const internetConnectionStatus = useConnectionStatusStore((state) => state.internetConnectionStatus)
const nodeConnectionStatus = useConnectionStatusStore((state) => state.nodeConnectionStatus)
const {
activeAccountId,
activeMemberId,
activeChannelId,
memberships,
membershipsLoading,
activeMembershipLoading,
extensionConnected,
isLoading,
} = useUser()
const [openUnsupportedBrowserDialog, closeUnsupportedBrowserDialog] = useConfirmationModal()
const [enterLocation] = useState(location.pathname)
const hasMembership = !!memberships?.length
const accountSet = !membershipsLoading && !!activeAccountId
const memberSet = accountSet && !!activeMemberId && hasMembership
const channelSet = memberSet && !!activeChannelId && hasMembership
useEffect(() => {
if (!isAllowedBrowser()) {
openUnsupportedBrowserDialog({
iconType: 'warning',
title: 'Unsupported browser detected',
description:
'It seems the browser you are using is not fully supported by Joystream Studio. Some of the features may not be accessible. For the best experience, please use a recent version of Chrome, Firefox or Edge.',
primaryButton: {
text: 'I understand',
onClick: () => {
closeUnsupportedBrowserDialog()
},
},
onExitClick: () => {
closeUnsupportedBrowserDialog()
},
})
}
}, [closeUnsupportedBrowserDialog, openUnsupportedBrowserDialog])
return (
<>
<TopbarStudio hideChannelInfo={!memberSet} />
<NoConnectionIndicator
hasSidebar={channelSet}
nodeConnectionStatus={nodeConnectionStatus}
isConnectedToInternet={internetConnectionStatus === 'connected'}
/>
{activeMembershipLoading ||
membershipsLoading ||
isLoading ||
extensionConnected === 'pending' ||
extensionConnected === null ? (
<StudioLoading />
) : (
<>
<CSSTransition
in={channelSet}
timeout={parseInt(transitions.timings.regular)}
classNames={SLIDE_ANIMATION}
mountOnEnter
unmountOnExit
>
<StyledSidenavStudio />
</CSSTransition>
<MainContainer hasSidebar={channelSet}>
<Routes location={displayedLocation}>
<Route
path={relativeRoutes.studio.index()}
element={<StudioEntrypoint enterLocation={enterLocation} />}
/>
<Route
path={relativeRoutes.studio.signIn()}
element={
<PrivateRoute element={<StudioWelcomeView />} isAuth={!channelSet} redirectTo={ENTRY_POINT_ROUTE} />
}
/>
<Route
path={relativeRoutes.studio.newChannel()}
element={
<PrivateRoute
element={<CreateEditChannelView newChannel />}
isAuth={memberSet}
redirectTo={ENTRY_POINT_ROUTE}
/>
}
/>
<Route
path={relativeRoutes.studio.editChannel()}
element={
<PrivateRoute
element={<CreateEditChannelView />}
isAuth={channelSet}
redirectTo={ENTRY_POINT_ROUTE}
/>
}
/>
<Route
path={relativeRoutes.studio.uploads()}
element={
<PrivateRoute element={<MyUploadsView />} isAuth={channelSet} redirectTo={ENTRY_POINT_ROUTE} />
}
/>
<Route
path={relativeRoutes.studio.videos()}
element={<PrivateRoute element={<MyVideosView />} isAuth={channelSet} redirectTo={ENTRY_POINT_ROUTE} />}
/>
<Route
path={relativeRoutes.studio.notifications()}
element={
<PrivateRoute element={<NotificationsView />} isAuth={channelSet} redirectTo={ENTRY_POINT_ROUTE} />
}
/>
</Routes>
</MainContainer>
{channelSet && <VideoWorkspace />}
</>
)}
</>
)
}