mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
# Backport This will backport the following commits from `main` to `8.x`: - [[scout] add script to discover playwright configs in repo (#208733)](https://github.com/elastic/kibana/pull/208733) <!--- Backport version: 9.4.3 --> ### Questions ? Please refer to the [Backport tool documentation](https://github.com/sqren/backport) <!--BACKPORT [{"author":{"name":"Dzmitry Lemechko","email":"dzmitry.lemechko@elastic.co"},"sourceCommit":{"committedDate":"2025-01-30T08:42:15Z","message":"[scout] add script to discover playwright configs in repo (#208733)\n\n## Summary\r\n\r\nAdding script to discover Scout playwright tests in Kibana repo, will be\r\nused to build CI pipeline step (running tests per plugin in a separate\r\nworker for the start). We can also consider using it to decide if code\r\nchange should trigger only specific plugin tests to run.\r\n\r\nUsage:\r\n```\r\nnode scripts/scout.js discover-playwright-configs --searchPaths x-pack/platform/plugins/private/discover_enhanced\r\n```\r\n\r\nOutput:\r\n```\r\n info Searching for playwright config files in the following paths:\r\n info - x-pack/platform/plugins/private/discover_enhanced/**/ui_tests/{playwright.config.ts,parallel.playwright.config.ts}\r\n info\r\n info Discovered playwright config files in '1' plugins\r\n info [discover_enhanced] plugin:\r\n info - x-pack/platform/plugins/private/discover_enhanced/ui_tests/parallel.playwright.config.ts\r\n info - x-pack/platform/plugins/private/discover_enhanced/ui_tests/playwright.config.ts\r\n```\r\n\r\nMore usage examples:\r\n```\r\nnode scripts/scout.js discover-playwright-configs // by default will search in ['src/platform/plugins', 'x-pack/**/plugins'] and return all existing ones\r\nnode scripts/scout.js discover-playwright-configs --searchPaths x-pack/platform // platform ones under x-pack\r\nnode scripts/scout.js discover-playwright-configs --searchPaths x-pack/** // all under x-pack\r\n```","sha":"cf7debdfa3fb4a253ac0ec1b43e588e5163f8a98","branchLabelMapping":{"^v9.0.0$":"main","^v8.18.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","v9.0.0","backport:prev-minor","test:scout"],"title":"[scout] add script to discover playwright configs in repo","number":208733,"url":"https://github.com/elastic/kibana/pull/208733","mergeCommit":{"message":"[scout] add script to discover playwright configs in repo (#208733)\n\n## Summary\r\n\r\nAdding script to discover Scout playwright tests in Kibana repo, will be\r\nused to build CI pipeline step (running tests per plugin in a separate\r\nworker for the start). We can also consider using it to decide if code\r\nchange should trigger only specific plugin tests to run.\r\n\r\nUsage:\r\n```\r\nnode scripts/scout.js discover-playwright-configs --searchPaths x-pack/platform/plugins/private/discover_enhanced\r\n```\r\n\r\nOutput:\r\n```\r\n info Searching for playwright config files in the following paths:\r\n info - x-pack/platform/plugins/private/discover_enhanced/**/ui_tests/{playwright.config.ts,parallel.playwright.config.ts}\r\n info\r\n info Discovered playwright config files in '1' plugins\r\n info [discover_enhanced] plugin:\r\n info - x-pack/platform/plugins/private/discover_enhanced/ui_tests/parallel.playwright.config.ts\r\n info - x-pack/platform/plugins/private/discover_enhanced/ui_tests/playwright.config.ts\r\n```\r\n\r\nMore usage examples:\r\n```\r\nnode scripts/scout.js discover-playwright-configs // by default will search in ['src/platform/plugins', 'x-pack/**/plugins'] and return all existing ones\r\nnode scripts/scout.js discover-playwright-configs --searchPaths x-pack/platform // platform ones under x-pack\r\nnode scripts/scout.js discover-playwright-configs --searchPaths x-pack/** // all under x-pack\r\n```","sha":"cf7debdfa3fb4a253ac0ec1b43e588e5163f8a98"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/208733","number":208733,"mergeCommit":{"message":"[scout] add script to discover playwright configs in repo (#208733)\n\n## Summary\r\n\r\nAdding script to discover Scout playwright tests in Kibana repo, will be\r\nused to build CI pipeline step (running tests per plugin in a separate\r\nworker for the start). We can also consider using it to decide if code\r\nchange should trigger only specific plugin tests to run.\r\n\r\nUsage:\r\n```\r\nnode scripts/scout.js discover-playwright-configs --searchPaths x-pack/platform/plugins/private/discover_enhanced\r\n```\r\n\r\nOutput:\r\n```\r\n info Searching for playwright config files in the following paths:\r\n info - x-pack/platform/plugins/private/discover_enhanced/**/ui_tests/{playwright.config.ts,parallel.playwright.config.ts}\r\n info\r\n info Discovered playwright config files in '1' plugins\r\n info [discover_enhanced] plugin:\r\n info - x-pack/platform/plugins/private/discover_enhanced/ui_tests/parallel.playwright.config.ts\r\n info - x-pack/platform/plugins/private/discover_enhanced/ui_tests/playwright.config.ts\r\n```\r\n\r\nMore usage examples:\r\n```\r\nnode scripts/scout.js discover-playwright-configs // by default will search in ['src/platform/plugins', 'x-pack/**/plugins'] and return all existing ones\r\nnode scripts/scout.js discover-playwright-configs --searchPaths x-pack/platform // platform ones under x-pack\r\nnode scripts/scout.js discover-playwright-configs --searchPaths x-pack/** // all under x-pack\r\n```","sha":"cf7debdfa3fb4a253ac0ec1b43e588e5163f8a98"}}]}] BACKPORT--> Co-authored-by: Dzmitry Lemechko <dzmitry.lemechko@elastic.co>
This commit is contained in:
parent
1cf806f83a
commit
019de9a8a3
10 changed files with 242 additions and 43 deletions
51
packages/kbn-scout/src/cli/config_discovery.ts
Normal file
51
packages/kbn-scout/src/cli/config_discovery.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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 { Command } from '@kbn/dev-cli-runner';
|
||||
import { getScoutPlaywrightConfigs, DEFAULT_TEST_PATH_PATTERNS } from '../config';
|
||||
import { measurePerformance } from '../common';
|
||||
|
||||
/**
|
||||
* Discover Playwright configuration files with Scout tests
|
||||
*/
|
||||
export const discoverPlaywrightConfigs: Command<void> = {
|
||||
name: 'discover-playwright-configs',
|
||||
description: `
|
||||
Discover Playwright configuration files with Scout tests.
|
||||
|
||||
Common usage:
|
||||
node scripts/scout discover-playwright-configs --searchPaths <search_paths>
|
||||
node scripts/scout discover-playwright-configs
|
||||
`,
|
||||
flags: {
|
||||
string: ['searchPaths'],
|
||||
default: { searchPaths: DEFAULT_TEST_PATH_PATTERNS },
|
||||
},
|
||||
run: ({ flagsReader, log }) => {
|
||||
const searchPaths = flagsReader.arrayOfStrings('searchPaths')!;
|
||||
|
||||
const plugins = 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`;
|
||||
|
||||
log.info(finalMessage);
|
||||
|
||||
plugins.forEach((files, plugin) => {
|
||||
log.info(`[${plugin}] plugin:`);
|
||||
files.forEach((file) => {
|
||||
log.info(`- ${file}`);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
|
@ -10,12 +10,19 @@ import { RunWithCommands } from '@kbn/dev-cli-runner';
|
|||
import { cli as reportingCLI } from '@kbn/scout-reporting';
|
||||
import { startServer } from './start_server';
|
||||
import { runTests } from './run_tests';
|
||||
import { discoverPlaywrightConfigs } from './config_discovery';
|
||||
|
||||
export async function run() {
|
||||
await new RunWithCommands(
|
||||
{
|
||||
description: 'Scout CLI',
|
||||
},
|
||||
[startServer, runTests, reportingCLI.initializeReportDatastream, reportingCLI.uploadEvents]
|
||||
[
|
||||
startServer,
|
||||
runTests,
|
||||
discoverPlaywrightConfigs,
|
||||
reportingCLI.initializeReportDatastream,
|
||||
reportingCLI.uploadEvents,
|
||||
]
|
||||
).execute();
|
||||
}
|
||||
|
|
|
@ -18,3 +18,33 @@ export async function silence(log: ToolingLog, milliseconds: number) {
|
|||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Measure the performance of a sync function
|
||||
*/
|
||||
export const measurePerformance = <T>(log: ToolingLog, label: string, fn: () => T): T => {
|
||||
const startTime = performance.now();
|
||||
const result = fn();
|
||||
|
||||
const duration = performance.now() - startTime;
|
||||
log.debug(`${label} took ${duration.toFixed(2)}ms`);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Measure the performance of an async function
|
||||
*/
|
||||
export const measurePerformanceAsync = 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;
|
||||
};
|
||||
|
|
|
@ -7,16 +7,4 @@
|
|||
* License v3.0 only", or the "Server Side Public License, v 1".
|
||||
*/
|
||||
|
||||
import { ScoutLogger } from '../fixtures/worker';
|
||||
|
||||
export const measurePerformance = async <T>(
|
||||
log: ScoutLogger,
|
||||
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;
|
||||
};
|
||||
export * from './search_configs';
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* 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 '@kbn/tooling-log';
|
||||
import fastGlob from 'fast-glob';
|
||||
import { getScoutPlaywrightConfigs } from './search_configs';
|
||||
|
||||
jest.mock('fast-glob');
|
||||
|
||||
describe('getScoutPlaywrightConfigs', () => {
|
||||
let mockLog: ToolingLog;
|
||||
|
||||
beforeEach(() => {
|
||||
mockLog = new ToolingLog({ level: 'verbose', writeTo: process.stdout });
|
||||
jest.spyOn(mockLog, 'info').mockImplementation(() => {});
|
||||
jest.spyOn(mockLog, 'warning').mockImplementation(() => {});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('should return an empty map if no matching files are found', () => {
|
||||
(fastGlob.sync as jest.Mock).mockReturnValueOnce([]);
|
||||
|
||||
const plugins = getScoutPlaywrightConfigs(['x-pack/plugins'], mockLog);
|
||||
expect(plugins.size).toBe(0);
|
||||
});
|
||||
|
||||
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/solutions/security/plugins/plugin_b/ui_tests/playwright.config.ts',
|
||||
]);
|
||||
|
||||
const plugins = getScoutPlaywrightConfigs(['x-pack/'], 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',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should log a warning if a file path does not match the expected pattern', () => {
|
||||
(fastGlob.sync as jest.Mock).mockReturnValue([
|
||||
'x-pack/security/plugins/unknown-path/playwright.config.ts',
|
||||
]);
|
||||
|
||||
const plugins = getScoutPlaywrightConfigs(['x-pack/security'], mockLog);
|
||||
|
||||
expect(plugins.size).toBe(0);
|
||||
expect(mockLog.warning).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Unable to extract plugin name from path')
|
||||
);
|
||||
});
|
||||
});
|
47
packages/kbn-scout/src/config/discovery/search_configs.ts
Normal file
47
packages/kbn-scout/src/config/discovery/search_configs.ts
Normal file
|
@ -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", 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 fastGlob from 'fast-glob';
|
||||
import path from 'path';
|
||||
import { ToolingLog } from '@kbn/tooling-log';
|
||||
|
||||
export const DEFAULT_TEST_PATH_PATTERNS = ['src/platform/plugins', 'x-pack/**/plugins'];
|
||||
|
||||
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:');
|
||||
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;
|
||||
|
||||
if (pluginName) {
|
||||
if (!acc.has(pluginName)) {
|
||||
acc.set(pluginName, []);
|
||||
}
|
||||
acc.get(pluginName)!.push(filePath);
|
||||
} else {
|
||||
log.warning(`Unable to extract plugin name from path: ${filePath}`);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, new Map<string, string[]>());
|
||||
|
||||
return plugins;
|
||||
};
|
|
@ -9,4 +9,5 @@
|
|||
|
||||
export { readConfigFile } from './loader';
|
||||
export { getConfigFilePath, loadServersConfig } from './utils';
|
||||
export { getScoutPlaywrightConfigs, DEFAULT_TEST_PATH_PATTERNS } from './discovery';
|
||||
export type { Config } from './config';
|
||||
|
|
|
@ -8,9 +8,10 @@
|
|||
*/
|
||||
|
||||
import { UiSettingValues } from '@kbn/test/src/kbn_client/kbn_client_ui_settings';
|
||||
import { formatTime, isValidUTCDate, measurePerformance } from '../../../utils';
|
||||
import { formatTime, isValidUTCDate } from '../../../utils';
|
||||
import { coreWorkerFixtures } from '..';
|
||||
import { ImportSavedObjects, ScoutSpaceParallelFixture } from '.';
|
||||
import { measurePerformanceAsync } from '../../../../common';
|
||||
|
||||
export const scoutSpaceParallelFixture = coreWorkerFixtures.extend<
|
||||
{},
|
||||
|
@ -24,7 +25,7 @@ export const scoutSpaceParallelFixture = coreWorkerFixtures.extend<
|
|||
name: spaceId,
|
||||
disabledFeatures: [],
|
||||
};
|
||||
await measurePerformance(log, `scoutSpace:${spaceId} 'spaces.create'`, async () => {
|
||||
await measurePerformanceAsync(log, `scoutSpace:${spaceId} 'spaces.create'`, async () => {
|
||||
return kbnClient.spaces.create(spacePayload);
|
||||
});
|
||||
|
||||
|
@ -32,26 +33,30 @@ export const scoutSpaceParallelFixture = coreWorkerFixtures.extend<
|
|||
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,
|
||||
});
|
||||
return measurePerformanceAsync(
|
||||
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 };
|
||||
}
|
||||
);
|
||||
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));
|
||||
imported.forEach((so) => savedObjectsCache.set(so.title, so.id));
|
||||
|
||||
return imported;
|
||||
});
|
||||
return imported;
|
||||
}
|
||||
);
|
||||
};
|
||||
const cleanStandardList = async () => {
|
||||
return measurePerformance(
|
||||
return measurePerformanceAsync(
|
||||
log,
|
||||
`scoutSpace:${spaceId} 'savedObjects.cleanStandardList'`,
|
||||
async () => {
|
||||
|
@ -63,7 +68,7 @@ export const scoutSpaceParallelFixture = coreWorkerFixtures.extend<
|
|||
);
|
||||
};
|
||||
const setDefaultIndex = async (dataViewName: string) => {
|
||||
return measurePerformance(
|
||||
return measurePerformanceAsync(
|
||||
log,
|
||||
`scoutSpace:${spaceId} 'savedObjects.setDefaultIndex'`,
|
||||
async () => {
|
||||
|
@ -81,19 +86,23 @@ export const scoutSpaceParallelFixture = coreWorkerFixtures.extend<
|
|||
);
|
||||
};
|
||||
const set = async (values: UiSettingValues) => {
|
||||
return measurePerformance(log, `scoutSpace:${spaceId} 'uiSettings.set'`, async () => {
|
||||
return measurePerformanceAsync(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 }))
|
||||
);
|
||||
});
|
||||
return measurePerformanceAsync(
|
||||
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(
|
||||
return measurePerformanceAsync(
|
||||
log,
|
||||
`scoutSpace:${spaceId} 'uiSettings.setDefaultTime'`,
|
||||
async () => {
|
||||
|
@ -125,7 +134,7 @@ export const scoutSpaceParallelFixture = coreWorkerFixtures.extend<
|
|||
await use({ savedObjects, uiSettings, id: spaceId });
|
||||
|
||||
// Cleanup space after tests via API call
|
||||
await measurePerformance(log, `scoutSpace:${spaceId} 'space.delete'`, async () => {
|
||||
await measurePerformanceAsync(log, `scoutSpace:${spaceId} 'space.delete'`, async () => {
|
||||
return kbnClient.spaces.delete(spaceId);
|
||||
});
|
||||
},
|
||||
|
|
|
@ -12,11 +12,11 @@ import {
|
|||
getLogger,
|
||||
getEsArchiver,
|
||||
createScoutConfig,
|
||||
measurePerformanceAsync,
|
||||
getEsClient,
|
||||
getKbnClient,
|
||||
} from '../../common';
|
||||
import { ScoutTestOptions } from '../types';
|
||||
import { measurePerformance } from '../utils';
|
||||
|
||||
export async function ingestTestDataHook(config: FullConfig, archives: string[]) {
|
||||
const log = getLogger();
|
||||
|
@ -26,7 +26,7 @@ export async function ingestTestDataHook(config: FullConfig, archives: string[])
|
|||
return;
|
||||
}
|
||||
|
||||
return measurePerformance(log, '[setup]: ingestTestDataHook', async () => {
|
||||
return measurePerformanceAsync(log, '[setup]: ingestTestDataHook', async () => {
|
||||
// TODO: This should be configurable local vs cloud
|
||||
const configName = 'local';
|
||||
const projectUse = config.projects[0].use as ScoutTestOptions;
|
||||
|
|
|
@ -8,4 +8,3 @@
|
|||
*/
|
||||
|
||||
export { isValidUTCDate, formatTime, getPlaywrightGrepTag } from './runner_utils';
|
||||
export { measurePerformance } from './performance';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue