Functions
Functions are reusable code components that can be called from your tests, providing powerful capabilities for complex operations and integrations.
Table of Contents
- Creating and Managing Functions
- Available Parameters
- Function Editor Features
- Importing Packages
- Common Use Cases
- Using Functions in Tests
- Function Testing
- Function Status
- Limitations
- Best Practices
Creating and Managing Functions
Functions are created and managed from the Functions sidebar menu (not from within tests):
- Navigate to Functions in the sidebar
- Click Create Function to open the function editor
- 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:
| Parameter | Type | Description |
|---|---|---|
page | Playwright Page | The current browser page |
testContext | Shiplight variable store | Read and write test variables. Aliases: $, ctx |
request | Playwright APIRequestContext | For making HTTP requests |
The following are available as globals inside function code (no need to add them to the function signature):
| Variable | Type | Description |
|---|---|---|
agent | Shiplight WebAgent | AI-powered actions — assertions, natural language step execution |
expect | Playwright assertions | For writing assertions |
console | Captured console | Logs appear in the test run output |
Your function signature should always start with the pre-defined parameters, followed by any custom parameters:
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.
// 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.
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.
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.
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.
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:
| Package | Description |
|---|---|
pdf-parse | Extract text from PDF files |
mammoth | Convert Word documents (.docx) to text or HTML |
exceljs | Read 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:
const mod = await import("some-package");
const lib = mod.default;Common Use Cases
API Integrations
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
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
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;
}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)
- Insert a new statement in your test
- From the dropdown menu, switch to Function (or press
Ctrl + Alt + F) - Pick a function from the list
- 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:
await get_order_details(page, testContext, request, $.orderId);
console.log($.orderTotal); // Access stored valueAccessing Function Results
Important: Functions cannot directly return values to tests. Instead, store values in testContext:
// 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
importstatements are not supported. Useawait 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
testContextto 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 likewindow,document, orlocalStorage.
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
testContextfor output — store all results intestContextso 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.