Тестирование аддонов ember-cli с разными зависимостями

post logo

Разработка аддонов для ember-cli — это уже хорошо налаженный процесс, по ходу которого практически нет «белых пятен». И действительно, сразу готова файловая структура, есть готовый механизм для запуска тестов и даже CI настроен (travis-ci). И что еще надо простым разработчикам?

Есть один вариант развития событий, над которым пришлось немного подумать во время работы над новым функционалом для ember-models-table. Как вы знаете (или не знаете), данный аддон используется для отображения таблиц разной степени сложности. В далекие времена он создавался только для работы с Twitter Bootstrap 3 и не более. То есть, кнопки, элементы ввода, выпадающие списки — все было «заточено» именно под этот фреймворк. При чем, речь идет не об ember-bootstrap, а про «обычный» Bootstrap, файлы стилей и скриптов подключались напрямую как «вендорские». Шло время и приходило понимание (очень медленно), что жизнь тлен нельзя «общий» табличный функционал затачивать только под одно отображение. Так появилась «темизация». То есть, появилась возможность определить свой внешний вид для Компонентов не влезая в функционал, который они реализуют.

Подробнее о самих темах можно прочитать по ссылке или же посмотреть в исходниках аддона (ember-models-table/themes).

Теперь появилась возможность использовать аддон ember-models-table не только для BS3, но и для BS4, SemanticUI и прочих.

Остановимся на этом немного подробнее. Например, выпадающий список выглядит и реализован по-разному в TW Bootstrap и Semantic UI. Другими словами, в темах будет вызов либо bs-dropdown, либо ui-dropdown.

И вот мы подходим к тому самому вопросу, о котором пришлось подумать — а как протестировать наши темы? Как убедиться, что упомянутый выпадающий список работает корректно в стандартной теме, и в теме для Semantic UI, и в теме для ember-bootstrap?

Для решения этой проблемы надо сделать ряд вещей:

  • Убрать любые жестко заданные селекторы из кода интеграционных тестов
  • Создать для каждой темы свой «Объект Компонента», например используя ember-cli-page-object
  • Переписать те тесты, которые через чур завязаны на каком-то одном фреймворке
  • Сделать отдельные сценарии запуска тестов для каждой темы

Первых три пункта реализуются параллельно — вы создаете Объекты Компонентов, вынося в них уникальные для темы составляющие, а повторяющиеся кладете в их общего «родителя». Например:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import {collection, create} from 'ember-cli-page-object';
 
const CommonComponentDefinition = {
  scope: '.models-table-wrapper'
};
 
const Bs3ComponentDefinition = Object.assign({}, CommonComponentDefinition, {
  columnsDropDown: collection('.columns-dropdown li a', {
    toggleLabel: text('button')
  }),
});
const Bs3Component = create(Bs3ComponentDefinition);
 
const Bs4ComponentDefinition = Object.assign({}, CommonComponentDefinition, {
  columnsDropDown: collection('.columns-dropdown a', {
    toggleLabel: text('button')
  }),
});
const Bs4Component = create(Bs4ComponentDefinition);
 
const SemanticUiComponentDefinition = Object.assign({}, CommonComponentDefinition, {
  columnsDropDown: collection('.ui.compact.menu.right.floated .menu .item', {
    toggleLabel: text(),
  }),
});
const SemanticUiComponent = create(SemanticUiComponentDefinition);

Здесь columnsDropDown — это описание для выпадающего списка с опциями для показа/скрытия колонок таблицы.

Как видим, свойство scope будет одинаковым для всех Объектов Компонентов, а вот columnsDropDown — для каждого свое. Главное, что его тип и свойства/методы те же. Плюсы наследования и полиморфизма на лицо.

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

1
2
3
test('some cool description', async function () {
  await this.ModelsTablePageObject.columnsDropDown.objectAt(1).click();
});

Код такого теста никак не зависит от используемой темы и подходит для каждой из них. Остается разобраться, как указать в тесте, какой из объявленных выше Объектов Компонентов должен быть использован в this.ModelsTablePageObject. И вот мы подходим к четвертому пункту нашего «todo»-списка. В каждом аддоне есть файл config/ember-try.js, в котором описаны настройки для сценариев тестов выполняющихся в Travis-CI. Вот туда в первую очередь и добавим три записи (для BS 3/4 и Semantic UI):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
'use strict';
const getChannelURL = require('ember-source-channel-url');
 
module.exports = function() {
  return Promise.all([
    getChannelURL('release'),
    getChannelURL('beta'),
    getChannelURL('canary')
  ]).then((urls) => {
    return {
      scenarios: [
        /* ... skipped default scenarios ... */
        {
          name: 'ember-default-with-ember-bootstrap-v3',
          env: {
            EMT_UI: 'bs3'
          },
          npm: {
            devDependencies: {
                'ember-bootstrap': '^2.3.0'
            }
          }
        },
        {
          name: 'ember-default-with-ember-bootstrap-v4',
          env: {
            EMT_UI: 'bs4'
          },
          npm: {
            devDependencies: {
              'ember-bootstrap': '^2.3.0'
            }
          }
        },
        {
          name: 'ember-default-with-semantic-ui-ember',
          env: {
            EMT_UI: 'semantic-ui'
          },
          npm: {
            devDependencies: {
              'semantic-ui-ember': '^3.0.0'
            }
          }
        }
      ]
    };
  });
};

Здесь name — это имя сценария, env — объект с парами «ключ-значение», которые будут доступны в качестве ENV-переменных при запуске тестов. Для каждого сценария задается EMT_UI со своим уникальным значением. В секции npm мы можем указать, какие npm-модули надо добавить и в каком виде (dependencies или devDependencies). Тут для двух новых сценариев мы указываем, что надо установить ember-bootstrap, а для одного — semantic-ui-ember.

Значение EMT_UI будет доступно внутри функции, экспортируемой из ember-cli-build.jsв виде process.env.EMT_UI:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ember-cli-build.js
'use strict';
const EmberAddon = require('ember-cli/lib/broccoli/ember-addon');
module.exports = function(defaults) {
  let options = {};
  switch(process.env.EMT_UI) {
    case 'bs3': {
      options['ember-bootstrap'] = {
        bootstrapVersion: 3
      };
      break;
    }
    case 'bs4': {
      options['ember-bootstrap'] = {
        bootstrapVersion: 4
      };
      break;
    }
  }
  let app = new EmberAddon(defaults, options);
  return app.toTree();
};

Для аддона ember-bootstrap необходимо указать, какую именно версию TW Bootstrap мы собираемся использовать (подробнее об этом в оф. документации). Она явно задается через options['ember-bootstrap'].

Тоже самое значение process.env.EMT_UI будет и в файле tests/dummy/config/environment.js:

1
2
3
4
5
6
7
8
9
10
11
12
'use strict';
 
module.exports = function(environment) {
  let ENV = {
    // common ENV declaration
  };
 
  if (environment === 'test') {
    ENV.APP.uiFramework = process.env.EMT_UI;
  }
  return ENV;
};

К значению APP.uiFramework внутри тестов можно получить доступ через следующий вызов:

1
2
3
test('some cool description', async function () {
  const uiFramework = get(this, 'owner.application.uiFramework');
});

Опираясь на полученное значение можно выбрать нужный Объект Компонента для this.ModelsTablePageObject. Более того, можно вынести это в отдельный файл, например в tests/helpers/get-page-object.js:

1
2
3
4
5
6
7
8
9
10
11
12
13
import {get} from '@ember/object';
import ModelsTableBs3 from '../pages/models-table-bs';
import ModelsTableBs4 from '../pages/models-table-bs4';
import ModelsTableSemanticUi from '../pages/models-table-semanticui';
 
export default testContext => {
  const uiFramework = get(testContext, 'owner.application.uiFramework') || 'bs3';
  return {
      bs3: ModelsTableBs3,
      bs4: ModelsTableBs4,
      'semantic-ui': ModelsTableSemanticUi
    }[uiFramework] || ModelsTableBs3;
}

Здесь в каждом из файлов ../pages/** находятся Объекты Компонентов под каждый фреймворк.

В файлах с тестами будет в хуках будет следующее:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import getPageObject from '../../helpers/get-page-object';
import {module} from 'qunit';
import {setupRenderingTest} from 'ember-qunit';
 
module('ModelsTable | Integration', function (hooks) {
  setupRenderingTest(hooks);
 
  hooks.beforeEach(function () {
    this.ModelsTablePageObject = getPageObject(this);    this.ModelsTablePageObject.setContext(this);  });
 
  hooks.afterEach(function () {
    this.ModelsTablePageObject.removeContext();  });
});

Остался последний штрих. Надо вписать новые сценарии в конфигурационный файл .travis.yml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
jobs:
 
  include:
    - stage: "Tests"
      name: "Tests"
      script:
        - npm run lint:hbs
        - npm run lint:js
        - npm test
 
    - stage: "Additional Tests"
      env: EMBER_TRY_SCENARIO=ember-lts-2.18
    - env: EMBER_TRY_SCENARIO=ember-release
    - env: EMBER_TRY_SCENARIO=ember-beta
    - env: EMBER_TRY_SCENARIO=ember-canary
    - env: EMBER_TRY_SCENARIO=ember-default-with-jquery
    - env: EMBER_TRY_SCENARIO=ember-default-with-ember-bootstrap-v3    - env: EMBER_TRY_SCENARIO=ember-default-with-ember-bootstrap-v4    - env: EMBER_TRY_SCENARIO=ember-default-with-semantic-ui-ember

Travis Scenarios

Вот так выглядит список сценариев в Travis-CI. Приятно видеть его полностью зеленым.

Вот и все. Полученный набор тестов работает для всех тем, а каждая из этих тем проверяется в своем сценарии. Стоит обратить внимание, что темы проверяются только с текущей версией Эмбера. Если вам надо проверить и с другими версиями, то придется описать больше сценариев. Но не стоит забывать, что Travis-CI запускает параллельно только пять из них. Это значит, что чем больше сценариев, тем дольше будет идти «проверка» коммитов и пул-реквестов.

, ,

2 комментария
  1. shamcode сказал(а):

    Подсветка кода съела «=>» в стрелочных функциях, вместо это отображается «=>»

  2. KronuS сказал(а):

    @shamcode, спасибо за замечание! Поправлю.

Оставить комментарий

Top ↑ | Main page | Back