misc/class
lib/jquery_pnotify, lib/moment, lib/lodash, misc/notification, site/engine, misc/social
if( $.browser.msie && $.browser.version <= 8 ) include('lib/respond'); $._social.__cfg = {"init":[{"service":"basic"},{"fb_app_id":"1997094873850041","service":"fb"},{"vk_app_id":"2978320","service":"vk"},{"service":"twi"}],"like":[{"service":"fb"},{"service":"vk"},{"via":"","channel":"","hash_tag":"","service":"twi"}]}; window._SiteEngine = new classes.SiteEngine( { user_id: 0, controller: 'homepage', action: 'index', content_css_version: '1459538664', social_enabled: 0} );

Faiwer

Блог web-программиста

IT Конфигурация mocha и mocha.opts

30 апреля 2017

Случайно наткнулся на то, что можно вынести command line parameters для консольного запуска mocha в отдельный файл test/mocha.opts. Но тут же наткнулся на грабли. Файл должен лежать именно в test/mocha.opts, а у меня директория тестов называлась tests, и этого хватило. Т.е. механизм поиска файла сильно уступает аналогичному для, скажем, .eslintrc или .gitignore. Сам формат файла простой, пример:

--require test/setup.js
--compilers js:babel-core/register

Теперь mocha можно запускать без параметров. Интересная выресовывается картинка, много разных вспомогательных файлов: mocha.opts, .babelrc, .eslintrc, .gitignore, webpack.config.js, README.md, package.json и пр. шушера. А ведь не так давно мне было привычно размещать исходники прямо в корневой директории проекта, теперь же нужно им отдельную директорию создавать.

  • Во-первых чтобы они не утонули среди вспомогательных файлов
  • Во-вторых т.к. всё равно нужно куда-то итоговую сборку собирать
  • В третьих таким образом удобно избегать node_modules (к примеру для поиска по файлам проекта)

IT Написание тестов для React с использованием enzyme

29 апреля 2017

Сразу отмечу, что статья не претендует на истину. Это всего лишь мои заметки о том, как вообще можно написать и запустить простые тесты для React, т.к. тут оказалось много места где заблудиться\окопаться. Итак, начнём.

Babel

Раз React значит и JSX, а значит и babel. Но простой запуск mocha просто запускает nodejs, в котором никакого JSX нет (и наверное никогда не будет). Что делать? Поиск подсказывает, что нужно запускать mocha (далее речь идёт именно о mocha) с флагом --compilers js:babel-core/register. Что сие есть? Это флаг заставит mocha предварительно компилировать код используя babel-core/register, который грубо изнасилуетподменяет require() в nodejs, что позволяет ему предварительно изуродоскомпилировать кодовую базу в нормальный javascript

Import\Export и .babelrc

Ок, но как babel-ю подсказать, что использовать в качестве конфига? Точного внятного ответа пока дать не могу, но как минимум можно создать в корне json-файл с именем .babelrc, который будет содержать json-конфиг. Мне прокатило, но в более сложных ситуациях нужно будет искать вариант поделикатнее. Собственно, чтобы не дублировать конфиг, имеет смысл подключить его и в webpack.config.js. Сразу отмечу, что взять и распарсить .babelrc как json простым вызовом require() nodejs почему-то не смогла (видимо мешает отсутствие расширения .json), поэтому fs.readFileSync и JSON.parse вам в помощь. 

Полифилы, JSDom и прочая нецензурщина

nodeJS это nodeJS, а не браузер, а для ряда тестов потребуется имитация браузера. И похоже, что на данный момент изящных решений нет, т.к. кодовая база react-* грубо сверяется с глобальным наличием window & document. Мде. Помимо прочего всякие полифилы, вроде regenerator-а (ну или отключить транспайлинг async-await в генераторы ещё до этапа компиляции) нужны. 

Для решения вопроса с полифилами достаточно import 'babel-polyfill'. а вот с JSDom так просто не срастётся. Я воспользовался таким решением:

  • import 'jsdom-global/register';
  • принудительно зафиксировал jsdom на 8-й версии (а не 10-й)

Замечу, что жизненноважно подключать этот костыль с JSDom до подключения React, иначе груды неадекватных глупых ошибок без внятных описаний украсят вашу безмятежную жизнь. Суть в том, что в недрах разных react-либ window кешируется ещё на этапе подключения файлов. И в последствии какой-нибудь флаг вида canUseDom === false будет вам яростно мешаться.

Можно подключать эти либы в каждом тест-файле, но тогда вы столкнётесь с тем, что при запуске сразу пачки тестов, babel-polifil будет падать со словами "я уже подключён", jsdom тоже будет что-нибудь базлать. Разумная альтернатива вынести обе эти строки в некий setup.js, а потом его подключать флагом --require tests/setup.js.

Сами тесты

React поставляет в пакете react-dom ряд инструментов для тестирования. Но даже на официальной странице этих инструментов упоминается, что вам стоит воспользоваться некой либой под названием enzyme. Установка оказалась не совсем тривиальной, т.к. сильно зависит от версии вашего React-а. Очень внимательно прочитайте инструкции по установке. Заодно доставьте к нему что-нибудь вроде chai для удобства тестирования.

Отмечу, что enzyme предоставляет по сути 3 набора утилит для работы с вашими React компонентами. А именно shallow для быстрого и поверхностного рендеринга, render для работы уже с html, и mount для полноценной работы с DOM и life-cycle React-компонентов. В любых маломальски сложных ситуациях готовьтесь к танцам с бубнами. Например .simulate метод для вызова событий умеет только React-события, а повешанные через addEventListener уже нет. Готовьтесь к нефинормативным ошибкам, куда же без них. 

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

IT .babelrc & webpack

29 апреля 2017

При попытке подключить mocha к redux-react проекту мне потребовалось вынести конфигурацию для babel в .babelrc файл. Дальше веселее, оказалось, что mocha категорически отказывается запускаться с опцией { modules: false }, а вот без неё пожалуйста. На этом приключения не закончились, оказалось, что cuid не хочет запускаться с ней, а вот без неё пожалуйста. Временно решил разделить конфиги для babel & mocha, перебив эту опцию в webpack-конфиге. Но не тут то было. Экспериментальным путём выяснил, что webpack чихал на указанный вами в файле конфиг, если видит .babelrc. Решительно не понимаю, на кой чёрт так было сделано. Непродолжительный гуглёж привёл меня к тому, что ему нужно насильно запрещать это опцией babelrc: false в query для babel-loader-а.

Эх, web свернул куда-то не туда. Он, не успевши спозтись, уже куда-то расползается. И это я всё ещё не подключил тесты. Там несмотря на следование мануалам у меня пока вообще ничего не заводится. А гляда на рабочие конфиги из github-а я натыкаюсь на такие адовые конструкции по 500+ строк кода, альтернативные настройки babel с какими-то тестовыми preset-ами и пр., что…

IT JSX и вветвления ― jsx-control-statements

16 февраля 2017

Одна из раздражающих меня в React вещей, это помесь XML и JS синтаксиса. И ладно бы в одном файле, но, блин, даже в одном блоке. Сочетания XML и конструкций вроде {arr.map(arr => { очень сильно бьют, как по читаемости, так и по глазам в целом. К счастью, есть возможность этого избежать, используя jsx-control-statements. Т.к. его внедрение оказалось делом не совсем тривиальным, я решил написать об этому небольшой очерк.

Для начала, что нам даёт этот плагин?

<If condition={anyCondition}>...</If>
// вместо
{anyCondition && ...}

А также

<For each="el" index="key" of={arr}>...</For>
// вместо
{arr.map((el, key) => ...}

Т.е. XML-like конструкции, похожие на соответствующие им из XSLT (только более компактные). Также там есть ещё <Choice/> для switch-ей.

Ok, а как оно работает? Оно городит новые DOM-элементы? Нет. Оно ещё на уровне babel-я трансформируется в те самые && и .map. Т.е. это синтаксический сахар для эстетов. Ok, как подключить? 

  1. npm i jsx-control-statements
  2. в настройки babel-я в plugins добавляем "jsx-control-statements"

Всё, работает. А как насчёт lint-инга? Оказалось, что тут тоже всё схвачено (для eslint), но нужно немного понастраивать:

  1. npm i eslint-plugin-jsx-control-statements
  2. в настройках eslint
    1. в plugins добавляем jsx-control-statements
    2. в extends добавляем jsx-control-statements

Теперь никаких неудобств. Проблем с подстветкой синтаксиса тоже никаких (если у вас вообще JSX нормально отображается, конечно).

IT Пара мелочей о терминале в Linux

14 февраля 2017

История и дубликаты

Чтобы, во время листания истории введённых команд (стрелками на клавиатуре), терминал не предлагал вам одни и те же строки-дубликаты добавьте к .bashrc это:

export HISTCONTROL=ignoreboth:erasedups

Autocomplete

Если у вас имеется некий большой скрипт, и вы всё никак не можете уложить в голове все его многочисленные параметры, то можно создать свой собственный autocomplete-список для этого скрипта/программы. Сами скрипты обычно кладутся в ~/.bash_completion.d,  но это зависит от настроек вашей системы и профиля. Можно сделать, к примеру так (.bashrc):

if [ -f /etc/bash_completion ] && ! shopt -oq posix; then
    . your_script's_path
fi

Где вместо your_script's_path путь к вашему скрипту. В самом скрипте уже может быть всё что угодно. Самый простой вариант с заранее заготовленными строками для автокомплита (бездумного):

complete -W "option1 option2 option3" your_program

Это приведёт к тому, что вбив "your_program " и нажав Tab дважды вам будут предложены все три опции. Если же нужно что-то хитрее, например специфическую фильтрацию, то можно указать отдельный метод, для обработки уже введённых символов. Или, скажем, вам может потрбоваться в качестве списка вариантов список директорий/файлов в каком-то конкретно заданном месте.

_your_program()
{
    local cur=${COMP_WORDS[COMP_CWORD]}
    COMPREPLY=( $(compgen -W " $( ls some_directory's_path )" -- $cur) )
}
complete -F _your_program your_program

Этот код позволит по Tab-у получать в помощь список файлов из some_directory's_path.

#!/bin/sh

Столкнулся с тем, что указывая вначале bash-скриптов этот интерпретатор, часть правильно записанных bash-команд не срабатывает, а ругается с ошибками. Оказалось всё дело в том, что в Ubuntu-based дистрибутивах в качестве sh-интерпретатора используется некий dash, и он не в полной мере совместим с bash. Поменяв sh на bash в этой строке проблемы испарились.

В моём случае dash ругался на:

DIR=$(dirname "$(readlink -f "$0")")

Эта строка, кстати, аналог получения __dirname.