mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
Scout: run tests in parallel (with spaces) (#207253)
## Summary This PR adds `spaceTest` interface to `kbn-scout` to run space aware tests, that can be executed in parallel. Most of Discover tests were converted to parallel run because we see runtime improvement with 2 parallel workers. Experiment 1: **ES data pre-ingested**, running 9 Discover **stateful** tests in **5 files** locally | Run setup | Took time | | ------------- | ------------- | | 1 worker | `1.3` min | | 2 workers | `58.7` sec | | 3 workers | `48.3` sec | | 4 workers | **tests fail** | Conclusion: using **2** workers is the optimal solution to continue Experiment 2: Running Discover tests for stateful/serverless in **Kibana CI** (starting servers, ingesting ES data, running tests) | Run setup | 1 worker | 2 workers | diff | ------------- | ------------- |------------- |------------- | | stateful, 9 tests / 5 files | `1.7` min | `1.2` min | `-29.4%`| | svl ES, 8 tests / 4 files | `1.7` min | `1.3` min | `-23.5%`| | svl Oblt, 8 tests / 4 files | `1.8` min | `1.4` min | `-22.2%`| | svl Search, 5 tests / 2 files | `59.9` sec | `51.6` sec | `-13.8%`| Conclusion: parallel run effectiveness benefits from tests being split in **more test files**. Experiment 3: Clone existing tests to have **3 times more test files** and re-run tests for stateful/serverless in **Kibana CI** (starting servers, ingesting ES data, running tests) | Run setup | 1 worker | 2 workers | diff | ------------- | ------------- |------------- |------------- | | stateful, 27 tests / 15 files | `4.3` min | `2.7` min | `-37.2%`| | svl ES, 24 tests / 12 files | `4.3` min | `2.7` min | `-37.2%`| Conclusion: parallel run effectiveness is **increasing** with more test files in place, **not linear** but with good test design we can expect **up to 40%** or maybe a bit more. How parallel run works: - `scoutSpace` fixture is loaded on Playwright worker setup (using `auto: true` config), creates a new Kibana Space, expose its id to other fixtures and deletes the space on teardown. - `browserAuth` fixture for parallel run caches Cookie per worker/space like `role:spaceId`. It is needed because Playwright doesn't spin up new browser for worker, but only new context. - kbnClient was updated to allow passing `createNewCopies: true` in query, it is needed to load the same Saved Objects in parallel workers/spaces and generate new ids to work with them. `scoutSpace` caches ids and allows to reach saved object by its name. This logic is different from single thread run, where we can use default ids from kbnArchives. How to run parallel tests locally, e.g. for stateful: ``` node scripts/scout run-tests --stateful --config x-pack/platform/plugins/private/discover_enhanced/ui_tests/parallel.playwright.config.ts ```
This commit is contained in:
parent
d7f801ab3e
commit
14c3235182
54 changed files with 1085 additions and 445 deletions
|
@ -26,6 +26,7 @@ EXIT_CODE=0
|
|||
|
||||
# Discovery Enhanced
|
||||
for run_mode in "--stateful" "--serverless=es" "--serverless=oblt" "--serverless=security"; do
|
||||
run_tests "Discovery Enhanced: Parallel Workers" "x-pack/platform/plugins/private/discover_enhanced/ui_tests/parallel.playwright.config.ts" "$run_mode"
|
||||
run_tests "Discovery Enhanced" "x-pack/platform/plugins/private/discover_enhanced/ui_tests/playwright.config.ts" "$run_mode"
|
||||
done
|
||||
|
||||
|
|
|
@ -8,15 +8,24 @@
|
|||
*/
|
||||
|
||||
export * as cli from './src/cli';
|
||||
export { expect, test, tags, createPlaywrightConfig, createLazyPageObject } from './src/playwright';
|
||||
export {
|
||||
expect,
|
||||
test,
|
||||
spaceTest,
|
||||
tags,
|
||||
createPlaywrightConfig,
|
||||
createLazyPageObject,
|
||||
ingestTestDataHook,
|
||||
} from './src/playwright';
|
||||
export type {
|
||||
ScoutPage,
|
||||
ScoutPlaywrightOptions,
|
||||
ScoutTestOptions,
|
||||
ScoutPage,
|
||||
PageObjects,
|
||||
ScoutTestFixtures,
|
||||
ScoutWorkerFixtures,
|
||||
EsArchiverFixture,
|
||||
ScoutParallelTestFixtures,
|
||||
ScoutParallelWorkerFixtures,
|
||||
} from './src/playwright';
|
||||
|
||||
export type { Client, KbnClient, KibanaUrl, SamlSessionManager, ToolingLog } from './src/types';
|
||||
|
|
|
@ -55,8 +55,9 @@ export class KibanaUrl {
|
|||
* @param appName name of the app to get the URL for
|
||||
* @param options optional modifications to apply to the URL
|
||||
*/
|
||||
app(appName: string, options?: PathOptions) {
|
||||
return this.get(`/app/${appName}`, options);
|
||||
app(appName: string, options?: { space?: string; pathOptions?: PathOptions }) {
|
||||
const relPath = options?.space ? `s/${options.space}/app/${appName}` : `/app/${appName}`;
|
||||
return this.get(relPath, options?.pathOptions);
|
||||
}
|
||||
|
||||
toString() {
|
||||
|
|
|
@ -21,6 +21,7 @@ export function createPlaywrightConfig(options: ScoutPlaywrightOptions): Playwri
|
|||
|
||||
return defineConfig<ScoutTestOptions>({
|
||||
testDir: options.testDir,
|
||||
globalSetup: options.globalSetup,
|
||||
/* Run tests in files in parallel */
|
||||
fullyParallel: false,
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
|
|
|
@ -7,20 +7,5 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { mergeTests } from '@playwright/test';
|
||||
|
||||
import { scoutWorkerFixtures } from './worker';
|
||||
import { scoutTestFixtures } from './test';
|
||||
|
||||
export const scoutCoreFixtures = mergeTests(scoutWorkerFixtures, scoutTestFixtures);
|
||||
|
||||
export type {
|
||||
EsArchiverFixture,
|
||||
ScoutTestFixtures,
|
||||
ScoutWorkerFixtures,
|
||||
ScoutPage,
|
||||
Client,
|
||||
KbnClient,
|
||||
KibanaUrl,
|
||||
ToolingLog,
|
||||
} from './types';
|
||||
export * from './single_thread_fixtures';
|
||||
export * from './parallel_run_fixtures';
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { mergeTests } from 'playwright/test';
|
||||
import { coreWorkerFixtures, scoutSpaceParallelFixture } from './worker';
|
||||
import type {
|
||||
EsClient,
|
||||
KbnClient,
|
||||
KibanaUrl,
|
||||
ScoutSpaceParallelFixture,
|
||||
ScoutTestConfig,
|
||||
ToolingLog,
|
||||
} from './worker';
|
||||
import {
|
||||
scoutPageParallelFixture,
|
||||
browserAuthParallelFixture,
|
||||
pageObjectsParallelFixture,
|
||||
validateTagsFixture,
|
||||
} from './test';
|
||||
import type { BrowserAuthFixture, ScoutPage, PageObjects } from './test';
|
||||
|
||||
export const scoutParallelFixtures = mergeTests(
|
||||
// worker scope fixtures
|
||||
coreWorkerFixtures,
|
||||
scoutSpaceParallelFixture,
|
||||
// test scope fixtures
|
||||
browserAuthParallelFixture,
|
||||
scoutPageParallelFixture,
|
||||
pageObjectsParallelFixture,
|
||||
validateTagsFixture
|
||||
);
|
||||
|
||||
export interface ScoutParallelTestFixtures {
|
||||
browserAuth: BrowserAuthFixture;
|
||||
page: ScoutPage;
|
||||
pageObjects: PageObjects;
|
||||
}
|
||||
|
||||
export interface ScoutParallelWorkerFixtures {
|
||||
log: ToolingLog;
|
||||
config: ScoutTestConfig;
|
||||
kbnUrl: KibanaUrl;
|
||||
kbnClient: KbnClient;
|
||||
esClient: EsClient;
|
||||
scoutSpace: ScoutSpaceParallelFixture;
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { mergeTests } from 'playwright/test';
|
||||
import { coreWorkerFixtures, esArchiverFixture, uiSettingsFixture } from './worker';
|
||||
import type {
|
||||
EsArchiverFixture,
|
||||
EsClient,
|
||||
KbnClient,
|
||||
KibanaUrl,
|
||||
ScoutTestConfig,
|
||||
ToolingLog,
|
||||
UiSettingsFixture,
|
||||
} from './worker';
|
||||
import {
|
||||
scoutPageFixture,
|
||||
browserAuthFixture,
|
||||
pageObjectsFixture,
|
||||
validateTagsFixture,
|
||||
BrowserAuthFixture,
|
||||
ScoutPage,
|
||||
PageObjects,
|
||||
} from './test';
|
||||
export type { PageObjects, ScoutPage } from './test';
|
||||
|
||||
export const scoutFixtures = mergeTests(
|
||||
// worker scope fixtures
|
||||
coreWorkerFixtures,
|
||||
esArchiverFixture,
|
||||
uiSettingsFixture,
|
||||
// test scope fixtures
|
||||
browserAuthFixture,
|
||||
scoutPageFixture,
|
||||
pageObjectsFixture,
|
||||
validateTagsFixture
|
||||
);
|
||||
|
||||
export interface ScoutTestFixtures {
|
||||
browserAuth: BrowserAuthFixture;
|
||||
page: ScoutPage;
|
||||
pageObjects: PageObjects;
|
||||
}
|
||||
|
||||
export interface ScoutWorkerFixtures {
|
||||
log: ToolingLog;
|
||||
config: ScoutTestConfig;
|
||||
kbnUrl: KibanaUrl;
|
||||
kbnClient: KbnClient;
|
||||
esClient: EsClient;
|
||||
esArchiver: EsArchiverFixture;
|
||||
uiSettings: UiSettingsFixture;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export type LoginFunction = (role: string) => Promise<void>;
|
||||
|
||||
export interface BrowserAuthFixture {
|
||||
/**
|
||||
* Logs in as a user with viewer-only permissions.
|
||||
* @returns A Promise that resolves once the cookie in browser is set.
|
||||
*/
|
||||
loginAsViewer: () => Promise<void>;
|
||||
/**
|
||||
* Logs in as a user with administrative privileges
|
||||
* @returns A Promise that resolves once the cookie in browser is set.
|
||||
*/
|
||||
loginAsAdmin: () => Promise<void>;
|
||||
/**
|
||||
* Logs in as a user with elevated, but not admin, permissions.
|
||||
* @returns A Promise that resolves once the cookie in browser is set.
|
||||
*/
|
||||
loginAsPrivilegedUser: () => Promise<void>;
|
||||
}
|
||||
|
||||
export { browserAuthParallelFixture } from './parallel';
|
||||
export { browserAuthFixture } from './single_thread';
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { BrowserAuthFixture, LoginFunction } from '.';
|
||||
import { PROJECT_DEFAULT_ROLES } from '../../../../common';
|
||||
import { serviceLoadedMsg } from '../../../utils';
|
||||
import { coreWorkerFixtures } from '../../worker';
|
||||
import { ScoutSpaceParallelFixture } from '../../worker/scout_space';
|
||||
|
||||
/**
|
||||
* The "browserAuth" fixture simplifies the process of logging into Kibana with
|
||||
* different roles during tests. It uses the "samlAuth" fixture to create an authentication session
|
||||
* for the specified role and the "context" fixture to update the cookie with the role-scoped session.
|
||||
*/
|
||||
export const browserAuthParallelFixture = coreWorkerFixtures.extend<
|
||||
{ browserAuth: BrowserAuthFixture },
|
||||
{ scoutSpace: ScoutSpaceParallelFixture }
|
||||
>({
|
||||
browserAuth: async ({ log, context, samlAuth, config, scoutSpace }, use) => {
|
||||
const setSessionCookie = async (cookieValue: string) => {
|
||||
await context.clearCookies();
|
||||
await context.addCookies([
|
||||
{
|
||||
name: 'sid',
|
||||
value: cookieValue,
|
||||
path: '/',
|
||||
domain: 'localhost',
|
||||
},
|
||||
]);
|
||||
};
|
||||
|
||||
const loginAs: LoginFunction = async (role) => {
|
||||
const spaceId = scoutSpace.id;
|
||||
const cookie = await samlAuth.getInteractiveUserSessionCookieWithRoleScope(role, { spaceId });
|
||||
await setSessionCookie(cookie);
|
||||
};
|
||||
|
||||
const loginAsAdmin = () => loginAs('admin');
|
||||
const loginAsViewer = () => loginAs('viewer');
|
||||
const loginAsPrivilegedUser = () => {
|
||||
const roleName = config.serverless
|
||||
? PROJECT_DEFAULT_ROLES.get(config.projectType!)!
|
||||
: 'editor';
|
||||
return loginAs(roleName);
|
||||
};
|
||||
|
||||
log.debug(serviceLoadedMsg(`browserAuth:${scoutSpace.id}`));
|
||||
await use({ loginAsAdmin, loginAsViewer, loginAsPrivilegedUser });
|
||||
},
|
||||
});
|
|
@ -7,19 +7,17 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { test as base } from '@playwright/test';
|
||||
import { PROJECT_DEFAULT_ROLES } from '../../../common';
|
||||
import { LoginFixture, ScoutWorkerFixtures } from '../types';
|
||||
import { serviceLoadedMsg } from '../../utils';
|
||||
|
||||
type LoginFunction = (role: string) => Promise<void>;
|
||||
import { PROJECT_DEFAULT_ROLES } from '../../../../common';
|
||||
import { serviceLoadedMsg } from '../../../utils';
|
||||
import { coreWorkerFixtures } from '../../worker';
|
||||
import { BrowserAuthFixture, LoginFunction } from '.';
|
||||
|
||||
/**
|
||||
* The "browserAuth" fixture simplifies the process of logging into Kibana with
|
||||
* different roles during tests. It uses the "samlAuth" fixture to create an authentication session
|
||||
* for the specified role and the "context" fixture to update the cookie with the role-scoped session.
|
||||
*/
|
||||
export const browserAuthFixture = base.extend<{ browserAuth: LoginFixture }, ScoutWorkerFixtures>({
|
||||
export const browserAuthFixture = coreWorkerFixtures.extend<{ browserAuth: BrowserAuthFixture }>({
|
||||
browserAuth: async ({ log, context, samlAuth, config }, use) => {
|
||||
const setSessionCookie = async (cookieValue: string) => {
|
||||
await context.clearCookies();
|
|
@ -7,15 +7,10 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { mergeTests } from '@playwright/test';
|
||||
import { browserAuthFixture } from './browser_auth';
|
||||
import { scoutPageFixture } from './page';
|
||||
import { pageObjectsFixture } from './page_objects';
|
||||
import { validateTagsFixture } from './validate_tags';
|
||||
|
||||
export const scoutTestFixtures = mergeTests(
|
||||
browserAuthFixture,
|
||||
scoutPageFixture,
|
||||
pageObjectsFixture,
|
||||
validateTagsFixture
|
||||
);
|
||||
export { browserAuthFixture, browserAuthParallelFixture } from './browser_auth';
|
||||
export type { BrowserAuthFixture } from './browser_auth';
|
||||
export { scoutPageFixture, scoutPageParallelFixture } from './scout_page';
|
||||
export type { ScoutPage } from './scout_page';
|
||||
export { validateTagsFixture } from './validate_tags';
|
||||
export { pageObjectsFixture, pageObjectsParallelFixture } from './page_objects';
|
||||
export type { PageObjects } from './page_objects';
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export type LoginFunction = (role: string) => Promise<void>;
|
||||
|
||||
export type { PageObjects } from '../../../page_objects';
|
||||
|
||||
export { pageObjectsParallelFixture } from './parallel';
|
||||
export { pageObjectsFixture } from './single_thread';
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { PageObjects, createCorePageObjects } from '../../../page_objects';
|
||||
import { serviceLoadedMsg } from '../../../utils';
|
||||
import { ScoutSpaceParallelFixture } from '../../worker';
|
||||
import { scoutPageParallelFixture } from '../scout_page';
|
||||
|
||||
/**
|
||||
* The "pageObjects" fixture provides a centralized and consistent way to access and
|
||||
* interact with reusable Page Objects in tests. This fixture automatically
|
||||
* initializes core Page Objects and makes them available to tests, promoting
|
||||
* modularity and reducing redundant setup.
|
||||
*
|
||||
* Note: Page Objects are lazily instantiated on first access.
|
||||
*/
|
||||
export const pageObjectsParallelFixture = scoutPageParallelFixture.extend<
|
||||
{
|
||||
pageObjects: PageObjects;
|
||||
},
|
||||
{ scoutSpace: ScoutSpaceParallelFixture }
|
||||
>({
|
||||
pageObjects: async ({ page, log, scoutSpace }, use) => {
|
||||
const corePageObjects = createCorePageObjects(page);
|
||||
log.debug(serviceLoadedMsg(`pageObjects:${scoutSpace.id}`));
|
||||
await use(corePageObjects);
|
||||
},
|
||||
});
|
|
@ -7,9 +7,9 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { test as base } from '@playwright/test';
|
||||
import { ScoutTestFixtures, ScoutWorkerFixtures } from '../types';
|
||||
import { createCorePageObjects } from '../../page_objects';
|
||||
import { PageObjects, createCorePageObjects } from '../../../page_objects';
|
||||
import { serviceLoadedMsg } from '../../../utils';
|
||||
import { scoutPageFixture } from '../scout_page';
|
||||
|
||||
/**
|
||||
* The "pageObjects" fixture provides a centralized and consistent way to access and
|
||||
|
@ -19,10 +19,12 @@ import { createCorePageObjects } from '../../page_objects';
|
|||
*
|
||||
* Note: Page Objects are lazily instantiated on first access.
|
||||
*/
|
||||
export const pageObjectsFixture = base.extend<ScoutTestFixtures, ScoutWorkerFixtures>({
|
||||
pageObjects: async ({ page }, use) => {
|
||||
export const pageObjectsFixture = scoutPageFixture.extend<{
|
||||
pageObjects: PageObjects;
|
||||
}>({
|
||||
pageObjects: async ({ page, log }, use) => {
|
||||
const corePageObjects = createCorePageObjects(page);
|
||||
|
||||
log.debug(serviceLoadedMsg(`pageObjects`));
|
||||
await use(corePageObjects);
|
||||
},
|
||||
});
|
|
@ -7,32 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { Page } from 'playwright/test';
|
||||
import { PageObjects } from '../../page_objects';
|
||||
|
||||
export interface ScoutTestFixtures {
|
||||
browserAuth: LoginFixture;
|
||||
page: ScoutPage;
|
||||
pageObjects: PageObjects;
|
||||
}
|
||||
|
||||
export interface LoginFixture {
|
||||
/**
|
||||
* Logs in as a user with viewer-only permissions.
|
||||
* @returns A Promise that resolves once the cookie in browser is set.
|
||||
*/
|
||||
loginAsViewer: () => Promise<void>;
|
||||
/**
|
||||
* Logs in as a user with administrative privileges
|
||||
* @returns A Promise that resolves once the cookie in browser is set.
|
||||
*/
|
||||
loginAsAdmin: () => Promise<void>;
|
||||
/**
|
||||
* Logs in as a user with elevated, but not admin, permissions.
|
||||
* @returns A Promise that resolves once the cookie in browser is set.
|
||||
*/
|
||||
loginAsPrivilegedUser: () => Promise<void>;
|
||||
}
|
||||
import { Page } from '@playwright/test';
|
||||
|
||||
/**
|
||||
* Extends the Playwright 'Page' interface with methods specific to Kibana.
|
||||
|
@ -124,3 +99,6 @@ export type ScoutPage = Page & {
|
|||
clearInput: (selector: string) => Promise<void>;
|
||||
};
|
||||
};
|
||||
|
||||
export { scoutPageFixture } from './single_thread';
|
||||
export { scoutPageParallelFixture } from './parallel';
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { Page, test as base } from '@playwright/test';
|
||||
import { ScoutPage } from '.';
|
||||
import { KibanaUrl, ToolingLog } from '../../worker';
|
||||
import { ScoutSpaceParallelFixture } from '../../worker/scout_space';
|
||||
import { extendPlaywrightPage } from './single_thread';
|
||||
import { serviceLoadedMsg } from '../../../utils';
|
||||
|
||||
export const scoutPageParallelFixture = base.extend<
|
||||
{ page: ScoutPage },
|
||||
{ log: ToolingLog; kbnUrl: KibanaUrl; scoutSpace: ScoutSpaceParallelFixture }
|
||||
>({
|
||||
page: async (
|
||||
{
|
||||
log,
|
||||
page,
|
||||
kbnUrl,
|
||||
scoutSpace,
|
||||
}: { log: ToolingLog; page: Page; kbnUrl: KibanaUrl; scoutSpace: ScoutSpaceParallelFixture },
|
||||
use: (extendedPage: ScoutPage) => Promise<void>
|
||||
) => {
|
||||
const extendedPage = extendPlaywrightPage({ page, kbnUrl });
|
||||
|
||||
// Overriding navigation to specific Kibana apps: url should respect the Kibana Space id
|
||||
extendedPage.gotoApp = (appName: string) =>
|
||||
page.goto(kbnUrl.app(appName, { space: scoutSpace.id }));
|
||||
|
||||
log.debug(serviceLoadedMsg(`scoutPage:${scoutSpace.id}`));
|
||||
await use(extendedPage);
|
||||
},
|
||||
});
|
|
@ -7,9 +7,11 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { Page, test as base } from '@playwright/test';
|
||||
import { Page } from '@playwright/test';
|
||||
import { subj } from '@kbn/test-subj-selector';
|
||||
import { ScoutPage, KibanaUrl, ScoutTestFixtures, ScoutWorkerFixtures } from '../types';
|
||||
import { KibanaUrl, ToolingLog, coreWorkerFixtures } from '../../worker';
|
||||
import { ScoutPage } from '.';
|
||||
import { serviceLoadedMsg } from '../../../utils';
|
||||
|
||||
/**
|
||||
* Instead of defining each method individually, we use a list of method names and loop through them, creating methods dynamically.
|
||||
|
@ -69,6 +71,26 @@ function extendPageWithTestSubject(page: Page): ScoutPage['testSubj'] {
|
|||
return extendedMethods as ScoutPage['testSubj'];
|
||||
}
|
||||
|
||||
export function extendPlaywrightPage({
|
||||
page,
|
||||
kbnUrl,
|
||||
}: {
|
||||
page: Page;
|
||||
kbnUrl: KibanaUrl;
|
||||
}): ScoutPage {
|
||||
const extendedPage = page as ScoutPage;
|
||||
// Extend page with '@kbn/test-subj-selector' support
|
||||
extendedPage.testSubj = extendPageWithTestSubject(page);
|
||||
// Method to navigate to specific Kibana apps
|
||||
extendedPage.gotoApp = (appName: string) => page.goto(kbnUrl.app(appName));
|
||||
// Method to wait for global loading indicator to be hidden
|
||||
extendedPage.waitForLoadingIndicatorHidden = () =>
|
||||
extendedPage.testSubj.waitForSelector('globalLoadingIndicator-hidden', {
|
||||
state: 'attached',
|
||||
});
|
||||
return extendedPage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the 'page' fixture with Kibana-specific functionality
|
||||
*
|
||||
|
@ -95,20 +117,17 @@ function extendPageWithTestSubject(page: Page): ScoutPage['testSubj'] {
|
|||
* await page.gotoApp('discover);
|
||||
* ```
|
||||
*/
|
||||
export const scoutPageFixture = base.extend<ScoutTestFixtures, ScoutWorkerFixtures>({
|
||||
export const scoutPageFixture = coreWorkerFixtures.extend<
|
||||
{ page: ScoutPage; log: ToolingLog },
|
||||
{ kbnUrl: KibanaUrl }
|
||||
>({
|
||||
page: async (
|
||||
{ page, kbnUrl }: { page: Page; kbnUrl: KibanaUrl },
|
||||
{ page, kbnUrl, log }: { page: Page; kbnUrl: KibanaUrl; log: ToolingLog },
|
||||
use: (extendedPage: ScoutPage) => Promise<void>
|
||||
) => {
|
||||
const extendedPage = page as ScoutPage;
|
||||
// Extend page with '@kbn/test-subj-selector' support
|
||||
extendedPage.testSubj = extendPageWithTestSubject(page);
|
||||
// Method to navigate to specific Kibana apps
|
||||
extendedPage.gotoApp = (appName: string) => page.goto(kbnUrl.app(appName));
|
||||
// Method to wait for global loading indicator to be hidden
|
||||
extendedPage.waitForLoadingIndicatorHidden = () =>
|
||||
extendedPage.testSubj.waitForSelector('globalLoadingIndicator-hidden', { state: 'attached' });
|
||||
const extendedPage = extendPlaywrightPage({ page, kbnUrl });
|
||||
|
||||
log.debug(serviceLoadedMsg(`scoutPage`));
|
||||
await use(extendedPage);
|
||||
},
|
||||
});
|
|
@ -8,7 +8,7 @@
|
|||
*/
|
||||
|
||||
import { test as base } from '@playwright/test';
|
||||
import { tags } from '../../tags';
|
||||
import { tags } from '../../../tags';
|
||||
|
||||
const supportedTags = tags.DEPLOYMENT_AGNOSTIC;
|
||||
|
|
@ -1,74 +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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import type { KbnClient, SamlSessionManager } from '@kbn/test';
|
||||
import type { ToolingLog } from '@kbn/tooling-log';
|
||||
import type { Client } from '@elastic/elasticsearch';
|
||||
import { LoadActionPerfOptions } from '@kbn/es-archiver';
|
||||
import { IndexStats } from '@kbn/es-archiver/src/lib/stats';
|
||||
import type { UiSettingValues } from '@kbn/test/src/kbn_client/kbn_client_ui_settings';
|
||||
|
||||
import { ScoutTestConfig } from '../../../types';
|
||||
import { KibanaUrl } from '../../../common/services/kibana_url';
|
||||
|
||||
export interface EsArchiverFixture {
|
||||
/**
|
||||
* Loads an Elasticsearch archive if the specified data index is not present.
|
||||
* @param name The name of the archive to load.
|
||||
* @param performance An object of type LoadActionPerfOptions to measure and
|
||||
* report performance metrics during the load operation.
|
||||
* @returns A Promise that resolves to an object containing index statistics.
|
||||
*/
|
||||
loadIfNeeded: (
|
||||
name: string,
|
||||
performance?: LoadActionPerfOptions | undefined
|
||||
) => Promise<Record<string, IndexStats>>;
|
||||
}
|
||||
|
||||
export interface UiSettingsFixture {
|
||||
/**
|
||||
* Applies one or more UI settings
|
||||
* @param values (UiSettingValues): An object containing key-value pairs of UI settings to apply.
|
||||
* @returns A Promise that resolves once the settings are applied.
|
||||
*/
|
||||
set: (values: UiSettingValues) => Promise<void>;
|
||||
/**
|
||||
* Resets specific UI settings to their default values.
|
||||
* @param values A list of UI setting keys to unset.
|
||||
* @returns A Promise that resolves after the settings are unset.
|
||||
*/
|
||||
unset: (...values: string[]) => Promise<any>;
|
||||
/**
|
||||
* Sets the default time range for Kibana.
|
||||
* @from The start time of the default time range.
|
||||
* @to The end time of the default time range.
|
||||
* @returns A Promise that resolves once the default time is set.
|
||||
*/
|
||||
setDefaultTime: ({ from, to }: { from: string; to: string }) => Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `ScoutWorkerFixtures` type defines the set of fixtures that are available
|
||||
*/
|
||||
export interface ScoutWorkerFixtures {
|
||||
log: ToolingLog;
|
||||
config: ScoutTestConfig;
|
||||
kbnUrl: KibanaUrl;
|
||||
esClient: Client;
|
||||
kbnClient: KbnClient;
|
||||
uiSettings: UiSettingsFixture;
|
||||
esArchiver: EsArchiverFixture;
|
||||
samlAuth: SamlSessionManager;
|
||||
}
|
||||
|
||||
// re-export to import types from '@kbn-scout'
|
||||
export type { KbnClient, SamlSessionManager } from '@kbn/test';
|
||||
export type { ToolingLog } from '@kbn/tooling-log';
|
||||
export type { Client } from '@elastic/elasticsearch';
|
||||
export type { KibanaUrl } from '../../../common/services/kibana_url';
|
|
@ -9,18 +9,28 @@
|
|||
|
||||
import { test as base } from '@playwright/test';
|
||||
|
||||
import { LoadActionPerfOptions } from '@kbn/es-archiver';
|
||||
import type { ToolingLog } from '@kbn/tooling-log';
|
||||
|
||||
import { KbnClient, SamlSessionManager } from '@kbn/test';
|
||||
import { Client } from '@elastic/elasticsearch';
|
||||
import {
|
||||
createKbnUrl,
|
||||
createEsArchiver,
|
||||
createEsClient,
|
||||
createKbnClient,
|
||||
createLogger,
|
||||
createSamlSessionManager,
|
||||
createScoutConfig,
|
||||
KibanaUrl,
|
||||
} from '../../../common/services';
|
||||
import { ScoutWorkerFixtures } from '../types/worker_scope';
|
||||
import { ScoutTestOptions } from '../../types';
|
||||
import { ScoutTestConfig } from '.';
|
||||
|
||||
// re-export to import types from '@kbn-scout'
|
||||
export type { KbnClient, SamlSessionManager } from '@kbn/test';
|
||||
export type { ToolingLog } from '@kbn/tooling-log';
|
||||
export type { Client as EsClient } from '@elastic/elasticsearch';
|
||||
export type { KibanaUrl } from '../../../common/services/kibana_url';
|
||||
export type { ScoutTestConfig } from '../../../types';
|
||||
|
||||
/**
|
||||
* The coreWorkerFixtures setup defines foundational fixtures that are essential
|
||||
|
@ -29,7 +39,17 @@ import { ScoutTestOptions } from '../../types';
|
|||
* and isolated access to critical services such as logging, configuration, and
|
||||
* clients for interacting with Kibana and Elasticsearch.
|
||||
*/
|
||||
export const coreWorkerFixtures = base.extend<{}, ScoutWorkerFixtures>({
|
||||
export const coreWorkerFixtures = base.extend<
|
||||
{},
|
||||
{
|
||||
log: ToolingLog;
|
||||
config: ScoutTestConfig;
|
||||
kbnUrl: KibanaUrl;
|
||||
esClient: Client;
|
||||
kbnClient: KbnClient;
|
||||
samlAuth: SamlSessionManager;
|
||||
}
|
||||
>({
|
||||
// Provides a scoped logger instance for each worker. This logger is shared across
|
||||
// all other fixtures within the worker scope.
|
||||
log: [
|
||||
|
@ -87,25 +107,6 @@ export const coreWorkerFixtures = base.extend<{}, ScoutWorkerFixtures>({
|
|||
{ scope: 'worker' },
|
||||
],
|
||||
|
||||
/**
|
||||
* Provides utilities for managing test data in Elasticsearch. The "loadIfNeeded" method
|
||||
* optimizes test execution by loading data archives only if required, avoiding redundant
|
||||
* data ingestion.
|
||||
*
|
||||
* Note: In order to speedup test execution and avoid the overhead of deleting the data
|
||||
* we only expose capability to ingest the data indexes.
|
||||
*/
|
||||
esArchiver: [
|
||||
({ log, esClient, kbnClient }, use) => {
|
||||
const esArchiverInstance = createEsArchiver(esClient, kbnClient, log);
|
||||
const loadIfNeeded = async (name: string, performance?: LoadActionPerfOptions | undefined) =>
|
||||
esArchiverInstance!.loadIfNeeded(name, performance);
|
||||
|
||||
use({ loadIfNeeded });
|
||||
},
|
||||
{ scope: 'worker' },
|
||||
],
|
||||
|
||||
/**
|
||||
* Creates a SAML session manager, that handles authentication tasks for tests involving
|
||||
* SAML-based authentication.
|
|
@ -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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { LoadActionPerfOptions } from '@kbn/es-archiver';
|
||||
import { IndexStats } from '@kbn/es-archiver/src/lib/stats';
|
||||
import { coreWorkerFixtures } from './core_fixtures';
|
||||
import { createEsArchiver } from '../../../common/services';
|
||||
|
||||
export interface EsArchiverFixture {
|
||||
/**
|
||||
* Loads an Elasticsearch archive if the specified data index is not present.
|
||||
* @param name The name of the archive to load.
|
||||
* @param performance An object of type LoadActionPerfOptions to measure and
|
||||
* report performance metrics during the load operation.
|
||||
* @returns A Promise that resolves to an object containing index statistics.
|
||||
*/
|
||||
loadIfNeeded: (
|
||||
name: string,
|
||||
performance?: LoadActionPerfOptions | undefined
|
||||
) => Promise<Record<string, IndexStats>>;
|
||||
}
|
||||
|
||||
export const esArchiverFixture = coreWorkerFixtures.extend<{}, { esArchiver: EsArchiverFixture }>({
|
||||
/**
|
||||
* Provides utilities for managing test data in Elasticsearch. The "loadIfNeeded" method
|
||||
* optimizes test execution by loading data archives only if required, avoiding redundant
|
||||
* data ingestion.
|
||||
*
|
||||
* Note: In order to speedup test execution and avoid the overhead of deleting the data
|
||||
* we only expose capability to ingest the data indexes.
|
||||
*/
|
||||
esArchiver: [
|
||||
({ log, esClient, kbnClient }, use) => {
|
||||
const esArchiverInstance = createEsArchiver(esClient, kbnClient, log);
|
||||
const loadIfNeeded = async (name: string, performance?: LoadActionPerfOptions | undefined) =>
|
||||
esArchiverInstance!.loadIfNeeded(name, performance);
|
||||
|
||||
use({ loadIfNeeded });
|
||||
},
|
||||
{ scope: 'worker' },
|
||||
],
|
||||
});
|
|
@ -7,8 +7,21 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { mergeTests } from 'playwright/test';
|
||||
import { uiSettingsFixture } from './ui_settings';
|
||||
import { coreWorkerFixtures } from './core';
|
||||
export { coreWorkerFixtures } from './core_fixtures';
|
||||
export type {
|
||||
ToolingLog,
|
||||
ScoutTestConfig,
|
||||
KibanaUrl,
|
||||
EsClient,
|
||||
KbnClient,
|
||||
SamlSessionManager,
|
||||
} from './core_fixtures';
|
||||
|
||||
export const scoutWorkerFixtures = mergeTests(coreWorkerFixtures, uiSettingsFixture);
|
||||
export { esArchiverFixture } from './es_archiver';
|
||||
export type { EsArchiverFixture } from './es_archiver';
|
||||
|
||||
export { uiSettingsFixture } from './ui_settings';
|
||||
export type { UiSettingsFixture } from './ui_settings';
|
||||
|
||||
export { scoutSpaceParallelFixture } from './scout_space';
|
||||
export type { ScoutSpaceParallelFixture } from './scout_space';
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { UiSettingValues } from '@kbn/test/src/kbn_client/kbn_client_ui_settings';
|
||||
|
||||
export interface ImportSavedObjects {
|
||||
type: string;
|
||||
destinationId: string;
|
||||
meta: { title: string };
|
||||
}
|
||||
export interface ImportExportResponse {
|
||||
successResults: ImportSavedObjects[];
|
||||
}
|
||||
export interface SavedObjectResponse {
|
||||
id: string;
|
||||
type: string;
|
||||
title: string;
|
||||
}
|
||||
|
||||
export interface ScoutSpaceParallelFixture {
|
||||
id: string;
|
||||
savedObjects: {
|
||||
load: (path: string) => Promise<SavedObjectResponse[]>;
|
||||
cleanStandardList: () => Promise<void>;
|
||||
};
|
||||
uiSettings: {
|
||||
setDefaultIndex: (dataViewName: string) => Promise<void>;
|
||||
set: (values: UiSettingValues) => Promise<void>;
|
||||
unset: (...keys: string[]) => Promise<any[]>;
|
||||
setDefaultTime: ({ from, to }: { from: string; to: string }) => Promise<void>;
|
||||
};
|
||||
}
|
||||
|
||||
export { scoutSpaceParallelFixture } from './parallel';
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { UiSettingValues } from '@kbn/test/src/kbn_client/kbn_client_ui_settings';
|
||||
import { formatTime, isValidUTCDate, measurePerformance, serviceLoadedMsg } from '../../../utils';
|
||||
import { coreWorkerFixtures } from '..';
|
||||
import { ImportSavedObjects, ScoutSpaceParallelFixture } from '.';
|
||||
|
||||
export const scoutSpaceParallelFixture = coreWorkerFixtures.extend<
|
||||
{},
|
||||
{ scoutSpace: ScoutSpaceParallelFixture }
|
||||
>({
|
||||
scoutSpace: [
|
||||
async ({ log, kbnClient }, use, workerInfo) => {
|
||||
const spaceId = `test-space-${workerInfo.workerIndex}`;
|
||||
const spacePayload = {
|
||||
id: spaceId,
|
||||
name: spaceId,
|
||||
disabledFeatures: [],
|
||||
};
|
||||
await measurePerformance(log, `scoutSpace:${spaceId} 'spaces.create'`, async () => {
|
||||
return kbnClient.spaces.create(spacePayload);
|
||||
});
|
||||
|
||||
// cache saved objects ids in space
|
||||
const savedObjectsCache = new Map<string, string>();
|
||||
|
||||
const load = async (path: string) => {
|
||||
return measurePerformance(log, `scoutSpace:${spaceId} 'savedObjects.load'`, async () => {
|
||||
const response = await kbnClient.importExport.load(path, {
|
||||
space: spaceId,
|
||||
// will create new copies of saved objects with unique ids
|
||||
createNewCopies: true,
|
||||
});
|
||||
|
||||
const imported = (response.successResults as ImportSavedObjects[]).map(
|
||||
(r: { type: string; destinationId: string; meta: { title: string } }) => {
|
||||
return { id: r.destinationId, type: r.type, title: r.meta.title };
|
||||
}
|
||||
);
|
||||
|
||||
imported.forEach((so) => savedObjectsCache.set(so.title, so.id));
|
||||
|
||||
return imported;
|
||||
});
|
||||
};
|
||||
const cleanStandardList = async () => {
|
||||
return measurePerformance(
|
||||
log,
|
||||
`scoutSpace:${spaceId} 'savedObjects.cleanStandardList'`,
|
||||
async () => {
|
||||
savedObjectsCache.clear();
|
||||
await kbnClient.savedObjects.cleanStandardList({
|
||||
space: spaceId,
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
const setDefaultIndex = async (dataViewName: string) => {
|
||||
return measurePerformance(
|
||||
log,
|
||||
`scoutSpace:${spaceId} 'savedObjects.setDefaultIndex'`,
|
||||
async () => {
|
||||
if (savedObjectsCache.has(dataViewName)) {
|
||||
return kbnClient.uiSettings.update(
|
||||
{
|
||||
defaultIndex: savedObjectsCache.get(dataViewName)!,
|
||||
},
|
||||
{ space: spaceId }
|
||||
);
|
||||
} else {
|
||||
throw new Error(`Data view id ${dataViewName} not found in space ${spaceId}`);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
const set = async (values: UiSettingValues) => {
|
||||
return measurePerformance(log, `scoutSpace:${spaceId} 'uiSettings.set'`, async () => {
|
||||
return kbnClient.uiSettings.update(values, { space: spaceId });
|
||||
});
|
||||
};
|
||||
const unset = async (...keys: string[]) => {
|
||||
return measurePerformance(log, `scoutSpace:${spaceId} 'uiSettings.unset'`, async () => {
|
||||
return Promise.all(
|
||||
keys.map((key) => kbnClient.uiSettings.unset(key, { space: spaceId }))
|
||||
);
|
||||
});
|
||||
};
|
||||
const setDefaultTime = async ({ from, to }: { from: string; to: string }) => {
|
||||
return measurePerformance(
|
||||
log,
|
||||
`scoutSpace:${spaceId} 'uiSettings.setDefaultTime'`,
|
||||
async () => {
|
||||
const utcFrom = isValidUTCDate(from) ? from : formatTime(from);
|
||||
const untcTo = isValidUTCDate(to) ? to : formatTime(to);
|
||||
return kbnClient.uiSettings.update(
|
||||
{
|
||||
'timepicker:timeDefaults': `{ "from": "${utcFrom}", "to": "${untcTo}"}`,
|
||||
},
|
||||
{ space: spaceId }
|
||||
);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
const savedObjects = {
|
||||
load,
|
||||
cleanStandardList,
|
||||
};
|
||||
|
||||
const uiSettings = {
|
||||
setDefaultIndex,
|
||||
set,
|
||||
unset,
|
||||
setDefaultTime,
|
||||
};
|
||||
|
||||
log.debug(serviceLoadedMsg(`scoutSpace:${spaceId}`));
|
||||
await use({ savedObjects, uiSettings, id: spaceId });
|
||||
|
||||
// Cleanup space after tests via API call
|
||||
await measurePerformance(log, `scoutSpace:${spaceId} 'space.delete'`, async () => {
|
||||
return kbnClient.spaces.delete(spaceId);
|
||||
});
|
||||
},
|
||||
{ scope: 'worker', auto: true },
|
||||
],
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { UiSettingValues } from '@kbn/test/src/kbn_client/kbn_client_ui_settings';
|
||||
|
||||
export interface UiSettingsFixture {
|
||||
/**
|
||||
* Applies one or more UI settings
|
||||
* @param values (UiSettingValues): An object containing key-value pairs of UI settings to apply.
|
||||
* @returns A Promise that resolves once the settings are applied.
|
||||
*/
|
||||
set: (values: UiSettingValues) => Promise<void>;
|
||||
/**
|
||||
* Resets specific UI settings to their default values.
|
||||
* @param values A list of UI setting keys to unset.
|
||||
* @returns A Promise that resolves after the settings are unset.
|
||||
*/
|
||||
unset: (...values: string[]) => Promise<any>;
|
||||
/**
|
||||
* Sets the default time range for Kibana.
|
||||
* @from The start time of the default time range.
|
||||
* @to The end time of the default time range.
|
||||
* @returns A Promise that resolves once the default time is set.
|
||||
*/
|
||||
setDefaultTime: ({ from, to }: { from: string; to: string }) => Promise<void>;
|
||||
}
|
||||
|
||||
export { uiSettingsFixture } from './single_thread';
|
|
@ -7,19 +7,21 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { test as base } from '@playwright/test';
|
||||
import { UiSettingValues } from '@kbn/test/src/kbn_client/kbn_client_ui_settings';
|
||||
import { ScoutWorkerFixtures } from '../types';
|
||||
import { isValidUTCDate, formatTime } from '../../utils';
|
||||
import { isValidUTCDate, formatTime, serviceLoadedMsg } from '../../../utils';
|
||||
import { coreWorkerFixtures } from '../core_fixtures';
|
||||
import { UiSettingsFixture } from '.';
|
||||
|
||||
/**
|
||||
* This fixture provides a way to interact with Kibana UI settings.
|
||||
*/
|
||||
export const uiSettingsFixture = base.extend<{}, ScoutWorkerFixtures>({
|
||||
export const uiSettingsFixture = coreWorkerFixtures.extend<{}, { uiSettings: UiSettingsFixture }>({
|
||||
uiSettings: [
|
||||
({ kbnClient }, use) => {
|
||||
async ({ kbnClient, log }, use) => {
|
||||
const kbnClientUiSettings = {
|
||||
set: async (values: UiSettingValues) => kbnClient.uiSettings.update(values),
|
||||
set: async (values: UiSettingValues) => {
|
||||
await kbnClient.uiSettings.update(values);
|
||||
},
|
||||
|
||||
unset: async (...keys: string[]) =>
|
||||
Promise.all(keys.map((key) => kbnClient.uiSettings.unset(key))),
|
||||
|
@ -33,7 +35,8 @@ export const uiSettingsFixture = base.extend<{}, ScoutWorkerFixtures>({
|
|||
},
|
||||
};
|
||||
|
||||
use(kbnClientUiSettings);
|
||||
log.debug(serviceLoadedMsg(`uiSettings`));
|
||||
await use(kbnClientUiSettings);
|
||||
},
|
||||
{ scope: 'worker' },
|
||||
],
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { FullConfig } from 'playwright/test';
|
||||
import {
|
||||
createEsArchiver,
|
||||
createEsClient,
|
||||
createKbnClient,
|
||||
createLogger,
|
||||
createScoutConfig,
|
||||
} from '../../common';
|
||||
import { ScoutTestOptions } from '../types';
|
||||
import { measurePerformance } from '../utils';
|
||||
|
||||
export async function ingestTestDataHook(config: FullConfig, archives: string[]) {
|
||||
const log = createLogger();
|
||||
|
||||
if (archives.length === 0) {
|
||||
log.info('[scout setup] no test data to ingest');
|
||||
return;
|
||||
}
|
||||
|
||||
return measurePerformance(log, '[scout setup]: ingestTestDataHook', async () => {
|
||||
// TODO: This should be configurable local vs cloud
|
||||
const configName = 'local';
|
||||
const projectUse = config.projects[0].use as ScoutTestOptions;
|
||||
const serversConfigDir = projectUse.serversConfigDir;
|
||||
const scoutConfig = createScoutConfig(serversConfigDir, configName, log);
|
||||
|
||||
const esClient = createEsClient(scoutConfig, log);
|
||||
const kbnCLient = createKbnClient(scoutConfig, log);
|
||||
const esArchiver = createEsArchiver(esClient, kbnCLient, log);
|
||||
|
||||
log.info('[scout setup] loading test data (only if indexes do not exist)...');
|
||||
for (const archive of archives) {
|
||||
await esArchiver.loadIfNeeded(archive);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -7,5 +7,4 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
export * from './test_scope';
|
||||
export * from './worker_scope';
|
||||
export { ingestTestDataHook } from './data_ingestion';
|
|
@ -7,24 +7,29 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { mergeTests } from 'playwright/test';
|
||||
import { scoutCoreFixtures } from './fixtures';
|
||||
import { scoutFixtures, scoutParallelFixtures } from './fixtures';
|
||||
|
||||
// Scout core fixtures: worker & test scope
|
||||
export const test = mergeTests(scoutCoreFixtures);
|
||||
export const test = scoutFixtures;
|
||||
|
||||
// Scout core 'space aware' fixtures: worker & test scope
|
||||
export const spaceTest = scoutParallelFixtures;
|
||||
|
||||
export { createPlaywrightConfig } from './config';
|
||||
export { createLazyPageObject } from './page_objects/utils';
|
||||
export { expect } from './expect';
|
||||
|
||||
export type { ScoutPlaywrightOptions, ScoutTestOptions } from './types';
|
||||
export type { PageObjects } from './page_objects';
|
||||
export type {
|
||||
ScoutTestFixtures,
|
||||
ScoutWorkerFixtures,
|
||||
ScoutParallelTestFixtures,
|
||||
ScoutParallelWorkerFixtures,
|
||||
ScoutPage,
|
||||
EsArchiverFixture,
|
||||
PageObjects,
|
||||
} from './fixtures';
|
||||
|
||||
// use to tag tests
|
||||
export { tags } from './tags';
|
||||
|
||||
export { ingestTestDataHook } from './global_hooks';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ScoutPage } from '../fixtures/types';
|
||||
import { ScoutPage } from '..';
|
||||
|
||||
type CommonlyUsedTimeRange =
|
||||
| 'Today'
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ScoutPage } from '../fixtures/types';
|
||||
import { expect } from '..';
|
||||
import { ScoutPage, expect } from '..';
|
||||
|
||||
export class DatePicker {
|
||||
constructor(private readonly page: ScoutPage) {}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ScoutPage } from '../fixtures/types';
|
||||
import { ScoutPage } from '..';
|
||||
|
||||
export class DiscoverApp {
|
||||
constructor(private readonly page: ScoutPage) {}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ScoutPage } from '../fixtures/types';
|
||||
import { ScoutPage } from '..';
|
||||
import { expect } from '..';
|
||||
|
||||
interface FilterCreationOptions {
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ScoutPage } from '../fixtures/types';
|
||||
import { ScoutPage } from '..';
|
||||
import { DashboardApp } from './dashboard_app';
|
||||
import { DatePicker } from './date_picker';
|
||||
import { DiscoverApp } from './discover_app';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ScoutPage } from '../../fixtures/types';
|
||||
import { ScoutPage } from '../../fixtures';
|
||||
|
||||
/**
|
||||
* Creates a lazily instantiated proxy for a Page Object class, deferring the creation of the instance until
|
||||
|
|
|
@ -18,7 +18,9 @@ export interface ScoutTestOptions extends PlaywrightTestOptions {
|
|||
[VALID_CONFIG_MARKER]: boolean;
|
||||
}
|
||||
|
||||
export interface ScoutPlaywrightOptions extends Pick<PlaywrightTestConfig, 'testDir' | 'workers'> {
|
||||
export interface ScoutPlaywrightOptions
|
||||
extends Pick<PlaywrightTestConfig, 'testDir' | 'workers' | 'globalSetup'> {
|
||||
testDir: string;
|
||||
workers?: 1 | 2;
|
||||
workers?: 1 | 2 | 3; // to keep performance consistent within test suites
|
||||
globalSetup?: string;
|
||||
}
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
*/
|
||||
|
||||
export { serviceLoadedMsg, isValidUTCDate, formatTime, getPlaywrightGrepTag } from './runner_utils';
|
||||
export { measurePerformance } from './performance';
|
||||
|
|
22
packages/kbn-scout/src/playwright/utils/performance.ts
Normal file
22
packages/kbn-scout/src/playwright/utils/performance.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
|
||||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ToolingLog } from '../../types';
|
||||
|
||||
export const measurePerformance = async <T>(
|
||||
log: ToolingLog,
|
||||
label: string,
|
||||
fn: () => Promise<T>
|
||||
): Promise<T> => {
|
||||
const startTime = performance.now();
|
||||
const result = await fn();
|
||||
const duration = performance.now() - startTime;
|
||||
log.debug(`${label} took ${duration.toFixed(2)}ms`);
|
||||
return result;
|
||||
};
|
|
@ -11,7 +11,7 @@ import moment from 'moment';
|
|||
import { Config } from '../../config';
|
||||
import { tagsByMode } from '../tags';
|
||||
|
||||
export const serviceLoadedMsg = (name: string) => `scout service loaded: ${name}`;
|
||||
export const serviceLoadedMsg = (name: string) => `[scout service] ${name}`;
|
||||
|
||||
export const isValidUTCDate = (date: string): boolean => {
|
||||
return !isNaN(Date.parse(date)) && new Date(date).toISOString() === date;
|
||||
|
|
|
@ -41,7 +41,8 @@ export interface SupportedRoles {
|
|||
}
|
||||
|
||||
export interface GetCookieOptions {
|
||||
forceNewSession: boolean;
|
||||
forceNewSession?: boolean;
|
||||
spaceId?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,18 +121,20 @@ Set env variable 'TEST_CLOUD=1' to run FTR against your Cloud deployment`
|
|||
};
|
||||
|
||||
private getSessionByRole = async (options: GetSessionByRole): Promise<Session> => {
|
||||
const { role, forceNewSession } = options;
|
||||
const { role, forceNewSession, spaceId } = options;
|
||||
|
||||
// Validate role before creating SAML session
|
||||
this.validateRole(role);
|
||||
|
||||
const cacheKey = spaceId ? `${role}:${spaceId}` : role;
|
||||
|
||||
// Check if session is cached and not forced to create the new one
|
||||
if (!forceNewSession && this.sessionCache.has(role)) {
|
||||
return this.sessionCache.get(role)!;
|
||||
if (!forceNewSession && this.sessionCache.has(cacheKey)) {
|
||||
return this.sessionCache.get(cacheKey)!;
|
||||
}
|
||||
|
||||
const session = await this.createSessionForRole(role);
|
||||
this.sessionCache.set(role, session);
|
||||
this.sessionCache.set(cacheKey, session);
|
||||
|
||||
if (forceNewSession) {
|
||||
this.log.debug(`Session for role '${role}' was force updated.`);
|
||||
|
@ -181,13 +184,20 @@ Set env variable 'TEST_CLOUD=1' to run FTR against your Cloud deployment`
|
|||
|
||||
async getApiCredentialsForRole(role: string, options?: GetCookieOptions) {
|
||||
const { forceNewSession } = options || { forceNewSession: false };
|
||||
const session = await this.getSessionByRole({ role, forceNewSession });
|
||||
const session = await this.getSessionByRole({
|
||||
role,
|
||||
forceNewSession: forceNewSession ?? false,
|
||||
});
|
||||
return { Cookie: `sid=${session.getCookieValue()}` };
|
||||
}
|
||||
|
||||
async getInteractiveUserSessionCookieWithRoleScope(role: string, options?: GetCookieOptions) {
|
||||
const { forceNewSession } = options || { forceNewSession: false };
|
||||
const session = await this.getSessionByRole({ role, forceNewSession });
|
||||
const forceNewSession = options?.forceNewSession ?? false;
|
||||
const session = await this.getSessionByRole({
|
||||
role,
|
||||
forceNewSession,
|
||||
spaceId: options?.spaceId,
|
||||
});
|
||||
return session.getCookieValue();
|
||||
}
|
||||
|
||||
|
|
|
@ -65,4 +65,5 @@ export interface RetryParams {
|
|||
export interface GetSessionByRole {
|
||||
role: string;
|
||||
forceNewSession: boolean;
|
||||
spaceId?: string;
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ export async function runCheckFtrConfigsCli() {
|
|||
}
|
||||
|
||||
// playwright config files
|
||||
if (file.match(/\/ui_tests\/*playwright*.config.ts$/)) {
|
||||
if (file.match(/\/*playwright*.config.ts$/)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ export class KbnClientImportExport {
|
|||
return absolutePath;
|
||||
}
|
||||
|
||||
async load(path: string, options?: { space?: string }) {
|
||||
async load(path: string, options?: { space?: string; createNewCopies?: boolean }) {
|
||||
const src = this.resolveAndValidatePath(path);
|
||||
this.log.debug('resolved import for', path, 'to', src);
|
||||
|
||||
|
@ -65,19 +65,20 @@ export class KbnClientImportExport {
|
|||
const formData = new FormData();
|
||||
formData.append('file', objects.map((obj) => JSON.stringify(obj)).join('\n'), 'import.ndjson');
|
||||
|
||||
const query = options?.createNewCopies ? { createNewCopies: true } : { overwrite: true };
|
||||
|
||||
// TODO: should we clear out the existing saved objects?
|
||||
const resp = await this.req<ImportApiResponse>(options?.space, {
|
||||
method: 'POST',
|
||||
path: '/api/saved_objects/_import',
|
||||
query: {
|
||||
overwrite: true,
|
||||
},
|
||||
query,
|
||||
body: formData,
|
||||
headers: formData.getHeaders(),
|
||||
});
|
||||
|
||||
if (resp.data.success) {
|
||||
this.log.success('import success');
|
||||
return resp.data;
|
||||
} else {
|
||||
throw createFailError(
|
||||
`failed to import all saved objects: ${inspect(resp.data, {
|
||||
|
|
|
@ -8,14 +8,24 @@
|
|||
export const LOGSTASH_DEFAULT_START_TIME = '2015-09-19T06:31:44.000Z';
|
||||
export const LOGSTASH_DEFAULT_END_TIME = '2015-09-23T18:31:44.000Z';
|
||||
|
||||
/**
|
||||
* Should be used in "single thread" tests to set default Data View
|
||||
* @example uiSettings.set({ defaultIndex: testData.DATA_VIEW_ID.ECOMMERCE });
|
||||
*/
|
||||
export const DATA_VIEW_ID = {
|
||||
ECOMMERCE: '5193f870-d861-11e9-a311-0fa548c5f953',
|
||||
LOGSTASH: 'logstash-*',
|
||||
NO_TIME_FIELD: 'c1e8af24-c7b7-4d9b-ab0e-e408c88d29c9',
|
||||
};
|
||||
|
||||
export const DATA_VIEW = {
|
||||
/**
|
||||
* Should be used in "parallel tests" to set default Data View, because ids are generated and can't be hardcoded
|
||||
* @example scoutSpace.uiSettings.setDefaultIndex(testData.DATA_VIEW_NAME.ECOMMERCE);
|
||||
*/
|
||||
export const DATA_VIEW_NAME = {
|
||||
ECOMMERCE: 'ecommerce',
|
||||
LOGSTASH: 'logstash-*',
|
||||
NO_TIME_FIELD: 'without-timefield',
|
||||
};
|
||||
|
||||
export const LOGSTASH_OUT_OF_RANGE_DATES = {
|
||||
|
|
|
@ -6,30 +6,62 @@
|
|||
*/
|
||||
|
||||
import {
|
||||
test as base,
|
||||
test as baseTest,
|
||||
spaceTest as spaceBaseTest,
|
||||
PageObjects,
|
||||
createLazyPageObject,
|
||||
ScoutTestFixtures,
|
||||
ScoutWorkerFixtures,
|
||||
ScoutParallelTestFixtures,
|
||||
ScoutParallelWorkerFixtures,
|
||||
} from '@kbn/scout';
|
||||
import { DemoPage } from './page_objects';
|
||||
|
||||
export interface ExtendedScoutTestFixtures extends ScoutTestFixtures {
|
||||
export interface ExtScoutTestFixtures extends ScoutTestFixtures {
|
||||
pageObjects: PageObjects & {
|
||||
demo: DemoPage;
|
||||
};
|
||||
}
|
||||
|
||||
export const test = base.extend<ExtendedScoutTestFixtures, ScoutWorkerFixtures>({
|
||||
export const test = baseTest.extend<ExtScoutTestFixtures, ScoutWorkerFixtures>({
|
||||
pageObjects: async (
|
||||
{
|
||||
pageObjects,
|
||||
page,
|
||||
}: {
|
||||
pageObjects: ExtendedScoutTestFixtures['pageObjects'];
|
||||
page: ExtendedScoutTestFixtures['page'];
|
||||
pageObjects: ExtScoutTestFixtures['pageObjects'];
|
||||
page: ExtScoutTestFixtures['page'];
|
||||
},
|
||||
use: (pageObjects: ExtendedScoutTestFixtures['pageObjects']) => Promise<void>
|
||||
use: (pageObjects: ExtScoutTestFixtures['pageObjects']) => Promise<void>
|
||||
) => {
|
||||
const extendedPageObjects = {
|
||||
...pageObjects,
|
||||
demo: createLazyPageObject(DemoPage, page),
|
||||
};
|
||||
|
||||
await use(extendedPageObjects);
|
||||
},
|
||||
});
|
||||
|
||||
export interface ExtParallelRunTestFixtures extends ScoutParallelTestFixtures {
|
||||
pageObjects: PageObjects & {
|
||||
demo: DemoPage;
|
||||
};
|
||||
}
|
||||
|
||||
export const spaceTest = spaceBaseTest.extend<
|
||||
ExtParallelRunTestFixtures,
|
||||
ScoutParallelWorkerFixtures
|
||||
>({
|
||||
pageObjects: async (
|
||||
{
|
||||
pageObjects,
|
||||
page,
|
||||
}: {
|
||||
pageObjects: ExtParallelRunTestFixtures['pageObjects'];
|
||||
page: ExtParallelRunTestFixtures['page'];
|
||||
},
|
||||
use: (pageObjects: ExtParallelRunTestFixtures['pageObjects']) => Promise<void>
|
||||
) => {
|
||||
const extendedPageObjects = {
|
||||
...pageObjects,
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 { createPlaywrightConfig } from '@kbn/scout';
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default createPlaywrightConfig({
|
||||
globalSetup: require.resolve('./parallel_tests/global_setup'),
|
||||
testDir: './parallel_tests',
|
||||
workers: 2,
|
||||
});
|
|
@ -6,29 +6,28 @@
|
|||
*/
|
||||
|
||||
import { expect, tags } from '@kbn/scout';
|
||||
import { test, testData } from '../fixtures';
|
||||
import { spaceTest, testData } from '../fixtures';
|
||||
|
||||
test.describe('Discover app - errors', { tag: tags.ESS_ONLY }, () => {
|
||||
test.beforeAll(async ({ esArchiver, kbnClient, uiSettings }) => {
|
||||
await kbnClient.savedObjects.clean({ types: ['search', 'index-pattern'] });
|
||||
await esArchiver.loadIfNeeded(testData.ES_ARCHIVES.LOGSTASH);
|
||||
await kbnClient.importExport.load(testData.KBN_ARCHIVES.INVALID_SCRIPTED_FIELD);
|
||||
await uiSettings.setDefaultTime({
|
||||
spaceTest.describe('Discover app - errors', { tag: tags.ESS_ONLY }, () => {
|
||||
spaceTest.beforeAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.savedObjects.cleanStandardList();
|
||||
await scoutSpace.savedObjects.load(testData.KBN_ARCHIVES.INVALID_SCRIPTED_FIELD);
|
||||
await scoutSpace.uiSettings.setDefaultTime({
|
||||
from: testData.LOGSTASH_DEFAULT_START_TIME,
|
||||
to: testData.LOGSTASH_DEFAULT_END_TIME,
|
||||
});
|
||||
});
|
||||
|
||||
test.afterAll(async ({ kbnClient }) => {
|
||||
await kbnClient.savedObjects.cleanStandardList();
|
||||
spaceTest.afterAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.savedObjects.cleanStandardList();
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ browserAuth, pageObjects }) => {
|
||||
spaceTest.beforeEach(async ({ browserAuth, pageObjects }) => {
|
||||
await browserAuth.loginAsViewer();
|
||||
await pageObjects.discover.goto();
|
||||
});
|
||||
|
||||
test('should render invalid scripted field error', async ({ page }) => {
|
||||
spaceTest('should render invalid scripted field error', async ({ page }) => {
|
||||
await page.testSubj.locator('discoverErrorCalloutTitle').waitFor({ state: 'visible' });
|
||||
await expect(
|
||||
page.testSubj.locator('painlessStackTrace'),
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 { ingestTestDataHook } from '@kbn/scout';
|
||||
import { type FullConfig } from '@playwright/test';
|
||||
import { testData } from '../fixtures';
|
||||
|
||||
async function globalSetup(config: FullConfig) {
|
||||
// add archives to load, if needed
|
||||
const archives = [
|
||||
testData.ES_ARCHIVES.LOGSTASH,
|
||||
testData.ES_ARCHIVES.NO_TIME_FIELD,
|
||||
testData.ES_ARCHIVES.ECOMMERCE,
|
||||
];
|
||||
|
||||
return ingestTestDataHook(config, archives);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default globalSetup;
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* 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/scout';
|
||||
import { spaceTest, testData } from '../fixtures';
|
||||
import type { ExtParallelRunTestFixtures } from '../fixtures';
|
||||
|
||||
const assertNoFilterAndEmptyQuery = async (
|
||||
filterBadge: { field: string; value: string },
|
||||
pageObjects: ExtParallelRunTestFixtures['pageObjects'],
|
||||
page: ExtParallelRunTestFixtures['page']
|
||||
) => {
|
||||
expect(
|
||||
// checking if filter exists, enabled or disabled
|
||||
await pageObjects.filterBar.hasFilter(filterBadge),
|
||||
`Filter ${JSON.stringify(filterBadge)} should not exist`
|
||||
).toBe(false);
|
||||
await expect(
|
||||
page.testSubj.locator('queryInput'),
|
||||
'Query Bar input field should be empty'
|
||||
).toHaveText('');
|
||||
};
|
||||
|
||||
const assertDataViewIsSelected = async (page: ExtParallelRunTestFixtures['page'], name: string) =>
|
||||
await expect(
|
||||
page.testSubj.locator('*dataView-switch-link'),
|
||||
'Incorrect data view is selected'
|
||||
).toHaveText(name);
|
||||
|
||||
spaceTest.describe(
|
||||
'Discover app - saved searches',
|
||||
{ tag: ['@ess', '@svlSearch', '@svlOblt'] },
|
||||
() => {
|
||||
// TODO: Update to use an ES archive with an index accessible to 'viewer'
|
||||
// for running this test against the Security serverless project.
|
||||
const START_TIME = '2019-04-27T23:56:51.374Z';
|
||||
const END_TIME = '2019-08-23T16:18:51.821Z';
|
||||
const PANEL_NAME = 'Ecommerce Data';
|
||||
const SEARCH_QUERY = 'customer_gender:MALE';
|
||||
const SAVED_SEARCH_NAME = 'test-unselect-saved-search';
|
||||
const filterFieldAndValue = {
|
||||
field: 'category',
|
||||
value: `Men's Shoes`,
|
||||
};
|
||||
|
||||
spaceTest.beforeAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.savedObjects.load(testData.KBN_ARCHIVES.DISCOVER);
|
||||
await scoutSpace.savedObjects.load(testData.KBN_ARCHIVES.ECOMMERCE);
|
||||
await scoutSpace.uiSettings.setDefaultIndex(testData.DATA_VIEW_NAME.ECOMMERCE);
|
||||
await scoutSpace.uiSettings.setDefaultTime({ from: START_TIME, to: END_TIME });
|
||||
});
|
||||
|
||||
spaceTest.afterAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.uiSettings.unset('defaultIndex', 'timepicker:timeDefaults');
|
||||
await scoutSpace.savedObjects.cleanStandardList();
|
||||
});
|
||||
|
||||
spaceTest.beforeEach(async ({ browserAuth }) => {
|
||||
await browserAuth.loginAsPrivilegedUser();
|
||||
});
|
||||
|
||||
spaceTest('should customize time range on dashboards', async ({ pageObjects, page }) => {
|
||||
await pageObjects.dashboard.goto();
|
||||
await pageObjects.dashboard.openNewDashboard();
|
||||
await pageObjects.dashboard.addPanelFromLibrary(PANEL_NAME);
|
||||
await page.testSubj.locator('savedSearchTotalDocuments').waitFor({
|
||||
state: 'visible',
|
||||
});
|
||||
|
||||
await pageObjects.dashboard.customizePanel({
|
||||
name: PANEL_NAME,
|
||||
customTimeRageCommonlyUsed: { value: 'Last_90 days' },
|
||||
});
|
||||
await expect(
|
||||
page.testSubj.locator('embeddedSavedSearchDocTable').locator('.euiDataGrid__noResults'),
|
||||
'No results message in Saved Search panel should be visible'
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
spaceTest(
|
||||
`should unselect saved search when navigating to a 'new'`,
|
||||
async ({ pageObjects, page }) => {
|
||||
await pageObjects.discover.goto();
|
||||
await assertDataViewIsSelected(page, testData.DATA_VIEW_NAME.ECOMMERCE);
|
||||
await pageObjects.filterBar.addFilter({
|
||||
...filterFieldAndValue,
|
||||
operator: 'is',
|
||||
});
|
||||
await page.testSubj.fill('queryInput', SEARCH_QUERY);
|
||||
await page.testSubj.click('querySubmitButton');
|
||||
await pageObjects.discover.waitForHistogramRendered();
|
||||
|
||||
await pageObjects.discover.saveSearch(SAVED_SEARCH_NAME);
|
||||
await pageObjects.discover.waitForHistogramRendered();
|
||||
|
||||
expect(
|
||||
await pageObjects.filterBar.hasFilter({
|
||||
...filterFieldAndValue,
|
||||
enabled: true, // Filter is enabled by default
|
||||
})
|
||||
).toBe(true);
|
||||
await expect(page.testSubj.locator('queryInput')).toHaveText(SEARCH_QUERY);
|
||||
|
||||
// create new search
|
||||
await pageObjects.discover.clickNewSearch();
|
||||
await assertDataViewIsSelected(page, testData.DATA_VIEW_NAME.ECOMMERCE);
|
||||
await assertNoFilterAndEmptyQuery(filterFieldAndValue, pageObjects, page);
|
||||
|
||||
// change data view
|
||||
await pageObjects.discover.selectDataView(testData.DATA_VIEW_NAME.LOGSTASH);
|
||||
await assertNoFilterAndEmptyQuery(filterFieldAndValue, pageObjects, page);
|
||||
|
||||
// change data view again
|
||||
await pageObjects.discover.selectDataView(testData.DATA_VIEW_NAME.ECOMMERCE);
|
||||
await assertNoFilterAndEmptyQuery(filterFieldAndValue, pageObjects, page);
|
||||
|
||||
// create new search again
|
||||
await pageObjects.discover.clickNewSearch();
|
||||
await assertDataViewIsSelected(page, testData.DATA_VIEW_NAME.ECOMMERCE);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
|
@ -6,38 +6,38 @@
|
|||
*/
|
||||
|
||||
import { expect, tags } from '@kbn/scout';
|
||||
import { test, testData, assertionMessages } from '../fixtures';
|
||||
import { spaceTest, testData, assertionMessages } from '../fixtures';
|
||||
|
||||
test.describe(
|
||||
spaceTest.describe(
|
||||
'Discover app - value suggestions: useTimeRange enabled',
|
||||
{ tag: tags.DEPLOYMENT_AGNOSTIC },
|
||||
() => {
|
||||
test.beforeAll(async ({ esArchiver, kbnClient, uiSettings }) => {
|
||||
await esArchiver.loadIfNeeded(testData.ES_ARCHIVES.LOGSTASH);
|
||||
await kbnClient.importExport.load(testData.KBN_ARCHIVES.DASHBOARD_DRILLDOWNS);
|
||||
await uiSettings.set({
|
||||
defaultIndex: testData.DATA_VIEW_ID.LOGSTASH, // TODO: investigate why it is required for `node scripts/playwright_test.js` run
|
||||
'timepicker:timeDefaults': `{ "from": "${testData.LOGSTASH_DEFAULT_START_TIME}", "to": "${testData.LOGSTASH_DEFAULT_END_TIME}"}`,
|
||||
spaceTest.beforeAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.savedObjects.load(testData.KBN_ARCHIVES.DASHBOARD_DRILLDOWNS);
|
||||
await scoutSpace.uiSettings.setDefaultIndex(testData.DATA_VIEW_NAME.LOGSTASH);
|
||||
await scoutSpace.uiSettings.setDefaultTime({
|
||||
from: testData.LOGSTASH_DEFAULT_START_TIME,
|
||||
to: testData.LOGSTASH_DEFAULT_END_TIME,
|
||||
});
|
||||
});
|
||||
|
||||
test.afterAll(async ({ kbnClient, uiSettings }) => {
|
||||
await uiSettings.unset('defaultIndex', 'timepicker:timeDefaults');
|
||||
await kbnClient.savedObjects.cleanStandardList();
|
||||
spaceTest.afterAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.uiSettings.unset('defaultIndex', 'timepicker:timeDefaults');
|
||||
await scoutSpace.savedObjects.cleanStandardList();
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ browserAuth, pageObjects }) => {
|
||||
spaceTest.beforeEach(async ({ browserAuth, pageObjects }) => {
|
||||
await browserAuth.loginAsViewer();
|
||||
await pageObjects.discover.goto();
|
||||
});
|
||||
|
||||
test('dont show up if outside of range', async ({ page, pageObjects }) => {
|
||||
spaceTest('dont show up if outside of range', async ({ page, pageObjects }) => {
|
||||
await pageObjects.datePicker.setAbsoluteRange(testData.LOGSTASH_OUT_OF_RANGE_DATES);
|
||||
await page.testSubj.fill('queryInput', 'extension.raw : ');
|
||||
await expect(page.testSubj.locator('autoCompleteSuggestionText')).toHaveCount(0);
|
||||
});
|
||||
|
||||
test('show up if in range', async ({ page, pageObjects }) => {
|
||||
spaceTest('show up if in range', async ({ page, pageObjects }) => {
|
||||
await pageObjects.datePicker.setAbsoluteRange(testData.LOGSTASH_IN_RANGE_DATES);
|
||||
await page.testSubj.fill('queryInput', 'extension.raw : ');
|
||||
await expect(
|
||||
|
@ -50,7 +50,7 @@ test.describe(
|
|||
expect(actualSuggestions.join(',')).toContain('jpg');
|
||||
});
|
||||
|
||||
test('also displays descriptions for operators', async ({ page, pageObjects }) => {
|
||||
spaceTest('also displays descriptions for operators', async ({ page, pageObjects }) => {
|
||||
await pageObjects.datePicker.setAbsoluteRange(testData.LOGSTASH_IN_RANGE_DATES);
|
||||
await page.testSubj.fill('queryInput', 'extension.raw');
|
||||
await expect(page.testSubj.locator('^autocompleteSuggestion-operator')).toHaveCount(2);
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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/scout';
|
||||
import { spaceTest, testData, assertionMessages } from '../fixtures';
|
||||
|
||||
spaceTest.describe(
|
||||
'Discover app - value suggestions non-time based',
|
||||
{ tag: ['@ess', '@svlSearch', '@svlOblt'] },
|
||||
// TODO: Update to use an ES archive with an index accessible to 'viewer'
|
||||
// for running this test against the Security serverless project.
|
||||
() => {
|
||||
spaceTest.beforeAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.savedObjects.load(testData.KBN_ARCHIVES.NO_TIME_FIELD);
|
||||
await scoutSpace.uiSettings.setDefaultIndex(testData.DATA_VIEW_NAME.NO_TIME_FIELD);
|
||||
});
|
||||
|
||||
spaceTest.afterAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.uiSettings.unset('defaultIndex');
|
||||
await scoutSpace.savedObjects.cleanStandardList();
|
||||
});
|
||||
|
||||
spaceTest.beforeEach(async ({ browserAuth, pageObjects }) => {
|
||||
await browserAuth.loginAsViewer();
|
||||
await pageObjects.discover.goto();
|
||||
});
|
||||
|
||||
spaceTest(
|
||||
'shows all auto-suggest options for a filter in discover context app',
|
||||
async ({ page }) => {
|
||||
await page.testSubj.fill('queryInput', 'type.keyword : ');
|
||||
await expect(
|
||||
page.testSubj.locator('autoCompleteSuggestionText'),
|
||||
assertionMessages.QUERY_BAR_VALIDATION.SUGGESTIONS_COUNT
|
||||
).toHaveCount(1);
|
||||
const actualSuggestions = await page.testSubj
|
||||
.locator('autoCompleteSuggestionText')
|
||||
.allTextContents();
|
||||
expect(actualSuggestions.join(',')).toContain('"apache"');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
|
@ -6,34 +6,34 @@
|
|||
*/
|
||||
|
||||
import { expect, tags } from '@kbn/scout';
|
||||
import { test, testData, assertionMessages } from '../fixtures';
|
||||
import { spaceTest, testData, assertionMessages } from '../fixtures';
|
||||
|
||||
test.describe(
|
||||
spaceTest.describe(
|
||||
'Discover app - value suggestions: useTimeRange disabled',
|
||||
{ tag: tags.DEPLOYMENT_AGNOSTIC },
|
||||
() => {
|
||||
test.beforeAll(async ({ esArchiver, kbnClient, uiSettings }) => {
|
||||
await esArchiver.loadIfNeeded(testData.ES_ARCHIVES.LOGSTASH);
|
||||
await kbnClient.importExport.load(testData.KBN_ARCHIVES.DASHBOARD_DRILLDOWNS);
|
||||
await uiSettings.set({
|
||||
defaultIndex: testData.DATA_VIEW_ID.LOGSTASH, // TODO: investigate why it is required for `node scripts/playwright_test.js` run
|
||||
'timepicker:timeDefaults': `{ "from": "${testData.LOGSTASH_DEFAULT_START_TIME}", "to": "${testData.LOGSTASH_DEFAULT_END_TIME}"}`,
|
||||
'autocomplete:useTimeRange': false,
|
||||
spaceTest.beforeAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.savedObjects.load(testData.KBN_ARCHIVES.DASHBOARD_DRILLDOWNS);
|
||||
await scoutSpace.uiSettings.setDefaultIndex(testData.DATA_VIEW_NAME.LOGSTASH);
|
||||
await scoutSpace.uiSettings.setDefaultTime({
|
||||
from: testData.LOGSTASH_DEFAULT_START_TIME,
|
||||
to: testData.LOGSTASH_DEFAULT_END_TIME,
|
||||
});
|
||||
await scoutSpace.uiSettings.set({ 'autocomplete:useTimeRange': false });
|
||||
});
|
||||
|
||||
test.afterAll(async ({ uiSettings, kbnClient }) => {
|
||||
await uiSettings.unset('defaultIndex', 'timepicker:timeDefaults');
|
||||
await uiSettings.set({ 'autocomplete:useTimeRange': true });
|
||||
await kbnClient.savedObjects.cleanStandardList();
|
||||
spaceTest.afterAll(async ({ scoutSpace }) => {
|
||||
await scoutSpace.uiSettings.unset('defaultIndex', 'timepicker:timeDefaults');
|
||||
await scoutSpace.uiSettings.set({ 'autocomplete:useTimeRange': true });
|
||||
await scoutSpace.savedObjects.cleanStandardList();
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ browserAuth, pageObjects }) => {
|
||||
spaceTest.beforeEach(async ({ browserAuth, pageObjects }) => {
|
||||
await browserAuth.loginAsViewer();
|
||||
await pageObjects.discover.goto();
|
||||
});
|
||||
|
||||
test('show up if outside of range', async ({ page, pageObjects }) => {
|
||||
spaceTest('show up if outside of range', async ({ page, pageObjects }) => {
|
||||
await pageObjects.datePicker.setAbsoluteRange(testData.LOGSTASH_OUT_OF_RANGE_DATES);
|
||||
await page.testSubj.fill('queryInput', 'extension.raw : ');
|
||||
await expect(
|
||||
|
@ -46,7 +46,7 @@ test.describe(
|
|||
expect(actualSuggestions.join(',')).toContain('jpg');
|
||||
});
|
||||
|
||||
test('show up if in range', async ({ page, pageObjects }) => {
|
||||
spaceTest('show up if in range', async ({ page, pageObjects }) => {
|
||||
await pageObjects.datePicker.setAbsoluteRange(testData.LOGSTASH_IN_RANGE_DATES);
|
||||
await page.testSubj.fill('queryInput', 'extension.raw : ');
|
||||
await expect(
|
|
@ -1,123 +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/scout';
|
||||
import { test, testData } from '../fixtures';
|
||||
import type { ExtendedScoutTestFixtures } from '../fixtures';
|
||||
|
||||
const assertNoFilterAndEmptyQuery = async (
|
||||
filterBadge: { field: string; value: string },
|
||||
pageObjects: ExtendedScoutTestFixtures['pageObjects'],
|
||||
page: ExtendedScoutTestFixtures['page']
|
||||
) => {
|
||||
expect(
|
||||
// checking if filter exists, enabled or disabled
|
||||
await pageObjects.filterBar.hasFilter(filterBadge),
|
||||
`Filter ${JSON.stringify(filterBadge)} should not exist`
|
||||
).toBe(false);
|
||||
await expect(
|
||||
page.testSubj.locator('queryInput'),
|
||||
'Query Bar input field should be empty'
|
||||
).toHaveText('');
|
||||
};
|
||||
|
||||
const assertDataViewIsSelected = async (page: ExtendedScoutTestFixtures['page'], name: string) =>
|
||||
await expect(
|
||||
page.testSubj.locator('*dataView-switch-link'),
|
||||
'Incorrect data view is selected'
|
||||
).toHaveText(name);
|
||||
|
||||
test.describe('Discover app - saved searches', { tag: ['@ess', '@svlSearch', '@svlOblt'] }, () => {
|
||||
// TODO: Update to use an ES archive with an index accessible to 'viewer'
|
||||
// for running this test against the Security serverless project.
|
||||
const START_TIME = '2019-04-27T23:56:51.374Z';
|
||||
const END_TIME = '2019-08-23T16:18:51.821Z';
|
||||
const PANEL_NAME = 'Ecommerce Data';
|
||||
const SEARCH_QUERY = 'customer_gender:MALE';
|
||||
const SAVED_SEARCH_NAME = 'test-unselect-saved-search';
|
||||
const filterFieldAndValue = {
|
||||
field: 'category',
|
||||
value: `Men's Shoes`,
|
||||
};
|
||||
|
||||
test.beforeAll(async ({ esArchiver, kbnClient, uiSettings }) => {
|
||||
await esArchiver.loadIfNeeded(testData.ES_ARCHIVES.ECOMMERCE);
|
||||
await kbnClient.importExport.load(testData.KBN_ARCHIVES.DISCOVER);
|
||||
await kbnClient.importExport.load(testData.KBN_ARCHIVES.ECOMMERCE);
|
||||
await uiSettings.set({
|
||||
defaultIndex: testData.DATA_VIEW_ID.ECOMMERCE,
|
||||
'timepicker:timeDefaults': `{ "from": "${START_TIME}", "to": "${END_TIME}"}`,
|
||||
});
|
||||
});
|
||||
|
||||
test.afterAll(async ({ kbnClient, uiSettings }) => {
|
||||
await uiSettings.unset('defaultIndex', 'timepicker:timeDefaults');
|
||||
await kbnClient.savedObjects.cleanStandardList();
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ browserAuth }) => {
|
||||
await browserAuth.loginAsPrivilegedUser();
|
||||
});
|
||||
|
||||
test('should customize time range on dashboards', async ({ pageObjects, page }) => {
|
||||
await pageObjects.dashboard.goto();
|
||||
await pageObjects.dashboard.openNewDashboard();
|
||||
await pageObjects.dashboard.addPanelFromLibrary(PANEL_NAME);
|
||||
await page.testSubj.locator('savedSearchTotalDocuments').waitFor({
|
||||
state: 'visible',
|
||||
});
|
||||
|
||||
await pageObjects.dashboard.customizePanel({
|
||||
name: PANEL_NAME,
|
||||
customTimeRageCommonlyUsed: { value: 'Last_90 days' },
|
||||
});
|
||||
await expect(
|
||||
page.testSubj.locator('embeddedSavedSearchDocTable').locator('.euiDataGrid__noResults'),
|
||||
'No results message in Saved Search panel should be visible'
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test(`should unselect saved search when navigating to a 'new'`, async ({ pageObjects, page }) => {
|
||||
await pageObjects.discover.goto();
|
||||
await assertDataViewIsSelected(page, testData.DATA_VIEW.ECOMMERCE);
|
||||
await pageObjects.filterBar.addFilter({
|
||||
...filterFieldAndValue,
|
||||
operator: 'is',
|
||||
});
|
||||
await page.testSubj.fill('queryInput', SEARCH_QUERY);
|
||||
await page.testSubj.click('querySubmitButton');
|
||||
await pageObjects.discover.waitForHistogramRendered();
|
||||
|
||||
await pageObjects.discover.saveSearch(SAVED_SEARCH_NAME);
|
||||
await pageObjects.discover.waitForHistogramRendered();
|
||||
|
||||
expect(
|
||||
await pageObjects.filterBar.hasFilter({
|
||||
...filterFieldAndValue,
|
||||
enabled: true, // Filter is enabled by default
|
||||
})
|
||||
).toBe(true);
|
||||
await expect(page.testSubj.locator('queryInput')).toHaveText(SEARCH_QUERY);
|
||||
|
||||
// create new search
|
||||
await pageObjects.discover.clickNewSearch();
|
||||
await assertDataViewIsSelected(page, testData.DATA_VIEW.ECOMMERCE);
|
||||
await assertNoFilterAndEmptyQuery(filterFieldAndValue, pageObjects, page);
|
||||
|
||||
// change data view
|
||||
await pageObjects.discover.selectDataView(testData.DATA_VIEW.LOGSTASH);
|
||||
await assertNoFilterAndEmptyQuery(filterFieldAndValue, pageObjects, page);
|
||||
|
||||
// change data view again
|
||||
await pageObjects.discover.selectDataView(testData.DATA_VIEW.ECOMMERCE);
|
||||
await assertNoFilterAndEmptyQuery(filterFieldAndValue, pageObjects, page);
|
||||
|
||||
// create new search again
|
||||
await pageObjects.discover.clickNewSearch();
|
||||
await assertDataViewIsSelected(page, testData.DATA_VIEW.ECOMMERCE);
|
||||
});
|
||||
});
|
|
@ -1,49 +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/scout';
|
||||
import { test, testData, assertionMessages } from '../fixtures';
|
||||
|
||||
test.describe(
|
||||
'Discover app - value suggestions non-time based',
|
||||
{ tag: ['@ess', '@svlSearch', '@svlOblt'] },
|
||||
// TODO: Update to use an ES archive with an index accessible to 'viewer'
|
||||
// for running this test against the Security serverless project.
|
||||
() => {
|
||||
test.beforeAll(async ({ esArchiver, kbnClient, uiSettings }) => {
|
||||
await esArchiver.loadIfNeeded(testData.ES_ARCHIVES.NO_TIME_FIELD);
|
||||
await kbnClient.importExport.load(testData.KBN_ARCHIVES.NO_TIME_FIELD);
|
||||
await uiSettings.set({
|
||||
defaultIndex: 'without-timefield',
|
||||
});
|
||||
});
|
||||
|
||||
test.afterAll(async ({ kbnClient, uiSettings }) => {
|
||||
await uiSettings.unset('defaultIndex');
|
||||
await kbnClient.savedObjects.cleanStandardList();
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ browserAuth, pageObjects }) => {
|
||||
await browserAuth.loginAsViewer();
|
||||
await pageObjects.discover.goto();
|
||||
});
|
||||
|
||||
test('shows all auto-suggest options for a filter in discover context app', async ({
|
||||
page,
|
||||
}) => {
|
||||
await page.testSubj.fill('queryInput', 'type.keyword : ');
|
||||
await expect(
|
||||
page.testSubj.locator('autoCompleteSuggestionText'),
|
||||
assertionMessages.QUERY_BAR_VALIDATION.SUGGESTIONS_COUNT
|
||||
).toHaveCount(1);
|
||||
const actualSuggestions = await page.testSubj
|
||||
.locator('autoCompleteSuggestionText')
|
||||
.allTextContents();
|
||||
expect(actualSuggestions.join(',')).toContain('"apache"');
|
||||
});
|
||||
}
|
||||
);
|
Loading…
Add table
Add a link
Reference in a new issue