├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .jscsrc ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTORS.md ├── LICENSE.txt ├── README.md ├── examples ├── .bowerrc ├── bower.json └── silly │ ├── .enb │ └── make.js │ └── blocks │ ├── block-with-i18n │ ├── block-with-i18n.js │ └── block-with-i18n.spec.js │ ├── block │ ├── block.js │ ├── block.post.css │ └── block.spec.js │ └── uncovered │ └── uncovered.js ├── lib ├── assets │ └── bemjson.jst ├── hooks │ └── mocha-phantomjs.js ├── index.js ├── node-configurator.js ├── plugin.js └── runner.js └── package.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | trim_trailing_whitespace = true 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | 10 | [package.json] 11 | indent_size = 2 12 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | examples/** 2 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | env: 2 | node: true 3 | browser: true 4 | 5 | extends: pedant 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | examples/libs 3 | examples/*/.enb/tmp 4 | examples/*/__coverage* 5 | examples/*/*specs 6 | examples/*/coverage.json 7 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | { 2 | "excludeFiles": [ 3 | "node_modules", 4 | "examples", 5 | "lib/assets/bemjson.js" 6 | ], 7 | "requireSpaceAfterKeywords": ["if", "else", "for", "while", "do", "switch", "return", "try", "catch"], 8 | "requireSpaceBeforeBlockStatements": true, 9 | "requireSpacesInConditionalExpression": true, 10 | "requireSpacesInFunction": { 11 | "beforeOpeningCurlyBrace": true 12 | }, 13 | "requireSpacesInAnonymousFunctionExpression": { 14 | "beforeOpeningRoundBrace": true 15 | }, 16 | "disallowSpacesInNamedFunctionExpression": { 17 | "beforeOpeningRoundBrace": true 18 | }, 19 | "requireMultipleVarDecl": true, 20 | "requireBlocksOnNewline": 1, 21 | "disallowPaddingNewlinesInBlocks": true, 22 | "disallowSpacesInsideArrayBrackets": "nested", 23 | "disallowSpacesInsideParentheses": true, 24 | "requireSpacesInsideObjectBrackets": "all", 25 | "disallowQuotedKeysInObjects": "allButReserved", 26 | "disallowSpaceAfterObjectKeys": true, 27 | "requireCommaBeforeLineBreak": true, 28 | "requireOperatorBeforeLineBreak": true, 29 | "disallowSpaceAfterPrefixUnaryOperators": true, 30 | "disallowSpaceBeforePostfixUnaryOperators": true, 31 | "requireSpaceBeforeBinaryOperators": true, 32 | "requireSpaceAfterBinaryOperators": true, 33 | "requireCamelCaseOrUpperCaseIdentifiers": true, 34 | "disallowKeywords": ["with"], 35 | "disallowMultipleLineStrings": true, 36 | "disallowMultipleLineBreaks": true, 37 | "validateLineBreaks": "LF", 38 | "validateQuoteMarks": { 39 | "mark": "'", 40 | "escape": true 41 | }, 42 | "disallowMixedSpacesAndTabs": true, 43 | "disallowTrailingWhitespace": true, 44 | "disallowKeywordsOnNewLine": ["else", "catch"], 45 | "requireLineFeedAtFileEnd": true, 46 | "maximumLineLength": 120, 47 | "requireCapitalizedConstructors": true, 48 | "safeContextKeyword": ["_this"], 49 | "disallowYodaConditions": true, 50 | "requireSpaceAfterLineComment": true, 51 | "disallowNewlineBeforeBlockStatements": true 52 | } 53 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | node_modules 3 | examples 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "4" 5 | - "6" 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | История изменений 2 | ================= 3 | 4 | 0.11.1 5 | ------ 6 | 7 | * Исправлена ошибка генерации `doctype` при включении эскейпинга в шаблонах (@tadatuta [#67]). 8 | 9 | [#67]: https://github.com/enb/enb-bem-specs/pull/67 10 | 11 | 0.11.0 12 | ------ 13 | 14 | * Добавлена опция `schemeBuilder` (@dmkoval [#62]). 15 | 16 | [#62]: https://github.com/enb/enb-bem-specs/pull/62 17 | 18 | 0.10.0 19 | ----- 20 | 21 | ### Крупные изменения 22 | 23 | * Добавлена возможность использовать другие препроцессоры с помощью опции `cssEngine` (@tadatuta [#60]). 24 | 25 | [#60]: https://github.com/enb/enb-bem-specs/pull/60 26 | 27 | ### Зависимости 28 | 29 | * Модуль `enb-borschik@2.3.1` обновлён до версии `2.4.0`. 30 | * Модуль `enb-stylus@2.3.2` обновлён до версии `2.4.0`. 31 | * Модуль `inherit@2.2.3` обновлён до версии `2.2.6`. 32 | * Модуль `istanbul@0.4.2` обновлён до версии `0.4.5`. 33 | * Модуль `lodash@4.7.0` обновлён до версии `4.16.1`. 34 | 35 | 0.9.0 36 | ----- 37 | 38 | ### Крупные изменения 39 | 40 | * Добавлена возможность использовать другие шаблонизаторы с помощью опции `templateEngine` (@kompolom) [#51]. 41 | * Добавлена возможность подключать дополнительные скрипты для тестов с помощью опции `scripts` (@rakchaev) [#42]. 42 | * Добавлена возможность отключать `YModules` с помощью `includeYM: false` (@iniel) [#46]. 43 | * Добавлена поддержка `npm@3` (@just-boris) [#47]. 44 | 45 | ### Зависимости 46 | 47 | * Модуль `bem-naming@0.5.1` обновлён до версии `1.0.1`. 48 | * Модуль `enb-bem-i18n@1.1.0` обновлён до версии `1.1.1`. 49 | * Модуль `enb-bem-techs@2.1.0` обновлён до версии `2.2.1`. 50 | * Модуль `enb-bemxjst@2.1.0` обновлён до версии `2.2.0`. 51 | * Модуль `enb-borschik@2.1.0` обновлён до версии `2.3.1`. 52 | * Модуль `enb-stylus@2.1.0` обновлён до версии `2.3.2`. 53 | * Модуль `inherit@2.2.2` обновлён до версии `2.2.3`. 54 | * Модуль `lodash@0.3.20` обновлён до версии `0.4.2`. 55 | * Модуль `phantomjs@1.9.18` обновлён до версии `1.9.20`. 56 | * Модуль `vow@0.4.10` обновлён до версии `0.4.12`. 57 | 58 | 0.8.0 59 | ----- 60 | 61 | ### Крупные изменения 62 | 63 | * Добавлена поддержка `enb` версии `1.x` ([#44]). 64 | 65 | ### Зависимости 66 | 67 | * Модуль `enb-bem-i18n@1.0.1` обновлён до версии `1.1.0`. 68 | * Модуль `enb-bem-pseudo-levels@0.2.6` обновлён до версии `0.3.0`. 69 | * Модуль `enb-bem-techs@2.0.1` обновлён до версии `2.1.0`. 70 | * Модуль `enb-bemxjst@2.0.2` обновлён до версии `2.1.0`. 71 | * Модуль `enb-borschik@2.0.0` обновлён до версии `2.1.0`. 72 | * Модуль `enb-stylus@2.0.0` обновлён до версии `2.1.0`. 73 | 74 | 0.7.2 75 | ----- 76 | 77 | * Исправлена зависимость от модуля `ym`. 78 | 79 | 0.7.1 80 | ----- 81 | 82 | * Модуль `enb-bem-i18n@1.0.0` обновлён до версии `1.0.1`. 83 | 84 | 0.7.0 85 | ----- 86 | 87 | ### Крупные изменения 88 | 89 | * Добавлена поддержка `i18n` ([#39]). Чтобы включить в сборку `i18n` используйте опцию `langs: true`. 90 | 91 | ### Зависимости 92 | 93 | * Модуль `enb-bem-techs@1.0.4` обновлён до версии `2.0.0`. 94 | * Модуль `enb-bemxjst@1.3.4` обновлён до версии `2.0.2`. 95 | * Модуль `enb-borschik@1.5.1` обновлён до версии `2.0.0`. 96 | * Модуль `enb-stylus@1.2.3` обновлён до версии `2.0.0`. 97 | * Модуль `istanbul@0.3.17` обновлён до версии `0.3.20`. 98 | * Модуль `phantomjs@1.9.17` обновлён до версии `1.9.18`. 99 | 100 | 0.6.0 101 | ----- 102 | 103 | ### Крупные изменения 104 | 105 | * Добавлена опция `depsTech` для выбора между технологиями `deps` и `deps-old` ([#37]). 106 | 107 | ### Зависимости 108 | 109 | * Модуль `lodash@3.10.0` обновлён до версии `3.10.1`. 110 | 111 | 0.5.7 112 | ----- 113 | 114 | ### Исправления ошибок 115 | 116 | * Исправлена ошибка, из-за которой в отчёт о покрытии кода тестами попадали `spec.js` файлы ([#25]). 117 | * В отчёт о покрытии кода тестами теперь будут попадать файлы, на которые не написаны тесты ([#29]). 118 | 119 | ### Зависимости 120 | 121 | * Модуль `borschik-tech-istanbul@0.2.0` обновлён до версии `0.3.0`. 122 | * Модуль `enb-borschik@1.5.0` обновлён до версии `1.5.1`. 123 | * Модуль `istanbul@0.3.10` обновлён до версии `0.3.17`. 124 | * Модуль `lodash@3.5.0` обновлён до версии `3.10.0`. 125 | * Модуль `phantomjs@1.9.16` обновлён до версии `1.9.17`. 126 | * Модуль `vow@0.4.9` обновлён до версии `0.4.10`. 127 | 128 | 0.5.6 129 | ----- 130 | 131 | * Исправлена ошибка, из-за которой не учитывались BEMHTML-зависимости для тестов ([#22]). 132 | * Модуль `enb-bem-techs@1.0.3` обновлён до версии `1.0.4`. 133 | * Модуль `enb-stylus@1.2.2` обновлён до версии `1.2.3`. 134 | * Модуль `istanbul@0.3.6` обновлён до версии `0.3.10`. 135 | * Модуль `lodash@3.2.0` обновлён до версии `3.5.0`. 136 | * Модуль `phantomjs@1.9.15` обновлён до версии `1.9.16`. 137 | * Модуль `vow@0.4.8` обновлён до версии `0.4.9`. 138 | 139 | 0.5.5 140 | ----- 141 | 142 | * Исправлена ошибка, из-за которой тесты блока запускались в тестах его элементов и модификаторов ([#19]). 143 | * Модуль `enb-bem-techs@1.0.2` обновлён до версии `1.0.3`. 144 | * Модуль `enb-borschik@1.4.0` обновлён до версии `1.5.0`. 145 | * Модуль `enb-stylus@1.2.0` обновлён до версии `1.2.2`. 146 | * Модуль `istanbul@0.3.5` обновлён до версии `0.3.6`. 147 | * Модуль `lodash@3.0.0` обновлён до версии `3.2.0`. 148 | * Модуль `phantomjs@1.9.13` обновлён до версии `1.9.15`. 149 | 150 | 0.5.4 151 | ----- 152 | 153 | * Исправлена обработка ошибок, возникающих при выполнении тестов ([#14]). 154 | * Модуль `lodash@2.4.1` обновлён до версии `3.0.0`. 155 | * Модуль `bem-naming@0.5.0` обновлён до версии `0.5.1`. 156 | * Модуль `mocha-phantomjs@3.5.2` обновлён до версии `3.5.3`. 157 | * Модуль `phantomjs@1.9.12` обновлён до версии `1.9.13`. 158 | * Модуль `enb-bem-techs@1.0.0` обновлён до версии `1.0.2`. 159 | * Модуль `enb-bemxjst@1.3.3` обновлён до версии `1.3.4`. 160 | * Модуль `vow@0.4.7` обновлён до версии `0.4.8`. 161 | 162 | 0.5.3 163 | ----- 164 | 165 | * Исправлены ошибки при использовании в Windows. 166 | * Модуль `bem-naming` обновлён до версии `0.5.0`. 167 | * Модуль `enb-bem-pseudo-levels` обновлён до версии `0.2.6`. 168 | * Модуль `istanbul` обновлён до версии `0.3.5`. 169 | * Модуль `mocha-phantomjs` обновлён до версии `3.5.2`. 170 | 171 | 0.5.2 172 | ----- 173 | 174 | * Модуль `enb-bem-techs` обновлён до версии `1.0.0`. 175 | * Модуль `enb-stylus` обновлён до версии `1.2.0`. 176 | 177 | 0.5.1 178 | ----- 179 | 180 | * Тесты не будут запускаться в `pre` режиме magic-нод. 181 | * Модуль `bem-naming` обновлён до версии `0.4.0`. 182 | * Модуль `enb-bemxjst` обновлён до версии `1.3.3`. 183 | * Модуль `enb-borschik` обновлён до версии `1.4.0`. 184 | * Модуль `phantomjs` обновлён до версии `1.9.12`. 185 | * Модуль `vow` обновлён до версии `0.4.7`. 186 | 187 | 0.5.0 188 | ----- 189 | 190 | * Переход на `enb-magic-factory@0.3.x`. 191 | * Модуль `vow` обновлён до версии `0.4.6`. 192 | * Модуль `phantomjs` обновлён до версии `1.9.11`. 193 | * Модуль `mocha-phantomjs` обновлён до версии `3.5.1`. 194 | * Модуль `enb-stylus` обновлён до версии `1.1.5`. 195 | * Модуль `enb-bem-techs` обновлён до версии `0.1.0-rc`. 196 | 197 | [#51]: https://github.com/enb/enb-bem-specs/pull/51 198 | [#47]: https://github.com/enb/enb-bem-specs/pull/47 199 | [#44]: https://github.com/enb/enb-bem-specs/pull/44 200 | [#42]: https://github.com/enb/enb-bem-specs/pull/42 201 | [#39]: https://github.com/enb/enb-bem-specs/issues/39 202 | [#37]: https://github.com/enb/enb-bem-specs/issues/37 203 | [#29]: https://github.com/enb/enb-bem-specs/pull/29 204 | [#25]: https://github.com/enb/enb-bem-specs/issues/25 205 | [#22]: https://github.com/enb/enb-bem-specs/issues/22 206 | [#19]: https://github.com/enb/enb-bem-specs/issues/19 207 | [#14]: https://github.com/enb/enb-bem-specs/issues/14 208 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | Контрибьюторам 2 | ============== 3 | 4 | Этот документ описывает некоторые положения о совместной работе над проектом `enb-bem-specs`. 5 | 6 | Мейнтейнер проекта — [Андрей Абрамов (@blond)](https://github.com/blond). 7 | 8 | Проект развивается с помощью сообщества. Мейнтейнер мерджит пулл-реквесты, исправляет особо критичные баги. 9 | 10 | Контрибьютеры 11 | ------------- 12 | 13 | Список контрибьютеров данного проекта доступен по ссылке https://github.com/enb/enb-bem-specs/graphs/contributors. 14 | 15 | Вы так же можете получить его с помощью команды `git log --pretty=format:"%an <%ae>" | sort -u`. 16 | 17 | Пулл-реквесты 18 | ------------- 19 | 20 | Если вы что-то исправили или добавили в пакет `enb-bem-specs`, вы можете отправить пулл-реквест. Он будет рассмотрен мейнтейнером и, либо принят, либо прокомментирован для дальнейшей доработки, либо отклонен. 21 | 22 | Ошибки 23 | ------ 24 | 25 | Если вы нашли ошибку, недоделку, опечатку и вообще любой дефект в `enb-bem-specs`, пожалуйста, сообщите об этом, используя github-issues. 26 | И да. Чем подробнее вы опишите ошибку, тем быстрее её смогут воспроизвести и исправить, и тем больше ментейнеры будут вам признательны :) 27 | К сожалению, неизбежны ситуации, когда ошибка будет воспроизводиться только на вашем проекте или в вашем серверном окружении, и ментейнер не сможет не только исправить, а даже воспроизвести её. В таких случаях, мы будем верить, вы сами сможете найти нужное исправление и со временем предложить ментейнеру фикс. 28 | 29 | Фичи 30 | ---- 31 | 32 | Если вы придумали новую фичу, весьма вероятно, что её реализацию вам придётся писать самому. Но вы не постесняйтесь, спросите у ментейнеров имена разработчиков, которые хорошо разбираются в `enb-bem-specs` — с ними можно скооперироваться, чтобы реализовать вашу задумку. 33 | Если вы не можете её реализовать сами и не смогли заинтересовать других крутых разработчиков, но фича невероятно крутая — заведите таск в Issues, что-нибудь придумаем. 34 | В крайнем случае объясним вам, что хотеть эту фичу вам не нужно. Или что с ней нужно повременить. Но это в крайнем случае :) 35 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | © YANDEX LLC, 2014 2 | 3 | The Source Code called `enb-bem-specs` available at https://github.com/enb/enb-bem-specs is subject to the terms of the Mozilla Public License, v. 2.0 (hereinafter - MPL). The text of MPL is the following: 4 | 5 | Mozilla Public License, version 2.0 6 | 7 | 1. Definitions 8 | 9 | 1.1. "Contributor" 10 | 11 | means each individual or legal entity that creates, contributes to the 12 | creation of, or owns Covered Software. 13 | 14 | 1.2. "Contributor Version" 15 | 16 | means the combination of the Contributions of others (if any) used by a 17 | Contributor and that particular Contributor's Contribution. 18 | 19 | 1.3. "Contribution" 20 | 21 | means Covered Software of a particular Contributor. 22 | 23 | 1.4. "Covered Software" 24 | 25 | means Source Code Form to which the initial Contributor has attached the 26 | notice in Exhibit A, the Executable Form of such Source Code Form, and 27 | Modifications of such Source Code Form, in each case including portions 28 | thereof. 29 | 30 | 1.5. "Incompatible With Secondary Licenses" 31 | means 32 | 33 | a. that the initial Contributor has attached the notice described in 34 | Exhibit B to the Covered Software; or 35 | 36 | b. that the Covered Software was made available under the terms of 37 | version 1.1 or earlier of the License, but not also under the terms of 38 | a Secondary License. 39 | 40 | 1.6. "Executable Form" 41 | 42 | means any form of the work other than Source Code Form. 43 | 44 | 1.7. "Larger Work" 45 | 46 | means a work that combines Covered Software with other material, in a 47 | separate file or files, that is not Covered Software. 48 | 49 | 1.8. "License" 50 | 51 | means this document. 52 | 53 | 1.9. "Licensable" 54 | 55 | means having the right to grant, to the maximum extent possible, whether 56 | at the time of the initial grant or subsequently, any and all of the 57 | rights conveyed by this License. 58 | 59 | 1.10. "Modifications" 60 | 61 | means any of the following: 62 | 63 | a. any file in Source Code Form that results from an addition to, 64 | deletion from, or modification of the contents of Covered Software; or 65 | 66 | b. any new file in Source Code Form that contains any Covered Software. 67 | 68 | 1.11. "Patent Claims" of a Contributor 69 | 70 | means any patent claim(s), including without limitation, method, 71 | process, and apparatus claims, in any patent Licensable by such 72 | Contributor that would be infringed, but for the grant of the License, 73 | by the making, using, selling, offering for sale, having made, import, 74 | or transfer of either its Contributions or its Contributor Version. 75 | 76 | 1.12. "Secondary License" 77 | 78 | means either the GNU General Public License, Version 2.0, the GNU Lesser 79 | General Public License, Version 2.1, the GNU Affero General Public 80 | License, Version 3.0, or any later versions of those licenses. 81 | 82 | 1.13. "Source Code Form" 83 | 84 | means the form of the work preferred for making modifications. 85 | 86 | 1.14. "You" (or "Your") 87 | 88 | means an individual or a legal entity exercising rights under this 89 | License. For legal entities, "You" includes any entity that controls, is 90 | controlled by, or is under common control with You. For purposes of this 91 | definition, "control" means (a) the power, direct or indirect, to cause 92 | the direction or management of such entity, whether by contract or 93 | otherwise, or (b) ownership of more than fifty percent (50%) of the 94 | outstanding shares or beneficial ownership of such entity. 95 | 96 | 97 | 2. License Grants and Conditions 98 | 99 | 2.1. Grants 100 | 101 | Each Contributor hereby grants You a world-wide, royalty-free, 102 | non-exclusive license: 103 | 104 | a. under intellectual property rights (other than patent or trademark) 105 | Licensable by such Contributor to use, reproduce, make available, 106 | modify, display, perform, distribute, and otherwise exploit its 107 | Contributions, either on an unmodified basis, with Modifications, or 108 | as part of a Larger Work; and 109 | 110 | b. under Patent Claims of such Contributor to make, use, sell, offer for 111 | sale, have made, import, and otherwise transfer either its 112 | Contributions or its Contributor Version. 113 | 114 | 2.2. Effective Date 115 | 116 | The licenses granted in Section 2.1 with respect to any Contribution 117 | become effective for each Contribution on the date the Contributor first 118 | distributes such Contribution. 119 | 120 | 2.3. Limitations on Grant Scope 121 | 122 | The licenses granted in this Section 2 are the only rights granted under 123 | this License. No additional rights or licenses will be implied from the 124 | distribution or licensing of Covered Software under this License. 125 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 126 | Contributor: 127 | 128 | a. for any code that a Contributor has removed from Covered Software; or 129 | 130 | b. for infringements caused by: (i) Your and any other third party's 131 | modifications of Covered Software, or (ii) the combination of its 132 | Contributions with other software (except as part of its Contributor 133 | Version); or 134 | 135 | c. under Patent Claims infringed by Covered Software in the absence of 136 | its Contributions. 137 | 138 | This License does not grant any rights in the trademarks, service marks, 139 | or logos of any Contributor (except as may be necessary to comply with 140 | the notice requirements in Section 3.4). 141 | 142 | 2.4. Subsequent Licenses 143 | 144 | No Contributor makes additional grants as a result of Your choice to 145 | distribute the Covered Software under a subsequent version of this 146 | License (see Section 10.2) or under the terms of a Secondary License (if 147 | permitted under the terms of Section 3.3). 148 | 149 | 2.5. Representation 150 | 151 | Each Contributor represents that the Contributor believes its 152 | Contributions are its original creation(s) or it has sufficient rights to 153 | grant the rights to its Contributions conveyed by this License. 154 | 155 | 2.6. Fair Use 156 | 157 | This License is not intended to limit any rights You have under 158 | applicable copyright doctrines of fair use, fair dealing, or other 159 | equivalents. 160 | 161 | 2.7. Conditions 162 | 163 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 164 | Section 2.1. 165 | 166 | 167 | 3. Responsibilities 168 | 169 | 3.1. Distribution of Source Form 170 | 171 | All distribution of Covered Software in Source Code Form, including any 172 | Modifications that You create or to which You contribute, must be under 173 | the terms of this License. You must inform recipients that the Source 174 | Code Form of the Covered Software is governed by the terms of this 175 | License, and how they can obtain a copy of this License. You may not 176 | attempt to alter or restrict the recipients' rights in the Source Code 177 | Form. 178 | 179 | 3.2. Distribution of Executable Form 180 | 181 | If You distribute Covered Software in Executable Form then: 182 | 183 | a. such Covered Software must also be made available in Source Code Form, 184 | as described in Section 3.1, and You must inform recipients of the 185 | Executable Form how they can obtain a copy of such Source Code Form by 186 | reasonable means in a timely manner, at a charge no more than the cost 187 | of distribution to the recipient; and 188 | 189 | b. You may distribute such Executable Form under the terms of this 190 | License, or sublicense it under different terms, provided that the 191 | license for the Executable Form does not attempt to limit or alter the 192 | recipients' rights in the Source Code Form under this License. 193 | 194 | 3.3. Distribution of a Larger Work 195 | 196 | You may create and distribute a Larger Work under terms of Your choice, 197 | provided that You also comply with the requirements of this License for 198 | the Covered Software. If the Larger Work is a combination of Covered 199 | Software with a work governed by one or more Secondary Licenses, and the 200 | Covered Software is not Incompatible With Secondary Licenses, this 201 | License permits You to additionally distribute such Covered Software 202 | under the terms of such Secondary License(s), so that the recipient of 203 | the Larger Work may, at their option, further distribute the Covered 204 | Software under the terms of either this License or such Secondary 205 | License(s). 206 | 207 | 3.4. Notices 208 | 209 | You may not remove or alter the substance of any license notices 210 | (including copyright notices, patent notices, disclaimers of warranty, or 211 | limitations of liability) contained within the Source Code Form of the 212 | Covered Software, except that You may alter any license notices to the 213 | extent required to remedy known factual inaccuracies. 214 | 215 | 3.5. Application of Additional Terms 216 | 217 | You may choose to offer, and to charge a fee for, warranty, support, 218 | indemnity or liability obligations to one or more recipients of Covered 219 | Software. However, You may do so only on Your own behalf, and not on 220 | behalf of any Contributor. You must make it absolutely clear that any 221 | such warranty, support, indemnity, or liability obligation is offered by 222 | You alone, and You hereby agree to indemnify every Contributor for any 223 | liability incurred by such Contributor as a result of warranty, support, 224 | indemnity or liability terms You offer. You may include additional 225 | disclaimers of warranty and limitations of liability specific to any 226 | jurisdiction. 227 | 228 | 4. Inability to Comply Due to Statute or Regulation 229 | 230 | If it is impossible for You to comply with any of the terms of this License 231 | with respect to some or all of the Covered Software due to statute, 232 | judicial order, or regulation then You must: (a) comply with the terms of 233 | this License to the maximum extent possible; and (b) describe the 234 | limitations and the code they affect. Such description must be placed in a 235 | text file included with all distributions of the Covered Software under 236 | this License. Except to the extent prohibited by statute or regulation, 237 | such description must be sufficiently detailed for a recipient of ordinary 238 | skill to be able to understand it. 239 | 240 | 5. Termination 241 | 242 | 5.1. The rights granted under this License will terminate automatically if You 243 | fail to comply with any of its terms. However, if You become compliant, 244 | then the rights granted under this License from a particular Contributor 245 | are reinstated (a) provisionally, unless and until such Contributor 246 | explicitly and finally terminates Your grants, and (b) on an ongoing 247 | basis, if such Contributor fails to notify You of the non-compliance by 248 | some reasonable means prior to 60 days after You have come back into 249 | compliance. Moreover, Your grants from a particular Contributor are 250 | reinstated on an ongoing basis if such Contributor notifies You of the 251 | non-compliance by some reasonable means, this is the first time You have 252 | received notice of non-compliance with this License from such 253 | Contributor, and You become compliant prior to 30 days after Your receipt 254 | of the notice. 255 | 256 | 5.2. If You initiate litigation against any entity by asserting a patent 257 | infringement claim (excluding declaratory judgment actions, 258 | counter-claims, and cross-claims) alleging that a Contributor Version 259 | directly or indirectly infringes any patent, then the rights granted to 260 | You by any and all Contributors for the Covered Software under Section 261 | 2.1 of this License shall terminate. 262 | 263 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 264 | license agreements (excluding distributors and resellers) which have been 265 | validly granted by You or Your distributors under this License prior to 266 | termination shall survive termination. 267 | 268 | 6. Disclaimer of Warranty 269 | 270 | Covered Software is provided under this License on an "as is" basis, 271 | without warranty of any kind, either expressed, implied, or statutory, 272 | including, without limitation, warranties that the Covered Software is free 273 | of defects, merchantable, fit for a particular purpose or non-infringing. 274 | The entire risk as to the quality and performance of the Covered Software 275 | is with You. Should any Covered Software prove defective in any respect, 276 | You (not any Contributor) assume the cost of any necessary servicing, 277 | repair, or correction. This disclaimer of warranty constitutes an essential 278 | part of this License. No use of any Covered Software is authorized under 279 | this License except under this disclaimer. 280 | 281 | 7. Limitation of Liability 282 | 283 | Under no circumstances and under no legal theory, whether tort (including 284 | negligence), contract, or otherwise, shall any Contributor, or anyone who 285 | distributes Covered Software as permitted above, be liable to You for any 286 | direct, indirect, special, incidental, or consequential damages of any 287 | character including, without limitation, damages for lost profits, loss of 288 | goodwill, work stoppage, computer failure or malfunction, or any and all 289 | other commercial damages or losses, even if such party shall have been 290 | informed of the possibility of such damages. This limitation of liability 291 | shall not apply to liability for death or personal injury resulting from 292 | such party's negligence to the extent applicable law prohibits such 293 | limitation. Some jurisdictions do not allow the exclusion or limitation of 294 | incidental or consequential damages, so this exclusion and limitation may 295 | not apply to You. 296 | 297 | 8. Litigation 298 | 299 | Any litigation relating to this License may be brought only in the courts 300 | of a jurisdiction where the defendant maintains its principal place of 301 | business and such litigation shall be governed by laws of that 302 | jurisdiction, without reference to its conflict-of-law provisions. Nothing 303 | in this Section shall prevent a party's ability to bring cross-claims or 304 | counter-claims. 305 | 306 | 9. Miscellaneous 307 | 308 | This License represents the complete agreement concerning the subject 309 | matter hereof. If any provision of this License is held to be 310 | unenforceable, such provision shall be reformed only to the extent 311 | necessary to make it enforceable. Any law or regulation which provides that 312 | the language of a contract shall be construed against the drafter shall not 313 | be used to construe this License against a Contributor. 314 | 315 | 316 | 10. Versions of the License 317 | 318 | 10.1. New Versions 319 | 320 | Mozilla Foundation is the license steward. Except as provided in Section 321 | 10.3, no one other than the license steward has the right to modify or 322 | publish new versions of this License. Each version will be given a 323 | distinguishing version number. 324 | 325 | 10.2. Effect of New Versions 326 | 327 | You may distribute the Covered Software under the terms of the version 328 | of the License under which You originally received the Covered Software, 329 | or under the terms of any subsequent version published by the license 330 | steward. 331 | 332 | 10.3. Modified Versions 333 | 334 | If you create software not governed by this License, and you want to 335 | create a new license for such software, you may create and use a 336 | modified version of this License if you rename the license and remove 337 | any references to the name of the license steward (except to note that 338 | such modified license differs from this License). 339 | 340 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 341 | Licenses If You choose to distribute Source Code Form that is 342 | Incompatible With Secondary Licenses under the terms of this version of 343 | the License, the notice described in Exhibit B of this License must be 344 | attached. 345 | 346 | Exhibit A - Source Code Form License Notice 347 | 348 | This Source Code Form is subject to the 349 | terms of the Mozilla Public License, v. 350 | 2.0. If a copy of the MPL was not 351 | distributed with this file, You can 352 | obtain one at 353 | http://mozilla.org/MPL/2.0/. 354 | 355 | If it is not possible or desirable to put the notice in a particular file, 356 | then You may include the notice in a location (such as a LICENSE file in a 357 | relevant directory) where a recipient would be likely to look for such a 358 | notice. 359 | 360 | You may add additional accurate notices of copyright ownership. 361 | 362 | Exhibit B - "Incompatible With Secondary Licenses" Notice 363 | 364 | This Source Code Form is "Incompatible 365 | With Secondary Licenses", as defined by 366 | the Mozilla Public License, v. 2.0. 367 | 368 | 369 | A copy of the MPL is also available at http://mozilla.org/MPL/2.0/. 370 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | enb-bem-specs 2 | ============= 3 | 4 | [![NPM version](https://img.shields.io/npm/v/enb-bem-specs.svg?style=flat)](http://npmjs.org/package/enb-bem-specs) [![Build Status](https://img.shields.io/travis/enb/enb-bem-specs/master.svg?style=flat)](https://travis-ci.org/enb/enb-bem-specs) [![Dependency Status](https://img.shields.io/david/enb/enb-bem-specs.svg?style=flat)](https://david-dm.org/enb/enb-bem-specs) 5 | 6 | Инструмент для сборки и запуска тестов (спеков) на клиентский JavaScript. В процессе сборки генерируются сеты из тестов БЭМ-блоков с помощью [ENB](http://enb-make.info/). 7 | 8 | Установка 9 | ---------- 10 | 11 | ```sh 12 | $ npm install --save-dev enb-bem-specs 13 | ``` 14 | 15 | Для работы модуля требуется зависимость от пакетов `enb-magic-factory` версии `0.3.x` или выше, а так же `enb` версии `0.13.0` или выше. 16 | 17 | Технология `spec.js` в файловой системе 18 | ---------------------------------------- 19 | 20 | У каждой БЭМ-сущности может быть свой набор тестов, которые будут выполняться независимо от тестов остальных БЭМ-сущностей. 21 | 22 | ```sh 23 | $ tree -a .blocks// 24 | 25 | / 26 | └── spec.js 27 | ``` 28 | 29 | В результате сборки будет построен сет из обычных бандлов (`nested`-уровень), каждый из которых представляет собой: 30 | 31 | ```sh 32 | $ tree -a .specs 33 | 34 | .specs 35 | └── / # Бандл для БЭМ-сущности. 36 | ├── .js # Клиентский JavaScript, 37 | # необходимый для выполнения тестов. 38 | ├── .css # Стили, необходимые для выполнения тестов. 39 | ├── .spec.js # Код тестов. 40 | └── .html # HTML, необходимый для выполнения тестов. 41 | # Включает в себя js, css и spec.js таргеты. 42 | ``` 43 | 44 | Frameworks 45 | ---------- 46 | 47 | * [mocha](https://github.com/visionmedia/mocha) 48 | * [should](https://github.com/shouldjs/should.js) 49 | 50 | Как написать тест? 51 | ------------------ 52 | 53 | Тесты пишутся в BDD-стиле с использованием асинхронной модульной системы [YModules](https://ru.bem.info/tools/bem/modules/). 54 | Чтобы добавить тест для БЭМ-сущности, нужно в её директории на требуемом уровне переопределения создать файл с названием `.spec.js`. 55 | 56 | Пример: 57 | 58 | ```js 59 | modules.define( 60 | 'spec', 61 | ['button', 'i-bem__dom', 'jquery', 'BEMHTML'], 62 | function(provide, Button, BEMDOM, $, BEMHTML) { 63 | 64 | describe('button', function() { 65 | var button; 66 | 67 | beforeEach(function() { 68 | button = BEMDOM.init($(BEMHTML.apply({ block: 'button', text: 'foo' })).appendTo('body')) 69 | .bem('button'); 70 | }); 71 | 72 | afterEach(function() { 73 | BEMDOM.destruct(button.domElem); 74 | }); 75 | 76 | it('should be focused on pressrelease on itself', function() { 77 | button.hasMod('focused').should.be.false; 78 | button.domElem 79 | .trigger('pointerpress') 80 | .trigger('pointerrelease'); 81 | button.hasMod('focused').should.be.true; 82 | }); 83 | }); 84 | 85 | provide(); 86 | 87 | }); 88 | ``` 89 | 90 | Запуск спеков 91 | ------------- 92 | 93 | После сборки сетов произойдёт запуск тестов для указанных БЭМ-сущностей. 94 | 95 | Собранные HTML-файлы для каждой БЭМ-сущности содержат в себе необходимый код стилей и JavaScript, а так же код тестов. Эти HTML-файлы передаются в [PhantomJS](https://github.com/ariya/phantomjs). 96 | 97 | ![2014-09-21 23 40 20](https://cloud.githubusercontent.com/assets/2225579/4349827/76e6ade2-41c7-11e4-8d1b-8d1faea381ad.png) 98 | 99 | Покрытие кода 100 | ------------- 101 | 102 | Если при запуске переменная окружения `ISTANBUL_COVERAGE` будет равна значению `yes`, 103 | то после выполнения тестов в корне появится файл `coverage.json` с информацией о покрытии исходного JavaScript-кода тестами. 104 | 105 | С помощью команды `report` инструмента [istanbul](https://github.com/gotwarlost/istanbul) 106 | можно составить `HTML`-отчёт на основе файла `coverage.json`: 107 | 108 | ```sh 109 | $ istanbul report coverage.json 110 | ``` 111 | 112 | ![2014-09-21 23 51 31](https://cloud.githubusercontent.com/assets/2225579/4352776/5020f592-422a-11e4-8770-8515ab046a35.png) 113 | 114 | Как использовать? 115 | ----------------- 116 | 117 | В `make`-файле (`.enb/make.js`) нужно подключить модуль `enb-bem-specs`. 118 | С помощью этого модуля следует создать конфигуратор, указав название таска, в рамках которого будет происходить сборка уровней сетов из тестов. 119 | 120 | Конфигуратор имеет единственный метод `configure`. Его можно вызывать несколько раз, чтобы задекларировать сборку нескольких сетов. 121 | 122 | ```js 123 | module.exports = function (config) { 124 | config.includeConfig('enb-bem-specs'); // Подключаем модуль `enb-bem-specs`. 125 | 126 | var examples = config.module('enb-bem-specs') // Создаём конфигуратор сетов 127 | .createConfigurator('specs'); // в рамках `specs`-таска. 128 | 129 | examples.configure({ 130 | destPath: 'desktop.specs', 131 | levels: ['blocks'], 132 | sourceLevels: [ 133 | { path: '../libs/bem-core/common.blocks', check: false }, 134 | { path: '../libs/bem-pr/spec.blocks', check: false }, 135 | 'blocks' 136 | ] 137 | }); 138 | }; 139 | ``` 140 | 141 | ### Опции 142 | 143 | * *String* `destPath` — путь относительно корня до нового сета с тестами, которые нужно собрать. Обязательная опция. 144 | * *String[] | Object[]* `levels` — уровни, в которых следует искать тесты. Обязательная опция. 145 | * *String[] | Object[]* `sourceLevels` — уровни, в которых следует искать JavaScript-код, необходимый для запуска тестов. 146 | * *String[]* `jsSuffixes` — суффиксы `js`-файлов БЭМ-сущностей. По умолчанию — `['js']`. 147 | * *String[]* `specSuffixes` — суффиксы `spec.js`-файлов БЭМ-сущностей. По умолчанию — `['spec.js']`. 148 | * *String|Function* `depsTech` — технология для раскрытия зависимостей. По умолчанию — `deps-old`. 149 | * *Boolean* `langs` — включает в сборку `i18n`. 150 | * *String[]* `scripts` — дополнительные js-скрипты, которые необходимо подключить на тестируемую страницу. Например: 151 | ```js 152 | scripts: ['https://yastatic.net/jquery/1.8.3/jquery.min.js', 153 | 'https://yastatic.net/lodash/2.4.1/lodash.min.js'], 154 | ``` 155 | * *Boolean* `includeYM` — добавляет в собираемый файл код модульной системы `ym`. По умолчанию — `true`. 156 | * *Function* `schemeBuilder` — схема именования. Требуется только для не-БЭМ схем именований. 157 | 158 | Запуск из консоли 159 | ----------------- 160 | 161 | В `make`-файле декларируется таск, в котором будет выполняться сборка сетов из тестов. 162 | 163 | В ENB запуск таска осуществляется с помощью команды `make`, которой передаётся имя таска: 164 | 165 | ```sh 166 | $ ./node_modules/.bin/enb make 167 | ``` 168 | 169 | ### Сборка и запуск всех тестов 170 | 171 | Если сборка сетов из тестов была задекларирована в `specs`-таске: 172 | 173 | ```sh 174 | $ ./node_modules/.bin/enb make specs 175 | ``` 176 | 177 | ### Сборка всех тестов для указанной БЭМ-сущности 178 | 179 | Чтобы собрать тесты БЭМ-сущности `block__elem` для сета `desktop.specs`: 180 | 181 | ```sh 182 | $ ./node_modules/.bin/enb make specs desktop.specs/block__elem 183 | ``` 184 | 185 | Лицензия 186 | -------- 187 | 188 | © 2014 YANDEX LLC. Код лицензирован [Mozilla Public License 2.0](LICENSE.txt). 189 | -------------------------------------------------------------------------------- /examples/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "libs" 3 | } 4 | -------------------------------------------------------------------------------- /examples/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enb-bem-specs-examples", 3 | "private": true, 4 | "dependencies": { 5 | "bem-core": "2.9.0", 6 | "bem-pr": "0.12.0" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/silly/.enb/make.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var rootPath = path.join(__dirname, '..', '..', '..'); 3 | 4 | module.exports = function (config) { 5 | config.includeConfig(rootPath); 6 | 7 | var specs = config.module('enb-bem-specs').createConfigurator('specs'); 8 | 9 | specs.configure({ 10 | langs: true, 11 | destPath: 'set.specs', 12 | levels: ['blocks'], 13 | sourceLevels: [ 14 | { path: '../libs/bem-core/common.blocks', check: false }, 15 | { path: '../libs/bem-pr/spec.blocks', check: false }, 16 | { path: 'blocks', check: true } 17 | ] 18 | }); 19 | 20 | var specsBH = config.module('enb-bem-specs').createConfigurator('specs-bh'); 21 | 22 | specsBH.configure({ 23 | langs: true, 24 | destPath: 'bh-set.specs', 25 | levels: ['blocks'], 26 | templateEngine: { 27 | templateTech: require('enb-bh/techs/bh-bundle'), 28 | templateOptions: { 29 | mimic: 'BEMHTML', 30 | bhOptions: { 31 | jsAttrName: 'data-bem', 32 | jsAttrScheme: 'json' 33 | } 34 | }, 35 | htmlTech: require('enb-bh/techs/bemjson-to-html'), 36 | htmlTechOptionNames: { bemjsonFile: 'bemjsonFile', templateFile: 'bhFile' }, 37 | }, 38 | sourceLevels: [ 39 | { path: '../libs/bem-core/common.blocks', check: false }, 40 | { path: '../libs/bem-pr/spec.blocks', check: false }, 41 | { path: 'blocks', check: true } 42 | ] 43 | }); 44 | 45 | var specsPostCSS = config.module('enb-bem-specs').createConfigurator('specs-postcss'); 46 | 47 | specsPostCSS.configure({ 48 | langs: true, 49 | destPath: 'postcss-set.specs', 50 | levels: ['blocks'], 51 | cssEngine: { 52 | tech: require('enb-postcss/techs/enb-postcss'), 53 | options: { 54 | plugins: [ 55 | require('postcss-import')() 56 | ] 57 | } 58 | }, 59 | sourceLevels: [ 60 | { path: '../libs/bem-core/common.blocks', check: false }, 61 | { path: '../libs/bem-pr/spec.blocks', check: false }, 62 | { path: 'blocks', check: true } 63 | ] 64 | }); 65 | }; 66 | -------------------------------------------------------------------------------- /examples/silly/blocks/block-with-i18n/block-with-i18n.js: -------------------------------------------------------------------------------- 1 | modules.define('block-with-i18n', ['i18n'], function(provide, i18n) { 2 | 3 | provide({ 4 | foo: function () { 5 | return i18n('block-with-i18n','bar'); 6 | } 7 | }); 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /examples/silly/blocks/block-with-i18n/block-with-i18n.spec.js: -------------------------------------------------------------------------------- 1 | modules.define('spec', ['block-with-i18n'], function(provide, block) { 2 | 3 | it('should be equal to one', function() { 4 | block.foo().should.eql('block-with-i18n:bar'); 5 | }); 6 | 7 | provide(); 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /examples/silly/blocks/block/block.js: -------------------------------------------------------------------------------- 1 | modules.define('block', function(provide) { 2 | 3 | provide({ 4 | foo: function () { 5 | return 'bar'; 6 | } 7 | }); 8 | 9 | }); 10 | -------------------------------------------------------------------------------- /examples/silly/blocks/block/block.post.css: -------------------------------------------------------------------------------- 1 | .block { 2 | color: red; 3 | } 4 | -------------------------------------------------------------------------------- /examples/silly/blocks/block/block.spec.js: -------------------------------------------------------------------------------- 1 | modules.define('spec', function(provide) { 2 | 3 | describe('block-1', function() { 4 | it('should be equal to one', function() { 5 | (1*1).should.to.equal(1); 6 | }); 7 | }); 8 | 9 | provide(); 10 | 11 | }); 12 | -------------------------------------------------------------------------------- /examples/silly/blocks/uncovered/uncovered.js: -------------------------------------------------------------------------------- 1 | modules.define('uncovered', function(provide) { 2 | 3 | console.log('this module is not covered by specs'); 4 | 5 | provide(); 6 | }); 7 | -------------------------------------------------------------------------------- /lib/assets/bemjson.jst: -------------------------------------------------------------------------------- 1 | ([ 2 | { html: '', tag: false }, 3 | { 4 | tag: 'html', 5 | content: [ 6 | { 7 | tag: 'head', 8 | content: [ 9 | { 10 | tag: 'meta', 11 | attrs: { charset: 'utf-8' } 12 | }, 13 | { 14 | tag: 'link', 15 | attrs: { 16 | href: '<%= name %>.css', rel: 'stylesheet' 17 | } 18 | }, 19 | <% _.forEach(scripts, function(script) { %> 20 | { 21 | tag: 'script', 22 | attrs: { src: '<%= script %>' } 23 | }, 24 | <% }); %> 25 | { 26 | tag: 'script', 27 | attrs: { src: '<%= name %>.spec.js' } 28 | } 29 | ] 30 | }, 31 | { 32 | tag: 'body', 33 | content: { 34 | block: 'spec' 35 | } 36 | }, 37 | { 38 | block: 'spec-runner' 39 | } 40 | ] 41 | }]); 42 | -------------------------------------------------------------------------------- /lib/hooks/mocha-phantomjs.js: -------------------------------------------------------------------------------- 1 | /** 2 | * An "after run" hook for [mocha-phantomjs](https://github.com/metaskills/mocha-phantomjs) 3 | * to store instrumented code coverage results to temporary buffer, 4 | * so they could be consumed into Istanbul#Collector for future manipulations. 5 | 6 | * @example 7 | * $ mocha-phantomjs \ 8 | * --hooks \ 9 | * --setting coverage-file= \ 10 | * 11 | */ 12 | var fs = require('fs'), 13 | sys = require('system'), 14 | args = sys.args; 15 | 16 | module.exports = { 17 | afterEnd: function (reporter) { 18 | var covData = reporter.page.evaluate(function () { 19 | return window.__coverage__; 20 | }); 21 | 22 | if (!covData || args.length < 3) { return; } 23 | 24 | args = JSON.parse(args[3]); 25 | 26 | var covBufferPath = args.settings['coverage-file']; 27 | if (!covBufferPath) { return; } 28 | 29 | fs.write(covBufferPath, JSON.stringify(covData), 'w'); 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | var plugin = require('./plugin'); 2 | 3 | function SpecsSets(config) { 4 | this._config = config; 5 | 6 | config.includeConfig(require.resolve('enb-magic-factory')); 7 | } 8 | 9 | SpecsSets.prototype.createConfigurator = function (taskName) { 10 | var sets = this._config.module('enb-magic-factory'); 11 | 12 | return plugin(sets.createHelper(taskName)); 13 | }; 14 | 15 | module.exports = function (config) { 16 | config.registerModule('enb-bem-specs', new SpecsSets(config)); 17 | }; 18 | -------------------------------------------------------------------------------- /lib/node-configurator.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | fs = require('fs'), 3 | _ = require('lodash'), 4 | 5 | levels = require('enb-bem-techs/techs/levels'), 6 | files = require('enb-bem-techs/techs/files'), 7 | mergeFiles = require('enb/techs/file-merge'), 8 | 9 | provide = require('enb/techs/file-provider'), 10 | bemjsonToBemdecl = require('enb-bem-techs/techs/bemjson-to-bemdecl'), 11 | depsByTechToBemdecl = require('enb-bem-techs/techs/deps-by-tech-to-bemdecl'), 12 | mergeBemdecl = require('enb-bem-techs/techs/merge-bemdecl'), 13 | mergeDeps = require('enb-bem-techs/techs/merge-deps'), 14 | deps = require('enb-bem-techs/techs/deps'), 15 | depsOld = require('enb-bem-techs/techs/deps-old'), 16 | 17 | templateEngineDefault = { 18 | templateTech: require('enb-bemxjst/techs/bemhtml'), 19 | templateOptions: { compat: true }, 20 | htmlTech: require('enb-bemxjst/techs/bemjson-to-html'), 21 | htmlTechOptionNames: { bemjsonFile: 'bemjsonFile', templateFile: 'bemhtmlFile' } 22 | }, 23 | 24 | cssEngineDefault = { 25 | tech: require('enb-stylus/techs/stylus'), 26 | options: {} 27 | }, 28 | 29 | js = require('enb-js/techs/browser-js'), 30 | borschikJs = require('enb-borschik/techs/js-borschik-include'), 31 | 32 | keysets = require('enb-bem-i18n/techs/keysets'), 33 | i18n = require('enb-bem-i18n/techs/i18n'), 34 | 35 | borschik = require('enb-borschik/techs/borschik'), 36 | copy = require('enb/techs/file-copy'), 37 | 38 | NEED_COVERAGE = process.env.ISTANBUL_COVERAGE, 39 | borschikTechIstanbulPath = require.resolve('borschik-tech-istanbul'); 40 | 41 | exports.configure = function (config, options) { 42 | var root = config.getRootPath(), 43 | pattern = path.join(options.destPath, '*'), 44 | templateEngine = options.templateEngine || templateEngineDefault, 45 | cssEngine = options.cssEngine || cssEngineDefault, 46 | sourceLevels = [].concat(options.sourceLevels), 47 | depsTech, 48 | htmlTechOpts = {}; 49 | 50 | if (typeof options.depsTech === 'function') { 51 | depsTech = options.depsTech; 52 | } else if (options.depsTech === 'deps') { 53 | depsTech = deps; 54 | } else { 55 | depsTech = depsOld; 56 | } 57 | 58 | htmlTechOpts[templateEngine.htmlTechOptionNames.bemjsonFile] = '?.bemjson.js'; 59 | htmlTechOpts[templateEngine.htmlTechOptionNames.templateFile] = '?.browser.template.js'; 60 | 61 | // setup template engine 62 | var templateEngineOpts = _.assign({}, templateEngine.templateOptions, { 63 | target: '?.browser.template.js', 64 | filesTarget: '?.template.files' 65 | }); 66 | 67 | config.nodes(pattern, function (nodeConfig) { 68 | var sublevel = path.join(nodeConfig.getNodePath(), 'blocks'); 69 | 70 | if (fs.existsSync(sublevel)) { 71 | sourceLevels.push(sublevel); 72 | } 73 | 74 | // Base techs 75 | nodeConfig.addTechs([ 76 | [levels, { levels: sourceLevels }] 77 | ]); 78 | 79 | // Deps 80 | nodeConfig.addTechs([ 81 | [provide, { target: '?.bemjson.js' }], 82 | [provide, { target: '?.base.bemdecl.js' }], 83 | 84 | [bemjsonToBemdecl, { target: '?.bemjson.bemdecl.js' }], 85 | [depsByTechToBemdecl, { 86 | target: '?.spec-js.bemdecl.js', 87 | filesTarget: '?.base.files', 88 | sourceTech: 'spec.js', 89 | destTech: 'js' 90 | }], 91 | [mergeDeps, { 92 | target: '?.bemdecl.js', 93 | sources: ['?.base.bemdecl.js', '?.bemjson.bemdecl.js', '?.spec-js.bemdecl.js'] 94 | }], 95 | 96 | [depsTech] 97 | ]); 98 | 99 | // Files 100 | nodeConfig.addTechs([ 101 | [files, { 102 | depsFile: '?.base.bemdecl.js', 103 | depsFormat: 'bemdecl.js', 104 | filesTarget: '?.base.files', 105 | dirsTarget: '?.base.dirs' 106 | }], 107 | [files] 108 | ]); 109 | 110 | // Client BEMHTML 111 | nodeConfig.addTechs([ 112 | [depsByTechToBemdecl, { 113 | target: '?.js.template.bemdecl.js', 114 | sourceTech: 'js', 115 | destTech: 'bemhtml' 116 | }], 117 | [depsByTechToBemdecl, { 118 | target: '?.spec-js.template.bemdecl.js', 119 | sourceTech: 'spec.js', 120 | destTech: 'bemhtml' 121 | }], 122 | [mergeBemdecl, { 123 | target: '?.template.bemdecl.js', 124 | sources: [ 125 | '?.js.template.bemdecl.js', 126 | '?.spec-js.template.bemdecl.js', 127 | '?.bemjson.bemdecl.js' 128 | ] 129 | }], 130 | 131 | [depsTech, { 132 | target: '?.template.deps.js', 133 | bemdeclFile: '?.template.bemdecl.js' 134 | }], 135 | [files, { 136 | depsFile: '?.template.deps.js', 137 | filesTarget: '?.template.files', 138 | dirsTarget: '?.template.dirs' 139 | }], 140 | 141 | [templateEngine.templateTech, templateEngineOpts] 142 | ]); 143 | 144 | // HTML 145 | nodeConfig.addTechs([ 146 | [templateEngine.templateTech, templateEngine.templateOptions], 147 | [templateEngine.htmlTech, htmlTechOpts] 148 | ]); 149 | 150 | if (options.langs) { 151 | nodeConfig.addTechs([ 152 | [keysets, { target: '?.keysets.js', lang: '__common__' }], 153 | [i18n, { target: '?.i18n.js', keysetsFile: '?.keysets.js', lang: '__common__' }] 154 | ]); 155 | } 156 | 157 | // Browser JS + i18n 158 | nodeConfig.addTechs([ 159 | [borschikJs, { 160 | sourceSuffixes: options.jsSuffixes, 161 | target: '?.source.browser.js' 162 | }], 163 | [js, { 164 | target: '?.pure.ym.js', 165 | sourceSuffixes: [], 166 | filesTarget: '?.base.files', 167 | includeYM: options.includeYM 168 | }], 169 | [js, { 170 | target: '?.pure.spec.js', 171 | sourceSuffixes: ['spec.js'], 172 | filesTarget: '?.base.files' 173 | }], 174 | [mergeFiles, { 175 | target: '?.pre.spec.js', 176 | sources: ['?.pure.ym.js', '?.source.browser.js', '?.browser.template.js', '?.pure.spec.js'] 177 | .concat(options.langs ? '?.i18n.js' : []) 178 | }] 179 | ]); 180 | 181 | if (NEED_COVERAGE) { 182 | nodeConfig.addTechs([ 183 | [borschik, { 184 | source: '?.pre.spec.js', 185 | target: '?.coverage.spec.js', 186 | tech: borschikTechIstanbulPath, 187 | techOptions: { 188 | root: root, 189 | instrumentPaths: options.levels.map(function (level) { 190 | return typeof level === 'string' ? level : level.path; 191 | }) 192 | }, 193 | levels: options.levels, 194 | freeze: true, 195 | minify: false 196 | }], 197 | [copy, { 198 | source: '?.coverage.spec.js', 199 | target: '?.spec.js' 200 | }] 201 | ]); 202 | } else { 203 | nodeConfig.addTech( 204 | [borschik, { 205 | source: '?.pre.spec.js', 206 | target: '?.spec.js', 207 | freeze: true, 208 | minify: false 209 | }] 210 | ); 211 | } 212 | 213 | // CSS 214 | nodeConfig.addTech([cssEngine.tech, _.assign({}, cssEngine.options, { 215 | target: '?.css', 216 | filesTarget: '?.files' 217 | })]); 218 | 219 | nodeConfig.addTargets([ 220 | '?.spec.js', '?.css', '?.html' 221 | ]); 222 | }); 223 | }; 224 | -------------------------------------------------------------------------------- /lib/plugin.js: -------------------------------------------------------------------------------- 1 | var path = require('path'), 2 | vow = require('vow'), 3 | enb = require('enb'), 4 | vfs = enb.asyncFs || require('enb/lib/fs/async-fs'), 5 | naming = require('bem-naming'), 6 | _ = require('lodash'), 7 | scanner = require('enb-bem-pseudo-levels/lib/level-scanner'), 8 | runner = require('./runner'), 9 | configurator = require('./node-configurator'); 10 | 11 | module.exports = function (helper) { 12 | return { 13 | configure: function (options) { 14 | var root = helper.getRootPath(), 15 | dstpath = path.resolve(root, options.destPath), 16 | nodes = {}, 17 | nodesToRun = {}, 18 | sources = []; 19 | 20 | options || (options = {}); 21 | options.sourceLevels || (options.sourceLevels = options.levels); 22 | options.jsSuffixes || (options.jsSuffixes = ['js']); 23 | options.specSuffixes || (options.specSuffixes = ['spec.js']); 24 | options.levels = options.levels.map(function (levelPath) { 25 | var level = (typeof levelPath === 'string') ? { path: levelPath } : levelPath; 26 | 27 | level.path = path.resolve(root, level.path); 28 | 29 | return level; 30 | }); 31 | typeof options.includeYM !== 'undefined' || (options.includeYM = true); 32 | 33 | helper.prebuild(function (magic) { 34 | return scanner.scan(options.levels, options.schemeBuilder) 35 | .then(function (files) { 36 | files.forEach(function (file) { 37 | if (options.specSuffixes.indexOf(file.suffix) !== -1) { 38 | var bemname = file.name.split('.')[0], 39 | node = path.join(options.destPath, bemname), 40 | specTarget = path.join(node, bemname + '.spec.js'), 41 | cssTarget = path.join(node, bemname + '.css'), 42 | htmlTarget = path.join(node, bemname + '.html'); 43 | 44 | if (magic.isRequiredNode(node)) { 45 | nodes[node] = true; 46 | magic.registerNode(node); 47 | 48 | if (magic.isRequiredTarget(specTarget) && 49 | magic.isRequiredTarget(cssTarget) && 50 | magic.isRequiredTarget(htmlTarget) 51 | ) { 52 | (nodesToRun[node] = true); 53 | } 54 | } 55 | } else if (options.jsSuffixes.indexOf(file.suffix) !== -1) { 56 | sources.push(path.relative(root, file.fullname)); 57 | } 58 | }); 59 | 60 | return vow.all(Object.keys(nodes).map(function (node) { 61 | var basename = path.basename(node), 62 | dirname = path.join(dstpath, basename), 63 | bemdeclFilename = path.join(dirname, basename + '.base.bemdecl.js'), 64 | bemjsonFilename = path.join(dirname, basename + '.bemjson.js'), 65 | notation = naming.parse(basename), 66 | dep = { block: notation.block }, 67 | decl = [dep], 68 | bemdeclSource, 69 | bemjsonSource; 70 | 71 | notation.elem && (dep.elem = notation.elem); 72 | 73 | if (notation.modName) { 74 | dep.mod = notation.modName; 75 | dep.val = notation.modVal; 76 | } 77 | 78 | if (options.langs) { 79 | decl.unshift({ block: 'i18n' }); 80 | } 81 | 82 | bemdeclSource = 'exports.deps = ' + JSON.stringify(decl) + ';'; 83 | 84 | return vfs.makeDir(dirname) 85 | .then(function () { 86 | var bemjsonAssetFilename = path.join(__dirname, 'assets', 'bemjson.jst'); 87 | 88 | return vow.all([ 89 | vfs.exists(bemjsonFilename).then(function (isExists) { 90 | if (!isExists) { 91 | return vfs.read(bemjsonAssetFilename) 92 | .then(function (bemjsonAsset) { 93 | var compiled = _.template(bemjsonAsset); 94 | 95 | bemjsonSource = compiled({ 96 | name: basename, 97 | scripts: options.scripts 98 | }); 99 | 100 | return vfs.write(bemjsonFilename, bemjsonSource, 'utf-8'); 101 | }); 102 | } 103 | }), 104 | vfs.write(bemdeclFilename, bemdeclSource, 'utf-8') 105 | ]); 106 | }); 107 | })); 108 | }); 109 | }); 110 | 111 | helper.configure(function (projectConfig) { 112 | configurator.configure(projectConfig, options); 113 | }); 114 | 115 | helper._projectConfig.task(helper.getTaskName(), function () { 116 | if (helper.getMode() === 'pre') { 117 | return; 118 | } 119 | 120 | var toRun = Object.keys(nodesToRun); 121 | 122 | return toRun.length && runner.run(toRun, root, sources); 123 | }); 124 | } 125 | }; 126 | }; 127 | -------------------------------------------------------------------------------- /lib/runner.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'), 2 | path = require('path'), 3 | url = require('url'), 4 | vow = require('vow'), 5 | util = require('util'), 6 | Logger = require('enb/lib/logger'), 7 | logger = new Logger(), 8 | oldTargets = [], 9 | NEED_COVERAGE = process.env.ISTANBUL_COVERAGE; 10 | 11 | if (NEED_COVERAGE) { 12 | var istanbul = require('istanbul'), 13 | instrumenter = new (istanbul.Instrumenter)(), 14 | covCollector = new (istanbul.Collector)(), 15 | covIdx = 0; 16 | } 17 | 18 | exports.run = function (targets, root, sources) { 19 | var isWinOS = path.sep === '\\', 20 | exec = require('child_process').exec, 21 | MOCHA_PHANTOM_BIN = require.resolve('.bin/mocha-phantomjs' + (isWinOS ? '.cmd' : '')), 22 | MOCHA_PHANTOM_REPORTER = process.env.MOCHA_PHANTOM_REPORTER || 'spec', 23 | MOCHA_PHANTOM_MAX_COUNT = parseInt(process.env.MOCHA_PHANTOM_MAX_COUNT, 10) || 10, 24 | MOCHA_PHANTOM_HOOK = path.resolve(__dirname, './hooks/mocha-phantomjs.js'), 25 | phantomCount = 0, 26 | phantomQueue = [], 27 | errorCount = 0, 28 | toRun = [], 29 | needRun = false; 30 | 31 | targets = targets.filter(function (target) { 32 | return target.indexOf('.bundles') === -1; 33 | }); 34 | 35 | targets.forEach(function (target) { 36 | if (oldTargets.indexOf(target) === -1) { 37 | oldTargets.push(target); 38 | toRun.push(target); 39 | 40 | needRun = true; 41 | } 42 | }); 43 | 44 | return needRun ? vow.allResolved(toRun.map(function (nodePath) { 45 | var nodeName = path.basename(nodePath), 46 | target = nodeName + '.html', 47 | targetPath = path.join(nodePath, target), 48 | fullpath = path.join(root, nodePath, target), 49 | args = '--reporter ' + MOCHA_PHANTOM_REPORTER, 50 | fileurl = url.format({ 51 | protocol: 'file', 52 | host: '/', 53 | pathname: fullpath.replace(/\\/g, '/') 54 | }), 55 | deferer = vow.defer(), 56 | covBufFile; 57 | 58 | // On Windows OS, after the function 'url.format', the URL becomes file:////... 59 | // But it should be file:///... (3 slahes) 60 | fileurl = isWinOS ? fileurl.replace('/', '') : fileurl; 61 | 62 | if (NEED_COVERAGE) { 63 | covBufFile = path.join(root, util.format( 64 | '__coverage-%s-%s-%s.json', 65 | targetPath.replace(/[^A-Za-z0-9_. ]/g, '_'), 66 | Date.now() - 0, 67 | covIdx++)); 68 | 69 | args += ' --hooks ' + MOCHA_PHANTOM_HOOK; 70 | args += ' --setting coverage-file=' + covBufFile; 71 | } 72 | 73 | phantomCount < MOCHA_PHANTOM_MAX_COUNT ? 74 | runMochaPhantom() : 75 | phantomQueue.push(runMochaPhantom); 76 | 77 | function getCovData() { 78 | var data = {}, 79 | exists = fs.existsSync(covBufFile); 80 | 81 | if (exists) { 82 | logger.logAction('coverage', path.relative(root, covBufFile)); 83 | data = JSON.parse(fs.readFileSync(covBufFile, 'utf8')); 84 | 85 | dropCovBuffer(); 86 | } else { 87 | logger.logErrorAction('coverage', path.relative(root, covBufFile)); 88 | } 89 | 90 | return data; 91 | } 92 | 93 | function dropCovBuffer() { 94 | try { 95 | fs.unlinkSync(covBufFile); 96 | } catch (e) { 97 | /* ignore error */ 98 | } 99 | } 100 | 101 | function runMochaPhantom() { 102 | phantomCount++; 103 | 104 | exec([MOCHA_PHANTOM_BIN, args, fileurl].join(' '), function (err, stdout) { 105 | --phantomCount; 106 | phantomQueue.length && phantomQueue.shift()(); 107 | 108 | var passed = err === null; 109 | 110 | if (passed) { 111 | if (NEED_COVERAGE) { 112 | covCollector.add(getCovData()); 113 | phantomCount || storeFinalCoverage(); 114 | } 115 | 116 | logger.logAction('spec', targetPath); 117 | 118 | deferer.resolve(); 119 | } else { 120 | logger.logErrorAction('spec', targetPath); 121 | 122 | ++errorCount; 123 | deferer.reject(err); 124 | } 125 | 126 | console.log(stdout); 127 | }); 128 | } 129 | 130 | function storeFinalCoverage() { 131 | var covFile = path.resolve(root, 'coverage.json'), 132 | store = covCollector.store, 133 | transformer = instrumenter.instrumentSync.bind(instrumenter), 134 | config = istanbul.config.loadFile(), 135 | data = {}; 136 | 137 | // add all files not covered by specs with zero coverage 138 | istanbul.matcherFor({ 139 | root: root, 140 | includes: sources, 141 | excludes: store.keys().concat(config.instrumentation.excludes()) 142 | }, function (err, matcher) { 143 | matcher.files.forEach(function (file) { 144 | // As implemented here: 145 | // https://github.com/gotwarlost/istanbul/blob/master/lib/command/common/run-with-cover.js#L222 146 | var filename = path.relative(root, file); 147 | transformer(fs.readFileSync(file, 'utf-8'), filename); 148 | 149 | Object.keys(instrumenter.coverState.s).forEach(function (key) { 150 | instrumenter.coverState.s[key] = 0; 151 | }); 152 | 153 | data[filename] = instrumenter.coverState; 154 | }); 155 | 156 | covCollector.add(data); 157 | 158 | fs.writeFileSync(covFile, JSON.stringify(covCollector.getFinalCoverage()), 'utf8'); 159 | 160 | covCollector.dispose(); 161 | }); 162 | } 163 | 164 | return deferer.promise(); 165 | })) 166 | .then(function () { 167 | if (errorCount) { 168 | return vow.reject(new Error('specs: ' + errorCount + ' failing')); 169 | } 170 | }) : vow.resolve([]); 171 | }; 172 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Andrew Abramov ", 3 | "name": "enb-bem-specs", 4 | "version": "0.11.1", 5 | "description": "BEM specs for ENB", 6 | "repository": "git://github.com/enb/enb-bem-specs", 7 | "homepage": "https://github.com/enb/enb-bem-specs", 8 | "bugs": "https://github.com/enb/enb-bem-specs/issues", 9 | "licenses": [ 10 | { 11 | "type": "MPL-2.0", 12 | "url": "https://github.com/enb/enb-bem-specs/blob/master/LICENSE.txt" 13 | } 14 | ], 15 | "engines": { 16 | "node": ">= 0.10.0" 17 | }, 18 | "peerDependencies": { 19 | "enb": ">= 0.13.0 < 2.0.0", 20 | "enb-magic-factory": ">= 0.3.0 < 1.0.0" 21 | }, 22 | "dependencies": { 23 | "bem-naming": "1.0.1", 24 | "borschik-tech-istanbul": "0.3.0", 25 | "enb-bem-i18n": "1.1.1", 26 | "enb-bem-pseudo-levels": "0.4.0", 27 | "enb-bem-techs": "2.2.1", 28 | "enb-bemxjst": "2.2.0", 29 | "enb-borschik": "2.4.0", 30 | "enb-js": "1.1.0", 31 | "enb-stylus": "2.4.0", 32 | "inherit": "2.2.6", 33 | "istanbul": "0.4.5", 34 | "lodash": "4.16.1", 35 | "mocha-phantomjs": "3.5.3", 36 | "phantomjs": "1.9.20", 37 | "vow": "0.4.12", 38 | "ym": "0.1.2" 39 | }, 40 | "devDependencies": { 41 | "bower": "^1.7.9", 42 | "enb": ">= 0.13.0 < 2.0.0", 43 | "enb-bh": "^1.2.0", 44 | "enb-magic-factory": ">= 0.3.0 < 1.0.0", 45 | "enb-postcss": "^1.1.0", 46 | "eslint": "^3.5.0", 47 | "eslint-config-pedant": "^0.8.0", 48 | "jscs": "^3.0.7", 49 | "postcss-import": "^8.1.2" 50 | }, 51 | "main": "lib/index.js", 52 | "scripts": { 53 | "test": "npm run lint && npm run test-silly", 54 | "lint": "eslint . && jscs .", 55 | "test-silly": "npm run clean && npm run fixtures && npm run build-examples && npm run clean && npm run build-examples-coverage", 56 | "fixtures": "cd examples && bower i", 57 | "build-examples": "enb -d examples/silly make __magic__ set.specs && enb -d examples/silly make specs-bh && enb -d examples/silly make specs-postcss", 58 | "build-examples-coverage": "ISTANBUL_COVERAGE=yes npm run build-examples && istanbul report text --include examples/silly/coverage.json", 59 | "clean": "rm -rf examples/*/set.* && rm -rf examples/*/.enb/tmp && rm -rf examples/*/coverage.json" 60 | } 61 | } 62 | --------------------------------------------------------------------------------