Как добавить одно ожидание двух разных элементов в Selenium [C#]

У меня есть две разные формы в моем приложении, и загрузка входных данных формы может быть выполнена с использованием предопределенного шаблона, который доступен в пользовательском интерфейсе (т.е. значение входного текстового поля будет предварительно заполнено на основе выбора шаблона). Когда произойдет загрузка данных, рендеринг элемента не произойдет, и будет заполнено только значение поля ввода. Чтобы подтвердить загрузку данных, я использую ожидание, как показано ниже.

var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(60));

wait.Until(x => x.FindElement(By.XPath(Element1_FieldXPath)).GetAttribute("value").Length > 1);

В любой момент времени будет видна только форма Form1 или Form2. Итак, мне нужно подтвердить загрузку данных либо в Form1, либо в Form2. Итак, мне нужно написать общий метод для обработки этой ситуации, и я нашел следующее решение:

public bool IsDataLoadingCompleted()
{
   var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(60));

   try
   {
       wait.Until(x => x.FindElement(By.XPath(Form1_FieldXPath)).GetAttribute("value").Length > 1);
       return true;
   }
   catch (TimeoutException)
   {
       try
       {
           wait.Until(x => x.FindElement(By.XPath(Form2_FieldXPath)).GetAttribute("value").Length > 1);
           return true;
       }
       catch (TimeoutException)
       {
           return false;
       }
   }      
}

В моем решении время ожидания в худшем случае составляет 120s, когда форма2 видна в пользовательском интерфейсе. Мне нужно решить вышеуказанную проблему с одиночным ожиданием вместо двойного ожидания.

Есть ли другой способ решить проблему с минимальным временем ожидания?

Пример HTML:

Form1 – поле 1 HTML:

<div class="form-group field field-string">
    <label class="control-label" for="root_employeeName">Employee name</label>
    <input class="form-control" id="root_employeeName" label="Employee name" placeholder="" type="text" value="">
</div>

Form2 – поле 1 HTML:

<div class="form-group field field-string">
    <label class="control-label" for="root_employeeAddress">Employee Address</label>
    <input class="form-control" id="root_employeeAddress" label="Employee Address" placeholder="" type="text" value="">
</div>

person Subburaj    schedule 14.09.2018    source источник
comment
ответ зависит от локаторов для этих форм. Можете ли вы поделиться html-кодом для форм?   -  person theGuy    schedule 14.09.2018
comment
@theGuy: образец HTML добавлен в вопрос   -  person Subburaj    schedule 14.09.2018


Ответы (3)


Вы можете разделить запятыми CssSelectors, чтобы построить логику OR, и вы можете использовать одно из следующих решений:

  • CSS_SELECTOR A:

    new WebDriverWait(_driver, TimeSpan.FromSeconds(120)).Until(ExpectedConditions.ElementToBeClickable(By.CssSelector('#root_employeeName, #root_employeeAddress')));
    
  • CSS_SELECTOR B:

    new WebDriverWait(_driver, TimeSpan.FromSeconds(120)).Until(ExpectedConditions.ElementToBeClickable(By.CssSelector("label[for='root_employeeName'], label[for='root_employeeAddress']")));
    
  • Вы можете найти ссылку в Группы селекторов

Рабочий код:

public bool IsDataLoadingCompleted(){

    var wait = new WebDriverWait(_driver, TimeSpan.FromSeconds(60));
    try
    {
        wait.Until(x => x.FindElement(By.CssSelector("[label='Form1_Field'],[label='Form1_Field']")).GetAttribute("value").Length > 1);
        return true;
    }
    catch (TimeoutException)
    {
        return false;
    }
}
person DebanjanB    schedule 14.09.2018
comment
Это было бы хорошим решением. Работает только для css-селекторов или также для xpath, className и linkText? - person Bouke; 15.09.2018
comment
Вышеупомянутое предложение разработано для меня. Я отредактировал ответ с рабочим кодом - person Subburaj; 17.09.2018

Я предполагаю, что всегда отображается одна форма, а локаторы имеют статический id

var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(60));
wait.Until(ExpectedConditions.ElementExists(By.CssSelector("input[class='form-control']")))
string elementid = FindElement(By.CssSelector("input[class='form-control']")).GetAttribute("id") // or GetAttribute("label") 
if(elementid=="root_employeeName"){
 // do your action here if name form is displayed
}
else if(elementid=="root_employeeAddress"){
// do you action here if Address form is displayed
}
person theGuy    schedule 14.09.2018

Вы можете решить эту проблему, используя параллельные задачи. Требуемый код будет выглядеть примерно так, как показано ниже, где Task.WaitAny делает свое дело.

public static bool WaitUntilOneFormLoaded(IWebDriver driver)
{
    var task1 = IsFormLoadedAsync(driver, "//some/xpath1");
    var task2 = IsFormLoadedAsync(driver, "//some/xpath2");
    Task.WaitAny(task1, task2);
    var completedTask = Task.WaitAny(task1, task2);
    switch (completedTask)
    {
        case 0: return task1.Result;
        case 1: return task2.Result;
        default: return false; // Timeout
    }
}

public static Task<bool> IsFormLoadedAsync(IWebDriver driver, string xpath)
{
    return Task.Run(() =>
    {
        try
        {
            var wait = new WebDriverWait(driver, TimeSpan.FromSeconds(60));
            wait.Until(x => driver.FindElement(By.XPath(xpath)).GetAttribute("value").Length > 1);
            return true;
        }
        catch (TimeoutException)
        {
            return false;
        }
    });
}
person Bouke    schedule 14.09.2018
comment
Это работает только тогда, когда тайм-ауты равны (в данном случае 60 секунд). Представьте, что происходит, когда один таймаут установлен на 30 секунд, а другой на 60. - person Bouke; 14.09.2018