diff --git a/examples/dotnet/SeleniumDocs/ActionsAPI/ActionsTest.cs b/examples/dotnet/SeleniumDocs/ActionsAPI/ActionsTest.cs new file mode 100644 index 000000000000..4bb87ee063fc --- /dev/null +++ b/examples/dotnet/SeleniumDocs/ActionsAPI/ActionsTest.cs @@ -0,0 +1,49 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Interactions; + +namespace SeleniumDocs.ActionsAPI +{ + [TestClass] + public class ActionsTest : BaseTest + { + [TestMethod] + public void Pause() + { + DateTime start = DateTime.Now; + + ActionBuilder actionBuilder = new ActionBuilder(); + PointerInputDevice mouse = new PointerInputDevice(PointerKind.Mouse, "default mouse"); + KeyInputDevice keyboard = new KeyInputDevice("default keyboard"); + actionBuilder.AddAction(mouse.CreatePause(TimeSpan.FromSeconds(1))); + actionBuilder.AddAction(keyboard.CreatePause(TimeSpan.FromSeconds(1))); + ((IActionExecutor)driver).PerformActions(actionBuilder.ToActionSequenceList()); + + TimeSpan duration = DateTime.Now - start; + Assert.IsTrue(duration > TimeSpan.FromSeconds(2)); + Assert.IsTrue(duration < TimeSpan.FromSeconds(3)); + } + + [TestMethod] + public void ReleaseAll() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement clickable = driver.FindElement(By.Id("clickable")); + var actions = new Actions(driver); + actions.ClickAndHold(clickable) + .KeyDown(Keys.Shift) + .KeyDown(Keys.Alt) + .SendKeys("a") + .Perform(); + + ((WebDriver)driver).ResetInputState(); + + new Actions(driver).SendKeys("a").Perform(); + var value = clickable.GetAttribute("value"); + Assert.AreEqual("Å", value[..1]); + Assert.AreEqual("a", value.Substring(1, 1)); + } + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/ActionsAPI/KeysTest.cs b/examples/dotnet/SeleniumDocs/ActionsAPI/KeysTest.cs new file mode 100644 index 000000000000..fc71810c9cb8 --- /dev/null +++ b/examples/dotnet/SeleniumDocs/ActionsAPI/KeysTest.cs @@ -0,0 +1,93 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Interactions; + +namespace SeleniumDocs.ActionsAPI +{ + [TestClass] + public class KeysTest : BaseTest + { + [TestMethod] + public void KeyDown() + { + driver.Url = "https://selenium.dev/selenium/web/single_text_input.html"; + + new Actions(driver) + .KeyDown(Keys.Shift) + .SendKeys("a") + .Perform(); + + IWebElement textField = driver.FindElement(By.Id("textInput")); + Assert.AreEqual("A", textField.GetAttribute("value")); + } + + [TestMethod] + public void KeyUp() + { + driver.Url = "https://selenium.dev/selenium/web/single_text_input.html"; + + new Actions(driver) + .KeyDown(Keys.Shift) + .SendKeys("a") + .KeyUp(Keys.Shift) + .SendKeys("b") + .Perform(); + + IWebElement textField = driver.FindElement(By.Id("textInput")); + Assert.AreEqual("Ab", textField.GetAttribute("value")); + } + + [TestMethod] + public void SendKeysToActiveElement() + { + driver.Url = "https://selenium.dev/selenium/web/single_text_input.html"; + + new Actions(driver) + .SendKeys("abc") + .Perform(); + + IWebElement textField = driver.FindElement(By.Id("textInput")); + Assert.AreEqual("abc", textField.GetAttribute("value")); + } + + [TestMethod] + public void SendKeysToDesignatedElement() + { + driver.Url = "https://selenium.dev/selenium/web/single_text_input.html"; + driver.FindElement(By.TagName("body")).Click(); + + IWebElement textField = driver.FindElement(By.Id("textInput")); + new Actions(driver) + .SendKeys(textField, "abc") + .Perform(); + + Assert.AreEqual("abc", textField.GetAttribute("value")); + } + + [TestMethod] + public void CopyAndPaste() + { + driver.Url = "https://selenium.dev/selenium/web/single_text_input.html"; + + var capabilities = ((WebDriver)driver).Capabilities; + String platformName = (string)capabilities.GetCapability("platformName"); + + String cmdCtrl = platformName.Contains("mac") ? Keys.Command : Keys.Control; + + new Actions(driver) + .SendKeys("Selenium!") + .SendKeys(Keys.ArrowLeft) + .KeyDown(Keys.Shift) + .SendKeys(Keys.ArrowUp) + .KeyUp(Keys.Shift) + .KeyDown(cmdCtrl) + .SendKeys("xvv") + .KeyUp(cmdCtrl) + .Perform(); + + IWebElement textField = driver.FindElement(By.Id("textInput")); + Assert.AreEqual("SeleniumSelenium!", textField.GetAttribute("value")); + } + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs b/examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs new file mode 100644 index 000000000000..a9f3bb6e4665 --- /dev/null +++ b/examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs @@ -0,0 +1,185 @@ +using System; +using System.Drawing; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Interactions; + +namespace SeleniumDocs.ActionsAPI +{ + [TestClass] + public class MouseTest : BaseTest + { + [TestMethod] + public void ClickAndHold() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement clickable = driver.FindElement(By.Id("clickable")); + new Actions(driver) + .ClickAndHold(clickable) + .Perform(); + + Assert.AreEqual("focused", driver.FindElement(By.Id("click-status")).Text); + } + + [TestMethod] + public void ClickAndRelease() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement clickable = driver.FindElement(By.Id("click")); + new Actions(driver) + .Click(clickable) + .Perform(); + + Assert.IsTrue(driver.Url.Contains("resultPage.html")); + } + + [TestMethod] + public void RightClick() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement clickable = driver.FindElement(By.Id("clickable")); + new Actions(driver) + .ContextClick(clickable) + .Perform(); + + Assert.AreEqual("context-clicked", driver.FindElement(By.Id("click-status")).Text); + } + + [TestMethod] + public void BackClick() + { + + throw new WebDriverException("Need to implement MouseButton.Back"); + } + + [TestMethod] + public void ForwardClick() + { + throw new WebDriverException("Need to implement MouseButton.Forward"); + } + + [TestMethod] + public void DoubleClick() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement clickable = driver.FindElement(By.Id("clickable")); + new Actions(driver) + .DoubleClick(clickable) + .Perform(); + + Assert.AreEqual("double-clicked", driver.FindElement(By.Id("click-status")).Text); + } + + [TestMethod] + public void Hovers() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement hoverable = driver.FindElement(By.Id("hover")); + new Actions(driver) + .MoveToElement(hoverable) + .Perform(); + + Assert.AreEqual("hovered", driver.FindElement(By.Id("move-status")).Text); + } + + [TestMethod] + public void MoveByOffsetFromTopLeftOfElement() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement tracker = driver.FindElement(By.Id("mouse-tracker")); + new Actions(driver) + .MoveToElement(tracker, 8, 11, MoveToElementOffsetOrigin.TopLeft) + .Perform(); + + string[] result = driver.FindElement(By.Id("relative-location")).Text.Split(", "); + Assert.IsTrue(Math.Abs(int.Parse(result[0]) - 8) < 2); + Assert.IsTrue(Math.Abs(int.Parse(result[1]) - 11) < 2); + } + + [TestMethod] + public void MoveByOffsetFromCenterOfElement() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement tracker = driver.FindElement(By.Id("mouse-tracker")); + new Actions(driver) + .MoveToElement(tracker, 8, 11, MoveToElementOffsetOrigin.Center) + .Perform(); + + string[] result = driver.FindElement(By.Id("relative-location")).Text.Split(", "); + Assert.IsTrue(Math.Abs(int.Parse(result[0]) - 100 - 8) < 2); + Assert.IsTrue(Math.Abs(int.Parse(result[1]) - 100 - 11) < 2); + } + + [TestMethod] + public void MoveByOffsetFromViewport() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + ActionBuilder actionBuilder = new ActionBuilder(); + PointerInputDevice mouse = new PointerInputDevice(PointerKind.Mouse, "default mouse"); + actionBuilder.AddAction(mouse.CreatePointerMove(CoordinateOrigin.Viewport, + 8, 12, TimeSpan.Zero)); + ((IActionExecutor)driver).PerformActions(actionBuilder.ToActionSequenceList()); + + string[] result = driver.FindElement(By.Id("absolute-location")).Text.Split(", "); + Assert.IsTrue(Math.Abs(int.Parse(result[0]) - 8) < 2); + Assert.IsTrue(Math.Abs(int.Parse(result[1]) - 12) < 2); + } + + [TestMethod] + public void MoveByOffsetFromCurrentPointerLocation() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + ActionBuilder actionBuilder = new ActionBuilder(); + PointerInputDevice mouse = new PointerInputDevice(PointerKind.Mouse, "default mouse"); + actionBuilder.AddAction(mouse.CreatePointerMove(CoordinateOrigin.Viewport, + 8, 12, TimeSpan.Zero)); + ((IActionExecutor)driver).PerformActions(actionBuilder.ToActionSequenceList()); + + new Actions(driver) + .MoveByOffset(13, 15) + .Perform(); + + string[] result = driver.FindElement(By.Id("absolute-location")).Text.Split(", "); + Assert.IsTrue(Math.Abs(int.Parse(result[0]) - 8 - 13) < 2); + Assert.IsTrue(Math.Abs(int.Parse(result[1]) - 12 - 15) < 2); + } + + [TestMethod] + public void DragToElement() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement draggable = driver.FindElement(By.Id("draggable")); + IWebElement droppable = driver.FindElement(By.Id("droppable")); + new Actions(driver) + .DragAndDrop(draggable, droppable) + .Perform(); + + Assert.AreEqual("dropped", driver.FindElement(By.Id("drop-status")).Text); + } + + [TestMethod] + public void DragByOffset() + { + driver.Url = "https://selenium.dev/selenium/web/mouse_interaction.html"; + + IWebElement draggable = driver.FindElement(By.Id("draggable")); + Point start = draggable.Location; + Point finish = driver.FindElement(By.Id("droppable")).Location; + new Actions(driver) + .DragAndDropToOffset(draggable, finish.X - start.X, finish.Y - start.Y) + .Perform(); + + Assert.AreEqual("dropped", driver.FindElement(By.Id("drop-status")).Text); + } + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/ActionsAPI/PenTest.cs b/examples/dotnet/SeleniumDocs/ActionsAPI/PenTest.cs new file mode 100644 index 000000000000..e08651a374de --- /dev/null +++ b/examples/dotnet/SeleniumDocs/ActionsAPI/PenTest.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Interactions; + +namespace SeleniumDocs.ActionsAPI +{ + [TestClass] + public class PenTest : BaseTest + { + [TestMethod] + public void UsePen() + { + driver.Url = "https://selenium.dev/selenium/web/pointerActionsPage.html"; + + IWebElement pointerArea = driver.FindElement(By.Id("pointerArea")); + ActionBuilder actionBuilder = new ActionBuilder(); + PointerInputDevice pen = new PointerInputDevice(PointerKind.Pen, "default pen"); + + actionBuilder.AddAction(pen.CreatePointerMove(pointerArea, 0, 0, TimeSpan.FromMilliseconds(800))); + actionBuilder.AddAction(pen.CreatePointerDown(MouseButton.Left)); + actionBuilder.AddAction(pen.CreatePointerMove(CoordinateOrigin.Pointer, + 2, 2, TimeSpan.Zero)); + actionBuilder.AddAction(pen.CreatePointerUp(MouseButton.Left)); + ((IActionExecutor)driver).PerformActions(actionBuilder.ToActionSequenceList()); + + var moves = driver.FindElements(By.ClassName("pointermove")); + var moveTo = getProperties(moves.ElementAt(0)); + var down = getProperties(driver.FindElement(By.ClassName("pointerdown"))); + var moveBy = getProperties(moves.ElementAt(1)); + var up = getProperties(driver.FindElement(By.ClassName("pointerup"))); + + Point location = pointerArea.Location; + Size size = pointerArea.Size; + int centerX = location.X + size.Width / 2; + int centerY = location.Y + size.Height / 2; + + Assert.AreEqual("-1", moveTo["button"]); + Assert.AreEqual((centerX).ToString(), moveTo["pageX"]); + Assert.AreEqual((centerY).ToString(), moveTo["pageY"]); + Assert.AreEqual("0", down["button"]); + Assert.AreEqual((centerX).ToString(), down["pageX"]); + Assert.AreEqual((centerY).ToString(), down["pageY"]); + Assert.AreEqual("-1", moveBy["button"]); + Assert.AreEqual((centerX + 2).ToString(), moveBy["pageX"]); + Assert.AreEqual((centerY + 2).ToString(), moveBy["pageY"]); + Assert.AreEqual("0", up["button"]); + Assert.AreEqual((centerX + 2).ToString(), up["pageX"]); + Assert.AreEqual((centerY + 2).ToString(), up["pageY"]); + } + + private Dictionary getProperties(IWebElement element) + { + var str = element.Text; + str = str[(str.Split()[0].Length + 1)..]; + IEnumerable keyValue = str.Split(", ").Select(part => part.Split(":")); + return keyValue.ToDictionary(split => split[0].Trim(), split => split[1].Trim()); + } + + } +} \ No newline at end of file diff --git a/examples/dotnet/SeleniumDocs/BaseTest.cs b/examples/dotnet/SeleniumDocs/BaseTest.cs new file mode 100644 index 000000000000..ec1bdc3e8c3a --- /dev/null +++ b/examples/dotnet/SeleniumDocs/BaseTest.cs @@ -0,0 +1,26 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenQA.Selenium; +using OpenQA.Selenium.Chrome; +using WebDriverManager; +using WebDriverManager.DriverConfigs.Impl; + +namespace SeleniumDocs +{ + public class BaseTest + { + protected IWebDriver driver; + + [TestInitialize] + public void CreateDriver() + { + new DriverManager().SetUpDriver(new ChromeConfig()); + driver = new ChromeDriver(); + } + + [TestCleanup] + public void QuitDriver() + { + driver.Quit(); + } + } +} \ No newline at end of file diff --git a/examples/java/pom.xml b/examples/java/pom.xml index c264885700ba..d04c0b9d13d4 100644 --- a/examples/java/pom.xml +++ b/examples/java/pom.xml @@ -12,7 +12,7 @@ org.seleniumhq.selenium selenium-java - 4.1.2 + 4.1.0 org.junit.jupiter diff --git a/examples/java/src/test/java/dev/selenium/BaseTest.java b/examples/java/src/test/java/dev/selenium/BaseTest.java new file mode 100644 index 000000000000..914e7c0183f0 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/BaseTest.java @@ -0,0 +1,25 @@ +package dev.selenium; + +import io.github.bonigarcia.wdm.WebDriverManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.chrome.ChromeDriver; +public class BaseTest { + public WebDriver driver; + + @BeforeAll static void setDriver() { + WebDriverManager.chromedriver().setup(); + } + + @BeforeEach + public void setup() { + driver = new ChromeDriver(); + } + + @AfterEach + public void quit() { + driver.quit(); + } +} diff --git a/examples/java/src/test/java/dev/selenium/actions_api/ActionsTest.java b/examples/java/src/test/java/dev/selenium/actions_api/ActionsTest.java new file mode 100644 index 000000000000..36f803e4ecf8 --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/actions_api/ActionsTest.java @@ -0,0 +1,53 @@ +package dev.selenium.actions_api; + +import dev.selenium.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Keys; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.remote.RemoteWebDriver; + +import java.time.Duration; + +public class ActionsTest extends BaseTest { + @Test + public void pause() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + long start = System.currentTimeMillis(); + + WebElement clickable = driver.findElement(By.id("clickable")); + new Actions(driver) + .moveToElement(clickable) + .pause(Duration.ofSeconds(1)) + .clickAndHold() + .pause(Duration.ofSeconds(1)) + .sendKeys("abc") + .perform(); + + long duration = System.currentTimeMillis() - start; + Assertions.assertTrue(duration > 2000); + Assertions.assertTrue(duration < 3000); + } + + @Test + public void releasesAll() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + WebElement clickable = driver.findElement(By.id("clickable")); + Actions actions = new Actions(driver); + actions.clickAndHold(clickable) + .keyDown(Keys.SHIFT) + .keyDown(Keys.ALT) + .sendKeys("a") + .perform(); + + ((RemoteWebDriver) driver).resetInputState(); + + actions.sendKeys("a").perform(); + Assertions.assertEquals("Å", String.valueOf(clickable.getAttribute("value").charAt(0))); + Assertions.assertEquals("a", String.valueOf(clickable.getAttribute("value").charAt(1))); + } +} diff --git a/examples/java/src/test/java/dev/selenium/actions_api/KeysTest.java b/examples/java/src/test/java/dev/selenium/actions_api/KeysTest.java new file mode 100644 index 000000000000..368c73273b6b --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/actions_api/KeysTest.java @@ -0,0 +1,89 @@ +package dev.selenium.actions_api; + +import dev.selenium.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.HasCapabilities; +import org.openqa.selenium.Keys; +import org.openqa.selenium.Platform; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; + +public class KeysTest extends BaseTest { + @Test + public void keyDown() { + driver.get("https://www.selenium.dev/selenium/web/single_text_input.html"); + + new Actions(driver) + .keyDown(Keys.SHIFT) + .sendKeys("a") + .perform(); + + WebElement textField = driver.findElement(By.id("textInput")); + Assertions.assertEquals("A", textField.getAttribute("value")); + } + + @Test + public void keyUp() { + driver.get("https://www.selenium.dev/selenium/web/single_text_input.html"); + + new Actions(driver) + .keyDown(Keys.SHIFT) + .sendKeys("a") + .keyUp(Keys.SHIFT) + .sendKeys("b") + .perform(); + + WebElement textField = driver.findElement(By.id("textInput")); + Assertions.assertEquals("Ab", textField.getAttribute("value")); + } + + @Test + public void sendKeysToActiveElement() { + driver.get("https://www.selenium.dev/selenium/web/single_text_input.html"); + + new Actions(driver) + .sendKeys("abc") + .perform(); + + WebElement textField = driver.findElement(By.id("textInput")); + Assertions.assertEquals("abc", textField.getAttribute("value")); + } + + @Test + public void sendKeysToDesignatedElement() { + driver.get("https://www.selenium.dev/selenium/web/single_text_input.html"); + driver.findElement(By.tagName("body")).click(); + + WebElement textField = driver.findElement(By.id("textInput")); + new Actions(driver) + .sendKeys(textField, "Selenium!") + .perform(); + + Assertions.assertEquals("Selenium!", textField.getAttribute("value")); + } + + @Test + public void copyAndPaste() { + driver.get("https://www.selenium.dev/selenium/web/single_text_input.html"); + + Platform platformName = ((HasCapabilities) driver).getCapabilities().getPlatformName(); + + Keys cmdCtrl = platformName.is(Platform.MAC) ? Keys.COMMAND : Keys.CONTROL; + + WebElement textField = driver.findElement(By.id("textInput")); + new Actions(driver) + .sendKeys(textField, "Selenium!") + .sendKeys(Keys.ARROW_LEFT) + .keyDown(Keys.SHIFT) + .sendKeys(Keys.ARROW_UP) + .keyUp(Keys.SHIFT) + .keyDown(cmdCtrl) + .sendKeys("xvv") + .keyUp(cmdCtrl) + .perform(); + + Assertions.assertEquals("SeleniumSelenium!", textField.getAttribute("value")); + } +} diff --git a/examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java b/examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java new file mode 100644 index 000000000000..2bc1fdc972fc --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java @@ -0,0 +1,198 @@ +package dev.selenium.actions_api; + +import dev.selenium.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Rectangle; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.interactions.PointerInput; +import org.openqa.selenium.interactions.Sequence; +import org.openqa.selenium.remote.RemoteWebDriver; + +import java.time.Duration; +import java.util.Collections; + +public class MouseTest extends BaseTest { + @Test + public void clickAndHold() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + WebElement clickable = driver.findElement(By.id("clickable")); + new Actions(driver) + .clickAndHold(clickable) + .perform(); + + Assertions.assertEquals("focused", driver.findElement(By.id("click-status")).getText()); + } + + @Test + public void clickAndRelease() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + WebElement clickable = driver.findElement(By.id("click")); + new Actions(driver) + .click(clickable) + .perform(); + + Assertions.assertTrue(driver.getCurrentUrl().contains("resultPage.html")); + } + + @Test + public void rightClick() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + WebElement clickable = driver.findElement(By.id("clickable")); + new Actions(driver) + .contextClick(clickable) + .perform(); + + Assertions.assertEquals("context-clicked", driver.findElement(By.id("click-status")).getText()); + } + + @Test + public void backClick() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + driver.findElement(By.id("click")).click(); + Assertions.assertEquals(driver.getTitle(), "We Arrive Here"); + + PointerInput mouse = new PointerInput(PointerInput.Kind.MOUSE, "default mouse"); + + Sequence actions = new Sequence(mouse, 0) + .addAction(mouse.createPointerDown(3)) + .addAction(mouse.createPointerUp(3)); + + ((RemoteWebDriver) driver).perform(Collections.singletonList(actions)); + + Assertions.assertEquals("BasicMouseInterfaceTest", driver.getTitle()); + } + + @Test + public void forwardClick() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + driver.findElement(By.id("click")).click(); + driver.navigate().back(); + Assertions.assertEquals(driver.getTitle(), "BasicMouseInterfaceTest"); + + PointerInput mouse = new PointerInput(PointerInput.Kind.MOUSE, "default mouse"); + + Sequence actions = new Sequence(mouse, 0) + .addAction(mouse.createPointerDown(4)) + .addAction(mouse.createPointerUp(4)); + + ((RemoteWebDriver) driver).perform(Collections.singletonList(actions)); + + Assertions.assertEquals("We Arrive Here", driver.getTitle()); + } + + @Test + public void doubleClick() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + WebElement clickable = driver.findElement(By.id("clickable")); + new Actions(driver) + .doubleClick(clickable) + .perform(); + + Assertions.assertEquals("double-clicked", driver.findElement(By.id("click-status")).getText()); + } + + @Test + public void hovers() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + WebElement hoverable = driver.findElement(By.id("hover")); + new Actions(driver) + .moveToElement(hoverable) + .perform(); + + Assertions.assertEquals("hovered", driver.findElement(By.id("move-status")).getText()); + } + + @Test + public void moveByOffsetFromElement() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + WebElement tracker = driver.findElement(By.id("mouse-tracker")); + new Actions(driver) + .moveToElement(tracker, 8, 11) + .perform(); + + String[] result = driver.findElement(By.id("relative-location")).getText().split(", "); + Assertions.assertTrue(Math.abs(Integer.parseInt(result[0]) - 100 - 8) < 2); + Assertions.assertTrue(Math.abs(Integer.parseInt(result[1]) - 100 - 11) < 2); + } + + @Test + public void moveByOffsetFromViewport() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + PointerInput mouse = new PointerInput(PointerInput.Kind.MOUSE, "default mouse"); + + Sequence actions = new Sequence(mouse, 0) + .addAction(mouse.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 8, 12)); + + ((RemoteWebDriver) driver).perform(Collections.singletonList(actions)); + + String[] result = driver.findElement(By.id("absolute-location")).getText().split(", "); + Assertions.assertTrue(Math.abs(Integer.parseInt(result[0]) - 8) < 2); + Assertions.assertTrue(Math.abs(Integer.parseInt(result[1]) - 12) < 2); + } + + @Test + public void moveByOffsetFromCurrentPointer() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + PointerInput mouse = new PointerInput(PointerInput.Kind.MOUSE, "default mouse"); + + Sequence actions = new Sequence(mouse, 0) + .addAction(mouse.createPointerMove(Duration.ZERO, PointerInput.Origin.viewport(), 8, 11)); + ((RemoteWebDriver) driver).perform(Collections.singletonList(actions)); + + new Actions(driver) + .moveByOffset(13, 15) + .perform(); + + String[] result = driver.findElement(By.id("absolute-location")).getText().split(", "); + Assertions.assertTrue(Math.abs(Integer.parseInt(result[0]) - 8 - 13) < 2); + Assertions.assertTrue(Math.abs(Integer.parseInt(result[1]) - 11 - 15) < 2); + } + + @Test + public void dragsToElement() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + WebElement draggable = driver.findElement(By.id("draggable")); + WebElement droppable = driver.findElement(By.id("droppable")); + new Actions(driver) + .dragAndDrop(draggable, droppable) + .perform(); + + Assertions.assertEquals("dropped", driver.findElement(By.id("drop-status")).getText()); + } + + @Test + public void dragsByOffset() { + driver.get("https://www.selenium.dev/selenium/web/mouse_interaction.html"); + + WebElement draggable = driver.findElement(By.id("draggable")); + Rectangle start = draggable.getRect(); + Rectangle finish = driver.findElement(By.id("droppable")).getRect(); + new Actions(driver) + .dragAndDropBy(draggable, finish.getX() - start.getX(), finish.getY() - start.getY()) + .perform(); + + Assertions.assertEquals("dropped", driver.findElement(By.id("drop-status")).getText()); + } +} + + + + + + + + + + diff --git a/examples/java/src/test/java/dev/selenium/actions_api/PenTest.java b/examples/java/src/test/java/dev/selenium/actions_api/PenTest.java new file mode 100644 index 000000000000..bd45fe7d3bad --- /dev/null +++ b/examples/java/src/test/java/dev/selenium/actions_api/PenTest.java @@ -0,0 +1,72 @@ +package dev.selenium.actions_api; + +import dev.selenium.BaseTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Rectangle; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.interactions.PointerInput; +import org.openqa.selenium.interactions.Sequence; +import org.openqa.selenium.remote.RemoteWebDriver; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class PenTest extends BaseTest { + @Test + public void usePen() { + driver.get("https://www.selenium.dev/selenium/web/pointerActionsPage.html"); + + WebElement pointerArea = driver.findElement(By.id("pointerArea")); + PointerInput pen = new PointerInput(PointerInput.Kind.PEN, "default pen"); + PointerInput.Origin origin = PointerInput.Origin.fromElement(pointerArea); + + Sequence actionListPen = new Sequence(pen, 0) + .addAction(pen.createPointerMove(Duration.ZERO, origin, 0, 0)) + .addAction(pen.createPointerDown(0)) + .addAction(pen.createPointerMove(Duration.ZERO, origin, 2, 2)) + .addAction(pen.createPointerUp(0)); + + ((RemoteWebDriver) driver).perform(Collections.singletonList(actionListPen)); + + List moves = driver.findElements(By.className("pointermove")); + Map moveTo = getPropertyInfo(moves.get(0)); + Map down = getPropertyInfo(driver.findElement(By.className("pointerdown"))); + Map moveBy = getPropertyInfo(moves.get(1)); + Map up = getPropertyInfo(driver.findElement(By.className("pointerup"))); + + Rectangle rect = pointerArea.getRect(); + + int centerX = (int) Math.floor(rect.width / 2 + rect.getX()); + int centerY = (int) Math.floor(rect.height / 2 + rect.getY()); + Assertions.assertEquals("-1", moveTo.get("button")); + Assertions.assertEquals(String.valueOf(centerX), moveTo.get("pageX")); + Assertions.assertEquals(String.valueOf(centerY), moveTo.get("pageY")); + Assertions.assertEquals("0", down.get("button")); + Assertions.assertEquals(String.valueOf(centerX), down.get("pageX")); + Assertions.assertEquals(String.valueOf(centerY), down.get("pageY")); + Assertions.assertEquals("-1", moveTo.get("button")); + Assertions.assertEquals(String.valueOf(centerX + 2), moveBy.get("pageX")); + Assertions.assertEquals(String.valueOf(centerY + 2), moveBy.get("pageY")); + Assertions.assertEquals("0", up.get("button")); + Assertions.assertEquals(String.valueOf(centerX + 2), up.get("pageX")); + Assertions.assertEquals(String.valueOf(centerY + 2), up.get("pageY")); + } + + private Map getPropertyInfo(WebElement element) { + String text = element.getText(); + text = text.substring(text.indexOf(' ') + 1); + + return Arrays.stream(text.split(", ")) + .map(s -> s.split(": ")) + .collect(Collectors.toMap( + a -> a[0], + a -> a[1] + )); + } +} diff --git a/examples/python/requirements.txt b/examples/python/requirements.txt index 1408b5c0c5dc..d4e7909e84ab 100644 --- a/examples/python/requirements.txt +++ b/examples/python/requirements.txt @@ -1,4 +1,4 @@ -selenium==4.1.3 +selenium==4.1.0 pytest flake8 webdriver_manager diff --git a/examples/python/tests/actions_api/test_actions.py b/examples/python/tests/actions_api/test_actions.py new file mode 100644 index 000000000000..8618e11c735f --- /dev/null +++ b/examples/python/tests/actions_api/test_actions.py @@ -0,0 +1,43 @@ +from time import time + +from selenium.webdriver import Keys, ActionChains +from selenium.webdriver.common.actions.action_builder import ActionBuilder +from selenium.webdriver.common.by import By + + +def test_pauses(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + start = time() + + clickable = driver.find_element(By.ID, "clickable") + ActionChains(driver)\ + .move_to_element(clickable)\ + .pause(1)\ + .click_and_hold()\ + .pause(1)\ + .send_keys("abc")\ + .perform() + + duration = time() - start + assert duration > 2 + assert duration < 3 + + +def test_releases_all(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + clickable = driver.find_element(By.ID, "clickable") + ActionChains(driver)\ + .click_and_hold(clickable)\ + .key_down(Keys.SHIFT)\ + .key_down(Keys.ALT)\ + .key_down("a")\ + .perform() + + ActionBuilder(driver).clear_actions() + + ActionChains(driver).key_down('a').perform() + + assert clickable.get_attribute('value')[0] == "Å" + assert clickable.get_attribute('value')[1] == "a" diff --git a/examples/python/tests/actions_api/test_keys.py b/examples/python/tests/actions_api/test_keys.py new file mode 100644 index 000000000000..66c8aeafcde8 --- /dev/null +++ b/examples/python/tests/actions_api/test_keys.py @@ -0,0 +1,68 @@ +import sys + +from selenium.webdriver import Keys, ActionChains +from selenium.webdriver.common.by import By + + +def test_key_down(driver): + driver.get('https://selenium.dev/selenium/web/single_text_input.html') + + ActionChains(driver)\ + .key_down(Keys.SHIFT)\ + .send_keys("abc")\ + .perform() + + assert driver.find_element(By.ID, "textInput").get_attribute('value') == "ABC" + + +def test_key_up(driver): + driver.get('https://selenium.dev/selenium/web/single_text_input.html') + + ActionChains(driver)\ + .key_down(Keys.SHIFT)\ + .send_keys("a")\ + .key_up(Keys.SHIFT)\ + .send_keys("b")\ + .perform() + + assert driver.find_element(By.ID, "textInput").get_attribute('value') == "Ab" + + +def test_send_keys_to_active_element(driver): + driver.get('https://selenium.dev/selenium/web/single_text_input.html') + + ActionChains(driver)\ + .send_keys("abc")\ + .perform() + + assert driver.find_element(By.ID, "textInput").get_attribute('value') == "abc" + + +def test_send_keys_to_designated_element(driver): + driver.get('https://selenium.dev/selenium/web/single_text_input.html') + driver.find_element(By.TAG_NAME, "body").click() + + text_input = driver.find_element(By.ID, "textInput") + ActionChains(driver)\ + .send_keys_to_element(text_input, "abc")\ + .perform() + + assert driver.find_element(By.ID, "textInput").get_attribute('value') == "abc" + + +def test_copy_and_paste(driver): + driver.get('https://selenium.dev/selenium/web/single_text_input.html') + cmd_ctrl = Keys.COMMAND if sys.platform == 'darwin' else Keys.CONTROL + + ActionChains(driver)\ + .send_keys("Selenium!")\ + .send_keys(Keys.ARROW_LEFT)\ + .key_down(Keys.SHIFT)\ + .send_keys(Keys.ARROW_UP)\ + .key_up(Keys.SHIFT)\ + .key_down(cmd_ctrl)\ + .send_keys("xvv")\ + .key_up(cmd_ctrl)\ + .perform() + + assert driver.find_element(By.ID, "textInput").get_attribute('value') == "SeleniumSelenium!" diff --git a/examples/python/tests/actions_api/test_mouse.py b/examples/python/tests/actions_api/test_mouse.py new file mode 100644 index 000000000000..018910d1a782 --- /dev/null +++ b/examples/python/tests/actions_api/test_mouse.py @@ -0,0 +1,164 @@ +from time import sleep + +from selenium.webdriver import ActionChains +from selenium.webdriver.common.actions.action_builder import ActionBuilder +from selenium.webdriver.common.by import By + + +def test_click_and_hold(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + clickable = driver.find_element(By.ID, "clickable") + ActionChains(driver)\ + .click_and_hold(clickable)\ + .perform() + + sleep(0.5) + assert driver.find_element(By.ID, "click-status").text == "focused" + + +def test_click_and_release(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + clickable = driver.find_element(By.ID, "click") + ActionChains(driver)\ + .click(clickable)\ + .perform() + + assert "resultPage.html" in driver.current_url + + +def test_right_click(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + clickable = driver.find_element(By.ID, "clickable") + ActionChains(driver)\ + .context_click(clickable)\ + .perform() + + sleep(0.5) + assert driver.find_element(By.ID, "click-status").text == "context-clicked" + + +def test_back_click_ab(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + driver.find_element(By.ID, "click").click() + assert driver.title == "We Arrive Here" + + action = ActionBuilder(driver) + action.pointer_action.pointer_down(3) + action.pointer_action.pointer_up(3) + action.perform() + + assert driver.title == "BasicMouseInterfaceTest" + + +def test_forward_click_ab(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + driver.find_element(By.ID, "click").click() + driver.back() + assert driver.title == "BasicMouseInterfaceTest" + + action = ActionBuilder(driver) + action.pointer_action.pointer_down(4) + action.pointer_action.pointer_up(4) + action.perform() + + assert driver.title == "We Arrive Here" + + +def test_double_click(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + clickable = driver.find_element(By.ID, "clickable") + ActionChains(driver)\ + .double_click(clickable)\ + .perform() + + assert driver.find_element(By.ID, "click-status").text == "double-clicked" + + +def test_hover(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + hoverable = driver.find_element(By.ID, "hover") + ActionChains(driver)\ + .move_to_element(hoverable)\ + .perform() + + assert driver.find_element(By.ID, "move-status").text == "hovered" + + +def test_move_by_offset_from_element(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + mouse_tracker = driver.find_element(By.ID, "mouse-tracker") + ActionChains(driver)\ + .move_to_element_with_offset(mouse_tracker, 8, 11)\ + .perform() + + coordinates = driver.find_element(By.ID, "relative-location").text.split(", ") + assert abs(int(coordinates[0]) - 8) < 2 + assert abs(int(coordinates[1]) - 11) < 2 + + +def test_move_by_offset_from_viewport_origin_ab(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + action = ActionBuilder(driver) + action.pointer_action.move_to_location(8, 12) + action.perform() + + coordinates = driver.find_element(By.ID, "absolute-location").text.split(", ") + + assert abs(int(coordinates[0]) - 8) < 2 + assert abs(int(coordinates[1]) - 11) < 2 + + +def test_move_by_offset_from_current_pointer_ab(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + action = ActionBuilder(driver) + action.pointer_action.move_to_location(6, 3) + action.perform() + + ActionChains(driver)\ + .move_by_offset( 13, 15)\ + .perform() + + coordinates = driver.find_element(By.ID, "absolute-location").text.split(", ") + + assert abs(int(coordinates[0]) - 6 - 13) < 2 + assert abs(int(coordinates[1]) - 3 - 15) < 2 + + +def test_drag_and_drop_onto_element(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + draggable = driver.find_element(By.ID, "draggable") + droppable = driver.find_element(By.ID, "droppable") + ActionChains(driver)\ + .drag_and_drop(draggable, droppable)\ + .perform() + + assert driver.find_element(By.ID, "drop-status").text == "dropped" + + +def test_drag_and_drop_by_offset(driver): + driver.get('https://selenium.dev/selenium/web/mouse_interaction.html') + + draggable = driver.find_element(By.ID, "draggable") + start = draggable.location + finish = driver.find_element(By.ID, "droppable").location + ActionChains(driver)\ + .drag_and_drop_by_offset(draggable, finish['x'] - start['x'], finish['y'] - start['y'])\ + .perform() + + assert driver.find_element(By.ID, "drop-status").text == "dropped" + + + + + + + diff --git a/examples/python/tests/actions_api/test_pen.py b/examples/python/tests/actions_api/test_pen.py new file mode 100644 index 000000000000..7e2b3fa069a0 --- /dev/null +++ b/examples/python/tests/actions_api/test_pen.py @@ -0,0 +1,50 @@ +import math + +from selenium.webdriver.common.actions.action_builder import ActionBuilder +from selenium.webdriver.common.actions.interaction import POINTER_PEN +from selenium.webdriver.common.by import By + + +def test_use_pen(driver): + driver.get('https://www.selenium.dev/selenium/web/pointerActionsPage.html') + + pointer_area = driver.find_element(By.ID, "pointerArea") + action = ActionBuilder(driver) + action.add_pointer_input(POINTER_PEN, "default pen") + action.pointer_action\ + .move_to(pointer_area)\ + .pointer_down(0) + action.pointer_action\ + .move_by(2, 2)\ + .pointer_up(0) + action.perform() + + moves = driver.find_elements(By.CLASS_NAME, "pointermove") + move_to = properties(moves[0]) + down = properties(driver.find_element(By.CLASS_NAME, "pointerdown")) + move_by = properties(moves[1]) + up = properties(driver.find_element(By.CLASS_NAME, "pointerup")) + + rect = pointer_area.rect + center_x = rect["x"] + rect["width"] / 2 + center_y = rect["y"] + rect["height"] / 2 + + assert move_to["button"] == "-1" + assert move_to["pageX"] == str(math.floor(center_x)) + assert move_to["pageY"] == str(math.floor(center_y)) + assert down["button"] == "0" + assert down["pageX"] == str(math.floor(center_x)) + assert down["pageY"] == str(math.floor(center_y)) + assert move_by["button"] == "-1" + assert move_by["pageX"] == str(math.floor(center_x + 2)) + assert move_by["pageY"] == str(math.floor(center_y + 2)) + assert up["button"] == "0" + assert up["pageX"] == str(math.floor(center_x + 2)) + assert up["pageY"] == str(math.floor(center_y + 2)) + + +def properties(element): + kv = element.text.split(' ', 1)[1].split(', ') + return {x[0]:x[1] for x in list(map(lambda item: item.split(': '), kv))} + + diff --git a/examples/python/tests/conftest.py b/examples/python/tests/conftest.py new file mode 100644 index 000000000000..0e4553f11a56 --- /dev/null +++ b/examples/python/tests/conftest.py @@ -0,0 +1,14 @@ +import pytest +from selenium import webdriver +from selenium.webdriver.chrome.service import Service as ChromeService +from webdriver_manager.chrome import ChromeDriverManager + + +@pytest.fixture(scope='function') +def driver(): + service = ChromeService(executable_path=ChromeDriverManager().install()) + driver = webdriver.Chrome(service=service) + + yield driver + + driver.quit() diff --git a/examples/ruby/Gemfile b/examples/ruby/Gemfile index 3d4a25c60f57..f9b5877af2f6 100644 --- a/examples/ruby/Gemfile +++ b/examples/ruby/Gemfile @@ -4,6 +4,5 @@ source 'https://rubygems.org' gem 'rake', '~> 13.0' gem 'rspec', '~> 3.0' -gem 'selenium-webdriver', '~> 4.1' +gem 'selenium-webdriver', '= 4.1.0' gem 'webdrivers', '~> 5.0' -gem 'nokogiri', '~> 1.13' diff --git a/examples/ruby/Gemfile.lock b/examples/ruby/Gemfile.lock index 699aa6f5cbdb..0dd88275a091 100644 --- a/examples/ruby/Gemfile.lock +++ b/examples/ruby/Gemfile.lock @@ -19,12 +19,12 @@ GEM rspec-expectations (3.11.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.11.0) - rspec-mocks (3.11.0) + rspec-mocks (3.11.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.11.0) rspec-support (3.11.0) rubyzip (2.3.2) - selenium-webdriver (4.1.0) + selenium-webdriver (4.0.0) childprocess (>= 0.5, < 5.0) rexml (~> 3.2, >= 3.2.5) rubyzip (>= 1.2.2) @@ -35,13 +35,13 @@ GEM PLATFORMS x86_64-darwin-19 + x86_64-darwin-20 x86_64-linux DEPENDENCIES - nokogiri (~> 1.13) rake (~> 13.0) rspec (~> 3.0) - selenium-webdriver (~> 4.1) + selenium-webdriver (= 4.0.0) webdrivers (~> 5.0) BUNDLED WITH diff --git a/examples/ruby/spec/actions_api/actions_spec.rb b/examples/ruby/spec/actions_api/actions_spec.rb new file mode 100644 index 000000000000..a3008f4248ac --- /dev/null +++ b/examples/ruby/spec/actions_api/actions_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Actions' do + it 'pauses' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + start = Time.now + + clickable = @driver.find_element(id: 'clickable') + @driver.action + .move_to(clickable) + .pause(action.get_device('mouse'), 1) + .click_and_hold + .pause(action.get_device('keyboard'), 1) + .send_keys("abc") + .perform + + duration = Time.now - start + expect(duration).to be > 2 + expect(duration).to be < 3 + end + + it 'releases all' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + clickable = @driver.find_element(id: 'clickable') + action = @driver.action + .click_and_hold(clickable) + .key_down(:shift) + .key_down(:alt) + .key_down("a") + action.perform + + @driver.action.release_actions + + action.key_down('a').perform + expect(clickable.attribute('value')[0]).to eq "Å" + expect(clickable.attribute('value')[-1]).to eq "a" + end +end diff --git a/examples/ruby/spec/actions_api/keys_spec.rb b/examples/ruby/spec/actions_api/keys_spec.rb new file mode 100644 index 000000000000..c9d78aa1276d --- /dev/null +++ b/examples/ruby/spec/actions_api/keys_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Keys' do + it 'key down' do + @driver.get 'https://www.selenium.dev/selenium/web/single_text_input.html' + @wait.until { @driver.find_element(id: 'textInput').attribute('autofocus') } + + @driver.action + .key_down(:shift) + .send_keys('a') + .perform + + expect(@driver.find_element(id: 'textInput').attribute('value')).to eq "A" + end + + it 'key up' do + @driver.get 'https://www.selenium.dev/selenium/web/single_text_input.html' + @wait.until { @driver.find_element(id: 'textInput').attribute('autofocus') } + + @driver.action + .key_down(:shift) + .send_keys('a') + .key_up(:shift) + .send_keys('b') + .perform + + expect(@driver.find_element(id: 'textInput').attribute('value')).to eq "Ab" + end + + it 'sends keys to active element' do + @driver.get 'https://www.selenium.dev/selenium/web/single_text_input.html' + @wait.until { @driver.find_element(id: 'textInput').attribute('autofocus') } + + @driver.action + .send_keys('abc') + .perform + + expect(@driver.find_element(id: 'textInput').attribute('value')).to eq "abc" + end + + it 'sends keys to designated element' do + @driver.get 'https://www.selenium.dev/selenium/web/single_text_input.html' + @driver.find_element(tag_name: 'body').click + @wait.until { @driver.find_element(id: 'textInput').attribute('autofocus') } + + text_field = @driver.find_element(id: 'textInput') + @driver.action + .send_keys(text_field, 'Selenium!') + .perform + + expect(text_field.attribute('value')).to eq "Selenium!" + end + + it 'copy and paste' do + @driver.get 'https://www.selenium.dev/selenium/web/single_text_input.html' + @wait.until { @driver.find_element(id: 'textInput').attribute('autofocus') } + + @cmd_ctrl = @driver.capabilities.platform_name.include?('mac') ? :command : :control + @driver.action + .send_keys('Selenium!') + .send_keys(:arrow_left) + .key_down(:shift) + .send_keys(:arrow_up) + .key_up(:shift) + .key_down(@cmd_ctrl) + .send_keys('xvv') + .key_up(@cmd_ctrl) + .perform + + expect(@driver.find_element(id: 'textInput').attribute('value')).to eq "SeleniumSelenium!" + end +end diff --git a/examples/ruby/spec/actions_api/mouse_spec.rb b/examples/ruby/spec/actions_api/mouse_spec.rb new file mode 100644 index 000000000000..a849c9d1fc2e --- /dev/null +++ b/examples/ruby/spec/actions_api/mouse_spec.rb @@ -0,0 +1,152 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Mouse' do + it 'click and hold' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + clickable = @driver.find_element(id: 'clickable') + @driver.action + .click_and_hold(clickable) + .perform + + expect(@driver.find_element(id: 'click-status').text).to eq 'focused' + end + + it 'click and release' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + clickable = @driver.find_element(id: 'click') + @driver.action + .click(clickable) + .perform + + expect(@driver.current_url).to include 'resultPage.html' + end + + it 'right click' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + clickable = @driver.find_element(id: 'clickable') + @driver.action + .context_click(clickable) + .perform + + expect(@driver.find_element(id: 'click-status').text).to eq 'context-clicked' + end + + it 'back click' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + @driver.find_element(id: 'click').click + expect(@driver.title).to eq("We Arrive Here") + + @driver.action + .pointer_down(3) + .pointer_up(3) + .perform + + expect(@driver.title).to eq("BasicMouseInterfaceTest") + end + + it 'forward click' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + @driver.find_element(id: 'click').click + @driver.navigate.back + expect(@driver.title).to eq("BasicMouseInterfaceTest") + + @driver.action + .pointer_down(4) + .pointer_up(4) + .perform + + expect(@driver.title).to eq("We Arrive Here") + end + + it 'double click' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + clickable = @driver.find_element(id: 'clickable') + @driver.action + .double_click(clickable) + .perform + + expect(@driver.find_element(id: 'click-status').text).to eq 'double-clicked' + end + + it 'hovers' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + hoverable = @driver.find_element(id: 'hover') + @driver.action + .move_to(hoverable) + .perform + + expect(@driver.find_element(id: 'move-status').text).to eq 'hovered' + end + + it 'moves mouse by offset' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + mouse_tracker = @driver.find_element(id: 'mouse-tracker') + @driver.action + .move_to(mouse_tracker, 8, 11) + .perform + + sleep 0.5 + x_coord, y_coord = @driver.find_element(id: 'relative-location').text.split(',').map(&:to_i) + expect(x_coord).to be_within(1).of(8) + expect(y_coord).to be_within(1).of(11) + end + + it 'moves mouse by offset from viewport origin' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + @driver.action + .move_to_location(8, 12) + .perform + + x_coord, y_coord = @driver.find_element(id: 'absolute-location').text.split(',').map(&:to_i) + expect(x_coord).to be_within(1).of(8) + expect(y_coord).to be_within(1).of(12) + end + + it 'moves mouse by offset from current pointer location' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + @driver.action.move_to_location(8, 11).perform + + @driver.action + .move_by(13, 15) + .perform + + x_coord, y_coord = @driver.find_element(id: 'absolute-location').text.split(',').map(&:to_i) + expect(x_coord).to be_within(1).of(8 + 13) + expect(y_coord).to be_within(1).of(11 + 15) + end + + it 'drags to another element' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + draggable = @driver.find_element(id: 'draggable') + droppable = @driver.find_element(id: 'droppable') + @driver.action + .drag_and_drop(draggable, droppable) + .perform + + expect(@driver.find_element(id: 'drop-status').text).to include("dropped") + end + + it 'drags by an offset' do + @driver.get 'https://www.selenium.dev/selenium/web/mouse_interaction.html' + + draggable = @driver.find_element(id: 'draggable') + start = draggable.rect + finish = @driver.find_element(id: 'droppable').rect + @driver.action + .drag_and_drop_by(draggable, finish.x - start.x, finish.y - start.y) + .perform + + expect(@driver.find_element(id: 'drop-status').text).to include("dropped") + end +end diff --git a/examples/ruby/spec/actions_api/pen_spec.rb b/examples/ruby/spec/actions_api/pen_spec.rb new file mode 100644 index 000000000000..1cda0cb4e9b7 --- /dev/null +++ b/examples/ruby/spec/actions_api/pen_spec.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Pen' do + it 'uses a pen' do + @driver.get 'https://www.selenium.dev/selenium/web/pointerActionsPage.html' + + pointer_area = @driver.find_element(id: 'pointerArea') + actions = @driver.action + actions.add_pointer_input(:pen, 'default pen') + actions.move_to(pointer_area, device: 'default pen') + .pointer_down(:left, device: 'default pen') + .move_by(2, 2, device: 'default pen') + .pointer_up(:left, device: 'default pen') + actions.perform + + moves = @driver.find_elements(class: 'pointermove') + move_to = properties(moves[0]) + down = properties(@driver.find_element(class: 'pointerdown')) + move_by = properties(moves[1]) + up = properties(@driver.find_element(class: 'pointerup')) + + rect = pointer_area.rect + center_x = rect.x + rect.width / 2 + center_y = rect.y + rect.height / 2 + + expect(move_to).to include("button" => "-1", + "pageX" => (center_x).to_s, + "pageY" => (center_y).floor.to_s) + expect(down).to include("button" => "0", + "pageX" => (center_x).to_s, + "pageY" => (center_y).floor.to_s) + expect(move_by).to include("button" => "-1", + "pageX" => (center_x + 2).to_s, + "pageY" => (center_y + 2).floor.to_s) + expect(up).to include("button" => "0", + "pageX" => (center_x + 2).to_s, + "pageY" => (center_y + 2).floor.to_s) + end + + def properties(element) + element.text.sub(/.*?\s/, '').split(',').map { |item| item.lstrip.split /\s*:\s*/ }.to_h + end +end diff --git a/examples/ruby/spec/getting_started/open_browser_spec.rb b/examples/ruby/spec/getting_started/open_browser_spec.rb index 37850f27e128..8075b3bf0d11 100644 --- a/examples/ruby/spec/getting_started/open_browser_spec.rb +++ b/examples/ruby/spec/getting_started/open_browser_spec.rb @@ -1,5 +1,5 @@ # frozen_string_literal: true -require 'spec_helper' +require 'selenium-webdriver' RSpec.describe 'Open Browser' do it 'chrome session' do diff --git a/examples/ruby/spec/spec_helper.rb b/examples/ruby/spec/spec_helper.rb index 1444d9a1af38..21f1fffcc185 100644 --- a/examples/ruby/spec/spec_helper.rb +++ b/examples/ruby/spec/spec_helper.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "selenium-webdriver" +require "webdrivers" RSpec.configure do |config| # Enable flags like --only-failures and --next-failure @@ -12,4 +13,10 @@ config.expect_with :rspec do |c| c.syntax = :expect end + + config.before do + @driver = Selenium::WebDriver.for :chrome + @wait = Selenium::WebDriver::Wait.new(timeout: 2) + end + config.after { @driver.quit } end diff --git a/website_and_docs/assets/scss/_tabs.scss b/website_and_docs/assets/scss/_tabs.scss new file mode 100644 index 000000000000..fa74badcede5 --- /dev/null +++ b/website_and_docs/assets/scss/_tabs.scss @@ -0,0 +1,67 @@ +.height-auto { + height: auto; +} + +.height-1 { + height: 100px; +} + +.height-2 { + height: 125px; +} + +.height-3 { + height: 150px; +} + +.height-4 { + height: 175px; +} + +.height-5 { + height: 200px; +} + +.height-6 { + height: 225px; +} + +.height-7 { + height: 250px; +} + +.height-8 { + height: 275px; +} + +.height-9 { + height: 300px; +} + +.height-10 { + height: 325px; +} + +.height-11 { + height: 350px; +} + +.height-12 { + height: 375px; +} + +.height-13 { + height: 400px; +} + +.height-14 { + height: 425px; +} + +.height-15 { + height: 450px; +} + +.my-3 .highlight { + margin: 0; +} diff --git a/website_and_docs/assets/scss/main.scss b/website_and_docs/assets/scss/main.scss index 730283578e87..e3b59b774eb1 100644 --- a/website_and_docs/assets/scss/main.scss +++ b/website_and_docs/assets/scss/main.scss @@ -8,3 +8,4 @@ @import "links"; @import "logo"; @import "screen"; +@import "tabs"; diff --git a/website_and_docs/content/documentation/about/style.en.md b/website_and_docs/content/documentation/about/style.en.md index 1826938db8d1..7cd4fdf2483a 100644 --- a/website_and_docs/content/documentation/about/style.en.md +++ b/website_and_docs/content/documentation/about/style.en.md @@ -210,7 +210,8 @@ Maybe you want something like this: For this you need to use `disableCodeBlock=true` instead of `langEqualsHeader=true` -You need to specify which parts are code and which are not yourself now, like this: +You need to be explicit about which parts are code and which are not, +do not indent plain text or it will still be treated like a codeblock: {{}} {{}} @@ -246,6 +247,17 @@ Which looks like this: {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/getting_started/FirstScriptTest.java#L15-L37" >}} +### Consistent Heights + +Typically, a user will pick their language and not switch tabs again. If they do, however, +the entire page will shift because the height of the block is based on the amount of code in the tab. +This can get especially jarring at the bottom of a page with a lot of tabs. + +You can add a "height" value to the `tabpane` that corresponds to the lines of code or some +equivalent for text: + + {{}} + ### Code Comments Minimize code comments because they are difficult to translate. diff --git a/website_and_docs/content/documentation/webdriver/actions_api/_index.en.md b/website_and_docs/content/documentation/webdriver/actions_api/_index.en.md index b8a8d78dc1c9..ef625759b6fc 100644 --- a/website_and_docs/content/documentation/webdriver/actions_api/_index.en.md +++ b/website_and_docs/content/documentation/webdriver/actions_api/_index.en.md @@ -6,9 +6,78 @@ description: > A low-level interface for providing virtualised device input to the web browser. --- -Unlike the high-level [element interactions]({{< ref "/documentation/webdriver/elements/interactions.md" >}}), -which conducts additional validations, -the [Actions API](https://w3c.github.io/webdriver/#dfn-actions) provides granular control over input devices. +in addition to the high-level [element interactions]({{< ref "/documentation/webdriver/elements/interactions.md" >}}), +the [Actions API](https://w3c.github.io/webdriver/#dfn-actions) provides granular control over +exactly what designated input devices can do. Selenium provides an interface for 3 kinds of input sources: +a key input for keyboard devices, a pointer input for a mouse, pen or touch devices, +and wheel inputs for scroll wheel devices (introduced in Selenium 4.2). +Selenium allows you to construct individual action commands assigned to specific +inputs and chain them together and call the associated perform method to execute them all at once. -Selenium provides access to 3 input sources: key inputs for keyboard devices, pointer inputs for a mouse, pen or -touch device, and a wheel inputs for scroll wheel support. +## Action Builder + +In the move from the legacy JSON Wire Protocol to the new W3C WebDriver Protocol, +the low level building blocks of actions became especially detailed. It is extremely +powerful, but each input device has a number of ways to use it and if you need to +manage more than one device, you are responsible for ensuring proper synchronization between them. + +Thankfully, you likely do not need to learn how to use the low level commands directly, since +almost everything you might want to do has been given a convenience method that combines the +lower level commands for you. These are all documented in [keyboard](), [mouse](), and [wheel]() pages. + +## Pause + +Pointer movements and Wheel scrolling allow the user to set a duration for the action, but sometimes you just need +to wait a beat between actions for things to work correctly. + +{{< tabpane disableCodeBlock=true height="8">}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/ActionsTest.java#L21-L28" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_actions.py#L13-L20" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/ActionsTest.cs#L16-L21" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/actions_spec.rb#L10-L17" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} + +## Release All Actions + +An important thing to note is that the driver remembers the state of all the input +items throughout a session. Even if you create a new instance of an actions class, the depressed keys and +the location of the pointer will be in whatever state a previously performed action left them. + +There is a special method to release all currently depressed keys and pointer buttons. +This method is implemented differently in each of the languages because +it does not get executed with the perform method. + +{{< tabpane disableCodeBlock=true height="1">}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/ActionsTest.java#L47" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_actions.py#L38" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/ActionsTest.cs#L41" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/actions_spec.rb#L35" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} diff --git a/website_and_docs/content/documentation/webdriver/actions_api/keyboard.en.md b/website_and_docs/content/documentation/webdriver/actions_api/keyboard.en.md index 103056074c47..665bd44f9f42 100644 --- a/website_and_docs/content/documentation/webdriver/actions_api/keyboard.en.md +++ b/website_and_docs/content/documentation/webdriver/actions_api/keyboard.en.md @@ -10,8 +10,10 @@ aliases: [ ] --- -Keyboard represents a KeyBoard event. KeyBoard actions are performed by using low-level -interface which allows us to provide virtualized device input to the web browser. +There are only 2 actions that can be accomplished with a keyboard: +pressing down on a key, and releasing a pressed key. +In addition to supporting ASCII characters, each keyboard key has +a representation that can be pressed or released in designated sequences. ## Keys @@ -20,343 +22,149 @@ unicode values have been assigned to other keyboard keys for use with Selenium. Each language has its own way to reference these keys; the full list can be found [here](https://www.w3.org/TR/webdriver/#keyboard-actions). -## Key down - -The keyDown is used to simulate action of depressing a key - -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -WebDriver driver = new ChromeDriver(); -try { - // Navigate to Url - driver.get("https://google.com"); - - // Enter "webdriver" text and perform "ENTER" keyboard action - driver.findElement(By.name("q")).sendKeys("webdriver" + Keys.ENTER); - - Actions actionProvider = new Actions(driver); - Action keydown = actionProvider.keyDown(Keys.CONTROL).sendKeys("a").build(); - keydown.perform(); -} finally { - driver.quit(); -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.common.keys import Keys -driver = webdriver.Chrome() - - # Navigate to url -driver.get("http://www.google.com") - - # Enter "webdriver" text and perform "ENTER" keyboard action -driver.find_element(By.NAME, "q").send_keys("webdriver" + Keys.ENTER) - - # Perform action ctrl + A (modifier CONTROL + Alphabet A) to select the page -webdriver.ActionChains(driver).key_down(Keys.CONTROL).send_keys("a").perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -IWebDriver driver = new ChromeDriver(); -try -{ - // Navigate to Url - driver.Navigate().GoToUrl("https://google.com"); - - // Enter "webdriver" text and perform "ENTER" keyboard action - driver.FindElement(By.Name("q")).SendKeys("webdriver" + Keys.Enter); - - // Perform action ctrl + A (modifier CONTROL + Alphabet A) to select the page - Actions actionProvider = new Actions(driver); - IAction keydown = actionProvider.KeyDown(Keys.Control).SendKeys("a").Build(); - keydown.Perform(); -} -finally -{ - driver.Quit(); -} - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome -begin - # Navigate to URL - driver.get 'https://google.com' - - # Enter "webdriver" text and perform "ENTER" keyboard action - driver.find_element(name: 'q').send_keys 'webdriver', :return - - # Perform action ctrl + A (modifier CONTROL + Alphabet A) to select the page - driver.action.key_down(:control).send_keys('a').perform - -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" disableCodeBlock=true >}} - {{< gh-codeblock path="/examples/javascript/actionsApi/keyboard/keyDown.js">}} - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.Keys -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions - -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://google.com") +{{< tabpane disableCodeBlock=true height="1">}} + {{< tab header="Java" >}} +Use the [Java Keys enum](https://github.com/SeleniumHQ/selenium/blob/selenium-4.1.0/java/src/org/openqa/selenium/Keys.java#L28) + {{< /tab >}} + {{< tab header="Python" >}} +Use the [Python Keys class](https://github.com/SeleniumHQ/selenium/blob/selenium-4.1.0/py/selenium/webdriver/common/keys.py#L23) + {{< /tab >}} + {{< tab header="CSharp" >}} +Use the [.NET static Keys class](https://github.com/SeleniumHQ/selenium/blob/selenium-4.1.0/dotnet/src/webdriver/Keys.cs#L28) + {{< /tab >}} + {{< tab header="Ruby" >}} +Use the [Ruby KEYS constant](https://github.com/SeleniumHQ/selenium/blob/selenium-4.1.0/rb/lib/selenium/webdriver/common/keys.rb#L28) + {{< /tab >}} + {{< tab header="JavaScript" >}} +Use the [Ruby KEYS constant](https://github.com/SeleniumHQ/selenium/blob/selenium-4.1.0/javascript/node/selenium-webdriver/lib/input.js#L44) + {{< /tab >}} + {{< tab header="Kotlin" >}} +Use the [Java Keys enum](https://github.com/SeleniumHQ/selenium/blob/selenium-4.1.0/java/src/org/openqa/selenium/Keys.java#L28) + {{< /tab >}} +{{< /tabpane >}} - // Enter "webdriver" text and perform "ENTER" keyboard action - driver.findElement(By.name("q")).sendKeys("webdriver" + Keys.ENTER) - val action = Actions(driver) +## Key down - // Perform action ctrl + A (modifier CONTROL + Alphabet A) to select the page - action.keyDown(Keys.CONTROL).sendKeys("a").build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} +{{< tabpane disableCodeBlock=true height="4">}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/KeysTest.java#L18-L21" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_keys.py#L10-L13" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/KeysTest.cs#L16-L19" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/keys_spec.rb#L10-L13" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} ## Key up -The keyUp is used to simulate key-up (or) key-release action - -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -import org.openqa.selenium.By; -import org.openqa.selenium.Keys; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.firefox.FirefoxDriver; -import org.openqa.selenium.interactions.Actions; - -public class HelloSelenium { - public static void main(String[] args) { - WebDriver driver = new FirefoxDriver(); - try { - // Navigate to Url - driver.get("https://google.com"); - Actions action = new Actions(driver); - - // Store google search box WebElement - WebElement search = driver.findElement(By.name("q")); - - // Enters text "qwerty" with keyDown SHIFT key and after keyUp SHIFT key (QWERTYqwerty) - action.keyDown(Keys.SHIFT).sendKeys(search,"qwerty").keyUp(Keys.SHIFT).sendKeys("qwerty").perform(); - } finally { - driver.quit(); - } - } -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.common.keys import Keys -driver = webdriver.Chrome() - - # Navigate to url -driver.get("http://www.google.com") - - # Store google search box WebElement -search = driver.find_element(By.NAME, "q") - -action = webdriver.ActionChains(driver) - - # Enters text "qwerty" with keyDown SHIFT key and after keyUp SHIFT key (QWERTYqwerty) -action.key_down(Keys.SHIFT).send_keys_to_element(search, "qwerty").key_up(Keys.SHIFT).send_keys("qwerty").perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; - -namespace HelloSelenium -{ - class HelloSelenium - { - public static void Main(string[] args) - { - IWebDriver driver = new ChromeDriver(); - try - { - // Navigate to Url - driver.Navigate().GoToUrl("https://google.com"); - - Actions action = new Actions(driver); - // Store google search box WebElement - IWebElement search = driver.FindElement(By.Name("q")); - - // Enters text "qwerty" with keyDown SHIFT key and after keyUp SHIFT key (QWERTYqwerty) - action.KeyDown(Keys.Shift).SendKeys(search, "qwerty").KeyUp(Keys.Shift).SendKeys("qwerty").Perform(); - - } - finally { - driver.Quit(); - } - } - } -} - - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome -begin - # Navigate to URL - driver.get 'https://google.com' - - # Store google search box WebElement - search = driver.find_element(name: 'q') - - # Enters text "qwerty" with keyDown SHIFT key and after keyUp SHIFT key (QWERTYqwerty) - driver.action.key_down(:shift).send_keys(search,'qwerty').key_up(:shift).send_keys("qwerty").perform - -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" disableCodeBlock=true >}} - {{< gh-codeblock path="/examples/javascript/actionsApi/keyboard/keyUp.js">}} - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.Keys -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions - -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://google.com") - - // Store google search box WebElement - val search = driver.findElement(By.name("q")) - val action = Actions(driver) - - // Enters text "qwerty" with keyDown SHIFT key and after keyUp SHIFT key (QWERTYqwerty) - action.keyDown(Keys.SHIFT).sendKeys(search, "qwerty").keyUp(Keys.SHIFT).sendKeys("qwerty").build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} +{{< tabpane disableCodeBlock=true height="6">}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/KeysTest.java#L31-L36" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_keys.py#L21-L26" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/KeysTest.cs#L30-L35" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/keys_spec.rb#L22-L27" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} ## Send keys This is a convenience method in the Actions API that combines keyDown and keyUp commands in one action. -Executing this command differs slightly from using the element method. - -{{< tabpane langEqualsHeader=true >}} -{{< tab header="Java" >}} -WebDriver driver = new FirefoxDriver(); -try { - -// Navigate to the url -driver.get("https://google.com"); - -// Create an object of Action class -Actions action = new Actions(driver); - -// Find google search box element -WebElement search = driver.findElement(By.name("q")); - -// Send value by action class to the search box -action.sendKeys(search, "Selenium").perform(); - -// Perform Keyboard action by Action class -action.sendKeys(Keys.ENTER).perform(); - -} finally { -driver.quit(); -} - -{{< /tab >}} -{{< tab header="Python" >}} - -from selenium import webdriver -from selenium.webdriver.common.by import By -from selenium.webdriver.common.keys import Keys -from selenium.webdriver.chrome.service import Service -from webdriver_manager.chrome import ChromeDriverManager - -driver = webdriver.Chrome(service=Service(ChromeDriverManager().install())) -driver.get("https://www.selenium.dev/selenium/docs/api/py/genindex.html") - -search = driver.find_element(By.NAME, "q") - -action = webdriver.ActionChains(driver) -action.move_to_element(search).click().send_keys("send_keys", Keys.ENTER).perform() - -{{< /tab >}} -{{< tab header="CSharp" >}} - -IWebDriver driver = new ChromeDriver(); -// Navigate to the url -driver.Url = "https://www.google.com"; -// Create an object of Action class -Actions action = new Actions(driver); -// Find google search box element -IWebElement search = driver.FindElement(By.Name("q")); -// Send value by action class to the search box -action.SendKeys(search, "Selenium").Perform(); -// Perform Keyboard action by Action class -action.SendKeys(Keys.Enter).Perform(); - - -{{< /tab >}} -{{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - # Navigate to URL - driver.get 'https://google.com' - # Store google search box WebElement - search = driver.find_element(name: 'q') - # Get focus on Search - driver.execute_script('arguments[0].focus()', search) - # Send value by action class to the search box - driver.action.send_keys('Selenium').perform - # Perform Keyboard action by Action class - driver.action.send_keys(:enter).perform -ensure - driver.quit -end -{{< /tab >}} - {{< tab header="JavaScript" disableCodeBlock=true >}} - {{< gh-codeblock path="/examples/javascript/actionsApi/keyboard/sendKeysAction.js">}} - {{< /tab >}} -{{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.Keys -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions - -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://www.selenium.dev/selenium/docs/api/java/overview-summary.html") +Executing this command differs slightly from using the element method, but +primarily this gets used when needing to type multiple characters in the middle of other actions. + +### Active Element + +{{< tabpane disableCodeBlock=true height="3">}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/KeysTest.java#L46-L48" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_keys.py#L34-L36" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/KeysTest.cs#L46-L48" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/keys_spec.rb#L36-L38" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} - // Store search box WebElement - val search = driver.findElement(By.id("search")) - val action = Actions(driver) - // Send value by action class to the search box - action.sendKeys(search, "devtools").perform(); +### Designated Element + +{{< tabpane disableCodeBlock=true height="4">}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/KeysTest.java#L59-L62" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_keys.py#L45-L48" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/KeysTest.cs#L60-L63" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/keys_spec.rb#L48-L51" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} - // Perform Keyboard action by Action class - action.sendKeys(search, Keys.ENTER).perform(); - } finally { - driver.quit() - } -} -{{< /tab >}} +## Copy and Paste + +Here's an example of using all of the above methods to conduct a copy / paste action. +Note that the key to use for this operation will be different depending on if it is a Mac OS or not. +This code will end up with the text: `SeleniumSelenium!` + +{{< tabpane disableCodeBlock=true height="13">}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/KeysTest.java#L73-L85" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_keys.py#L55-L66" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/KeysTest.cs#L76-L87" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/keys_spec.rb#L60-L70" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} diff --git a/website_and_docs/content/documentation/webdriver/actions_api/mouse.en.md b/website_and_docs/content/documentation/webdriver/actions_api/mouse.en.md index 75abf0a0e95d..05a998f8113c 100644 --- a/website_and_docs/content/documentation/webdriver/actions_api/mouse.en.md +++ b/website_and_docs/content/documentation/webdriver/actions_api/mouse.en.md @@ -11,1056 +11,367 @@ aliases: [ ] --- -## Click and hold - -It will move to the element and clicks (without releasing) in the middle of the given element. - -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.interactions.Actions; - -public class clickAndHold { - public static void main(String[] args) { - WebDriver driver = new ChromeDriver(); - try { - // Navigate to Url - driver.get("https://google.com"); - - // Store 'signIn' button web element - WebElement signIn = driver.findElement(By.linkText("Sign in")); - Actions actionProvider = new Actions(driver); - // Perform click-and-hold action on the element - actionProvider.clickAndHold(signIn).build().perform(); - } finally { - driver.quit(); - } - } -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -from selenium.webdriver.common.by import By -driver = webdriver.Chrome() +There are only 3 actions that can be accomplished with a mouse: +pressing down on a button, releasing a pressed button, and moving the mouse. +Selenium provides convenience methods that combine these actions in the most common ways. -# Navigate to url -driver.get("http://www.google.com") - -# Store 'Sign In' button web element -signIn = driver.find_element(By.LINK_TEXT, "Sign in") - -# Perform click-and-hold action on the element -webdriver.ActionChains(driver).click_and_hold(signIn).perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; - -namespace SeleniumApp -{ - public class ClickAndHold - { - public static void Main(string[] args) - { - IWebDriver driver = new ChromeDriver(); - try - { - // Navigate to Url - driver.Navigate().GoToUrl("https://google.com"); - // Store 'Sign In' button web element - IWebElement signIn = driver.FindElement(By.LinkText("Sign in")); - Actions actionProvider = new Actions(driver); - // Perform click-and-hold action on the element - actionProvider.ClickAndHold(signIn).Build().Perform(); - } - finally - { - driver.Quit(); - } - } - } -} - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - # Navigate to Url - driver.get 'https://www.google.com' - # Store 'Sign In' button web element - sign_in = driver.find_element(link_text: 'Sign in') - # Perform click-and-hold action on the element - driver.action.click_and_hold(sign_in).perform -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" disableCodeBlock=true >}} - {{< gh-codeblock path="/examples/javascript/actionsApi/mouse/clickAndHold.js">}} - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions +## Click and hold -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://google.com") - // Store 'Sign In' button web element - val signIn = driver.findElement(By.linkText("Sign in")) - val actionProvider = Actions(driver) - // Perform click-and-hold action on the element - actionProvider.clickAndHold(signIn).build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} +This method combines moving the mouse to the center of an element with pressing the left mouse button. +This is useful for focusing a specific element: + +{{< tabpane disableCodeBlock=true height="4" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L22-L25" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L11-L14" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L17-L20" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L9-L12" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} -## Context click -This method firstly performs a mouse-move to the location of the element and performs the context-click (right click) on the given element. - -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.interactions.Actions; - -public class contextClick { - public static void main(String[] args) { - WebDriver driver = new ChromeDriver(); - try { - // Navigate to Url - driver.get("https://google.com"); - - // Store 'signIn' button web element - WebElement signIn = driver.findElement(By.linkText("Sign in")); - Actions actionProvider = new Actions(driver); - // Perform context-click action on the element - actionProvider.contextClick(signIn).build().perform(); - } finally { - driver.quit(); - } - } -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -driver = webdriver.Chrome() - -# Navigate to url -driver.get("http://www.google.com") - -# Store 'signIn' button web element -signIn = driver.find_element(By.LINK_TEXT, "Sign in") - -# Perform context-click action on the element -webdriver.ActionChains(driver).context_click(signIn).perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; - -namespace SeleniumApp -{ - public class ContextClick - { - public static void Main(string[] args) - { - IWebDriver driver = new ChromeDriver(); - try - { - // Navigate to Url - driver.Navigate().GoToUrl("https://google.com"); - // Store 'signIn' button web element - IWebElement signIn = driver.FindElement(By.LinkText("Sign in")); - Actions actionProvider = new Actions(driver); - // Perform context-click action on the element - actionProvider.ContextClick(signIn).Build().Perform(); - } - finally - { - driver.Quit(); - } - } - } -} - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome +## Click and release + +This method combines moving to the center of an element with pressing and releasing the left mouse button. +This is otherwise known as "clicking": + +{{< tabpane disableCodeBlock=true height="4" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L34-L37" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L23-L26" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L30-L33" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L20-L23" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} -begin - # Navigate to Url - driver.get 'https://www.google.com' - # Store 'Sign In' button web element - sign_in = driver.find_element(link_text: 'Sign in') - # Perform context-click action on the element - driver.action.context_click(sign_in).perform -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" >}} -const {Builder, By} = require('selenium-webdriver'); +## Alternate Button Clicks + +There are a total of 5 defined buttons for a Mouse: +* 0 — Left Button (the default) +* 1 — Middle Button (currently unsupported) +* 2 — Right Button +* 3 — X1 (Back) Button +* 4 — X2 (Forward) Button + +### Context Click + +This method combines moving to the center of an element with pressing and releasing the right mouse button (button 2). +This is otherwise known as "right-clicking": + +{{< tabpane disableCodeBlock=true height="4" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L46-L49" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L34-L37" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L43-L46" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L31-L34" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} -(async function contextClick() { - let driver = await new Builder().forBrowser('chrome').build(); - try { - // Navigate to Url - await driver.get('https://www.google.com'); - // Store 'signIn' button web element - let signIn = driver.findElement(By.linkText("Sign in")); - const actions = driver.actions({async: true}); - // Perform context-click action on the element - await actions.contextClick(signIn).perform(); - } - finally { - await driver.quit(); - } -})(); - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions +### Back Click + +There is no convenience method for this, it is just pressing and releasing mouse button 3 + +{{< tabpane disableCodeBlock=true height="7" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L60-L66" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L48-L51" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L55" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L44-L47" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://google.com") - // Store 'signIn' button web element - val signIn = driver.findElement(By.linkText("Sign in")) - val actionProvider = Actions(driver) - // Perform context-click action on the element - actionProvider.contextClick(signIn).build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} +### Forward Click + +There is no convenience method for this, it is just pressing and releasing mouse button 4 + +{{< tabpane disableCodeBlock=true height="7" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L78-L84" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L62-L65" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L61" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L58-L61" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} ## Double click -It will move to the element and performs a double-click in the middle of the given element. - -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.interactions.Actions; - -public class doubleClick { - public static void main(String[] args) { - WebDriver driver = new ChromeDriver(); - try { - // Navigate to Url - driver.get("https://google.com"); - - // Store 'signIn' button web element - WebElement signIn = driver.findElement(By.linkText("Sign in")); - Actions actionProvider = new Actions(driver); - // Perform double-click action on the element - actionProvider.doubleClick(signIn).build().perform(); - } finally { - driver.quit(); - } - } -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -driver = webdriver.Chrome() - -# Navigate to url -driver.get("http://www.google.com") - -# Store 'signIn' button web element -signIn = driver.find_element(By.LINK_TEXT, "Sign in") - -# Perform double-click action on the element -webdriver.ActionChains(driver).double_click(signIn).perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; - -namespace SeleniumApp -{ - public class DoubleClick - { - public static void Main(string[] args) - { - IWebDriver driver = new ChromeDriver(); - try - { - // Navigate to Url - driver.Navigate().GoToUrl("https://google.com"); - // Store 'signIn' button web element - IWebElement signIn = driver.FindElement(By.LinkText("Sign in")); - Actions actionProvider = new Actions(driver); - // Perform double-click action on the element - actionProvider.DoubleClick(signIn).Build().Perform(); - } - finally - { - driver.Quit(); - } - } - } -} - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - # Navigate to Url - driver.get 'https://www.google.com' - # Store 'Sign In' button web element - sign_in = driver.find_element(link_text: 'Sign in') - # Perform double-click action on the element - driver.action.double_click(sign_in).perform -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" disableCodeBlock=true >}} - {{< gh-codeblock path="/examples/javascript/actionsApi/mouse/doubleClick.js">}} - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://google.com") - // Store 'signIn' button web element - val signIn = driver.findElement(By.linkText("Sign in")) - val actionProvider = Actions(driver) - // Perform double-click action on the element - actionProvider.doubleClick(signIn).build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} +This method combines moving to the center of an element with pressing and releasing the left mouse button twice. + +{{< tabpane disableCodeBlock=true height="4" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L93-L96" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L73-L76" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L69-L72" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L69-L72" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} ## Move to element -This method moves the mouse to the middle of the element. The element is also scrolled into the view on performing this action. -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.interactions.Actions; - -public class moveToElement { - public static void main(String[] args) { - WebDriver driver = new ChromeDriver(); - try { - // Navigate to Url - driver.get("https://google.com"); - - // Store 'Gmail' anchor web element - WebElement gmailLink = driver.findElement(By.linkText("Gmail")); - Actions actionProvider = new Actions(driver); - // Performs mouse move action onto the element - actionProvider.moveToElement(gmailLink).build().perform(); - } finally { - driver.quit(); - } - } -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -driver = webdriver.Chrome() - - # Navigate to url -driver.get("http://www.google.com") - - # Store 'google search' button web element -gmailLink = driver.find_element(By.LINK_TEXT, "Gmail") - - # Performs mouse move action onto the element -webdriver.ActionChains(driver).move_to_element(gmailLink).perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; - -namespace SeleniumApp -{ - public class MoveToElement - { - public static void Main(string[] args) - { - IWebDriver driver = new ChromeDriver(); - try - { - // Navigate to Url - driver.Navigate().GoToUrl("https://google.com"); - // Store 'google search' button web element - IWebElement gmailLink = driver.FindElement(By.LinkText("Gmail")); - Actions actionProvider = new Actions(driver); - // Performs mouse move action onto the element - actionProvider.MoveToElement(gmailLink).Build().Perform(); - } - finally - { - driver.Quit(); - } - } - } -} - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - # Navigate to Url - driver.get 'https://www.google.com' - # Store 'Gmail' anchor web element - gmail_link = driver.find_element(link_text: 'Gmail') - # Performs mouse move action onto the element - driver.action.move_to(gmail_link).perform -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" disableCodeBlock=true >}} - {{< gh-codeblock path="/examples/javascript/actionsApi/mouse/moveToElement.js">}} - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions - -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://google.com") - // Store 'Gmail' anchor web element - val gmailLink = driver.findElement(By.linkText("Gmail")) - val actionProvider = Actions(driver) - // Performs mouse move action onto the element - actionProvider.moveToElement(gmailLink).build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} +This method moves the mouse to the in-view center point of the element. +This is otherwise known as "hovering." +Note that the element must be in the viewport or else the command will error. + +{{< tabpane disableCodeBlock=true height="4" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L105-L108" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L84-L87" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L82-L85" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L80-L83" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} ## Move by offset -This method moves the mouse from its current position (or 0,0) by the given offset. If the coordinates are outside the view window, then the mouse will end up outside the browser window. - -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.interactions.Actions; - -public class moveByOffset { - public static void main(String[] args) { - WebDriver driver = new ChromeDriver(); - try { - // Navigate to Url - driver.get("https://google.com"); - - // Store 'Gmail' anchor web element - WebElement gmailLink = driver.findElement(By.linkText("Gmail")); - // Capture x and y offset positions of element - int xOffset = gmailLink.getRect().getX(); - int yOffset = gmailLink.getRect().getY(); - Actions actionProvider = new Actions(driver); - // Performs mouse move action onto the offset position - actionProvider.moveByOffset(xOffset, yOffset).build().perform(); - } finally { - driver.quit(); - } - } -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -driver = webdriver.Chrome() - - # Navigate to url -driver.get("http://www.google.com") - - # Store 'google search' button web element -gmailLink = driver.find_element(By.LINK_TEXT, "Gmail") - # Set x and y offset positions of element -xOffset = 100 -yOffset = 100 - # Performs mouse move action onto the element -webdriver.ActionChains(driver).move_by_offset(xOffset,yOffset).perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; - -namespace SeleniumApp -{ - public class MoveByOffset - { - public static void Main(string[] args) - { - IWebDriver driver = new ChromeDriver(); - try - { - // Navigate to Url - driver.Navigate().GoToUrl("https://google.com"); - // Store 'google search' button web element - IWebElement gmailLink = driver.FindElement(By.LinkText("Gmail")); - // Set x and y offset positions of element - int xOffset = 100; - int yOffset = 100; - Actions actionProvider = new Actions(driver); - // Performs mouse move action onto the offset position - actionProvider.MoveByOffset(xOffset, yOffset).Build().Perform(); - } - finally - { - driver.Quit(); - } - } - } -} - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome +These methods first move the mouse to the designated origin and then +by the number of pixels in the provided offset. +Note that the position of the mouse must be in the viewport or else the command will error. + +### Offset from Element (Top Left Origin) + +This method moves the mouse to the in-view center point of the element +then attempts to move to the upper left corner of the element and then moves by the +provided offset. + +This will be removed as an option in Selenium 4.3, and only an offset from center of the element +will be supported. As of Selenium 4.2, this is the default behavior for Ruby, .NET and Python in order +to be backwards compatible with previous versions of Selenium. +This approach does not work correctly when the element is not entirely inside the viewport. + +{{< tabpane disableCodeBlock=true height="4" >}} + {{< tab header="Java" >}} +**Not Implemented in Java** + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L95-L98" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L95-L98" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L91-L94" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} -begin - # Navigate to Url - driver.get 'https://www.google.com' - # Store 'Gmail' anchor web element - gmail_link = driver.find_element(link_text: 'Gmail') - # Capture x and y offset positions of element - x_offset = gmail_link.rect.x - y_offset = gmail_link.rect.y - # Performs mouse move action onto the offset position - driver.action.move_to_location(x_offset, y_offset).perform -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" >}} -const {Builder, By} = require('selenium-webdriver'); +### Offset from Element (Center Origin) + +This method moves to the in-view center point of the element, +then moves the mouse by the provided offset + +This is the default behavior in Java as of Selenium 4.0, and will be the default +for the remaining languages as of Selenium 4.3. + +{{< tabpane disableCodeBlock=true height="4" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L117-L120" >}} + {{< /tab >}} + {{< tab header="Python" >}} +**Coming in Selenium 4.3** + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L110-L113" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} +**Coming in Selenium 4.3** + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} -(async function moveByOffset() { - let driver = await new Builder().forBrowser('chrome').build(); - try { - // Navigate to Url - await driver.get('https://www.google.com'); - // Store 'Gmail' anchor web element - let gmailLink = driver.findElement(By.linkText("Gmail")); - // Capture offset positions of element - let offset = await gmailLink.getRect(); - let x = await offset.x; - let y = await offset.y; - const actions = driver.actions({async: true}); - // Performs mouse move action onto the element - await actions.move({x:parseInt(x),y:parseInt(y)}).pause(3000).perform(); - } - finally { - await driver.quit(); - } -})(); - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions +### Offset from Viewport + +This method moves the mouse from the upper left corner of the current viewport by the provided +offset. + +{{< tabpane disableCodeBlock=true height="6" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L131-L136" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L108-L110" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L125-L129" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L105-L107" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://google.com") - // Store 'Gmail' anchor web element - val gmailLink = driver.findElement(By.linkText("Gmail")) - // Capture x and y offset positions of element - val xOffset = gmailLink.rect.getX() - val yOffset = gmailLink.rect.getY() - val actionProvider = Actions(driver) - // Performs mouse move action onto the element - actionProvider.moveByOffset(xOffset, yOffset).build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} +### Offset from Current Pointer Location + +This method moves the mouse from its current position by the offset provided by the user. +If the mouse has not previously been moved, the position will be in the upper left +corner of the viewport. +Note that the pointer position does not change when the page is scrolled. + +{{< tabpane disableCodeBlock=true height="3" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L153-L155" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L125-L127" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L147-L149" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L119-L121" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} -## dragAndDrop +## Drag and Drop on Element This method firstly performs a click-and-hold on the source element, moves to the location of the target element and then releases the mouse. -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.interactions.Actions; - -public class dragAndDrop { - public static void main(String[] args) { - WebDriver driver = new ChromeDriver(); - try { - // Navigate to Url - driver.get("https://crossbrowsertesting.github.io/drag-and-drop"); - // Store 'box A' as source element - WebElement sourceEle = driver.findElement(By.id("draggable")); - // Store 'box B' as source element - WebElement targetEle = driver.findElement(By.id("droppable")); - Actions actionProvider = new Actions(driver); - // Performs drag and drop action of sourceEle onto the targetEle - actionProvider.dragAndDrop(sourceEle, targetEle).build().perform(); - } finally { - driver.quit(); - } - } -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -from selenium.webdriver.common.by import By -driver = webdriver.Chrome() - - # Navigate to url -driver.get("https://crossbrowsertesting.github.io/drag-and-drop") - - # Store 'box A' as source element -sourceEle = driver.find_element(By.ID, "draggable") - # Store 'box B' as source element -targetEle = driver.find_element(By.ID, "droppable") - # Performs drag and drop action of sourceEle onto the targetEle -webdriver.ActionChains(driver).drag_and_drop(sourceEle,targetEle).perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; - -namespace SeleniumApp -{ - public class DragAndDrop - { - public static void Main(string[] args) - { - IWebDriver driver = new ChromeDriver(); - try - { - // Navigate to Url - driver.Navigate().GoToUrl("https://crossbrowsertesting.github.io/drag-and-drop"); - // Store 'box A' as source element - IWebElement sourceEle = driver.FindElement(By.Id("draggable")); - // Store 'box B' as source element - IWebElement targetEle = driver.FindElement(By.Id("droppable")); - Actions actionProvider = new Actions(driver); - // Performs drag and drop action of sourceEle onto the targetEle - actionProvider.DragAndDrop(sourceEle, targetEle).Build().Perform(); - } - finally - { - driver.Quit(); - } - } - } -} - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - # Navigate to Url - driver.get 'https://crossbrowsertesting.github.io/drag-and-drop' - # Store 'box A' as source element - source_ele = driver.find_element(id: 'draggable') - # Store 'box B' as source element - target_ele = driver.find_element(id: 'droppable') - # Performs drag and drop action of sourceEle onto the targetEle - driver.action.drag_and_drop(source_ele, target_ele).perform -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" >}} -const {Builder, By} = require('selenium-webdriver'); - -(async function dragAndDrop() { - let driver = await new Builder().forBrowser('chrome').build(); - try { - // Navigate to Url - await driver.get('https://crossbrowsertesting.github.io/drag-and-drop'); - // Store 'box A' as source element - let sourceEle = driver.findElement(By.id("draggable")); - // Store 'box B' as source element - let targetEle = driver.findElement(By.id("droppable")); - const actions = driver.actions({async: true}); - // Performs drag and drop action of sourceEle onto the targetEle - await actions.dragAndDrop(sourceEle, targetEle).perform(); - } - finally { - await driver.quit(); - } -})(); - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions - -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://crossbrowsertesting.github.io/drag-and-drop") - // Store 'box A' as source element - val sourceEle = driver.findElement(By.id("draggable")) - // Store 'box B' as source element - val targetEle = driver.findElement(By.id("droppable")) - val actionProvider = Actions(driver) - // Performs drag and drop action of sourceEle onto the targetEle - actionProvider.dragAndDrop(sourceEle, targetEle).build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} +{{< tabpane disableCodeBlock=true height="5" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L166-L170" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L138-L142" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L161-L165" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L131-L135" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} -## dragAndDropBy +## Drag and Drop by Offset This method firstly performs a click-and-hold on the source element, moves to the given offset and then releases the mouse. -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.interactions.Actions; - -public class dragAndDropBy { - public static void main(String[] args) { - WebDriver driver = new ChromeDriver(); - try { - // Navigate to Url - driver.get("https://crossbrowsertesting.github.io/drag-and-drop"); - // Store 'box A' as source element - WebElement sourceEle = driver.findElement(By.id("draggable")); - // Store 'box B' as source element - WebElement targetEle = driver.findElement(By.id("droppable")); - int targetEleXOffset = targetEle.getLocation().getX(); - int targetEleYOffset = targetEle.getLocation().getY(); - Actions actionProvider = new Actions(driver); - // Performs dragAndDropBy onto the target element offset position - actionProvider.dragAndDropBy(sourceEle, targetEleXOffset, targetEleYOffset).build().perform(); - } finally { - driver.quit(); - } - } -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -driver = webdriver.Chrome() - - # Navigate to url -driver.get("https://crossbrowsertesting.github.io/drag-and-drop") - - # Store 'box A' as source element -sourceEle = driver.find_element(By.ID, "draggable") - # Store 'box B' as source element -targetEle = driver.find_element(By.ID, "droppable") -targetEleXOffset = targetEle.location.get("x") -targetEleYOffset = targetEle.location.get("y") - - # Performs dragAndDropBy onto the target element offset position -webdriver.ActionChains(driver).drag_and_drop_by_offset(sourceEle, targetEleXOffset, targetEleYOffset).perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; - -namespace SeleniumApp -{ - public class DragAndDropToOffset - { - public static void Main(string[] args) - { - IWebDriver driver = new ChromeDriver(); - try - { - // Navigate to Url - driver.Navigate().GoToUrl("https://crossbrowsertesting.github.io/drag-and-drop"); - // Store 'box A' as source element - IWebElement sourceEle = driver.FindElement(By.Id("draggable")); - // Store 'box B' as source element - IWebElement targetEle = driver.FindElement(By.Id("droppable")); - int targetEleXOffset = targetEle.Location.X; - int targetEleYOffset = targetEle.Location.Y; - Actions actionProvider = new Actions(driver); - // Performs drag and drop action of sourceEle onto the targetEle - actionProvider.DragAndDropToOffset(sourceEle, targetEleXOffset, targetEleYOffset).Build().Perform(); - } - finally - { - driver.Quit(); - } - } - } -} - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - # Navigate to Url - driver.get 'https://crossbrowsertesting.github.io/drag-and-drop' - # Store 'box A' as source element - source_ele = driver.find_element(id: 'draggable') - target_ele = driver.find_element(id: 'droppable') - # Capture x and y offset positions of element - x_offset = target_ele.rect.x - y_offset = target_ele.rect.y - # Performs dragAndDropBy onto the target element offset position - driver.action.drag_and_drop_by(source_ele, x_offset, y_offset).perform -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" >}} -const {Builder, By} = require('selenium-webdriver'); - -(async function dragAndDropBy() { - let driver = await new Builder().forBrowser('chrome').build(); - try { - // Navigate to Url - await driver.get('https://crossbrowsertesting.github.io/drag-and-drop'); - // Store 'box A' as source element - let sourceEle = driver.findElement(By.id("draggable")); - // Store 'box B' as source element - let targetEle = driver.findElement(By.id("droppable")); - let offset = await targetEle.getRect(); - let x = await offset.x; - let y = await offset.y; - const actions = driver.actions({async: true}); - // Performs dragAndDropBy onto the target element offset position - await actions.dragAndDrop(sourceEle, {x:parseInt(x), y:parseInt(y)}).perform(); - } - finally { - await driver.quit(); - } -})(); - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions - -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://crossbrowsertesting.github.io/drag-and-drop") - // Store 'box A' as source element - val sourceEle = driver.findElement(By.id("draggable")) - // Store 'box B' as source element - val targetEle = driver.findElement(By.id("droppable")) - val targetEleXOffset = targetEle.location.getX() - val targetEleYOffset = targetEle.location.getY() - val actionProvider = Actions(driver) - // Performs dragAndDropBy onto the target element offset position - actionProvider.dragAndDropBy(sourceEle, targetEleXOffset, targetEleYOffset).build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} -{{< /tabpane >}} - -## release - -This action releases the depressed left mouse button. If WebElement is passed, -it will release depressed left mouse button on the given WebElement - -{{< tabpane langEqualsHeader=true >}} - {{< tab header="Java" >}} -import org.openqa.selenium.By; -import org.openqa.selenium.WebDriver; -import org.openqa.selenium.WebElement; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.interactions.Actions; - -public class release { - public static void main(String[] args) { - WebDriver driver = new ChromeDriver(); - try { - // Navigate to Url - driver.get("https://crossbrowsertesting.github.io/drag-and-drop"); - // Store 'box A' as source element - WebElement sourceEle = driver.findElement(By.id("draggable")); - // Store 'box B' as source element - WebElement targetEle = driver.findElement(By.id("droppable")); - Actions actionProvider = new Actions(driver); - actionProvider.clickAndHold(sourceEle).moveToElement(targetEle).build().perform(); - // Performs release event - actionProvider.release().build().perform(); - } finally { - driver.quit(); - } - } -} - {{< /tab >}} - {{< tab header="Python" >}} -from selenium import webdriver -driver = webdriver.Chrome() - - # Navigate to url -driver.get("https://crossbrowsertesting.github.io/drag-and-drop") - - # Store 'box A' as source element -sourceEle = driver.find_element(By.ID, "draggable") - # Store 'box B' as source element -targetEle = driver.find_element(By.ID, "droppable") - - # Performs dragAndDropBy onto the target element offset position -webdriver.ActionChains(driver).click_and_hold(sourceEle).move_to_element(targetEle).perform() - # Performs release event -webdriver.ActionChains(driver).release().perform() - {{< /tab >}} - {{< tab header="CSharp" >}} -using OpenQA.Selenium; -using OpenQA.Selenium.Chrome; -using OpenQA.Selenium.Interactions; - -namespace SeleniumApp -{ - public class Release - { - public static void Main(string[] args) - { - IWebDriver driver = new ChromeDriver(); - try - { - // Navigate to Url - driver.Navigate().GoToUrl("https://crossbrowsertesting.github.io/drag-and-drop"); - // Store 'box A' as source element - IWebElement sourceEle = driver.FindElement(By.Id("draggable")); - // Store 'box B' as source element - IWebElement targetEle = driver.FindElement(By.Id("droppable")); - Actions actionProvider = new Actions(driver); - actionProvider.ClickAndHold(sourceEle).MoveToElement(targetEle).Build().Perform(); - // Performs release event - actionProvider.Release().Build().Perform(); - } - finally - { - driver.Quit(); - } - } - } -} - {{< /tab >}} - {{< tab header="Ruby" >}} -require 'selenium-webdriver' -driver = Selenium::WebDriver.for :chrome - -begin - # Navigate to Url - driver.get 'https://crossbrowsertesting.github.io/drag-and-drop' - source_ele = driver.find_element(id: 'draggable') - target_ele = driver.find_element(id: 'droppable') - driver.action.click_and_hold(source_ele).move_to(target_ele).perform - # Performs release event - driver.action.release.perform -ensure - driver.quit -end - {{< /tab >}} - {{< tab header="JavaScript" >}} -const {Builder, By} = require('selenium-webdriver'); - -(async function release() { - let driver = await new Builder().forBrowser('chrome').build(); - try { - // Navigate to Url - await driver.get('https://crossbrowsertesting.github.io/drag-and-drop'); - // Store 'box A' as source element - let sourceEle = driver.findElement(By.id("draggable")); - // Store 'box B' as source element - let targetEle = driver.findElement(By.id("droppable")); - const actions = driver.actions({async: true}); - await actions.move({origin:sourceEle}).press().perform(); - // Performs release event on target element - await actions.move({origin:targetEle}).release().perform(); - } - finally { - await driver.quit(); - } -})(); - {{< /tab >}} - {{< tab header="Kotlin" >}} -import org.openqa.selenium.By -import org.openqa.selenium.chrome.ChromeDriver -import org.openqa.selenium.interactions.Actions - -fun main() { - val driver = ChromeDriver() - try { - // Navigate to Url - driver.get("https://crossbrowsertesting.github.io/drag-and-drop") - // Store 'box A' as source element - val sourceEle = driver.findElement(By.id("draggable")) - // Store 'box B' as source element - val targetEle = driver.findElement(By.id("droppable")) - val actionProvider = Actions(driver) - actionProvider.clickAndHold(sourceEle).moveToElement(targetEle).build().perform() - // Performs release event - actionProvider.release().build().perform() - } finally { - driver.quit() - } -} - {{< /tab >}} +{{< tabpane disableCodeBlock=true height="6" >}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/MouseTest.java#L179-L184" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_mouse.py#L150-L155" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/MouseTest.cs#L175-L180" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/mouse_spec.rb#L143-L148" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} {{< /tabpane >}} - diff --git a/website_and_docs/content/documentation/webdriver/actions_api/pen.en.md b/website_and_docs/content/documentation/webdriver/actions_api/pen.en.md new file mode 100644 index 000000000000..3405ae4d8e37 --- /dev/null +++ b/website_and_docs/content/documentation/webdriver/actions_api/pen.en.md @@ -0,0 +1,39 @@ +--- +title: "Pen actions" +linkTitle: "Pen" +weight: 5 +description: > + A representation of a pen stylus kind of pointer input for interacting with a web page. +--- + +A Pen is a type of pointer input that has most of the same behavior as a mouse, but can +also have event properties unique to a stylus. Additionally, while a mouse +has 5 buttons, a pen has 3 equivalent button states: + +* 0 — Touch Contact (the default; equivalent to a left click) +* 2 — Barrel Button (equivalent to a right click) +* 5 — Eraser Button (currently unsupported by drivers) + +## Using a Pen + +{{< tabpane disableCodeBlock=true height="11">}} + {{< tab header="Java" >}} + {{< gh-codeblock path="examples/java/src/test/java/dev/selenium/actions_api/PenTest.java#L25-L35" >}} + {{< /tab >}} + {{< tab header="Python" >}} + {{< gh-codeblock path="examples/python/tests/actions_api/test_pen.py#L11-L20" >}} + {{< /tab >}} + {{< tab header="CSharp" >}} + {{< gh-codeblock path="examples/dotnet/SeleniumDocs/ActionsAPI/PenTest.cs#L19-L28" >}} + {{< /tab >}} + {{< tab header="Ruby" >}} + {{< gh-codeblock path="examples/ruby/spec/actions_api/pen_spec.rb#L9-L16" >}} + {{< /tab >}} + {{< tab header="JavaScript" >}} + // Add Code + {{< /tab >}} + {{< tab header="Kotlin" >}} + // Add Code + {{< /tab >}} +{{< /tabpane >}} + diff --git a/website_and_docs/layouts/shortcodes/gh-codeblock.html b/website_and_docs/layouts/shortcodes/gh-codeblock.html index 514b3654d6cf..16bda56f9f90 100644 --- a/website_and_docs/layouts/shortcodes/gh-codeblock.html +++ b/website_and_docs/layouts/shortcodes/gh-codeblock.html @@ -2,7 +2,7 @@ {{ $webBaseUrl := .Get "webBaseUrl" | default "https://github.com" }} {{ $org := .Get "org" | default "SeleniumHQ" }} {{ $repo := .Get "repo" | default "seleniumhq.github.io" }} -{{ $branch := .Get "branch" | default "trunk" }} +{{ $branch := .Get "branch" | default "actions" }} {{ $fullPath := .Get "path" }} {{ $path := index (split $fullPath "#") 0 }} diff --git a/website_and_docs/layouts/shortcodes/tabpane.html b/website_and_docs/layouts/shortcodes/tabpane.html index 3215be43f3f6..d6536a8e321d 100644 --- a/website_and_docs/layouts/shortcodes/tabpane.html +++ b/website_and_docs/layouts/shortcodes/tabpane.html @@ -19,7 +19,8 @@ -
+{{- $height := default "auto" ($.Get "height") -}} +
{{- range $index, $element := $.Scratch.Get "tabs" -}} {{- $tabid := printf "tabs-%v-%v-tab" $.Ordinal $index | anchorize -}} {{- $entryid := printf "tabs-%v-%v" $.Ordinal $index | anchorize -}} @@ -28,7 +29,7 @@ id="{{ $entryid }}" role="tabpanel" aria-labelled-by="{{ $tabid }}"> {{ if $.Get "disableCodeBlock" }} -
+
{{- index . "content" | markdownify -}}
{{ else }}