Advanced web test patterns: login flows, variable capture and custom metrics

How to handle multi-step logins, capture values from the page into variables, assert against them, and publish custom metrics to VerOps — illustrated with a real-world example.

syntheticsweb-testscredentialsvariablescustom-metricsexecute-jsassertions

This article walks through the features used to build a real-world synthetic web test that logs into Fieldwire, navigates to the People page, counts the number of users, asserts the count is within a threshold, and publishes the count as a VerOps metric. Each section covers one feature with a worked example.

Two-step login flows

Some applications split the login form across two pages — email on the first, password on the second. Model each screen as its own steps.

Step 1 — Navigate to the login page

Action type: Navigate

URL: https://app.fieldwire.com/auth/sign_in?lang=en

Step 2 — Type the email address

Action type: Type

Selector: input[type="email"]

Value: {{credential.Fieldwire.username}}

Step 3 — Click Continue

Action type: Click

Selector: button[type="submit"]

Step 4 — Wait for the password field

After clicking Continue the browser navigates to a new URL. Wait for the password field before proceeding.

Action type: Wait

Wait condition: Element Visible

Selector: input[type="password"]

Step 5 — Type the password

Action type: Type

Selector: input[type="password"]

Value: {{credential.Fieldwire.password}}

Step 6 — Click Sign In

Action type: Click

Selector: button[type="submit"]

Step 7 — Wait for the app to finish loading

Action type: Wait

Wait condition: Ajax Complete

---

Secure credentials

Credentials are stored encrypted in the VerOps vault and injected at run time. They are never stored in the test configuration and are masked in screenshots and logs.

Placeholder syntax

{{credential.CredentialName.field}}

where field is one of username, password, or totp.

Example

{{credential.Fieldwire.username}}

{{credential.Fieldwire.password}}

Allowed hosts — restrict each credential to the domains where it is valid (e.g. app.fieldwire.com). The agent refuses to resolve the placeholder on any other host.

Allow VerOps cloud agents — credentials are restricted to your own self-hosted agents by default. Enable this toggle to allow the credential to be resolved on VerOps cloud agents.

---

Capturing page values with Execute JavaScript

The Execute JavaScript step runs a script in the browser and stores the return value in a named variable. Any string or number returned by the script is captured.

Step 8 — Navigate to the People page

Action type: Navigate

URL: https://app.fieldwire.com/index/people

Step 9 — Wait for the list to render

Action type: Wait

Wait condition: Element Visible

Selector: td.user-name-cell

Step 10 — Count the users

Action type: Execute JavaScript

Variable Name: userCount

  (function() { return document.querySelectorAll('td.user-name-cell').length.toString();  })()

The value stored in userCount is available to all subsequent steps via ${userCount}.

Selector tip — prefer stable attributes over generated class names. data-e2e, data-testid, and semantic class names like user-name-cell survive most application updates. Avoid Angular's _ngcontent-* scoped attributes — they are regenerated with every build.

Publishing a custom metric from a test step

Any numeric value captured during a test can be published to VerOps as a metric via the OTLP HTTP endpoint. The trick is to navigate to the ingest domain before making the call so the fetch is same-origin and no CORS negotiation is required.

Step 11 — Navigate to the ingest domain

Action type: Navigate

URL: https://ingest.verops.io

Step 12 — POST the metric

Action type: Execute JavaScript

  (function() {
    return fetch('/v1/metrics', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer {{credential.VerOpsIngestAPI.password}}'
      },
      body: JSON.stringify({
        resourceMetrics: [{
          scopeMetrics: [{
            metrics: [{
              name: 'fieldwire_user_count',
              gauge: {
                dataPoints: [{
                  asDouble: parseInt('${userCount}'),
                  timeUnixNano: String(Date.now() * 1000000)
                }]
              }
            }]
          }]
        }]
      })
    }).then(function(r) { return r.status.toString(); });
  })()

Store the ingest API key as a credential named VerOpsIngestAPI with ingest.verops.io as the allowed host and Allow

VerOps cloud agents enabled.

Once published, fieldwire_user_count appears in the metrics table and can be used in dashboards and alerts:

  SELECT max(value) AS user_count FROM metrics  WHERE metric_name = 'fieldwire_user_count'  AND timestamp >= now() - INTERVAL 7 DAY

Alerting on the metric

SELECT ifNull(max(value), 0) AS user_count FROM metrics WHERE metric_name = 'fieldwire_user_count' AND timestamp >= now() - INTERVAL 3 HOUR

Set the alert threshold to user_count >= 50 (or your chosen limit). Because the test runs on a schedule, the 3-hour

window ensures the most recent run is always captured.