[Search Profiler] Enable functional tests for serverless (#172656)

## Summary

This PR adds a test that profiles a simple query with a test index to
stateful and enables 2 tests for serverless (loading a query from url
and profiling a simple query with a test index).
This commit is contained in:
Yulia Čech 2023-12-18 16:53:10 +01:00 committed by GitHub
parent 387cb38bd9
commit 7faa488a13
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 222 additions and 33 deletions

View file

@ -6,26 +6,27 @@
*/
import expect from '@kbn/expect';
import { compressToEncodedURIComponent } from 'lz-string';
import { asyncForEach } from '@kbn/std';
import { FtrProviderContext } from '../../ftr_provider_context';
const testIndex = 'test-index';
const testQuery = {
query: {
match_all: {},
},
};
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const PageObjects = getPageObjects(['common']);
const testSubjects = getService('testSubjects');
const aceEditor = getService('aceEditor');
const PageObjects = getPageObjects(['common', 'searchProfiler']);
const retry = getService('retry');
const security = getService('security');
const es = getService('es');
const log = getService('log');
const editorTestSubjectSelector = 'searchProfilerEditor';
describe('Search Profiler Editor', () => {
before(async () => {
await security.testUser.setRoles(['global_devtools_read']);
await PageObjects.common.navigateToApp('searchProfiler');
expect(await testSubjects.exists('searchProfilerEditor')).to.be(true);
expect(await PageObjects.searchProfiler.editorExists()).to.be(true);
});
after(async () => {
@ -36,7 +37,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
// The below inputs are written to work _with_ ace's autocomplete unlike console's unit test
// counterparts in src/legacy/core_plugins/console/public/tests/src/editor.test.js
const okInput = [
const okInputs = [
`{
"query": {
"match_all": {}`,
@ -46,7 +47,7 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
"test": """{ "more": "json" }"""`,
];
const notOkInput = [
const notOkInputs = [
`{
"query": {
"match_all": {
@ -59,24 +60,24 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
const expectHasParseErrorsToBe = (expectation: boolean) => async (inputs: string[]) => {
for (const input of inputs) {
await aceEditor.setValue(editorTestSubjectSelector, input);
await PageObjects.searchProfiler.setQuery(input);
await retry.waitFor(
`parser errors to match expectation: HAS ${expectation ? 'ERRORS' : 'NO ERRORS'}`,
async () => {
const actual = await aceEditor.hasParseErrors(editorTestSubjectSelector);
const actual = await PageObjects.searchProfiler.editorHasParseErrors();
return expectation === actual;
}
);
}
};
await expectHasParseErrorsToBe(false)(okInput);
await expectHasParseErrorsToBe(true)(notOkInput);
await expectHasParseErrorsToBe(false)(okInputs);
await expectHasParseErrorsToBe(true)(notOkInputs);
});
it('supports pre-configured search query', async () => {
const searchQuery = {
const query = {
query: {
bool: {
should: [
@ -106,24 +107,21 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
// this index name is just an input placeholder and does not exist
const indexName = 'my_index';
const searchQueryURI = compressToEncodedURIComponent(JSON.stringify(searchQuery, null, 2));
await PageObjects.common.navigateToUrl(
'searchProfiler',
`/searchprofiler?index=${indexName}&load_from=${searchQueryURI}`,
PageObjects.searchProfiler.getUrlWithIndexAndQuery({ indexName, query }),
{
useActualUrl: true,
}
);
const indexInput = await testSubjects.find('indexName');
const indexInputValue = await indexInput.getAttribute('value');
const indexInputValue = await PageObjects.searchProfiler.getIndexName();
expect(indexInputValue).to.eql(indexName);
await retry.try(async () => {
const searchProfilerInput = JSON.parse(await aceEditor.getValue('searchProfilerEditor'));
expect(searchProfilerInput).to.eql(searchQuery);
const searchProfilerInput = await PageObjects.searchProfiler.getQuery();
expect(searchProfilerInput).to.eql(query);
});
});
@ -148,23 +146,35 @@ export default function ({ getPageObjects, getService }: FtrProviderContext) {
});
it('returns error if profile is executed with no valid indices', async () => {
const input = {
query: {
match_all: {},
},
};
await PageObjects.searchProfiler.setIndexName('_all');
await PageObjects.searchProfiler.setQuery(testQuery);
await testSubjects.setValue('indexName', '_all');
await aceEditor.setValue(editorTestSubjectSelector, JSON.stringify(input));
await testSubjects.click('profileButton');
await PageObjects.searchProfiler.clickProfileButton();
await retry.waitFor('notification renders', async () => {
const notification = await testSubjects.find('noShardsNotification');
const notificationText = await notification.getVisibleText();
return notificationText.includes('Unable to profile');
return await PageObjects.searchProfiler.editorHasErrorNotification();
});
});
});
describe('With a test index', () => {
before(async () => {
await es.indices.create({ index: testIndex });
});
after(async () => {
await es.indices.delete({ index: testIndex });
});
it('profiles a simple query', async () => {
await PageObjects.searchProfiler.setIndexName(testIndex);
await PageObjects.searchProfiler.setQuery(testQuery);
await PageObjects.searchProfiler.clickProfileButton();
const content = await PageObjects.searchProfiler.getProfileContent();
expect(content).to.contain(testIndex);
});
});
});
}

View file

@ -50,6 +50,7 @@ import { UpgradeAssistantPageObject } from './upgrade_assistant_page';
import { UptimePageObject } from './uptime_page';
import { UserProfilePageProvider } from './user_profile_page';
import { WatcherPageObject } from './watcher_page';
import { SearchProfilerPageProvider } from './search_profiler_page';
// just like services, PageObjects are defined as a map of
// names to Providers. Merge in Kibana's or pick specific ones
@ -87,6 +88,7 @@ export const pageObjects = {
reporting: ReportingPageObject,
roleMappings: RoleMappingsPageProvider,
rollup: RollupPageObject,
searchProfiler: SearchProfilerPageProvider,
searchSessionsManagement: SearchSessionsPageProvider,
security: SecurityPageObject,
shareSavedObjectsToSpace: ShareSavedObjectsToSpacePageProvider,

View file

@ -0,0 +1,54 @@
/*
* 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 { compressToEncodedURIComponent } from 'lz-string';
import { FtrProviderContext } from '../ftr_provider_context';
export function SearchProfilerPageProvider({ getService }: FtrProviderContext) {
const find = getService('find');
const testSubjects = getService('testSubjects');
const aceEditor = getService('aceEditor');
const editorTestSubjectSelector = 'searchProfilerEditor';
return {
async editorExists() {
return await testSubjects.exists(editorTestSubjectSelector);
},
async setQuery(query: any) {
await aceEditor.setValue(editorTestSubjectSelector, JSON.stringify(query));
},
async getQuery() {
return JSON.parse(await aceEditor.getValue(editorTestSubjectSelector));
},
async setIndexName(indexName: string) {
await testSubjects.setValue('indexName', indexName);
},
async getIndexName() {
const indexInput = await testSubjects.find('indexName');
return await indexInput.getAttribute('value');
},
async clickProfileButton() {
await testSubjects.click('profileButton');
},
async getProfileContent() {
const profileTree = await find.byClassName('prfDevTool__main__profiletree');
return profileTree.getVisibleText();
},
getUrlWithIndexAndQuery({ indexName, query }: { indexName: string; query: any }) {
const searchQueryURI = compressToEncodedURIComponent(JSON.stringify(query, null, 2));
return `/searchprofiler?index=${indexName}&load_from=${searchQueryURI}`;
},
async editorHasParseErrors() {
return await aceEditor.hasParseErrors(editorTestSubjectSelector);
},
async editorHasErrorNotification() {
const notification = await testSubjects.find('noShardsNotification');
const text = await notification.getVisibleText();
return text.includes('Unable to profile');
},
};
}

View file

@ -103,6 +103,10 @@ export function createTestConfig(options: CreateTestConfigOptions) {
pathname: '/app/discover',
hash: '/context',
},
searchProfiler: {
pathname: '/app/dev_tools',
hash: '/searchprofiler',
},
},
// choose where screenshots should be saved
screenshots: {

View file

@ -0,0 +1,14 @@
/*
* 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 ({ loadTestFile }: FtrProviderContext) => {
describe('Serverless Common UI - Dev Tools', function () {
loadTestFile(require.resolve('./search_profiler'));
});
};

View file

@ -0,0 +1,102 @@
/*
* 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';
const testIndex = 'test-index';
const testQuery = {
query: {
match_all: {},
},
};
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const PageObjects = getPageObjects(['svlCommonPage', 'common', 'searchProfiler']);
const retry = getService('retry');
const es = getService('es');
describe('Search Profiler Editor', () => {
before(async () => {
await PageObjects.svlCommonPage.login();
await PageObjects.common.navigateToApp('searchProfiler');
expect(await PageObjects.searchProfiler.editorExists()).to.be(true);
});
after(async () => {
await PageObjects.svlCommonPage.forceLogout();
});
it('supports pre-configured search query', async () => {
const query = {
query: {
bool: {
should: [
{
match: {
name: 'fred',
},
},
{
terms: {
name: ['sue', 'sally'],
},
},
],
},
},
aggs: {
stats: {
stats: {
field: 'price',
},
},
},
};
// Since we're not actually running the query in the test,
// this index name is just an input placeholder and does not exist
const indexName = 'my_index';
await PageObjects.common.navigateToUrl(
'searchProfiler',
PageObjects.searchProfiler.getUrlWithIndexAndQuery({ indexName, query }),
{
useActualUrl: true,
}
);
const indexInputValue = await PageObjects.searchProfiler.getIndexName();
expect(indexInputValue).to.eql(indexName);
await retry.try(async () => {
const searchProfilerInput = await PageObjects.searchProfiler.getQuery();
expect(searchProfilerInput).to.eql(query);
});
});
describe('With a test index', () => {
before(async () => {
await es.indices.create({ index: testIndex });
});
after(async () => {
await es.indices.delete({ index: testIndex });
});
it('profiles a simple query', async () => {
await PageObjects.searchProfiler.setIndexName(testIndex);
await PageObjects.searchProfiler.setQuery(testQuery);
await PageObjects.searchProfiler.clickProfileButton();
const content = await PageObjects.searchProfiler.getProfileContent();
expect(content).to.contain(testIndex);
});
});
});
}

View file

@ -15,6 +15,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
testFiles: [
require.resolve('../../common/home_page'),
require.resolve('../../common/management'),
require.resolve('../../common/dev_tools'),
require.resolve('../../common/platform_security'),
require.resolve('../../common/reporting'),
require.resolve('../../common/grok_debugger'),

View file

@ -15,6 +15,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
testFiles: [
require.resolve('../../common/home_page'),
require.resolve('../../common/management'),
require.resolve('../../common/dev_tools'),
require.resolve('../../common/platform_security'),
require.resolve('../../common/reporting'),
require.resolve('../../common/console'),

View file

@ -15,6 +15,7 @@ export default async function ({ readConfigFile }: FtrConfigProviderContext) {
testFiles: [
require.resolve('../../common/home_page'),
require.resolve('../../common/management'),
require.resolve('../../common/dev_tools'),
require.resolve('../../common/platform_security'),
require.resolve('../../common/reporting'),
require.resolve('../../common/grok_debugger'),