Uibook — инструмент для визуального тестирования React-компонентов с медиа-запросами +19




Всем привет! Меня зовут Виталий Ризо, я фронтенд-разработчик в «Амплифере». Мы сделали Uibook — простой инструмент для визуального тестирования React-компонентов с реальными медиа-запросами. Расскажу, как он работает и чем может быть полезен вам.




Зачем сделали и в чём польза


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


Uibook позволяет быстро увидеть компоненты в любых состояниях и комбинациях параметров (props). Благодаря поддержке медиа-запросов, разработчик на одной странице может отобразить настольные и мобильные версии компонентов. Но Uibook полезен не только разработчикам:


  • дизайнеры могут на своём устройстве, не поднимая локальный сервер, посмотреть всевозможные состояния компонентов и передать правки;
  • менеджеры видят, что даже простой, на первый взгляд попап, может содержать кучу граничных состояний, которые разработчики вынуждены учитывать — это помогает им лучше понимать устройство интерфейса продукта изнутри;
  • редакторы могут с помощью режима Live Text Editing примерить текст для интерфейса в реальных компонентах, чтобы он выглядел безупречно.

Чем отличается от аналогов



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


  • В Uibook сразу можно смотреть компоненты в условиях ограниченной ширины и высоты устройств;
  • Он не требует отдельного сборщика и легко подключается к существующему проекту парой строк в конфигурационном файле;
  • Подразумевается, что страницы с компонентами будут доступны публично, чтобы любой пользователь мог найти ошибки и оставить обратную связь.

Uibook нужен в основном для визуального тестирования, а не разработки, хотя и разрабатывать «представляющую» часть проекта с ним удобно. Пришлось внести глобальные изменения в проект? Пробегитесь по всем страницам, чтобы убедиться в корректном отображении всех компонентов.



Техническая реализация


Uibook представляет собой React-приложение, в которое передаются Страницы — наборы «кейсов», то есть, состояний одного компонента (props и callbacks). Далее, Uibook рендерит выбранную Страницу на одном экране, используя два контроллера: с медиа-запросами и без них.


Поскольку эмулировать медиа-запросы средствами CSS и JavaScript невозможно, мы пошли простым путём: рендерить компонент внутри <iframe>, если пользователь указал ширину или высоту экрана.


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


Я не хотел иметь отдельные сборщики для проекта и визуального тестирования. Другие продукты вынуждают это делать, из-за чего приходится хранить больше файлов, зависимостей, всё это дольше настраивается, дольше запускается, сложнее собирается и деплоится. Uibook же интегрируется в сборщик проекта, так как просто добавляется как Webpack-плагин:


plugins: [
 …
 new UibookPlugin({
   controller: path.join(__dirname, '../controllers/uibook.js')
 })
]

webpack.config.js


Uibook создаёт отдельный чанк и не увеличивает размер основного приложения. Работает это через вебпаковские SingleEntryPlugin или MultiEntryPlugin. Он подтягивает стили и скрипты из основного приложения с учётом кэширования (”cachebuster”). Вот как плагин получает список нужных файлов:


 let files = compilation.chunks.find(function (i) {
   return i.name === 'uibook'
 }).files

Затем он генерирует HTML-файл без использования зависимостей. Ведь это очень простая задача, нет необходимости тянуть библиотеки для этого. Берём шаблон, добавляем импорты, добавляем в выдачу:


 compilation.assets[outputPath + '/index.html'] = { … }

Но если у вас всё же подключён HtmlWebpackPlugin, то придётся добавить uibook в исключения, о чём Uibook мило напомнит.



Uibook очень прост


У него в зависимостях только React, Webpack да create-react-class. Он написан на ES5, поэтому будет работать, даже если у вас нет Babel в проекте. А если есть, то не будет конфликтов плагинов. В Uibook встроены подсказки, если в конфигурационном файле что-то не так.



Uibook гибок



Вы можете обернуть все компоненты в свой контроллер. Это может быть обёртка для Redux, Context или всего сразу. Вот пример с новым Context API:


export default UibookStarter({
 wrapper: (children, props) =>
   <Context.Provider value={ props }>
     { children }
   </Context.Provider>,
 values: {
   locale: ['ru', 'en'],
   theme: ['dark', 'light']
 },
 …
})

Список пользовательских ключей и их значений будет отображаться в верхнем навигационном меню.


Как внедрить Uibook в проект


Например, мы хотим добавить компонент Кнопка, который лежит в src/button.js. Нужно установить пакет uibook, создать файл-контроллер и файл-страницу. Файл-контроллер служит для импорта ваших Uibook-тестов, а файл-страница — это набор «кейсов», комбинаций параметров для одного компонента.


Вот как это сделать:


1) Приступим, $ yarn add uibook;
2) Здесь можно воспользоваться командой $ npm init uibook, которая создаст файлы-примеры, а можно сделать всё вручную. Приблизительно структура получится такой:


your-project
+-- uibook
¦   +-- button.uibook.js
¦   L-- uibook-controller.js
+-- src
¦   L-- button.js
+-- webpack.config.js
L-- package.json

3) Подключаем плагин в конфигурационном файле Webpack:


let UibookPlugin = require('uibook/plugin')

module.exports = {
 …
 plugins: [
   new UibookPlugin({
     controller: path.join(__dirname, '../src/uibook-controller.js'),
   })
 ],
}

webpack.config.js


4) Запишем тест в uibook/button.uibook.js. Если вы воспользовались командой init, то этот пример уже создан:


import UibookCase from 'uibook/case'
import Button from '../src/button.js'

const PROPS = {
 onClick: UibookCase.event('onClick')
}

const ButtonUibook = {
 component: Button,
 name: 'Button',
 cases: [
     () => <UibookCase props={{ ...PROPS, isLarge: true }}>
       Large Button
     </UibookCase>,
     () => <UibookCase props={{ ...PROPS, isDisabled: true }}>
       Disabled Button
     </UibookCase>
 ]
}

export default ButtonUibook

button.uibook.js


5) Импортируем и передаём этот uibook-тест в файле-контроллере:


import UibookStarter from 'uibook/starter'
import ButtonUibook from './button.uibook'

export default UibookStarter({
 pages: {
   Button: ButtonUibook,
 }
})

uibook-controller.js


6) Готово! Запускаем свой проект как обычно (например, $ yarn start) и открываем страницу /uibook в браузере. Мы увидим три кейса с кнопкой (если у вас есть компонент /src/button.js, конечно):



Как Uibook помог нам


Мы используем Uibook в своей работе уже больше года всей командой. Фронтендеры разрабатывают новые компоненты только через Uibook, попутно создавая тест-файл с граничными параметрами (props). Это намного быстрее, чем писать параллельно контроллер, чтобы увидеть компонент в реальном веб-приложении. Более того, этот тест-файл будет использоваться в дальнейшем для визуального тестирования после каких-либо глобальных изменений.


Андрей Ситник Iskin, ведущий фронтенд-разработчик в «Злых марсианах» отмечает, что Uibook делает работу спокойнее:


Uibook дал нам наконец-то уверенность, что после обновления normalize.css у нас ничего не сломалось. Просто открываем и просматриваем подряд все компоненты. Тут сильно помогает главная фича — поддержка @media, так что на странице все состояния компонентов. У разработчиков меньше страхов, у менеджеров — меньше багов. Все довольны.


Да и сам процесс тестирования упростился. Теперь фронтендер пишет новый компонент (view-составляющую), попутно создавая файл с параметрами (props). Контроллер при этом не нужен — можно деплоить по ходу разработки, не внедряя компонент в само веб-приложение.


Другие фронтенд-разработчики ревьюят компонент, используя локальный или задеплоенный Uibook: можно понажимать на все кнопочки и проверить, что callback вызываются. Так мы экономим до 30 часов каждый месяц на тестировании компонентов.


Дамир Мельников, фронтенд-разработчик «Амплифера», также отмечает возможность совместной работы с дизайнерами, продактами и редакторами:


Uibook позволяет мне быстро работать над компонентами — примерять новые стили, следить за мобильной версией, изучать, как ведёт себя компонента при разных вводных. Кроме того, Uibook позволяет быстро поделиться своей работой с дизайнером (живой макет), редактором (для вычитки интерфейсных текстов) и другими фронтенд-разработчиками.


Контент-лид «Амплифера» Александр Марфицин замечает, как Uibook упростил работу по написанию интерфейсных текстов:


Когда делаешь тексты для интерфейса, то часто работаешь вслепую и не видишь, как будут выглядеть надписи в «живом» продукте. Uibook решает эту проблему. Можно не только вычитывать старые тексты, но и создавать новые, опираясь на ограничения компонентов и примеряя свои черновики на реальном интерфейсе. Все текстовые элементы редактируемы, это позволяет получать цельный результат — от заголовка до даже самой маленькой надписи.

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


???


Надеюсь, что Uibook найдёт применение в вашем проекте. Если остались вопросы, то посмотрите детальную инструкцию в репозитории на Гитхабе. Или пишите мне в твиттере или на почту.


Спасибо Александру Марфицину marfitsin за помощь в подготовке статьи.

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (13):

  1. astec
    /#20224262 / +1

    На английском статью можно для коллег?

  2. Bhudh
    /#20224746 / -2

    TL;DR, но кратенькое замечание: при нейминге продукта очень полезно его (name) произносить, а не просто лепить хайповые буковки.
    Представьте — Вы разговариваете с русским клиентом и произносите: «Сейчас я Вам уибук…»

    • justboris
      /#20224798

      Вроде же это читается как "юай-бук"

      • Bhudh
        /#20229560 / -1

        А GUI читается исключительно как «джыюай».
        Блаженны, кто верует.

        • 4tlen
          /#20229842

          Напишите uibook и gui так, чтобы не было смешно русскоговорящим малолеткам и понятно всем остальным. Спасибо.

          • Bhudh
            /#20229930

            Хотя бы UI Book, чтобы было видно, что это точно UI.
            Строчные буквы в слитном слове не сильно вызывают желание спеллинга.
            И зря Вы про малолеток: не знают, как английские буквы читаются, не только они, но и вполне взрослые люди, не знающие английского. Откуда-то же взялись "эс как доллар" и "вэ как галочка".

  3. i_surin
    /#20228484

    а почему решили использовать border для выделения активных элементов? есть же outline с которым вёрстка не будет путешествовать в разные стороны

    • KillBill
      /#20228498

      Если речь про режим редактирования текста, то там, конечно, используется `outline`. Но есть зелёная рамка вокруг всего рабочего пространства, она сделана через `border`, так как иначе она окажется снаружи. Можно сделать тенями, но зачем?

      • 4tlen
        /#20229846

        Тени не ломают флоу. Меньше беспокойства.

      • justboris
        /#20230894

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

  4. kashey
    /#20229980 / +1

    Просто открываем и просматриваем подряд все компоненты

    Вот так делать не надо. Этот момент следует автоматизировать и показывать визуальные дифы, если они есть.

    • Iskin
      /#20230870

      Да, автоматическое визуальное тестирование — правильная вещь. Но в рамках этой статьи не важно кто именно смотрит изменения. Для тестирования по скриншотам всё равно нужны такие же страницы UiBook со всеми состояниями.

      Но у нас чуть сложнее — мы используем UiBook ещё и для тестирования анимаций. У нас есть страницы, который постоянно крутят анимации:
      amplifr.com/uikit/#MobileLayout:ru