Автоматизируем тестирование AngularJS с Protractor

О том как установить и запустить Protractor уже было в этом посте – Тестируем AngularJS используя Protractor. А сейчас мы сделаем фокус  на том, как мы можем интегрировать Protractor в нашу систему и подкючить к Grunt.

Подключаем Protractor к Grunt

Для подключение нам необходимо поставить специальный таск-плагин grunt-protractor-runner к grunt путем выполнения следующей команды:

$ npm install grunt-protractor-runner

После чего можем дополнить наш конфигурационный файл секцией для него:

protractor: {
  options: {
    configFile: "protractor.conf.js"
  },
  all: {}
}

protractor.conf.js – конфигурационный файл, который уже у нас есть (создание подробно описано в предыдущем посте)

all: {} – grunt требует как минимум один “target”, поэтому, если все параметры уже указаны в конфиге протрактора, то можем просто добавить пустую сецию.

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

$ grunt protractor:all

PhantomJS и Protractor

В случае, когда мы хотим запускать наши тесты не только локально, но и на сервере, где нет возможности использовать браузер на помощь спешит PhantomJS. Ознакомиться с PhantomJS вы можете в посте – Создаем юнит-тесты с phantomjs, а сейчас мы поговорим о том как его встроить в Protractor.

Теперь вместо вебдрайвера мы должны запустить фантом-сервер:

$ phantomjs --webdriver=4444

а наша секция grunt конфига преобразиться следующим образом:

protractor: {
  options: {
    configFile: "protractor.conf.js"
  },
  all: {
        options: {
          args:{
            seleniumAddress: 'http://localhost:4444',
            capabilities: {
              browserName: 'phantomjs'
            }
          }
        }
      }
}

иногда бывает необходимо специфицировать конкретную версию/путь к phantomjs, тогда добавляем еще одну опцию – phantomjs.binary.path, а аргументы к фантому можем передавать через опцию phantomjs.cli.args; итого получим:

protractor: {
  options: {
    configFile: "protractor.conf.js"
  },
  all: {
        options: {
          args:{
            seleniumAddress: 'http://localhost:4444',
            capabilities: {
              browserName: 'phantomjs',
              'phantomjs.binary.path':'./node_modules/.bin/phantomjs',
              'phantomjs.cli.args': ['--ignore-ssl-errors=true',  '--web-security=false']
            }
          }
        }
      }
}

Если мы хотим, чтобы вебдрайвер запустил фантом за нас, то просто не указываем параметр seleniumAddress.

Отладка тест-сценариев в WebStorm

Пару слов о том как можно дебажить тесты в WebStorm. Что для этого нужно сделать:

Открываем настройки Run/Debug:

Добравляем новую конфигурацию для Node.js:

Прописываем следующие ключевые настройки:

  • Working directory: корневой путь вашего проекта (например: ‘/Users/stevermeister/workspace/academy-js’)
  • JavaScript file: путь к cli.js файлу протрактора (как правило это node_modules\protractor\lib\cli.js)
  • Application parameters: путь к файлу конфиг файлу протрактора(например: protractor.conf.js)

Сохраняем. Готово:

Теперь можно проставлять бреймпоинты и дебажить.

Подключение вспомогательных файлов

Иногда возникает необходимость подключить файлы, которые не являются тест сценариями, но, в которых содержится вспомогательный функционал(например логин). Для этого необходимо подключаем файл сделать формата модуля node.js (module.exports), то есть(helpers.js):

function login(){
//...
}
function logout(){
//...
}
var Helper = {};
Helper.login = login;
Helper.logout = logout;

и потом подключить в файле сценария:

var helpers = require('./helpers');
helpers.login();

Для тех, кто ранее не работал с таким синтаксисом, подчеркну: расширение “.js” в данном случае не пишется.

Примеры тест сценариев

Авторизация пользователя в приложении:

describe('Auth Module', function() {
  var ptor = protractor.getInstance();

  it('should login user', function() {
    browser.get('/login');
    browser.waitForAngular();

    element(by.model('username')).sendKeys(browser.params.login.user);
    element(by.model('password')).sendKeys(browser.params.login.password);
    element.all(by.css('.button--primary')).first().click();

    element(by.binding('user.name')).getText().then(function(username) {
      expect(username).equal(browser.params.login.username);
    });
    ptor.getCurrentUrl().then(function(url) {
      expect(url.slice(-10)).equal('/dashboard');
    });
  });
});

Параметры типа пароля в тестах, понятное дело лучше не держать. Здесь они вынесены в отдельную секцию конфига протрактора (да, согласен, тоже не комильфо, но всеравно лучше, чем прям в тестах):

params: {
  login: {
    user: 'stepan@mail.com',
    password: 'XXXXXXXXX',
    username: 'Stepan Suvorov'
  }
}

Покупка продукта

describe('Purchase item', function() {
  var ptor = protractor.getInstance();

  beforeEach(function() {
    helpers.logout();
    helpers.login();
    browser.sleep(3000);
  });

  it('should purchase an item', function() {

    browser.get('/items/552/buy');
    browser.sleep(3000);
    element(by.css('.modal-window__block .button--primary')).click();

    ptor.ignoreSynchronization = true;

    element(by.css('#mainSubmit')).click();
    element(by.css('input[type="submit"]')).click();

    ptor.ignoreSynchronization = false;
  });
});

хотел бы отметить очень важный момент в этом куске кода:

ptor.ignoreSynchronization = true;

этим мы говорим протрактору, что мы покидаем приложение и переходим на страницу без AngularJS.

Поиск по каталогу

describe('Search', function() {
  it('should do search', function() {

    browser.get('/items');
    element(by.model('itemsSearch.query')).sendKeys('iBrick', protractor.Key.ENTER);

    element.all(by.repeater('item in items')).then(function(rows) {
      var itemTitleElement = rows[0].element(by.className('item-tile__name'));
      itemTitleElement.getText().then(function(itemTitle) {
        expect(itemTitle).equal('iBrick Plus');
      })

    });
  });
});

 

Дополнительно