// 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;

import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.openqa.selenium.testing.drivers.Browser.CHROME;
import static org.openqa.selenium.testing.drivers.Browser.EDGE;
import static org.openqa.selenium.testing.drivers.Browser.FIREFOX;
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.environment.webserver.Page;
import org.openqa.selenium.testing.JupiterTestBase;
import org.openqa.selenium.testing.NotYetImplemented;

class ElementDomAttributeTest extends JupiterTestBase {

  @Test
  void testShouldReturnNullWhenGettingTheValueOfAnAttributeThatIsNotListed() {
    driver.get(pages.simpleTestPage);
    WebElement head = driver.findElement(By.xpath("/html"));
    String attribute = head.getDomAttribute("cheese");
    assertThat(attribute).isNull();
  }

  @Test
  void testShouldReturnNullWhenGettingSrcAttributeOfInvalidImgTag() {
    driver.get(pages.simpleTestPage);
    WebElement img = driver.findElement(By.id("invalidImgTag"));
    String attribute = img.getDomAttribute("src");
    assertThat(attribute).isNull();
  }

  @Test
  void testShouldReturnTheActualValueWhenGettingSrcAttributeOfAValidImgTag() {
    driver.get(pages.simpleTestPage);
    WebElement img = driver.findElement(By.id("validImgTag"));
    String attribute = img.getDomAttribute("src");
    assertThat(attribute).isEqualTo("icon.gif");
  }

  @Test
  void testShouldReturnTheActualValueWhenGettingHrefAttributeOfAValidAnchorTag() {
    driver.get(pages.simpleTestPage);
    WebElement img = driver.findElement(By.id("validAnchorTag"));
    String attribute = img.getDomAttribute("href");
    assertThat(attribute).isEqualTo("icon.gif");
  }

  @Test
  void testShouldReturnEmptyAttributeValuesWhenPresentAndTheValueIsActuallyEmpty() {
    driver.get(pages.simpleTestPage);
    WebElement body = driver.findElement(By.xpath("//body"));
    assertThat(body.getDomAttribute("style")).isEmpty();
  }

  @Test
  void testShouldReturnTheValueOfTheDisabledAttributeAsNullIfNotSet() {
    driver.get(pages.formPage);
    WebElement inputElement = driver.findElement(By.xpath("//input[@id='working']"));
    assertThat(inputElement.getDomAttribute("disabled")).isNull();
    assertThat(inputElement.isEnabled()).isTrue();

    WebElement pElement = driver.findElement(By.id("peas"));
    assertThat(pElement.getDomAttribute("disabled")).isNull();
    assertThat(pElement.isEnabled()).isTrue();
  }

  @Test
  void testShouldNotReturnTheValueOfTheIndexAttributeIfItIsMissing() {
    driver.get(pages.formPage);
    WebElement multiSelect = driver.findElement(By.id("multi"));
    List<WebElement> options = multiSelect.findElements(By.tagName("option"));
    assertThat(options.get(1).getDomAttribute("index")).isNull();
  }

  @Test
  void testShouldIndicateTheElementsThatAreDisabledAreNotEnabled() {
    driver.get(pages.formPage);
    WebElement inputElement = driver.findElement(By.xpath("//input[@id='notWorking']"));
    assertThat(inputElement.isEnabled()).isFalse();

    inputElement = driver.findElement(By.xpath("//input[@id='working']"));
    assertThat(inputElement.isEnabled()).isTrue();
  }

  @Test
  void testElementsShouldBeDisabledIfTheyAreDisabledUsingRandomDisabledStrings() {
    driver.get(pages.formPage);
    WebElement disabledTextElement1 = driver.findElement(By.id("disabledTextElement1"));
    assertThat(disabledTextElement1.isEnabled()).isFalse();

    WebElement disabledTextElement2 = driver.findElement(By.id("disabledTextElement2"));
    assertThat(disabledTextElement2.isEnabled()).isFalse();

    WebElement disabledSubmitElement = driver.findElement(By.id("disabledSubmitElement"));
    assertThat(disabledSubmitElement.isEnabled()).isFalse();
  }

  @Test
  @NotYetImplemented(SAFARI)
  public void testShouldThrowExceptionIfSendingKeysToElementDisabledUsingRandomDisabledStrings() {
    driver.get(pages.formPage);
    WebElement disabledTextElement1 = driver.findElement(By.id("disabledTextElement1"));
    assertThatExceptionOfType(InvalidElementStateException.class)
        .isThrownBy(() -> disabledTextElement1.sendKeys("foo"));
    assertThat(disabledTextElement1.getText()).isEmpty();

    WebElement disabledTextElement2 = driver.findElement(By.id("disabledTextElement2"));
    assertThatExceptionOfType(InvalidElementStateException.class)
        .isThrownBy(() -> disabledTextElement2.sendKeys("bar"));
    assertThat(disabledTextElement2.getText()).isEmpty();
  }

  @Test
  void testShouldIndicateWhenATextAreaIsDisabled() {
    driver.get(pages.formPage);
    WebElement textArea = driver.findElement(By.xpath("//textarea[@id='notWorkingArea']"));
    assertThat(textArea.isEnabled()).isFalse();
  }

  @Test
  void testShouldIndicateWhenASelectIsDisabled() {
    driver.get(pages.formPage);

    WebElement enabled = driver.findElement(By.name("selectomatic"));
    WebElement disabled = driver.findElement(By.name("no-select"));

    assertThat(enabled.isEnabled()).isTrue();
    assertThat(disabled.isEnabled()).isFalse();
  }

  @Test
  void testShouldReturnTheValueOfSelectedForOptionsOnlyIfTheyAreSelected() {
    driver.get(pages.formPage);
    WebElement selectBox = driver.findElement(By.xpath("//select[@name='selectomatic']"));
    List<WebElement> options = selectBox.findElements(By.tagName("option"));
    WebElement one = options.get(0);
    WebElement two = options.get(1);
    assertThat(one.isSelected()).isTrue();
    assertThat(two.isSelected()).isFalse();
    assertThat(one.getDomAttribute("selected")).isEqualTo("true");
    assertThat(two.getDomAttribute("selected")).isNull();
  }

  @Test
  void testShouldReturnValueOfClassAttributeOfAnElement() {
    driver.get(pages.xhtmlTestPage);

    WebElement heading = driver.findElement(By.xpath("//h1"));
    String className = heading.getDomAttribute("class");

    assertThat(className).isEqualTo("header");
  }

  @Test
  void testShouldNotReturnTheContentsOfATextAreaAsItsValue() {
    driver.get(pages.formPage);
    String value = driver.findElement(By.id("withText")).getDomAttribute("value");
    assertThat(value).isNull();
  }

  @Test
  void testShouldNotReturnInnerHtmlProperty() {
    driver.get(pages.simpleTestPage);
    String html = driver.findElement(By.id("wrappingtext")).getDomAttribute("innerHTML");
    assertThat(html).isNull();
  }

  @Test
  void testShouldTreatReadonlyAsAValue() {
    driver.get(pages.formPage);

    WebElement element = driver.findElement(By.name("readonly"));
    String readonly = element.getDomAttribute("readonly");

    assertThat(readonly).isNotNull();

    WebElement textInput = driver.findElement(By.name("x"));
    String notReadonly = textInput.getDomAttribute("readonly");

    assertThat(readonly).isNotEqualTo(notReadonly);
  }

  @Test
  void testShouldNotReturnTextContentProperty() {
    driver.get(pages.simpleTestPage);
    WebElement element = driver.findElement(By.id("hiddenline"));
    assertThat(element.getDomAttribute("textContent")).isNull();
  }

  @Test
  void testShouldGetNumericAttribute() {
    driver.get(pages.formPage);
    WebElement element = driver.findElement(By.id("withText"));
    assertThat(element.getDomAttribute("rows")).isEqualTo("5");
  }

  @Test
  void testCanReturnATextApproximationOfTheStyleAttribute() {
    driver.get(pages.javascriptPage);

    String style = driver.findElement(By.id("red-item")).getDomAttribute("style");

    assertThat(style.toLowerCase().contains("background-color")).isTrue();
  }

  @Test
  void testShouldCorrectlyReportValueOfColspan() {
    driver.get(pages.tables);

    WebElement th1 = driver.findElement(By.id("th1"));
    WebElement td2 = driver.findElement(By.id("td2"));

    assertThat(th1.getDomAttribute("id")).isEqualTo("th1");
    assertThat(th1.getDomAttribute("colspan")).isEqualTo("3");

    assertThat(td2.getDomAttribute("id")).isEqualTo("td2");
    assertThat(td2.getDomAttribute("colspan")).isEqualTo("2");
  }

  // This is a test-case re-creating issue 900.
  @Test
  void testShouldReturnValueOfOnClickAttribute() {
    driver.get(pages.javascriptPage);

    WebElement mouseclickDiv = driver.findElement(By.id("mouseclick"));

    String onClickValue = mouseclickDiv.getDomAttribute("onclick");
    String expectedOnClickValue = "displayMessage('mouse click');";
    assertThat(onClickValue)
        .as("Javascript code")
        .isIn(
            "javascript:" + expectedOnClickValue, // Non-IE
            "function anonymous()\n{\n" + expectedOnClickValue + "\n}", // IE
            "function onclick()\n{\n" + expectedOnClickValue + "\n}"); // IE

    WebElement mousedownDiv = driver.findElement(By.id("mousedown"));
    assertThat(mousedownDiv.getDomAttribute("onclick")).isNull();
  }

  @Test
  void testgetDomAttributeDoesNotReturnAnObjectForSvgProperties() {
    driver.get(pages.svgPage);
    WebElement svgElement = driver.findElement(By.id("rotate"));
    assertThat(svgElement.getDomAttribute("transform")).isEqualTo("rotate(30)");
  }

