[SECURITY] Don't use link for the last breadcrumb in the navigation bar for Security/Spaces management apps (#118117) (#118350)

* fix last breadcrumb item to only be text

* commit using @elastic.co

* cleaner

* tests are coming ;)

* used my brain to simplify it

* fix types

* fix/add jest test

* fix lint + jest tests for spaces

* fix accessibality

Co-authored-by: Xavier Mouligneau <xavier.mouligneau@elastic.co>
This commit is contained in:
Kibana Machine 2021-11-11 10:37:39 -05:00 committed by GitHub
parent 7ed19d7696
commit a09d2a8c38
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 245 additions and 161 deletions

View file

@ -0,0 +1,75 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { render } from '@testing-library/react';
import React from 'react';
import { coreMock } from 'src/core/public/mocks';
import { Breadcrumb, BreadcrumbsProvider, createBreadcrumbsChangeHandler } from './breadcrumb';
describe('security breadcrumbs', () => {
const setBreadcrumbs = jest.fn();
const { chrome } = coreMock.createStart();
beforeEach(() => {
setBreadcrumbs.mockReset();
chrome.docTitle.reset.mockReset();
chrome.docTitle.change.mockReset();
});
it('rendering one breadcrumb and it should NOT have an href attributes', async () => {
render(
<BreadcrumbsProvider onChange={createBreadcrumbsChangeHandler(chrome, setBreadcrumbs)}>
<Breadcrumb text={'Find'} href="/">
<div>{'Find'}</div>
</Breadcrumb>
</BreadcrumbsProvider>
);
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ text: 'Find' }]);
});
it('rendering two breadcrumb and our last breadcrumb should NOT have an href attributes', async () => {
render(
<BreadcrumbsProvider onChange={createBreadcrumbsChangeHandler(chrome, setBreadcrumbs)}>
<Breadcrumb text={'Find'} href="/">
<div>{'Find'}</div>
<Breadcrumb text={'Sandy'} href="/sandy">
<div>{'Sandy is a sweet dog'}</div>
</Breadcrumb>
</Breadcrumb>
</BreadcrumbsProvider>
);
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: '/', text: 'Find' }, { text: 'Sandy' }]);
});
it('rendering three breadcrumb and our last breadcrumb should NOT have an href attributes', async () => {
render(
<BreadcrumbsProvider onChange={createBreadcrumbsChangeHandler(chrome, setBreadcrumbs)}>
<Breadcrumb text={'Find'} href="/">
<div>{'Find'}</div>
<Breadcrumb text={'Sandy'} href="/sandy">
<div>{'Sandy is a sweet dog'}</div>
<Breadcrumb text={'Breed'} href="/sandy/breed">
<div>{'Sandy is a mutts'}</div>
</Breadcrumb>
</Breadcrumb>
</Breadcrumb>
</BreadcrumbsProvider>
);
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([
{ href: '/', text: 'Find' },
{ href: '/sandy', text: 'Sandy' },
{ text: 'Breed' },
]);
});
});

View file

@ -80,11 +80,17 @@ export const BreadcrumbsProvider: FunctionComponent<BreadcrumbsProviderProps> =
const breadcrumbsRef = useRef<BreadcrumbProps[]>([]);
const handleChange = (breadcrumbs: BreadcrumbProps[]) => {
const newBreadcrumbs = breadcrumbs.map((item, index) => {
if (index === breadcrumbs.length - 1) {
return { ...item, href: undefined };
}
return item;
});
if (onChange) {
onChange(breadcrumbs);
onChange(newBreadcrumbs);
} else if (services.chrome) {
const setBreadcrumbs = createBreadcrumbsChangeHandler(services.chrome);
setBreadcrumbs(breadcrumbs);
setBreadcrumbs(newBreadcrumbs);
}
};

View file

@ -56,7 +56,7 @@ describe('apiKeysManagementApp', () => {
});
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: '/', text: 'API keys' }]);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ text: 'API keys' }]);
expect(docTitle.change).toHaveBeenCalledWith(['API keys']);
expect(docTitle.reset).not.toHaveBeenCalled();
expect(container).toMatchInlineSnapshot(`

View file

@ -5,6 +5,14 @@
* 2.0.
*/
import { act } from '@testing-library/react';
import { noop } from 'lodash';
import { coreMock, scopedHistoryMock } from 'src/core/public/mocks';
import type { Unmount } from 'src/plugins/management/public/types';
import { roleMappingsManagementApp } from './role_mappings_management_app';
jest.mock('./role_mappings_grid', () => ({
RoleMappingsGridPage: (props: any) =>
// `docLinks` object is too big to include into test snapshot, so we just check its existence.
@ -23,24 +31,23 @@ jest.mock('./edit_role_mapping', () => ({
})}`,
}));
import { coreMock, scopedHistoryMock } from 'src/core/public/mocks';
import { roleMappingsManagementApp } from './role_mappings_management_app';
async function mountApp(basePath: string, pathname: string) {
const container = document.createElement('div');
const setBreadcrumbs = jest.fn();
const startServices = await coreMock.createSetup().getStartServices();
const unmount = await roleMappingsManagementApp
.create({ getStartServices: () => Promise.resolve(startServices) as any })
.mount({
basePath,
element: container,
setBreadcrumbs,
history: scopedHistoryMock.create({ pathname }),
});
let unmount: Unmount = noop;
await act(async () => {
unmount = await roleMappingsManagementApp
.create({ getStartServices: () => Promise.resolve(startServices) as any })
.mount({
basePath,
element: container,
setBreadcrumbs,
history: scopedHistoryMock.create({ pathname }),
});
});
return { unmount, container, setBreadcrumbs, docTitle: startServices[0].chrome.docTitle };
}
@ -65,7 +72,7 @@ describe('roleMappingsManagementApp', () => {
const { setBreadcrumbs, container, unmount, docTitle } = await mountApp('/', '/');
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Role Mappings' }]);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ text: 'Role Mappings' }]);
expect(docTitle.change).toHaveBeenCalledWith('Role Mappings');
expect(docTitle.reset).not.toHaveBeenCalled();
expect(container).toMatchInlineSnapshot(`
@ -114,8 +121,8 @@ describe('roleMappingsManagementApp', () => {
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([
{ href: `/`, text: 'Role Mappings' },
{ href: `/edit/${encodeURIComponent(roleMappingName)}`, text: roleMappingName },
{ href: '/', text: 'Role Mappings' },
{ text: roleMappingName },
]);
expect(docTitle.change).toHaveBeenCalledWith('Role Mappings');
expect(docTitle.reset).not.toHaveBeenCalled();
@ -139,9 +146,8 @@ describe('roleMappingsManagementApp', () => {
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([
{ href: `/`, text: 'Role Mappings' },
{ href: '/', text: 'Role Mappings' },
{
href: '/edit/some%20%E5%AE%89%E5%85%A8%E6%80%A7%20role%20mapping',
text: roleMappingName,
},
]);

View file

@ -7,13 +7,18 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { Route, Router, Switch, useParams } from 'react-router-dom';
import { Route, Router, useParams } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import type { StartServicesAccessor } from 'src/core/public';
import type { RegisterManagementAppArgs } from 'src/plugins/management/public';
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
import {
Breadcrumb,
BreadcrumbsProvider,
createBreadcrumbsChangeHandler,
} from '../../components/breadcrumb';
import type { PluginStartDependencies } from '../../plugin';
import { tryDecodeURIComponent } from '../url_utils';
@ -27,21 +32,12 @@ export const roleMappingsManagementApp = Object.freeze({
const title = i18n.translate('xpack.security.management.roleMappingsTitle', {
defaultMessage: 'Role Mappings',
});
return {
id: this.id,
order: 40,
title,
async mount({ element, setBreadcrumbs, history }) {
const [coreStart] = await getStartServices();
const roleMappingsBreadcrumbs = [
{
text: title,
href: `/`,
},
];
coreStart.chrome.docTitle.change(title);
const [
[core],
{ RoleMappingsGridPage },
@ -56,20 +52,9 @@ export const roleMappingsManagementApp = Object.freeze({
import('../roles'),
]);
core.chrome.docTitle.change(title);
const roleMappingsAPIClient = new RoleMappingsAPIClient(core.http);
const RoleMappingsGridPageWithBreadcrumbs = () => {
setBreadcrumbs(roleMappingsBreadcrumbs);
return (
<RoleMappingsGridPage
notifications={core.notifications}
rolesAPIClient={new RolesAPIClient(core.http)}
roleMappingsAPI={roleMappingsAPIClient}
docLinks={core.docLinks}
history={history}
navigateToApp={coreStart.application.navigateToApp}
/>
);
};
const EditRoleMappingsPageWithBreadcrumbs = () => {
const { name } = useParams<{ name?: string }>();
@ -78,26 +63,26 @@ export const roleMappingsManagementApp = Object.freeze({
// See https://github.com/elastic/kibana/issues/82440
const decodedName = name ? tryDecodeURIComponent(name) : undefined;
setBreadcrumbs([
...roleMappingsBreadcrumbs,
name
const breadcrumbObj =
name && decodedName
? { text: decodedName, href: `/edit/${encodeURIComponent(name)}` }
: {
text: i18n.translate('xpack.security.roleMappings.createBreadcrumb', {
defaultMessage: 'Create',
}),
},
]);
};
return (
<EditRoleMappingPage
name={decodedName}
roleMappingsAPI={roleMappingsAPIClient}
rolesAPIClient={new RolesAPIClient(core.http)}
notifications={core.notifications}
docLinks={core.docLinks}
history={history}
/>
<Breadcrumb text={breadcrumbObj.text} href={breadcrumbObj.href}>
<EditRoleMappingPage
name={decodedName}
roleMappingsAPI={roleMappingsAPIClient}
rolesAPIClient={new RolesAPIClient(core.http)}
notifications={core.notifications}
docLinks={core.docLinks}
history={history}
/>
</Breadcrumb>
);
};
@ -105,14 +90,25 @@ export const roleMappingsManagementApp = Object.freeze({
<KibanaContextProvider services={core}>
<core.i18n.Context>
<Router history={history}>
<Switch>
<Route path={['/', '']} exact={true}>
<RoleMappingsGridPageWithBreadcrumbs />
</Route>
<Route path="/edit/:name?">
<EditRoleMappingsPageWithBreadcrumbs />
</Route>
</Switch>
<BreadcrumbsProvider
onChange={createBreadcrumbsChangeHandler(core.chrome, setBreadcrumbs)}
>
<Breadcrumb text={title} href="/">
<Route path={['/', '']} exact={true}>
<RoleMappingsGridPage
notifications={core.notifications}
rolesAPIClient={new RolesAPIClient(core.http)}
roleMappingsAPI={roleMappingsAPIClient}
docLinks={core.docLinks}
history={history}
navigateToApp={core.application.navigateToApp}
/>
</Route>
<Route path="/edit/:name?">
<EditRoleMappingsPageWithBreadcrumbs />
</Route>
</Breadcrumb>
</BreadcrumbsProvider>
</Router>
</core.i18n.Context>
</KibanaContextProvider>,
@ -120,7 +116,6 @@ export const roleMappingsManagementApp = Object.freeze({
);
return () => {
coreStart.chrome.docTitle.reset();
unmountComponentAtNode(element);
};
},

View file

@ -5,7 +5,11 @@
* 2.0.
*/
import { act } from '@testing-library/react';
import { noop } from 'lodash';
import { coreMock, scopedHistoryMock } from 'src/core/public/mocks';
import type { Unmount } from 'src/plugins/management/public/types';
import { featuresPluginMock } from '../../../../features/public/mocks';
import { licenseMock } from '../../../common/licensing/index.mock';
@ -29,20 +33,23 @@ async function mountApp(basePath: string, pathname: string) {
const featuresStart = featuresPluginMock.createStart();
const coreStart = coreMock.createStart();
const unmount = await rolesManagementApp
.create({
license: licenseMock.create(),
fatalErrors,
getStartServices: jest
.fn()
.mockResolvedValue([coreStart, { data: {}, features: featuresStart }]),
})
.mount({
basePath,
element: container,
setBreadcrumbs,
history: scopedHistoryMock.create({ pathname }),
});
let unmount: Unmount = noop;
await act(async () => {
unmount = await rolesManagementApp
.create({
license: licenseMock.create(),
fatalErrors,
getStartServices: jest
.fn()
.mockResolvedValue([coreStart, { data: {}, features: featuresStart }]),
})
.mount({
basePath,
element: container,
setBreadcrumbs,
history: scopedHistoryMock.create({ pathname }),
});
});
return { unmount, container, setBreadcrumbs, docTitle: coreStart.chrome.docTitle };
}
@ -71,7 +78,7 @@ describe('rolesManagementApp', () => {
const { setBreadcrumbs, container, unmount, docTitle } = await mountApp('/', '/');
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Roles' }]);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ text: 'Roles' }]);
expect(docTitle.change).toHaveBeenCalledWith('Roles');
expect(docTitle.reset).not.toHaveBeenCalled();
expect(container).toMatchInlineSnapshot(`
@ -116,10 +123,7 @@ describe('rolesManagementApp', () => {
);
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([
{ href: `/`, text: 'Roles' },
{ href: `/edit/${encodeURIComponent(roleName)}`, text: roleName },
]);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Roles' }, { text: roleName }]);
expect(docTitle.change).toHaveBeenCalledWith('Roles');
expect(docTitle.reset).not.toHaveBeenCalled();
expect(container).toMatchInlineSnapshot(`
@ -169,7 +173,6 @@ describe('rolesManagementApp', () => {
expect(setBreadcrumbs).toHaveBeenCalledWith([
{ href: `/`, text: 'Roles' },
{
href: '/edit/some%20%E5%AE%89%E5%85%A8%E6%80%A7%20role',
text: roleName,
},
]);

View file

@ -7,7 +7,7 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { Route, Router, Switch, useParams } from 'react-router-dom';
import { Route, Router, useParams } from 'react-router-dom';
import { i18n } from '@kbn/i18n';
import type { FatalErrorsSetup, StartServicesAccessor } from 'src/core/public';
@ -15,6 +15,11 @@ import type { RegisterManagementAppArgs } from 'src/plugins/management/public';
import { KibanaContextProvider } from '../../../../../../src/plugins/kibana_react/public';
import type { SecurityLicense } from '../../../common/licensing';
import {
Breadcrumb,
BreadcrumbsProvider,
createBreadcrumbsChangeHandler,
} from '../../components/breadcrumb';
import type { PluginStartDependencies } from '../../plugin';
import { tryDecodeURIComponent } from '../url_utils';
@ -35,13 +40,6 @@ export const rolesManagementApp = Object.freeze({
order: 20,
title,
async mount({ element, setBreadcrumbs, history }) {
const rolesBreadcrumbs = [
{
text: title,
href: `/`,
},
];
const [
[startServices, { data, features, spaces }],
{ RolesGridPage },
@ -72,16 +70,6 @@ export const rolesManagementApp = Object.freeze({
chrome.docTitle.change(title);
const rolesAPIClient = new RolesAPIClient(http);
const RolesGridPageWithBreadcrumbs = () => {
setBreadcrumbs(rolesBreadcrumbs);
return (
<RolesGridPage
notifications={notifications}
rolesAPIClient={rolesAPIClient}
history={history}
/>
);
};
const EditRolePageWithBreadcrumbs = ({ action }: { action: 'edit' | 'clone' }) => {
const { roleName } = useParams<{ roleName?: string }>();
@ -90,38 +78,38 @@ export const rolesManagementApp = Object.freeze({
// See https://github.com/elastic/kibana/issues/82440
const decodedRoleName = roleName ? tryDecodeURIComponent(roleName) : undefined;
setBreadcrumbs([
...rolesBreadcrumbs,
action === 'edit' && roleName
const breadcrumbObj =
action === 'edit' && roleName && decodedRoleName
? { text: decodedRoleName, href: `/edit/${encodeURIComponent(roleName)}` }
: {
text: i18n.translate('xpack.security.roles.createBreadcrumb', {
defaultMessage: 'Create',
}),
},
]);
};
const spacesApiUi = spaces?.ui;
return (
<EditRolePage
action={action}
roleName={decodedRoleName}
rolesAPIClient={rolesAPIClient}
userAPIClient={new UserAPIClient(http)}
indicesAPIClient={new IndicesAPIClient(http)}
privilegesAPIClient={new PrivilegesAPIClient(http)}
getFeatures={features.getFeatures}
http={http}
notifications={notifications}
fatalErrors={fatalErrors}
license={license}
docLinks={docLinks}
uiCapabilities={application.capabilities}
indexPatterns={data.indexPatterns}
history={history}
spacesApiUi={spacesApiUi}
/>
<Breadcrumb text={breadcrumbObj.text} href={breadcrumbObj.href}>
<EditRolePage
action={action}
roleName={decodedRoleName}
rolesAPIClient={rolesAPIClient}
userAPIClient={new UserAPIClient(http)}
indicesAPIClient={new IndicesAPIClient(http)}
privilegesAPIClient={new PrivilegesAPIClient(http)}
getFeatures={features.getFeatures}
http={http}
notifications={notifications}
fatalErrors={fatalErrors}
license={license}
docLinks={docLinks}
uiCapabilities={application.capabilities}
indexPatterns={data.indexPatterns}
history={history}
spacesApiUi={spacesApiUi}
/>
</Breadcrumb>
);
};
@ -129,26 +117,32 @@ export const rolesManagementApp = Object.freeze({
<KibanaContextProvider services={startServices}>
<i18nStart.Context>
<Router history={history}>
<Switch>
<Route path={['/', '']} exact={true}>
<RolesGridPageWithBreadcrumbs />
</Route>
<Route path="/edit/:roleName?">
<EditRolePageWithBreadcrumbs action="edit" />
</Route>
<Route path="/clone/:roleName">
<EditRolePageWithBreadcrumbs action="clone" />
</Route>
</Switch>
<BreadcrumbsProvider
onChange={createBreadcrumbsChangeHandler(chrome, setBreadcrumbs)}
>
<Breadcrumb text={title} href="/">
<Route path={['/', '']} exact={true}>
<RolesGridPage
notifications={notifications}
rolesAPIClient={rolesAPIClient}
history={history}
/>
</Route>
<Route path="/edit/:roleName?">
<EditRolePageWithBreadcrumbs action="edit" />
</Route>
<Route path="/clone/:roleName">
<EditRolePageWithBreadcrumbs action="clone" />
</Route>
</Breadcrumb>
</BreadcrumbsProvider>
</Router>
</i18nStart.Context>
</KibanaContextProvider>,
element
);
return () => {
chrome.docTitle.reset();
unmountComponentAtNode(element);
};
},

View file

@ -5,7 +5,11 @@
* 2.0.
*/
import { act } from '@testing-library/react';
import { noop } from 'lodash';
import { coreMock, scopedHistoryMock } from 'src/core/public/mocks';
import type { Unmount } from 'src/plugins/management/public/types';
import { securityMock } from '../../mocks';
import { usersManagementApp } from './users_management_app';
@ -22,16 +26,19 @@ describe('usersManagementApp', () => {
const setBreadcrumbs = jest.fn();
const history = scopedHistoryMock.create({ pathname: '/create' });
const unmount = await usersManagementApp.create({ authc, getStartServices }).mount({
basePath: '/',
element,
setBreadcrumbs,
history,
let unmount: Unmount = noop;
await act(async () => {
unmount = await usersManagementApp.create({ authc, getStartServices }).mount({
basePath: '/',
element,
setBreadcrumbs,
history,
});
});
expect(setBreadcrumbs).toHaveBeenLastCalledWith([
{ href: '/', text: 'Users' },
{ href: '/create', text: 'Create' },
{ text: 'Create' },
]);
unmount();

View file

@ -119,7 +119,6 @@ export const usersManagementApp = Object.freeze({
);
return () => {
coreStart.chrome.docTitle.reset();
unmountComponentAtNode(element);
};
},

View file

@ -77,7 +77,7 @@ describe('spacesManagementApp', () => {
const { setBreadcrumbs, container, unmount, docTitle } = await mountApp('/', '/');
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ href: `/`, text: 'Spaces' }]);
expect(setBreadcrumbs).toHaveBeenCalledWith([{ text: 'Spaces' }]);
expect(docTitle.change).toHaveBeenCalledWith('Spaces');
expect(docTitle.reset).not.toHaveBeenCalled();
expect(container).toMatchInlineSnapshot(`
@ -102,7 +102,7 @@ describe('spacesManagementApp', () => {
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([
{ href: `/`, text: 'Spaces' },
{ href: '/create', text: 'Create' },
{ text: 'Create' },
]);
expect(docTitle.change).toHaveBeenCalledWith('Spaces');
expect(docTitle.reset).not.toHaveBeenCalled();
@ -134,7 +134,7 @@ describe('spacesManagementApp', () => {
expect(setBreadcrumbs).toHaveBeenCalledTimes(1);
expect(setBreadcrumbs).toHaveBeenCalledWith([
{ href: `/`, text: 'Spaces' },
{ href: `/edit/${spaceId}`, text: `space with id some-space` },
{ text: `space with id some-space` },
]);
expect(docTitle.change).toHaveBeenCalledWith('Spaces');
expect(docTitle.reset).not.toHaveBeenCalled();

View file

@ -43,18 +43,16 @@ export const spacesManagementApp = Object.freeze({
const [[coreStart, { features }], { SpacesGridPage }, { ManageSpacePage }] =
await Promise.all([getStartServices(), import('./spaces_grid'), import('./edit_space')]);
const spacesBreadcrumbs = [
{
text: title,
href: `/`,
},
];
const spacesFirstBreadcrumb = {
text: title,
href: `/`,
};
const { notifications, i18n: i18nStart, application, chrome } = coreStart;
chrome.docTitle.change(title);
const SpacesGridPageWithBreadcrumbs = () => {
setBreadcrumbs(spacesBreadcrumbs);
setBreadcrumbs([{ ...spacesFirstBreadcrumb, href: undefined }]);
return (
<SpacesGridPage
capabilities={application.capabilities}
@ -69,12 +67,11 @@ export const spacesManagementApp = Object.freeze({
const CreateSpacePageWithBreadcrumbs = () => {
setBreadcrumbs([
...spacesBreadcrumbs,
spacesFirstBreadcrumb,
{
text: i18n.translate('xpack.spaces.management.createSpaceBreadcrumb', {
defaultMessage: 'Create',
}),
href: '/create',
},
]);
@ -94,10 +91,9 @@ export const spacesManagementApp = Object.freeze({
const onLoadSpace = (space: Space) => {
setBreadcrumbs([
...spacesBreadcrumbs,
spacesFirstBreadcrumb,
{
text: space.name,
href: `/edit/${encodeURIComponent(space.id)}`,
},
]);
};

View file

@ -88,11 +88,13 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('a11y test for edit user panel', async () => {
await PageObjects.settings.clickLinkText('Users');
await PageObjects.settings.clickLinkText('deleteA11y');
await a11y.testAppSnapshot();
});
it('a11y test for change password screen', async () => {
await PageObjects.settings.clickLinkText('Users');
await PageObjects.settings.clickLinkText('deleteA11y');
await find.clickByButtonText('Change password');
await a11y.testAppSnapshot();
@ -100,6 +102,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
});
it('a11y test for deactivate user screen', async () => {
await PageObjects.settings.clickLinkText('Users');
await PageObjects.settings.clickLinkText('deleteA11y');
await find.clickByButtonText('Deactivate user');
await a11y.testAppSnapshot();