From d291637cc178b3d557429608804bf3af43014a18 Mon Sep 17 00:00:00 2001 From: Spencer Date: Tue, 25 May 2021 10:25:09 -0600 Subject: [PATCH] [ftr] implement FtrService classes and migrate common services (#99546) Co-authored-by: spalger Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../development-functional-tests.asciidoc | 9 +- .../lib/providers/provider_collection.ts | 12 +- .../functional_test_runner/public_types.ts | 12 +- ...r_context.d.ts => ftr_provider_context.ts} | 3 +- test/common/services/deployment.ts | 60 +++++----- test/common/services/index.ts | 16 +-- .../services/kibana_server/kibana_server.ts | 2 +- test/common/services/randomness.ts | 93 ++++++++------- test/common/services/retry/index.ts | 2 +- test/common/services/retry/retry.ts | 104 ++++++++--------- test/common/services/saved_object_info.ts | 74 ++++++------ test/common/services/security/security.ts | 32 ++--- test/common/services/security/test_user.ts | 110 ++++++++++-------- test/functional/apps/visualize/index.ts | 2 +- .../functional/apps/visualize/legacy/index.ts | 2 +- ...r_context.d.ts => ftr_provider_context.ts} | 3 +- test/functional/page_objects/time_picker.ts | 2 +- .../page_objects/visual_builder_page.ts | 2 +- x-pack/test/functional/apps/lens/index.ts | 2 +- ...r_context.d.ts => ftr_provider_context.ts} | 3 +- 20 files changed, 292 insertions(+), 253 deletions(-) rename test/common/{ftr_provider_context.d.ts => ftr_provider_context.ts} (76%) rename test/functional/{ftr_provider_context.d.ts => ftr_provider_context.ts} (78%) rename x-pack/test/functional/{ftr_provider_context.d.ts => ftr_provider_context.ts} (74%) diff --git a/docs/developer/contributing/development-functional-tests.asciidoc b/docs/developer/contributing/development-functional-tests.asciidoc index 110704a8e569a9f..f0041b85c14ebf6 100644 --- a/docs/developer/contributing/development-functional-tests.asciidoc +++ b/docs/developer/contributing/development-functional-tests.asciidoc @@ -139,11 +139,14 @@ export default function (/* { providerAPI } */) { } ----------- -**Services**::: -Services are named singleton values produced by a Service Provider. Tests and other services can retrieve service instances by asking for them by name. All functionality except the mocha API is exposed via services. +**Service**::: +A Service is a named singleton created using a subclass of `FtrService`. Tests and other services can retrieve service instances by asking for them by name. All functionality except the mocha API is exposed via services. When you write your own functional tests check for existing services that help with the interactions you're looking to execute, and add new services for interactions which aren't already encoded in a service. + +**Service Providers**::: +For legacy purposes, and for when creating a subclass of `FtrService` is inconvenient, you can also create services using a "Service Provider". These are functions which which create service instances and return them. These instances are cached and provided to tests. Currently these providers may also return a Promise for the service instance, allowing the service to do some setup work before tests run. We expect to fully deprecate and remove support for async service providers in the near future and instead require that services use the `lifecycle` service to run setup before tests. Providers which return instances of classes other than `FtrService` will likely remain supported for as long as possible. **Page objects**::: -Page objects are a special type of service that encapsulate behaviors common to a particular page or plugin. When you write your own plugin, you’ll likely want to add a page object (or several) that describes the common interactions your tests need to execute. +Page objects are functionally equivalent to services, except they are loaded with a slightly different mechanism and generally defined separate from services. When you write your own functional tests you might want to write some of your services as Page objects, but it is not required. **Test Files**::: The `FunctionalTestRunner`'s primary purpose is to execute test files. These files export a Test Provider that is called with a Provider API but is not expected to return a value. Instead Test Providers define a suite using https://mochajs.org/#bdd[mocha's BDD interface]. diff --git a/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts b/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts index 1aa5df1105f460c..2d05d5bba5ff6fe 100644 --- a/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts +++ b/packages/kbn-test/src/functional_test_runner/lib/providers/provider_collection.ts @@ -12,6 +12,7 @@ import { loadTracer } from '../load_tracer'; import { createAsyncInstance, isAsyncInstance } from './async_instance'; import { Providers } from './read_provider_spec'; import { createVerboseInstance } from './verbose_instance'; +import { GenericFtrService } from '../../public_types'; export class ProviderCollection { private readonly instances = new Map(); @@ -58,12 +59,19 @@ export class ProviderCollection { } public invokeProviderFn(provider: (args: any) => any) { - return provider({ + const ctx = { getService: this.getService, hasService: this.hasService, getPageObject: this.getPageObject, getPageObjects: this.getPageObjects, - }); + }; + + if (provider.prototype instanceof GenericFtrService) { + const Constructor = (provider as any) as new (ctx: any) => any; + return new Constructor(ctx); + } + + return provider(ctx); } private findProvider(type: string, name: string) { diff --git a/packages/kbn-test/src/functional_test_runner/public_types.ts b/packages/kbn-test/src/functional_test_runner/public_types.ts index 915cb34f6ffe509..4a30744c09b5162 100644 --- a/packages/kbn-test/src/functional_test_runner/public_types.ts +++ b/packages/kbn-test/src/functional_test_runner/public_types.ts @@ -13,7 +13,7 @@ import { Test, Suite } from './fake_mocha_types'; export { Lifecycle, Config, FailureMetadata }; -interface AsyncInstance { +export interface AsyncInstance { /** * Services that are initialized async are not ready before the tests execute, so you might need * to call `init()` and await the promise it returns before interacting with the service @@ -39,7 +39,11 @@ export type ProvidedType any> = MaybeAsyncInstance * promise types into the async instances that other providers will receive. */ type ProvidedTypeMap = { - [K in keyof T]: T[K] extends (...args: any[]) => any ? ProvidedType : unknown; + [K in keyof T]: T[K] extends new (...args: any[]) => infer X + ? X + : T[K] extends (...args: any[]) => any + ? ProvidedType + : unknown; }; export interface GenericFtrProviderContext< @@ -84,6 +88,10 @@ export interface GenericFtrProviderContext< loadTestFile(path: string): void; } +export class GenericFtrService> { + constructor(protected readonly ctx: ProviderContext) {} +} + export interface FtrConfigProviderContext { log: ToolingLog; readConfigFile(path: string): Promise; diff --git a/test/common/ftr_provider_context.d.ts b/test/common/ftr_provider_context.ts similarity index 76% rename from test/common/ftr_provider_context.d.ts rename to test/common/ftr_provider_context.ts index 91d35a2dbc32a6f..6d21aedfe1d5ec4 100644 --- a/test/common/ftr_provider_context.d.ts +++ b/test/common/ftr_provider_context.ts @@ -6,8 +6,9 @@ * Side Public License, v 1. */ -import { GenericFtrProviderContext } from '@kbn/test'; +import { GenericFtrProviderContext, GenericFtrService } from '@kbn/test'; import { services } from './services'; export type FtrProviderContext = GenericFtrProviderContext; +export class FtrService extends GenericFtrService {} diff --git a/test/common/services/deployment.ts b/test/common/services/deployment.ts index 65466ca966ad25b..b250d39ce65d652 100644 --- a/test/common/services/deployment.ts +++ b/test/common/services/deployment.ts @@ -10,39 +10,37 @@ import { get } from 'lodash'; import fetch from 'node-fetch'; import { getUrl } from '@kbn/test'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrService } from '../ftr_provider_context'; -export function DeploymentProvider({ getService }: FtrProviderContext) { - const config = getService('config'); +export class DeploymentService extends FtrService { + private readonly config = this.ctx.getService('config'); - return { - /** - * Returns Kibana host URL - */ - getHostPort() { - return getUrl.baseUrl(config.get('servers.kibana')); - }, + /** + * Returns Kibana host URL + */ + getHostPort() { + return getUrl.baseUrl(this.config.get('servers.kibana')); + } - /** - * Returns ES host URL - */ - getEsHostPort() { - return getUrl.baseUrl(config.get('servers.elasticsearch')); - }, + /** + * Returns ES host URL + */ + getEsHostPort() { + return getUrl.baseUrl(this.config.get('servers.elasticsearch')); + } - async isCloud(): Promise { - const baseUrl = this.getHostPort(); - const username = config.get('servers.kibana.username'); - const password = config.get('servers.kibana.password'); - const response = await fetch(baseUrl + '/api/stats?extended', { - method: 'get', - headers: { - 'Content-Type': 'application/json', - Authorization: 'Basic ' + Buffer.from(username + ':' + password).toString('base64'), - }, - }); - const data = await response.json(); - return get(data, 'usage.cloud.is_cloud_enabled', false); - }, - }; + async isCloud(): Promise { + const baseUrl = this.getHostPort(); + const username = this.config.get('servers.kibana.username'); + const password = this.config.get('servers.kibana.password'); + const response = await fetch(baseUrl + '/api/stats?extended', { + method: 'get', + headers: { + 'Content-Type': 'application/json', + Authorization: 'Basic ' + Buffer.from(username + ':' + password).toString('base64'), + }, + }); + const data = await response.json(); + return get(data, 'usage.cloud.is_cloud_enabled', false); + } } diff --git a/test/common/services/index.ts b/test/common/services/index.ts index cc4859b7016bf31..02aafc59fa80f46 100644 --- a/test/common/services/index.ts +++ b/test/common/services/index.ts @@ -6,26 +6,26 @@ * Side Public License, v 1. */ -import { DeploymentProvider } from './deployment'; +import { DeploymentService } from './deployment'; import { LegacyEsProvider } from './legacy_es'; import { ElasticsearchProvider } from './elasticsearch'; import { EsArchiverProvider } from './es_archiver'; import { KibanaServerProvider } from './kibana_server'; -import { RetryProvider } from './retry'; -import { RandomnessProvider } from './randomness'; +import { RetryService } from './retry'; +import { RandomnessService } from './randomness'; import { SecurityServiceProvider } from './security'; import { EsDeleteAllIndicesProvider } from './es_delete_all_indices'; -import { SavedObjectInfoProvider } from './saved_object_info'; +import { SavedObjectInfoService } from './saved_object_info'; export const services = { - deployment: DeploymentProvider, + deployment: DeploymentService, legacyEs: LegacyEsProvider, es: ElasticsearchProvider, esArchiver: EsArchiverProvider, kibanaServer: KibanaServerProvider, - retry: RetryProvider, - randomness: RandomnessProvider, + retry: RetryService, + randomness: RandomnessService, security: SecurityServiceProvider, esDeleteAllIndices: EsDeleteAllIndicesProvider, - savedObjectInfo: SavedObjectInfoProvider, + savedObjectInfo: SavedObjectInfoService, }; diff --git a/test/common/services/kibana_server/kibana_server.ts b/test/common/services/kibana_server/kibana_server.ts index f366a864db980d1..63803bd511bd14a 100644 --- a/test/common/services/kibana_server/kibana_server.ts +++ b/test/common/services/kibana_server/kibana_server.ts @@ -11,7 +11,7 @@ import { KbnClient } from '@kbn/test'; import { FtrProviderContext } from '../../ftr_provider_context'; -export function KibanaServerProvider({ getService }: FtrProviderContext) { +export function KibanaServerProvider({ getService }: FtrProviderContext): KbnClient { const log = getService('log'); const config = getService('config'); const lifecycle = getService('lifecycle'); diff --git a/test/common/services/randomness.ts b/test/common/services/randomness.ts index 88b0411f98033e9..82f06fb681066a0 100644 --- a/test/common/services/randomness.ts +++ b/test/common/services/randomness.ts @@ -7,8 +7,20 @@ */ import Chance from 'chance'; +import { ToolingLog } from '@kbn/dev-utils'; -import { FtrProviderContext } from '../ftr_provider_context'; +import { FtrService } from '../ftr_provider_context'; + +let __CACHED_SEED__: number | undefined; +function getSeed(log: ToolingLog) { + if (__CACHED_SEED__ !== undefined) { + return __CACHED_SEED__; + } + + __CACHED_SEED__ = Date.now(); + log.debug('randomness seed: %j', __CACHED_SEED__); + return __CACHED_SEED__; +} interface CharOptions { pool?: string; @@ -27,52 +39,45 @@ interface NumberOptions { max?: number; } -export function RandomnessProvider({ getService }: FtrProviderContext) { - const log = getService('log'); - - const seed = Date.now(); - log.debug('randomness seed: %j', seed); - - const chance = new Chance(seed); +export class RandomnessService extends FtrService { + private readonly chance = new Chance(getSeed(this.ctx.getService('log'))); - return new (class RandomnessService { - /** - * Generate a random natural number - * - * range: 0 to 9007199254740991 - * - */ - naturalNumber(options?: NumberOptions) { - return chance.natural(options); - } + /** + * Generate a random natural number + * + * range: 0 to 9007199254740991 + * + */ + naturalNumber(options?: NumberOptions) { + return this.chance.natural(options); + } - /** - * Generate a random integer - */ - integer(options?: NumberOptions) { - return chance.integer(options); - } + /** + * Generate a random integer + */ + integer(options?: NumberOptions) { + return this.chance.integer(options); + } - /** - * Generate a random number, defaults to at least 4 and no more than 8 syllables - */ - word(options: { syllables?: number } = {}) { - const { syllables = this.naturalNumber({ min: 4, max: 8 }) } = options; + /** + * Generate a random number, defaults to at least 4 and no more than 8 syllables + */ + word(options: { syllables?: number } = {}) { + const { syllables = this.naturalNumber({ min: 4, max: 8 }) } = options; - return chance.word({ - syllables, - }); - } + return this.chance.word({ + syllables, + }); + } - /** - * Generate a random string, defaults to at least 8 and no more than 15 alpha-numeric characters - */ - string(options: StringOptions = {}) { - return chance.string({ - length: this.naturalNumber({ min: 8, max: 15 }), - ...(options.pool === 'undefined' ? { alpha: true, numeric: true, symbols: false } : {}), - ...options, - }); - } - })(); + /** + * Generate a random string, defaults to at least 8 and no more than 15 alpha-numeric characters + */ + string(options: StringOptions = {}) { + return this.chance.string({ + length: this.naturalNumber({ min: 8, max: 15 }), + ...(options.pool === 'undefined' ? { alpha: true, numeric: true, symbols: false } : {}), + ...options, + }); + } } diff --git a/test/common/services/retry/index.ts b/test/common/services/retry/index.ts index 4914b3cff2261e2..08ce3f9bd46611a 100644 --- a/test/common/services/retry/index.ts +++ b/test/common/services/retry/index.ts @@ -6,4 +6,4 @@ * Side Public License, v 1. */ -export { RetryProvider } from './retry'; +export { RetryService } from './retry'; diff --git a/test/common/services/retry/retry.ts b/test/common/services/retry/retry.ts index 8ea2a52b6adf698..5c823e256ddc8d9 100644 --- a/test/common/services/retry/retry.ts +++ b/test/common/services/retry/retry.ts @@ -6,64 +6,62 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrService } from '../../ftr_provider_context'; import { retryForSuccess } from './retry_for_success'; import { retryForTruthy } from './retry_for_truthy'; -export function RetryProvider({ getService }: FtrProviderContext) { - const config = getService('config'); - const log = getService('log'); +export class RetryService extends FtrService { + private readonly config = this.ctx.getService('config'); + private readonly log = this.ctx.getService('log'); - return new (class Retry { - public async tryForTime( - timeout: number, - block: () => Promise, - onFailureBlock?: () => Promise - ) { - return await retryForSuccess(log, { - timeout, - methodName: 'retry.tryForTime', - block, - onFailureBlock, - }); - } + public async tryForTime( + timeout: number, + block: () => Promise, + onFailureBlock?: () => Promise + ) { + return await retryForSuccess(this.log, { + timeout, + methodName: 'retry.tryForTime', + block, + onFailureBlock, + }); + } - public async try(block: () => Promise, onFailureBlock?: () => Promise) { - return await retryForSuccess(log, { - timeout: config.get('timeouts.try'), - methodName: 'retry.try', - block, - onFailureBlock, - }); - } + public async try(block: () => Promise, onFailureBlock?: () => Promise) { + return await retryForSuccess(this.log, { + timeout: this.config.get('timeouts.try'), + methodName: 'retry.try', + block, + onFailureBlock, + }); + } - public async waitForWithTimeout( - description: string, - timeout: number, - block: () => Promise, - onFailureBlock?: () => Promise - ) { - await retryForTruthy(log, { - timeout, - methodName: 'retry.waitForWithTimeout', - description, - block, - onFailureBlock, - }); - } + public async waitForWithTimeout( + description: string, + timeout: number, + block: () => Promise, + onFailureBlock?: () => Promise + ) { + await retryForTruthy(this.log, { + timeout, + methodName: 'retry.waitForWithTimeout', + description, + block, + onFailureBlock, + }); + } - public async waitFor( - description: string, - block: () => Promise, - onFailureBlock?: () => Promise - ) { - await retryForTruthy(log, { - timeout: config.get('timeouts.waitFor'), - methodName: 'retry.waitFor', - description, - block, - onFailureBlock, - }); - } - })(); + public async waitFor( + description: string, + block: () => Promise, + onFailureBlock?: () => Promise + ) { + await retryForTruthy(this.log, { + timeout: this.config.get('timeouts.waitFor'), + methodName: 'retry.waitFor', + description, + block, + onFailureBlock, + }); + } } diff --git a/test/common/services/saved_object_info.ts b/test/common/services/saved_object_info.ts index 02ab38d4ecb1db3..1558b364f539163 100644 --- a/test/common/services/saved_object_info.ts +++ b/test/common/services/saved_object_info.ts @@ -6,48 +6,44 @@ * Side Public License, v 1. */ -import { Client } from '@elastic/elasticsearch'; -import url from 'url'; -import { Either, fromNullable, chain, getOrElse } from 'fp-ts/Either'; -import { flow } from 'fp-ts/function'; -import { FtrProviderContext } from '../ftr_provider_context'; - -const pluck = (key: string) => (obj: any): Either => - fromNullable(new Error(`Missing ${key}`))(obj[key]); - -const types = (node: string) => async (index: string = '.kibana') => { - let res: unknown; - try { - const { body } = await new Client({ node }).search({ - index, - body: { - aggs: { - savedobjs: { - terms: { - field: 'type', +import { inspect } from 'util'; + +import { TermsAggregate } from '@elastic/elasticsearch/api/types'; + +import { FtrService } from '../ftr_provider_context'; + +export class SavedObjectInfoService extends FtrService { + private readonly es = this.ctx.getService('es'); + + public async getTypes(index = '.kibana') { + try { + const { body } = await this.es.search({ + index, + size: 0, + body: { + aggs: { + savedobjs: { + terms: { + field: 'type', + }, }, }, }, - }, - }); - - res = flow( - pluck('aggregations'), - chain(pluck('savedobjs')), - chain(pluck('buckets')), - getOrElse((err) => `${err.message}`) - )(body); - } catch (err) { - throw new Error(`Error while searching for saved object types: ${err}`); - } + }); - return res; -}; + const agg = body.aggregations?.savedobjs as + | TermsAggregate<{ key: string; doc_count: number }> + | undefined; -export const SavedObjectInfoProvider: any = ({ getService }: FtrProviderContext) => { - const config = getService('config'); + if (!agg?.buckets) { + throw new Error( + `expected es to return buckets of saved object types: ${inspect(body, { depth: 100 })}` + ); + } - return { - types: types(url.format(config.get('servers.elasticsearch'))), - }; -}; + return agg.buckets; + } catch (error) { + throw new Error(`Error while searching for saved object types: ${error}`); + } + } +} diff --git a/test/common/services/security/security.ts b/test/common/services/security/security.ts index 52fb6bdd70330e9..b8fea0a0c59b269 100644 --- a/test/common/services/security/security.ts +++ b/test/common/services/security/security.ts @@ -10,23 +10,27 @@ import { Role } from './role'; import { User } from './user'; import { RoleMappings } from './role_mappings'; import { FtrProviderContext } from '../../ftr_provider_context'; -import { createTestUserService, TestUserSupertestProvider } from './test_user'; +import { createTestUserService, TestUserSupertestProvider, TestUser } from './test_user'; -export async function SecurityServiceProvider(context: FtrProviderContext) { - const { getService } = context; - const log = getService('log'); - const kibanaServer = getService('kibanaServer'); +export class SecurityService { + constructor( + public readonly roleMappings: RoleMappings, + public readonly testUser: TestUser, + public readonly role: Role, + public readonly user: User, + public readonly testUserSupertest: ReturnType + ) {} +} + +export async function SecurityServiceProvider(ctx: FtrProviderContext) { + const log = ctx.getService('log'); + const kibanaServer = ctx.getService('kibanaServer'); const role = new Role(log, kibanaServer); const user = new User(log, kibanaServer); - const testUser = await createTestUserService(role, user, context); - const testUserSupertest = TestUserSupertestProvider(context); + const testUser = await createTestUserService(ctx, role, user); + const testUserSupertest = TestUserSupertestProvider(ctx); + const roleMappings = new RoleMappings(log, kibanaServer); - return new (class SecurityService { - roleMappings = new RoleMappings(log, kibanaServer); - testUser = testUser; - role = role; - user = user; - testUserSupertest = testUserSupertest; - })(); + return new SecurityService(roleMappings, testUser, role, user, testUserSupertest); } diff --git a/test/common/services/security/test_user.ts b/test/common/services/security/test_user.ts index d5e1f02e1bc8ca5..8b0a1c34e790cbd 100644 --- a/test/common/services/security/test_user.ts +++ b/test/common/services/security/test_user.ts @@ -11,41 +11,84 @@ import supertestAsPromised from 'supertest-as-promised'; import { Role } from './role'; import { User } from './user'; -import { FtrProviderContext } from '../../ftr_provider_context'; +import { FtrService, FtrProviderContext } from '../../ftr_provider_context'; import { Browser } from '../../../functional/services/common'; import { TestSubjects } from '../../../functional/services/common'; const TEST_USER_NAME = 'test_user'; const TEST_USER_PASSWORD = 'changeme'; -export async function createTestUserService( - role: Role, - user: User, - { getService, hasService }: FtrProviderContext -) { - const log = getService('log'); - const config = getService('config'); - // @ts-ignore browser service is not normally available in common. - const browser: Browser | void = hasService('browser') && getService('browser'); - const testSubjects: TestSubjects | undefined = +export class TestUser extends FtrService { + private readonly config = this.ctx.getService('config'); + private readonly log = this.ctx.getService('log'); + + private readonly browser: Browser | void = + // browser service is not normally available in common. + this.ctx.hasService('browser') ? (this.ctx.getService('browser' as any) as Browser) : undefined; + + private readonly testSubjects: TestSubjects | undefined = // testSubject service is not normally available in common. - hasService('testSubjects') ? (getService('testSubjects' as any) as TestSubjects) : undefined; - const kibanaServer = getService('kibanaServer'); + this.ctx.hasService('testSubjects') + ? (this.ctx.getService('testSubjects' as any) as TestSubjects) + : undefined; + + constructor( + ctx: FtrProviderContext, + private readonly enabled: boolean, + private readonly user: User + ) { + super(ctx); + } + + async restoreDefaults(shouldRefreshBrowser: boolean = true) { + if (this.enabled) { + await this.setRoles(this.config.get('security.defaultRoles'), shouldRefreshBrowser); + } + } + + async setRoles(roles: string[], shouldRefreshBrowser: boolean = true) { + if (this.enabled) { + this.log.debug(`set roles = ${roles}`); + await this.user.create(TEST_USER_NAME, { + password: TEST_USER_PASSWORD, + roles, + full_name: 'test user', + }); + + if (this.browser && this.testSubjects && shouldRefreshBrowser) { + if (await this.testSubjects.exists('kibanaChrome', { allowHidden: true })) { + await this.browser.refresh(); + // accept alert if it pops up + const alert = await this.browser.getAlert(); + await alert?.accept(); + await this.testSubjects.find('kibanaChrome', this.config.get('timeouts.find') * 10); + } + } + } + } +} + +export async function createTestUserService(ctx: FtrProviderContext, role: Role, user: User) { + const log = ctx.getService('log'); + const config = ctx.getService('config'); + const kibanaServer = ctx.getService('kibanaServer'); const enabledPlugins = config.get('security.disableTestUser') ? [] : await kibanaServer.plugins.getEnabledIds(); - const isEnabled = () => { - return enabledPlugins.includes('security') && !config.get('security.disableTestUser'); - }; - if (isEnabled()) { + + const enabled = enabledPlugins.includes('security') && !config.get('security.disableTestUser'); + + if (enabled) { log.debug('===============creating roles and users==============='); + + // create the defined roles (need to map array to create roles) for (const [name, definition] of Object.entries(config.get('security.roles'))) { - // create the defined roles (need to map array to create roles) await role.create(name, definition); } + + // delete the test_user if present (will it error if the user doesn't exist?) try { - // delete the test_user if present (will it error if the user doesn't exist?) await user.delete(TEST_USER_NAME); } catch (exception) { log.debug('no test user to delete'); @@ -60,34 +103,7 @@ export async function createTestUserService( }); } - return new (class TestUser { - async restoreDefaults(shouldRefreshBrowser: boolean = true) { - if (isEnabled()) { - await this.setRoles(config.get('security.defaultRoles'), shouldRefreshBrowser); - } - } - - async setRoles(roles: string[], shouldRefreshBrowser: boolean = true) { - if (isEnabled()) { - log.debug(`set roles = ${roles}`); - await user.create(TEST_USER_NAME, { - password: TEST_USER_PASSWORD, - roles, - full_name: 'test user', - }); - - if (browser && testSubjects && shouldRefreshBrowser) { - if (await testSubjects.exists('kibanaChrome', { allowHidden: true })) { - await browser.refresh(); - // accept alert if it pops up - const alert = await browser.getAlert(); - await alert?.accept(); - await testSubjects.find('kibanaChrome', config.get('timeouts.find') * 10); - } - } - } - } - })(); + return new TestUser(ctx, enabled, user); } export function TestUserSupertestProvider({ getService }: FtrProviderContext) { diff --git a/test/functional/apps/visualize/index.ts b/test/functional/apps/visualize/index.ts index eb224b3c9b87983..b87184bab3c0d71 100644 --- a/test/functional/apps/visualize/index.ts +++ b/test/functional/apps/visualize/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from '../../ftr_provider_context.d'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, loadTestFile }: FtrProviderContext) { const browser = getService('browser'); diff --git a/test/functional/apps/visualize/legacy/index.ts b/test/functional/apps/visualize/legacy/index.ts index 187e8f3f3a663cb..914559e5cea9250 100644 --- a/test/functional/apps/visualize/legacy/index.ts +++ b/test/functional/apps/visualize/legacy/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from '../../../ftr_provider_context.d'; +import { FtrProviderContext } from '../../../ftr_provider_context'; import { UI_SETTINGS } from '../../../../../src/plugins/data/common'; export default function ({ getPageObjects, getService, loadTestFile }: FtrProviderContext) { diff --git a/test/functional/ftr_provider_context.d.ts b/test/functional/ftr_provider_context.ts similarity index 78% rename from test/functional/ftr_provider_context.d.ts rename to test/functional/ftr_provider_context.ts index 4c827393e1ef3ba..a1a29f50b776119 100644 --- a/test/functional/ftr_provider_context.d.ts +++ b/test/functional/ftr_provider_context.ts @@ -6,9 +6,10 @@ * Side Public License, v 1. */ -import { GenericFtrProviderContext } from '@kbn/test'; +import { GenericFtrProviderContext, GenericFtrService } from '@kbn/test'; import { pageObjects } from './page_objects'; import { services } from './services'; export type FtrProviderContext = GenericFtrProviderContext; +export class FtrService extends GenericFtrService {} diff --git a/test/functional/page_objects/time_picker.ts b/test/functional/page_objects/time_picker.ts index cfe250831e06cc2..d3b6edaffdbd324 100644 --- a/test/functional/page_objects/time_picker.ts +++ b/test/functional/page_objects/time_picker.ts @@ -7,7 +7,7 @@ */ import moment from 'moment'; -import { FtrProviderContext } from '../ftr_provider_context.d'; +import { FtrProviderContext } from '../ftr_provider_context'; import { WebElementWrapper } from '../services/lib/web_element_wrapper'; export type CommonlyUsed = diff --git a/test/functional/page_objects/visual_builder_page.ts b/test/functional/page_objects/visual_builder_page.ts index 3ed5d74808fce59..997a1127005ee58 100644 --- a/test/functional/page_objects/visual_builder_page.ts +++ b/test/functional/page_objects/visual_builder_page.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { FtrProviderContext } from '../ftr_provider_context.d'; +import { FtrProviderContext } from '../ftr_provider_context'; import { WebElementWrapper } from '../services/lib/web_element_wrapper'; export function VisualBuilderPageProvider({ getService, getPageObjects }: FtrProviderContext) { diff --git a/x-pack/test/functional/apps/lens/index.ts b/x-pack/test/functional/apps/lens/index.ts index ab7cee13ffebda8..d0466b8814fec1e 100644 --- a/x-pack/test/functional/apps/lens/index.ts +++ b/x-pack/test/functional/apps/lens/index.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { FtrProviderContext } from '../../ftr_provider_context.d'; +import { FtrProviderContext } from '../../ftr_provider_context'; export default function ({ getService, loadTestFile }: FtrProviderContext) { const browser = getService('browser'); diff --git a/x-pack/test/functional/ftr_provider_context.d.ts b/x-pack/test/functional/ftr_provider_context.ts similarity index 74% rename from x-pack/test/functional/ftr_provider_context.d.ts rename to x-pack/test/functional/ftr_provider_context.ts index 24f5087ef7fe2f0..e757164fa1de920 100644 --- a/x-pack/test/functional/ftr_provider_context.d.ts +++ b/x-pack/test/functional/ftr_provider_context.ts @@ -5,9 +5,10 @@ * 2.0. */ -import { GenericFtrProviderContext } from '@kbn/test'; +import { GenericFtrProviderContext, GenericFtrService } from '@kbn/test'; import { pageObjects } from './page_objects'; import { services } from './services'; export type FtrProviderContext = GenericFtrProviderContext; +export class FtrService extends GenericFtrService {}