Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/platform/packages/shared/kbn-scout/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export {
apiTest,
globalSetupHook,
tags,
createTest,
AbstractPageObject,
} from './src/playwright';

// Fixtures & configuration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ export { apiTest } from './test/api';

// Test helpers for EUI components
export * from './eui_components';

export * from './test/utils';
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* 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 { ScoutPage } from '../../../..';

export class AbstractPageObject {
constructor(public readonly page: ScoutPage) {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* 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 { test as base } from '../../../..';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like this could be done for the lighthouseTest and spaceTest as well

import type { ScoutPage, ScoutTestFixtures, ScoutWorkerFixtures } from '../../../..';
import type { PageObjects } from '../../../..';
import { createLazyPageObject } from '../../../..';
import type { AbstractPageObject } from './abstract_page_object';

type PageObjectClass = new (page: ScoutPage) => AbstractPageObject;

export const createTest = function <PageObjectsExtensions = Record<string, AbstractPageObject>>(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps extendTest or extendPageObjects would make more sense

Copy link
Contributor

@csr csr Nov 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createTest() is only extending the pageObjects fixture (test-scoped) but notice it still passes two type parameters to base.extend(): test-scoped fixtures and worker-scoped fixtures. It seems to me here we limit the scope of what you can do with createTest to just page object creation, which imho isn't ideal.

See some examples of Scout packages and plugins extending multiple fixtures (not just pageObjects) using base.extend():

  • The Security Scout package extends both pageObjects and apiServices here.
  • The Scout Observability package is extending both fixtures as well here.
  • The APM plugin extends both pageObjects and browserAuth here.

What I appreciate about the current architecture is how flexible it is when it comes to extending any Scout core fixture (test-scoped and worker-scoped) to expose additional methods. While your method is convenient I'm afraid it will limit what plugins (but most especially Scout packages) can expose to other plugins. What do you think?

pageObjectClassMap: Record<string, PageObjectClass>
) {
type PageObjectsExtended = PageObjectsExtensions & PageObjects;

function extendPOs(pageObjects: PageObjects, page: ScoutPage): PageObjectsExtended {
const initedLazyPageObjects = Object.keys(pageObjectClassMap).reduce<
Record<string, AbstractPageObject>
>((col, value) => {
col[value] = createLazyPageObject(pageObjectClassMap[value], page);
return col;
}, {});

return {
...initedLazyPageObjects,
...pageObjects,
} as PageObjectsExtended;
Copy link
Contributor Author

@mattkime mattkime Nov 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer a better solution than using as PageObjectsExtended but it was the best I could do.

Also, I bet a number of these things could be better named.

}

return base.extend<
ScoutTestFixtures & {
pageObjects: PageObjectsExtended;
},
ScoutWorkerFixtures
>({
pageObjects: async (
{
pageObjects,
page,
}: {
pageObjects: PageObjectsExtended;
page: ScoutPage;
},
use: (pageObjects: PageObjectsExtended) => Promise<void>
) => {
const extendedPageObjects = extendPOs(pageObjects, page);
await use(extendedPageObjects);
},
});
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* 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".
*/

export { createTest } from './create_test';
export { AbstractPageObject } from './abstract_page_object';
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@
import type {
PageObjects,
ScoutTestFixtures,
ScoutWorkerFixtures,
ScoutParallelTestFixtures,
ScoutParallelWorkerFixtures,
} from '@kbn/scout';
import { test as baseTest, spaceTest as spaceBaseTest, createLazyPageObject } from '@kbn/scout';
import { spaceTest as spaceBaseTest, createLazyPageObject } from '@kbn/scout';
import { createTest } from '@kbn/scout';
import { DemoPage } from './page_objects';

export interface ExtScoutTestFixtures extends ScoutTestFixtures {
Expand All @@ -21,24 +21,10 @@ export interface ExtScoutTestFixtures extends ScoutTestFixtures {
};
}

export const test = baseTest.extend<ExtScoutTestFixtures, ScoutWorkerFixtures>({
pageObjects: async (
{
pageObjects,
page,
}: {
pageObjects: ExtScoutTestFixtures['pageObjects'];
page: ExtScoutTestFixtures['page'];
},
use: (pageObjects: ExtScoutTestFixtures['pageObjects']) => Promise<void>
) => {
const extendedPageObjects = {
...pageObjects,
demo: createLazyPageObject(DemoPage, page),
};

await use(extendedPageObjects);
},
export const test = createTest<{
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the result I desired, a simple and terse way to extend the set of page objects. The createTest function is dense and difficult to read but the code it replaces didn't have any more meaning as best I could tell.

demo: DemoPage;
}>({
demo: DemoPage,
});

export interface ExtParallelRunTestFixtures extends ScoutParallelTestFixtures {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,9 @@
* 2.0.
*/

import type { ScoutPage } from '@kbn/scout';

export class DemoPage {
constructor(private readonly page: ScoutPage) {}
import { AbstractPageObject } from '@kbn/scout';

export class DemoPage extends AbstractPageObject {
async goto() {
await this.page.gotoApp('not_implemented');
}
Expand Down