Ab тестирование bootstrap

A/b-тестирование мощный инструмент. с его помощью можно улучшать различные маркетинговые показатели, начиная от конверсии отдельно взятого рекламного объявления и

A/B-тестирование — мощный инструмент. С его помощью можно улучшать различные маркетинговые показатели, начиная от конверсии отдельно взятого рекламного объявления и заканчивая экономическими показателями бизнеса в целом. О том, как добиваться реально крутых результатов, мы спросили профессионалов в области экспериментов Виталия Черемисинова, Валерия Бабушкина и Виктора Рындина.

Читайте в этом материале:

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

Виктор Рындин

Виктор Рындин, генеральный директор агентства комплексного digital-маркетинга WeMakeFab

— Что нужно знать, чтобы А/B-тестирование прошло как надо?

1. Чем меньше у вас трафика во время тестирования, тем больше вероятность выявить статистическую погрешность, а не сделать далеко идущие выводы об эффективности того или иного приема. В тестировании гипотезы должно принять участие не менее 3000 уникальных пользователей.

2. Не торопитесь с выводами. Две недели — это минимальный срок для проведения А/B-тестирования, по истечении которого вы можете рассчитывать на статистически достоверные данные.

3. Самая распространенная ошибка — это когда тестирование проводится на разных аудиториях, с разными креативами и с разными посадочными страницами. Неизвестная должна быть только одна! Хотите протестировать разный вид блоков на посадочной странице? Создайте абсолютно одинаковые условия в остальном: используйте одинаковые креативы в рекламных объявлениях, которые видит схожая аудитория, приходящая из одного рекламного источника.

4. Тестируйте несколько гипотез одновременно. Все привыкли тестировать только 2 варианта посадочной страницы, хотя в рамках одного тестирования вы можете добавить и 3, и 4 варианта, существенно сэкономив время. Главное, убедитесь, что минимального количества посетителей хватит для каждого из вариантов.

5. Проводите тестирование на всей длине воронки. Что толку, если посадочная страница приносит конверсию в 3 раза больше обычной, но до продаж эти лиды не доходят?

— Покажите на конкретном кейсе, какие выводы можно делать, исходя из сплит-тестов.

Гипотеза: клиенты, интересующиеся бухгалтерскими услугами, обращают внимание только на цену. Можно отказаться от лонгрида с информацией о компании в пользу простой посадочной страницы с калькулятором стоимости услуг.

Тест дизайна сайта бухгалтерских услуг

  А B
Посетителей 1464 1543
Процент отказа 38,8 31,3
Время на сайте 0:41 0:34
Целевое действие 19 7
Конверсия 1,30 % 0,45 %

Гипотеза подтвердилась: на короткой версии посадочной страницы пользователи меньше терялись и чаще оставляли заявку.

Особенно важно проводить сравнительные тестирования после обновления дизайна сайта. Даже если вам кажется, что новая версия очевидно лучше, чем старая. Например, из-за редизайна сайта полностью изменился внешний вид заявочной формы с предложением скидки. Визуально новая версия получилась более привлекательной, однако на деле конверсия в ее заполнение упала с 1,51 (вариант B) до 0,51 % (вариант A)!

Тест после обновления дизайна

Бабушкин Валерий

Бабушкин Валерий, директор департамента моделирования и анализа данных Х5 Retail Group

— Какие инструменты вы сейчас используете для проведения тестирований?

Если разговор идет об А/B-тестировании или просто об оценке эффективности каких-то мероприятий — у нас самописная платформа, мы используем различные методы, начиная от базовых статистик и bootstrap. Дальше — метод бакетов и линеаризации, методы снижения дисперсии, CUPED, методы вычитания предсказаний и прочая статистическая машинерия.

— Как эффективно организовать процесс генерации гипотез для тестов?

Сложно сказать, вообще исследование — это непросто. Для начала нужно собрать команду, обсудить, какая у нас есть задача и чего мы хотим достичь. Дальше команда начинает генерировать гипотезы, мы получаем какое-то их значительное количество (потому что сгенерировать предположение не очень сложно). Следующий шаг — правильно ранжировать эти гипотезы в порядке возможного выигрыша и таким образом расставить приоритеты. Затем начинаем проверку.

— Какие есть фишки или лайфхаки, позволяющие сделать процесс проверки гипотез более эффективным?

Основной инструмент — приоритизация. Гипотезы надо приоритизировать, и, кроме того, здесь важен опыт: надо смотреть, что раньше «взлетало» или «не взлетало», и в зависимости от этого не принимать участия в тестировании тех гипотез, которые точно никаких результатов не дадут. Это вряд ли можно назвать каким-то мегалайфхаком или фишкой, но тем не менее это здóрово помогает и экономит время.

— Нужны ли A/B-тесты, если ты не Яндекс или Ozon, а обычный региональный бизнес с 10 тысячами визитов и 100 лидами в месяц?

Конечно, нужны. Вопрос в том, что если бизнес никаких изменений не делает и ничего не пробует, никуда не движется и не улучшается, тогда изменения, безусловно, не нужны, потому что тестировать нечего. В противном случае это даже не вопрос: необходимость изменений и улучшений — это данность.

