From 4bc813b8c53b2ddef38f555c83f757a11c9968df Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Mon, 23 Mar 2026 17:35:56 +0100 Subject: [PATCH 1/5] extend regexp, add srv config --- .../rules/scout_no_cross_boundary_imports.js | 8 ++ .../private/kbn-scout-info/src/paths.ts | 12 ++- .../src/registry/test_config.test.ts | 51 ++++++++++ .../src/registry/test_config.ts | 94 +++++++++++++++---- .../configs/config_sets/examples/shared.ts | 21 +++++ .../stateful/classic.stateful.config.ts | 20 ++++ 6 files changed, 185 insertions(+), 21 deletions(-) create mode 100644 src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/examples/shared.ts create mode 100644 src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/examples/stateful/classic.stateful.config.ts diff --git a/packages/kbn-eslint-plugin-eslint/rules/scout_no_cross_boundary_imports.js b/packages/kbn-eslint-plugin-eslint/rules/scout_no_cross_boundary_imports.js index 556e0143c941a..e49bd68d646db 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/scout_no_cross_boundary_imports.js +++ b/packages/kbn-eslint-plugin-eslint/rules/scout_no_cross_boundary_imports.js @@ -41,6 +41,14 @@ const getAllowedPackage = (filePath) => { return { package: '@kbn/scout' }; } + // Developer example plugins (OSS and x-pack) + if (/\/examples\/[^/]+\/test\/(?:scout|scout_\w+)\//.test(filePath)) { + return { package: '@kbn/scout' }; + } + if (/\/x-pack\/examples\/[^/]+\/test\/(?:scout|scout_\w+)\//.test(filePath)) { + return { package: '@kbn/scout' }; + } + return null; }; diff --git a/src/platform/packages/private/kbn-scout-info/src/paths.ts b/src/platform/packages/private/kbn-scout-info/src/paths.ts index 3bf408432d4ec..6a275f0ff5fb0 100644 --- a/src/platform/packages/private/kbn-scout-info/src/paths.ts +++ b/src/platform/packages/private/kbn-scout-info/src/paths.ts @@ -33,7 +33,7 @@ export const SCOUT_PLAYWRIGHT_CONFIGS_PATH = path.resolve( ); export const TESTABLE_COMPONENT_SCOUT_ROOT_PATH_GLOB = - '{src/platform,src/core,x-pack/**}/{plugins,packages}/**/test/scout{_*,}'; + '{src/platform,src/core,x-pack/**}/{plugins,packages}/**/test/scout{_*,},{examples,x-pack/examples}/**/test/scout{_*,}'; export const TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX = new RegExp( `(?:src|x-pack)` + @@ -59,5 +59,15 @@ export const SCOUT_CONFIG_PATH_REGEX = new RegExp( export const SCOUT_CONFIG_MANIFEST_PATH_GLOB = TESTABLE_COMPONENT_SCOUT_ROOT_PATH_GLOB + `/.meta/{${SCOUT_TEST_CATEGORIES.join(',')}}/*.json`; +/** + * Playwright configs under top-level `examples/` and `x-pack/examples/` (developer example plugins). + * `module.name` for these paths is resolved from `plugin.id` in kibana.jsonc (see test_config.fromPath). + */ +export const SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX = new RegExp( + `^(examples|x-pack/examples)/([^/]+)/test/scout(?:_([^/]*))?/(${SCOUT_TEST_CATEGORIES.join( + '|' + )})/(\\w*)\\.?playwright\\.config\\.ts$` +); + // Scout CI export const SCOUT_CI_CONFIG_PATH = path.resolve(REPO_ROOT, '.buildkite', 'scout_ci_config.yml'); diff --git a/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.test.ts b/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.test.ts index 2514a42f13d16..d1e828d85b5b4 100644 --- a/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.test.ts +++ b/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.test.ts @@ -8,6 +8,7 @@ */ import { testConfig, testConfigs } from './test_config'; +import { readKibanaModuleManifest } from '../helpers/read_manifest'; import { REPO_ROOT } from '@kbn/repo-info'; import fs from 'node:fs'; import fg from 'fast-glob'; @@ -15,6 +16,9 @@ import path from 'node:path'; jest.mock('node:fs'); jest.mock('fast-glob'); +jest.mock('../helpers/read_manifest', () => ({ + readKibanaModuleManifest: jest.fn(), +})); const dummyManifestProps = { exists: false, @@ -22,9 +26,14 @@ const dummyManifestProps = { tests: [], }; +const mockReadKibanaModuleManifest = readKibanaModuleManifest as jest.MockedFunction< + typeof readKibanaModuleManifest +>; + describe('test_config module', () => { beforeEach(() => { jest.clearAllMocks(); + mockReadKibanaModuleManifest.mockReset(); }); describe('testConfig.fromPath', () => { @@ -211,6 +220,48 @@ describe('test_config module', () => { ); }); + it('parses examples/ developer plugin paths using plugin.id from kibana.jsonc', () => { + const moduleRoot = path.join('examples', 'hello_world'); + const scoutRoot = path.join(moduleRoot, 'test/scout_examples'); + const configPath = path.join(scoutRoot, '/api/playwright.config.ts'); + const manifestPath = path.join(scoutRoot, '/.meta/api/standard.json'); + + mockReadKibanaModuleManifest.mockReturnValue({ + id: 'helloWorld', + type: 'plugin', + group: 'platform', + visibility: 'private', + owner: [], + }); + + jest.spyOn(fs, 'existsSync').mockReturnValue(false); + + const config = testConfig.fromPath(configPath); + + expect(mockReadKibanaModuleManifest).toHaveBeenCalledWith( + path.join(REPO_ROOT, moduleRoot, 'kibana.jsonc') + ); + expect(config).toEqual({ + path: configPath, + category: 'api', + type: 'standard', + module: { + name: 'helloWorld', + group: 'platform', + type: 'plugin', + visibility: 'private', + root: moduleRoot, + }, + manifest: { + path: manifestPath, + ...dummyManifestProps, + }, + server: { + configSet: 'examples', + }, + }); + }); + it('throws if the manifest file is present but invalid', () => { const moduleName = 'moddy_mc_moduleface'; const moduleRoot = path.join('src/platform/plugins/shared', moduleName); diff --git a/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts b/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts index f0a269583a209..4730eaa68b2a9 100644 --- a/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts +++ b/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts @@ -11,8 +11,13 @@ import { globSync } from 'fast-glob'; import { REPO_ROOT } from '@kbn/repo-info'; import path from 'node:path'; import { ToolingLog } from '@kbn/tooling-log'; -import { SCOUT_CONFIG_PATH_GLOB, SCOUT_CONFIG_PATH_REGEX } from '@kbn/scout-info'; +import { + SCOUT_CONFIG_PATH_GLOB, + SCOUT_CONFIG_PATH_REGEX, + SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX, +} from '@kbn/scout-info'; import { existsSync, readFileSync } from 'node:fs'; +import { readKibanaModuleManifest } from '../helpers/read_manifest'; import type { ScoutTestableModule } from './testable_module'; import type { ScoutConfigManifest } from './manifest'; @@ -27,6 +32,29 @@ export interface ScoutTestConfig { }; } +const loadScoutManifestFile = ( + manifestPath: string +): { exists: boolean } & Pick => { + const absoluteManifestPath = path.join(REPO_ROOT, manifestPath); + const manifestExists = existsSync(absoluteManifestPath); + if (manifestExists) { + try { + return { + exists: true, + ...JSON.parse(readFileSync(absoluteManifestPath, 'utf8')), + }; + } catch (e) { + e.message = `Failed while trying to load manifest file at '${manifestPath}': ${e.message}`; + throw e; + } + } + return { + exists: false, + sha1: '000000000000000-000000000000000', + tests: [], + }; +}; + export const testConfig = { fromPath(configPath: string): ScoutTestConfig { // Make sure we're working with a path relative to the repo root @@ -41,6 +69,47 @@ export const testConfig = { ); } + const examplesMatch = configPath.match(SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX); + if (examplesMatch) { + const [, , , serverConfigSet, testCategory, testConfigType] = examplesMatch; + const moduleRoot = configPath.split('/test/scout')[0]; + const scoutDirName = `scout${serverConfigSet ? `_${serverConfigSet}` : ''}`; + const manifestPath = path.join( + moduleRoot, + 'test', + scoutDirName, + '.meta', + testCategory, + `${testConfigType || 'standard'}.json` + ); + const manifestFileData = loadScoutManifestFile(manifestPath); + const kibanaManifest = readKibanaModuleManifest( + path.join(REPO_ROOT, moduleRoot, 'kibana.jsonc') + ); + + return { + path: configPath, + category: testCategory, + type: testConfigType || 'standard', + module: { + name: kibanaManifest.id, + group: kibanaManifest.group, + type: kibanaManifest.type as ScoutTestableModule['type'], + visibility: kibanaManifest.visibility as ScoutTestableModule['visibility'], + root: moduleRoot, + }, + manifest: { + path: manifestPath, + exists: manifestFileData.exists, + sha1: manifestFileData.sha1, + tests: manifestFileData.tests, + }, + server: { + configSet: serverConfigSet || 'default', + }, + }; + } + const match = configPath.match(SCOUT_CONFIG_PATH_REGEX); if (match == null) { @@ -73,23 +142,7 @@ export const testConfig = { testCategory, `${testConfigType || 'standard'}.json` ); - const absoluteManifestPath = path.join(REPO_ROOT, manifestPath); - const manifestExists = existsSync(absoluteManifestPath); - let manifestFileData; - - if (manifestExists) { - try { - manifestFileData = JSON.parse(readFileSync(absoluteManifestPath, 'utf8')); - } catch (e) { - e.message = `Failed while trying to load manifest file at '${manifestPath}': ${e.message}`; - throw e; - } - } else { - manifestFileData = { - sha1: '000000000000000-000000000000000', - tests: [], - }; - } + const manifestFileData = loadScoutManifestFile(manifestPath); return { path: configPath, @@ -104,8 +157,9 @@ export const testConfig = { }, manifest: { path: manifestPath, - exists: manifestExists, - ...manifestFileData, + exists: manifestFileData.exists, + sha1: manifestFileData.sha1, + tests: manifestFileData.tests, }, server: { configSet: serverConfigSet || 'default', diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/examples/shared.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/examples/shared.ts new file mode 100644 index 0000000000000..aa1e40f230675 --- /dev/null +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/examples/shared.ts @@ -0,0 +1,21 @@ +/* + * 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 { resolve } from 'path'; +import { REPO_ROOT } from '@kbn/repo-info'; +import { findTestPluginPaths } from '@kbn/test'; + +/** + * Server args aligned with x-pack/platform/test/examples/config.ts so developer + * example plugins (and their cross-dependencies) load via --plugin-path. + */ +export const examplesServerArgs = [ + '--data.search.sessions.enabled=true', + ...findTestPluginPaths([resolve(REPO_ROOT, 'examples'), resolve(REPO_ROOT, 'x-pack/examples')]), +]; diff --git a/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/examples/stateful/classic.stateful.config.ts b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/examples/stateful/classic.stateful.config.ts new file mode 100644 index 0000000000000..3ab3486280712 --- /dev/null +++ b/src/platform/packages/shared/kbn-scout/src/servers/configs/config_sets/examples/stateful/classic.stateful.config.ts @@ -0,0 +1,20 @@ +/* + * 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 { ScoutServerConfig } from '../../../../../types'; +import { defaultConfig } from '../../default/stateful/base.config'; +import { examplesServerArgs } from '../shared'; + +export const servers: ScoutServerConfig = { + ...defaultConfig, + kbnTestServer: { + ...defaultConfig.kbnTestServer, + serverArgs: [...defaultConfig.kbnTestServer.serverArgs, ...examplesServerArgs], + }, +}; From 31eefd3ae0b1a09725517452c1f624f68b4da8fd Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Mon, 23 Mar 2026 17:48:28 +0100 Subject: [PATCH 2/5] update comment --- .../rules/scout_no_cross_boundary_imports.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kbn-eslint-plugin-eslint/rules/scout_no_cross_boundary_imports.js b/packages/kbn-eslint-plugin-eslint/rules/scout_no_cross_boundary_imports.js index e49bd68d646db..84996f5252395 100644 --- a/packages/kbn-eslint-plugin-eslint/rules/scout_no_cross_boundary_imports.js +++ b/packages/kbn-eslint-plugin-eslint/rules/scout_no_cross_boundary_imports.js @@ -41,7 +41,7 @@ const getAllowedPackage = (filePath) => { return { package: '@kbn/scout' }; } - // Developer example plugins (OSS and x-pack) + // example plugins (OSS and x-pack) if (/\/examples\/[^/]+\/test\/(?:scout|scout_\w+)\//.test(filePath)) { return { package: '@kbn/scout' }; } From 9cd4e0a637ab00c2d244c43a4a9e582caa67dd93 Mon Sep 17 00:00:00 2001 From: Dzmitry Lemechko Date: Thu, 26 Mar 2026 16:39:45 +0100 Subject: [PATCH 3/5] refactor discovery, combine regExp, add tests for paths --- examples/flyout_system/kibana.jsonc | 2 + .../private/kbn-scout-info/src/paths.test.ts | 381 ++++++++++++++++++ .../private/kbn-scout-info/src/paths.ts | 30 +- .../src/helpers/read_manifest.test.ts | 35 ++ .../src/helpers/read_manifest.ts | 5 +- .../src/registry/test_config.ts | 129 +++--- 6 files changed, 503 insertions(+), 79 deletions(-) create mode 100644 src/platform/packages/private/kbn-scout-info/src/paths.test.ts diff --git a/examples/flyout_system/kibana.jsonc b/examples/flyout_system/kibana.jsonc index f1a72aa3585b2..1b4e166e8385c 100644 --- a/examples/flyout_system/kibana.jsonc +++ b/examples/flyout_system/kibana.jsonc @@ -3,6 +3,8 @@ "id": "@kbn/flyout-system-example-plugin", "owner": "@elastic/appex-sharedux", "description": "Example app that shows use cases for the platform flyouts system", + "group": "platform", + "visibility": "private", "plugin": { "id": "flyoutSystemExamples", "server": false, diff --git a/src/platform/packages/private/kbn-scout-info/src/paths.test.ts b/src/platform/packages/private/kbn-scout-info/src/paths.test.ts new file mode 100644 index 0000000000000..b66c95a29e3ab --- /dev/null +++ b/src/platform/packages/private/kbn-scout-info/src/paths.test.ts @@ -0,0 +1,381 @@ +/* + * 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 mm from 'micromatch'; +import { + TESTABLE_COMPONENT_SCOUT_ROOT_PATH_GLOB, + SCOUT_CONFIG_PATH_GLOB, + SCOUT_CONFIG_PATH_REGEX, + SCOUT_CONFIG_MANIFEST_PATH_GLOB, + SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX, + SCOUT_UNIFIED_CONFIG_PATH_REGEX, + TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX, +} from './paths'; + +describe('Scout path globs', () => { + describe('TESTABLE_COMPONENT_SCOUT_ROOT_PATH_GLOB', () => { + const shouldMatch = [ + 'src/platform/plugins/shared/my_plugin/test/scout', + 'src/platform/plugins/private/my_plugin/test/scout', + 'src/platform/packages/shared/my_package/test/scout', + 'src/platform/packages/private/my_package/test/scout', + 'src/core/packages/my_package/test/scout', + 'x-pack/platform/plugins/shared/my_plugin/test/scout', + 'x-pack/solutions/security/plugins/my_plugin/test/scout', + 'x-pack/solutions/observability/plugins/my_plugin/test/scout', + 'src/platform/plugins/shared/my_plugin/test/scout_custom', + 'x-pack/solutions/security/plugins/my_plugin/test/scout_uiam_local', + 'examples/hello_world/test/scout', + 'examples/hello_world/test/scout_examples', + 'x-pack/examples/hello_world/test/scout', + 'x-pack/examples/hello_world/test/scout_examples', + ]; + + const shouldNotMatch = [ + 'random/path/test/scout', + 'src/platform/plugins/shared/my_plugin/src/scout', + 'not_examples/hello_world/test/scout', + ]; + + it.each(shouldMatch)('matches: %s', (testPath) => { + expect(mm.isMatch(testPath, TESTABLE_COMPONENT_SCOUT_ROOT_PATH_GLOB)).toBe(true); + }); + + it.each(shouldNotMatch)('does not match: %s', (testPath) => { + expect(mm.isMatch(testPath, TESTABLE_COMPONENT_SCOUT_ROOT_PATH_GLOB)).toBe(false); + }); + }); + + describe('SCOUT_CONFIG_PATH_GLOB', () => { + const shouldMatch = [ + 'src/platform/plugins/shared/my_plugin/test/scout/api/playwright.config.ts', + 'src/platform/plugins/shared/my_plugin/test/scout/ui/playwright.config.ts', + 'src/platform/plugins/shared/my_plugin/test/scout/api/parallel.playwright.config.ts', + 'x-pack/solutions/security/plugins/my_plugin/test/scout/api/playwright.config.ts', + 'x-pack/solutions/security/plugins/my_plugin/test/scout_custom/ui/playwright.config.ts', + 'src/core/packages/my_package/test/scout/api/playwright.config.ts', + 'examples/hello_world/test/scout_examples/api/playwright.config.ts', + 'examples/hello_world/test/scout_examples/ui/playwright.config.ts', + 'x-pack/examples/hello_world/test/scout_examples/api/playwright.config.ts', + 'x-pack/examples/hello_world/test/scout/ui/parallel.playwright.config.ts', + ]; + + const shouldNotMatch = [ + 'examples/hello_world/test/scout_examples/api/jest.config.ts', + 'random/path/test/scout/api/playwright.config.ts', + 'src/platform/plugins/shared/my_plugin/test/scout/api/not_playwright.config.ts', + ]; + + it.each(shouldMatch)('matches: %s', (testPath) => { + expect(mm.isMatch(testPath, SCOUT_CONFIG_PATH_GLOB)).toBe(true); + }); + + it.each(shouldNotMatch)('does not match: %s', (testPath) => { + expect(mm.isMatch(testPath, SCOUT_CONFIG_PATH_GLOB)).toBe(false); + }); + }); + + describe('SCOUT_CONFIG_MANIFEST_PATH_GLOB', () => { + const shouldMatch = [ + 'src/platform/plugins/shared/my_plugin/test/scout/.meta/api/standard.json', + 'src/platform/plugins/shared/my_plugin/test/scout/.meta/ui/parallel.json', + 'x-pack/solutions/security/plugins/my_plugin/test/scout/.meta/api/standard.json', + 'examples/hello_world/test/scout_examples/.meta/api/standard.json', + 'x-pack/examples/hello_world/test/scout_examples/.meta/ui/standard.json', + ]; + + const shouldNotMatch = [ + 'random/path/.meta/api/standard.json', + 'examples/hello_world/test/scout_examples/.meta/other/standard.json', + ]; + + it.each(shouldMatch)('matches: %s', (testPath) => { + expect(mm.isMatch(testPath, SCOUT_CONFIG_MANIFEST_PATH_GLOB)).toBe(true); + }); + + it.each(shouldNotMatch)('does not match: %s', (testPath) => { + expect(mm.isMatch(testPath, SCOUT_CONFIG_MANIFEST_PATH_GLOB)).toBe(false); + }); + }); +}); + +describe('Scout path regexes', () => { + describe('TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX', () => { + it('matches platform plugin paths with correct groups', () => { + const match = + 'src/platform/plugins/shared/my_plugin/test/scout'.match( + TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX + ); + expect(match).not.toBeNull(); + expect(match![1]).toBe('platform'); + expect(match![2]).toBeUndefined(); + expect(match![3]).toBe('plugins'); + expect(match![4]).toBe('shared'); + expect(match![5]).toBe('my_plugin'); + expect(match![6]).toBeUndefined(); + }); + + it('matches solution plugin paths with correct groups', () => { + const match = + 'x-pack/solutions/security/plugins/my_plugin/test/scout'.match( + TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX + ); + expect(match).not.toBeNull(); + expect(match![1]).toBeUndefined(); + expect(match![2]).toBe('security'); + expect(match![3]).toBe('plugins'); + }); + + it('captures custom config set names', () => { + const match = + 'src/platform/plugins/shared/my_plugin/test/scout_uiam_local'.match( + TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX + ); + expect(match).not.toBeNull(); + expect(match![6]).toBe('uiam_local'); + }); + + it('does not match examples/ paths', () => { + expect( + TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX.test('examples/hello_world/test/scout') + ).toBe(false); + }); + }); + + describe('SCOUT_CONFIG_PATH_REGEX', () => { + it('matches standard playwright config', () => { + const match = + 'src/platform/plugins/shared/my_plugin/test/scout/api/playwright.config.ts'.match( + SCOUT_CONFIG_PATH_REGEX + ); + expect(match).not.toBeNull(); + expect(match![7]).toBe('api'); + expect(match![8]).toBe(''); + }); + + it('matches named playwright config', () => { + const match = + 'src/platform/plugins/shared/my_plugin/test/scout/ui/parallel.playwright.config.ts'.match( + SCOUT_CONFIG_PATH_REGEX + ); + expect(match).not.toBeNull(); + expect(match![7]).toBe('ui'); + expect(match![8]).toBe('parallel'); + }); + + it('does not match examples/ paths', () => { + expect( + SCOUT_CONFIG_PATH_REGEX.test( + 'examples/hello_world/test/scout_examples/api/playwright.config.ts' + ) + ).toBe(false); + }); + }); + + describe('SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX', () => { + it('matches examples/ plugin config with custom config set', () => { + const match = + 'examples/hello_world/test/scout_examples/api/playwright.config.ts'.match( + SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX + ); + expect(match).not.toBeNull(); + expect(match![1]).toBe('examples'); + expect(match![2]).toBe('hello_world'); + expect(match![3]).toBe('examples'); + expect(match![4]).toBe('api'); + expect(match![5]).toBe(''); + }); + + it('matches x-pack/examples/ plugin config', () => { + const match = + 'x-pack/examples/hello_world/test/scout_examples/ui/playwright.config.ts'.match( + SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX + ); + expect(match).not.toBeNull(); + expect(match![1]).toBe('x-pack/examples'); + expect(match![2]).toBe('hello_world'); + expect(match![3]).toBe('examples'); + expect(match![4]).toBe('ui'); + }); + + it('matches examples/ with bare scout/ (no custom config set)', () => { + const match = 'examples/hello_world/test/scout/api/playwright.config.ts'.match( + SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX + ); + expect(match).not.toBeNull(); + expect(match![1]).toBe('examples'); + expect(match![3]).toBeUndefined(); + }); + + it('matches named playwright config under examples/', () => { + const match = + 'examples/hello_world/test/scout_examples/api/parallel.playwright.config.ts'.match( + SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX + ); + expect(match).not.toBeNull(); + expect(match![5]).toBe('parallel'); + }); + + it('does not match platform plugin paths', () => { + expect( + SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX.test( + 'src/platform/plugins/shared/my_plugin/test/scout/api/playwright.config.ts' + ) + ).toBe(false); + }); + }); + + describe('SCOUT_UNIFIED_CONFIG_PATH_REGEX', () => { + it('matches platform plugin path with correct named groups', () => { + const match = + 'src/platform/plugins/shared/my_plugin/test/scout/api/playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); + expect(match?.groups).toEqual( + expect.objectContaining({ + examplesRoot: undefined, + examplePlugin: undefined, + platformOrCore: 'platform', + solution: undefined, + moduleKind: 'plugins', + moduleVisibility: 'shared', + moduleName: 'my_plugin', + serverConfigSet: undefined, + testCategory: 'api', + testConfigType: '', + }) + ); + }); + + it('matches core package path with correct named groups', () => { + const match = + 'src/core/packages/my_package/test/scout/api/playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); + expect(match?.groups).toEqual( + expect.objectContaining({ + platformOrCore: 'core', + moduleKind: 'packages', + moduleName: 'my_package', + testCategory: 'api', + }) + ); + }); + + it('matches solution plugin path with correct named groups', () => { + const match = + 'x-pack/solutions/security/plugins/my_plugin/test/scout/ui/parallel.playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); + expect(match?.groups).toEqual( + expect.objectContaining({ + examplesRoot: undefined, + platformOrCore: undefined, + solution: 'security', + moduleKind: 'plugins', + moduleVisibility: '', + moduleName: 'my_plugin', + testCategory: 'ui', + testConfigType: 'parallel', + }) + ); + }); + + it('matches custom server config set', () => { + const match = + 'src/platform/plugins/shared/my_plugin/test/scout_uiam_local/api/playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); + expect(match?.groups).toEqual( + expect.objectContaining({ + serverConfigSet: 'uiam_local', + testCategory: 'api', + }) + ); + }); + + it('matches nested module names (e.g. vis_types/timelion)', () => { + const match = + 'src/platform/plugins/private/vis_types/timelion/test/scout/ui/playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); + expect(match?.groups).toEqual( + expect.objectContaining({ + moduleName: 'vis_types/timelion', + moduleVisibility: 'private', + }) + ); + }); + + it('matches examples/ path with correct named groups', () => { + const match = + 'examples/hello_world/test/scout_examples/api/playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); + expect(match?.groups).toEqual( + expect.objectContaining({ + examplesRoot: 'examples', + examplePlugin: 'hello_world', + platformOrCore: undefined, + solution: undefined, + moduleKind: undefined, + moduleName: undefined, + serverConfigSet: 'examples', + testCategory: 'api', + testConfigType: '', + }) + ); + }); + + it('matches x-pack/examples/ path with correct named groups', () => { + const match = + 'x-pack/examples/hello_world/test/scout_examples/ui/parallel.playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); + expect(match?.groups).toEqual( + expect.objectContaining({ + examplesRoot: 'x-pack/examples', + examplePlugin: 'hello_world', + serverConfigSet: 'examples', + testCategory: 'ui', + testConfigType: 'parallel', + }) + ); + }); + + it('matches examples/ with bare scout/ (no config set)', () => { + const match = + 'examples/hello_world/test/scout/api/playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); + expect(match?.groups).toEqual( + expect.objectContaining({ + examplesRoot: 'examples', + examplePlugin: 'hello_world', + serverConfigSet: undefined, + testCategory: 'api', + }) + ); + }); + + it('does not match random paths', () => { + expect( + SCOUT_UNIFIED_CONFIG_PATH_REGEX.test('random/path/test/scout/api/playwright.config.ts') + ).toBe(false); + }); + + it('does not match non-playwright configs', () => { + expect( + SCOUT_UNIFIED_CONFIG_PATH_REGEX.test( + 'src/platform/plugins/shared/my_plugin/test/scout/api/jest.config.ts' + ) + ).toBe(false); + }); + }); +}); diff --git a/src/platform/packages/private/kbn-scout-info/src/paths.ts b/src/platform/packages/private/kbn-scout-info/src/paths.ts index 6a275f0ff5fb0..8bd62fee52bef 100644 --- a/src/platform/packages/private/kbn-scout-info/src/paths.ts +++ b/src/platform/packages/private/kbn-scout-info/src/paths.ts @@ -32,8 +32,16 @@ export const SCOUT_PLAYWRIGHT_CONFIGS_PATH = path.resolve( 'scout_playwright_configs.json' ); -export const TESTABLE_COMPONENT_SCOUT_ROOT_PATH_GLOB = - '{src/platform,src/core,x-pack/**}/{plugins,packages}/**/test/scout{_*,},{examples,x-pack/examples}/**/test/scout{_*,}'; +export const PLATFORM_AND_SOLUTION_SCOUT_ROOT_PATH_GLOB = + '{src/platform,src/core,x-pack/**}/{plugins,packages}/**/test/scout{_*,}'; + +export const EXAMPLE_PLUGIN_SCOUT_ROOT_PATH_GLOB = + '{examples,x-pack/examples}/**/test/scout{_*,}'; + +export const TESTABLE_COMPONENT_SCOUT_ROOT_PATH_GLOB = `{${[ + PLATFORM_AND_SOLUTION_SCOUT_ROOT_PATH_GLOB, + EXAMPLE_PLUGIN_SCOUT_ROOT_PATH_GLOB, +].join(',')}}`; export const TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX = new RegExp( `(?:src|x-pack)` + @@ -69,5 +77,23 @@ export const SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX = new RegExp( )})/(\\w*)\\.?playwright\\.config\\.ts$` ); +/** + * Unified regex matching both platform/solution and example plugin Playwright config paths. + * Uses named capture groups so callers can branch on `examplesRoot` to decide how to + * resolve module metadata (kibana.jsonc vs directory-derived). + */ +export const SCOUT_UNIFIED_CONFIG_PATH_REGEX = new RegExp( + `^(?:` + + `(?examples|x-pack/examples)/(?[^/]+)` + + `|` + + `(?:src|x-pack)/(?:(?platform|core)|solutions/(?\\w+))` + + `/(?plugins|packages)/?(?shared|private|)` + + `/(?[\\w|-]+(?:\\/[\\w|-]+)*)` + + `)` + + `/test/scout(?:_(?[^/]*))?` + + `/(?${SCOUT_TEST_CATEGORIES.join('|')})` + + `/(?\\w*)\\.?playwright\\.config\\.ts$` +); + // Scout CI export const SCOUT_CI_CONFIG_PATH = path.resolve(REPO_ROOT, '.buildkite', 'scout_ci_config.yml'); diff --git a/src/platform/packages/private/kbn-scout-reporting/src/helpers/read_manifest.test.ts b/src/platform/packages/private/kbn-scout-reporting/src/helpers/read_manifest.test.ts index 3275f1e15ab69..edb78f0ab06eb 100644 --- a/src/platform/packages/private/kbn-scout-reporting/src/helpers/read_manifest.test.ts +++ b/src/platform/packages/private/kbn-scout-reporting/src/helpers/read_manifest.test.ts @@ -152,6 +152,41 @@ describe('read_manifest', () => { ); }); + it('should normalize a string owner to an array', () => { + const fileContent = ` + { + "id": "@kbn/example-plugin", + "type": "plugin", + "group": "platform", + "visibility": "private", + "owner": "@elastic/kibana-core", + "plugin": { "id": "examplePlugin" } + } + `; + existsSyncSpy.mockReturnValue(true); + readFileSyncSpy.mockReturnValue(fileContent); + + const result = readKibanaModuleManifest(pluginFilePath); + expect(result.owner).toEqual(['@elastic/kibana-core']); + }); + + it('should default owner to empty array when missing', () => { + const fileContent = ` + { + "id": "@kbn/example-plugin", + "type": "plugin", + "group": "platform", + "visibility": "private", + "plugin": { "id": "examplePlugin" } + } + `; + existsSyncSpy.mockReturnValue(true); + readFileSyncSpy.mockReturnValue(fileContent); + + const result = readKibanaModuleManifest(pluginFilePath); + expect(result.owner).toEqual([]); + }); + it('should throw an error for missing required fields', () => { const fileContent = `{ "group": "platform", diff --git a/src/platform/packages/private/kbn-scout-reporting/src/helpers/read_manifest.ts b/src/platform/packages/private/kbn-scout-reporting/src/helpers/read_manifest.ts index efa200fe6ae59..6ab6084318c9e 100644 --- a/src/platform/packages/private/kbn-scout-reporting/src/helpers/read_manifest.ts +++ b/src/platform/packages/private/kbn-scout-reporting/src/helpers/read_manifest.ts @@ -15,7 +15,7 @@ export interface KibanaJsoncMetadata { id: string; type: string; group: string; - owner: string[]; + owner: string | string[]; visibility: string; plugin?: { id: string }; } @@ -85,7 +85,8 @@ export const readKibanaModuleManifest = (filePath: string): KibanaModuleMetadata ); } - return { id, type, group, visibility, owner: owner || [] }; + const normalizedOwner = Array.isArray(owner) ? owner : owner ? [owner] : []; + return { id, type, group, visibility, owner: normalizedOwner }; }; /** diff --git a/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts b/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts index 4730eaa68b2a9..a675a2b6ebaeb 100644 --- a/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts +++ b/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts @@ -11,11 +11,7 @@ import { globSync } from 'fast-glob'; import { REPO_ROOT } from '@kbn/repo-info'; import path from 'node:path'; import { ToolingLog } from '@kbn/tooling-log'; -import { - SCOUT_CONFIG_PATH_GLOB, - SCOUT_CONFIG_PATH_REGEX, - SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX, -} from '@kbn/scout-info'; +import { SCOUT_CONFIG_PATH_GLOB, SCOUT_UNIFIED_CONFIG_PATH_REGEX } from '@kbn/scout-info'; import { existsSync, readFileSync } from 'node:fs'; import { readKibanaModuleManifest } from '../helpers/read_manifest'; import type { ScoutTestableModule } from './testable_module'; @@ -55,6 +51,53 @@ const loadScoutManifestFile = ( }; }; +const resolveModuleMetadata = ( + configPath: string, + moduleRoot: string +): { + module: ScoutTestableModule; + serverConfigSet: string | undefined; + testCategory: string; + testConfigType: string; +} => { + const match = configPath.match(SCOUT_UNIFIED_CONFIG_PATH_REGEX); + if (!match?.groups) { + throw new Error( + `Failed to create Scout config from path '${configPath}': ` + + 'path did not match the expected regex pattern' + ); + } + + const g = match.groups; + const module: ScoutTestableModule = g.examplesRoot + ? (() => { + const manifest = readKibanaModuleManifest( + path.join(REPO_ROOT, moduleRoot, 'kibana.jsonc') + ); + return { + name: manifest.id, + group: manifest.group, + type: manifest.type as ScoutTestableModule['type'], + visibility: manifest.visibility as ScoutTestableModule['visibility'], + root: moduleRoot, + }; + })() + : { + name: g.moduleName, + group: g.platformOrCore ?? g.solution, + type: g.moduleKind.slice(0, -1) as ScoutTestableModule['type'], + visibility: (g.moduleVisibility || 'private') as ScoutTestableModule['visibility'], + root: moduleRoot, + }; + + return { + module, + serverConfigSet: g.serverConfigSet, + testCategory: g.testCategory, + testConfigType: g.testConfigType, + }; +}; + export const testConfig = { fromPath(configPath: string): ScoutTestConfig { // Make sure we're working with a path relative to the repo root @@ -69,71 +112,13 @@ export const testConfig = { ); } - const examplesMatch = configPath.match(SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX); - if (examplesMatch) { - const [, , , serverConfigSet, testCategory, testConfigType] = examplesMatch; - const moduleRoot = configPath.split('/test/scout')[0]; - const scoutDirName = `scout${serverConfigSet ? `_${serverConfigSet}` : ''}`; - const manifestPath = path.join( - moduleRoot, - 'test', - scoutDirName, - '.meta', - testCategory, - `${testConfigType || 'standard'}.json` - ); - const manifestFileData = loadScoutManifestFile(manifestPath); - const kibanaManifest = readKibanaModuleManifest( - path.join(REPO_ROOT, moduleRoot, 'kibana.jsonc') - ); - - return { - path: configPath, - category: testCategory, - type: testConfigType || 'standard', - module: { - name: kibanaManifest.id, - group: kibanaManifest.group, - type: kibanaManifest.type as ScoutTestableModule['type'], - visibility: kibanaManifest.visibility as ScoutTestableModule['visibility'], - root: moduleRoot, - }, - manifest: { - path: manifestPath, - exists: manifestFileData.exists, - sha1: manifestFileData.sha1, - tests: manifestFileData.tests, - }, - server: { - configSet: serverConfigSet || 'default', - }, - }; - } - - const match = configPath.match(SCOUT_CONFIG_PATH_REGEX); - - if (match == null) { - throw new Error( - `Failed to create Scout config from path '${configPath}': ` + - 'path did not match the expected regex pattern' - ); - } - - const [ - _, - platform, - solution, - moduleType, - moduleVisibility, - moduleName, - serverConfigSet, - testCategory, - testConfigType, - ] = match; - - const scoutDirName = `scout${serverConfigSet ? `_${serverConfigSet}` : ''}`; const moduleRoot = configPath.split('/test/scout')[0]; + const { module, serverConfigSet, testCategory, testConfigType } = resolveModuleMetadata( + configPath, + moduleRoot + ); + const scoutDirName = `scout${serverConfigSet ? `_${serverConfigSet}` : ''}`; const manifestPath = path.join( moduleRoot, 'test', @@ -148,13 +133,7 @@ export const testConfig = { path: configPath, category: testCategory, type: testConfigType || 'standard', - module: { - name: moduleName, - group: platform ?? solution, - type: moduleType.slice(0, -1) as ScoutTestableModule['type'], - visibility: (moduleVisibility || 'private') as ScoutTestableModule['visibility'], - root: moduleRoot, - }, + module, manifest: { path: manifestPath, exists: manifestFileData.exists, From 9f251e00286212f77d0a4b77d6069fcc17dec375 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:06:09 +0000 Subject: [PATCH 4/5] Changes from node scripts/regenerate_moon_projects.js --update --- examples/flyout_system/moon.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/flyout_system/moon.yml b/examples/flyout_system/moon.yml index 0b587b4fd877d..ad84b4416ec6e 100644 --- a/examples/flyout_system/moon.yml +++ b/examples/flyout_system/moon.yml @@ -26,7 +26,8 @@ dependsOn: tags: - plugin - prod - - group-undefined + - group-platform + - private fileGroups: src: - index.ts From a8acd3dcf884c0355cfbda1a2cda99aa91a47f6d Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Thu, 26 Mar 2026 16:33:15 +0000 Subject: [PATCH 5/5] Changes from node scripts/eslint_all_files --no-cache --fix --- .../private/kbn-scout-info/src/paths.test.ts | 62 ++++++++----------- .../private/kbn-scout-info/src/paths.ts | 11 ++-- .../src/registry/test_config.ts | 4 +- 3 files changed, 33 insertions(+), 44 deletions(-) diff --git a/src/platform/packages/private/kbn-scout-info/src/paths.test.ts b/src/platform/packages/private/kbn-scout-info/src/paths.test.ts index b66c95a29e3ab..a1a9f46214921 100644 --- a/src/platform/packages/private/kbn-scout-info/src/paths.test.ts +++ b/src/platform/packages/private/kbn-scout-info/src/paths.test.ts @@ -108,10 +108,9 @@ describe('Scout path globs', () => { describe('Scout path regexes', () => { describe('TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX', () => { it('matches platform plugin paths with correct groups', () => { - const match = - 'src/platform/plugins/shared/my_plugin/test/scout'.match( - TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX - ); + const match = 'src/platform/plugins/shared/my_plugin/test/scout'.match( + TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX + ); expect(match).not.toBeNull(); expect(match![1]).toBe('platform'); expect(match![2]).toBeUndefined(); @@ -122,10 +121,9 @@ describe('Scout path regexes', () => { }); it('matches solution plugin paths with correct groups', () => { - const match = - 'x-pack/solutions/security/plugins/my_plugin/test/scout'.match( - TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX - ); + const match = 'x-pack/solutions/security/plugins/my_plugin/test/scout'.match( + TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX + ); expect(match).not.toBeNull(); expect(match![1]).toBeUndefined(); expect(match![2]).toBe('security'); @@ -133,18 +131,17 @@ describe('Scout path regexes', () => { }); it('captures custom config set names', () => { - const match = - 'src/platform/plugins/shared/my_plugin/test/scout_uiam_local'.match( - TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX - ); + const match = 'src/platform/plugins/shared/my_plugin/test/scout_uiam_local'.match( + TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX + ); expect(match).not.toBeNull(); expect(match![6]).toBe('uiam_local'); }); it('does not match examples/ paths', () => { - expect( - TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX.test('examples/hello_world/test/scout') - ).toBe(false); + expect(TESTABLE_COMPONENT_SCOUT_ROOT_PATH_REGEX.test('examples/hello_world/test/scout')).toBe( + false + ); }); }); @@ -180,10 +177,9 @@ describe('Scout path regexes', () => { describe('SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX', () => { it('matches examples/ plugin config with custom config set', () => { - const match = - 'examples/hello_world/test/scout_examples/api/playwright.config.ts'.match( - SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX - ); + const match = 'examples/hello_world/test/scout_examples/api/playwright.config.ts'.match( + SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX + ); expect(match).not.toBeNull(); expect(match![1]).toBe('examples'); expect(match![2]).toBe('hello_world'); @@ -193,10 +189,9 @@ describe('Scout path regexes', () => { }); it('matches x-pack/examples/ plugin config', () => { - const match = - 'x-pack/examples/hello_world/test/scout_examples/ui/playwright.config.ts'.match( - SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX - ); + const match = 'x-pack/examples/hello_world/test/scout_examples/ui/playwright.config.ts'.match( + SCOUT_EXAMPLES_PLAYWRIGHT_CONFIG_REGEX + ); expect(match).not.toBeNull(); expect(match![1]).toBe('x-pack/examples'); expect(match![2]).toBe('hello_world'); @@ -254,10 +249,9 @@ describe('Scout path regexes', () => { }); it('matches core package path with correct named groups', () => { - const match = - 'src/core/packages/my_package/test/scout/api/playwright.config.ts'.match( - SCOUT_UNIFIED_CONFIG_PATH_REGEX - ); + const match = 'src/core/packages/my_package/test/scout/api/playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); expect(match?.groups).toEqual( expect.objectContaining({ platformOrCore: 'core', @@ -314,10 +308,9 @@ describe('Scout path regexes', () => { }); it('matches examples/ path with correct named groups', () => { - const match = - 'examples/hello_world/test/scout_examples/api/playwright.config.ts'.match( - SCOUT_UNIFIED_CONFIG_PATH_REGEX - ); + const match = 'examples/hello_world/test/scout_examples/api/playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); expect(match?.groups).toEqual( expect.objectContaining({ examplesRoot: 'examples', @@ -350,10 +343,9 @@ describe('Scout path regexes', () => { }); it('matches examples/ with bare scout/ (no config set)', () => { - const match = - 'examples/hello_world/test/scout/api/playwright.config.ts'.match( - SCOUT_UNIFIED_CONFIG_PATH_REGEX - ); + const match = 'examples/hello_world/test/scout/api/playwright.config.ts'.match( + SCOUT_UNIFIED_CONFIG_PATH_REGEX + ); expect(match?.groups).toEqual( expect.objectContaining({ examplesRoot: 'examples', diff --git a/src/platform/packages/private/kbn-scout-info/src/paths.ts b/src/platform/packages/private/kbn-scout-info/src/paths.ts index 8bd62fee52bef..8fc5600a4b47b 100644 --- a/src/platform/packages/private/kbn-scout-info/src/paths.ts +++ b/src/platform/packages/private/kbn-scout-info/src/paths.ts @@ -35,8 +35,7 @@ export const SCOUT_PLAYWRIGHT_CONFIGS_PATH = path.resolve( export const PLATFORM_AND_SOLUTION_SCOUT_ROOT_PATH_GLOB = '{src/platform,src/core,x-pack/**}/{plugins,packages}/**/test/scout{_*,}'; -export const EXAMPLE_PLUGIN_SCOUT_ROOT_PATH_GLOB = - '{examples,x-pack/examples}/**/test/scout{_*,}'; +export const EXAMPLE_PLUGIN_SCOUT_ROOT_PATH_GLOB = '{examples,x-pack/examples}/**/test/scout{_*,}'; export const TESTABLE_COMPONENT_SCOUT_ROOT_PATH_GLOB = `{${[ PLATFORM_AND_SOLUTION_SCOUT_ROOT_PATH_GLOB, @@ -89,10 +88,10 @@ export const SCOUT_UNIFIED_CONFIG_PATH_REGEX = new RegExp( `(?:src|x-pack)/(?:(?platform|core)|solutions/(?\\w+))` + `/(?plugins|packages)/?(?shared|private|)` + `/(?[\\w|-]+(?:\\/[\\w|-]+)*)` + - `)` + - `/test/scout(?:_(?[^/]*))?` + - `/(?${SCOUT_TEST_CATEGORIES.join('|')})` + - `/(?\\w*)\\.?playwright\\.config\\.ts$` + `)` + + `/test/scout(?:_(?[^/]*))?` + + `/(?${SCOUT_TEST_CATEGORIES.join('|')})` + + `/(?\\w*)\\.?playwright\\.config\\.ts$` ); // Scout CI diff --git a/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts b/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts index a675a2b6ebaeb..451ed8b5bed3e 100644 --- a/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts +++ b/src/platform/packages/private/kbn-scout-reporting/src/registry/test_config.ts @@ -71,9 +71,7 @@ const resolveModuleMetadata = ( const g = match.groups; const module: ScoutTestableModule = g.examplesRoot ? (() => { - const manifest = readKibanaModuleManifest( - path.join(REPO_ROOT, moduleRoot, 'kibana.jsonc') - ); + const manifest = readKibanaModuleManifest(path.join(REPO_ROOT, moduleRoot, 'kibana.jsonc')); return { name: manifest.id, group: manifest.group,