## Summary This PR adds `spaceTest` interface to `kbn-scout` to run space aware tests, that can be executed in parallel. Most of Discover tests were converted to parallel run because we see runtime improvement with 2 parallel workers. Experiment 1: **ES data pre-ingested**, running 9 Discover **stateful** tests in **5 files** locally | Run setup | Took time | | ------------- | ------------- | | 1 worker | `1.3` min | | 2 workers | `58.7` sec | | 3 workers | `48.3` sec | | 4 workers | **tests fail** | Conclusion: using **2** workers is the optimal solution to continue Experiment 2: Running Discover tests for stateful/serverless in **Kibana CI** (starting servers, ingesting ES data, running tests) | Run setup | 1 worker | 2 workers | diff | ------------- | ------------- |------------- |------------- | | stateful, 9 tests / 5 files | `1.7` min | `1.2` min | `-29.4%`| | svl ES, 8 tests / 4 files | `1.7` min | `1.3` min | `-23.5%`| | svl Oblt, 8 tests / 4 files | `1.8` min | `1.4` min | `-22.2%`| | svl Search, 5 tests / 2 files | `59.9` sec | `51.6` sec | `-13.8%`| Conclusion: parallel run effectiveness benefits from tests being split in **more test files**. Experiment 3: Clone existing tests to have **3 times more test files** and re-run tests for stateful/serverless in **Kibana CI** (starting servers, ingesting ES data, running tests) | Run setup | 1 worker | 2 workers | diff | ------------- | ------------- |------------- |------------- | | stateful, 27 tests / 15 files | `4.3` min | `2.7` min | `-37.2%`| | svl ES, 24 tests / 12 files | `4.3` min | `2.7` min | `-37.2%`| Conclusion: parallel run effectiveness is **increasing** with more test files in place, **not linear** but with good test design we can expect **up to 40%** or maybe a bit more. How parallel run works: - `scoutSpace` fixture is loaded on Playwright worker setup (using `auto: true` config), creates a new Kibana Space, expose its id to other fixtures and deletes the space on teardown. - `browserAuth` fixture for parallel run caches Cookie per worker/space like `role:spaceId`. It is needed because Playwright doesn't spin up new browser for worker, but only new context. - kbnClient was updated to allow passing `createNewCopies: true` in query, it is needed to load the same Saved Objects in parallel workers/spaces and generate new ids to work with them. `scoutSpace` caches ids and allows to reach saved object by its name. This logic is different from single thread run, where we can use default ids from kbnArchives. How to run parallel tests locally, e.g. for stateful: ``` node scripts/scout run-tests --stateful --config x-pack/platform/plugins/private/discover_enhanced/ui_tests/parallel.playwright.config.ts ``` |
||
---|---|---|
.. | ||
src | ||
index.ts | ||
jest.config.js | ||
kibana.jsonc | ||
package.json | ||
README.md | ||
tsconfig.json |
@kbn/scout
kbn-scout
is a modern test framework for Kibana. It uses Playwright for UI integration tests. Its primary goal is to enhance the developer experience by offering a lightweight and flexible testing solution to create UI tests next to the plugin source code. This README explains the structure of the kbn-scout
package and provides an overview of its key components.
Table of Contents
- Overview
- Folder Structure
- Key Components
- How to Use
- Contributing
Overview
The kbn-scout
framework provides:
- Ease of integration: a simplified mechanism to write and run tests closer to plugins.
- Deployment-agnostic tests: enables the testing of Kibana features across different environments (e.g., Stateful, Serverless).
- Fixture-based design: built on Playwright's fixture model to modularize and standardize test setup.
- Focus on Developer Productivity: faster test execution and minimal boilerplate for writing tests.
Folder Structure
The kbn-scout
structure includes the following key directories and files:
packages/kbn-scout/
├── src/
│ ├── cli/
│ ├── common/
│ │ ├── services/
│ │ ├── utils/
│ ├── config/
│ │ ├── loader/
│ │ ├── schema/
│ │ └── serverless/
│ │ └── stateful/
│ │ └── config.ts
│ ├── playwright/
│ │ ├── config/
│ │ └── fixtures
│ │ │ └── test/
│ │ │ └── worker/
│ │ └── page_objects/
│ │ └── runner
│ │ │ └── config_validator.ts
│ │ │ └── run_tests.ts
│ ├── servers/
│ │ ├── run_elasticsearch.ts
│ │ └── run_kibana_server.ts
│ │ └── start_servers.ts
│ ├── types/
│ └── index.ts
├── package.json
├── tsconfig.json
Key Components
- src/cli/
Contains the logic to start servers, with or without running tests. It is accessed through the scripts/scout
script.
- src/common/
services
directory includes test helpers used across UI and API integration tests, such as Kibana and Elasticsearch clients
, esArchiver
, and samlSessionManager
. These services are used to initialize instances and expose them to tests via Playwright worker fixtures.
- src/config/
config
directory holds configurations for running servers locally. serverless
and stateful
directories contain deployment-specific configurations. Configuration attributes are defined in schema
directory.
The Config
class in config.ts serves as the main entry point. It is instantiated using the config loader in
the loader
directory. This instance is compatible with the kbn-test
input format and is passed to functions
for starting servers.
- src/playwright/
Config
playwright
directory manages the default Playwright configuration. It exports the createPlaywrightConfig
function, which is used by Kibana plugins to define Scout playwright configurations and serves as the entry point to run tests.
import { createPlaywrightConfig } from '@kbn/scout';
// eslint-disable-next-line import/no-default-export
export default createPlaywrightConfig({
testDir: './tests',
workers: 2,
});
Scout relies on configuration to determine the test files and opt-in parallel test execution against the single Elastic cluster.
The Playwright configuration should only be created this way to ensure compatibility with Scout functionality. For configuration
verification, we use a marker VALID_CONFIG_MARKER
, and Scout will throw an error if the configuration is invalid.
Fixtures
The fixtures
directory contains core Scout capabilities required for testing the majority of Kibana plugins. Fixtures can be
scoped to either test
or worker
. Scope decides when to init a new fixture instance: once per worker or for every test function. It is important to choose the correct scope to keep test execution optimally fast: if a new instance is not needed for every test, the fixture should be scoped to worker. Otherwise, it should be scoped to test.
Core worker
scoped fixtures:
log
config
esClient
kbnClient
esArchiver
samlAuth
test.beforeAll(async ({ kbnClient }) => {
await kbnClient.importExport.load(testData.KBN_ARCHIVES.ECOMMERCE);
});
Core test
scoped fixtures:
browserAuth
pageObjects
page
test.beforeEach(async ({ browserAuth }) => {
await browserAuth.loginAsViewer();
});
If a new fixture depends on a fixture with a test
scope, it must also be test
scoped.
Page Objects
The page_objects
directory contains all the Page Objects that represent Platform core functionality such as Discover, Dashboard, Index Management, etc.
If a Page Object is likely to be used in more than one plugin, it should be added here. This allows other teams to reuse it, improving collaboration across teams, reducing code duplication, and simplifying support and adoption.
Page Objects must be registered with the createLazyPageObject
function, which guarantees its instance is lazy-initialized. This way, we can have all the page objects available in the test context, but only the ones that are called will be actually initialized:
export function createCorePageObjects(page: ScoutPage): PageObjects {
return {
dashboard: createLazyPageObject(DashboardApp, page),
discover: createLazyPageObject(DiscoverApp, page),
// Add new page objects here
};
}
All registered Page Objects are available via the pageObjects
fixture:
test.beforeEach(async ({ pageObjects }) => {
await pageObjects.discover.goto();
});
- src/servers/
Here we have logic to start Kibana and Elasticsearch servers using kbn-test
functionality in Scout flavor.
The instance of the Config
class is passed to start servers for the specific deployment type. The loadServersConfig
function not only returns a kbn-test
compatible config instance, but also converts it to ScoutServiceConfig
format and saves it on disk to ./scout/servers/local.json
in the Kibana root directory. Scout config
fixture reads it and expose to UI tests.
How to Use
Starting Servers Only
To start the servers without running tests, use the following command:
node scripts/scout.js start-server [--stateful|--serverless=[es|oblt|security]]
This is useful for manual testing or running tests via an IDE.
Running Servers and Tests
To start the servers and run tests, use:
node scripts/scout.js run-tests [--stateful|--serverless=[es|oblt|security]] --config <plugin-path>/ui_tests/playwright.config.ts
This command starts the required servers and then automatically executes the tests using Playwright.
Running Tests Separately
If the servers are already running, you can execute tests independently using either:
- Playwright Plugin in IDE: Run tests directly within your IDE using Playwright's integration.
- Command Line: Use the following command to run tests:
npx playwright test --config <plugin-path>/ui_tests/playwright.config.ts
Contributing
We welcome contributions to improve and extend kbn-scout
. This guide will help you get started, add new features, and align with existing project standards.
Make sure to run unit tests before opening the PR:
node scripts/jest --config packages/kbn-scout/jest.config.js
Setting Up the Environment
Ensure you have the latest local copy of the Kibana repository.
Install dependencies by running the following commands:
yarn kbn bootstrap
to install dependencies.node scripts/build_kibana_platform_plugins.js
to build plugins.
Move to the packages/kbn-scout
directory to begin development.
Adding or Modifying Features
Contributions to sharable fixtures and page objects are highly encouraged to promote reusability, stability, and ease of adoption. Follow these steps:
Create a New Page Object: Add your Page Object to the src/playwright/page_objects
directory. For instance:
Adding Page Objects
- Create a New Page Object: Add your Page Object to the src/playwright/page_objects directory. For instance:
export class NewPage {
constructor(private readonly page: ScoutPage) {}
// implementation
}
- Register the Page Object: Update the index file to include the new Page Object:
export function createCorePageObjects(page: ScoutPage): PageObjects {
return {
...
newPage: createLazyPageObject(NewPage, page),
};
}
Adding Fixtures
-
Determine Fixture Scope: Decide if your fixture should apply to the
test
(per-test) orworker
(per-worker) scope. -
Implement the Fixture: Add the implementation to
src/playwright/fixtures/test
orsrc/playwright/fixtures/worker
.
export const newTestFixture = base.extend<ScoutTestFixtures, ScoutWorkerFixtures>({
newFixture: async ({}, use) => {
const myFn = // implementation
await use(myFn);
// optionally, cleanup on test completion
},
});
- Register the Fixture: Add the fixture to the appropriate scope:
export const scoutTestFixtures = mergeTests(
...
newTestFixture,
);
Best Practices
- Reusable Code: When creating Page Objects or Fixtures that apply to more than one plugin, ensure they are added to the kbn-scout package.
- Adhere to Existing Structure: Maintain consistency with the project's architecture.
- Add Unit Tests: Include tests for new logic where applicable, ensuring it works as expected.
- Playwright documentation: Official best practices