Черемисинов Виталий

Черемисинов Виталий, Head of data в AIC, сo-founder в Experiment Fest

— Какие инструменты вы сейчас используете для проведения тестирований?

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

— Как правильно проверять статистическую значимость тестов, более сложных, чем замер конверсии? Например, для небинарных показателей типа среднего чека?

Часто даже с бинарными данными допускают очень большие грубые ошибки.

При работе с данными, у которых достаточно большая размерность (выручка, например), одной из самых значимых проблем является большая дисперсия. Это первое, про что стоит помнить. Просто так убрать наблюдения, которые больше всего на эту дисперсию влияют, мы не можем: это покупатели, и далеко не факт, что они случайны (хотя и такое бывает). Следовательно, нам нужно уметь эту дисперсию сокращать малой кровью, и один из таких способов — стратификация и децильный анализ (подробнее можно почитать в моем блоге:
https://medium.com/statistics-experiments.

Если говорить именно про методы проверки статистических гипотез, то я бы хотел начать с того, что лучше не использовать. Я бы рекомендовал не использовать ранговые критерии (Манна —Уитни, например). Работает критерий следующим образом:

  1. Объедините все данные в единый ряд, пометив те из них, которые принадлежат разным выборкам.
  2. Проранжируйте значения, приписывая меньшему значению меньший ранг.
  3. Подсчитайте сумму рангов отдельно для каждой выборки.
  4. Определите большую из двух ранговых сумм.
  5. Определите значение U по формуле:

Формула

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

Мы для решения таких задач используем либо bootstrap, либо бакетный метод.
Бакетирование подходит тогда, когда необходимо:
а) сохранить информацию о дисперсии и среднем в выборке до трансформации;
б) привести к нормальному распределению.

Мы формируем бакеты n-размера, внутри каждого бакета считаем среднее, далее берем новый бакет, но без возвращения.

Далее мы можем использовать для оценки параметрический критерий.

Критерий

— Какие есть фишки, лайфхаки, приемы, позволяющие сделать процесс проверки гипотез более эффективным?

Главным источником фишек является учебник по математической статистике.😁

А вообще, основная фишка и лайфхак одновременно — уметь при помощи теоретической базы (математическая статистика, комбинаторика и т. п.) решать конкретные задачи бизнеса.


А/B-тестирование — это не просто проверка, что лучше работает: синяя или красная кнопка на сайте. Это сложная работа, в результате которой решаются конкретные маркетинговые задачи, например увеличение среднего чека. Изучайте математическую статистику (кстати, здесь крутая подборка книг по аналитике) и экспериментируйте! Как сказал Валерий Бабушкин, необходимость изменений и улучшений — это не вопрос. Это данность.

Показать меню

Найти на сайте

На этом уроке узнаем, как скачать и подключить фреймворк Bootstrap (версию 3 или 4) к сайту.

Набор инструментов для изучения Bootstrap

Минимальный набор инструментов (программ) для создания веб-проектов на фреймворке Bootstrap:

  • текстовый редактор для работы с кодом («Блокнот», «Brackets», «Notepad++» или др.);
  • браузер (Google Chrome, Mozilla Firefox, Opera, Safari, Internet Explorer или др.).

Загрузка фреймворка Bootstrap

Создание веб-проекта, в основу дизайна которого положен фреймворк Bootstrap всегда начинают с его загрузки. Загрузить фреймворк Bootstrap можно различными способами
. Например, посредством ссылки, расположенной на сайте getbootstrap.com или с помощью пакетного менеджера npm, Composer, Bower или др. Способ как это осуществить зависит от вашего опыта или конкретной ситуации
.

Как установить фреймворк Bootstrap с помощью пакетных менеджеров, а также как осуществить его сборку посредством Grunt, можно почитать в этой статье .

Наиболее просто выполнить загрузку – это воспользоваться ссылкой
. На сайте Bootstrap присутствуют 2 ссылки.

Первая ссылка содержит готовые к использованию файлы CSS и JavaScript
. Эту сборку в основном используют для изучения фреймворка или для использования в проектах, дизайн которых может быть выполнен в стилях заложенных авторами по умолчанию.

Скачать Bootstrap 3.4.0
Скачать Bootstrap 4.2.1

Вторая ссылка содержит фреймворк в исходных кодах
. Эта версия более удобна для разработки сайтов, т.к. позволяет очень просто изменить стили, цветовую гамму компонентов, выполнить их настройку и др. Но эти исходные файлы, перед тем как использовать на странице, нужно будет скомпилировать и минимизировать. Данный процесс обычно автоматизируют, например, с помощью Gulp.

Исходные коды Bootstrap 3.4.0
Исходные коды Bootstrap 4.2.1

Распаковка архива Bootstrap

После скачивания архива (с готовыми к применению CSS и JavaScript файлами), его необходимо распаковать в каталог вашего веб-проекта.

Если рассмотреть архив, то можно заметить, что он имеет следующее содержимое (на примере Bootstrap 3.4.0):

Bootstrap/
├── css/
│ ├── bootstrap.css
│ ├── bootstrap.min.css
│ ├── bootstrap-theme.css
│ └── bootstrap-theme.min.css
├── js/
│ ├── bootstrap.js
│ └── bootstrap.min.js
└── fonts/
├── glyphicons-halflings-regular.eot
├── glyphicons-halflings-regular.svg
├── glyphicons-halflings-regular.ttf
└── glyphicons-halflings-regular.woff

В каталоге css находятся стили фреймворка Bootstrap, а в js — плагины для обеспечения работы некоторых компонентов. Плагины написаны с использованием функций библиотеки jQuery. Поэтому перед Bootstrap JS необходимо подключить библиотеку jQuery.

Как вы можете заметить, в архиве есть 2 версии CSS и JavaScript файлов, т.е. с суффиксом min и без него. Версия файла с min ничем ни отличается от без min
, она просто минимизирована (сжата).

В продакшене (на рабочем сайте) лучше использовать минимизированные версии файлов. Эти файлы имеют меньший размер, и, следовательно, обеспечивают более быструю загрузку страниц сайта.

Не минимизированные версии более удобно использовать при разработке, а также для изучения.

Кроме этих файлов, в данный архив ещё входит иконочный шрифт «Glyphicons» . Шрифт «Glyphicons» насчитывает более 250 иконок из набора «Glyphicon Halflings». Шрифт представлен с помощью 4 файлов: glyphicons-halflings-regular.eot , glyphicons-halflings-regular.svg , glyphicons-halflings-regular.ttf , glyphicons-halflings-regular.woff).

Такое разнообразие форматов одного и того же шрифта необходимо для того чтобы обеспечить его отображение во всех браузерах.

Про иконки в формате шрифта, а также, про то какие они имеют достоинства и недостатки, можно почитать в этой статье .

Архив фреймворка Bootstrap 4 практически ничем ни отличается от Bootstrap 3
. Основное его отличие в том, что он не содержит шрифт «Glyphicons»
. Если вам нужны шрифтовые иконки, то их необходимо будет подключить самостоятельно. Например, воспользовавшись одним из следующих наборов: FontAwesome , Octicons, Glyphicons, IcoMoon или др. Если же вы не хотите использовать готовый шрифт, а создать свой, который будет состоять только из нужных значков, то воспользуйтесь этой информацией .

Кроме этого архив Bootstrap 4 содержит ещё файлы
bootstrap-grid.css и bootstrap-reboot.css . Данные файлы необходимы только тем, кому нужен не целый фреймворк, а только его часть
.

Первый файл (bootstrap-grid.css) содержит сетку Bootstrap , а второй (bootstrap-reboot.css) — нормализатор, который устанавливает базовые стили, для того чтобы они у всех HTML-элементов во всех браузерах были одинаковыми.

Подключение Bootstrap к HTML странице

Процесс установки фреймворка Bootstrap 3 состоит из подключения следующих файлов к HTML 5 странице:

  1. Bootstrap CSS (bootstrap.min.css);
  2. Последней версии библиотеки jQuery (необходима для работы JS плагинов Bootstrap);
  3. Bootstrap JavaScript (bootstrap.min.js).

Примечание:
Файлы JavaScript лучше подключать перед закрывающим тегом body (

Bootstrap это библиотека. Библиотека содержит шаблоны CSS, HTML, JavaScript, дизайн для кнопок, меток, таблиц, веб-форм и другие элементы web страниц.

Bootstrap использует библиотеку

jQuery

Bootstrap полезный для мобильных телефонов и устройств с маленькими экранами. Он поставляется с библиотекой кнопок, стилями навигации и другими инструментами, допускающими многократное применение.

Самое главное, Bootstrap облегчает работу с разными браузерами и платформами.


  Html  

<html>

<!— заголовок —>

     <head>

          <meta charset=«utf-8»>

          <title>Example</title>

     </head>

<!— страница —>

     <body>

          <!— подключить библиотеку jQuery —>

          <script src=«http://dream-land.by/example_lib/jquery/jquery-3.3.1.min.js»></script>

<!— подключить библиотеку Bootstrap (css файл) —>

          <link rel=«stylesheet» href=«http://dream-land.by/example_lib/bootstrap-4.1.3/css/bootstrap.min.css»>

<!— подключить библиотеку Bootstrap (js файл) —>

          <script src=«http://dream-land.by/example_lib/bootstrap-4.1.3/js/bootstrap.min.js»></script>

<!— HTML элементы —>

          <form>

               <div class=«form-group»>

                    <label for=«email»>Email address:</label>

                    <input type=«email» style=’width:300px;’ class=«form-control» id=«email»>

               </div>

               <div class=«form-group»>

                    <label for=«pwd»>Password:</label>

                    <input type=«password» style=’width:300px;’ class=«form-control» id=«pwd»>

               </div>

               <div class=«checkbox»>

                    <label><input type=«checkbox»> Remember me</label>

               </div>

               <button type=«submit» class=«btn btn-default»>Submit</button>

          </form>

     </body>

</html>

Если использовать стили Bootstrap 3 и подключаете библиотеку Bootstrap 4 тогда не все будет работать правильно (Например: navbar плохо работал ).

Решение проблем с внедрением bootstrap

Здравствуйте, уважаемые форумчане!
Решил познать азы шаблоностроения, но вот вопросов, больше чем решений пока :(
Знаний моих очень маловато, так что надеюсь, эта тема будет полезна не только мне, но и другим новичкам.
Итак, к делу…
Значит, дефолтный шаблон скопировал, переименовал с заменой дефолтного названия на новое по всем файлам. Далее скачал бутстрап с сайта и все скрипты. Пытаюсь прикрутить его к шаблону, а потом уже ваять позиции и все такое прочее.
Файл template пока такой

  1. <?php

  2. /******************************************************************************/

  3. // //

  4. // InstantCMS v1.10 //

  5. // http://instantcms.ru/ //

  6. // Шаблон от AndroS //

  7. // //

  8. /******************************************************************************/

  9. // Предотвращаем прямой доступ к файлу

  10. if(!defined('VALID_CMS')) die('Forbidden');

  11. // Подключаем классы

  12. $inUser = cmsUser::getInstance();

  13. $inPage = cmsPage::getInstance();

  14. // Здесь же определяем переменную для системных сообщений

  15. $mod_count['top'] = $inPage->countModules('top');

  16. $mod_count['right'] = $inPage->countModules('right');

  17. ?>

  18. <!DOCTYPE html>

  19. <html lang="ru">

  20. <head>

  21. <meta charset="utf-8">

  22. <!-- Подключаем jQuery и <meta> с описанием сайта и т.д. -->

  23. <?php $inPage->printHead(); ?>

  24. <link href="/css/bootstrap.css" rel="stylesheet">

  25. <style>

  26. body {

  27. padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */

  28. }

  29. </style>

  30. <link href="/css/bootstrap-responsive.css" rel="stylesheet">

  31. <!--[if lt IE 9]-->

  32. <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>

  33. <!-- Подключаем скрипты для админа -->

  34. <?php if($inUser->is_admin){ ?>

  35. <script src="/admin/js/modconfig.js" type="text/javascript"></script>

  36. <script src="/templates/bimall/js/nyromodal.js" type="text/javascript"></script>

  37. <link href="/templates/bimall/css/modconfig.css" rel="stylesheet" type="text/css" />

  38. <link href="/templates/bimall/css/nyromodal.css" rel="stylesheet" type="text/css" />

  39. <?php } ?>

  40. </head>

  41. <body>

  42. <?php if (cmsConfig::getConfig('siteoff') && $inUser->is_admin) { ?>

  43. <div style="margin:4px; padding:5px; border:solid 1px red; background:#FFF; position: fixed;opacity: 0.8; z-index:999"><strong style="color:red">Сайт отключен.</strong> Только администраторы видят его содержимое.</div>

  44. <?php } ?>

  45. <div id="topmenu">

  46. <?php $inPage->printModules('topmenu'); ?>

  47. </div>

  48. <div id="usermenu">

  49. <?php if (!$inUser->id){ ?>

  50. <span class="register"><a href="/registration">Регистрация</a></span>

  51. <span class="login"><a href="/login">Вход</a></span>

  52. <?php } else { ?>

  53. <?php $inPage->printModules('usermenu'); ?>

  54. <?php } ?>

  55. </div>

  56. <div class="navbar navbar-fixed-top">

  57. <div class="navbar-inner">

  58. <div class="container">

  59. <a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">

  60. <span class="icon-bar"></span>

  61. <span class="icon-bar"></span>

  62. <span class="icon-bar"></span>

  63. </a>

  64. <a class="brand" href="#">Project name</a>

  65. </div>

  66. </div>

  67. </div>

  68. <div class="container">

  69. <h1>Bootstrap starter template</h1>

  70. <p>Use this document as a way to quick start any new project.<br> All you get is this message and a barebones HTML document.</p>

  71. </div> <!-- /container -->

  72. <!-- Подключаем скрипты бутстрап-->

  73. <script src="/templates/bimall/js/bootstrap.js"></script>

  74. <script src="/templates/bimall/js/bootstrap.min.js"></script>

  75. <script src="/templates/bimall/js/bootstrap-transition.js"></script>

  76. <script src="/templates/bimall/js/bootstrap-alert.js"></script>

  77. <script src="/templates/bimall/js/bootstrap-modal.js"></script>

  78. <script src="/templates/bimall/js/bootstrap-dropdown.js"></script>

  79. <script src="/templates/bimall/js/bootstrap-scrollspy.js"></script>

  80. <script src="/templates/bimall/js/bootstrap-tab.js"></script>

  81. <script src="/templates/bimall/js/bootstrap-tooltip.js"></script>

  82. <script src="/templates/bimall/js/bootstrap-popover.js"></script>

  83. <script src="/templates/bimall/js/bootstrap-button.js"></script>

  84. <script src="/templates/bimall/js/bootstrap-collapse.js"></script>

  85. <script src="/templates/bimall/js/bootstrap-carousel.js"></script>

  86. <script src="/templates/bimall/js/bootstrap-typeahead.js"></script>

  87. </body>

  88. </html>

Уважаемые знатоки, подскажите для начала, может, я какую-то ошибку допустил в прицеплении js и в оформлении шаблона в целом? На структуру прошу внимания не обращать, ибо пока проверяю работоспособность js. Почему-то изменения не применяются, кеш чистил, в том числе и системную папку cache. Модуль меню пользователя отображается тупо в таком виде вместо выпадающей кнопки

А вот код моего wwwtemplatesbimallmodulesmod_usermenu.tpl

  1. <div class="mod_user_menu">

  2. <div class="btn-group">

  3. <button class="btn"><a href="{profile_url login=$login}">{$nickname}</a></button>

  4. <button class="btn dropdown-toggle" data-toggle="dropdown"><span class="caret"></span></button>

  5. <ul class="dropdown-menu">

  6. {if $is_billing}

  7. <li><span>

  8. <a href="{profile_url login=$login}#upr_p_balance" title="Баланс">{if $balance}{$balance}{else}0{/if}</a>

  9. </li>

  10. {/if}

  11. {if $users_cfg.sw_msg}

  12. <li>

  13. {if $newmsg.total}

  14. <a class="has_new" href="/users/{$id}/messages{if !$newmsg.messages}-notices{/if}.html" title="{$LANG.NEW_MESSAGES}: {$newmsg.messages}, {$LANG.NEW_NOTICES}: {$newmsg.notices}">{$LANG.USERMENU_MESS} ({$newmsg.total})</a>

  15. {else}

  16. <a href="/users/{$id}/messages.html">{$LANG.USERMENU_MESS}</a>

  17. {/if}

  18. </li>

  19. {/if}

  20. {if $users_cfg.sw_blogs}

  21. <li>

  22. <a href="/blogs/my_blog.html">{$LANG.USERMENU_MY_BLOG}</a>

  23. <li>

  24. {/if}

  25. {if $users_cfg.sw_photo}

  26. <li>

  27. <a href="/users/{$id}/photoalbum.html">{$LANG.USERMENU_PHOTOS}</a>

  28. </li>

  29. {/if}

  30. {if $is_can_add && !$is_admin}

  31. <li>

  32. <a href="/content/my.html">{$LANG.USERMENU_ARTICLES}</a>

  33. </li>

  34. <li>

  35. <a href="/content/add.html">{$LANG.USERMENU_ADD_ARTICLE}</a>

  36. </li>

  37. {/if}

  38. {if $is_admin}

  39. <li>

  40. <a href="/admin" target="_blank">{$LANG.USERMENU_ADMININTER}</a>

  41. </li>

  42. {/if}

  43. <li class="divider"></li>

  44. <li>

  45. <a href="/logout">{$LANG.USERMENU_EXIT}</a>

  46. </li></ul>

  47. </div>

  48. </div>

Ну где же наши знатоки, спят еще? :)

нужно проверить правильность указания и размещения файлов стилей и скриптов, возможно где то ошибка, лучше сделать тестовую площадку чтоб можно было смотреть «в живую»
вот это:

  1. <link href="/css/bootstrap.css" rel="stylesheet">

у меня вызывает сомнения — стили точно подгружаются?

и это

  1. </style> <link href="/css/bootstrap-responsive.css" rel="stylesheet">

расположение правильное?

Попробуй прописать полный путь до bootstrap-responsive.css вот так

  1. <link href="/templates/bimall/css/bootstrap-responsive.css" rel="stylesheet">

А в mod_usermenu.tpl прописать только ту JS которая будет отвечать за меню

  1. {literal}<script src="/templates/bimall/js/Скрипт.js" type="text/javascript"></script>{/literal}

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

У вас путь к файлу стилей прописан от корня сайта а не от папки с шаблоном

Так и должно быть, Юрич верно подсказал.

я сомневаюсь, судя по вашему коду, что вы все фишки bootstrap использовать будете

Потом удалю неиспользуемые. Сейчас важно разобраться, что к чему.
Теперь вот вопрос: нужно ли какие-то правки в js делать? Кнопка появилась, только вот нажатия на нее не выполняют никаких действий

это надо основную js обновлять до 1,7 минимум. а по умолчанию в ICMS 1.5.2

секция head

  1. <head>

  2. <!-- HEAD !-->

  3. <?php $inPage->printHead(); ?>

  4. <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>

  5. <?php if($inUser->is_admin){ ?>

  6. <script src="/admin/js/modconfig.js" type="text/javascript"></script>

  7. <script src="/templates/bootstrap/js/nyromodal.js" type="text/javascript"></script>

  8. <link href="/templates/bootstrap/css/modconfig.css" rel="stylesheet" type="text/css" />

  9. <link href="/templates/bootstrap/css/nyromodal.css" rel="stylesheet" type="text/css" />

  10. <?php } ?>

  11. <script src="/templates/bootstrap/js/bootstrap.js"></script>

  12. <link href="/templates/bootstrap/css/bootstrap.css" rel="stylesheet">

  13. <link href="/templates/bootstrap/css/styles.css" rel="stylesheet">

  14. <link href="/templates/bootstrap/css/bootstrap-responsive.css" rel="stylesheet">

  15. <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->

  16. <!--[if lt IE 9]>

  17. <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>

  18. <![endif]-->

  19. </head><!-- END HEAD -->

где bootstrap = имя шаблона

юзер меню

  1. <ul class="nav pull-right">

  2. <li><a href="{profile_url login=$login}">{$nickname}</a></li>

  3. {if $is_billing}<li><a href="{profile_url login=$login}#upr_p_balance" title="Баланс">{if $balance}{$balance}{else}0{/if}</a></li>{/if}

  4. {if $users_cfg.sw_msg}

  5. {if $newmsg.total}

  6. <li><a class="has_new" href="/users/{$id}/messages{if !$newmsg.messages}-notices{/if}.html" title="{$LANG.NEW_MESSAGES}: {$newmsg.messages}, {$LANG.NEW_NOTICES}: {$newmsg.notices}">{$LANG.USERMENU_MESS} ({$newmsg.total})</a></li>

  7. {else}

  8. <li><a href="/users/{$id}/messages.html">{$LANG.USERMENU_MESS}</a></li>

  9. {/if}

  10. {/if}

  11. {if $users_cfg.sw_blogs}<li><a href="/blogs/my_blog.html">{$LANG.USERMENU_MY_BLOG}</a></li>{/if}

  12. {if $users_cfg.sw_photo}<li><a href="/users/{$id}/photoalbum.html">{$LANG.USERMENU_PHOTOS}</a></li>{/if}

  13. {if $is_can_add && !$is_admin}

  14. <li><a href="/content/my.html">{$LANG.USERMENU_ARTICLES}</a></li>

  15. <li><a href="/content/add.html">{$LANG.USERMENU_ADD_ARTICLE}</a></li>

  16. {/if}

  17. {if $is_admin}<li><a href="/admin" target="_blank">{$LANG.USERMENU_ADMININTER}</a></li>{/if}

  18. <li><a href="/logout">{$LANG.USERMENU_EXIT}</a></li>

  19. <li class="dropdown">

  20. <a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>

  21. <ul class="dropdown-menu">

  22. <li><a href="#">Action</a></li>

  23. <li><a href="#">Another action</a></li>

  24. <li><a href="#">Something else here</a></li>

  25. <li class="divider"></li>

  26. <li><a href="#">Separated link</a></li>

  27. </ul>

  28. </li>

  29. </ul>

это надо основную js обновлять до 1,7 минимум. а по умолчанию в ICMS 1.5.2

да! без этого работать не будет
но с обновленным js перестанут работать некоторые функции в админке

js тут вот какое дело ребята, система наша работает с версией библиотеки v1.5.2, замена ее на более новую версию влечет за собой появление глюков на сайте а без последней версии библиотеки Bootstrap работать не будет.
bootstrap.veliovgroup.com/javascript.php под блоками почитайте о чем вас предупреждают.
smileшаблон на bootstrap наверное только в части цсс получится а js наверное пока еще нет

Сам с этой проблемой столкнулся, нужно было было модальное окно на страницу добавить а оно вот как со мной ну и с вами

Понятно, будем обновлять тогда jquery

но с обновленным js перестанут работать некоторые функции в админке

Если только в админке не будут работать, то можно в админке оставить старую версию, а на самом сайте обновить до 1.8.2 (последняя на текущий момент). Придется хакнуть page.class.php

да, я птался обновить до последней версии, но перестают работать комментарии и быстрая настройка модулей в пользовальской части интерфейса- не всплывают окна

У меня после обновления все работало, кроме некоторых вещей в админки.

Используя этот сайт, вы соглашаетесь с тем, что мы используем файлы cookie.

1

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

Когда мы начинаем разработку приложений с сотнями тысяч строк JavaScript, мы не можем избежать тестирования кода. Несколько разработчиков JavaScript говорят, что тестирование JavaScript еще более важно, так как поведение языка неизвестно до выполнения.

К счастью, AngularJS облегчает тестирование кода, с использованием фреймворка, при помощи таких функций, как Dependency Injection (DI). Эта статья расскажет о тестировании Bootstrap блоков приложения AngularJS (включает блоки конфигурации, блоки запуска и resolve блоки маршрутов), системе событий и анимации.

Тестирование блоков конфигурации и запуска

Блоки конфигурации и запуска выполняются в начале жизненного цикла модуля. Они содержат важную логику, которая управляет тем, как работает модуль, виджет или приложение. Немного сложно проверить их, поскольку они не могут быть тотчас вызваны, как другие компоненты. В то же время, они не могут быть проигнорированы, так как они играют ключевую роль.

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

angular.module('configAndRunBlocks', ['ngRoute'])
.config(function ($routeProvider) {
$routeProvider.when('/home', {
templateUrl: 'home.html',
controller: 'HomeController',
resolve: {
bootstrap: ['$q', function ($q) {
return $q.when({
prop: 'value'
});
}]
}
})
.when('/details/:id', {
templateUrl: 'details.html',
controller: 'DetailsController'
})
.otherwise({
redirectTo: '/home'
});
})
.run(function ($rootScope, messenger) {
messenger.send('Bootstrapping application');
$rootScope.$on('$locationChangeStart', function (event, next, current) {
messenger.send('Changing route to ' + next + ' from ' + current);
});
});

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

Следующий фрагмент моделирует зависимости, используемые в блоке выше и загружает модуль:

describe('config and run blocks', function () {
var routeProvider, messenger;
beforeEach(function () {
module('ngRoute');
module(function ($provide, $routeProvider) {
routeProvider = $routeProvider;
spyOn(routeProvider, 'when').andCallThrough();
spyOn(routeProvider, 'otherwise').andCallThrough();
messenger = {
send: jasmine.createSpy('send')
};
$provide.value('messenger', messenger);
});
module('configAndRunBlocks');
});
beforeEach(inject());
});

Я намеренно не моделировал объект $routeProvider, так как мы протестируем зарегистрированные маршруты позже в этой статье.

Теперь, когда модуль загружен, блоки конфигурации и запуска уже были выполнены. Таким образом, мы можем начать тестировать их поведение. Так как блок конфигурации регистрирует маршруты, мы можем проверить, зарегистрировал ли он правильные маршруты. Мы проверим, зарегистрировано ли ожидаемое количество маршрутов. Следующие тесты проверяют работоспособность блока конфигурации:

describe('config block tests', function () {
it('should have called registered 2 routes', function () {
//Otherwise internally calls when. So, call count of when has to be 3
expect(routeProvider.when.callCount).toBe(3);
});
it('should have registered a default route', function () {
expect(routeProvider.otherwise).toHaveBeenCalled();
});
});

Блок запуска в образце кода вызывает службу и регистрирует событие. Мы протестируем событие позже в этой статье. На данный момент, давайте проверим вызов метода службы:

describe('run block tests', function () {
var rootScope;
beforeEach(inject(function ($rootScope) {
rootScope = $rootScope;
}));
it('should send application bootstrap message', function () {
expect(messenger.send).toHaveBeenCalled();
expect(messenger.send).toHaveBeenCalledWith("Bootstrapping application");
});
});

Тестирование системы событий

Событие скопления является одним из хороших способов того, чтобы два объекта взаимодействовали друг с другом, даже если они совершенно не знают друг друга. AngularJS обеспечивает эту функцию через события $emit/$broadcast на $scope. Любой объект в приложении может вызвать событие или слушать событие, в зависимости от необходимости.

При запуске приложения доступны и подписчик, и издатель событий. Но, так как модульные тесты написаны в изоляции, мы имеем только один из объектов, доступный в модульных тестах. Так, спецификации теста будут имитировать другой конец, чтобы иметь возможность проверить работоспособность.

Давайте протестируем событие, зарегистрированное в блоке запуска выше:

$rootScope.$on('$locationChangeStart', function (event, next, current) {
messenger.send('Changing route to ' + next + ' from ' + current);
});

Событие $locationChangeStart транслируется службой $location всякий раз, когда меняется расположение приложения. Как уже упоминалось, мы должны вручную запустить это событие и протестировать, отправляется ли сообщение отправителем. Следующий тест выполняет эту задачу:

it('should handle the $locationChangeStart event', function () {
var next = '/second';
var current = '/first';
rootScope.$broadcast('$locationChangeStart', next, current);
expect(messenger.send).toHaveBeenCalled();
expect(messenger.send).toHaveBeenCalledWith('Changing route to ' + next + ' from ' + current);
});

Тестирование маршрутов

Маршруты определяют способ навигации приложения пользователями. Любое неправильное или случайное изменение конфигурации маршрутов приведет к плохому ux. Так, у маршрутов тоже должны быть тесты.

Пока ngRoute и ui-router являются наиболее широко используемыми маршрутами в приложениях AngularJS. Маршруты для обоих из этих провайдеров должны быть определены в блоке конфигурации, в то время как данные маршрута доступны через службы. Данные маршрута, конфигурированные ngRoute, доступны через сервис $route. Данные маршрута ui-router доступны через сервис $state. Эти услуги могут быть использованы для проверки, правильно ли конфигурирован набор маршрутов.

Рассмотрим следующий блок конфигурации:

angular.module('configAndRunBlocks', ['ngRoute'])
.config(function ($routeProvider) {
$routeProvider.when('/home', {
templateUrl: 'home.html',
controller: 'HomeController',
resolve: {
bootstrap: ['$q', function ($q) {
return $q.when({
prop: 'value'
});
}]
}
})
.when('/details/:id', {
templateUrl: 'details.html',
controller: 'DetailsController'
})
.otherwise({
redirectTo: '/home'
});
});

Давайте теперь протестируем эти маршруты. В первую очередь, давайте получим ссылку службы $route:

beforeEach(inject(function ($route) {
route = $route;
}));

Маршрут /home выше, имеет templateUrl, контроллер и конфигурированный resolve блок. Давайте напишем утверждения, чтобы протестировать их:

it('should have home route with right template, controller and a resolve block', function () {
var homeRoute = route.routes['/home'];
expect(homeRoute).toBeDefined();
expect(homeRoute.controller).toEqual('HomeController');
expect(homeRoute.templateUrl).toEqual('home.html');
expect(homeRoute.resolve.bootstrap).toBeDefined();
});

Проверьте детали, на которые может быть похож маршрут. У нас также есть маршрут по умолчанию, конфигурированный с помощью блока. Маршруты по умолчанию зарегистрированы как null в качестве ключевого значения. Ниже тест для этого:

it('should have a default route', function () {
var defaultRoute = route.routes['null'];
expect(defaultRoute).toBeDefined();
});

Тестирование resolve блоков

Resolve блоки – заводы, которые создаются, когда маршрут будет загружен и доступны контроллеру, связанного с маршрутом. Это интересный сценарий для тестирования, так как их возможности ограничены, и нам по-прежнему необходимо получить ссылку на объект.

Единственный способ проверить resolve блок состоит в применении его с помощью сервиса $ инжектора $injector. После вызова, он может быть протестирован. Следующий фрагмент проверяет resolve блок, конфигурированный домашним маршрутом, который мы создали выше:
 

it('should return data on calling the resolve block', function () {
var homeRoute = route.routes['/home'];
var bootstrapResolveBlock = homeRoute.resolve.bootstrap;
httpBackend.expectGET('home.html').respond('<div>This is the homepage!</div>');
var bootstrapSvc = injector.invoke(bootstrapResolveBlock); //[1].call(q);
bootstrapSvc.then(function (data) {
expect(data).toEqual({
prop: 'value'
});
});
rootScope.$digest();
httpBackend.flush();
});

Мне пришлось имитировать templateUrl в вышеупомянутом тесте, так как AngularJS пытается перейти на маршрут по умолчанию, когда цикл дайджест вызывается.

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

Тестирование Анимации

Методика тестирования анимации имеет некоторое сходство с тестированием директив, но тестирование анимации проще, так как анимация не так сложна, как директивы.

Библиотека angular-макетов содержит модуль ngAnimateMock, чтобы облегчить работу тестирования анимации. Этот модуль должен быть загружен перед тестированием анимации.

Рассмотрим следующую JavaScript анимацию:

angular.module('animationsApp', ['ngAnimate']).animation('.view-slide-in', function () {
return {
enter: function (element, done) {
element.css({
opacity: 0.5,
position: "relative",
top: "10px",
left: "20px"
})
.animate({
top: 0,
left: 0,
opacity: 1
}, 500, done);
},
leave: function (element, done) {
element.animate({
opacity: 0.5,
top: "10px",
left: "20px"
}, 100, done);
}
};
});

Давайте теперь напишем тесты для проверки точности этой анимации. Мы должны загрузить необходимые модули и получить ссылки на необходимые объекты.

beforeEach(function () {
module('ngAnimate', 'ngAnimateMock', 'animationsApp');
inject(function ($animate, $rootScope, $rootElement) {
$animate.enabled(true);
animate = $animate;
rootScope = $rootScope;
rootElement = $rootElement;
divElement = angular.element('<div class="view-slide-in">This is my view</div>');
rootScope.$digest();
});
});

Чтобы проверить вводную часть анимации, определенную выше, мы должны программно ввести элемнет в rootElement, на который сослались в приведенном фрагменте.

Важно помнить перед проверкой анимации, что AngularJS мешает анимации запуститься до того, пока не завершится первый цикл дайджеста. Это делается, чтобы ускорить первоначальное связывание. Последнее утверждение в приведенном фрагменте начинает первый дайджест цикл так, что мы не должны делать это в каждом тесте.

Давайте протестируем вводную анимацию, обозначенную выше. Она имеет два случая теста:

Элемент должен располагаться на 10px сверху 20px слева с непрозрачностью 0,5 при вводе
Элемент должен располагаться на 0px сверху 0px слева с непрозрачностью 1, после 1 секунды ввода. Это должно быть асинхронным тестом, так как контролю придется ждать в течение 1 секунды, прежде чем утверждать.
Ниже приведены тесты для указанных двух случаев:
 

it('element should start entering from bottom right', function () {
animate.enter(divElement, rootElement);
rootScope.$digest();
expect(divElement.css('opacity')).toEqual('0.5');
expect(divElement.css('position')).toEqual('relative');
expect(divElement.css('top')).toEqual('10px');
expect(divElement.css('left')).toEqual('20px');
});
it('element should be positioned after 1 sec', function (done) {
animate.enter(divElement, rootElement);
rootScope.$digest();
setTimeout(function () {
expect(divElement.css('opacity')).toEqual('1');
expect(divElement.css('position')).toEqual('relative');
expect(divElement.css('top')).toEqual('0px');
expect(divElement.css('left')).toEqual('0px');
done();
}, 1000);
});

Аналогично, для конечной анимации нам нужно проверить значения свойств CSS после 100 мс. Так тест должен ждать завершения анимации, мы должны сделать тест асинхронным.

it('element should leave by sliding towards bottom right for 100ms', function (done) {
rootElement.append(divElement);
animate.leave(divElement, rootElement);
rootScope.$digest();
setTimeout(function () {
expect(divElement.css('opacity')).toEqual('0.5');
expect(divElement.css('top')).toEqual('10px');
expect(divElement.css('left')).toEqual('20px');
done();
}, 105);
//5 ms delay in the above snippet is to include some time for the digest cycle
});

Вывод

В этой статье я поделился советами, которые я узнал за последние два года во время тестирования AngularJS кода. Это не конец, и вы узнаете, гораздо больше, когда будете писать тесты для реальных приложений. Я надеюсь, что вы получили достаточно информации о тестировании AngularJS.

Высоких конверсий!

31-03-2016