简体   繁体   中英

Xpath can't click to button (Button id & value dynamically changes) Selenium WebDriver Java

<form action="/assessments/1290/questions" id="questionform" method="post">
  <div class="happiness-reaction__item animated fadeIn selected">
    I often get upset over minor issues.
    <div class="inputs">

      <input id="choice_14832" value="14832" name="Answers[0].Choices" data-scv-reaction-id="strongly-disagree1" spellcheck="true" type="radio">
      <label class="u" for="choice_14832">
        <i class="sc-strongly-disagree"></i>
      </label>

      <input id="choice_14833" value="14833" name="Answers[0].Choices" data-scv-reaction-id="slightly-disagree1" spellcheck="true" type="radio">
      <label class="u" for="choice_14833">
        <i class="sc-slightly-disagree"></i>
      </label>

      <input id="choice_14834" value="14834" name="Answers[0].Choices" data-scv-reaction-id="none1" spellcheck="true" type="radio">
      <label class="u" for="choice_14834">
        <i class="sc-none"></i>
      </label>

      <input id="choice_14835" value="14835" name="Answers[0].Choices" data-scv-reaction-id="slightly-agree1" spellcheck="true" type="radio">
      <label class="u" for="choice_14835">
        <i class="sc-slightly-agree"></i>
      </label>

      <input id="choice_14836" value="14836" name="Answers[0].Choices" data-scv-reaction-id="strongly-agree1" spellcheck="true" type="radio">
      <label class="u" for="choice_14836">
        <i class="sc-strongly-agree"></i>
      </label>
    </div>
  </div>
                 .....

I want to click on that radio buttons. Since ID & value etc change dynamically , I should be able to make a simple click via using XPATH , But i cant. I get no error but buttons stays not-clicked.

Check this picture please

So far I was not able to click by doing :

WebElement button = driver.findElement(By.xpath(".//*[@id='questionform']/div[1]/div/label[1]/i"));
button.click();

Edited: I added this info , just in case if some1 wants more detailed info about the page I am talking about. There must be a way to click that simple button without workarounds.

"I know that would be too much effort to ask , but you can go "supercareer.com" , and take Personality under assessments , and see what I am actually talking about. Note: doesnt have to be real mail , easy to register 1 click , no verification you can access the questions that I am talking about. Thats the only way to provide 100% info about my questions."

if you can't use ID and Value - use any other attribute. In your case 'data-scv-reaction-id' is a good option)

//*[@data-scv-reaction-id='strongly-disagree1']

Here's a couple of tricks I use to keep my xpaths concise and more robust to change.

  1. You can reference deeper elements than the one you want to select by adding .. to the end of your xpath. For example I'd use the <i class="x" /> to distinguish what to click and then use .. to select the parent <label />

  2. You can use // in the middle of an xpath as a wildcard to navigate through elements that aren't interesting.

Eg: By.xpath("//form[@id='questionform']//i[@class='sc-strongly-disagree']/..")

So let's understand it this way:

  1. Each question has a reaction section.
  2. Each reaction section has a multiple reaction buttons/icons.

I've faced such a scenario and it needs to be handled in the following manner:

public void reactToQuestion(String question, String reaction) {
    // Get the question element
    // For eg. question = "I always complete all aspects of my work."
    By locQuestion = By.xpath("//*[text()='" + question + "']");
    WebElement element = driver.findElement(locQuestion);

    // Get the parent 'div' container of the question
    WebElement container = element.findElement("..");

    // Get reaction RELATIVELY to the question
    // reaction = 'strongly-disagree1', 'slightly-disagree1', etc.
    By locReaction = By.xpath(".//*[@class='inputs']/input[@data-scv-reaction-id=" + reaction + "]");
    WebElement reaction = container.findElement(locReaction);
    reaction.click();

}

Please try and let me know if there are any issues.

Good luck.

What you want to do in cases like this is to find a "container" element that holds all the choices that you want to select from. In this case, we can use the question text to find the DIV that contains all 5 choices using XPath. From there, you can select the desired choice based on the classname of the I tag, eg <i class="sc-strongly-agree"> . So provided the question text and the desired choice, we can make our selection.

In cases like this where you are likely use the same code over and over, you generally want to create a function to handle this. In addition, since you are selecting one of 5 choices repeatedly, I prefer to use an enum. You could use String but you would have to handle a potential wide variety of inputs and if someone ever misspells or somehow typos the string, the script fails. An enum makes it easy to select the desired choice because the IDE always shows you the choices making it a no-brainer. If you typo the enum, it won't compile... and it's always better to know earlier if a script is going to fail. An enum is also a container for those choices so that if the class names ever change, you change the class names in the enum and all scripts are fixed.

The function

public static void selectChoice(String question, Choices choice)
{
    driver.findElement(By.xpath("//div[contains(@class,'happiness-reaction__item')][contains(.,'" + question + "')]//i[@class='" + choice.value() + "']")).click();
}

The enum

public enum Choices
{
    STRONGLYDISAGREE("sc-strongly-disagree"),
    SLIGHTLYDISAGREE("sc-slightly-disagree"),
    NONE("sc-none"),
    SLIGHTLYAGREE("sc-slightly-agree"),
    STRONGLYAGREE("sc-strongly-agree");

    private String value;

    Choices(String value)
    {
        this.value = value;
    }

    public String value()
    {
        return value;
    }
}

and here's how you call it

selectChoice("I often get upset over minor issues.", Choices.STRONGLYAGREE);

As a general xpath, first input from question, it could be something like:

//form[@id='questionform']/div//input

If you need any input for certain question:

//form[@id='questionform']/div[contains(@class, 'happiness-reaction')]//input

If you need certain answer for a certain question then:

//form[@id='questionform']/div[contains(@class, 'happiness-reaction')]//input[@data-scv-reaction-id='strongly-disagree1']

If you need to add params for question and/or answer then:

//form[@id='questionform']/div[contains(@class, 'part_of_question_class')]//input[@data-scv-reaction-id='desired_answer_attr']

These params can be stored in an array/list or any form you like.

All these can be done in css also:

form#questionform>div input
form#questionform>div[class*=happiness-reaction] input
form#questionform>div[class*=happiness-reaction] input[data-scv-reaction-id='strongly-disagree1']

You get the idea.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM