Tutorials

Tutorials

Left-Click and Download a File

A workflow that closes an ad nested inside an iframe using a persistent selector, then left-clicks a download link and saves the resulting PDF to disk. Covers GPALFile as a download target, LeftClickAndDownload, and persistent selectors for nags that can reappear at any time.

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;

GPAL

.WithExceptionHandler(ExceptionHandler)

.WithInformationHandler(InformationHandler)

.WithImageMatchingPercentage(80)

.WithSimulateMouseMovement(false)

.WithTypingDelay(100)

.WithPublishToConsole();

string url = "https://example.com/sample-downloads/";

Selector downloadLink = (Selector)GPAL.Selector

.WithXPath("//a[normalize-space()='Download sample pdf file']")

.WithCSS("#table-files > tbody > tr:nth-of-type(1) > td.file-link > a")

.WithOffsetX(115)

.WithOffsetY(18);

GPALFile downloadTarget = @"c:sdisample.pdf";

Selector adIframe = (Selector)GPAL.CssSelector("iframe").MatchText("Advertisement").WithSelectorName("Ad Iframe");

Selector closeByText = (Selector)GPAL.Selector

.WithText("Close").CallIfFound(CloseCallIfFound).WithSelectorName("Close by Close");

Selector closeByButton = (Selector)GPAL.Selector

.WithCSS("#dismiss-button").CallIfFound(CloseCallIfFound).WithSelectorName("Close by X");

IBrowser browser = GPAL.Browser

.WithBrowserType(BrowserType.Chrome)

.WithUseAutomationEngine(AutomationEngine.Selenium)

.WithLoadImages(true)

.WithBlockPopUps(true)

.WithOverwriteExistingFile(true)

.WithPromptForDownload(true)

.WithDriverLocation(@"C:drivers")

.WithOpenPDFExternally(true)

.ToGPALObject();

browser.GoTo(url);

browser.WithPersistentSelector(closeByText).InPersistentIframe(adIframe).PersistentCallIfFound(CloseCallIfFound);

browser.WithPersistentSelector(closeByButton).InPersistentIframe(adIframe).PersistentCallIfFound(CloseCallIfFound);

browser

.WithSelector(downloadLink)

.LeftClickAndDownload(downloadTarget);

browser.Close(true);

static CallIfStatus CloseCallIfFound(IBrowser browser, List<GPALElement> foundElements, List<GPALElement> matchedElements, Selector selector, bool matchedAll)

{

if (1 == matchedElements.Count && false == matchedElements[0].TagName.ToLower().Equals("script"))

matchedElements[0].Click();

return CallIfStatus.Handled;

}

GPALFile as a Download Target

downloadTarget is a GPALFile, not a plain string. Assign it from a full path the same way you'd assign a string, but it carries file metadata GPAL can use - here it's the destination LeftClickAndDownload writes the PDF to once the click triggers the browser's download.

GPALFile downloadTarget = @"c:sdisample.pdf";

browser

.WithSelector(downloadLink)

.LeftClickAndDownload(downloadTarget);

TIP

WithOverwriteExistingFile(true) lets a repeat run replace the file at the same path instead of appending (1), (2), and so on. WithPromptForDownload(true) and WithOpenPDFExternally(true) tell the browser settings to behave like a normal download dialog rather than rendering the PDF inline.

Persistent Selectors for an Ad Inside an Iframe

Some ads load inside an iframe and can reappear after the page settles, so a one-time LeftClick isn't enough. WithPersistentSelector registers a selector GPAL keeps watching for; InPersistentIframe scopes that watch to elements inside a matching iframe (here, any iframe whose text contains "Advertisement"); PersistentCallIfFound wires up the callback that closes it whenever it shows up.

Selector adIframe = (Selector)GPAL.CssSelector("iframe").MatchText("Advertisement").WithSelectorName("Ad Iframe");

Selector closeByText = (Selector)GPAL.Selector

.WithText("Close").CallIfFound(CloseCallIfFound).WithSelectorName("Close by Close");

Selector closeByButton = (Selector)GPAL.Selector

.WithCSS("#dismiss-button").CallIfFound(CloseCallIfFound).WithSelectorName("Close by X");

browser.WithPersistentSelector(closeByText).InPersistentIframe(adIframe).PersistentCallIfFound(CloseCallIfFound);

browser.WithPersistentSelector(closeByButton).InPersistentIframe(adIframe).PersistentCallIfFound(CloseCallIfFound);

TIP

It's common to register more than one persistent selector for the same nag - one that matches by visible text ("Close") and one that matches by a stable CSS id (#dismiss-button). Whichever one actually appears on a given page load triggers the callback.

The Close Callback

CloseCallIfFound is a CallIfDelegate - the same callback shape used by CallIfFound and CallIfNotFound elsewhere in GPAL. It receives the matched elements and clicks the one that matched, as long as it isn't a stray <script> tag (which can sometimes match broad selectors like WithText).

static CallIfStatus CloseCallIfFound(IBrowser browser, List<GPALElement> foundElements, List<GPALElement> matchedElements, Selector selector, bool matchedAll)

{

if (1 == matchedElements.Count && false == matchedElements[0].TagName.ToLower().Equals("script"))

matchedElements[0].Click();

return CallIfStatus.Handled;

}

Click the Link and Download

With the ad-closer registered, the rest of the workflow is a single chain: select the download link, and call LeftClickAndDownload with the GPALFile target. GPAL clicks the element and waits for the resulting file to land at the path you specified, then Close(true) shuts down the driver.

browser

.WithSelector(downloadLink)

.LeftClickAndDownload(downloadTarget);

browser.Close(true);

WARNING

Persistent selectors keep watching for the rest of the browser's life, not just for the next action. Register them once after navigation and they'll keep firing in the background - including while LeftClickAndDownload is waiting for the file to finish writing.