[Stateful sidenav] Enable on-prem (#203239)

This commit is contained in:
Sébastien Loix 2024-12-11 13:12:15 +00:00 committed by GitHub
parent c423e3e9d3
commit 771166bf7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 139 additions and 352 deletions

View file

@ -264,7 +264,6 @@ enabled:
- x-pack/test/functional/apps/security/config.ts
- x-pack/test/functional/apps/snapshot_restore/config.ts
- x-pack/test/functional/apps/spaces/config.ts
- x-pack/test/functional/apps/spaces/solution_view_flag_enabled/config.ts
- x-pack/test/functional/apps/status_page/config.ts
- x-pack/test/functional/apps/transform/creation/index_pattern/config.ts
- x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/config.ts

View file

@ -9,7 +9,4 @@ export interface ConfigType {
maxSpaces: number;
allowFeatureVisibility: boolean;
allowSolutionVisibility: boolean;
experimental: {
forceSolutionVisibility: boolean;
};
}

View file

@ -27,9 +27,6 @@ describe('ManagementService', () => {
maxSpaces: 1000,
allowFeatureVisibility: true,
allowSolutionVisibility: true,
experimental: {
forceSolutionVisibility: false,
},
};
describe('#setup', () => {

View file

@ -45,9 +45,6 @@ const config: ConfigType = {
maxSpaces: 1000,
allowFeatureVisibility: true,
allowSolutionVisibility: true,
experimental: {
forceSolutionVisibility: false,
},
};
const eventTracker = new EventTracker({ reportEvent: jest.fn() });

View file

@ -72,6 +72,7 @@ export const SolutionViewTour: FC<Props> = ({ children, solution, isTourOpen, on
onFinish={onFinishTour}
step={1}
stepsTotal={1}
repositionOnScroll
title={i18n.translate('xpack.spaces.navControl.tour.title', {
defaultMessage: 'You chose the {solution} solution view',
values: { solution: solutionLabel },

View file

@ -5,7 +5,6 @@
* 2.0.
*/
import { cloudMock } from '@kbn/cloud-plugin/public/mocks';
import { coreMock } from '@kbn/core/public/mocks';
import { homePluginMock } from '@kbn/home-plugin/public/mocks';
import {
@ -20,7 +19,7 @@ describe('Spaces plugin', () => {
it('should register the space selector app when buildFlavor is traditional', () => {
const coreSetup = coreMock.createSetup();
const mockInitializerContext = coreMock.createPluginInitializerContext(
{},
{ allowSolutionVisibility: true },
{ buildFlavor: 'traditional' }
);
@ -40,7 +39,7 @@ describe('Spaces plugin', () => {
it('should not register the space selector app when buildFlavor is serverless and maxSpaces is 1', () => {
const coreSetup = coreMock.createSetup();
const mockInitializerContext = coreMock.createPluginInitializerContext(
{ maxSpaces: 1 },
{ maxSpaces: 1, allowSolutionVisibility: true },
{ buildFlavor: 'serverless' }
);
@ -60,7 +59,7 @@ describe('Spaces plugin', () => {
it('should register the space selector app when buildFlavor is serverless and and maxSpaces is >1', () => {
const coreSetup = coreMock.createSetup();
const mockInitializerContext = coreMock.createPluginInitializerContext(
{ maxSpaces: 2 },
{ maxSpaces: 2, allowSolutionVisibility: true },
{ buildFlavor: 'serverless' }
);
@ -88,7 +87,7 @@ describe('Spaces plugin', () => {
management.sections.section.kibana = mockSection;
const mockInitializerContext = coreMock.createPluginInitializerContext(
{},
{ allowSolutionVisibility: true },
{ buildFlavor: 'traditional' }
);
@ -122,7 +121,9 @@ describe('Spaces plugin', () => {
management.sections.section.kibana = mockSection;
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext({ maxSpaces: 1 }));
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext({ maxSpaces: 1, allowSolutionVisibility: true })
);
plugin.setup(coreSetup, {
management,
home,
@ -152,7 +153,9 @@ describe('Spaces plugin', () => {
management.sections.section.kibana = mockSection;
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext({ maxSpaces: 2 }));
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext({ maxSpaces: 2, allowSolutionVisibility: true })
);
plugin.setup(coreSetup, {
management,
home,
@ -179,7 +182,7 @@ describe('Spaces plugin', () => {
const coreStart = coreMock.createStart();
const mockInitializerContext = coreMock.createPluginInitializerContext(
{},
{ allowSolutionVisibility: true },
{ buildFlavor: 'traditional' }
);
@ -196,7 +199,7 @@ describe('Spaces plugin', () => {
const coreStart = coreMock.createStart();
const mockInitializerContext = coreMock.createPluginInitializerContext(
{ maxSpaces: 1 },
{ maxSpaces: 1, allowSolutionVisibility: true },
{ buildFlavor: 'serverless' }
);
@ -214,7 +217,9 @@ describe('Spaces plugin', () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext({ maxSpaces: 1 }));
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext({ maxSpaces: 1, allowSolutionVisibility: true })
);
const spacesSetup = plugin.setup(coreSetup, {});
const spacesStart = plugin.start(coreStart);
@ -226,7 +231,9 @@ describe('Spaces plugin', () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const plugin = new SpacesPlugin(coreMock.createPluginInitializerContext({ maxSpaces: 1000 }));
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext({ maxSpaces: 1000, allowSolutionVisibility: true })
);
const spacesSetup = plugin.setup(coreSetup, {});
const spacesStart = plugin.start(coreStart);
@ -236,129 +243,45 @@ describe('Spaces plugin', () => {
});
describe('isSolutionViewEnabled', () => {
it('when onCloud, not serverless and allowSolutionVisibility is "true"', () => {
it('when allowSolutionVisibility is "true"', () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const cloud = cloudMock.createSetup();
cloud.isCloudEnabled = true;
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext(
{ allowSolutionVisibility: true },
{ buildFlavor: 'traditional' }
)
coreMock.createPluginInitializerContext({ allowSolutionVisibility: true })
);
const spacesSetup = plugin.setup(coreSetup, { cloud });
const spacesSetup = plugin.setup(coreSetup, {});
const spacesStart = plugin.start(coreStart);
expect(spacesSetup.isSolutionViewEnabled).toBe(true);
expect(spacesStart.isSolutionViewEnabled).toBe(true);
});
it('when not onCloud and allowSolutionVisibility is "true"', () => {
it('when allowSolutionVisibility is "false"', () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
{
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext(
{ allowSolutionVisibility: true }, // it is true but we are not onCloud
{ buildFlavor: 'traditional' }
)
coreMock.createPluginInitializerContext({ allowSolutionVisibility: false })
);
const spacesSetup = plugin.setup(coreSetup, {});
const spacesStart = plugin.start(coreStart);
expect(spacesSetup.isSolutionViewEnabled).toBe(false); // so it should be false
expect(spacesStart.isSolutionViewEnabled).toBe(false);
}
{
// unless the forceSolutionVisibility flag is set
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext(
{ allowSolutionVisibility: false, experimental: { forceSolutionVisibility: true } },
{ buildFlavor: 'traditional' }
)
);
const spacesSetup = plugin.setup(coreSetup, {}); // we are not onCloud but forceSolutionVisibility is true
const spacesStart = plugin.start(coreStart);
expect(spacesSetup.isSolutionViewEnabled).toBe(true);
expect(spacesStart.isSolutionViewEnabled).toBe(true);
}
});
it('when onCloud, not serverless and allowSolutionVisibility is "false"', () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const cloud = cloudMock.createSetup();
cloud.isCloudEnabled = true;
{
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext(
{ allowSolutionVisibility: false },
{ buildFlavor: 'traditional' }
)
);
const spacesSetup = plugin.setup(coreSetup, { cloud });
const spacesStart = plugin.start(coreStart);
expect(spacesSetup.isSolutionViewEnabled).toBe(false);
expect(spacesStart.isSolutionViewEnabled).toBe(false);
}
{
// unless the forceSolutionVisibility flag is set
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext(
{ allowSolutionVisibility: false, experimental: { forceSolutionVisibility: true } },
{ buildFlavor: 'traditional' }
)
);
const spacesSetup = plugin.setup(coreSetup, { cloud });
const spacesStart = plugin.start(coreStart);
expect(spacesSetup.isSolutionViewEnabled).toBe(true);
expect(spacesStart.isSolutionViewEnabled).toBe(true);
}
});
it('when onCloud and serverless', () => {
it('when allowSolutionVisibility is "undefined"', () => {
const coreSetup = coreMock.createSetup();
const coreStart = coreMock.createStart();
const cloud = cloudMock.createSetup();
cloud.isCloudEnabled = true;
{
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext(
{ allowSolutionVisibility: true },
{ buildFlavor: 'serverless' }
)
);
const spacesSetup = plugin.setup(coreSetup, { cloud });
const spacesStart = plugin.start(coreStart);
expect(spacesSetup.isSolutionViewEnabled).toBe(false);
expect(spacesStart.isSolutionViewEnabled).toBe(false);
}
{
// unless the forceSolutionVisibility flag is set
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext(
{ allowSolutionVisibility: true, experimental: { forceSolutionVisibility: true } },
{ buildFlavor: 'serverless' }
)
);
const spacesSetup = plugin.setup(coreSetup, { cloud });
const spacesStart = plugin.start(coreStart);
expect(spacesSetup.isSolutionViewEnabled).toBe(true);
expect(spacesStart.isSolutionViewEnabled).toBe(true);
}
const plugin = new SpacesPlugin(
coreMock.createPluginInitializerContext({ allowSolutionVisibility: undefined })
);
expect(() => plugin.setup(coreSetup, {})).toThrowErrorMatchingInlineSnapshot(
`"allowSolutionVisibility has not been set in the Spaces plugin config."`
);
});
});
});

View file

@ -59,14 +59,11 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart
}
public setup(core: CoreSetup<PluginsStart, SpacesPluginStart>, plugins: PluginsSetup) {
const hasOnlyDefaultSpace = this.config.maxSpaces === 1;
const onCloud = plugins.cloud !== undefined && plugins.cloud.isCloudEnabled;
if (this.config.allowSolutionVisibility === undefined) {
throw new Error('allowSolutionVisibility has not been set in the Spaces plugin config.');
}
// We only allow "solution" to be set on cloud environments, not on prem
// unless the forceSolutionVisibility flag is set
const allowSolutionVisibility =
(onCloud && !this.isServerless && this.config.allowSolutionVisibility) ||
Boolean(this.config.experimental?.forceSolutionVisibility);
const hasOnlyDefaultSpace = this.config.maxSpaces === 1;
this.spacesManager = new SpacesManager(core.http);
this.spacesApi = {
@ -77,12 +74,7 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart
getActiveSpace$: () => this.spacesManager.onActiveSpaceChange$,
getActiveSpace: () => this.spacesManager.getActiveSpace(),
hasOnlyDefaultSpace,
isSolutionViewEnabled: allowSolutionVisibility,
};
this.config = {
...this.config,
allowSolutionVisibility,
isSolutionViewEnabled: this.config.allowSolutionVisibility,
};
registerSpacesEventTypes(core);
@ -167,7 +159,7 @@ export class SpacesPlugin implements Plugin<SpacesPluginSetup, SpacesPluginStart
registerAnalyticsContext(core.analytics, this.spacesManager.onActiveSpaceChange$);
return { hasOnlyDefaultSpace, isSolutionViewEnabled: allowSolutionVisibility };
return { hasOnlyDefaultSpace, isSolutionViewEnabled: this.config.allowSolutionVisibility };
}
public start(core: CoreStart) {

View file

@ -34,9 +34,6 @@ export const config: PluginConfigDescriptor = {
maxSpaces: true,
allowFeatureVisibility: true,
allowSolutionVisibility: true,
experimental: {
forceSolutionVisibility: true,
},
},
};

View file

@ -36,10 +36,7 @@ describe('Spaces plugin', () => {
"hasOnlyDefaultSpace$": Observable {
"operator": [Function],
"source": Observable {
"operator": [Function],
"source": Observable {
"_subscribe": [Function],
},
"_subscribe": [Function],
},
},
"spacesClient": Object {
@ -123,10 +120,7 @@ describe('Spaces plugin', () => {
"hasOnlyDefaultSpace$": Observable {
"operator": [Function],
"source": Observable {
"operator": [Function],
"source": Observable {
"_subscribe": [Function],
},
"_subscribe": [Function],
},
},
"spacesService": Object {

View file

@ -6,7 +6,7 @@
*/
import type { Observable } from 'rxjs';
import { BehaviorSubject, combineLatest, map } from 'rxjs';
import { map } from 'rxjs';
import type { CloudSetup } from '@kbn/cloud-plugin/server';
import type {
@ -120,24 +120,8 @@ export class SpacesPlugin
private defaultSpaceService?: DefaultSpaceService;
private onCloud$ = new BehaviorSubject<boolean>(false);
constructor(private readonly initializerContext: PluginInitializerContext) {
this.config$ = combineLatest([
initializerContext.config.create<ConfigType>(),
this.onCloud$,
]).pipe(
map(
([config, onCloud]): ConfigType => ({
...config,
// We only allow "solution" to be set on cloud environments, not on prem
// unless the forceSolutionVisibility flag is set.
allowSolutionVisibility:
(onCloud && config.allowSolutionVisibility) ||
Boolean(config.experimental?.forceSolutionVisibility),
})
)
);
this.config$ = initializerContext.config.create<ConfigType>();
this.hasOnlyDefaultSpace$ = this.config$.pipe(map(({ maxSpaces }) => maxSpaces === 1));
this.log = initializerContext.logger.get();
this.spacesService = new SpacesService();
@ -148,7 +132,6 @@ export class SpacesPlugin
}
public setup(core: CoreSetup<PluginsStart>, plugins: PluginsSetup): SpacesPluginSetup {
this.onCloud$.next(plugins.cloud !== undefined && plugins.cloud.isCloudEnabled);
const spacesClientSetup = this.spacesClientService.setup({ config$: this.config$ });
core.uiSettings.registerGlobal(getUiSettings());

View file

@ -54,6 +54,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.spaceSelector.clickCreateSpace();
await PageObjects.spaceSelector.clickEnterSpaceName();
await PageObjects.spaceSelector.addSpaceName('space_a');
await PageObjects.spaceSelector.changeSolutionView('classic');
await a11y.testAppSnapshot();
});
@ -90,6 +91,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
await PageObjects.spaceSelector.clickCreateSpace();
await PageObjects.spaceSelector.clickEnterSpaceName();
await PageObjects.spaceSelector.addSpaceName('space_b');
await PageObjects.spaceSelector.changeSolutionView('classic');
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await PageObjects.common.navigateToApp('home');
await PageObjects.spaceSelector.openSpacesNav();

View file

@ -15,10 +15,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
testFiles: [require.resolve('.')],
kbnTestServer: {
...functionalConfig.get('kbnTestServer'),
serverArgs: [
...functionalConfig.get('kbnTestServer.serverArgs'),
'--xpack.spaces.experimental.forceSolutionVisibility=true',
],
serverArgs: [...functionalConfig.get('kbnTestServer.serverArgs')],
},
};
}

View file

@ -15,6 +15,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const testSubjects = getService('testSubjects');
const spacesServices = getService('spaces');
const log = getService('log');
const find = getService('find');
describe('Spaces Management: Create and Edit', () => {
before(async () => {
@ -43,6 +44,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
it('create a space with a given name', async () => {
await PageObjects.spaceSelector.addSpaceName(spaceName);
await PageObjects.spaceSelector.changeSolutionView('classic');
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await testSubjects.existOrFail(`spacesListTableRow-${spaceId}`);
});
@ -60,6 +62,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
name: spaceName,
disabledFeatures: [],
color: '#AABBCC',
solution: 'classic',
});
await PageObjects.common.navigateToApp('spacesManagement');
@ -103,14 +106,100 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
describe('solution view', () => {
it('does not show solution view panel', async () => {
it('does show the solution view panel', async () => {
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
await testSubjects.existOrFail('spaces-view-page');
await testSubjects.existOrFail('spaces-view-page > generalPanel');
await testSubjects.missingOrFail('spaces-view-page > navigationPanel'); // xpack.spaces.allowSolutionVisibility is not enabled, so the solution view picker should not appear
await testSubjects.existOrFail('spaces-view-page > navigationPanel');
});
it('changes the space solution and updates the side navigation', async () => {
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
// Make sure we are on the classic side nav
await testSubjects.existOrFail('mgtSideBarNav');
await testSubjects.missingOrFail('searchSideNav');
// change to Enterprise Search
await PageObjects.spaceSelector.changeSolutionView('es');
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await PageObjects.spaceSelector.confirmModal();
await find.waitForDeletedByCssSelector('.kibanaWelcomeLogo');
// Search side nav is loaded
await testSubjects.existOrFail('searchSideNav');
await testSubjects.missingOrFail('mgtSideBarNav');
// change back to classic
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
await testSubjects.missingOrFail('space-edit-page-user-impact-warning');
await PageObjects.spaceSelector.changeSolutionView('classic');
await testSubjects.existOrFail('space-edit-page-user-impact-warning'); // Warn that the change will impact other users
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await PageObjects.spaceSelector.confirmModal();
await testSubjects.existOrFail('mgtSideBarNav');
await testSubjects.missingOrFail('searchSideNav');
});
});
describe('API-created Space', () => {
before(async () => {
await spacesServices.create({
id: 'foo-space',
name: 'Foo Space',
disabledFeatures: [],
color: '#AABBCC',
});
});
after(async () => {
await spacesServices.delete('foo-space');
});
it('enabled features can be changed while the solution view remains unselected', async () => {
const securityFeatureCheckboxId = 'featureCategoryCheckbox_securitySolution';
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/foo-space', {
shouldUseHashForSubUrl: false,
});
await testSubjects.existOrFail('spaces-view-page');
// ensure security feature is selected by default
expect(await testSubjects.isChecked(securityFeatureCheckboxId)).to.be(true);
// Do not set a solution view first!
await PageObjects.spaceSelector.toggleFeatureCategoryCheckbox('securitySolution');
//
// ensure security feature now unselected
expect(await testSubjects.isChecked(securityFeatureCheckboxId)).to.be(false);
await testSubjects.existOrFail('space-edit-page-user-impact-warning');
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await testSubjects.click('confirmModalConfirmButton');
await testSubjects.existOrFail('spaces-view-page');
await testSubjects.click('foo-space-hyperlink');
await testSubjects.existOrFail('spaces-view-page');
// ensure security feature is still unselected
expect(await testSubjects.isChecked(securityFeatureCheckboxId)).to.be(false);
});
});
});

View file

@ -15,5 +15,6 @@ export default function spacesApp({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./enter_space'));
loadTestFile(require.resolve('./create_edit_space'));
loadTestFile(require.resolve('./spaces_grid'));
loadTestFile(require.resolve('./solution_tour'));
});
}

View file

@ -7,7 +7,7 @@
import expect from '@kbn/expect';
import type { SolutionView, Space } from '@kbn/spaces-plugin/common';
import { FtrProviderContext } from '../../../ftr_provider_context';
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');

View file

@ -1,32 +0,0 @@
/*
* 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 { FtrConfigProviderContext } from '@kbn/test';
/**
* NOTE: The solution view is currently only available in the cloud environment.
* This test suite fakes a cloud environement by setting the cloud.id and cloud.base_url
*/
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js'));
return {
...functionalConfig.getAll(),
testFiles: [require.resolve('.')],
kbnTestServer: {
...functionalConfig.get('kbnTestServer'),
serverArgs: [
...functionalConfig.get('kbnTestServer.serverArgs'),
// Note: the base64 string in the cloud.id config contains the ES endpoint required in the functional tests
'--xpack.cloud.id=ftr_fake_cloud_id:aGVsbG8uY29tOjQ0MyRFUzEyM2FiYyRrYm4xMjNhYmM=',
'--xpack.cloud.base_url=https://cloud.elastic.co',
'--xpack.cloud.deployment_url=/deployments/deploymentId',
],
},
};
}

View file

@ -1,125 +0,0 @@
/*
* 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 expect from '@kbn/expect';
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'settings', 'security', 'spaceSelector']);
const testSubjects = getService('testSubjects');
const spacesService = getService('spaces');
const find = getService('find');
describe('edit space', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
after(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
describe('solution view', () => {
it('does show the solution view panel', async () => {
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
await testSubjects.existOrFail('spaces-view-page');
await testSubjects.existOrFail('spaces-view-page > generalPanel');
await testSubjects.existOrFail('spaces-view-page > navigationPanel');
});
it('changes the space solution and updates the side navigation', async () => {
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
// Make sure we are on the classic side nav
await testSubjects.existOrFail('mgtSideBarNav');
await testSubjects.missingOrFail('searchSideNav');
// change to Enterprise Search
await PageObjects.spaceSelector.changeSolutionView('es');
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await PageObjects.spaceSelector.confirmModal();
await find.waitForDeletedByCssSelector('.kibanaWelcomeLogo');
// Search side nav is loaded
await testSubjects.existOrFail('searchSideNav');
await testSubjects.missingOrFail('mgtSideBarNav');
// change back to classic
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
await testSubjects.missingOrFail('space-edit-page-user-impact-warning');
await PageObjects.spaceSelector.changeSolutionView('classic');
await testSubjects.existOrFail('space-edit-page-user-impact-warning'); // Warn that the change will impact other users
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await PageObjects.spaceSelector.confirmModal();
await testSubjects.existOrFail('mgtSideBarNav');
await testSubjects.missingOrFail('searchSideNav');
});
});
describe('API-created Space', () => {
before(async () => {
await spacesService.create({
id: 'foo-space',
name: 'Foo Space',
disabledFeatures: [],
color: '#AABBCC',
});
});
after(async () => {
await spacesService.delete('foo-space');
});
it('enabled features can be changed while the solution view remains unselected', async () => {
const securityFeatureCheckboxId = 'featureCategoryCheckbox_securitySolution';
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/foo-space', {
shouldUseHashForSubUrl: false,
});
await testSubjects.existOrFail('spaces-view-page');
// ensure security feature is selected by default
expect(await testSubjects.isChecked(securityFeatureCheckboxId)).to.be(true);
// Do not set a solution view first!
await PageObjects.spaceSelector.toggleFeatureCategoryCheckbox('securitySolution');
//
// ensure security feature now unselected
expect(await testSubjects.isChecked(securityFeatureCheckboxId)).to.be(false);
await testSubjects.existOrFail('space-edit-page-user-impact-warning');
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await testSubjects.click('confirmModalConfirmButton');
await testSubjects.existOrFail('spaces-view-page');
await testSubjects.click('foo-space-hyperlink');
await testSubjects.existOrFail('spaces-view-page');
// ensure security feature is still unselected
expect(await testSubjects.isChecked(securityFeatureCheckboxId)).to.be(false);
});
});
});
}

View file

@ -1,15 +0,0 @@
/*
* 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 { FtrProviderContext } from '../../../ftr_provider_context';
export default function spacesApp({ loadTestFile }: FtrProviderContext) {
describe('Spaces app (with solution view)', function spacesAppTestSuite() {
loadTestFile(require.resolve('./create_edit_space'));
loadTestFile(require.resolve('./solution_tour'));
});
}

View file

@ -30,12 +30,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
},
kbnTestServer: {
...functionalConfig.get('kbnTestServer'),
serverArgs: [
...functionalConfig.get('kbnTestServer.serverArgs'),
// Note: the base64 string in the cloud.id config contains the ES endpoint required in the functional tests
'--xpack.cloud.id=ftr_fake_cloud_id:aGVsbG8uY29tOjQ0MyRFUzEyM2FiYyRrYm4xMjNhYmM=',
'--xpack.cloud.base_url=https://cloud.elastic.co',
],
serverArgs: [...functionalConfig.get('kbnTestServer.serverArgs')],
},
};
}

View file

@ -20,12 +20,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
testFiles: [require.resolve('.')],
kbnTestServer: {
...functionalConfig.get('kbnTestServer'),
serverArgs: [
...functionalConfig.get('kbnTestServer.serverArgs'),
// Note: the base64 string in the cloud.id config contains the ES endpoint required in the functional tests
'--xpack.cloud.id=ftr_fake_cloud_id:aGVsbG8uY29tOjQ0MyRFUzEyM2FiYyRrYm4xMjNhYmM=',
'--xpack.cloud.base_url=https://cloud.elastic.co',
],
serverArgs: [...functionalConfig.get('kbnTestServer.serverArgs')],
},
};
}