Skip to content

[Feature]: A hook to run before webServer #37237

@kettanaito

Description

@kettanaito

🚀 Feature Request

Hi!

It would be great if Playwright supported an optional hook that would allow developers to execute custom logic related to their tests before Playwright spawns the tested application (i.e. executes webServer.command).

With that, I am proposing two new hooks:

interface ProjectConfig {
  runnerSetup?: string
  runnerTeardown?: string
}

Mirroring the globalSetup and globalTeardown hooks, the newly proposed ones allow you to include a setup/teardown file and await that respective logic before spawning your tested app.

With the proposed hooks, here's the order of things:

- runnerSetup
  - webServer
  - globalSetup
    - beforeAll
    - afterAll
  - globalTeardown
- runnerTeardown

Example

Basic usage

The runnerSetup and runnerTeardown hooks behave identically to globalSetup and globalTeardown, in a sense that they expect the specified modules to have a default export describing the logic to run:

// runner-setup.ts
export default async function runnerSetup() {
  setupLogic()
}
// runner-teardown.ts
export default async function runnerTeardown() {
  teardownLogic()
}
// playwright.config.ts
export default defineConfig({
  runnerSetup: './runner-setup.ts',
  runnerTeardown: './runner-teardown.ts',
})

Shorthand teardown declaration

Similar to the globalSetup optionally returning the teardown callback, I propose the runnerSetup support the same pattern:

export default async runnerSetup({ context }) {
  const container = await new PostgresContainer().start()

  return async () => {
    await container.stop()
  }
}

This is extremely handy to help manage persisted resources, like my testcontainer above.

Motivation

Currently, there's no way to run custom logic before the webServer step of Playwright:

  • webServer is a static object, not a function to allow logic (primarily async one) to run before spawning the app (and spawning the app dynamically based on that logic).
  • globalSetup runs after webServer, making it impossible to influence the spawned app (e.g. by providing different env based on some async logic). This is intended as you expect your tested app to be up by this point to create browsers and do custom browser/page-related setup.

Because of this, it becomes redundantly complex to introduce trivial custom logic related to the test run. Take the aforementioned Testcontainers as an example. Without the proposed hooks, I need to:

  • Introduce a custom script.
  • Start the testcontainer there.
  • Spawn the entire Playwright process dynamically to provide dynamic env variables.
  • Manage the spawned PW process.
  • Close the testcontainer manually.

This is an extreme level of complexity for something as small as spawning something alongside but before my tested app. I strongly suggest you consider this proposal to simplify such setups as I believe they are rather common.

Any other approaches, like chaining shell scripts or creating a custom webServer.command, suffer from the same issue: they introduce redundant complexity because you are trying to manage a test runner-related resource outside of that test runner.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions