UI Test Script examples
Summarize
Summary of UI Test Script examples
This guide provides practical examples of UI Test Scripts for ServiceNow’s Automated Test Framework (ATF) in the Australia release. It demonstrates how to automate common UI interactions such as verifying elements, navigating and editing records, handling asynchronous conditions, impersonating users, uploading attachments, and accessing test step outputs and parameters.
Show less
Key Features
- Element Verification: Use role-based selectors like
getByRoleto assert visibility of headings, textboxes, and buttons on both classic and workspace forms. - Record Navigation and Editing: Automate navigation to records, fill fields (e.g., Caller, Short description), and save changes with button clicks, including handling classic form quirks such as duplicated buttons and label associations.
- Waiting for Async Conditions: Use
waitForto poll for conditions like button enablement or element visibility with customizable timeouts, ensuring reliable test execution in asynchronous scenarios. - Impersonation: Run test steps as another user by impersonating via username or sysid, with automatic session restoration after the step completes.
- Attachment Uploads: Upload files to forms by copying existing attachments server-side or uploading new files via REST, including support for workspace records.
- Step Outputs and Parameter Access: Retrieve output variables from previous steps and parameter values from parameterized tests using
steps(sysid)andparams(name)with dotwalking syntax for nested fields. - Scoped Queries: Use
withinto limit element searches to specific page sections to avoid ambiguous matches. - Handling Duplicated Elements: Locate the visible instance when multiple elements share the same text on a page.
Practical Application for ServiceNow Customers
These examples empower customers to create robust UI test scripts that:
- Verify UI elements are present and interactable to ensure expected page states.
- Automate end-to-end scenarios like creating or updating incidents with specific field values.
- Handle asynchronous UI behavior and delays reliably, reducing test flakiness.
- Test functionality under different user roles by impersonating users.
- Validate file upload workflows within forms and workspace contexts.
- Leverage test step outputs and parameters to build dynamic and reusable test cases.
- Reduce ambiguity in element selection by scoping queries and targeting visible elements.
By applying these scripting patterns, customers can enhance the stability, coverage, and maintainability of their automated UI testing within ServiceNow.
Reference example scripts to use in a Run UI Test Script step.
- Element verification,
- filling and saving records,
- waiting for async conditions,
- impersonation,
- file uploads,
- step outputs,
- parameter values,
- and scoped queries.
Assert that a heading is visible on the current page
const heading = screen.getByRole('heading', { name: 'My Application' });
expect(heading).toBeVisible();
Navigate to a record, fill a field, and save
await sn_atf.navigate('/incident.do?sys_id=-1');
// Update Caller
const caller = screen.getByRole('searchbox', { name: 'Caller' });
await user.click(caller);
await user.type(caller, 'Abel Tuter');
await user.tab();
// Update Short description
const short_desc = screen.getByRole('textbox', { name: 'Short description' });
await user.click(short_desc);
await user.type(short_desc, 'Cannot connect to VPN');
// Click Submit
await sn_atf.delay(1000);
const [submitBtn] = screen.getAllByRole('button', { name: 'Submit' });
await user.click(submitBtn);
await waitFor(() => {
const numField = screen.getBySelector('[name="incident.number"]');
expect(numField.value).toMatch(/^INC/);
}, { timeout: 10000 });
Test as an impersonated user
// Accepts a user_name or sys_id. The iframe reloads automatically.
// The original session is always restored when the step ends.
await sn_atf.impersonate('beth.anglin');
await sn_atf.navigate('/now/sow/home');
const listTab = await screen.findByRole('tab', { name: 'List', timeout: 10000 });
await user.click(listTab);
const incidentItem = await screen.findByRole('treeitem', { name: 'Incidents', timeout: 10000 });
expect(incidentItem).toBeVisible();
Upload an attachment to the current form
// Pass a sys_id to copy an existing attachment server-side (no file bytes transferred).
// Pass a File object (from sn_atf.getAttachmentFile) to upload through REST.
await sn_atf.navigate('/incident.do?sys_id=' + steps('PREVIOUS_STEP_SYS_ID').record_id);
await sn_atf.upload(steps('PREVIOUS_STEP_SYS_ID').attachment_sys_id);
await screen.findByText('evidence.pdf');
Wait for an async condition by using waitFor
await sn_atf.navigate('/incident.do?sys_id=<sys_id>');
await screen.findByRole('heading', { name: /INC/ });
const [updateBtn] = screen.getAllByRole('button', { name: 'Update' });
await waitFor(
() => expect(updateBtn).toBeEnabled(),
{ timeout: 10000 }
);
Access step outputs and parameter values
// steps() retrieves output variables from a prior step by its sys_id.
// params() retrieves parameter values when running a parameterized test.
// Both support dotwalking for reference fields, for example
// steps('...').assigned_to.department.name.
// Use == or String() for comparisons — not ===.
const dept = String(steps('PREVIOUS_STEP_SYS_ID').assigned_to.department.name);
const expected = String(params('expected_department'));
expect(dept == expected).toBe(true);
steps(SYS_ID) takes the sys_id of the step record, not the step result. Find it in the URL when you open the step record.
Verify an element on a classic form
await sn_atf.navigate('/incident.do?sys_id=<sys_id>');
const heading = await screen.findByRole('heading', { name: /INC/ });
expect(heading).toBeVisible();
const shortDesc = screen.getByRole('textbox', { name: 'Short description' });
expect(shortDesc).toBeVisible();
Navigate to a record, fill a field, and save
await sn_atf.navigate('/incident.do?sys_id=<sys_id>');
await screen.findByRole('heading', { name: /INC/ });
const shortDesc = screen.getByRole('textbox', { name: 'Short description' });
await user.tripleClick(shortDesc);
await user.type(shortDesc, 'Updated by ATF');
const [updateBtn] = screen.getAllByRole('button', { name: 'Update' });
await user.click(updateBtn);
await screen.findByRole('heading', { name: /INC/ });
On classic forms, findByLabelText can throw a "found multiple elements" error because
ServiceNow uses both <label for> and aria-labelledby associations
at the same time. Prefer getByRole('textbox', { name: '...' }) for text inputs, or
getByRole('searchbox', { name: '...' }) for reference fields. Classic forms also render
every action button — Submit, Update, Delete — in both the header and the footer. Use
getAllByRole and destructure to take the first one.
Wait for an async condition
Use waitFor to poll a condition that is not simply a new element appearing. The callback
is retried until it stops throwing or the timeout expires.
await sn_atf.navigate('/incident.do?sys_id=<sys_id>');
await screen.findByRole('heading', { name: /INC/ });
const [updateBtn] = screen.getAllByRole('button', { name: 'Update' });
await waitFor(
() => expect(updateBtn).toBeEnabled(),
{ timeout: 10000 }
);
Test as an impersonated user
await sn_atf.impersonate('beth.anglin');
await sn_atf.navigate('/now/sow/home');
const listTab = await screen.findByRole('tab', { name: 'List', timeout: 10000 });
await user.click(listTab);
const incidentItem = await screen.findByRole('treeitem', { name: 'Incidents', timeout: 10000 });
expect(incidentItem).toBeVisible();
Upload an attachment
// Copy an existing attachment to the current form (no file bytes transferred)
await sn_atf.navigate('/incident.do?sys_id=<sys_id>');
await sn_atf.upload('<source_attachment_sys_id>');
await screen.findByText('<file_name>;);
Upload to a Workspace record
await sn_atf.navigate('/now/sow/record/incident/<sys_id>');
const result = await sn_atf.uploadInWS('<source_attachment_sys_id>');
expect(result.name).toBe('evidence.pdf');
Use step outputs with dotwalking
// steps(<step_sys_id>) — use the sys_id shown in the step record's URL
const assigneeDept = String(steps('abc123def456abc123def456abc12345').assigned_to.department.name);
expect(assigneeDept).toBe('IT');
Use parameter set values
// In a parameterized test, read values from the active parameter row
await sn_atf.navigate('/incident.do?sys_id=-1');
const shortDesc = screen.getByRole('textbox', { name: 'Short description' });
await user.type(shortDesc, String(params('description')));
const [submitBtn] = screen.getAllByRole('button', { name: 'Submit' });
await user.click(submitBtn);
await waitFor(() => {
const numField = screen.getBySelector('[name="incident.number"]');
expect(numField.value).toMatch(/^INC/);
}, { timeout: 10000 });
Use within to scope queries
// Scope queries to a specific section of the page to avoid ambiguous matches
const form = screen.getBySelector('form');
const shortDesc = within(form).getByRole('textbox', { name: 'Short description' });
expect(shortDesc).toBeVisible();
Find the visible instance of a duplicated element
// The page renders two elements with the same text; find the visible one
const matches = await screen.findAllByText('Number');
const label = matches.find(el => isVisible(el));
expect(label).toBeTruthy();