Tutorials

Tutorials

Recording and Replaying a Selector Sequence

OttoRecording captures a full search-click-scroll sequence as a chain of pre-built Selectors, then replays it against a live browser over a debug port. Each Selector carries multiple lookup strategies (CSS, XPath, value/text matches) recorded ahead of time, so the replay can fall back gracefully if one strategy stops matching.

Complete Program

Here's the whole workflow, start to finish. Each piece is broken down and explained below.

using GenerallyPositive;

using GenerallyPositive.Browser;

using static GenerallyPositive.Enums;

static Selector searchInput = (Selector)GPAL.Selector

.WithCSS("#gh-ac")

.WithXPath("//*[@id='gh-ac']")

.MatchPlaceholder("Search for anything");

static Selector searchButton = (Selector)GPAL.Selector

.WithCSS("#gh-btn")

.WithXPath("//*[@id='gh-btn']")

.WithValue("Search")

.MatchValue("Search");

static Selector resultLink = (Selector)GPAL.Selector

.WithCSS("#srp-river-results > ul > li:nth-of-type(2) > div > div.s-item__info > a > div > span")

.WithXPath("//*[@id='srp-river-results']/ul/li[2]/div/div[2]/a/div/span");

static Selector backToResults = (Selector)GPAL.Selector

.WithCSS("#bc > li > div > a > span")

.WithXPath("//*[@id='bc']/li[1]/div/a/span[2]")

.WithText("Back to search results")

.MatchText("Back to search results");

GPAL

.WithSimulateMouseMovement(true)

.WithPublishToConsole()

.WithExceptionHandler(ExceptionEventHandler)

.WithInformationHandler(InformationHandler);

Browser browser = (Browser)GPAL.Browser

.WithBrowserType(BrowserType.Chrome)

.WithDriverLocation(@"C:drivers")

.WithUseAutomationEngine(AutomationEngine.OttoMagic)

.WithUseDebugPort(6565)

.ToGPALObject();

browser.GoTo("https://example.com");

browser

.WaitFor(5)

.WithSelector(searchInput)

.LeftClick()

.FillInFrom("vintage vinyl records")

.WithSelector(searchButton)

.LeftClick()

.WaitFor(5)

.WithSelector(resultLink)

.LeftClick()

.WithSelector(backToResults)

.LeftClick()

.WaitFor(10);

browser.Close(true);

public static void ExceptionEventHandler(object sender, EventArgs e)

{

var args = (GPAL.GPALEventArgs)e;

Debug.WriteLine(args.Message);

Debug.WriteLine(args.ExceptionRaised?.Message);

}

public static void InformationHandler(object sender, EventArgs e)

{

Debug.WriteLine(((GPAL.GPALEventArgs)e).Message);

}

Pre-Recorded Selectors with Multiple Lookup Paths

Each Selector here is built once at class scope using GPAL.Selector, stacking up multiple WithCSS and WithXPath calls plus value/text/placeholder matches. This mirrors how a recording tool would capture an element: grab every identifying detail available at the moment of recording, so replay has several ways to find the same element later even if the page shifts slightly.

static Selector searchInput = (Selector)GPAL.Selector

.WithCSS("#gh-ac")

.WithXPath("//*[@id='gh-ac']")

.MatchPlaceholder("Search for anything");

static Selector searchButton = (Selector)GPAL.Selector

.WithCSS("#gh-btn")

.WithXPath("//*[@id='gh-btn']")

.WithValue("Search")

.MatchValue("Search");

TIP

GPAL.Selector returns a builder; the (Selector) cast at the front converts the finished chain into a reusable Selector value you can assign to a field and pass around. See The Selector System.

Connect Over a Debug Port

WithUseDebugPort(6565) attaches GPAL to a browser already running with remote debugging enabled on that port, rather than launching a fresh instance. This is useful for replaying a recorded sequence against a browser session you've already set up and signed into.

Browser browser = (Browser)GPAL.Browser

.WithBrowserType(BrowserType.Chrome)

.WithDriverLocation(@"C:drivers")

.WithUseAutomationEngine(AutomationEngine.OttoMagic)

.WithUseDebugPort(6565)

.ToGPALObject();

browser.GoTo("https://example.com");

Replay the Recorded Sequence

The replay is one continuous chain: click the search input, fill it in, click the search button, wait for results, click into a result, then click back to the results list. Each WithSelector/LeftClick pair is one recorded step - GPAL works through the chain in order, the same shape a recording tool would generate.

browser

.WaitFor(5)

.WithSelector(searchInput)

.LeftClick()

.FillInFrom("vintage vinyl records")

.WithSelector(searchButton)

.LeftClick()

.WaitFor(5)

.WithSelector(resultLink)

.LeftClick()

.WithSelector(backToResults)

.LeftClick()

.WaitFor(10);

browser.Close(true);

WARNING

Because this browser was attached over a debug port, Close(true) still stops the underlying driver process. If other workflows are sharing that debug port session, use Close(false) instead.

Wire Up Exception and Information Handlers

WithExceptionHandler and WithInformationHandler hook GPAL's event system so every exception and informational event during replay gets written to the debug output - handy for diagnosing which recorded step stopped matching if the page changes.

GPAL

.WithSimulateMouseMovement(true)

.WithPublishToConsole()

.WithExceptionHandler(ExceptionEventHandler)

.WithInformationHandler(InformationHandler);

public static void ExceptionEventHandler(object sender, EventArgs e)

{

var args = (GPAL.GPALEventArgs)e;

Debug.WriteLine(args.Message);

Debug.WriteLine(args.ExceptionRaised?.Message);

}

public static void InformationHandler(object sender, EventArgs e)

{

Debug.WriteLine(((GPAL.GPALEventArgs)e).Message);

}

TIP

The actual OttoRecording test program loops over every BrowserType, AutomationEngine, and headless/headful combination to regression-test the recorded sequence across all of them. A real workflow only needs the single pass shown here. See Automation Engines.