list
logo

Playwright Plugin

The Webswing Playwright Plugin is a Node.js helper for testing Swing and JavaFX applications rendered by Webswing. The application UI is drawn on one or more HTML canvases, so its buttons, text fields, tables, and other desktop components cannot be located with ordinary Playwright DOM locators.

The helper locates components through Webswing Test Mode, validates the number of matches, and uses Playwright mouse and keyboard input to interact with the component at its canvas coordinates.

Prerequisites

  • Webswing 26.2 or later;
  • a running Webswing server and application;
  • Enable Test Mode = ON in the tested application's configuration;
  • Node.js and npm;
  • Playwright and at least one installed browser.

Test Mode exposes the requestComponentTreeElement() API used by the helper. If Test Mode is disabled, component lookups and all selector-based actions fail.

Get the helper

The helper is included in the Webswing Test Tool distribution at:

playwright-plugin/webswing-playwright-plugin.js

When Test Tool is deployed, download it from:

<test-tool-url>/playwright-plugin/webswing-playwright-plugin.js

Use a helper from the same Webswing version as the application under test.

Webswing Test Tool can record a workflow and export it as a Playwright Test spec. You can edit the generated spec or write tests manually using selectors created with Pick and the helper API described below.

Export a recording from Test Tool

  1. Record and finish a test case in Webswing Test Tool.
  2. Select Download Test Case in the recording view or file explorer.
  3. Select the Playwright tab.
  4. Download webswing-playwright-plugin.js from the Required helper/plugin section.
  5. Enter the Webswing application URL, login username, login password, and startup timeout in milliseconds.
  6. Select Download Playwright to generate the test spec.

Recorded web elements use DOM CSS selectors. The generated spec calls webswingWebAssertion(page, selector, actionScript, validationScript, click) to run the action script, require a truthy validation script result, and then perform a recorded click.

All export fields are required. The URL must be an absolute http:// or https:// URL, and the startup timeout must be a positive integer. The generated filename includes the generator name, for example login-test-playwright.spec.js.

The generated spec contains the application URL and login credentials as plain text. Store it securely and avoid committing secrets to source control.

Set up a Playwright project

Create a Node.js project, install Playwright Test, and install a browser:

mkdir webswing-playwright-test
cd webswing-playwright-test
npm init -y
npm install --save-dev @playwright/test
npx playwright install chromium

Place the helper in the project and create a test file:

webswing-playwright-test/
├── node_modules/
├── package.json
└── tests/
    ├── generated-test-playwright.spec.js
    └── webswing-playwright-plugin.js

Run all tests with:

npx playwright test

Run the tests interactively or in a visible browser with:

npx playwright test --ui
npx playwright test --headed

Write a test

Import the helper and pass the Playwright page as the first argument to every assertion or action:

const { test, expect } = require('@playwright/test')
const webswing = require('./webswing-playwright-plugin')

test('enters and saves a value', async ({ page }) => {
  await page.goto('http://localhost:8080/webswing-demo/')

  // The login form is HTML, so ordinary Playwright locators are used here.
  await page.locator('[name="username"]').fill('admin')
  await page.locator('[name="password"]').fill('pwd')
  await page.locator('.ws-btn-submit').click()

  await expect(page.locator('.webswing-canvas').first()).toBeVisible()

  // The desktop application is rendered on canvas, so use Webswing selectors.
  await webswing.webswingShouldVisible(page, 'JTextField[name="username"]')
  await webswing.webswingType(page, 'JTextField[name="username"]', 'alice')
  await webswing.webswingCheckValue(page, 'JTextField[name="username"]', 'alice')
  await webswing.webswingClick(page, 'JButton[name="saveButton"]')
})

All helper functions that accept page are asynchronous and must be awaited.

Use ordinary Playwright locators for the login page, embedding page, and other HTML controls. Use the Webswing helper only for Swing and JavaFX components rendered on the canvas.

Component selectors

The helper accepts Webswing's CSS-like Query Syntax. This is not a DOM CSS selector. Prefer stable properties such as component type, name, and value:

JButton[name="saveButton"]
JTextField[name="username"]
JDialog[value="Confirm"] > JButton[value="OK"]
JTable[columnheader="Name"][rownumber="1"]

Selectors used for actions and single-component assertions must identify exactly one component. Webswing Test Tool can generate a selector for a component:

  1. Start recording and select Create Assertion.
  2. Select Pick, then select the component in the application.
  3. Select Evaluate to verify that exactly one component matches.
  4. Copy the generated Query Syntax into the Playwright spec.

Common attribute operators include =, !=, *=, ^=, and $=. Direct-child (>), descendant (space), adjacent-sibling (+), and general-sibling (~) relationships can be used to disambiguate components.

Avoid screen coordinates and dimensions in selectors unless necessary because layout changes can make them unstable. If you control the application, setting a stable Swing component name provides a more reliable selector.

Helper API

