Skip to content

Repository files navigation

SeleniumScripter (.NET)

An expressive, YAML-driven scripting layer over Selenium WebDriver — a modern .NET 8 / C# 12 port of the original Java SeleniumScripter (Spicule / Kythera Labs).

Write browser automation as a numbered list of steps in YAML — no C# required to author a test — and run it on Selenium 4. The port preserves the original YAML contract: existing scripts run unchanged.

The same script, now on .NET

This is the canonical example from the Java repo's SCRIPT_EXAMPLES.md. It runs as-is on the .NET port — same operation names, selector keys, parameter names, interpolation syntax, and subscript semantics:

# Populate the selects, enter a token, wait for the autocomplete, then loop the results.
0: { operation: wait,   selector: id, name: mcoveragetype, timeout: 30 }
1: { operation: select, selector: id, name: mcoveragetype, selectBy: value, value: 6 }
2: { operation: select, selector: id, name: benefitYear,  selectBy: value, value: 2021 }
3: { operation: keys,   selector: id, name: medicationname, value: Zyvox }
4: { operation: wait,   selector: class, name: ui-autocomplete, timeout: 30 }
# Capture the autocomplete labels into a list...
6:
  operation: captureList
  selector: class
  name: ui-menu-item
  variable: autocompletelabels
# ...then loop a subscript over each captured label.
7:
  operation: loop
  variable: autocompletelabels
  subscript: searchSubScript
subscripts:
  searchSubScript:
    1: { operation: keys, selector: id, name: medicationname, value: "{autocompletelabels}" }
    2: { operation: snapshot }

Run it from C#:

using OpenQA.Selenium;
using SeleniumScripter.Drivers;
using SeleniumScripter.Loaders;
using SeleniumScripter.Model;
using SeleniumScripter.Operations;

// Selenium Manager resolves the browser binary automatically (Firefox by default,
// matching the Java side; Chrome and Edge are also supported).
IWebDriver driver = new DriverFactory().CreateFirefox();
driver.Navigate().GoToUrl("https://example.com/search");

ScriptDocument script = ScriptLoader.LoadFile("samples/myprime.yaml");

var runner = new ScriptRunner(OperationRegistry.CreateDefault(), driver, initialUrl: driver.Url);
await runner.RunAsync(script);

// Captured lists and snapshots are available after the run.
IReadOnlyList<object?>? labels = runner.CaptureLists.Get("autocompletelabels");
IReadOnlyList<Snapshot> pages = (IReadOnlyList<Snapshot>)runner.Snapshots;

YAML and JSON are both accepted; ScriptLoader.LoadFile picks the parser by extension.

Command-line runner

No code needed — run a script straight from the CLI (src/SeleniumScripter.Cli):

dotnet run --project src/SeleniumScripter.Cli -- run samples/forward.yaml --browser chrome --url https://example.com
Options:
  -b, --browser <chrome|firefox|edge>   default: chrome
  -u, --url <url>                       navigate here before running
  -o, --output <dir>                    base dir for screenshot/dumpstack output
      --headless                        run the browser headless
      --dev                             enable dev-only operations (pause, dumpstack)
      --arg <browser-arg>               extra browser argument (repeatable)

It is also packaged as a .NET global tool (ToolCommandName: seleniumscripter), so after dotnet pack + dotnet tool install it runs as seleniumscripter run script.yaml.

Framework architecture

YAML / JSON script
   │  ScriptLoader            → ScriptDocument (numbered steps + subscripts)
   ▼
OperationRegistry             → one IOperationHandler per operation (dispatch table, not a switch)
   │
   ├─ control flow            try · do_while · if · for · loop · break · noop · set
   ├─ interaction             wait · click · clickListItem · select · keys · jsclick · …
   ├─ navigation / utility    loadpage · window · restore · alert · token · execute_js · recaptcha
   └─ capture / snapshot      captureList · snapshot · screenshot · pushsnapshot · dumpstack
   │
   ▼
StepContext  →  IWebDriver · VariableStore ({…} interpolation) · CaptureListStore · snapshots stack
                DriverFactory (Chrome/Firefox/Edge) · Conditions (owned, not DotNetSeleniumExtras)

Design highlights:

  • Page-Object-friendly, YAML-on-top. The DSL sits over Selenium 4 so non-engineers can author tests in YAML while engineers extend the operation set in C#.
  • One handler per operation, registered by the literal YAML name — adding an operation never means editing a monolithic dispatch switch.
  • Owned wait conditions (Conditions) rather than the unofficial DotNetSeleniumExtras.WaitHelpers.
  • Parity-first port. Every original golden script parses and runs; behavioral deviations from the Java source (and the bugs deliberately not reproduced) are catalogued in PORTING_NOTES.md.
  • Full-page screenshots without AShot — FullPageScreenshotter reproduces AShot's scroll-and-stitch using ImageSharp (cross-platform).

The YAML contract

These are stable strings the port must never rename:

Concept Examples
Operations wait, select, keys, click, clickListItem, captureList, loop, for, if, try, do_while, snapshot, screenshot, …
Selector strategies id, class, css / cssSelector, xpath, name, linkText, partialLinkText, tagName
Interpolation {variable} — substituted from the flat variable store (note: ${x} keeps the literal $; see PORTING_NOTES.md)

Build & test

dotnet build
dotnet test          # unit + parity tests; browser tests skip automatically

Browser-launching tests are gated — they run only when SELENIUMSCRIPTER_RUN_BROWSER_TESTS=1 is set (locally or on a runner with browsers installed). The default lane is fast and browser-free.

CI runs on Azure DevOps Pipelines (azure-pipelines.yml) and is mirrored on GitHub Actions (.github/workflows/ci.yml, Ubuntu + Windows).

QA surface

Beyond unit + parity tests, the repo demonstrates a full QA toolbox:

  • Parity tests — every original golden script parses and its operations resolve.
  • UI tests — gated headless-browser runs driving real scripts end to end.
  • API testsSeleniumScripter.Api.Tests (RestSharp + FluentAssertions): data-driven cases asserting status, body shape, and timing. Gated by SELENIUMSCRIPTER_RUN_API_TESTS=1 so the default lane is offline.
  • Performance smoke — a JMeter plan (perf/posts-smoke.jmx) with a latency-ceiling assertion; see perf/README.md.
  • Defect artifacts — on a failed UI test, a triable bundle (screenshot, page source, console log, structured defect.json with environment + expected-vs-actual) is written for attachment to a build or filing into Azure DevOps Boards / Jira.

Status

Every operation from the Java source is ported except filter (intentionally not carried over — it was Groovy-backed). See PORTING_NOTES.md for the full deviation log.

Roadmap

  • Web UI. The original Java repo ships a zero-framework web GUI (build/paste a YAML script, pick a browser + URL, run it, and view logs + snapshots in the browser). A SeleniumScripter.Web port is planned: an ASP.NET Core minimal API mirroring the same /api/operations, /api/yaml, /api/run, and /api/run/{id}/… endpoints, reusing the original's static index.html / app.js / styles.css frontend verbatim (it only talks to those endpoints). The runner, loader, and driver factory it needs already exist.
  • NuGet polish. Symbol packages (.snupkg), SourceLink, deterministic build, and a final version/package-id decision (currently 2.0.0-alpha.1).
  • Optional reporting. ExtentReports / Allure HTML output attached to CI artifacts (the dependency-free defect-artifact bundle already covers per-failure evidence).

License

MIT (preserved from the original).

About

An expressive, YAML-driven scripting layer over Selenium WebDriver — a modern .NET 8 / C# 12 port of the original Java SeleniumScripter (Spicule / Kythera Labs).

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages