Loading

Page objects

Page objects wrap UI interactions (navigation, clicking, filling forms) so tests read like user workflows and stay maintainable as the UI evolves.

Tip

Keep page objects focused on UI interactions. Don’t hide API setup/teardown inside page objects—use API services or fixtures instead.

For practical tips, see best practices.

Page objects are exposed through the pageObjects fixture and are lazy-initialized:

import { tags } from '@kbn/scout';
import { test } from '../fixtures';

test.describe('My suite', { tag: tags.deploymentAgnostic }, () => {
  test.beforeEach(async ({ browserAuth, pageObjects }) => {
    await browserAuth.loginAsViewer();
    await pageObjects.discover.goto();
  });
});
		
  • Core page objects: @kbn/scout (available as pageObjects.<name>)
  • Solution Scout packages may provide additional page objects (their internal folder layout varies—search within the package for page_objects if you need the source).
  • Plugin-local page objects: <plugin-root>/test/scout/ui/fixtures/page_objects

To make your page object available as pageObjects.newPage, register it in your plugin fixtures.

  1. Create a plugin page object

    Create a class that takes ScoutPage and exposes locators + actions:

    import { ScoutPage } from '@kbn/scout';
    
    export class NewPage {
      constructor(private readonly page: ScoutPage) {}
    
      async goto() {
        await this.page.gotoApp('myPlugin');
      }
    }
    		
    1. replace with your app id
  2. Register a plugin page object

    Register it in fixtures/page_objects/index.ts

    import type { PageObjects, ScoutPage } from '@kbn/scout';
    import { createLazyPageObject } from '@kbn/scout';
    import { NewPage } from './new_page';
    
    export type MyPluginPageObjects = PageObjects & {
      newPage: NewPage;
    };
    
    export function extendPageObjects(pageObjects: PageObjects, page: ScoutPage): MyPluginPageObjects {
      return {
        ...pageObjects,
        newPage: createLazyPageObject(NewPage, page),
      };
    }
    		
  3. Wire it into your plugin test fixture

    In <plugin-root>/test/scout/ui/fixtures/index.ts, extend Scout’s test so pageObjects has your extended type:

    import { test as base } from '@kbn/scout';
    
    import type { MyPluginPageObjects } from './page_objects';
    import { extendPageObjects } from './page_objects';
    
    export const test = base.extend<{ pageObjects: MyPluginPageObjects }>({
      pageObjects: async ({ pageObjects, page }, use) => {
        await use(extendPageObjects(pageObjects, page));
      },
    });
    		

    Now your specs can use pageObjects.newPage without importing the page object class directly.

    Note

    If your page object constructor needs extra arguments, pass them after page:

    createLazyPageObject(NewPage, page, extraArg1, extraArg2).

    If you use spaceTest (parallel UI suites), extend it the same way: import spaceTest as base from @kbn/scout, then export const spaceTest = base.extend<{ pageObjects: MyPluginPageObjects }>(...).