Playwright

This document describes the functionality of the Playwright Discovery plugin and its integration with PandoraFMS. 

Introduction

Ver. 02-08-2026

This document describes the functionality of the Playwright Discovery plugin (pandorafms.playwright.1) and its integration with PandoraFMS. The plugin runs a customer-provided Playwright .ts test inside a preconfigured Docker container, turning the result into transactional monitoring modules that plug into the console's WUX transactional view (global status/time, per-phase status/time, error screenshot, custom metrics, and an optional error-history module).

It is the Playwright counterpart of pandorafms.selenium.4, but built around Playwright's native execution model: the customer writes standard Playwright code — no custom library to import, no DSL to learn — and the plugin harvests everything from Playwright's own JSON reporter.

Type: Discovery plugin (.disco)

Compatibility matrix

**Systems where tested**Rocky Linux 9/10 (Pandora server), Docker image `pandorafms/pandora_playwright:noble` (Ubuntu Noble)
**Systems where it works**Any system that can run Docker as the local server, or reach a remote host over SSH that can run Docker

Pre requisites

1. Docker The machine that executes the test must have Docker available: the Discovery/Pandora server itself for worker_mode = local, or the SSH target for worker_mode = remote.

2. The Playwright Docker image pandorafms/pandora_playwright:noble must be available on the machine that runs the test (browsers preinstalled).

docker pull pandorafms/pandora_playwright:noble once published.

3. SSH access (remote worker only) For worker_mode = remote, an SSH user able to run Docker on the target host, plus a temporary folder writable by that user (default /tmp) to stage the test file.

4. Tentacle (optional, CLI/standalone use) If invoking the plugin outside the Discovery task pipeline with --xml_mode, a reachable tentacle_serverd is required to receive the generated agent XML.

Parameters

Discovery task wizard parameters

**Field****Macro****Notes****Default**
Worker mode`_workerMode_``local` or `remote`. `remote` runs Docker on an SSH host.`local`
Browser`_browser_``chromium`, `firefox`, `webkit``chromium`
SSH address`_sshAddress_`Host that runs Docker (remote only)
SSH port`_sshPort_``22`
SSH user`_sshUser_`Must be able to run Docker`root`
SSH password`_sshPassword_`Encryptable via `password_encrypter.py`
Encrypt password`_sshPasswordEncrypt_`Obfuscates the password in the task configon
Temporal folder`_sshTemp_`Where the test file is copied on the remote host`/tmp`
Docker image`_dockerImage_``pandorafms/pandora_playwright:noble`
Browser width`_browserWidth_``1920`
Browser height`_browserHeight_``1080`
Test timeout`_globalTimeout_`Per-test timeout, in **seconds**`30`
Send full report`_fullReport_`Adds a verbose text report moduleoff
Report agent name`_reportAgent_`Agent that holds the full report; empty = first test's agent
Generate error history module`_errorHistoryModule_`Adds a synchronous string module per status/phase (`OK` or the error text), so Pandora keeps a historic value series of errorsoff
Playwright test (.ts)`_playwrightTest_`The full test file content

Task configuration (JSON the runner receives)

{
  "worker_mode": "local",
  "browser": "chromium",
  "ssh_address": "", "ssh_port": "22", "ssh_user": "root",
  "ssh_password": "", "ssh_password_encrypt": "0", "ssh_temp_folder": "/tmp",
  "docker_image": "pandorafms/pandora_playwright:noble",
  "browser_width": "1920", "browser_height": "1080",
  "global_timeout": "30",
  "full_report": "0",
  "report_agent": "",
  "error_history_module": "0"
}

CLI parameters (manual / standalone execution)

**Short****Long****Required****Default****Description**
`-c``--conf`yesPath to the task configuration JSON
`-s``--test`yesPath to the Playwright `.ts` test file
`-t``--task`yesTask name (used to derive the container name)
`-i``--interval`no`300`Agent interval (seconds)
`-g``--group`no`0`Group id for the created agents
`-x``--xml_mode`nooffBuild agent XML and send it via Tentacle (standalone mode)
`-S``--server`no`127.0.0.1:41121`Tentacle `server:port` (with `-x`)
`-T``--temp`no`/tmp`Temp folder for the XML (with `-x`)
`-v``--verbose`nooffStep-by-step trace to STDERR

password_encrypter -e -p <password> encrypts a password; -d decrypts it (used by the console for the SSH password field).

Manual execution

Execution format

pandora_playwright -c <conf.json> -s <test.ts> -t <task_name> \
  [-i <interval>] [-g <group_id>] \
  [-x [-S <server:port>] [-T <temp_folder>]] \
  [-v]

Examples

Native mode (JSON to STDOUT, as run by a Discovery task):

pandora_playwright -c conf.json -s task.spec.ts -t qa-test -g 0 -v

Standalone mode against a real Pandora server (creates real agents/modules via Tentacle):

pandora_playwright -x -S 127.0.0.1:41121 \
    -c conf.json -s task.spec.ts -t qa-console -g 2 -T /tmp

Remote worker (Docker runs over SSH; worker_mode=remote in conf.json):

pandora_playwright -c conf_remote.json -s task.spec.ts -t qa-remote -g 0 -v

Verbose mode

-v prints a timestamped, step-by-step trace to STDERR — useful for manual runs. It logs every Docker/SSH command verbatim ($ local, ssh$ remote), the config summary, report size, screenshot harvest, per-agent build, and emission summary:

Task <id>: worker=remote browser=chromium image=...:noble timeout=15s ...
Connecting SSH to 10.0.0.5:22 as root
SSH authenticated
SCP test.ts -> /tmp/<id>.spec.ts
ssh$ docker run -d --name <id> ...:noble sleep 300
ssh$ docker cp "/tmp/<id>.spec.ts" <id>:/pandora/task.spec.ts
ssh$ docker exec <id> sh -c 'cd /pandora && ... npx playwright test ... --reporter=json'
ssh$ docker exec <id> cat /tmp/report.json
Report retrieved (10744 bytes)
Screenshot harvested: .../test-failed-1.png (7416 b64 chars)
ssh$ docker rm -f <id>
Agent a...  [passing checkout]: PASS, 2 phases, 9 modules
Emitting monitoring_data: 2 agents

Configuration in PandoraFMS

The plugin is delivered as a .disco Discovery package. To configure it:

1. Install the .disco package, if not already installed: go to Discovery → Extension manager and upload the package (or confirm it is already listed).

2. Create a new Discovery task: go to Discovery → Applications → Playwright.

3. Fill in the wizard steps (see Parameters above):

4. Save the task. On each execution, the plugin runs the test in Docker and reports one agent per Playwright test(...) found in the file.

5. Open the resulting agent(s) in the console. The WUX transactional view renders the phases derived from test.step(...); standard module views show status, time, error screenshot, metrics, and (if enabled) the error-history modules.

Agent and modules generated by the plugin

One agent per Playwright test(...).

**Module name****Type****Description****`extra_data`**
`Global status``generic_proc`Overall test result: 1 if `passed`, 0 otherwise`wux:global_status:`
`Global time``generic_data`Total test duration, in seconds`wux:global_time:`
`Phase status``generic_proc`Result of each `test.step`: 1 if it had no error, 0 otherwise`wux:phase_status::`
`Phase time``generic_data`Duration of each `test.step`, in seconds`wux:phase_time::`
`Last error screenshot``generic_data_string`Screenshot on failure, as a `data:image/png;base64,...` value (renders as an image in the console)`wux:error_screenshot:`
`Global error``generic_data_string`Only when `_errorHistoryModule_` is on. `OK` when the test passes, or the test's error text when it fails — a synchronous module, so Pandora keeps a real historic value series (not just the status module's description, which gets overwritten every run)`wux:global_error:`
`Phase error``generic_data_string`Only when `_errorHistoryModule_` is on. `OK` or the phase's error text, same history rationale as `Global error``wux:phase_error::`
`metric name``generic_data` / `generic_data_string`From a `pandora.metric` annotation (`name=value`, split on the first `=`); numeric values become `generic_data`, everything else `generic_data_string``pw:metric:`
`Full report``async_string`Only when `_fullReport_` is on. Verbose text report derived from the JSON reporter (status, steps, stdout/stderr, annotations, attachments)`pw:full_report`

The wux:* modules render in the console's WUX transactional view. pw:* modules are regular agent modules (metrics and the full report).

Recording a transaction

A "transaction" is a standard Playwright test — no PandoraFMS import required. Three native constructs map to plugin behavior:

You write Becomes
test.step('name', ...) a monitored phase (status + time)
test.info().annotations.push({ type: 'pandora.metric', description: 'name=value' }) a custom metric module
a failing assertion the test fails; a screenshot is captured automatically

1. Record the flow with Playwright's recorder (codegen)

Playwright ships its own recorder, codegen: it opens a real browser, and every click, fill, and navigation you perform is turned into Playwright code in real time, plus a Pick Locator / Explore mode to test selectors against the live page. Official documentation: playwright.dev/docs/codegen-intro. General authoring reference: playwright.dev/docs/writing-tests.

On any machine with Node and Playwright installed (this does not need to be the plugin's Docker image):

npm init playwright@latest    # first time only, if the project isn't set up yet
npx playwright codegen https://your-app.example.com

Two windows open: the browser you interact with, and the Playwright Inspector, which shows the generated code live and lets you pick/copy a locator for any element on the page. Useful flags:

codegen output is flat, ungrouped code — clicks and assertions one after another, with no test.step(...) and no metric annotations. It is a starting point, not the final transaction: copy it into your .ts file and go to step 2.

2. Turn it into phases

Wrap each meaningful part of the recorded flow in test.step('name', async () => { ... }). Every top-level test.step call — one written directly inside the test(...) callback — becomes one phase, with its own Phase <name> status and Phase <name> time module (see Agent and modules generated by the plugin). Official reference: test.step() API.

await test.step('open home', async () => {
  await page.goto('https://your-app.example.com');
  await expect(page).toHaveTitle(/Shop/);
});

Things to know:

3. Add custom metrics

Push a pandora.metric annotation with test.info() — from anywhere in the test body, including inside a test.step:

const count = await page.locator('.cart-count').innerText();
test.info().annotations.push({ type: 'pandora.metric', description: `cart_items=${count}` });

Official reference for annotations: test.info().annotations.

Parsing rules (exact, from the runner):

Full example

import { test, expect } from '@playwright/test';

test('checkout flow', async ({ page }) => {
  await test.step('open home', async () => {
    await page.goto('https://your-app.example.com');
    await expect(page).toHaveTitle(/Shop/);
  });

  await test.step('login', async () => {
    await page.fill('#user', 'demo');
    await page.fill('#password', 'demo');
    await page.click('#submit');
    await expect(page.locator('.dashboard')).toBeVisible();
  });

  await test.step('add to cart', async () => {
    await page.click('text=Add to cart');
    const count = await page.locator('.cart-count').innerText();
    test.info().annotations.push({ type: 'pandora.metric', description: `cart_items=${count}` });
  });
});

Notes

Generating tests with an AI agent

Instead of hand-writing the .ts transaction, or recording it once with codegen and hoping the selectors hold, a local coding agent — Claude Code, Codex, or similar — can drive a real browser through the flow and write the transaction for you, validating every locator against the live target before handing it over. This catches exactly the class of bug that makes a transaction flaky in production (duplicate status badges, ambiguous hasText filters, a CSS-only visual match that isn't really unique in the DOM) — things plain codegen cannot, because it only records literal actions without checking whether the resulting locator is actually unique.

1. Install a Playwright browser-automation capability

The agent needs a tool that can open a real browser and drive it (click, fill, read the DOM), not just guess from a screenshot. The standard way is the official Playwright MCP server, @playwright/mcp (by Microsoft) — it exposes browser control as MCP tools to any MCP-compatible agent.

Claude Code:

claude mcp add playwright -- npx @playwright/mcp@latest

(Anthropic's official plugin marketplace also ships a ready-made "playwright" plugin that wires this same MCP server — either path works.)

Codex CLI:

codex mcp add playwright -- npx @playwright/mcp@latest

Both commands register the server for stdio use; npx fetches @playwright/mcp on first run. This needs Node available on the machine running the agent — not the plugin's Docker image, since this step happens locally, before the test file even exists.

2. Prompt template

Validate [FLOW NAME] on [URL] and write it as a Playwright transaction for the
pandorafms.playwright.1 plugin.

What the transaction should check:
- [step 1, e.g. "open the page and confirm the title"]
- [step 2, e.g. "log in and confirm the dashboard loads"]
- [step 3, e.g. "read a value and publish it as a pandora.metric"]

Deliverable:
- Plain Playwright: wrap each meaningful step in a top-level `test.step('name', ...)`
  so it becomes a monitored phase, and use
  `test.info().annotations.push({ type: 'pandora.metric', description: 'name=value' })`
  for anything that should become a custom metric module.
- No PandoraFMS import, no DSL — this plugin harvests everything from
  Playwright's own JSON reporter.
- Validate every locator against the real target yourself (drive the browser,
  don't just infer from a snapshot) before handing me the file — fix anything
  ambiguous or strict-mode-violating first.
- If a later step depends on a hard assertion in an earlier step, tell me
  whether to keep it that way or switch to `expect.soft()` so every phase gets
  measured even when one fails.

3. After you get the file

Run it through the plugin locally before wiring it into a Discovery task — see Manual execution above — so you see the real agent/module output, not just "the test passed."

Self-signed certificates

By default Playwright validates TLS certificates like a real browser, so a target using a self-signed or internally-issued certificate makes every page.goto(...) fail with net::ERR_CERT_AUTHORITY_INVALID before the test logic even runs.

There is no plugin-level toggle for this — it is set explicitly in the transaction with Playwright's own test.use():

import { test, expect } from '@playwright/test';

test.use({ ignoreHTTPSErrors: true });

test('checkout flow', async ({ page }) => {
  await page.goto('https://internal.example.com');
  // ...
});

Debugging and Troubleshooting

Debugging a test

When a transaction fails and the screenshot/Full report isn't enough, run the test interactively inside the same image the plugin uses, with Playwright's HTML report (trace + failure video) served from the container. The image ships playwright.config.debug.ts (in /pandora) preconfigured with trace: 'on-first-retry', screenshot: 'only-on-failure', video: 'retain-on-failure', and an HTML reporter bound to 0.0.0.0:9323.

docker run -it --rm \
  -v "$(pwd)/test.spec.ts:/pandora/test.spec.ts" \
  -p 9323:9323 \
  pandorafms/pandora_playwright:noble bash

# inside the container:
npx playwright test test.spec.ts --config=playwright.config.debug.ts --browser=chromium --timeout=30000
npx playwright show-report --host 0.0.0.0 --port 9323

With -p 9323:9323 published, open http://localhost:9323 on the host to browse the report: per-step results, the trace viewer, and the video of the failure.

Troubleshooting