Skip to content

Functions

Functions are reusable code components that can be called from your tests, providing powerful capabilities for complex operations and integrations.

Table of Contents

  1. Creating and Managing Functions
  2. Available Parameters
  3. Function Editor Features
  4. Importing Packages
  5. Common Use Cases
  6. Using Functions in Tests
  7. Function Testing
  8. Function Status
  9. Limitations
  10. Best Practices

Creating and Managing Functions

Functions are created and managed from the Functions sidebar menu (not from within tests):

  1. Navigate to Functions in the sidebar
  2. Click Create Function to open the function editor
  3. Define your function with:
    • Name - Unique identifier for the function
    • Description - What the function does
    • Code - The function implementation
    • Test Code - "Try it out" test to validate the function

Available Parameters

Every function receives three pre-defined parameters that are automatically passed when the function is called:

ParameterTypeDescription
pagePlaywright PageThe current browser page
testContextShiplight variable storeRead and write test variables. Aliases: $, ctx
requestPlaywright APIRequestContextFor making HTTP requests

The following are available as globals inside function code (no need to add them to the function signature):

VariableTypeDescription
agentShiplight WebAgentAI-powered actions — assertions, natural language step execution
expectPlaywright assertionsFor writing assertions
consoleCaptured consoleLogs appear in the test run output

Your function signature should always start with the pre-defined parameters, followed by any custom parameters:

javascript
async function my_function(page, testContext, request, customParam1, customParam2) {
  // Use page for browser interactions
  await page.click("#button");

  // Use request for API calls
  const response = await request.get("https://api.example.com/data");

  // Use testContext to store/retrieve variables
  testContext.myValue = "stored value";
  $.anotherValue = "also stored"; // $ is an alias for testContext

  // Use agent (available as global)
  await agent.assert(page, "The button was clicked successfully");
}

Working with testContext

testContext (also $ and ctx) is a proxy to the test's variable store. Values set here are available in all subsequent steps — including natural language steps — for the duration of the test run.

javascript
// Write a variable
testContext.orderId = "12345";

// Read a variable
const id = testContext.orderId;

// Write a sensitive value (masked in logs)
testContext.set("apiToken", "secret-value", true);

// Read explicitly
const token = testContext.get("apiToken");

Using agent

The agent object gives you access to Shiplight's AI capabilities from inside a function.

agent.assert(page, statement) — AI-powered assertion. The agent looks at the current page and verifies whether the statement is true.

javascript
await agent.assert(page, "The shopping cart shows 3 items");

agent.execute(page, statement) — Execute a natural language instruction. The agent reads the page and performs the described action, just like a natural language step.

javascript
await agent.execute(page, "Fill out the registration form with test data");

agent.extract(page, description, variableName) — Extract a value from the page using AI and store it in testContext.

javascript
await agent.extract(page, "the order confirmation number", "orderNumber");

Function Editor Features

The function editor provides a tabbed interface for managing your functions:

Editor Tabs

The function editor includes three main tabs:

  • Overview - The main editing interface where you define your function:

    • Split-pane interface - Code editor on left, live browser view on right
    • Live browser preview - If your function operates on the page, see real-time results
    • "Try it out" testing - Write and run test code that calls your function
    • Status management - Draft or Active
  • Usage - View which of your tests are using this function

  • Settings - Configure function-specific settings and preferences, such as selecting the environment to run test code

Importing Packages

You can use await import() to import Node.js built-in modules and pre-installed npm packages inside a function.

javascript
async function read_downloaded_pdf(page, testContext, request) {
  const fs = await import("node:fs");
  let pdfParse = await import("pdf-parse");
  pdfParse = pdfParse.default || pdfParse;

  const filePath = agent.getRecentDownloadedFilePath();
  const buffer = fs.readFileSync(filePath);
  const result = await pdfParse(buffer);
  testContext.pdfText = result.text;
}

Pre-installed Packages

The following third-party packages are available out of the box:

PackageDescription
pdf-parseExtract text from PDF files
mammothConvert Word documents (.docx) to text or HTML
exceljsRead and write Excel files (.xlsx)

Node.js built-in modules (fs, path, crypto, etc.) are always available.

Dynamic import only

Static import statements (e.g., import fs from 'fs') are not supported — only await import() works. This is because function code runs as an inline script, not as an ES module.

CommonJS packages

Some older packages only expose a default export when imported this way. If destructuring doesn't work, access the module via .default:

javascript
const mod = await import("some-package");
const lib = mod.default;

Common Use Cases

API Integrations

javascript
async function get_order_details(page, testContext, request, orderId) {
  const response = await request.get(`${testContext.apiUrl}/orders/${orderId}`, {
    headers: { Authorization: `Bearer ${testContext.apiToken}` },
  });
  const data = await response.json();

  // Store results in test context for subsequent steps to access
  $.orderData = data;
  testContext.orderId = data.id;
  $.orderStatus = data.status;
}

Complex Operations

javascript
async function authenticate_with_mfa(page, testContext, request, username, password) {
  await page.fill("#username", username);
  await page.fill("#password", password);
  await page.click("#login");

  // Wait for MFA prompt
  await page.waitForSelector("#mfa-code");

  // Generate TOTP code from secret key
  const crypto = await import("node:crypto");
  const mfaCode = generateTOTP(testContext.get("otp_secret_key"), crypto);
  await page.fill("#mfa-code", mfaCode);
  await page.click("#verify");

  // Verify login succeeded
  await agent.assert(page, "The user is logged in and on the dashboard");
}

File Processing

javascript
async function verify_excel_report(page, testContext, request) {
  await agent.waitForDownloadComplete(page, 10);
  const filePath = agent.getRecentDownloadedFilePath();

  let ExcelJS = await import("exceljs");
  ExcelJS = ExcelJS.default || ExcelJS;
  const workbook = new ExcelJS.Workbook();
  await workbook.xlsx.readFile(filePath);

  const sheet = workbook.getWorksheet(1);
  testContext.rowCount = sheet.rowCount;
  testContext.headers = sheet.getRow(1).values;
}
javascript
async function verify_word_document(page, testContext, request) {
  await agent.waitForDownloadComplete(page, 10);
  const filePath = agent.getRecentDownloadedFilePath();

  let mammoth = await import("mammoth");
  mammoth = mammoth.default || mammoth;
  const result = await mammoth.extractRawText({ path: filePath });
  testContext.documentText = result.value;
}

Using Functions in Tests

Once created, functions can be called from your tests in two ways:

Using the Function Statement (Easy)

  1. Insert a new statement in your test
  2. From the dropdown menu, switch to Function (or press Ctrl + Alt + F)
  3. Pick a function from the list
  4. Fill in the parameters

This is the recommended approach for most users.

Using a Code Block (Programmatic Way)

Create a Code step and call the function directly by name:

javascript
await get_order_details(page, testContext, request, $.orderId);
console.log($.orderTotal); // Access stored value

Accessing Function Results

Important: Functions cannot directly return values to tests. Instead, store values in testContext:

javascript
// In your function:
async function get_order_details(page, testContext, request, orderId) {
  const response = await request.get(`/api/orders/${orderId}`);
  const data = await response.json();

  // Store results in test context instead of returning
  $.orderTotal = data.total;
  $.orderStatus = data.status;
  ctx.orderItems = data.items;
}

Function Testing

Each function includes a "Try it out" section where you can:

  • Write test code that calls your function
  • Execute the test to verify function behavior
  • See live browser results if the function interacts with pages
  • Debug and refine your function implementation

Function Status

Functions have two status levels:

  • Draft - In development, can be edited and tested
  • Active - Ready for use in tests

Limitations

  • Dynamic import only. Static import statements are not supported. Use await import() instead. See Importing Packages.
  • Pre-installed packages only. You can only import packages that are already available in the Shiplight runtime. You cannot install arbitrary npm packages.
  • No return values. Functions cannot return values to tests. Use testContext to pass data between functions and steps.
  • Node.js context only. Function code runs on the Node.js side, not in the browser. Use page.evaluate() to access browser globals like window, document, or localStorage.

Best Practices

  • Keep functions focused — each function should do one thing well with a clear input/output contract.
  • Document your functions — write clear descriptions and document expected parameters.
  • Use testContext for output — store all results in testContext so subsequent steps can access them.
  • Test thoroughly — use the "Try it out" feature to validate, including edge cases and error conditions.
  • Handle errors — throw meaningful errors so test failures are easy to diagnose.

Released under the MIT License.