3

I'm working on a Robot Framework project with Selenium on Java.

The website I'm testing has a language dropdown element to switch the page language. Whenever a new language is selected, the "lang" attribute in the html tag changes. E.g., it goes from <html lang="en"> to if Spanish is selected from the list.

This is what I'm using to test the language switch:

Select From List By Value       ${DROPDOWN}  ?hl=es
${language}                     Get Element Attribute  html@lang
Wait For Condition              ${language}==es  timeout=15s

Even though I can see the page switching languages, and I can inspect the DOM while the test is running and I see the lang attribute switching to the new language, I keep getting Condition 'en == es' did not become true in 15 seconds. I tried adding quotes to make the comparison about strings but to no avail. I also tried increasing the timeout.

I can't understand why the condition keeps returning false when I'm seeing live (while the test executes) that the language has indeed changed:

enter image description here

Todor Minakov
  • 19,097
  • 3
  • 55
  • 60
Floella
  • 1,279
  • 1
  • 22
  • 41

1 Answers1

3

The check doesn't succeed, because you are getting the attribute's value one time, and then waiting on that one-off value to change. And the moment you got it the DOM still hasn't been updated.

Also, the keyword Wait For Condition is designed to execute an user's javascript number of times, and stop when it evaluates to True. Your condition is not a js :), plus it's a syntax error (you have to put the variable and the value in quotes, as currently they are references to undefined variables).

You have (at least) 3 options:

  • Use the keyword Wait Until Page Contains Element and pass to it a locator to the html with the desired attribute's value - something like /html[@lang=es"]; this will take care of the DOM state polling for you.
  • Implement custom pooling - in an iteration bound loop, get the attribute, check if it is with the desired value, break the loop if so; if not - Sleep 100-200ms and check on the next iteration. If the loop hits the upper bound w/o the value being the expected one - fail the case.
  • Use the Wait For Condition kw, by crafting the check in javascript method or expression (and locating the element, and its attribute value in it).

If I was to do it, my preference would be in the order above - taking into account easiness to implement, and most RF-native approach.

Todor Minakov
  • 19,097
  • 3
  • 55
  • 60
  • 1
    Thanks for the detailed explanation :) I'm actually trying to implement the first option by doing `Wait Until Page Contains Element html@lang="es" timeout=30s` but I keep getting `Element 'html@lang="es"' did not appear in 30 seconds`. I also tried with just `html@lang` but it seems the attribute is not recognized at all. I've been reading the keyword reference but it doesn't mention how to inlclude an element attribute and its value. Is it the syntax I'm not getting right, or this keyword doesn't support element attributes? Thanks. – Floella Jan 22 '18 at 13:06
  • Btw, I also tried with `/html[@lang="es"]` as an argument but got the same error message. I also tried the javascript option by doing `Wait For Condition window.document.documentElement.lang==es` (with and without quotes for 'es') and still no luck. – Floella Jan 22 '18 at 13:28
  • 1
    Ok, I just got it to work! I had to use relative // xpath. So checking for `//html[@lang='es']` did the trick. Thanks again :) – Floella Jan 22 '18 at 13:36
  • 1
    @Floella `/html[@lang="es"]` is an XPATH expression, so you should set it as a locator strategy; e.g. `Wait Until Page Contains Element xpath=/html[@lang="es"]` – Todor Minakov Jan 22 '18 at 13:37
  • 1
    Better stick with a single `/` - it's the fastest possible xpath, it means "the element `html` which is the first child of the DOM"; with 2 backslashes, it'll be "an `html` element somewhere down the tree". It worked for you with the 2, cause SeleniumLibrary automatically detects the locator for an xpath one if it starts like this, and didn't work when 1 was used, cause you have to specify the strategy explicitly (my previous comment). Glad it worked out for you. – Todor Minakov Jan 22 '18 at 13:39
  • That makes total sense. Thank you again for your time :) – Floella Jan 22 '18 at 13:42
  • 1
    Accepting the solution won't hurt, then :D – Todor Minakov Jan 22 '18 at 13:49
  • One more question: is there a way I can create a generic variable for the xpath and pass an argument to select the language? So I can do something like `Wait Until Page Contains Element ${MY XPATH} ${SPANISH}` – Floella Jan 22 '18 at 13:50
  • 1
    Absolutely, you can do that - and for locators, that is the preferred way to manage them - defined as variables in external files, not in the cases or keywords, so if a locator must be changed, that's done in a single place. `${MY XPATH}= Set Variable xpath=/html[@lang="es"]`, in your case here. – Todor Minakov Jan 22 '18 at 13:58
  • I see, but this way I'd need one variable for each language I want to select, right? I'm trying to do something like set just one variable for the xpath, like `xpath=/html[@lang="${LANGUAGE}"]` and then pass the language identifier as an argument. So I can do something like `Wait Until Page Contains Element '${MY XPATH}' 'es' timeout=15s`. Is there a way to achieve this? – Floella Jan 22 '18 at 14:09
  • 1
    Ok, that's doable, but definitely cannot be explained in comments. Either create a new question, or even better, [check if this one](https://stackoverflow.com/questions/40973598/how-to-use-variable-which-value-should-be-set-in-keyword-or-test-in-xpath/42732216#42732216) is close to what you want to accomplish. – Todor Minakov Jan 22 '18 at 14:46