# Playwright Scripts

> Loadster supports the Playwright Test framework for load testing and monitoring, ideal for developers who prefer writing tests in JavaScript or TypeScript.

Source: https://loadster.com/manual/playwright-scripts/

Playwright scripts let you use standard [Playwright Test](https://playwright.dev/docs/intro) code for
[Playwright load testing](https://loadster.com/use-cases/playwright-load-testing/) and monitoring in Loadster. They're a code-first alternative to [Browser Bots](https://loadster.com/manual/browser-scripts/)
for developers who have Playwright experience or just prefer writing JavaScript tests with the open source framework.

If you've worked with the Playwright Test framework before, you already know how to write Loadster Playwright
scripts! Just write the same JavaScript code you'd write for any Playwright test and Loadster takes care of the rest.

We highly recommend checking out the [Official Playwright Test Documentation](https://playwright.dev/docs/writing-tests)
since most of what you read there will apply in Loadster as well.

## Using Playwright in Loadster

When it comes to load testing and monitoring in Loadster, Playwright scripts are a good fit when:

* You're comfortable writing JavaScript
* You want precise control over browser automation
* You have existing Playwright tests you'd like to reuse
* You prefer writing code over visual editors

For simpler use cases or if you prefer a visual approach, [Browser Bots](https://loadster.com/manual/browser-scripts/) might make it easier
to get up and running quickly. For API testing without browser overhead, [Protocol Bots](https://loadster.com/manual/protocol-scripts/)
are more efficient.

## Playwright Test vs Playwright

Loadster supports **Playwright Test** scripts, not ordinary Playwright browser automation library scripts. In short,
your scripts must use the `test` function from the `@playwright/test` library.

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

test('my test', async ({page}) => {
    await page.goto('https://example.com');
    await expect(page).toHaveTitle(/Example/);
});
```

Scripts that launch browsers manually with `chromium.launch()` won't work here, because Loadster ties into the
Playwright Test framework and manages the browser lifecycle for you.

If you have existing Playwright automation scripts that aren't Playwright Test and want to convert them to the
Playwright Test framework, you can pretty much wrap the automation in a `test()` block and using the provided fixtures.

## JavaScript or TypeScript

You can write Loadster Playwright scripts in either **JavaScript** or **TypeScript**, whichever you prefer. Loadster runs
your script through the Playwright Test runner, which compiles TypeScript automatically—so type annotations, interfaces,
and other TypeScript features work out of the box with no extra configuration.

```typescript
import {test, expect, type Page} from '@playwright/test';

interface Credentials {
    username: string;
    password: string;
}

test('login with typed credentials', async ({page}: {page: Page}) => {
    const creds: Credentials = {username: 'demo', password: 'secret'};

    await page.goto('https://example.com/login');
    await page.fill('#username', creds.username);
    await page.fill('#password', creds.password);
    await page.click('button[type="submit"]');
});
```

Both styles are fully supported, and you can paste an existing JavaScript or TypeScript Playwright test straight into the
editor. There's nothing extra to configure either way.

## Creating a Playwright Script

When you create a new Playwright script in Loadster, it will start with a template containing one empty test case:

```javascript
import {test} from '@playwright/test';

test('main test case', async ({page}) => {
    // TODO: Add your test steps here
});
```

If you define multiple test cases in a single script Loadster will execute them in series, but for
most load testing and monitoring use cases we recommend a single focused test case.

## Editing Playwright Scripts

The Playwright script editor in Loadster provides:

* **A syntax highlighting text editor** for JavaScript or TypeScript with Playwright APIs
* **A Play button** to execute the script in the editor and validate it works
* **A results panel** showing the playback results timeline, screenshots, and traces

Unlike Browser Bot scripts, Playwright scripts are written entirely in code, but you can use them for load testing and
monitoring in much the same way.

## Playing Playwright Scripts

Playing a script in the editor is your chance to verify and debug your script before putting it to use in a load test
or monitor. You can play it in the editor as many times as you need to get it right.

When you play in the editor, Loadster runs the script once with a single bot and reports detailed
diagnostics, giving you an opportunity to find and fix errors.

This is different from running a load test (where many bots execute the script concurrently) or using
the script as a monitor (where a single bot runs on a schedule to check availability). Playing in the editor is for
development and troubleshooting.

Click the **Play** button to execute your script. Loadster will:

1. Save any pending changes
2. Execute the script on a cloud engine
3. Stream results back in real time
4. Display each test case as a card in the results timeline

While running, you'll see:

* Test case names and their status (running, passed, failed)
* Execution time for each test
* Screenshots captured during execution
* Any errors or failures with details

Always play through your script in the editor after writing or making changes to make sure it runs as intended.

## Viewing Script Results

After execution, each test case displays as a result card showing its name, status, execution time, and a screenshot
thumbnail (if available). Click on any result card to expand it and view full details.

### Result Details

* **Status** — Whether the test passed or failed
* **Duration** — How long the test took to execute
* **Error details** — Stack traces and failure messages for failed tests

### Screenshots

Playwright automatically captures screenshots during test execution. These appear in the Screenshots tab of the result
modal. You can also explicitly capture screenshots in your code:

```javascript
await page.screenshot({path: 'screenshot.png'});
```

### Video Recording

In most cases, when you play from the script editor a video recording of the test execution are available in the
Videos tab.

### Playwright Traces

Loadster integrates with Playwright's Trace Viewer. Click on a test case result card to open it and inspect:

* Timeline of actions
* DOM snapshots before/after each action
* Network requests
* Console logs

### Page Timings

If your Playwright test navigates to a page, you can view a Page Timings tab alongside the trace. This shows
navigation timing details including Time to First Byte (TTFB), First Contentful Paint (FCP), Largest Contentful
Paint (LCP), and Cumulative Layout Shift (CLS) for each navigation in the test case.

### Console Output

The Console tab shows all log output from your test, including `console.log()` statements and Playwright's internal
logging.

## Using Script Variables

You can access Loadster [Datasets](https://loadster.com/manual/dynamic-datasets/) in your Playwright scripts using the
[`@loadster/bot` module](https://loadster.com/manual/playwright-scripts/bot/). This module provides a `getVariable()` function that
retrieves values from CSV datasets attached to your script.

```javascript
import {test, expect} from '@playwright/test';
import bot from '@loadster/bot';

test('login with unique credentials', async ({page}) => {
    // Get username and password from a multi-column dataset
    const [username, password] = bot.getVariable('credentials');

    await page.goto('https://example.com/login');
    await page.fill('#username', username);
    await page.fill('#password', password);
    await page.click('button[type="submit"]');
});
```

The `@loadster/bot` module also provides methods for identifying the current bot and iteration, which is useful for
logging or implementing bot-specific behavior. See the [@loadster/bot Reference](https://loadster.com/manual/playwright-scripts/bot/) for
the full API.

Note that Loadster's standard [Variables and Expressions](https://loadster.com/manual/variables-and-expressions/) syntax (like
`${variableName}`) doesn't work directly in Playwright scripts – you need to use the `@loadster/bot` module instead.

## Using Playwright Scripts for Load Testing

Playwright scripts can be added to load test scenarios just like Protocol Bot or Browser Bot scripts:

1. Create or open a [load test scenario](https://loadster.com/manual/load-test-scenarios/)
2. In the population editor, selectyour Playwright script
3. Configure the number of bots and ramp settings
4. [Run the load test](https://loadster.com/manual/running-load-tests/)

Each bot in a load test executes the Playwright script independently, simulating real browser traffic at scale. This is
useful for:

* Testing single-page applications under load
* Validating that user journeys work under concurrent usage
* Measuring real browser rendering and JavaScript execution times

## Using Playwright Scripts for Monitoring

Playwright scripts work seamlessly for [site monitoring](https://loadster.com/manual/monitoring/):

1. Create a new monitor
2. Select your Playwright script
3. Configure the check interval and locations
4. Set up alerting thresholds and escalations

The monitor will execute your Playwright script at the configured interval from your selected locations. You'll see:

* Pass/fail status for each check cycle
* Response time trends
* Screenshots from each execution
* Full result details including traces

This is particularly useful for:

* Monitoring JavaScript-heavy applications
* Validating complete user flows (login, checkout, etc.)
* Detecting visual or functional regressions
* Checking that third-party integrations work correctly

## Playwright Best Practices in Loadster

### Keep Your Tests Focused

Each Playwright script should test a specific user journey or feature. Smaller, focused tests are easier to debug and
provide clearer metrics.

When a focused test fails, you know exactly which user journey has a problem. When a sprawling test that covers many
different flows fails, you have to dig through the results to figure out what actually went wrong. Focused tests also
give you more granular response time metrics in your load test results—you can see exactly how long each specific
action takes rather than just getting one big number for the whole script.

If you want to test multiple behaviors together you can include multiple test cases. You can
also run multiple bot groups in a load test with each running a different script.

### Use Meaningful Test Names

Test names appear in results and monitoring dashboards, so descriptive names make it much easier to understand what's
happening at a glance—especially when you're troubleshooting a failure at 3am or reviewing load test results weeks
later.

```javascript
// Good - you know exactly what this test does
test('complete checkout with credit card', async ({page}) => {
});

// Less helpful - what does this even test?
test('test 1', async ({page}) => {
});
```

### Handle Dynamic Content

Modern web applications often load content asynchronously, which means elements might not be immediately available when
the page first loads. Using Playwright's built-in waiting mechanisms makes your tests more reliable than using fixed
delays.

```javascript
// Good - waits for specific element, proceeds as soon as it appears
await page.waitForSelector('.product-loaded');

// Avoid - arbitrary delay that might be too short (causing failures) or too long (wasting time)
await page.waitForTimeout(5000);
```

Fixed delays are problematic for two reasons. If the page loads faster than the delay, you're wasting time on every
iteration—and that adds up quickly in a load test with hundreds of bots. If the page loads slower (maybe because it's
under load), the test fails even though the application might still be working correctly. Waiting for specific elements
handles both cases gracefully.

### Clean Up State Afterwards

If your test creates data (like a new user account, a saved item, or a draft document), consider cleaning it up at the
end of the test. This prevents "test pollution" where leftover data from previous runs affects future runs.

```javascript
test('create and delete item', async ({page}) => {
    // Create item
    await page.click('button.create');

    // Verify and clean up
    await page.click('button.delete');
});
```

This matters in load testing or monitoring if another bot will be trying to do the same thing soon afterwards and
expects a clean state.

### Use Assertions

Playwright Test includes the `expect` API for assertions. Assertions verify that the application is actually behaving
correctly, not just that it didn't crash.

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

test('homepage loads correctly', async ({page}) => {
    await page.goto('https://example.com');
    await expect(page).toHaveTitle(/Example/);
    await expect(page.locator('h1')).toBeVisible();
});
```

Without assertions, a test might "pass" even when something is wrong. For example, if your login page accidentally
redirects to an error page, a test without assertions would happily report success because no exception was thrown.
Adding assertions like `await expect(page).toHaveURL(/dashboard/)` catches these problems. In load testing and
monitoring, assertions help distinguish between "the server responded" and "the server responded correctly".

## Timeout Settings

By default, Loadster's Playwright runtime uses a 30-second timeout for navigations and a 15-second timeout for actions
like clicks, fills, and assertions. You can adjust these defaults in the
<a href="https://loadster.com/dashboard/settings?tab=playwright-bots">Playwright Bots</a> section of Settings.

* **Navigation Timeout** — The maximum time allowed for page navigations (`page.goto()`, `page.waitForNavigation()`,
  etc.) to complete. Default: 30000 ms.
* **Action Timeout** — The maximum time allowed for actions like `page.click()`, `page.fill()`, and `expect()`
  assertions to complete. Default: 15000 ms.

These settings apply to all Playwright scripts across your team. If your site is slower to load or you're testing
against a site under heavy load, increasing the navigation timeout can prevent false failures. On the other hand,
if you want faster feedback when something is wrong you can lower the timeouts.

## Limitations

* **No recording.** Unlike Browser Bot scripts, Playwright scripts are written manually. You can use Playwright's
  [codegen tool](https://playwright.dev/docs/codegen) externally and paste the result into Loadster.
* **Single code block.** Each Playwright script contains one code editor, not multiple visual steps like browser
  scripts. A Loadster Playwright script is a single self-contained file, so patterns that normally span multiple files
  (like the Page Object Model) need to live in that one file. See [Organizing Playwright Scripts](https://loadster.com/manual/playwright-scripts/organizing-scripts/)
  for how to structure larger scripts and adapt an existing test suite.

## Playwright Test Script Examples

Here are a few examples of Playwright Test scripts, just so you can get an idea. You can find many more examples
elsewhere, and LLMs are quite good at it too!

### Playwright Basic Navigation Example

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

test('homepage loads and displays welcome message', async ({page}) => {
    await page.goto('https://example.com');

    await expect(page.locator('h1')).toContainText('Welcome');
    await expect(page.locator('.hero-image')).toBeVisible();
});
```

### Playwright Form Submission Example

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

test('contact form submission', async ({page}) => {
    await page.goto('https://example.com/contact');

    await page.fill('input[name="email"]', 'test@example.com');
    await page.fill('textarea[name="message"]', 'Hello from Loadster!');
    await page.click('button[type="submit"]');

    await expect(page.locator('.success-message')).toBeVisible();
});
```

### Playwright Authentication Flow Example

```javascript
import {test, expect} from '@playwright/test';
import bot from '@loadster/bot';

test('user can log in', async ({page}) => {
    const [username, password] = bot.getVariable('credentials');

    await page.goto('https://example.com/login');

    await page.fill('input[name="username"]', username);
    await page.fill('input[name="password"]', password);
    await page.click('button[type="submit"]');

    await expect(page).toHaveURL(/dashboard/);
    await expect(page.locator('.user-menu')).toContainText(username);
});
```

### Playwright User Journey Example

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

test('complete purchase flow', async ({page}) => {
    // Browse to product
    await page.goto('https://shop.example.com');
    await page.click('.product-card >> nth=0');

    // Add to cart
    await page.click('button.add-to-cart');
    await expect(page.locator('.cart-count')).toContainText('1');

    // Go to checkout
    await page.click('a.checkout');
    await expect(page).toHaveURL(/checkout/);

    // Fill shipping info
    await page.fill('input[name="address"]', '123 Test St');
    await page.fill('input[name="city"]', 'Test City');
    await page.click('button.continue');

    // Verify order summary
    await expect(page.locator('.order-total')).toBeVisible();
});
```

