Skip to content

Commit

Permalink
[scout] fix playwright configs discovery script and add save flag f…
Browse files Browse the repository at this point in the history
…or CI (#213147)

## Summary

This PR fixes the search logic to look for playwright configs in all
possible & expected locations (`src/*` was not working), matching one of
3 regexp:
```
      /(x-pack\/platform\/plugins\/(?:private|shared|[^\/]+)\/([^\/]+))\/ui_tests\//,
      /(x-pack\/solutions\/[^\/]+\/plugins\/([^\/]+))\/ui_tests\//,
      /(src\/platform\/plugins\/(?:private|shared)?\/?([^\/]+))\/ui_tests\//,
```

For each plugin we also have `usesParallelWorkers` prop (`true` if at
least 1 config runs with concurrent workers) to decide later, if we need
worker with 4 or 8 VCPUs.

The idea is to run `node scripts/scout discover-playwright-configs
--save` on CI and use generated json as source to build test run
pipeline.

Current output:

```
{
  "discover_enhanced": {
    "group": "platform",
    "pluginPath": "x-pack/platform/plugins/private/discover_enhanced",
    "configs": [
      "x-pack/platform/plugins/private/discover_enhanced/ui_tests/parallel.playwright.config.ts",
      "x-pack/platform/plugins/private/discover_enhanced/ui_tests/playwright.config.ts"
    ],
    "usesParallelWorkers": true
  },
  "maps": {
    "group": "platform",
    "pluginPath": "x-pack/platform/plugins/shared/maps",
    "configs": [
      "x-pack/platform/plugins/shared/maps/ui_tests/playwright.config.ts"
    ],
    "usesParallelWorkers": false
  },
  "observability_onboarding": {
    "group": "observability",
    "pluginPath": "x-pack/solutions/observability/plugins/observability_onboarding",
    "configs": [
      "x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/parallel.playwright.config.ts",
      "x-pack/solutions/observability/plugins/observability_onboarding/ui_tests/playwright.config.ts"
    ],
    "usesParallelWorkers": true
  }
}
```

(cherry picked from commit 1e3bb05)
  • Loading branch information
dmlemeshko committed Mar 5, 2025
1 parent 4fbbea9 commit dc3ff03
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 34 deletions.
31 changes: 23 additions & 8 deletions src/platform/packages/shared/kbn-scout/src/cli/config_discovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import fs from 'fs';
import { Command } from '@kbn/dev-cli-runner';
import { SCOUT_OUTPUT_ROOT } from '@kbn/scout-info';
import { resolve } from 'path';
import { getScoutPlaywrightConfigs, DEFAULT_TEST_PATH_PATTERNS } from '../config';
import { measurePerformance } from '../common';

Expand All @@ -21,29 +24,41 @@ export const discoverPlaywrightConfigs: Command<void> = {
Common usage:
node scripts/scout discover-playwright-configs --searchPaths <search_paths>
node scripts/scout discover-playwright-configs --save
node scripts/scout discover-playwright-configs
`,
flags: {
string: ['searchPaths'],
default: { searchPaths: DEFAULT_TEST_PATH_PATTERNS },
boolean: ['save'],
default: { searchPaths: DEFAULT_TEST_PATH_PATTERNS, save: false },
},
run: ({ flagsReader, log }) => {
const searchPaths = flagsReader.arrayOfStrings('searchPaths')!;

const plugins = measurePerformance(log, 'Discovering playwright config files', () => {
const pluginsMap = measurePerformance(log, 'Discovering playwright config files', () => {
return getScoutPlaywrightConfigs(searchPaths, log);
});

const finalMessage =
plugins.size === 0
? 'No playwright config files found'
: `Found playwright config files in '${plugins.size}' plugins`;
pluginsMap.size === 0
? 'No Playwright config files found'
: `Found Playwright config files in '${pluginsMap.size}' plugins`;

if (pluginsMap.size > 0 && flagsReader.boolean('save')) {
const scoutConfigsFilePath = resolve(SCOUT_OUTPUT_ROOT, 'scout_playwright_configs.json');
fs.writeFileSync(
scoutConfigsFilePath,
JSON.stringify(Object.fromEntries(pluginsMap), null, 2)
);
log.info(`${finalMessage}. Saved to '${scoutConfigsFilePath}'`);
return;
}

log.info(finalMessage);

plugins.forEach((files, plugin) => {
log.info(`[${plugin}] plugin:`);
files.forEach((file) => {
pluginsMap.forEach((data, plugin) => {
log.info(`${data.group} / [${plugin}] plugin:`);
data.configs.map((file) => {
log.info(`- ${file}`);
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,36 @@ describe('getScoutPlaywrightConfigs', () => {

it('should correctly extract plugin names and group config files', () => {
(fastGlob.sync as jest.Mock).mockReturnValue([
'x-pack/platform/plugins/plugin_a/ui_tests/playwright.config.ts',
'x-pack/platform/plugins/plugin_a/ui_tests/parallel.playwright.config.ts',
'x-pack/platform/plugins/private/plugin_a/ui_tests/playwright.config.ts',
'x-pack/platform/plugins/private/plugin_a/ui_tests/parallel.playwright.config.ts',
'x-pack/solutions/security/plugins/plugin_b/ui_tests/playwright.config.ts',
'src/platform/plugins/shared/plugin_c/ui_tests/playwright.config.ts',
]);

const plugins = getScoutPlaywrightConfigs(['x-pack/'], mockLog);
const plugins = getScoutPlaywrightConfigs(['x-pack/', 'src/'], mockLog);

expect(plugins.size).toBe(2);
expect(plugins.get('plugin_a')).toEqual([
'x-pack/platform/plugins/plugin_a/ui_tests/playwright.config.ts',
'x-pack/platform/plugins/plugin_a/ui_tests/parallel.playwright.config.ts',
]);
expect(plugins.get('plugin_b')).toEqual([
'x-pack/solutions/security/plugins/plugin_b/ui_tests/playwright.config.ts',
]);
expect(plugins.size).toBe(3);
expect(plugins.get('plugin_a')).toEqual({
configs: [
'x-pack/platform/plugins/private/plugin_a/ui_tests/playwright.config.ts',
'x-pack/platform/plugins/private/plugin_a/ui_tests/parallel.playwright.config.ts',
],
usesParallelWorkers: true,
group: 'platform',
pluginPath: 'x-pack/platform/plugins/private/plugin_a',
});
expect(plugins.get('plugin_b')).toEqual({
configs: ['x-pack/solutions/security/plugins/plugin_b/ui_tests/playwright.config.ts'],
usesParallelWorkers: false,
group: 'security',
pluginPath: 'x-pack/solutions/security/plugins/plugin_b',
});
expect(plugins.get('plugin_c')).toEqual({
configs: ['src/platform/plugins/shared/plugin_c/ui_tests/playwright.config.ts'],
usesParallelWorkers: false,
group: 'platform',
pluginPath: 'src/platform/plugins/shared/plugin_c',
});
});

it('should log a warning if a file path does not match the expected pattern', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,78 @@ import { ToolingLog } from '@kbn/tooling-log';

export const DEFAULT_TEST_PATH_PATTERNS = ['src/platform/plugins', 'x-pack/**/plugins'];

interface PluginScoutConfig {
group: string;
pluginPath: string;
usesParallelWorkers: boolean;
configs: string[];
}

export const getScoutPlaywrightConfigs = (searchPaths: string[], log: ToolingLog) => {
const patterns = searchPaths.map((basePath) =>
path.join(basePath, '**/ui_tests/{playwright.config.ts,parallel.playwright.config.ts}')
);

log.info('Searching for playwright config files in the following paths:');
log.info('Searching for Playwright config files in the following paths:');
patterns.forEach((pattern) => log.info(`- ${pattern}`));
log.info(''); // Add a newline for better readability

const files = patterns.flatMap((pattern) => fastGlob.sync(pattern, { onlyFiles: true }));

// Group config files by plugin
const plugins = files.reduce((acc: Map<string, string[]>, filePath: string) => {
const match = filePath.match(
/(?:src\/platform\/plugins|x-pack\/.*?\/plugins)\/(?:.*?\/)?([^\/]+)\/ui_tests\//
);
const pluginName = match ? match[1] : null;
const typeMappings: Record<string, string> = {
'x-pack/solutions/security': 'security',
'x-pack/solutions/search': 'search',
'x-pack/solutions/observability': 'observability',
'x-pack/platform/plugins': 'platform',
'src/platform/plugins': 'platform',
};

const matchPluginPath = (filePath: string): { pluginPath: string; pluginName: string } | null => {
const regexes = [
/(x-pack\/platform\/plugins\/(?:private|shared|[^\/]+)\/([^\/]+))\/ui_tests\//,
/(x-pack\/solutions\/[^\/]+\/plugins\/([^\/]+))\/ui_tests\//,
/(src\/platform\/plugins\/(?:private|shared)?\/?([^\/]+))\/ui_tests\//,
];

if (pluginName) {
if (!acc.has(pluginName)) {
acc.set(pluginName, []);
for (const regex of regexes) {
const match = filePath.match(regex);
if (match) {
return { pluginPath: match[1], pluginName: match[2] };
}
acc.get(pluginName)!.push(filePath);
} else {
}
return null;
};

const pluginsWithConfigs = new Map<string, PluginScoutConfig>();

files.forEach((filePath) => {
const matchResult = matchPluginPath(filePath);
if (!matchResult) {
log.warning(`Unable to extract plugin name from path: ${filePath}`);
return;
}

return acc;
}, new Map<string, string[]>());
const { pluginPath, pluginName } = matchResult;
const group =
Object.entries(typeMappings).find(([key]) => filePath.includes(key))?.[1] || 'unknown';

if (!pluginsWithConfigs.has(pluginName)) {
pluginsWithConfigs.set(pluginName, {
group,
pluginPath,
configs: [],
usesParallelWorkers: false,
});
}

const pluginData = pluginsWithConfigs.get(pluginName)!;
if (!pluginData.configs.includes(filePath)) {
pluginData.configs.push(filePath);
if (filePath.endsWith('parallel.playwright.config.ts')) {
pluginData.usesParallelWorkers = true;
}
}
});

return plugins;
return pluginsWithConfigs;
};

0 comments on commit dc3ff03

Please sign in to comment.