code: // Licensed to the Software Freedom Conservancy (SFC) under one
  // or more contributor license agreements.  See the NOTICE file
  // distributed with this work for additional information
  // regarding copyright ownership.  The SFC licenses this file
  // to you under the Apache License, Version 2.0 (the
  // "License"); you may not use this file except in compliance
  // with the License.  You may obtain a copy of the License at
  //
  //   http://www.apache.org/licenses/LICENSE-2.0
  //
  // Unless required by applicable law or agreed to in writing,
  // software distributed under the License is distributed on an
  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  // KIND, either express or implied.  See the License for the
  // specific language governing permissions and limitations
  // under the License.
  
  package org.openqa.selenium.interactions;
  
  import static org.assertj.core.api.Assertions.assertThat;
  import static org.junit.jupiter.api.Assumptions.assumeFalse;
  import static org.openqa.selenium.WaitingConditions.elementValueToEqual;
  import static org.openqa.selenium.WaitingConditions.windowHandleCountToBe;
  import static org.openqa.selenium.support.ui.ExpectedConditions.presenceOfElementLocated;
  import static org.openqa.selenium.support.ui.ExpectedConditions.titleIs;
  import static org.openqa.selenium.testing.TestUtilities.getEffectivePlatform;
  import static org.openqa.selenium.testing.TestUtilities.getIEVersion;
  import static org.openqa.selenium.testing.TestUtilities.isInternetExplorer;
  import static org.openqa.selenium.testing.drivers.Browser.CHROME;
  import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;
  import static org.openqa.selenium.testing.drivers.Browser.HTMLUNIT;
  import static org.openqa.selenium.testing.drivers.Browser.IE;
  import static org.openqa.selenium.testing.drivers.Browser.SAFARI;
  
  import java.util.List;
  import org.junit.jupiter.api.Test;
  import org.openqa.selenium.By;
  import org.openqa.selenium.Dimension;
  import org.openqa.selenium.JavascriptExecutor;
  import org.openqa.selenium.Keys;
  import org.openqa.selenium.Platform;
  import org.openqa.selenium.Point;
  import org.openqa.selenium.WaitingConditions;
  import org.openqa.selenium.WebElement;
  import org.openqa.selenium.testing.Ignore;
  import org.openqa.selenium.testing.JupiterTestBase;
  import org.openqa.selenium.testing.NotYetImplemented;
  import org.openqa.selenium.testing.SwitchToTopAfterTest;
  
  /** Tests combined input actions. */
  class CombinedInputActionsTest extends JupiterTestBase {
  
    @Test
    @Ignore(IE)
    @NotYetImplemented(SAFARI)
    public void testPlainClickingOnMultiSelectionList() {
      driver.get(pages.formSelectionPage);
  
      List<WebElement> options = driver.findElements(By.tagName("option"));
  
      Actions actions = new Actions(driver);
      Action selectThreeOptions =
          actions.click(options.get(1)).click(options.get(2)).click(options.get(3)).build();
  
      selectThreeOptions.perform();
  
      WebElement showButton = driver.findElement(By.name("showselected"));
      showButton.click();
  
      WebElement resultElement = driver.findElement(By.id("result"));
      assertThat(resultElement.getText())
          .describedAs("Should have picked the third option only")
          .isEqualTo("cheddar");
    }
  
    @Test
    @Ignore(IE)
    @NotYetImplemented(SAFARI)
    public void testShiftClickingOnMultiSelectionList() {
      driver.get(pages.formSelectionPage);
  
      List<WebElement> options = driver.findElements(By.tagName("option"));
  
      Actions actions = new Actions(driver);
      Action selectThreeOptions =
          actions
              .click(options.get(1))
              .keyDown(Keys.SHIFT)
              .click(options.get(3))
              .keyUp(Keys.SHIFT)
              .build();
  
      selectThreeOptions.perform();
  
      WebElement showButton = driver.findElement(By.name("showselected"));
      showButton.click();
  
      WebElement resultElement = driver.findElement(By.id("result"));
      assertThat(resultElement.getText())
          .describedAs("Should have picked the last three options")
          .isEqualTo("roquefort parmigiano cheddar");
    }
  
    @Test
    @Ignore(IE)
    @NotYetImplemented(SAFARI)
    public void testMultipleInputs() {
      driver.get(pages.formSelectionPage);
  
      List<WebElement> options = driver.findElements(By.tagName("option"));
  
      Actions actions = new Actions(driver);
      Action selectThreeOptions =
          actions
              .setActivePointer(PointerInput.Kind.PEN, "default pen")
              .click(options.get(1))
              .keyDown(Keys.SHIFT)
              .click(options.get(1))
              .setActivePointer(PointerInput.Kind.MOUSE, "default mouse")
              .click(options.get(3))
              .keyUp(Keys.SHIFT)
              .build();
  
      selectThreeOptions.perform();
  
      WebElement showButton = driver.findElement(By.name("showselected"));
      showButton.click();
  
      WebElement resultElement = driver.findElement(By.id("result"));
      assertThat(resultElement.getText())
          .describedAs("Should have picked the last three options")
          .isEqualTo("roquefort parmigiano cheddar");
    }
  
    @Test
    @Ignore(IE)
    @Ignore(value = FIREFOX, travis = true)
    public void testControlClickingOnMultiSelectionList() {
      assumeFalse(
          getEffectivePlatform(driver).is(Platform.MAC), "FIXME: macs don't have CONTROL key");
      driver.get(pages.formSelectionPage);
  
      List<WebElement> options = driver.findElements(By.tagName("option"));
  
      Actions actions = new Actions(driver);
      Action selectThreeOptions =
          actions
              .click(options.get(1))
              .keyDown(Keys.CONTROL)
              .click(options.get(3))
              .keyUp(Keys.CONTROL)
              .build();
  
      selectThreeOptions.perform();
  
      WebElement showButton = driver.findElement(By.name("showselected"));
      showButton.click();
  
      WebElement resultElement = driver.findElement(By.id("result"));
      assertThat(resultElement.getText())
          .describedAs("Should have picked the first and the third options")
          .isEqualTo("roquefort cheddar");
    }
  
    @Test
    @Ignore(IE)
    @Ignore(value = FIREFOX, travis = true)
    public void testControlClickingOnCustomMultiSelectionList() {
      driver.get(pages.selectableItemsPage);
      Keys key = getEffectivePlatform(driver).is(Platform.MAC) ? Keys.COMMAND : Keys.CONTROL;
  
      WebElement reportingElement = driver.findElement(By.id("infodiv"));
  
      assertThat(reportingElement.getText()).isEqualTo("no info");
  
      List<WebElement> listItems = driver.findElements(By.tagName("li"));
  
      Actions actions = new Actions(driver);
      Action selectThreeItems =
          actions
              .keyDown(key)
              .click(listItems.get(1))
              .click(listItems.get(3))
              .click(listItems.get(5))
              .keyUp(key)
              .build();
  
      selectThreeItems.perform();
  
      assertThat(reportingElement.getText()).isEqualTo("#item2 #item4 #item6");
  
      // Now click on another element, make sure that's the only one selected.
      actions = new Actions(driver);
      actions.click(listItems.get(6)).build().perform();
      assertThat(reportingElement.getText()).isEqualTo("#item7");
    }
  
    @Test
    @Ignore(IE)
    @Ignore(value = FIREFOX, travis = true)
    public void testControlClickingWithMultiplePointers() {
      driver.get(pages.selectableItemsPage);
  
      Keys key = getEffectivePlatform(driver).is(Platform.MAC) ? Keys.COMMAND : Keys.CONTROL;
      WebElement reportingElement = driver.findElement(By.id("infodiv"));
  
      assertThat(reportingElement.getText()).isEqualTo("no info");
  
      List<WebElement> listItems = driver.findElements(By.tagName("li"));
  
      Actions actions = new Actions(driver);
      Action selectThreeItems =
          actions
              .keyDown(key)
              .setActivePointer(PointerInput.Kind.PEN, "default pen")
              .click(listItems.get(1))
              .setActivePointer(PointerInput.Kind.MOUSE, "default mouse")
              .click(listItems.get(3))
              .setActivePointer(PointerInput.Kind.PEN, "default pen")
              .click(listItems.get(5))
              .keyUp(key)
              .build();
  
      selectThreeItems.perform();
  
      assertThat(reportingElement.getText()).isEqualTo("#item2 #item4 #item6");
    }
  
    private void navigateToClicksPageAndClickLink() {
      driver.get(pages.clicksPage);
  
      wait.until(presenceOfElementLocated(By.id("normal")));
      WebElement link = driver.findElement(By.id("normal"));
  
      new Actions(driver).click(link).perform();
  
      wait.until(titleIs("XHTML Test Page"));
    }
  
    @SwitchToTopAfterTest
    @Test
    void canMoveMouseToAnElementInAnIframeAndClick() {
      driver.get(appServer.whereIs("click_tests/click_in_iframe.html"));
  
      wait.until(presenceOfElementLocated(By.id("ifr")));
      driver.switchTo().frame("ifr");
  
      WebElement link = driver.findElement(By.id("link"));
  
      new Actions(driver).moveToElement(link).click().perform();
  
      wait.until(titleIs("Submitted Successfully!"));
    }
  
    @Test
    void testCanClickOnLinks() {
      navigateToClicksPageAndClickLink();
    }
  
    @Test
    @NotYetImplemented(HTMLUNIT)
    public void testCanClickOnLinksWithAnOffset() {
      driver.get(pages.clicksPage);
  
      wait.until(presenceOfElementLocated(By.id("normal")));
      WebElement link = driver.findElement(By.id("normal"));
  
      new Actions(driver).moveToElement(link, 1, 1).click().perform();
  
      wait.until(titleIs("XHTML Test Page"));
    }
  
    @Test
    @NotYetImplemented(HTMLUNIT)
    public void testClickAfterMoveToAnElementWithAnOffsetShouldUseLastMousePosition() {
      driver.get(pages.clickEventPage);
  
      WebElement element = driver.findElement(By.id("eventish"));
      Dimension size = element.getSize();
      Point location = element.getLocation();
  
      new Actions(driver)
          .moveToElement(element, 20 - size.getWidth() / 2, 10 - size.getHeight() / 2)
          .click()
          .perform();
  
      wait.until(presenceOfElementLocated(By.id("pageX")));
  
      int x;
      int y;
      if (isInternetExplorer(driver) && getIEVersion(driver) < 10) {
        x = Integer.parseInt(driver.findElement(By.id("clientX")).getText());
        y = Integer.parseInt(driver.findElement(By.id("clientY")).getText());
      } else {
        x = Integer.parseInt(driver.findElement(By.id("pageX")).getText());
        y = Integer.parseInt(driver.findElement(By.id("pageY")).getText());
      }
  
      assertThat(fuzzyPositionMatching(location.getX() + 20, location.getY() + 10, x, y)).isTrue();
    }
  
    private boolean fuzzyPositionMatching(int expectedX, int expectedY, int actualX, int actualY) {
      // Everything within 5 pixels range is OK
      final int ALLOWED_DEVIATION = 5;
      return Math.abs(expectedX - actualX) < ALLOWED_DEVIATION
          && Math.abs(expectedY - actualY) < ALLOWED_DEVIATION;
    }
  
    /**
     * This test demonstrates the following problem: When the representation of the mouse in the
     * driver keeps the wrong state, mouse movement will end up at the wrong coordinates.
     */
    @Test
    @NotYetImplemented(HTMLUNIT)
    public void testMouseMovementWorksWhenNavigatingToAnotherPage() {
      navigateToClicksPageAndClickLink();
  
      WebElement linkId = driver.findElement(By.id("linkId"));
      new Actions(driver).moveToElement(linkId, 1, 1).click().perform();
  
      wait.until(titleIs("We Arrive Here"));
    }
  
    @Test
    @Ignore(value = FIREFOX, issue = "https://github.com/mozilla/geckodriver/issues/646")
    @NotYetImplemented(CHROME)
    public void testChordControlCutAndPaste() {
      assumeFalse(
          getEffectivePlatform(driver).is(Platform.MAC), "FIXME: macs don't have CONTROL key");
      assumeFalse(
          getEffectivePlatform(driver).is(Platform.WINDOWS) && isInternetExplorer(driver),
          "Windows: native events library  does not support storing modifiers state yet");
  
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("keyReporter"));
  
      new Actions(driver).sendKeys(element, "abc def").perform();
  
      wait.until(elementValueToEqual(element, "abc def"));
  
      // TODO: Figure out why calling sendKey(Key.CONTROL + "a") and then
      // sendKeys("x") does not work on Linux.
      new Actions(driver).sendKeys(Keys.CONTROL + "a" + "x").perform();
  
      // Release keys before next step.
      new Actions(driver).sendKeys(Keys.NULL).perform();
  
      wait.until(elementValueToEqual(element, ""));
  
      new Actions(driver).sendKeys(Keys.CONTROL + "v").sendKeys("v").perform();
  
      new Actions(driver).sendKeys(Keys.NULL).perform();
  
      wait.until(elementValueToEqual(element, "abc defabc def"));
    }
  
    @Test
    @Ignore(IE)
    @NotYetImplemented(SAFARI)
    public void testCombiningShiftAndClickResultsInANewWindow() {
      driver.get(pages.linkedImage);
      WebElement link = driver.findElement(By.id("link"));
      String originalTitle = driver.getTitle();
  
      int nWindows = driver.getWindowHandles().size();
      new Actions(driver).moveToElement(link).keyDown(Keys.SHIFT).click().keyUp(Keys.SHIFT).perform();
  
      wait.until(windowHandleCountToBe(nWindows + 1));
      assertThat(driver.getTitle())
          .describedAs("Should not have navigated away")
          .isEqualTo(originalTitle);
    }
  
    @Test
    @Ignore(IE)
    @NotYetImplemented(SAFARI)
    public void testHoldingDownShiftKeyWhileClicking() {
      driver.get(pages.clickEventPage);
  
      WebElement toClick = driver.findElement(By.id("eventish"));
  
      new Actions(driver).keyDown(Keys.SHIFT).click(toClick).keyUp(Keys.SHIFT).perform();
  
      WebElement shiftInfo = wait.until(presenceOfElementLocated(By.id("shiftKey")));
      assertThat(shiftInfo.getText()).isEqualTo("true");
    }
  
    @Test
    @NotYetImplemented(SAFARI)
    public void canClickOnASuckerFishStyleMenu() throws InterruptedException {
      driver.get(pages.javascriptPage);
  
      // Move to a different element to make sure the mouse is not over the
      // element with id 'item1' (from a previous test).
      new Actions(driver).moveToElement(driver.findElement(By.id("dynamo"))).build().perform();
  
      WebElement element = driver.findElement(By.id("menu1"));
  
      final WebElement item = driver.findElement(By.id("item1"));
      assertThat(item.getText()).isEmpty();
  
      ((JavascriptExecutor) driver).executeScript("arguments[0].style.background = 'green'", element);
      new Actions(driver).moveToElement(element).build().perform();
  
      // Intentionally wait to make sure hover persists.
      Thread.sleep(2000);
  
      item.click();
  
      WebElement result = driver.findElement(By.id("result"));
      wait.until(WaitingConditions.elementTextToContain(result, "item 1"));
    }
  
    @Test
    void testCanClickOnSuckerFishMenuItem() {
      driver.get(pages.javascriptPage);
  
      WebElement element = driver.findElement(By.id("menu1"));
  
      new Actions(driver).moveToElement(element).build().perform();
  
      WebElement target = driver.findElement(By.id("item1"));
  
      assertThat(target.isDisplayed()).isTrue();
      target.click();
  
      String text = driver.findElement(By.id("result")).getText();
      assertThat(text).contains("item 1");
    }
  }
  