Обеспечение того, чтобы ваше приложение выглядело так, как вы ожидаете на разных устройствах, является важной частью обеспечения качественного опыта для всех ваших пользователей. Визуальный снимок автоматизирует этот процесс, и, к счастью, его довольно легко настроить с помощью Storybook. Существует множество инструментов, которые предоставляют визуальные снимки прямо из коробки - некоторые из них имеют открытый исходный код, некоторые из них являются платными. В этом посте будет показано, как визуально делать снимки ваших историй с помощью портов просмотра различных устройств, используя внутренние addon-storyshots
и addon-storyshots-puppeteer
(это было сделано не так давно Thomas BERTET 💪). Последний пример можно найти в этом репо.
Здесь я предполагаю, что вы знакомы с Storybook и имеете базовые знания о его концепциях. Чтобы этот пост был понятен широкому кругу разработчиков внешнего интерфейса, я буду использовать пакет the@storybook/html
, который поставляется с версией 4 (на момент написания это v4.0.0-alpha.18), так что там нет необходимости понимать фреймворки, такие как _4 _ / _ 5_ или другие.
У Storybook есть специальный аддон - addon-viewport
, который позволяет отображать различные порты просмотра в пользовательском интерфейсе Storybook. Этот аддон «имитирует» порт просмотра реального устройства, изменяя размер области предварительного просмотра в Storybook, поскольку Storybook - это веб-приложение, которое запускается в браузере, мы немного ограничены в том, что мы можно изменить в браузере. С кукольником мы можем управлять конфигурацией браузера из среды тестирования.
Настройка сборника рассказов
Прежде всего, нам нужно установить все необходимые зависимости, чтобы показывать наши простые html-истории. Я буду использовать yarn
, но, конечно, все достижимо и с npm
:
yarn add -D @storybook/html@alpha babel-loader @babel/core
Обратите внимание, что мы устанавливаем babel-loader
и babel/core
, поскольку сборник рассказов зависит от них (для babel-loader
v8 требуется babel/core
)
Теперь давайте создадим config.js
в каталоге .storybook
:
import { configure } from '@storybook/html'; function loadStories() { require('../src/story'); } configure(loadStories, module);
Наша конфигурация загрузит один файл истории с именем story.js
из каталога src
. Эта история будет содержать несколько простых примеров HTML:
import { storiesOf } from '@storybook/html'; import './style.css'; storiesOf('Example', module) .add('Div', () => { return '<div>Here is a first story in div</div>'; }) .add('Button', () => { return '<button>Button</button>'; }) .add('Table', () => { return `<table> <tr> <td>td</td> <td>td</td> </tr> <tr> <td>td</td> <td>td</td> </tr> </table>`; });
style.css
, импортированный туда, будет использовать медиа-запросы, чтобы создать впечатление о разных стилях для разных портов просмотра (на основе https://css-tricks.com/snippets/css/media-queries-for-standard-devices ):
/* ----------- iPhone 5, 5S, 5C and 5SE ----------- */ @media only screen and (min-device-width: 320px) and (max-device-width: 568px) and (-webkit-min-device-pixel-ratio: 2) { body { background-color: #33800b; } } /* ----------- iPhone 6, 6S, 7 and 8 ----------- */ @media only screen and (min-device-width: 375px) and (max-device-width: 667px) and (-webkit-min-device-pixel-ratio: 2) { body { background-color: #408053; } } /* ----------- iPhone 6+, 7+ and 8+ ----------- */ @media only screen and (min-device-width: 414px) and (max-device-width: 736px) and (-webkit-min-device-pixel-ratio: 3) { body { background-color: #4c8004; } } /* ----------- iPad 1, 2, Mini and Air ----------- */ @media only screen and (min-device-width: 768px) and (max-device-width: 1024px) and (-webkit-min-device-pixel-ratio: 1) { body { background-color: #7a8023; } }
Как видите, мы раскрасим фон тела в разные цвета в соответствии с согласованным медиа-запросом.
Теперь нам нужно добавить следующие сценарии в package.json
, чтобы иметь возможность запускать Storybook в режимах разработки и производства:
"scripts": { "storybook": "start-storybook -p 8008", "build-storybook": "build-storybook -o ./public" }
После выполнения описанных выше действий у вас должна получиться следующая структура файлов:
project/ .storybook/ config.js src/ story.js style.js node_modules/ package.json yarn.lock
Если до этого момента все шло хорошо, yarn storybook
покажет нам этот простой пример (переход по адресу http: // localhost: 8008 в Chrome):
Попробуем сымитировать устройства с помощью Chrome и посмотреть, какие цвета появятся. Откройте DevTools и используйте инструмент Device Emulation для эмуляции различных устройств. Соответствующий медиа-запрос запускается, чтобы раскрасить тело запрошенным цветом. И это поведение также может быть достигнуто с помощью API кукольника.
Настройка Jest + Storyshots
Теперь давайте настроим jest, чтобы иметь возможность запускать наши визуальные снимки. Прежде всего, нам нужно добавить следующие пакеты:
jest
- тестовый раннер от Facebookbabel-jest, @babel/preset-env, [email protected]
- вся эта вавилонская штука, чтобы можно было запускать ES6 в шутку@storybook/addon-storyshots
- аддон, объединяющий сборник рассказов для шуток@storybook/addon-storyshots-puppeteer
— интеграция сборника рассказов вpuppeteer
+jest-image-snapshot
И все это вместе:
yarn add -D jest
[email protected]babel-jest @babel/preset-env @storybook/addon-storyshots@alpha @storybook/addon-storyshots-puppeteer@alpha
Теперь нам нужно добавить jest.confg.js
:
module.exports = { cacheDirectory: '.cache/jest', clearMocks: true, moduleNameMapper: { '\\.(css|scss)$': '<rootDir>/styleMock.js', }, roots: [ '<rootDir>/src', ], transform: { '^.+\\.jsx?$': 'babel-jest', }, testEnvironment: 'jsdom', moduleFileExtensions: ['js', 'jsx', 'json'], };
Обратите внимание, здесь я высмеиваю файлы css - это не очень важно, поскольку мы не используем jsdom в этом примере, поэтому для его работы просто добавьте пустой styleMock.js
файл на корневой уровень.
А вот и простой файл .babelrc
:
{ "presets": [ "@babel/preset-env" ] }
На последнем этапе тестирования нам нужно добавить скрипт в package.json
(честно говоря, он в основном нужен пользователям npm):
"scripts": { "storybook": "start-storybook -p 8008", "build-storybook": "build-storybook -o ./public", "test": "jest" }
Моментальные снимки изображений можно настроить как для Storybook, работающего в режиме разработки, так и для статической сборки (режим prod). Мы уже настроили сценарий build-storybook
для создания статической версии Storybook в каталоге public
, поэтому мы будем использовать этот режим для нашего теста (я также считаю, что это лучшее решение для CI, вместо того, чтобы запускать сервер разработки во время сборки, но используйте -корпуса могут отличаться). Чтобы создать статическую сборку, выполните следующую команду:
yarn build-storybook
Теперь давайте создадим наш imageSnapshots.test.js
в каталоге src
. Шаги выше должны были быть организованы в следующую структуру файлов:
project/
.storybook/
config.js
public/
src/
imageSnapshots.test.js
story.js
style.js
node_modules/
.bablerc
jest.config.js
styleMock.js
package.json
yarn.lock
Для логики начального теста мы добавим imageSnapshot
тестовый метод в storyshots:
import path from 'path'; import initStoryshots from '@storybook/addon-storyshots'; import {imageSnapshot} from '@storybook/addon-storyshots-puppeteer'; const storybookUrl = path.resolve('public'); initStoryshots({ framework: 'html', suite: 'Image storyshots', test: imageSnapshot({ storybookUrl, }) });
Как вы можете видеть выше, мы добавили storybookUrl
в расположение файла нашей статически экспортированной Storybook. Кроме того, framework
настроен на html
, поскольку мы используем пример html, но для React / Angular / Whatever-supported следует использовать соответствующую альтернативу.
В настоящее время запущенная команда test с yarn test
будет генерировать снимки изображений для стандартного режима предварительного просмотра:
Мы хотим автоматизировать создание визуальных снимков для разных устройств. Для этого мы можем использовать DeviceDescriptors
модуль, который поставляется с кукольником. Этот файл поставляется с огромным количеством предварительно настроенных портов просмотра, которые мы можем использовать для эмуляции устройства (как мы сделали это с помощью Chrome devtools). В этом примере мы ограничим список устройств следующим: iPad, iPhone 5, iPhone 6 и iPhone 7 Plus. Чтобы сымитировать Chrome без головы на устройстве, мы будем использовать параметр customizePage
в imageSnapshot
.
import path from 'path'; import pupDevices from 'puppeteer/DeviceDescriptors'; import initStoryshots from '@storybook/addon-storyshots'; import {imageSnapshot} from '@storybook/addon-storyshots-puppeteer'; const storybookUrl = path.resolve('public'); const supportedDevices = new Set(['iPad', 'iPhone 5', 'iPhone 6', 'iPhone 7 Plus']); function createCustomizePage(pupDevice) { return function(page) { return page.emulate(pupDevice); } } for (let supportedDevice of supportedDevices) { const pupDevice = pupDevices[supportedDevice]; if (!pupDevice) { continue; } const customizePage = createCustomizePage(pupDevice); initStoryshots({ framework: 'html', suite: `Image storyshots: ${pupDevice.name}`, test: imageSnapshot({ storybookUrl, customizePage, }) }); }
Как вы можете видеть в изменениях выше, мы перебираем определение разрешенных устройств и инициализируем сторишоты на каждой итерации с разными экземплярами устройства.
Давайте теперь снова yarn test
, чтобы увидеть разницу:
Заглянув в сгенерированные изображения, мы можем увидеть разницу в цветах и размерах 😎:
Для финального проекта вы можете заглянуть в репо это. Он также включает изменения, необходимые для группировки каждого устройства в его собственный каталог моментальных снимков, но для целей этой публикации это менее актуально.
Обобщая сказанное выше, я надеюсь, что мне удалось открыть вам еще одну мощную сторону Storybook, которая укрепит вашу кодовую базу. Как обычно, наши ссылки: