Любые предложения по тестированию кода extjs в браузере, желательно с селеном?

Мы использовали селен с большим успехом для обработки высокоуровневого веб-сайта (в дополнение к обширным доктринам python на уровне модуля). Однако теперь мы используем extjs для большого количества страниц, и его сложно включить тесты Selenium для сложных компонентов, таких как сетки.

Кто-нибудь успел написать автоматические тесты для веб-страниц на основе extjs? Многие поисковики ищут людей с похожими проблемами, но мало ответов. Спасибо!

Ответ 1

Самое большое препятствие при тестировании ExtJS с Selenium заключается в том, что ExtJS не отображает стандартные HTML-элементы, а Selenium IDE наивно (и по праву) генерирует команды, ориентированные на элементы, которые просто действуют как декор - лишние элементы, которые помогают ExtJS с весь рабочий стол и внешний вид. Вот несколько советов и трюков, которые я собрал при написании автоматизированного теста Selenium против приложения ExtJS.

Общие советы

Расположение элементов

При генерации тестовых примеров Selenium путем записи действий пользователя с Selenium IDE в Firefox Selenium будет основывать записанные действия на идентификаторах элементов HTML. Однако для большинства интерактивных элементов ExtJS использует генерируемые идентификаторы типа ext-gen-345, которые могут измениться при последующем посещении на той же странице, даже если никаких изменений кода не было сделано. После записи действий пользователя для теста необходимо выполнить ручное усилие, чтобы пройти все такие действия, которые зависят от сгенерированных идентификаторов и их замены. Существует два типа замещений:

Замена локатора идентификаторов с помощью указателя CSS или XPath

Локаторы CSS начинаются с "css =", а локаторы XPath начинаются с "//" (префикс "xpath =" является необязательным). Локаторы CSS менее подробные и их легче читать, и они должны быть предпочтительнее локаторов XPath. Однако могут быть случаи, когда необходимо использовать локаторы XPath, поскольку локатор CSS просто не может его обрезать.

Выполнение JavaScript

Некоторые элементы требуют больше, чем простое взаимодействие с мышью и клавиатурой из-за сложного рендеринга, выполняемого ExtJS. Например, Ext.form.CombBox на самом деле не является элементом <select>, а текстовым вводом с отдельным раскрывающимся списком, который находится где-то внизу дерева документов. Чтобы правильно имитировать выбор ComboBox, сначала можно имитировать щелчок по стрелке раскрывающегося списка, а затем щелкнуть по списку, который появляется. Однако размещение этих элементов с помощью CSS или локаторов XPath может быть громоздким. Альтернативой является поиск самого компонента ComoBox и методы вызова для имитации выбора:

var combo = Ext.getCmp('genderComboBox'); // returns the ComboBox components
combo.setValue('female'); // set the value
combo.fireEvent('select'); // because setValue() doesn't trigger the event

В Selenium команда runScript может использоваться для выполнения вышеуказанной операции в более сжатой форме:

with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }

Работа с AJAX и медленным рендерингом

Selenium имеет "* AndWait" ароматы для всех команд для ожидания загрузки страницы, когда действие пользователя приводит к переходу или перезагрузке страницы. Однако, поскольку выборки AJAX не связаны с фактическими загрузками страниц, эти команды не могут использоваться для синхронизации. Решение состоит в том, чтобы использовать визуальные подсказки, такие как наличие/отсутствие индикатора прогресса AJAX или появление строк в сетке, дополнительные компоненты, ссылки и т.д. Например:

Command: waitForElementNotPresent
Target: css=div:contains('Loading...')

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

Command: waitForElementPresent
Target: css=span:contains('Do the funky thing')
Command: click
Target: css=span:contains('Do the funky thing')

Опора на произвольные паузы не является хорошей идеей, так как разницы во времени, возникающие в результате запуска тестов в разных браузерах или на разных машинах, сделают тестовые случаи flaky.

Элементы без клика

Некоторые элементы не могут быть вызваны командой click. Это потому, что прослушиватель событий на самом деле находится в контейнере, наблюдая за событиями мыши над его дочерними элементами, которые в конечном итоге переходят к родительскому элементу. Элемент управления вкладкой - один из примеров. Чтобы щелкнуть вкладку "a", вам нужно смоделировать событие mouseDown на ярлыке вкладки:

Command: mouseDownAt
Target: css=.x-tab-strip-text:contains('Options')
Value: 0,0

Проверка поля

Поля формы (компоненты Ext.form. *), которые связаны с регулярными выражениями или vtypes для проверки, инициируют проверку с определенной задержкой (см. свойство validationDelay, которое по умолчанию установлено на 250 мс), после ввода пользователем текста или сразу же, когда поле теряет фокус - или размывает (см. свойство validateOnDelay). Чтобы инициировать проверку поля после выдачи команды типа Selenium для ввода некоторого текста внутри поля, вам необходимо выполнить одно из следующих действий:

  • Завершение отложенной проверки

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

    Command: keyUp
    Target: someTextArea
    Value: x
    Command: pause
    Target: 500
    
  • Запуск немедленной проверки

    Вы можете ввести событие размытия в поле, чтобы вызвать немедленную проверку:

    Command: runScript
    Target: someComponent.nameTextField.fireEvent("blur")
    

Проверка результатов проверки

После проверки вы можете проверить наличие или отсутствие поля ошибки:

Command: verifyElementNotPresent   
Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))]

Command: verifyElementPresent   
Target: //*[@id="nameTextField"]/../*[@class="x-form-invalid-msg" and not(contains(@style, "display: none"))]

Обратите внимание, что проверка "display: none" необходима, потому что, когда отображается поле ошибки, а затем оно должно быть скрыто, ExtJS просто спрячет поле ошибки, а не полностью удалит его из дерева DOM.

Советы по использованию элементов

Нажатие кнопки Ext.form.Button

  • Вариант 1

    Команда: нажмите  Цель: css = button: contains ('Сохранить')

    Выбирает кнопку по ее заголовку

  • Вариант 2

    Команда: нажмите  Цель: кнопка css = # save-options

    Выбор кнопки по идентификатору

Выбор значения из Ext.form.ComboBox

Command: runScript
Target: with (Ext.getCmp('genderComboBox')) { setValue('female'); fireEvent('select'); }

Сначала задает значение и затем явно запускает событие select в случае наличия наблюдателей.

Ответ 2

Этот blog мне очень помог. Он много писал на эту тему, и кажется, что он все еще активен. Парень также, кажется, ценит хороший дизайн.

В основном он говорит об использовании отправки javascript для выполнения запросов и использовании метода Ext.ComponentQuery.query для извлечения файлов так же, как и в своем внешнем приложении. Таким образом, вы можете использовать xtypes и itemIds и не должны беспокоиться о том, чтобы попытаться разобрать любой из безумных автоматически генерируемых вещей.

Я нашел эту статью, в частности очень полезную.

Возможно, вы скоро опубликуете что-то более подробное здесь - все еще пытаюсь понять, как это сделать правильно

Ответ 3

Я тестировал свое веб-приложение ExtJs с помощью селена. Одна из самых больших проблем заключалась в выборе элемента в сетке, чтобы что-то с ним сделать.

Для этого я написал вспомогательный метод (в классе SeleniumExtJsUtils, который представляет собой набор полезных методов для более простого взаимодействия с ExtJs):

/**
 * Javascript needed to execute in order to select row in the grid
 * 
 * @param gridId Grid id
 * @param rowIndex Index of the row to select
 * @return Javascript to select row
 */
public static String selectGridRow(String gridId, int rowIndex) {
    return "Ext.getCmp('" + gridId + "').getSelectionModel().selectRow(" + rowIndex + ", true)";
}

и когда мне нужно было выбрать строку, я бы просто позвонил:

selenium.runScript( SeleniumExtJsUtils.selectGridRow("<myGridId>", 5) );

Для этого мне нужно установить свой идентификатор в сетке и не позволить ExtJs генерировать его.

Ответ 4

Чтобы обнаружить, что элемент виден, вы используете предложение: not(contains(@style, "display: none")

Лучше использовать это:

visible_clause = "not(ancestor::*[contains(@style,'display: none')" +
    " or contains(@style, 'visibility: hidden') " + 
    " or contains(@class,'x-hide-display')])"

hidden_clause = "parent::*[contains(@style,'display: none')" + 
    " or contains(@style, 'visibility: hidden')" + 
    " or contains(@class,'x-hide-display')]"

Ответ 5

Можете ли вы более подробно рассказать о проблемах, с которыми вы сталкиваетесь при тестировании extjs?

Одно расширение Selenium, которое я считаю полезным, waitForCondition. Если проблема связана с событиями Ajax, вы можете использовать waitForCondition для ожидания событий.

Ответ 6

Внешние веб-страницы JS могут быть сложными для тестирования, поскольку из-за сложного HTML они в конечном итоге генерируются как с сетями Ext JS.

HTML5 Robot рассматривает это, используя ряд передовых методов для надежного поиска и взаимодействия с компонентами на основе атрибутов и условий, которые не динамический. Затем он предоставляет ярлыки для этого со всеми компонентами HTML, Ext JS и Sencha Touch, с которыми вам нужно будет взаимодействовать. Он поставляется в двух вариантах:

  • Java - известный API Selenium и JUnit, встроенный в поддержку веб-драйверов для всех современных браузеров.
  • Gwen - язык человеческого стиля для быстрого и легкого создания и поддержки тестов браузера, который поставляется с собственной интегрированной средой разработки. Все они основаны на Java API.

Например, если вы хотите найти строку сетки Ext JS, содержащую текст "Foo", вы можете сделать следующее в Java:

findExtJsGridRow("Foo");

... и вы можете сделать следующее в Гвен:

extjsgridrow by text "Foo"

Существует много документации для Java и Gwen для работы с компонентами Ext JS. В документации также подробно описывается итоговый HTML для всех этих компонентов Ext JS, которые также могут оказаться полезными.

Ответ 7

Полезные советы для выборки сетки через идентификатор сетки на странице: Я думаю, вы можете расширить полезную функцию из этого API.

   sub get_grid_row {
        my ($browser, $grid, $row)  = @_;


        my $script = "var doc = this.browserbot.getCurrentWindow().document;\n" .
            "var grid = doc.getElementById('$grid');\n" .
            "var table = grid.getElementsByTagName('table');\n" .
            "var result = '';\n" .
            "var row = 0;\n" . 
            "for (var i = 0; i < table.length; i++) {\n" .
            "   if (table[i].className == 'x-grid3-row-table') {\n".
            "       row++;\n" . 
            "       if (row == $row) {\n" .
            "           var cols_len = table[i].rows[0].cells.length;\n" .
            "           for (var j = 0; j < cols_len; j++) {\n" .
            "               var cell = table[i].rows[0].cells[j];\n" .
            "               if (result.length == 0) {\n" .
            "                   result = getText(cell);\n" .
            "               } else { \n" .
            "                   result += '|' + getText(cell);\n" .
            "               }\n" .
            "           }\n" .
            "       }\n" .
            "   }\n" .
            "}\n" .
            "result;\n";

        my $result = $browser->get_eval($script);
        my @res = split('\|', $result);
        return @res;
    }

Ответ 8

Мы разрабатываем платформу тестирования, которая использует селен и сталкивается с проблемами с extjs (с момента рендеринга на стороне клиента). Мне удобнее искать элемент, когда DOM готов.

public static boolean waitUntilDOMIsReady(WebDriver driver) {
    def maxSeconds = DEFAULT_WAIT_SECONDS * 10
    for (count in 1..maxSeconds) {
        Thread.sleep(100)
        def ready = isDOMReady(driver);
        if (ready) {
            break;
        }
    }
}

public static boolean isDOMReady(WebDriver driver){
    return driver.executeScript("return document.readyState");
}

Ответ 9

Для сложного пользовательского интерфейса, который не является формальным HTML, xPath всегда есть на что-то, на что вы можете рассчитывать, но немного сложный, когда дело касается реализации различных пользовательских интерфейсов с использованием ExtJs.

Вы можете использовать Firebug и Firexpath в качестве расширений Firefox для проверки определенного элемента xpath и просто передать полный xpath как параметр в selenium.

Например, в java-коде:

String fullXpath = "xpath=//div[@id='mainDiv']//div[contains(@class,'x-grid-row')]//table/tbody/tr[1]/td[1]//button"

selenium.click(fullXpath);

Ответ 10

Более легкое тестирование через пользовательские атрибуты HTML-данных

Из документации Sencha:

ItemId может использоваться как альтернативный способ получить ссылку на компонент, если ссылка на объект недоступна. Вместо использования id с Ext.getCmp используйте itemId с Ext.container.Container.getComponent, который будет извлекать itemId или id. Поскольку itemId является индексом внутреннего контейнера MixedCollection контейнера, itemId локально привязывается к контейнеру - избегая потенциальных конфликтов с Ext.ComponentManager, для которого требуется уникальный идентификатор.

Переопределяя метод Ext.AbstractComponent onBoxReady, я устанавливаю атрибут пользовательских данных (чье имя происходит из моего пользовательского свойства testIdAttr каждого компонента) до значения компонента itemId, если оно существует. Добавьте класс Testing.overrides.AbstractComponent в массив application.js file requires.

/**
 * Overrides the Ext.AbstracComponent onBoxReady
 * method to add custom data attributes to the
 * component dom structure.
 *
 * @author Brian Wendt
 */
Ext.define('Testing.overrides.AbstractComponent', {
  override: 'Ext.AbstractComponent',


  onBoxReady: function () {
    var me = this,
      el = me.getEl();


    if (el && el.dom && me.itemId) {
      el.dom.setAttribute(me.testIdAttr || 'data-selenium-id', me.itemId);
    }


    me.callOverridden(arguments);
  }
});

Этот метод предоставляет разработчикам возможность повторно использовать описательный идентификатор в своем коде и предоставлять эти идентификаторы каждый раз, когда страница отображается. Больше не искать через не описательные, динамически генерируемые идентификаторы.

Ответ 11

Когда я тестировал приложение ExtJS с помощью WebDriver, я использовал следующий подход: я искал поле по тексту ярлыка и получил атрибут @for от метки. Например, у нас есть метка

<label id="dynamic_id_label" class="TextboxLabel" for="textField_which_I_am_lloking_for">
Name Of Needed Label
<label/>

И нам нужно указать WebDriver на ввод: //input[@id=(//label[contains(text(),'Name Of Needed Label')]/@for)].

Таким образом, он будет выбирать идентификатор из атрибута @for и использовать его далее. Это, вероятно, самый простой случай, но он дает вам возможность найти элемент. Это намного сложнее, если у вас нет метки, но тогда вам нужно найти какой-то элемент и написать xpath, ища брата и сестры, спускаться/взойти.