Create a new synthetic test

edit

Syntax

edit

[beta] This functionality is in beta and is subject to change. The design and code is less mature than official GA features and is being provided as-is with no warranties. Beta features are not subject to the support SLA of official GA features. To write synthetic tests for your application, you’ll need to know basic JavaScript and Playwright syntax. Synthetics agent exposes a an API for creating and running tests:

  • journey(name, ({ page, browser, client, params }) => {}) — A journey tests one discrete unit of functionality. For example, logging into a website, adding something to a cart, or joining a mailing list. Each journey provides a fresh playwright browser, context, and page instance. See Create a journey for more information.
  • step(name, function) — A journey consists of multiple steps, i.e., actions that should be completed in a specific order. Steps are displayed individually in the Uptime app for easy debugging and error tracking.
  • beforeAll(({ params }) => {}) — Runs a provided function prior to all journey runs. If the provided function is a promise, the runner will wait for the promise to resolve before invoking the journey. This function is useful for setting up global state or a server that will be used across multiple journeys.
  • before(({ params }) => {}) — Runs a provided function prior to a single journey runs. This function is useful for setting up global state or a server that will be used for a single journey.
  • afterAll(({ params }) => {}) — Runs a provided function after all the journeys have completed. This function is useful for removing global state or closing a server that was used in multiple journeys.
  • after(({ params }) => {}) — Runs a provided function after a single journey has completed. This function is useful for removing global state or closing a server that was used in a single journey.

Playwright is browser testing library developed by Microsoft. It is reliable and fast and features a modern API that auto waits for page elements to be ready.

Create a journey
edit

The journey function takes a single parameter, name, and provides access to page, params, browser, and context:

  • browser — A browser object created by Playwright.
  • page — A page object from Playwright that lets you control the browser’s current page.
  • context — A browser context that doesn’t share cookies or cache with other browser contexts.
  • params — User-defined variables that allow you to invoke the Synthetics suite with custom parameters. For example, if you want to use a different homepage depending on the env (localhost for dev, and a URL for prod). See Working with parameters and secrets for more information.

Putting it all together, a basic, two step journey might look something like this:

journey("Journey name", ({ page }) => {
    step("Step 1 name", () => {
      // Do something here
    })
    step("Step 2 name", () => {
      // Do something else here
    })
});
Create a step
edit

Steps can be as simple or complex as you need them to be. The Playwright page API reference will be your friend while writing tests.

A basic first step might simply load a page:

await page.goto('https://elastic.github.io/synthetics-demo/'); 

See the page.goto reference for more information.

A more complex second step might wait for a page element to be selected, and then ensure that it matches an expected value. Consider the following HTML snippet:

<header class="header">
  <h1>todos</h1>
  <input class="new-todo"
    autofocus autocomplete="off"
    placeholder="What needs to be done?">
</header>

You can verify that new-todo class element contains the expected placeholder (input hint) with the following test:

step("Check placeholder text", async () => {
  const placeholderValue = await page.getAttribute(
      'input.new-todo',
      'placeholder'
  ); 
  expect(placeholderValue).toBe('What needs to be done?'); 
})

Query the page for the input.new-todo selector and get the placeholder attribute value. See the page.getAttribute reference for more information.

Use the assertion library provided by the Synthetics agent to look for the expected value. See Jest expect docs for more information.

Sample Synthetic test
edit

A complete example of a basic synthetic test looks like this:

import { journey, step, expect } from '@elastic/synthetics';

journey("Ensure placeholder is correct", ({page}) => {
    step("go to todos demo page", async () => {
      await page.goto('https://elastic.github.io/synthetics-demo/');
    })
    step("assert placeholder text", async () => {
      const placeholderValue = await page.getAttribute(
        'input.new-todo',
        'placeholder'
      );
      expect(placeholderValue).toBe('What needs to be done?');
    })
});

Running synthetic tests

edit

As explained in the quickstart, there are two ways to run synthetic tests:

  • Run as an inline journey — embed into heartbeat.yml
  • Run a test suite — import your tests into Heartbeat

Which option is right for you? That depends. If you want to invoke a single journey with few steps, an inline journey might be the easiest workflow — you only need to write your tests into your heartbeat.yml file.

Note that you can only paste the steps into an inline script, so these cannot be dependent on any packages that you may want to import. If you do have any dependencies, these would need to be defined with an import outside of the journey object and run via a test suite. If you have a complex test suite, or your tests need to live with your application code, you’re likely better off setting up a synthetic test suite.

Run an inline journey
edit

The easiest way to run a synthetic test is by creating an inline journey. The journey keyword isn’t required, and access to variables like page and params is automatic. Remember you cannot import any dependencies using inline scripts.

Copy and paste your test steps into heartbeat.yml. Heartbeat spawns a separate Node.js process, schedules your tests, and runs them on a chromium browser. You don’t need to worry about anything else.

An example, short.js, is provided in the elastic/synthetics GitHub repository:

// test-homepage-hover.js
step("load homepage", async () => {
    await page.goto('https://www.elastic.co');
});
step("hover over products menu", async () => {
    await page.hover('css=[data-nav-item=products]');
});

To run this, or any other inline example locally, change into the directory of your test, and pipe the file contents to the npx @elastic/synthetics command.

For example:

cat examples/inline/short.js | npx @elastic/synthetics --inline

If everything works as expected, you’ll get the similar response:

Journey: inline
   ✓  Step: 'load homepage' succeeded (1831 ms)
   ✓  Step: 'hover over products menu' succeeded (97 ms)

 2 passed (2511 ms)

The script can then be copied into your in your heartbeat.yml:

heartbeat.monitors:
- type: browser
  id: elastic-website
  name: Elastic website
  schedule: "@every 1m"
  source:
    inline:
      script: |-
        step("load homepage", async () => {
            await page.goto('https://www.elastic.co');
        });
        step("hover over products menu", async () => {
            await page.hover('css=[data-nav-item=products]');
        });

That’s it! You can either spin up Heartbeat yourself, or jump to Step 3: Run the container, connecting it to Elasticsearch of the Quickstart to use the provided Docker project template.

Run a test suite
edit

If you have a suite of tests you’d like to implement, you can use Elastic synthetics as a library. In this method, you use Docker to run both Heartbeat and elastic-synthetics.

Install the @elastic/synthetics package globally to get started:

npm install -g @elastic/synthetics

Now it’s time to write your tests:

  1. Create a new NPM/Node.js project.
  2. Create a javascript or typescript file that imports your tests. All synthetic test files must use the .journey.ts or .journey.js file extension.
  3. Compile everything together.

At Elastic, we’re fans of examples, so one is provided in the elastic/synthetics repository. If you’d like to test it locally, clone the repo, and install the example:

# Check out the synthetics repo and included examples
git clone git@github.com:elastic/synthetics.git &&\
cd synthetics/examples/todos/ &&\
# Install all required dependencies for the todos example
npm install

You are now inside the a synthetics test-suite, which is also an NPM project. You can now run the provided tests; Note that, by default, only files matching the filename *.journey.(ts|js)* will be run.

# Run tests on the current directory. Please note the dot `.` which indicates
# that we want to run tests on the current directory
npx @elastic/synthetics .

Once you have your tests up and running, follow the steps in the quickstart guide to integrate with the provided Docker project template.