Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>RuvioltaNew to Visual Studio Code? Get it now.
Ruviolta

Ruviolta

Ruviolta

|
5 installs
| (1) | Free
Language support, diagnostics, formatting, UI + API syntax, multi-project flows, and Stepflow Debugger integration for Ruviolta .ut test files.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Ruviolta

Language support, diagnostics, formatting, UI + API syntax, and multi-project commands for Ruviolta .ut test files in Visual Studio Code.

Features

  • Syntax highlighting for UI commands, API requests, expectations, protocol helpers, flow commands, and runtime response values
  • Full JavaScript expression highlighting after let
  • Embedded JavaScript highlighting inside script { } blocks
  • Ruviolta syntax diagnostics
  • JavaScript syntax and undefined identifier diagnostics
  • Multiline variable declarations
  • Code snippets
  • Structural document formatting with a Ruviolta editor-title Format button
  • Lightweight automatic indentation while entering new DSL lines
  • Hover information for Ruviolta commands
  • Run the current Ruviolta file with its own project configuration
  • Debug UI, API, and mixed flows step by step with editable Retry and Apply to code
  • Highlight, format, diagnose, and document reusable run() flows with explicit inputs and -> output capture
  • Multiline API command diagnostics and formatting
  • API, GraphQL, SOAP, OAuth 2.0, polling, WebSocket, SSE, and Cleanup: hover/snippet support
  • Optional Cases: tables, safe literal datasets, and project-bound JSON/CSV case sources with highlighting, diagnostics, formatting, hover, and snippets
  • Open the latest report for the active project
  • Project-aware Run, Debug, and Stop editor-title controls
  • Ruviolta Dark and Ruviolta Light color themes

Multi-project layout

projects/
├── customer-portal/
│   ├── config/
│   │   └── ruviolta.config.mjs
│   ├── tests/
│   │   └── login.ut
│   └── reports/
│
└── admin-portal/
    ├── config/
    │   └── ruviolta.config.mjs
    ├── tests/
    │   └── search.ut
    └── reports/

The extension locates the closest project configuration for the active .ut file. It recomputes the owning project after editor, file, workspace, and configuration changes. Files without a resolvable project are not guessed.

File example

File Description: Login-related tests
  Global Variables:
            let username = "test-user"

Scenario: User logs in successfully
  Variables:
            let message = `Logging in as ${username}`

        visit("/login")
        input("#username", username)
        click("#login")

        script {
          await Promise.resolve()
          console.log(message)
        }

Formatting

Use Ruviolta: Format File, the blue Format button in the editor title, or VS Code's standard Format Document action. All three use the same structural formatter:

  • tags, File Description:, and Scenario: use 0 spaces;
  • section headings use 2 spaces;
  • declarations inside Global Variables: and Variables: use 12 spaces;
  • scenario, Verify, Cases, and Cleanup content uses 8 spaces.

