| // <copyright file="BasicMouseInterfaceTest.cs" company="Selenium Committers"> |
| // Licensed to the Software Freedom Conservancy (SFC) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The SFC licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // </copyright> |
| |
| using NUnit.Framework; |
| using OpenQA.Selenium.Environment; |
| using System; |
| using System.Drawing; |
| |
| namespace OpenQA.Selenium.Interactions; |
| |
| [TestFixture] |
| public class BasicMouseInterfaceTest : DriverTestFixture |
| { |
| [SetUp] |
| public void SetupTest() |
| { |
| IActionExecutor actionExecutor = driver as IActionExecutor; |
| if (actionExecutor != null) |
| { |
| actionExecutor.ResetInputState(); |
| } |
| } |
| |
| [Test] |
| public void ShouldSetActivePointer() |
| { |
| Actions actionProvider = new Actions(driver); |
| actionProvider.SetActivePointer(PointerKind.Mouse, "test mouse"); |
| |
| PointerInputDevice device = actionProvider.GetActivePointer(); |
| |
| Assert.That(device.DeviceName, Is.EqualTo("test mouse")); |
| } |
| |
| [Test] |
| public void ShouldAllowDraggingElementWithMouseMovesItToAnotherList() |
| { |
| PerformDragAndDropWithMouse(); |
| IWebElement dragInto = driver.FindElement(By.Id("sortable1")); |
| Assert.That(dragInto.FindElements(By.TagName("li")), Has.Exactly(6).Items); |
| } |
| |
| // This test is very similar to DraggingElementWithMouse. The only |
| // difference is that this test also verifies the correct events were fired. |
| [Test] |
| public void DraggingElementWithMouseFiresEvents() |
| { |
| PerformDragAndDropWithMouse(); |
| IWebElement dragReporter = driver.FindElement(By.Id("dragging_reports")); |
| // This is failing under HtmlUnit. A bug was filed. |
| Assert.That(dragReporter.Text, Does.Match("Nothing happened\\. (?:DragOut *)+DropIn RightItem 3")); |
| } |
| |
| [Test] |
| public void ShouldAllowDoubleClickThenNavigate() |
| { |
| driver.Url = javascriptPage; |
| |
| IWebElement toDoubleClick = driver.FindElement(By.Id("doubleClickField")); |
| |
| Actions actionProvider = new Actions(driver); |
| IAction dblClick = actionProvider.DoubleClick(toDoubleClick).Build(); |
| |
| dblClick.Perform(); |
| driver.Url = droppableItems; |
| } |
| |
| [Test] |
| public void ShouldAllowDragAndDrop() |
| { |
| driver.Url = droppableItems; |
| |
| DateTime waitEndTime = DateTime.Now.Add(TimeSpan.FromSeconds(15)); |
| |
| while (!IsElementAvailable(driver, By.Id("draggable")) && (DateTime.Now < waitEndTime)) |
| { |
| System.Threading.Thread.Sleep(200); |
| } |
| |
| if (!IsElementAvailable(driver, By.Id("draggable"))) |
| { |
| throw new Exception("Could not find draggable element after 15 seconds."); |
| } |
| |
| IWebElement toDrag = driver.FindElement(By.Id("draggable")); |
| IWebElement dropInto = driver.FindElement(By.Id("droppable")); |
| |
| Actions actionProvider = new Actions(driver); |
| |
| IAction holdDrag = actionProvider.ClickAndHold(toDrag).Build(); |
| |
| IAction move = actionProvider.MoveToElement(dropInto).Build(); |
| |
| IAction drop = actionProvider.Release(dropInto).Build(); |
| |
| holdDrag.Perform(); |
| move.Perform(); |
| drop.Perform(); |
| |
| dropInto = driver.FindElement(By.Id("droppable")); |
| string text = dropInto.FindElement(By.TagName("p")).Text; |
| |
| Assert.That(text, Is.EqualTo("Dropped!")); |
| } |
| |
| [Test] |
| public void ShouldAllowDoubleClick() |
| { |
| driver.Url = javascriptPage; |
| |
| IWebElement toDoubleClick = driver.FindElement(By.Id("doubleClickField")); |
| |
| Actions actionProvider = new Actions(driver); |
| IAction dblClick = actionProvider.DoubleClick(toDoubleClick).Build(); |
| |
| dblClick.Perform(); |
| Assert.That(toDoubleClick.GetAttribute("value"), Is.EqualTo("DoubleClicked")); |
| } |
| |
| [Test] |
| public void ShouldAllowContextClick() |
| { |
| driver.Url = javascriptPage; |
| |
| IWebElement toContextClick = driver.FindElement(By.Id("doubleClickField")); |
| |
| Actions actionProvider = new Actions(driver); |
| IAction contextClick = actionProvider.ContextClick(toContextClick).Build(); |
| |
| contextClick.Perform(); |
| Assert.That(toContextClick.GetAttribute("value"), Is.EqualTo("ContextClicked")); |
| } |
| |
| [Test] |
| [IgnoreBrowser(Browser.Remote, "API not implemented in driver")] |
| public void ShouldAllowMoveAndClick() |
| { |
| driver.Url = javascriptPage; |
| |
| IWebElement toClick = driver.FindElement(By.Id("clickField")); |
| |
| Actions actionProvider = new Actions(driver); |
| IAction contextClick = actionProvider.MoveToElement(toClick).Click().Build(); |
| |
| contextClick.Perform(); |
| Assert.That(toClick.GetAttribute("value"), Is.EqualTo("Clicked"), "Value should change to Clicked."); |
| } |
| |
| [Test] |
| [IgnoreBrowser(Browser.Chrome, "Not working properly in RBE, works locally with pinned browsers")] |
| [IgnoreBrowser(Browser.Edge, "Not working properly in RBE, works locally with pinned browsers")] |
| [IgnoreBrowser(Browser.Firefox, "Not working properly in RBE, works locally with pinned browsers")] |
| public void ShouldMoveToLocation() |
| { |
| driver.Url = mouseInteractionPage; |
| |
| Actions actionProvider = new Actions(driver); |
| actionProvider.MoveToLocation(100, 200).Build().Perform(); |
| |
| IWebElement location = driver.FindElement(By.Id("absolute-location")); |
| var coordinates = location.Text.Split(','); |
| Assert.That(coordinates[0].Trim(), Is.EqualTo("100")); |
| Assert.That(coordinates[1].Trim(), Is.EqualTo("200")); |
| } |
| |
| [Test] |
| public void ShouldNotMoveToANullLocator() |
| { |
| driver.Url = javascriptPage; |
| |
| Assert.That(() => new Actions(driver).MoveToElement(null).Perform(), Throws.InstanceOf<ArgumentException>()); |
| } |
| |
| [Test] |
| [IgnoreBrowser(Browser.Chrome, "Drivers correctly click at current mouse position without another move, preserving mouse position.")] |
| [IgnoreBrowser(Browser.Edge, "Drivers correctly click at current mouse position without another move, preserving mouse position.")] |
| [IgnoreBrowser(Browser.Firefox, "Drivers correctly click at current mouse position without another move, preserving mouse position.")] |
| [IgnoreBrowser(Browser.IE, "Drivers correctly click at current mouse position without another move, preserving mouse position.")] |
| [IgnoreBrowser(Browser.Safari, "Drivers correctly click at current mouse position without another move, preserving mouse position.")] |
| public void MousePositionIsNotPreservedInActionsChain() |
| { |
| driver.Url = javascriptPage; |
| IWebElement toMoveTo = driver.FindElement(By.Id("clickField")); |
| |
| new Actions(driver).MoveToElement(toMoveTo).Perform(); |
| Assert.That(() => new Actions(driver).Click().Perform(), Throws.InstanceOf<WebDriverException>()); |
| } |
| |
| [Test] |
| [IgnoreBrowser(Browser.All, "Behaviour not finalized yet regarding linked images.")] |
| public void MovingIntoAnImageEnclosedInALink() |
| { |
| driver.Url = linkedImage; |
| |
| // Note: For some reason, the Accessibility API in Firefox will not be available before we |
| // click on something. As a work-around, click on a different element just to get going. |
| driver.FindElement(By.Id("linkToAnchorOnThisPage")).Click(); |
| |
| IWebElement linkElement = driver.FindElement(By.Id("linkWithEnclosedImage")); |
| |
| // Image is 644 x 41 - move towards the end. |
| // Note: The width of the link element itself is correct - 644 pixels. However, |
| // the height is 17 pixels and the rectangle containing it is *underneath* the image. |
| // For this reason, this action will fail. |
| new Actions(driver).MoveToElement(linkElement, 500, 30).Click().Perform(); |
| |
| WaitFor(TitleToBe("We Arrive Here"), "Title was not expected value"); |
| } |
| |
| [Test] |
| [IgnoreBrowser(Browser.Chrome, "Moving outside of view port throws exception in spec-compliant driver")] |
| [IgnoreBrowser(Browser.Edge, "Moving outside of view port throws exception in spec-compliant driver")] |
| [IgnoreBrowser(Browser.Firefox, "Moving outside of view port throws exception in spec-compliant driver")] |
| [IgnoreBrowser(Browser.IE, "Moving outside of view port throws exception in spec-compliant driver")] |
| [IgnoreBrowser(Browser.Safari, "Moving outside of view port throws exception in spec-compliant driver")] |
| public void MovingMouseBackAndForthPastViewPort() |
| { |
| driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("veryLargeCanvas.html"); |
| |
| IWebElement firstTarget = driver.FindElement(By.Id("r1")); |
| new Actions(driver).MoveToElement(firstTarget).Click().Perform(); |
| |
| IWebElement resultArea = driver.FindElement(By.Id("result")); |
| String expectedEvents = "First"; |
| WaitFor(ElementTextToEqual(resultArea, expectedEvents), "Element text did not equal " + expectedEvents); |
| |
| // Move to element with id 'r2', at (2500, 50) to (2580, 100) |
| new Actions(driver).MoveByOffset(2540 - 150, 75 - 125).Click().Perform(); |
| expectedEvents += " Second"; |
| WaitFor(ElementTextToEqual(resultArea, expectedEvents), "Element text did not equal " + expectedEvents); |
| |
| // Move to element with id 'r3' at (60, 1500) to (140, 1550) |
| new Actions(driver).MoveByOffset(100 - 2540, 1525 - 75).Click().Perform(); |
| expectedEvents += " Third"; |
| WaitFor(ElementTextToEqual(resultArea, expectedEvents), "Element text did not equal " + expectedEvents); |
| |
| // Move to element with id 'r4' at (220,180) to (320, 230) |
| new Actions(driver).MoveByOffset(270 - 100, 205 - 1525).Click().Perform(); |
| expectedEvents += " Fourth"; |
| WaitFor(ElementTextToEqual(resultArea, expectedEvents), "Element text did not equal " + expectedEvents); |
| } |
| |
| [Test] |
| public void ShouldClickElementInIFrame() |
| { |
| driver.Url = clicksPage; |
| try |
| { |
| driver.SwitchTo().Frame("source"); |
| IWebElement element = driver.FindElement(By.Id("otherframe")); |
| new Actions(driver).MoveToElement(element).Click().Perform(); |
| driver.SwitchTo().DefaultContent().SwitchTo().Frame("target"); |
| WaitFor(() => { return driver.FindElement(By.Id("span")).Text == "An inline element"; }, "Could not find element with text 'An inline element'"); |
| } |
| finally |
| { |
| driver.SwitchTo().DefaultContent(); |
| } |
| } |
| |
| [Test] |
| public void ShouldAllowUsersToHoverOverElements() |
| { |
| driver.Url = javascriptPage; |
| |
| IWebElement element = driver.FindElement(By.Id("menu1")); |
| |
| IWebElement item = driver.FindElement(By.Id("item1")); |
| Assert.That(item.Text, Is.EqualTo("")); |
| |
| ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].style.background = 'green'", element); |
| Actions actionBuilder = new Actions(driver); |
| actionBuilder.MoveToElement(element).Perform(); |
| |
| item = driver.FindElement(By.Id("item1")); |
| Assert.That(item.Text, Is.EqualTo("Item 1")); |
| } |
| |
| [Test] |
| public void HoverPersists() |
| { |
| driver.Url = javascriptPage; |
| // Move to a different element to make sure the mouse is not over the |
| // element with id 'item1' (from a previous test). |
| new Actions(driver).MoveToElement(driver.FindElement(By.Id("dynamo"))).Perform(); |
| |
| IWebElement element = driver.FindElement(By.Id("menu1")); |
| |
| IWebElement item = driver.FindElement(By.Id("item1")); |
| Assert.That(item.Text, Is.Empty); |
| |
| ((IJavaScriptExecutor)driver).ExecuteScript("arguments[0].style.background = 'green'", element); |
| new Actions(driver).MoveToElement(element).Perform(); |
| |
| // Intentionally wait to make sure hover persists. |
| System.Threading.Thread.Sleep(2000); |
| |
| WaitFor(ElementTextToNotEqual(item, ""), "Element text was empty after timeout"); |
| |
| Assert.That(item.Text, Is.EqualTo("Item 1")); |
| } |
| |
| [Test] |
| public void MovingMouseByRelativeOffset() |
| { |
| driver.Url = mouseTrackerPage; |
| |
| IWebElement trackerDiv = driver.FindElement(By.Id("mousetracker")); |
| new Actions(driver).MoveToElement(trackerDiv).Build().Perform(); |
| |
| IWebElement reporter = driver.FindElement(By.Id("status")); |
| |
| WaitFor(FuzzyMatchingOfCoordinates(reporter, 50, 200), "Coordinate matching was not within tolerance"); |
| |
| new Actions(driver).MoveByOffset(10, 20).Build().Perform(); |
| |
| WaitFor(FuzzyMatchingOfCoordinates(reporter, 60, 220), "Coordinate matching was not within tolerance"); |
| } |
| |
| [Test] |
| public void MovingMouseToRelativeElementOffset() |
| { |
| driver.Url = mouseTrackerPage; |
| |
| IWebElement trackerDiv = driver.FindElement(By.Id("mousetracker")); |
| Size size = trackerDiv.Size; |
| |
| new Actions(driver).MoveToElement(trackerDiv, 95 - size.Width / 2, 195 - size.Height / 2).Build().Perform(); |
| |
| IWebElement reporter = driver.FindElement(By.Id("status")); |
| |
| WaitFor(FuzzyMatchingOfCoordinates(reporter, 95, 195), "Coordinate matching was not within tolerance"); |
| } |
| |
| [Test] |
| public void MovingMouseToRelativeZeroElementOffset() |
| { |
| driver.Url = mouseTrackerPage; |
| |
| IWebElement trackerDiv = driver.FindElement(By.Id("mousetracker")); |
| Size size = trackerDiv.Size; |
| |
| new Actions(driver).MoveToElement(trackerDiv, -size.Width / 2, -size.Height / 2).Perform(); |
| |
| IWebElement reporter = driver.FindElement(By.Id("status")); |
| |
| WaitFor(FuzzyMatchingOfCoordinates(reporter, 0, 0), "Coordinate matching was not within tolerance"); |
| } |
| |
| [Test] |
| [NeedsFreshDriver(IsCreatedBeforeTest = true)] |
| public void MoveRelativeToBody() |
| { |
| driver.Url = mouseTrackerPage; |
| |
| new Actions(driver).MoveByOffset(50, 100).Build().Perform(); |
| |
| IWebElement reporter = driver.FindElement(By.Id("status")); |
| |
| WaitFor(FuzzyMatchingOfCoordinates(reporter, 40, 20), "Coordinate matching was not within tolerance"); |
| } |
| |
| [Test] |
| public void MoveMouseByOffsetOverAndOutOfAnElement() |
| { |
| driver.Url = mouseOverPage; |
| |
| IWebElement greenbox = driver.FindElement(By.Id("greenbox")); |
| IWebElement redbox = driver.FindElement(By.Id("redbox")); |
| Point greenboxPosition = greenbox.Location; |
| Point redboxPosition = redbox.Location; |
| int shiftX = redboxPosition.X - greenboxPosition.X; |
| int shiftY = redboxPosition.Y - greenboxPosition.Y; |
| |
| Size greenBoxSize = greenbox.Size; |
| int xOffset = 2 - greenBoxSize.Width / 2; |
| int yOffset = 2 - greenBoxSize.Height / 2; |
| |
| new Actions(driver).MoveToElement(greenbox, xOffset, yOffset).Perform(); |
| WaitFor(ElementColorToBe(redbox, Color.Green), "element color was not green"); |
| |
| new Actions(driver).MoveToElement(greenbox, xOffset, yOffset).MoveByOffset(shiftX, shiftY).Perform(); |
| WaitFor(ElementColorToBe(redbox, Color.Red), "element color was not red"); |
| |
| new Actions(driver).MoveToElement(greenbox, xOffset, yOffset).MoveByOffset(shiftX, shiftY).MoveByOffset(-shiftX, -shiftY).Perform(); |
| WaitFor(ElementColorToBe(redbox, Color.Green), "element color was not red"); |
| } |
| |
| [Test] |
| public void CanMouseOverAndOutOfAnElement() |
| { |
| driver.Url = mouseOverPage; |
| |
| IWebElement greenbox = driver.FindElement(By.Id("greenbox")); |
| IWebElement redbox = driver.FindElement(By.Id("redbox")); |
| Size greenSize = greenbox.Size; |
| Size redSize = redbox.Size; |
| |
| new Actions(driver).MoveToElement(greenbox, 1 - greenSize.Width / 2, 1 - greenSize.Height / 2).Perform(); |
| Assert.That(redbox.GetCssValue("background-color"), Is.EqualTo("rgba(0, 128, 0, 1)").Or.EqualTo("rgb(0, 128, 0)")); |
| |
| new Actions(driver).MoveToElement(redbox).Perform(); |
| Assert.That(redbox.GetCssValue("background-color"), Is.EqualTo("rgba(255, 0, 0, 1)").Or.EqualTo("rgb(255, 0, 0)")); |
| |
| new Actions(driver).MoveToElement(redbox, redSize.Width / 2 + 2, redSize.Height / 2 + 2).Perform(); |
| Assert.That(redbox.GetCssValue("background-color"), Is.EqualTo("rgba(0, 128, 0, 1)").Or.EqualTo("rgb(0, 128, 0)")); |
| } |
| |
| private Func<bool> FuzzyMatchingOfCoordinates(IWebElement element, int x, int y) |
| { |
| return () => |
| { |
| return FuzzyPositionMatching(x, y, element.Text); |
| }; |
| } |
| |
| private bool FuzzyPositionMatching(int expectedX, int expectedY, String locationTuple) |
| { |
| string[] splitString = locationTuple.Split(','); |
| int gotX = Convert.ToInt16(Math.Round(Convert.ToDouble(splitString[0].Trim()))); |
| int gotY = Convert.ToInt16(Math.Round(Convert.ToDouble(splitString[1].Trim()))); |
| |
| // Everything within 5 pixels range is OK |
| const int ALLOWED_DEVIATION = 5; |
| return Math.Abs(expectedX - gotX) < ALLOWED_DEVIATION && Math.Abs(expectedY - gotY) < ALLOWED_DEVIATION; |
| } |
| |
| private void PerformDragAndDropWithMouse() |
| { |
| driver.Url = draggableLists; |
| |
| IWebElement dragReporter = driver.FindElement(By.Id("dragging_reports")); |
| |
| IWebElement toDrag = driver.FindElement(By.Id("rightitem-3")); |
| IWebElement dragInto = driver.FindElement(By.Id("sortable1")); |
| |
| IAction holdItem = new Actions(driver).ClickAndHold(toDrag).Build(); |
| |
| IAction moveToSpecificItem = new Actions(driver).MoveToElement(driver.FindElement(By.Id("leftitem-4"))).Build(); |
| |
| IAction moveToOtherList = new Actions(driver).MoveToElement(dragInto).Build(); |
| |
| IAction drop = new Actions(driver).Release(dragInto).Build(); |
| |
| Assert.That(dragReporter.Text, Is.EqualTo("Nothing happened.")); |
| |
| holdItem.Perform(); |
| moveToSpecificItem.Perform(); |
| moveToOtherList.Perform(); |
| |
| Assert.That(dragReporter.Text, Does.Match("Nothing happened\\. (?:DragOut *)+")); |
| drop.Perform(); |
| } |
| |
| private bool IsElementAvailable(IWebDriver driver, By locator) |
| { |
| try |
| { |
| driver.FindElement(locator); |
| return true; |
| } |
| catch (NoSuchElementException) |
| { |
| return false; |
| } |
| } |
| |
| private Func<bool> TitleToBe(string desiredTitle) |
| { |
| return () => driver.Title == desiredTitle; |
| } |
| |
| private Func<bool> ValueToBe(IWebElement element, string desiredValue) |
| { |
| return () => element.GetDomProperty("value") == desiredValue; |
| } |
| |
| private Func<bool> ElementTextToEqual(IWebElement element, string text) |
| { |
| return () => element.Text == text; |
| } |
| |
| private Func<bool> ElementTextToNotEqual(IWebElement element, string text) |
| { |
| return () => element.Text != text; |
| } |
| |
| private Func<bool> ElementColorToBe(IWebElement element, Color color) |
| { |
| return () => |
| { |
| string rgb = string.Format("rgb({0}, {1}, {2})", color.R, color.G, color.B); |
| string rgba = string.Format("rgba({0}, {1}, {2}, 1)", color.R, color.G, color.B); |
| string value = element.GetCssValue("background-color"); |
| return value == rgb || value == rgba; |
| }; |
| } |
| } |