Function Purpose
webswingShouldExist(page, selector) Requires exactly one matching component.
webswingShouldExistCount(page, selector, count) Requires exactly the specified number of matches.
webswingShouldNotExist(page, selector) Requires zero matching components.
webswingShouldVisible(page, selector) Requires exactly one effectively visible component.
webswingShouldNotVisible(page, selector) Requires exactly one component with no effective visible area.
webswingCheckValue(page, selector, value) Requires exactly one match whose value equals the expected value.
webswingClick(page, selector) Clicks one matched component.
webswingMove(page, selector, moveX, moveY) Drags from one matched component by the specified pixel offsets.
webswingType(page, selector, text) Focuses one matched component, moves the caret to the end, and types text or a supported key.
webswingClearInput(page, selector) Focuses one matched component and clears its current value using keyboard input.
setWebswingInstanceName(name) Changes the Webswing instance name used by subsequent lookups.

The lookup function is internal. Call an assertion or action directly; each call obtains a fresh view of the component tree.

Assertions

await webswing.webswingShouldExist(page, 'JButton[name="saveButton"]')
await webswing.webswingShouldExistCount(page, 'JButton', 3)
await webswing.webswingShouldNotExist(page, 'JDialog[value="Error"]')
await webswing.webswingShouldVisible(page, 'JTextField[name="username"]')
await webswing.webswingShouldNotVisible(page, 'JLabel[name="offscreenStatus"]')
await webswing.webswingCheckValue(page, 'JTextField[name="username"]', 'alice')

webswingShouldExistCount() accepts a non-negative integer. All other component assertions require a selector that resolves to zero or exactly one component, according to the assertion.

Actions

await webswing.webswingClick(page, 'JButton[name="saveButton"]')
await webswing.webswingType(page, 'JTextField[name="username"]', 'alice')
await webswing.webswingClearInput(page, 'JTextField[name="username"]')
await webswing.webswingMove(page, 'ScrollBar[name="verticalScrollBar"]', 0, 100)

Actions require exactly one matching component. Click and type also require the component to be enabled. The helper validates the component size and interaction point before sending input.

webswingType() accepts text and recognizes Backspace, Delete, Enter, Return, Escape, and End as special keys. It also recognizes the corresponding Selenium key codes used by generated or converted recordings.

webswingMove() starts at the center of the component and applies moveX and moveY as pixel offsets. Positive X moves right and positive Y moves down. Both values must be valid integers, and the destination must remain inside the active Webswing canvas.

Configure the Webswing instance

The helper uses window.webswingInstance0 by default. If the embedding page exposes the application under another name, configure it before the first lookup:

webswing.setWebswingInstanceName('myWebswingInstance')

await webswing.webswingClick(page, 'JButton[name="saveButton"]')

Lookup retries

Component lookup is retried for up to five seconds at 100-millisecond intervals. This allows assertions and actions to wait for components that appear asynchronously. A call still fails after the timeout if the query is invalid, the Webswing instance is unavailable, or the expected number of components is not found.

Use Playwright waits for page-level transitions such as authentication and initial canvas creation. Avoid fixed delays where a visible locator or application state can be awaited instead.

Existence and visibility

Existence and visibility are intentionally different. A component can exist in the Swing component tree and have visible: true, but be completely clipped by a JScrollPane or JViewport.

Visibility assertions use the effective on-screen geometry returned by Webswing:

  • visible: visible !== false, visibleWidth > 0, and visibleHeight > 0;
  • not visible: the component exists but has no effective visible area;
  • not existing: the selector matches no component.

For example, a component with visible: true and visibleHeight: 0 exists but is not visible to the user.

Multiple canvases and windows

Webswing can render application windows, dialogs, and popup menus using multiple canvases. The helper uses the component's absolute on-screen coordinates and selects an active, non-blocked canvas when validating the interaction point. Tests do not need to select a canvas by index.

Always perform a fresh helper call after opening a menu, dialog, or another window so the component is resolved from the current component tree:

await webswing.webswingClick(page, 'WJMenu[value="File"]')
await webswing.webswingClick(page, 'WJMenuItem[value="Exit"]')

Troubleshooting

  • requestComponentTreeElement is not available: use Webswing 26.2 or later, enable Test Mode, and reload the application.
  • Webswing instance is not available: wait for the application to initialize or configure the correct instance name with setWebswingInstanceName().
  • Expected one match but found zero or multiple components: use Pick to inspect the component and add a stable attribute or parent relationship.
  • The component exists but an action fails: ensure it is enabled, has a positive size, and is not clipped or outside the active canvas.
  • The action uses the wrong window or popup: avoid canvas indexes and resolve the target component again after opening the window.
  • Login fails: use Playwright locators appropriate for your deployment's login or single sign-on flow before calling the helper.
  • Helper import fails: keep webswing-playwright-plugin.js at the path used by require(...).
  • A test is flaky during startup: wait for the Webswing canvas and application state before the first helper call; component lookup itself retries for five seconds.

Browser selection

Playwright runs tests with Chromium by default. Install and select other supported browsers when required:

npx playwright install firefox webkit
npx playwright test --project=firefox
npx playwright test --project=webkit

Browser projects must be defined in playwright.config.js before they can be selected with --project.