The editor adjusts only the active line after Enter or completion of a structural token such as a section heading, let, command call, Cases row, or script {. It does not reformat the document after ordinary character input. Strings, XPath, CSS, JavaScript expressions, inline comments, and command chains are preserved.

JavaScript expressions

let total = price * quantity
let status = total >= 100 ? "large" : "small"

let activeUsers = users
  .filter(user => user.active)
  .map(user => user.name)

let response = await loadData()

Full JavaScript statements such as if, for, while, and try belong inside a script { } block.

External files

let text = readText("data/message.txt")
let data = readJson("data/user.json")
let rows = readCsv("data/users.csv")
let bytes = readBytes("data/file.bin")
let value = env("VARIABLE_NAME", "fallback")
let helpers = await importJs("helpers/functions.js")

Relative paths are resolved from the current .ut file.

Data cases

Variables:, Cases:, and Cleanup: are optional. Cases: runs the complete scenario independently for each row or object, and Cleanup runs once per case.

Scenario: Verify roles
Cases:
| role    | expectedStatus |
| admin   | 200            |
| student | 403            |

api("GET", "/permissions", { query: { role } })
expectStatus(expectedStatus)

The extension also supports Cases: readJson("data/roles.json"), Cases: readCsv("data/roles.csv"), and safe literal object arrays without converting one form into another. The formatter preserves the document line ending and never inserts an absent optional section.

API commands

The extension understands the Ruviolta API DSL in the same .ut files used for browser automation. Multiline request objects are highlighted, formatted, and syntax-checked as JavaScript expressions.

api("POST", "/users", {
  auth: { type: "bearer", token },
  json: { name: userName }
})
expectStatus(201)
expectJson("$.name", userName)
expectExists("$.id")
expectContainsOnly("$.roles", ["admin", "teacher"])
expectSchema("$.user", { id: "integer", name: "string" })

The response runtime values recognized by highlighting and diagnostics are:

response
responseStatus
responseHeaders
responseBody
responseText
responseTime
request

Supported API and protocol commands include:

api()
expectStatus()
expectHeader()
expectCookie()
expectJson()
expectJsonNotEqual()
expectExists()
expectMissing()
expectContains()
expectNotContains()
expectContainsAny()
expectContainsOnly()
expectCount()
expectSchema()
expectEach()
expectResponseTime()
expectText()
graphql()
soap()
poll()
oauth2()
websocket()
wsSend()
wsReceive()
wsClose()
sse()
sseReceive()
sseClose()
output()

wsReceive() and sseReceive() can capture values with ->:

wsReceive("feed", { match: /update/, count: 3 }) -> updates
sseReceive("events", { match: { event: "status" }, count: 2 }) -> statuses

Mixed API + UI

API and UI steps can share variables in one scenario:

Scenario: Create through API and verify in UI
Variables:

api("POST", "/users", { json: { name: "Test User" } })
expectStatus(201)
let userId = responseBody.id

visit("/users/" + userId)
waitForText("h1", "Test User")

Cleanup:
api("DELETE", "/users/{id}", { path: { id: userId } })
expectStatus(204)

Cleanup: is highlighted, formatted, diagnosed, and documented. It belongs to the scenario and is executed by Ruviolta even when a main API or UI step fails.

For authentication bridges, request options can use browserCookies: true to send the active browser session cookies and syncCookiesToBrowser: true to apply response cookies to the browser.

API-only tests can also be debugged. Ruviolta uses its standalone Stepflow Debugger window while no tested page exists; when a mixed flow reaches a browser step, the normal injected debugger panel continues in the tested page. API request/response and protocol details are shown in redacted form.

Browser commands

visit("/")
refresh()
scrollTo("#save")
upload("#avatar", "data/photo.png")

check("#terms")
uncheck("#newsletter")
select("important option text")
select("//select[@name='country']", "Canada")
selectRepeated(
  "//tr[@id='row-{{current}}-0']//select[contains(@class, 'courseSelect')]",
  [1, 2, 3, 8],
  ["86", "26", "27", "125436"]
)
inputRepeated(
  "//tr[@id='row-{{current}}-0']//input[contains(@class, 'weeks1')]",
  [1, 2, 3, 8],
  "18"
)

waitForText("#status", "completed", 10000)
waitUntilMissing(".loading", 10000)
waitForEnabled("#submit", 8000)
waitForDisabled("#locked", 8000)
waitForHidden(".overlay", 8000)
waitForChecked("#terms", 8000)
waitForUnchecked("#newsletter", 8000)

waitForUrl("/dashboard", 15000)
waitForUrlContains("/students/", 15000)
waitForTitle("Student profile", 15000)

hover("#profile-menu")
radio("input[name='role'][value='teacher']")

inputByLabel("Name", firstName)
inputByLabel("Phone", phoneNumber)
clickByLabel("Account:", "//button[normalize-space()='Select']")

pageDown()
pageUP()

clickRepeated("//button[.='Save and continue']", 5)
waitFor("#save").clickRepeated(3)
waitForLink("Add").clickRepeated(5)
waitForButton("Save").clickRepeated(5)
clickLink("read more")
clickButton("save")

waitForLink("Add").click()
waitForLink("//a", "Add", [2], 5000).click()
waitForButton("Save", [2], 3000).click()
waitForButton("button.action", "Save", [4], 3000).click()

click("#dialog-button")
waitForDialog(10000)
dialog(accept)

cookies(accept)
waitToastMessage("success", 10000)
closeToastMessage("success")
closeAllToasts()

CSS selectors and XPath expressions are supported where a locator is expected. inputByLabel() finds an editable input or textarea by exact visible label text. clickByLabel() uses the label as a readable anchor and scopes the supplied target locator to nearby containers. closeAllToasts() is best effort and never fails when no toast can be closed.

waitForLink() and waitForButton() accept exact whitespace-normalized text or a recognized CSS/XPath locator. The optional [n] occurrence is 1-based ([1] is the first match) and must appear before an optional millisecond timeout. A locator plus exact text is also supported, and both commands provide fresh locator context to chained actions.

All other conditional UI waits also accept an optional final positive integer timeout in milliseconds. Without it they use the project timeout; with it only that wait is overridden. This includes waitFor(), waitForVisible(), text/value/attribute/property/count waits, missing/state waits, URL/title waits, waitForDialog(), and waitToastMessage(). Timeout variables and expressions are validated after evaluation. wait(milliseconds) remains an unconditional delay and is unchanged.

clickRepeated(locator, count) performs intentional repeated clicks using the existing between-click page-change waiting behavior. In a command chain, clickRepeated(count) inherits the current locator, while clickRepeated(locator, count) uses the explicit locator instead.

selectRepeated(locatorExpression, positions, values) performs one normal select() operation for every item in positions and remains one Ruviolta step. {{current}} means the current value from the second argument, not the array index or a normal Ruviolta variable. Every occurrence is replaced in the generated CSS/XPath locator. An array third argument uses strict one-to-one positional mapping and must have the same length as positions. A scalar third argument reuses the same selection value for every position.

let rows = [1, 2, 3, 8]
let courses = ["86", "26", "27", "125436"]

selectRepeated(
  "#row-{{current}}-0 select.courseSelect",
  rows,
  courses
)

selectRepeated(
  "#row-{{current}}-0 select.teacherSelect",
  rows,
  "John"
)

["John"] is an array and therefore does not mean repeat mode; use the scalar value "John" to select it for every position. {{current}} is local to the first argument of selectRepeated() and does not reserve or shadow a variable named current.

inputRepeated(locatorExpression, positions, values) uses the same explicit position and {{current}} model while executing the normal input() behavior for each generated locator. The second argument alone determines which positions are changed. A scalar third argument repeats one input value, while an array uses strict one-to-one positional mapping and must have the same length as positions.

inputRepeated(
  "//tr[@id='row-{{current}}-0']//input[contains(@class, 'weeks1')]",
  [1, 2, 3, 4],
  "18"
)

inputRepeated(
  "//tr[@id='row-{{current}}-0']//input[contains(@class, 'weeks1')]",
  [1, 2, 3, 4],
  ["18", "20", "22", "24"]
)

["18"] remains positional array mode and is not repeat mode; use the scalar "18" to enter the same value for every position. {{current}} uses the same dedicated placeholder highlighting as selectRepeated() and remains separate from normal variables and concatenated locator expressions.

Reusable test flows

Variables:
let userName = param("Test User")
let role = param("student")

run("tests/users.ut@createUser")
run("tests/users.ut@createUser") { "Peter" }
run("tests/users.ut@createUser") { "Peter", role: selectedRole } -> created

run() executes a tagged scenario or all scenarios from another .ut file. param(defaultExpression) marks the bindable let slots in source order. Invocation entries may be positional, named, or mixed; a named value wins when it targets a slot already filled positionally. Expressions are evaluated when the run step executes, and missing arguments use the called flow's defaults while explicit falsy and null values remain supplied values.

The same flow mechanism works for UI-only, API-only, and mixed flows. References may target the current project or a sibling project under the same projects/ directory:

run("identity/tests/users.ut@createUser") { userName: "Test User" } -> created

A cross-project flow temporarily uses the called project's UI and API environment settings, including api.baseUrl, and restores the caller context afterward. When a browser already exists it is reused; API-only nested flows stay browser-free. Each concrete scenario and Case still receives a fresh local variable scope, and nested calls receive only values explicitly forwarded by their direct caller.

let in a variable section is initialization; let among scenario or Cleanup steps is chronological executable content and may create or update a scenario-local variable. The formatter keeps section declarations at 12 spaces and runtime lets at 8 spaces.

output(value) returns an explicit result from the called scenario, and -> name captures it in the caller. The extension highlights, validates, formats, and documents runtime lets, param(...), and invocation blocks. The legacy second-argument input object and ruviolta.accept() remain supported for existing tests.

runRepeated("tests/users.ut@createUser")[N] reuses the same run() engine sequentially. It accepts zero datasets, one dataset reused N times, or exactly N positional datasets. A different count is diagnosed before execution.

Advanced parallel definitions under tests/multiTestRunner/ use named Worker: blocks containing only ordered run() or runRepeated() calls. Worker blocks execute concurrently without work stealing or automatic dependency/data management.

runRepeated("tests/classDiary.ut@createClassDiary")[2] {"11", "R"}, {"12", "A"} binds each dataset positionally to the called Scenario's param() declarations. One dataset is reused for every iteration; zero datasets preserve called-flow defaults.

Web Scenarios can declare let multiWindows = N and use openWindow(n) as a synchronous switch between isolated browser sessions. Browser network rules use mockNetwork(urlPattern, options) and are automatically Scenario-scoped; clearNetworkMocks() removes them early.

Project reports

Reports are written to the active project only:

projects/<project-name>/reports/YYYY-MM-DD_HH-mm-ss-SSS/report.html
projects/<project-name>/reports/latest.html

Ruviolta: Open Latest Report prefers the active .ut file's project. When no project is active and several projects exist, the extension asks which project report should open.

After Ruviolta: Run Current File finishes, the notification offers an Open Report button for the same project.

Ruviolta: Debug Current File starts the root project through the interactive Stepflow Debugger. UI steps keep the injected Shadow DOM panel, while API-only execution uses the standalone Stepflow window until a browser is needed. Every supported single-line step has a pencil editor; Retry executes an edited command and a successful retry can be written back with Apply to code. Same-project and cross-project called flows are shown in one compact colored row, API steps show redacted request/response details, and source updates are written to the correct root or called .ut file.

Browser selection

The browser is selected in the active project's file:

projects/<project-name>/config/ruviolta.config.mjs

Supported values are "chrome", "edge", and "firefox".

Ruviolta controls Chrome and Edge through CDP and Firefox through WebDriver BiDi.

Commands

  • Ruviolta: Format File
  • Ruviolta: Run Ruviolta Test
  • Ruviolta: Debug Ruviolta Test
  • Ruviolta: Stop Ruviolta Test
  • Ruviolta: Open Latest Report

Keyboard shortcut

Run the current Ruviolta file with:

Ctrl+Alt+R

Debug the current Ruviolta file with:

Ctrl+Alt+D

Terminal examples

npx ruviolta run
npx ruviolta run projects/customer-portal
npx ruviolta run projects/customer-portal/tests/login.ut
npx ruviolta run "@loginTest"
npx ruviolta run projects/customer-portal "@smoke,@critical"
npx ruviolta run projects/customer-portal --env production
npx ruviolta debug projects/customer-portal "@loginTest"

In PowerShell, quote arguments beginning with @.

Extension configuration

ruviolta.nodePath

Path to the Node.js executable used to run Ruviolta tests.

Default: node

ruviolta.runScript

Optional path to the Ruviolta CLI script. Leave this empty to auto-detect the Ruviolta installation in the workspace.

Default: auto-detect

ruviolta.format.enable

Enables formatting for Ruviolta files.

ruviolta.diagnostics.enable

Enables syntax diagnostics for Ruviolta files.

Color themes

  • Ruviolta Dark
  • Ruviolta Light

Author

Georgi Todorov

License

Ruviolta VS Code Extension License.

Free to use, modify, and redistribute at no charge. Selling, sublicensing for a fee, or commercially redistributing the extension is not permitted without prior written permission.

See the LICENSE file included with the extension for the full license terms.

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft