Добро пожаловать во вторую часть учебника E2E Testing with Nightwatch. В этой части мы напишем реальные тесты для React TodoMVC App. Первую часть читайте здесь.

Эта часть заимствована из тестов на g00glen00b. Исходный код можно найти здесь.

Написание первого теста

В корне каталога проекта создайте новый каталог с именем tests. Это имя папки соответствует значению свойства src_folders в файле nightwatch.json. В Nightwatch все тесты представляют собой модули Node.js.

Для нашего первого теста мы проверим, что основной и нижний разделы приложения не существуют без каких-либо задач. Это поведение по умолчанию TodoMVC App.

module.exports = {
  'Does not show the task list if there are no tasks'(client) {
    client
      .url('http://todomvc.com/examples/react/#/')
      .waitForElementVisible('.header h1')
      .expect.element('.main').to.not.be.present;
    client.end();
  },
  'Does not show the footer if there are no tasks'(client) {
    client
      .url('http://todomvc.com/examples/react/#/')
      .waitForElementVisible('.header h1')
      .expect.element('.footer').to.not.be.present;
    client.end();
  },
}

Во-первых, давайте перейдем на страницу приложения и подождем, пока не появится представление, дождавшись заголовка на этой странице, .header h1. Когда он загружается, давайте проверим, существует ли элемент с классом .main (который не должен существовать, если нет задач). Давайте вызовем метод end() и прикажем ему закрыть текущий браузер.

Если вы забудете вызвать метод end(), ваш тест все равно будет работать. Однако, если вы напишете несколько тестов в одном файле, состояние будет сохранено, что может привести к сбою ваших тестов, поскольку данные из более ранних тестов не будут удалены.

В Nightwatch вы можете выбрать стиль утверждений "чай" assert или expect. Мы не используем здесь стиль assert, поэтому нам не нужно связывать метод end(), поскольку утверждение expect в настоящее время не поддерживает связывание методов. Выбор стиля утверждения полностью зависит от вас, самое главное, чтобы вы были последовательны.

Второй тест аналогичен первому. На этот раз он проверяет, существует ли элемент с классом .footer.

Следующий тест проверит, сфокусировано ли поле ввода при загрузке страницы. Для этого мы воспользуемся селектором :focus CSS, который возвращает только элементы с фокусом. Примените это к полю ввода .new-todo - ваш тест будет успешным, только если этот элемент действительно сфокусирован:

'On page load, it sets focus on the input field'(client) {
    client
      .url('http://todomvc.com/examples/react/#/')
      .waitForElementVisible('.header h1')
      .assert.elementPresent('.header .new-todo:focus')
      .end();
  },

С elementPresent() мы можем проверить, существует ли элемент. Это не обязательно должно быть видно.

Для следующего теста мы проверим добавление новой задачи, когда мы вводим задачу в поле ввода и нажимаем клавишу Enter.

'When I add a task, it shows todo items'(client) {
    client
      .url('http://todomvc.com/examples/react/#/')
      .waitForElementVisible('.header h1')
      .setValue('.new-todo', 'New task')
      .keys(client.Keys.ENTER)
      .assert.containsText('.todo-list li:first-child label', 'New task')
      .end();
  },

Первые несколько строк остаются такими же, но с setValue() мы устанавливаем значение в поле ввода с классом new-todo. После этого мы отправляем его, нажимая клавишу Enter с помощью keys(client.Keys.ENTER) и проверяя, есть ли в .todo-list элемент с меткой с тем же текстом, что и значение, которое мы ввели ранее.

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

'Marks a todo item as completed by striking through it'(client) {
    client
      .url('http://todomvc.com/examples/react/#/')
      .waitForElementVisible('.header h1')
      .setValue('.new-todo', 'New task')
      .keys(client.Keys.ENTER)
      .click('.todo-list li:first-child .toggle')
      .assert.cssClassPresent('.todo-list li:first-child', 'completed')
      .end();
  },

На самом деле мы не проверяли, есть ли здесь линия в тексте, но проверили, присутствует ли класс completed. CSS для этого класса предоставляет строку через текст. Чтобы проверить элемент, мы использовали click() API, чтобы установить флажок.

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

Для этого мы просто добавляем задачи и выполняем их, а затем проверяем, изменяется ли число соответствующим образом:

'Shows how many items there are left'(client) {
    client
      .url('http://todomvc.com/examples/react/#/')
      .waitForElementVisible('.header h1')
      .setValue('.new-todo', 'New task')
      .keys(client.Keys.ENTER)
      .setValue('.new-todo', 'Another task')
      .keys(client.Keys.ENTER)
      .assert.containsText('.todo-count', '2 items left')
      .click('.todo-list li:first-child .toggle')
      .assert.containsText('.todo-count', '1 item left')
      .end();
  },

Здесь ничего нового. Мы добавили два элемента, проверили, соответствует ли текст ’2 items left’, затем выполнили задачу и проверили, есть ли только ’1 item left’.

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

'Does not add empty or blank tasks'(client) {
    client
      .url('http://todomvc.com/examples/react/#/')
      .waitForElementVisible('.header h1')
      .setValue('.new-todo', 'New task')
      .keys(client.Keys.ENTER)
      .keys(client.Keys.ENTER)
      .setValue('.new-todo', '  ')
      .keys(client.Keys.ENTER)
      .assert.containsText('.todo-count', '1 item left')
      .end();
  }

В том, что мы здесь сделали, нет ничего нового. Мы добавили правильную задачу, попытались отправить форму без ввода нового значения, ввели несколько пробелов и попытались отправить еще раз. Ни в одном из этих случаев не следует добавлять задачу. Должен остаться только один элемент - первая и действительная задача, которую мы добавили.

Глобальная конфигурация

Команды Nightwatch, такие как waitForElementVisible() или утверждения, требуют, чтобы параметр тайм-аута был передан элементу, чтобы тест выдал ошибку при достижении этого лимита тайм-аута. Обычно метод waitForElementVisible() выглядит так:

waitForElementVisible(‘@anElement’, 3000)

3000 - количество миллисекунд, по истечении которых тест генерирует исключение для невидимого элемента. К счастью, мы можем переместить это значение за пределы теста, чтобы код был чище. Для этого создайте файл globals.js в корне каталога проекта и вставьте этот код:

export default {  
  waitForConditionTimeout: 10000
};

После этого отредактируйте nightwatch.json файл конфигурации, изменив globals_path: “” на globals_path : “globals”.
Теперь для всех методов Nightwatch, требующих тайм-аута, этот глобальный десятисекундный тайм-аут будет задан по умолчанию. Вы по-прежнему можете определить специальный тайм-аут для одиночных вызовов, если это необходимо.

Текущие тесты

Вот и все! Все, что осталось сделать, это запустить тест. В терминале перейдите в корневой каталог ваших проектов и введите следующую команду:

npm test or yarn test

Если все настроено правильно, вы должны увидеть вывод консоли, подобный этому:

Starting selenium server... started - PID:  30647
[Overview Spec] Test Suite
==============================
Running:  Does not show the task list if there are no tasks
 ✔ Element <.header h1> was visible after 84 milliseconds.
 ✔ Expected element <.main> to not be present - element was not found in 40ms.
OK. 2 assertions passed. (8.655s)
Running:  Does not show the footer if there are no tasks
 ✔ Element <.header h1> was visible after 577 milliseconds.
 ✔ Expected element <.footer> to not be present - element was not found in 25ms.
OK. 2 assertions passed. (3.731s)
Running:  On page load, it sets focus on the input field
 ✔ Element <.header h1> was visible after 573 milliseconds.
 ✔ Testing if element <.header .new-todo:focus> is present.
OK. 2 assertions passed. (4.36s)
Running:  When I add a task, it shows todo items
 ✔ Element <.header h1> was visible after 563 milliseconds.
 ✔ Testing if element <.todo-list li:first-child label> contains text: "New task".
OK. 2 assertions passed. (4.812s)
Running:  Marks a todo item as completed by striking through it
 ✔ Element <.header h1> was visible after 561 milliseconds.
 ✔ Testing if element <.todo-list li:first-child> has css class: "completed".
OK. 2 assertions passed. (4.808s)
Running:  Shows how many items there are left
 ✔ Element <.header h1> was visible after 1599 milliseconds.
 ✔ Testing if element <.todo-count> contains text: "2 items left".
 ✔ Testing if element <.todo-count> contains text: "1 item left".
OK. 3 assertions passed. (6s)
Running:  Does not add empty or blank tasks
 ✔ Element <.header h1> was visible after 562 milliseconds.
 ✔ Testing if element <.todo-count> contains text: "1 item left".
OK. 2 assertions passed. (4.858s)
OK. 15  total assertions passed. (37.525s)

Резюме

В этом руководстве вы узнали, как:

  • создать сквозной тест и
  • используйте файл globals.js.

Следите за следующей частью, где я расскажу о Nightwatch pages!

Следуйте за мной в Twitter @codejockie