.agents/skills/scout-ui-testing/references/scout-ui-parallelism.md
Use this when working under .../test/scout*/ui/parallel_tests/ or a parallel.playwright.config.ts.
spaceTest (not test) so you can access scoutSpace.spaceTest.describe(...) must include { tag: ... }.ui/parallel.playwright.config.ts: testDir: './parallel_tests', workers: 2..3, optional runGlobalSetup: true.ui/parallel_tests/global.setup.ts: define globalSetupHook(...) (runs once total).ui/parallel_tests/global.teardown.ts (optional): define globalTeardownHook(...) to reset cluster/Kibana state once after the suite. Picked up automatically when runGlobalSetup: true; no extra config flag.import { spaceTest, tags } from '@kbn/scout'; // or the module's Scout package
import { expect } from '@kbn/scout/ui'; // or '@kbn/scout-oblt/ui', etc.
spaceTest.describe('my feature', { tag: tags.deploymentAgnostic }, () => {
spaceTest.beforeAll(async ({ scoutSpace }) => {
// Worker-scoped setup in the isolated space.
await scoutSpace.savedObjects.cleanStandardList();
});
spaceTest.afterAll(async ({ scoutSpace }) => {
await scoutSpace.savedObjects.cleanStandardList();
});
spaceTest('does something', async ({ browserAuth, pageObjects, page }) => {
await browserAuth.loginAsViewer();
await pageObjects.somePage.goto();
await expect(page.testSubj.locator('someElement')).toBeVisible();
});
});
esArchiver, apiServices, kbnClient, esClient, log).page, browserAuth, or pageObjects in global.setup.ts.Use globalTeardownHook in parallel_tests/global.teardown.ts to reset Kibana/cluster state once after all workers finish — runs even if tests failed. Same fixture surface as setup, except esArchiver is intentionally not exposed.
import { globalTeardownHook } from '@kbn/scout'; // or '@kbn/scout-oblt' / '@kbn/scout-security'
globalTeardownHook('Reset shared Kibana state', async ({ esClient, kbnClient, apiServices, log }) => {
log.info('[teardown] resetting shared state');
// Drop hand-indexed data ingested by global.setup.ts that affects other Scout configs
// sharing this cluster (Scout's esArchiver intentionally has no unload — see cautions)
await esClient.indices.delete({ index: 'apm-8.0.0-*', ignore_unavailable: true });
await esClient.indices.deleteDataStream({ name: 'logs-my_dataset-*' }).catch(() => {});
// Revert any uiSettings / advanced settings the suite set globally
await kbnClient.uiSettings.unset('discover:searchOnPageLoad');
await kbnClient.uiSettings.updateGlobal({ hideAnnouncements: false });
// Revert feature-flag overrides flipped via apiServices.core.settings(...)
await apiServices.core.settings({ 'feature_flags.overrides': { 'discover.isEsqlDefault': 'false' } });
// Saved-object cleanup the suite created cluster-wide (per-space cleanup belongs in afterAll)
await kbnClient.savedObjects.cleanStandardList();
});
Cautions:
esArchiver on the teardown surface — by design. Scout's esArchiver fixture only ever exposed loadIfNeeded; archive-driven unloading was never supported because it's slow and adds nothing — leftover indexes in the cluster don't break tests when setup uses idempotent loadIfNeeded. For state that does need resetting, use esClient.indices.delete / deleteDataStream / deleteByQuery directly.globalSetupHook (which is idempotent via esArchiver.loadIfNeeded).afterEach/afterAll — the global teardown is for state shared across the whole suite (or leaked into the cluster from setup) that other configs running in the same Kibana/ES would otherwise inherit.log, config, kbnUrl, esClient, kbnClient, apiServices (plus the same lazy samlAuth/isSnapshotBuild from coreWorkerFixtures). No page/browserAuth/pageObjects and no esArchiver.parallel_tests/global.teardown.ts calling globalTeardownHook(...). No new config flag — runGlobalSetup: true is enough.