  @Test
  void testCanRetrieveTheCurrentValueOfATextFormFieldWithPresetText() {
    driver.get(pages.formPage);
    WebElement element = driver.findElement(By.id("inputWithText"));
    assertThat(element.getDomAttribute("value")).isEqualTo("Example text");
    element.sendKeys("hello@example.com");
    assertThat(element.getDomAttribute("value")).isEqualTo("Example text");
  }

  @Test
  void testShouldNotReturnTextOfATextArea() {
    driver.get(pages.formPage);
    WebElement element = driver.findElement(By.id("withText"));
    assertThat(element.getDomAttribute("value")).isNull();
  }

  @Test
  void testShouldReturnNullForNonPresentBooleanAttributes() {
    driver.get(pages.booleanAttributes);
    WebElement element1 = driver.findElement(By.id("working"));
    assertThat(element1.getDomAttribute("required")).isNull();
    WebElement element2 = driver.findElement(By.id("wallace"));
    assertThat(element2.getDomAttribute("nowrap")).isNull();
  }

  @Test
  @NotYetImplemented(value = CHROME, reason = "It returns a property")
  @NotYetImplemented(EDGE)
  @NotYetImplemented(FIREFOX)
  public void testShouldReturnEmptyStringForPresentBooleanAttributes() {
    driver.get(pages.booleanAttributes);
    WebElement element1 = driver.findElement(By.id("emailRequired"));
    assertThat(element1.getDomAttribute("required")).isEmpty();
    WebElement element2 = driver.findElement(By.id("emptyTextAreaRequired"));
    assertThat(element2.getDomAttribute("required")).isEqualTo("required");
    WebElement element3 = driver.findElement(By.id("inputRequired"));
    assertThat(element3.getDomAttribute("required")).isEmpty();
    WebElement element4 = driver.findElement(By.id("textAreaRequired"));
    assertThat(element4.getDomAttribute("required")).isEqualTo("false");
    WebElement element5 = driver.findElement(By.id("unwrappable"));
    assertThat(element5.getDomAttribute("nowrap")).isEmpty();
  }

  @Test
  void testMultipleAttributeShouldBeNullWhenNotSet() {
    driver.get(pages.selectPage);
    WebElement element = driver.findElement(By.id("selectWithoutMultiple"));
    assertThat(element.getDomAttribute("multiple")).isNull();
  }

  @Test
  void testMultipleAttributeShouldBeTrueWhenSet() {
    driver.get(pages.selectPage);
    WebElement element = driver.findElement(By.id("selectWithMultipleEqualsMultiple"));
    assertThat(element.getDomAttribute("multiple")).isEqualTo("true");
  }

  @Test
  void testMultipleAttributeShouldBeTrueWhenSelectHasMultipleWithValueAsBlank() {
    driver.get(pages.selectPage);
    WebElement element = driver.findElement(By.id("selectWithEmptyStringMultiple"));
    assertThat(element.getDomAttribute("multiple")).isEqualTo("true");
  }

  @Test
  void testMultipleAttributeShouldBeTrueWhenSelectHasMultipleWithoutAValue() {
    driver.get(pages.selectPage);
    WebElement element = driver.findElement(By.id("selectWithMultipleWithoutValue"));
    assertThat(element.getDomAttribute("multiple")).isEqualTo("true");
  }

  @Test
  void testMultipleAttributeShouldBeTrueWhenSelectHasMultipleWithValueAsSomethingElse() {
    driver.get(pages.selectPage);
    WebElement element = driver.findElement(By.id("selectWithRandomMultipleValue"));
    assertThat(element.getDomAttribute("multiple")).isEqualTo("true");
  }

  @Test
  void shouldTreatContenteditableAsEnumeratedButNotBoolean() {
    checkEnumeratedAttribute("contenteditable", "true", "false", "yes", "no", "", "blabla");
  }

  @Test
  @NotYetImplemented(IE)
  @NotYetImplemented(SAFARI)
  public void shouldTreatDraggableAsEnumeratedButNotBoolean() {
    checkEnumeratedAttribute("draggable", "true", "false", "yes", "no", "", "blabla");
  }

  private void checkEnumeratedAttribute(String name, String... values) {
    asList(values)
        .forEach(
            value -> {
              driver.get(
                  appServer.create(
                      new Page()
                          .withBody(String.format("<div id=\"attr\" %s=\"%s\">", name, value))));
              assertThat(driver.findElement(By.id("attr")).getDomAttribute(name)).isEqualTo(value);
            });

    driver.get(appServer.create(new Page().withBody(String.format("<div id=\"attr\" %s>", name))));
    assertThat(driver.findElement(By.id("attr")).getDomAttribute(name)).isEmpty();

    driver.get(appServer.create(new Page().withBody("<div id=\"attr\">")));
    assertThat(driver.findElement(By.id("attr")).getDomAttribute(name)).isNull();
  }
}
