Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ElementScope.DblClick() #234

Merged
merged 1 commit into from
Feb 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,12 @@ Click any other element by calling the Click method on the returned `ElementScop
browser.FindCss("span#i-should-be-a-link", text: "Log in").Click();
```

In this example, due to the way Coypu defers execution of finders, the FindCss will also be retried, should the Click fail. For example if the DOM is shifting under the driver's feet, the link may have become stale after it is found but before the click is actioned while part of the page is reloaded.
Or double click if you need to test this:
```c#
browser.FindButton("Open").DblClick();
```:

In these example, due to the way Coypu defers execution of finders, the Finder will also be retried, should the Click fail. For example if the DOM is shifting under the driver's feet, the link may have become stale after it is found but before the click is actioned while part of the page is reloaded.

This introduces the idea of `Scope`. The browser.Find methods return a Scope on which you may perform actions, or make further scoped queries. There is more on scope below.

Expand Down
14 changes: 14 additions & 0 deletions src/Coypu.AcceptanceTests/Examples/ClickExamples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ public void Click()
Is.EqualTo("Click me - clicked"));
}

[Test]
public void DblClick()
{
var element = Browser.FindButton("dblClickMeTest");
Assert.That(Browser.FindButton("dblClickMeTest")
.Value,
Is.EqualTo("Double Click me"));

element.DblClick();
Assert.That(Browser.FindButton("dblClickMeTest")
.Value,
Is.EqualTo("Double Click me - dblclicked"));
}

[Test]
public void ClickButton()
{
Expand Down
1 change: 1 addition & 0 deletions src/Coypu.Drivers.Tests/html/InteractionTestsPage.htm
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ <h2>These are unique so should be found</h2>
<span id="classButtonSpanButtonId" class="other button stuff">I'm a span with the class of button</span>
<span id="classBtnSpanButtonId" class="other btn stuff">I'm a span with the class of btn</span>
<input type="button" id="clickMeTest" onclick="this.value += ' - clicked'; return false;" value="Click me" />
<input type="button" id="dblClickMeTest" ondblclick="this.value += ' - dblclicked'; return false;" value="Double Click me" />

<button id="firstInvisibleInputId" name="firstInvisibleInputName" style="visibility:hidden">I am an invisible button</button>
<span id="firstInvisibleSpanId" name="firstInvisibleSpanName" style="visibility:hidden">I am an invisible span</span>
Expand Down
6 changes: 6 additions & 0 deletions src/Coypu.Tests/TestDoubles/FakeDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class FakeDriver : IDriver
public readonly IList<Element> ChosenElements = new List<Element>();
public readonly List<SelectOptionParams> SelectedOptions = new List<SelectOptionParams>();
public readonly IList<Element> ClickedElements = new List<Element>();
public readonly IList<Element> DblClickedElements = new List<Element>();
public readonly IList<FindXPathParams> FindXPathRequests = new List<FindXPathParams>();
public readonly IList<Scope> GoBackCalls = new List<Scope>();
public readonly IList<Scope> GoForwardCalls = new List<Scope>();
Expand Down Expand Up @@ -64,6 +65,11 @@ public void Click(Element element)
ClickedElements.Add(element);
}

public void DblClick(Element element)
{
DblClickedElements.Add(element);
}

public void Hover(Element element)
{
HoveredElements.Add(element);
Expand Down
2 changes: 2 additions & 0 deletions src/Coypu.Tests/TestDoubles/StubDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public void ClearBrowserCookies() { }

public void Click(Element element) { }

public void DblClick(Element element) { }

public void Visit(string url,
Scope scope) { }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,101 +1,101 @@
using System;
using System.Linq;
using Coypu.Drivers;
using Coypu.Queries;
using Coypu.Tests.TestDoubles;
using NUnit.Framework;
namespace Coypu.Tests.When_interacting_with_the_browser
{
[TestFixture]
public class When_clicking_buttons : BrowserInteractionTests
{
[Test]
public void It_robustly_finds_by_text_and_clicks()
{
var buttonToBeClicked = StubButtonToBeClicked("Some button locator");
browserSession.ClickButton("Some button locator");
AssertButtonNotClickedYet(buttonToBeClicked);
RunQueryAndCheckTiming();
AssertClicked(buttonToBeClicked);
}
private void AssertClicked(StubElement buttonToBeClicked)
{
Assert.That(driver.ClickedElements, Has.Member(buttonToBeClicked));
}
[TestCase(true, 1)]
[TestCase(false, 1)]
[TestCase(false, 321)]
public void It_tries_clicking_robustly_until_expected_conditions_met(bool stubUntil, int waitBeforeRetrySecs)
{
var overallTimeout = TimeSpan.FromMilliseconds(waitBeforeRetrySecs * 1000);
var options = new Options {Timeout = overallTimeout};
var waitBetweenRetries = TimeSpan.FromSeconds(waitBeforeRetrySecs);
var buttonToBeClicked = StubButtonToBeClicked("Some button locator", options);
browserSession.ClickButton("Some button locator", new LambdaPredicateQuery(() => stubUntil, new Options{Timeout = waitBetweenRetries}) , options);
var tryUntilArgs = SpyTimingStrategy.DeferredTryUntils.Single();
AssertButtonNotClickedYet(buttonToBeClicked);
tryUntilArgs.TryThisBrowserAction.Act();
AssertClicked(buttonToBeClicked);
var queryResult = tryUntilArgs.Until.Run();
Assert.That(queryResult, Is.EqualTo(stubUntil));
Assert.That(tryUntilArgs.Until.Options.Timeout, Is.EqualTo(waitBetweenRetries));
Assert.That(tryUntilArgs.OverallTimeout, Is.EqualTo(overallTimeout));
}
[TestCase(200)]
[TestCase(300)]
public void It_waits_between_find_and_click_as_configured(int waitMs)
{
var stubButtonToBeClicked = StubButtonToBeClicked("Some button locator");
var expectedWait = TimeSpan.FromMilliseconds(waitMs);
sessionConfiguration.WaitBeforeClick = expectedWait;
var waiterCalled = false;
fakeWaiter.DoOnWait(milliseconds =>
{
Assert.That(milliseconds, Is.EqualTo(expectedWait));
AssertButtonFound();
AssertButtonNotClickedYet(stubButtonToBeClicked);
waiterCalled = true;
});
browserSession.ClickButton("Some button locator");
SpyTimingStrategy.QueriesRan<object>().Last().Run();
Assert.That(waiterCalled, "The waiter was not called");
AssertClicked(stubButtonToBeClicked);
}
private void AssertButtonFound()
{
Assert.That(driver.FindXPathRequests.Any(), "Wait called before find");
}
private void AssertButtonNotClickedYet(StubElement buttonToBeClicked)
{
Assert.That(driver.ClickedElements, Has.No.Member(buttonToBeClicked));
}
private StubElement StubButtonToBeClicked(string locator, Options options = null)
{
var buttonToBeClicked = new StubElement {Id = Guid.NewGuid().ToString()};
var buttonXpath = new Html(browserSession.Browser.UppercaseTagNames).Button(locator, options ?? sessionConfiguration);
driver.StubAllXPath(buttonXpath, new []{buttonToBeClicked}, browserSession, options ?? sessionConfiguration);
return buttonToBeClicked;
}
}
}
using System;
using System.Linq;
using Coypu.Drivers;
using Coypu.Queries;
using Coypu.Tests.TestDoubles;
using NUnit.Framework;

namespace Coypu.Tests.When_interacting_with_the_browser
{
[TestFixture]
public class When_clicking_buttons : BrowserInteractionTests
{
[Test]
public void It_robustly_finds_by_text_and_clicks()
{
var buttonToBeClicked = StubButtonToBeClicked("Some button locator");

browserSession.ClickButton("Some button locator");

AssertButtonNotClickedYet(buttonToBeClicked);

RunQueryAndCheckTiming();

AssertClicked(buttonToBeClicked);
}

private void AssertClicked(StubElement buttonToBeClicked)
{
Assert.That(driver.ClickedElements, Has.Member(buttonToBeClicked));
}

[TestCase(true, 1)]
[TestCase(false, 1)]
[TestCase(false, 321)]
public void It_tries_clicking_robustly_until_expected_conditions_met(bool stubUntil, int waitBeforeRetrySecs)
{
var overallTimeout = TimeSpan.FromMilliseconds(waitBeforeRetrySecs * 1000);
var options = new Options {Timeout = overallTimeout};
var waitBetweenRetries = TimeSpan.FromSeconds(waitBeforeRetrySecs);
var buttonToBeClicked = StubButtonToBeClicked("Some button locator", options);

browserSession.ClickButton("Some button locator", new LambdaPredicateQuery(() => stubUntil, new Options{Timeout = waitBetweenRetries}) , options);

var tryUntilArgs = SpyTimingStrategy.DeferredTryUntils.Single();

AssertButtonNotClickedYet(buttonToBeClicked);
tryUntilArgs.TryThisBrowserAction.Act();
AssertClicked(buttonToBeClicked);

var queryResult = tryUntilArgs.Until.Run();
Assert.That(queryResult, Is.EqualTo(stubUntil));
Assert.That(tryUntilArgs.Until.Options.Timeout, Is.EqualTo(waitBetweenRetries));
Assert.That(tryUntilArgs.OverallTimeout, Is.EqualTo(overallTimeout));
}

[TestCase(200)]
[TestCase(300)]
public void It_waits_between_find_and_click_as_configured(int waitMs)
{
var stubButtonToBeClicked = StubButtonToBeClicked("Some button locator");
var expectedWait = TimeSpan.FromMilliseconds(waitMs);
sessionConfiguration.WaitBeforeClick = expectedWait;

var waiterCalled = false;
fakeWaiter.DoOnWait(milliseconds =>
{
Assert.That(milliseconds, Is.EqualTo(expectedWait));

AssertButtonFound();
AssertButtonNotClickedYet(stubButtonToBeClicked);

waiterCalled = true;
});
browserSession.ClickButton("Some button locator");
SpyTimingStrategy.QueriesRan<object>().Last().Run();

Assert.That(waiterCalled, "The waiter was not called");
AssertClicked(stubButtonToBeClicked);
}

private void AssertButtonFound()
{
Assert.That(driver.FindXPathRequests.Any(), "Wait called before find");
}

private void AssertButtonNotClickedYet(StubElement buttonToBeClicked)
{
Assert.That(driver.ClickedElements, Has.No.Member(buttonToBeClicked));
}

private StubElement StubButtonToBeClicked(string locator, Options options = null)
{
var buttonToBeClicked = new StubElement {Id = Guid.NewGuid().ToString()};
var buttonXpath = new Html(browserSession.Browser.UppercaseTagNames).Button(locator, options ?? sessionConfiguration);

driver.StubAllXPath(buttonXpath, new []{buttonToBeClicked}, browserSession, options ?? sessionConfiguration);

return buttonToBeClicked;
}
}
}
28 changes: 28 additions & 0 deletions src/Coypu/Actions/DblClickAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using Coypu.Timing;

namespace Coypu.Actions
{
internal class DblClickAction : DriverAction
{
private readonly ElementScope _elementScope;
private readonly TimeSpan _waitBeforeClick;
private readonly Waiter _waiter;

internal DblClickAction(ElementScope elementScope,
IDriver driver,
Options options,
Waiter waiter) : base(driver, elementScope, options)
{
_waitBeforeClick = options.WaitBeforeClick;
_elementScope = elementScope;
_waiter = waiter;
}

public override void Act()
{
_waiter.Wait(_waitBeforeClick);
Driver.DblClick(_elementScope);
}
}
}
9 changes: 7 additions & 2 deletions src/Coypu/Drivers/Playwright/PlaywrightDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public class PlaywrightDriver : IDriver
{
private readonly Browser _browser;
private readonly bool _headless;
private readonly Dialogs _dialogs;
private readonly IPlaywright _playwright;
private readonly Dialogs _dialogs;
private readonly IPlaywright _playwright;
private readonly IBrowser _playwrightBrowser;
private readonly IBrowserContext _context;

Expand Down Expand Up @@ -216,6 +216,11 @@ public void Click(Element element)
PlaywrightElement(element).ClickAsync().Sync();
}

public void DblClick(Element element)
{
PlaywrightElement(element).DblClickAsync().Sync();
}

public void Hover(Element element)
{
PlaywrightElement(element).HoverAsync().Sync();
Expand Down
7 changes: 7 additions & 0 deletions src/Coypu/Drivers/Selenium/SeleniumWebDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using System.Text.RegularExpressions;
using OpenQA.Selenium;
using Interactions = OpenQA.Selenium.Interactions;
using Cookie = System.Net.Cookie;

#pragma warning disable 1591
Expand Down Expand Up @@ -114,6 +115,12 @@ public void Click(Element element)
.Click();
}

public void DblClick(Element element)
{
var act = new Interactions.Actions(_webDriver);
act.DoubleClick();
}

public void Hover(Element element)
{
_mouseControl.Hover(element);
Expand Down
8 changes: 7 additions & 1 deletion src/Coypu/ElementScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ public ElementScope Click(Options options = null)
return this;
}

public ElementScope DblClick(Options options = null)
{
Try(new DblClickAction(this, _driver, Merge(options), Waiter));
return this;
}

/// <summary>
/// Treat this scope as an input field and fill in with the specified value
/// </summary>
Expand Down Expand Up @@ -199,4 +205,4 @@ public bool HasNoValue(string text,
internal abstract bool Try(Query<bool> query);
internal abstract T Try<T>(Func<T> getAttribute);
}
}
}
2 changes: 2 additions & 0 deletions src/Coypu/IDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ public interface IDriver : IDisposable
[Obsolete("Please use instead: _browserSession.Driver.Cookies.DeleteAll()")]
void ClearBrowserCookies();
void Click(Element element);
void DblClick(Element element);

[Obsolete("Please use instead: AcceptAlert/AcceptConfirm/AcceptPrompt")]
void AcceptModalDialog(Scope scope);
[Obsolete("Please use instead: CancelAlert/CancelConfirm/CancelPrompt")]
Expand Down