mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
Update onboarding interstitial to handle default Fleet assets (#108193)
This commit is contained in:
parent
468daebe76
commit
66a06f97df
19 changed files with 380 additions and 19 deletions
|
@ -4,6 +4,7 @@ exports[`should render a Welcome screen with no telemetry disclaimer 1`] = `
|
|||
<EuiPortal>
|
||||
<div
|
||||
className="homWelcome"
|
||||
data-test-subj="homeWelcomeInterstitial"
|
||||
>
|
||||
<header
|
||||
className="homWelcome__header"
|
||||
|
@ -65,6 +66,7 @@ exports[`should render a Welcome screen with the telemetry disclaimer 1`] = `
|
|||
<EuiPortal>
|
||||
<div
|
||||
className="homWelcome"
|
||||
data-test-subj="homeWelcomeInterstitial"
|
||||
>
|
||||
<header
|
||||
className="homWelcome__header"
|
||||
|
@ -163,6 +165,7 @@ exports[`should render a Welcome screen with the telemetry disclaimer when optIn
|
|||
<EuiPortal>
|
||||
<div
|
||||
className="homWelcome"
|
||||
data-test-subj="homeWelcomeInterstitial"
|
||||
>
|
||||
<header
|
||||
className="homWelcome__header"
|
||||
|
@ -261,6 +264,7 @@ exports[`should render a Welcome screen with the telemetry disclaimer when optIn
|
|||
<EuiPortal>
|
||||
<div
|
||||
className="homWelcome"
|
||||
data-test-subj="homeWelcomeInterstitial"
|
||||
>
|
||||
<header
|
||||
className="homWelcome__header"
|
||||
|
|
|
@ -70,15 +70,9 @@ export class Home extends Component {
|
|||
}
|
||||
}, 500);
|
||||
|
||||
const resp = await this.props.find({
|
||||
type: 'index-pattern',
|
||||
fields: ['title'],
|
||||
search: `*`,
|
||||
search_fields: ['title'],
|
||||
perPage: 1,
|
||||
});
|
||||
const { isNewInstance } = await this.props.http.get('/internal/home/new_instance_status');
|
||||
|
||||
this.endLoading({ isNewKibanaInstance: resp.total === 0 });
|
||||
this.endLoading({ isNewKibanaInstance: isNewInstance });
|
||||
} catch (err) {
|
||||
// An error here is relatively unimportant, as it only means we don't provide
|
||||
// some UI niceties.
|
||||
|
|
|
@ -188,7 +188,9 @@ describe('home', () => {
|
|||
defaultProps.localStorage.getItem = sinon.spy(() => 'true');
|
||||
|
||||
const component = await renderHome({
|
||||
find: () => Promise.resolve({ total: 0 }),
|
||||
http: {
|
||||
get: () => Promise.resolve({ isNewInstance: true }),
|
||||
},
|
||||
});
|
||||
|
||||
sinon.assert.calledOnce(defaultProps.localStorage.getItem);
|
||||
|
|
|
@ -33,6 +33,7 @@ export function HomeApp({ directories, solutions }) {
|
|||
addBasePath,
|
||||
environmentService,
|
||||
telemetry,
|
||||
http,
|
||||
} = getServices();
|
||||
const environment = environmentService.getEnvironment();
|
||||
const isCloudEnabled = environment.cloud;
|
||||
|
@ -71,10 +72,10 @@ export function HomeApp({ directories, solutions }) {
|
|||
addBasePath={addBasePath}
|
||||
directories={directories}
|
||||
solutions={solutions}
|
||||
find={savedObjectsClient.find}
|
||||
localStorage={localStorage}
|
||||
urlBasePath={getBasePath()}
|
||||
telemetry={telemetry}
|
||||
http={http}
|
||||
/>
|
||||
</Route>
|
||||
<Route path="*" exact={true} component={RedirectToDefaultApp} />
|
||||
|
|
|
@ -119,7 +119,7 @@ export class Welcome extends React.Component<Props> {
|
|||
const { urlBasePath, telemetry } = this.props;
|
||||
return (
|
||||
<EuiPortal>
|
||||
<div className="homWelcome">
|
||||
<div className="homWelcome" data-test-subj="homeWelcomeInterstitial">
|
||||
<header className="homWelcome__header">
|
||||
<div className="homWelcome__content eui-textCenter">
|
||||
<EuiSpacer size="xl" />
|
||||
|
|
35
src/plugins/home/server/routes/fetch_new_instance_status.ts
Normal file
35
src/plugins/home/server/routes/fetch_new_instance_status.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { IRouter } from 'src/core/server';
|
||||
import { isNewInstance } from '../services/new_instance_status';
|
||||
|
||||
export const registerNewInstanceStatusRoute = (router: IRouter) => {
|
||||
router.get(
|
||||
{
|
||||
path: '/internal/home/new_instance_status',
|
||||
validate: false,
|
||||
},
|
||||
router.handleLegacyErrors(async (context, req, res) => {
|
||||
const { client: soClient } = context.core.savedObjects;
|
||||
const { client: esClient } = context.core.elasticsearch;
|
||||
|
||||
try {
|
||||
return res.ok({
|
||||
body: {
|
||||
isNewInstance: await isNewInstance({ esClient, soClient }),
|
||||
},
|
||||
});
|
||||
} catch (e) {
|
||||
return res.customError({
|
||||
statusCode: 500,
|
||||
});
|
||||
}
|
||||
})
|
||||
);
|
||||
};
|
|
@ -8,7 +8,9 @@
|
|||
|
||||
import { IRouter } from 'src/core/server';
|
||||
import { registerHitsStatusRoute } from './fetch_es_hits_status';
|
||||
import { registerNewInstanceStatusRoute } from './fetch_new_instance_status';
|
||||
|
||||
export const registerRoutes = (router: IRouter) => {
|
||||
registerHitsStatusRoute(router);
|
||||
registerNewInstanceStatusRoute(router);
|
||||
};
|
||||
|
|
129
src/plugins/home/server/services/new_instance_status.test.ts
Normal file
129
src/plugins/home/server/services/new_instance_status.test.ts
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isNewInstance } from './new_instance_status';
|
||||
import { elasticsearchServiceMock, savedObjectsClientMock } from '../../../../core/server/mocks';
|
||||
|
||||
describe('isNewInstance', () => {
|
||||
const esClient = elasticsearchServiceMock.createScopedClusterClient();
|
||||
const soClient = savedObjectsClientMock.create();
|
||||
|
||||
beforeEach(() => jest.resetAllMocks());
|
||||
|
||||
it('returns true when there are no index patterns', async () => {
|
||||
soClient.find.mockResolvedValue({
|
||||
page: 1,
|
||||
per_page: 100,
|
||||
total: 0,
|
||||
saved_objects: [],
|
||||
});
|
||||
expect(await isNewInstance({ esClient, soClient })).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns false when there are any index patterns other than metrics-* or logs-*', async () => {
|
||||
soClient.find.mockResolvedValue({
|
||||
page: 1,
|
||||
per_page: 100,
|
||||
total: 1,
|
||||
saved_objects: [
|
||||
{
|
||||
id: '1',
|
||||
references: [],
|
||||
type: 'index-pattern',
|
||||
score: 99,
|
||||
attributes: { title: 'my-pattern-*' },
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(await isNewInstance({ esClient, soClient })).toEqual(false);
|
||||
});
|
||||
|
||||
describe('when only metrics-* and logs-* index patterns exist', () => {
|
||||
beforeEach(() => {
|
||||
soClient.find.mockResolvedValue({
|
||||
page: 1,
|
||||
per_page: 100,
|
||||
total: 2,
|
||||
saved_objects: [
|
||||
{
|
||||
id: '1',
|
||||
references: [],
|
||||
type: 'index-pattern',
|
||||
score: 99,
|
||||
attributes: { title: 'metrics-*' },
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
references: [],
|
||||
type: 'index-pattern',
|
||||
score: 99,
|
||||
attributes: { title: 'logs-*' },
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('calls /_cat/indices for the index patterns', async () => {
|
||||
await isNewInstance({ esClient, soClient });
|
||||
expect(esClient.asCurrentUser.cat.indices).toHaveBeenCalledWith({
|
||||
index: 'logs-*,metrics-*',
|
||||
format: 'json',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns true if no logs or metrics indices exist', async () => {
|
||||
esClient.asCurrentUser.cat.indices.mockReturnValue(
|
||||
elasticsearchServiceMock.createSuccessTransportRequestPromise([])
|
||||
);
|
||||
expect(await isNewInstance({ esClient, soClient })).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns true if no logs or metrics indices contain data', async () => {
|
||||
esClient.asCurrentUser.cat.indices.mockReturnValue(
|
||||
elasticsearchServiceMock.createSuccessTransportRequestPromise([
|
||||
{ index: '.ds-metrics-foo', 'docs.count': '0' },
|
||||
])
|
||||
);
|
||||
expect(await isNewInstance({ esClient, soClient })).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns true if only metrics-elastic_agent index contains data', async () => {
|
||||
esClient.asCurrentUser.cat.indices.mockReturnValue(
|
||||
elasticsearchServiceMock.createSuccessTransportRequestPromise([
|
||||
{ index: '.ds-metrics-elastic_agent', 'docs.count': '100' },
|
||||
])
|
||||
);
|
||||
expect(await isNewInstance({ esClient, soClient })).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns true if only logs-elastic_agent index contains data', async () => {
|
||||
esClient.asCurrentUser.cat.indices.mockReturnValue(
|
||||
elasticsearchServiceMock.createSuccessTransportRequestPromise([
|
||||
{ index: '.ds-logs-elastic_agent', 'docs.count': '100' },
|
||||
])
|
||||
);
|
||||
expect(await isNewInstance({ esClient, soClient })).toEqual(true);
|
||||
});
|
||||
|
||||
it('returns false if any other logs or metrics indices contain data', async () => {
|
||||
esClient.asCurrentUser.cat.indices.mockReturnValue(
|
||||
elasticsearchServiceMock.createSuccessTransportRequestPromise([
|
||||
{ index: '.ds-metrics-foo', 'docs.count': '100' },
|
||||
])
|
||||
);
|
||||
expect(await isNewInstance({ esClient, soClient })).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns false if an authentication error is thrown', async () => {
|
||||
esClient.asCurrentUser.cat.indices.mockReturnValue(
|
||||
elasticsearchServiceMock.createErrorTransportRequestPromise({})
|
||||
);
|
||||
expect(await isNewInstance({ esClient, soClient })).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
67
src/plugins/home/server/services/new_instance_status.ts
Normal file
67
src/plugins/home/server/services/new_instance_status.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { IScopedClusterClient, SavedObjectsClientContract } from '../../../../core/server';
|
||||
import type { IndexPatternSavedObjectAttrs } from '../../../data/common/index_patterns/index_patterns';
|
||||
|
||||
const LOGS_INDEX_PATTERN = 'logs-*';
|
||||
const METRICS_INDEX_PATTERN = 'metrics-*';
|
||||
|
||||
const INDEX_PREFIXES_TO_IGNORE = [
|
||||
'.ds-metrics-elastic_agent', // ignore index created by Fleet server itself
|
||||
'.ds-logs-elastic_agent', // ignore index created by Fleet server itself
|
||||
];
|
||||
|
||||
interface Deps {
|
||||
esClient: IScopedClusterClient;
|
||||
soClient: SavedObjectsClientContract;
|
||||
}
|
||||
|
||||
export const isNewInstance = async ({ esClient, soClient }: Deps): Promise<boolean> => {
|
||||
const indexPatterns = await soClient.find<IndexPatternSavedObjectAttrs>({
|
||||
type: 'index-pattern',
|
||||
fields: ['title'],
|
||||
search: `*`,
|
||||
searchFields: ['title'],
|
||||
perPage: 100,
|
||||
});
|
||||
|
||||
// If there are no index patterns, assume this is a new instance
|
||||
if (indexPatterns.total === 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If there are any index patterns that are not the default metrics-* and logs-* ones created by Fleet, assume this
|
||||
// is not a new instance
|
||||
if (
|
||||
indexPatterns.saved_objects.some(
|
||||
(ip) =>
|
||||
ip.attributes.title !== LOGS_INDEX_PATTERN && ip.attributes.title !== METRICS_INDEX_PATTERN
|
||||
)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const logsAndMetricsIndices = await esClient.asCurrentUser.cat.indices({
|
||||
index: `${LOGS_INDEX_PATTERN},${METRICS_INDEX_PATTERN}`,
|
||||
format: 'json',
|
||||
});
|
||||
|
||||
const anyIndicesContainerUserData = logsAndMetricsIndices.body
|
||||
// Ignore some data that is shipped by default
|
||||
.filter(({ index }) => !INDEX_PREFIXES_TO_IGNORE.some((prefix) => index?.startsWith(prefix)))
|
||||
// If any other logs and metrics indices have data, return false
|
||||
.some((catResult) => (catResult['docs.count'] ?? '0') !== '0');
|
||||
|
||||
return !anyIndicesContainerUserData;
|
||||
} catch (e) {
|
||||
// If any errors are encountered return false to be safe
|
||||
return false;
|
||||
}
|
||||
};
|
|
@ -41,7 +41,6 @@ export default function () {
|
|||
)}`,
|
||||
`--elasticsearch.username=${kibanaServerTestUser.username}`,
|
||||
`--elasticsearch.password=${kibanaServerTestUser.password}`,
|
||||
`--home.disableWelcomeScreen=true`,
|
||||
// Needed for async search functional tests to introduce a delay
|
||||
`--data.search.aggs.shardDelay.enabled=true`,
|
||||
`--security.showInsecureClusterWarning=false`,
|
||||
|
|
30
test/functional/apps/home/_welcome.ts
Normal file
30
test/functional/apps/home/_welcome.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { FtrProviderContext } from '../../ftr_provider_context';
|
||||
|
||||
export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const browser = getService('browser');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const PageObjects = getPageObjects(['common', 'home']);
|
||||
|
||||
describe('Welcome interstitial', () => {
|
||||
before(async () => {
|
||||
// Need to navigate to page first to clear storage before test can be run
|
||||
await PageObjects.common.navigateToUrl('home', undefined);
|
||||
await browser.clearLocalStorage();
|
||||
await esArchiver.emptyKibanaIndex();
|
||||
});
|
||||
|
||||
it('is displayed on a fresh on-prem install', async () => {
|
||||
await PageObjects.common.navigateToUrl('home', undefined, { disableWelcomePrompt: false });
|
||||
expect(await PageObjects.home.isWelcomeInterstitialDisplayed()).to.be(true);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -21,5 +21,6 @@ export default function ({ getService, loadTestFile }) {
|
|||
loadTestFile(require.resolve('./_newsfeed'));
|
||||
loadTestFile(require.resolve('./_add_data'));
|
||||
loadTestFile(require.resolve('./_sample_data'));
|
||||
loadTestFile(require.resolve('./_welcome'));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ interface NavigateProps {
|
|||
shouldLoginIfPrompted: boolean;
|
||||
useActualUrl: boolean;
|
||||
insertTimestamp: boolean;
|
||||
disableWelcomePrompt: boolean;
|
||||
}
|
||||
export class CommonPageObject extends FtrService {
|
||||
private readonly log = this.ctx.getService('log');
|
||||
|
@ -37,11 +38,17 @@ export class CommonPageObject extends FtrService {
|
|||
* Logins to Kibana as default user and navigates to provided app
|
||||
* @param appUrl Kibana URL
|
||||
*/
|
||||
private async loginIfPrompted(appUrl: string, insertTimestamp: boolean) {
|
||||
private async loginIfPrompted(
|
||||
appUrl: string,
|
||||
insertTimestamp: boolean,
|
||||
disableWelcomePrompt: boolean
|
||||
) {
|
||||
// Disable the welcome screen. This is relevant for environments
|
||||
// which don't allow to use the yml setting, e.g. cloud production.
|
||||
// It is done here so it applies to logins but also to a login re-use.
|
||||
await this.browser.setLocalStorageItem('home:welcome:show', 'false');
|
||||
if (disableWelcomePrompt) {
|
||||
await this.browser.setLocalStorageItem('home:welcome:show', 'false');
|
||||
}
|
||||
|
||||
let currentUrl = await this.browser.getCurrentUrl();
|
||||
this.log.debug(`currentUrl = ${currentUrl}\n appUrl = ${appUrl}`);
|
||||
|
@ -76,6 +83,7 @@ export class CommonPageObject extends FtrService {
|
|||
appConfig,
|
||||
ensureCurrentUrl,
|
||||
shouldLoginIfPrompted,
|
||||
disableWelcomePrompt,
|
||||
useActualUrl,
|
||||
insertTimestamp,
|
||||
} = navigateProps;
|
||||
|
@ -95,7 +103,7 @@ export class CommonPageObject extends FtrService {
|
|||
await alert?.accept();
|
||||
|
||||
const currentUrl = shouldLoginIfPrompted
|
||||
? await this.loginIfPrompted(appUrl, insertTimestamp)
|
||||
? await this.loginIfPrompted(appUrl, insertTimestamp, disableWelcomePrompt)
|
||||
: await this.browser.getCurrentUrl();
|
||||
|
||||
if (ensureCurrentUrl && !currentUrl.includes(appUrl)) {
|
||||
|
@ -117,6 +125,7 @@ export class CommonPageObject extends FtrService {
|
|||
basePath = '',
|
||||
ensureCurrentUrl = true,
|
||||
shouldLoginIfPrompted = true,
|
||||
disableWelcomePrompt = true,
|
||||
useActualUrl = false,
|
||||
insertTimestamp = true,
|
||||
shouldUseHashForSubUrl = true,
|
||||
|
@ -136,6 +145,7 @@ export class CommonPageObject extends FtrService {
|
|||
appConfig,
|
||||
ensureCurrentUrl,
|
||||
shouldLoginIfPrompted,
|
||||
disableWelcomePrompt,
|
||||
useActualUrl,
|
||||
insertTimestamp,
|
||||
});
|
||||
|
@ -156,6 +166,7 @@ export class CommonPageObject extends FtrService {
|
|||
basePath = '',
|
||||
ensureCurrentUrl = true,
|
||||
shouldLoginIfPrompted = true,
|
||||
disableWelcomePrompt = true,
|
||||
useActualUrl = true,
|
||||
insertTimestamp = true,
|
||||
} = {}
|
||||
|
@ -170,6 +181,7 @@ export class CommonPageObject extends FtrService {
|
|||
appConfig,
|
||||
ensureCurrentUrl,
|
||||
shouldLoginIfPrompted,
|
||||
disableWelcomePrompt,
|
||||
useActualUrl,
|
||||
insertTimestamp,
|
||||
});
|
||||
|
@ -202,7 +214,13 @@ export class CommonPageObject extends FtrService {
|
|||
|
||||
async navigateToApp(
|
||||
appName: string,
|
||||
{ basePath = '', shouldLoginIfPrompted = true, hash = '', insertTimestamp = true } = {}
|
||||
{
|
||||
basePath = '',
|
||||
shouldLoginIfPrompted = true,
|
||||
disableWelcomePrompt = true,
|
||||
hash = '',
|
||||
insertTimestamp = true,
|
||||
} = {}
|
||||
) {
|
||||
let appUrl: string;
|
||||
if (this.config.has(['apps', appName])) {
|
||||
|
@ -233,7 +251,7 @@ export class CommonPageObject extends FtrService {
|
|||
this.log.debug('returned from get, calling refresh');
|
||||
await this.browser.refresh();
|
||||
let currentUrl = shouldLoginIfPrompted
|
||||
? await this.loginIfPrompted(appUrl, insertTimestamp)
|
||||
? await this.loginIfPrompted(appUrl, insertTimestamp, disableWelcomePrompt)
|
||||
: await this.browser.getCurrentUrl();
|
||||
|
||||
if (currentUrl.includes('app/kibana')) {
|
||||
|
|
|
@ -30,6 +30,10 @@ export class HomePageObject extends FtrService {
|
|||
return !(await this.testSubjects.exists(`addSampleDataSet${id}`));
|
||||
}
|
||||
|
||||
async isWelcomeInterstitialDisplayed() {
|
||||
return await this.testSubjects.isDisplayed('homeWelcomeInterstitial');
|
||||
}
|
||||
|
||||
async getVisibileSolutions() {
|
||||
const solutionPanels = await this.testSubjects.findAll('~homSolutionPanel', 2000);
|
||||
const panelAttributes = await Promise.all(
|
||||
|
|
|
@ -469,6 +469,15 @@ class BrowserService extends FtrService {
|
|||
await this.driver.executeScript('return window.localStorage.removeItem(arguments[0]);', key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all values in local storage for the focused window/frame.
|
||||
*
|
||||
* @return {Promise<void>}
|
||||
*/
|
||||
public async clearLocalStorage(): Promise<void> {
|
||||
await this.driver.executeScript('return window.localStorage.clear();');
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears session storage for the focused window/frame.
|
||||
*
|
||||
|
|
|
@ -153,7 +153,7 @@ export async function handleInstallPackageFailure({
|
|||
try {
|
||||
const installType = getInstallType({ pkgVersion, installedPkg });
|
||||
if (installType === 'install' || installType === 'reinstall') {
|
||||
logger.error(`uninstalling ${pkgkey} after error installing`);
|
||||
logger.error(`uninstalling ${pkgkey} after error installing: [${error.toString()}]`);
|
||||
await removeInstallation({ savedObjectsClient, pkgkey, esClient });
|
||||
}
|
||||
|
||||
|
@ -271,6 +271,7 @@ async function installPackageFromRegistry({
|
|||
return { assets, status: 'installed', installType };
|
||||
})
|
||||
.catch(async (err: Error) => {
|
||||
logger.warn(`Failure to install package [${pkgName}]: [${err.toString()}]`);
|
||||
await handleInstallPackageFailure({
|
||||
savedObjectsClient,
|
||||
error: err,
|
||||
|
|
17
x-pack/test/fleet_functional/apps/home/index.ts
Normal file
17
x-pack/test/fleet_functional/apps/home/index.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
* 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 (providerContext: FtrProviderContext) {
|
||||
const { loadTestFile } = providerContext;
|
||||
|
||||
describe('home onboarding', function () {
|
||||
this.tags('ciGroup7');
|
||||
loadTestFile(require.resolve('./welcome'));
|
||||
});
|
||||
}
|
48
x-pack/test/fleet_functional/apps/home/welcome.ts
Normal file
48
x-pack/test/fleet_functional/apps/home/welcome.ts
Normal file
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 ({ getService, getPageObjects }: FtrProviderContext) {
|
||||
const browser = getService('browser');
|
||||
const esArchiver = getService('esArchiver');
|
||||
const PageObjects = getPageObjects(['common', 'home']);
|
||||
const kibanaServer = getService('kibanaServer');
|
||||
|
||||
describe('Welcome interstitial', () => {
|
||||
before(async () => {
|
||||
// Need to navigate to page first to clear storage before test can be run
|
||||
await PageObjects.common.navigateToUrl('home', undefined);
|
||||
await browser.clearLocalStorage();
|
||||
await esArchiver.emptyKibanaIndex();
|
||||
});
|
||||
|
||||
/**
|
||||
* When we run this against a Cloud cluster, we also test the case where Fleet server is running
|
||||
* and ingesting elastic_agent data.
|
||||
*/
|
||||
it('is displayed on a fresh install with Fleet setup executed', async () => {
|
||||
// Setup Fleet and verify the metrics index pattern was created
|
||||
await kibanaServer.request({ path: '/api/fleet/setup', method: 'POST' });
|
||||
const metricsIndexPattern = await kibanaServer.savedObjects.get({
|
||||
type: 'index-pattern',
|
||||
id: 'metrics-*',
|
||||
});
|
||||
expect(metricsIndexPattern?.attributes.title).to.eql('metrics-*');
|
||||
|
||||
// Reload the home screen and verify the interstitial is displayed
|
||||
await PageObjects.common.navigateToUrl('home', undefined, { disableWelcomePrompt: false });
|
||||
expect(await PageObjects.home.isWelcomeInterstitialDisplayed()).to.be(true);
|
||||
});
|
||||
|
||||
// Pending tests we should add once the FTR supports Elastic Agent / Fleet Server
|
||||
it('is still displayed after a Fleet server is enrolled with agent metrics');
|
||||
it('is not displayed after an agent is enrolled with system metrics');
|
||||
it('is not displayed after a standalone agent is enrolled with system metrics');
|
||||
});
|
||||
}
|
|
@ -16,7 +16,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
|
|||
return {
|
||||
...xpackFunctionalConfig.getAll(),
|
||||
pageObjects,
|
||||
testFiles: [resolve(__dirname, './apps/fleet')],
|
||||
testFiles: [resolve(__dirname, './apps/fleet'), resolve(__dirname, './apps/home')],
|
||||
junit: {
|
||||
reportName: 'X-Pack Fleet Functional Tests',
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue