├── .circleci
└── config.yml
├── .coverage.json
├── .eslintignore
├── .eslintrc.json
├── .github
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── tests.yml
├── .gitignore
├── .travis.yml
├── .versions
├── LICENSE
├── README.md
├── assets
├── base.css
├── block-navigation.js
├── sort-arrow-sprite.png
├── sorter.js
└── vendor
│ ├── prettify.css
│ └── prettify.js
├── changelog.md
├── client
├── client.instrumentation.tests.js
├── main.tests.js
├── methods.e2e.tests.js
├── methods.js
└── methods.unit.tests.js
├── conf
└── default-coverage.json
├── meteor-legacy-coverage
├── .gitignore
├── .versions
├── LICENSE
├── README.md
├── conf
│ └── default-coverage.json
├── package.js
├── package.json
└── server
│ ├── boot.js
│ ├── context
│ ├── conf.js
│ └── log.js
│ ├── handlers.js
│ ├── index.js
│ ├── main.js
│ ├── router.js
│ └── services
│ ├── core.js
│ ├── coverage-data.js
│ ├── instrumenter.js
│ └── source-map.js
├── package.js
├── package.json
├── server
├── boot.js
├── context
│ ├── conf.js
│ └── log.js
├── handlers.js
├── index.js
├── main.js
├── report
│ ├── report-common.js
│ ├── report-coverage.js
│ ├── report-generic.js
│ ├── report-html.js
│ ├── report-http.js
│ ├── report-json-summary.js
│ ├── report-remap.js
│ ├── report-service.js
│ ├── report-teamcity.js
│ └── report-text-summary.js
├── router.js
├── services
│ ├── core.js
│ └── coverage-data.js
└── tests.js
└── someapp
└── package.json
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: circleci/node:latest-browsers
6 | working_directory: ~/meteor-coverage
7 | steps:
8 | - checkout
9 | - run: npm install -g meteor
10 | - run: meteor npm install
11 | - run: mkdir .coverage
12 | - run: meteor npm test
13 | - run: meteor npm run lint || true # ignore eslint error
14 |
--------------------------------------------------------------------------------
/.coverage.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": [
3 | "**/packages/lmieulet_meteor-coverage.js"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | assets
2 | .npm
3 | .github
4 | conf
5 | test
6 | node_modules
7 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": ["eslint:recommended"],
3 | "plugins": ["babel", "mocha", "import"],
4 | "parser": "babel-eslint",
5 | "env":{
6 | "es6": true
7 | },
8 | "parserOptions": {
9 | "ecmaVersion": 6,
10 | "sourceType": "module"
11 | },
12 | "rules": {
13 | "no-useless-escape": [0],
14 | "consistent-return": [0],
15 | "eqeqeq": [2, "smart"],
16 | "import/no-extraneous-dependencies": [0],
17 | "mocha/no-exclusive-tests": "error",
18 | "no-console": [0],
19 | "no-debugger": [0],
20 | "no-unused-vars": [0],
21 | "no-undefined": [0],
22 | "no-undef": [0],
23 | "prefer-template": [0],
24 | "no-mixed-requires": [0],
25 | "quotes": [2, "single"],
26 | "radix": [0],
27 | "semi": [2, "always"],
28 |
29 | "camelcase": 0,
30 | "no-underscore-dangle": 0,
31 |
32 | "indent": [2, 2],
33 | "comma-dangle": [2, "never"],
34 | "no-dupe-args": 2,
35 | "no-dupe-keys": 2,
36 | "no-empty": 2,
37 | "no-extra-boolean-cast": 2,
38 | "no-extra-parens": 2,
39 | "no-extra-semi": 2,
40 | "no-func-assign": 2,
41 | "no-inner-declarations": 2,
42 | "no-irregular-whitespace": 2,
43 | "no-negated-in-lhs": 2,
44 | "no-sparse-arrays": 2,
45 | "no-unexpected-multiline": 2,
46 | "no-unreachable": 2,
47 | "use-isnan": 2,
48 |
49 | "block-scoped-var": 2,
50 | "curly": [2, "multi-line"],
51 | "default-case": 2,
52 | "guard-for-in": 0,
53 | "no-alert": 2,
54 | "no-caller": 2,
55 | "no-case-declarations": 2,
56 | "no-else-return": 2,
57 | "no-labels": 2,
58 | "no-empty-pattern": 2,
59 | "no-eq-null": 2,
60 | "no-eval": 2,
61 |
62 | "import/no-mutable-exports": 0,
63 | "import/no-unresolved": 0,
64 | "import/named": 2,
65 | "import/namespace": 2,
66 | "import/export": 2,
67 | "import/no-duplicates": 2
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Expected Behavior
4 |
5 |
6 |
7 |
8 | ## Current Behavior
9 |
10 |
11 |
12 |
13 | ## Possible Solution
14 |
15 |
16 |
17 |
18 | ## Steps to Reproduce (for bugs)
19 |
20 |
21 |
22 | 1.
23 | 2.
24 | 3.
25 | 4.
26 |
27 | ## Context
28 |
29 |
30 |
31 |
32 | ## Your Environment
33 |
34 |
35 | * Version used:
36 | * Environment name and version (e.g. Node 0.10.47):
37 | * Operating System and version:
38 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Description
4 |
5 |
6 |
7 | ## Motivation and Context
8 |
9 |
10 |
11 |
12 | ## How Has This Been Tested?
13 |
14 |
15 |
16 |
17 |
18 | ## Screenshots (if appropriate):
19 |
20 | ## Types of changes
21 |
22 |
23 | - [ ] Bug fix (non-breaking change which fixes an issue)
24 | - [ ] New feature (non-breaking change which adds functionality)
25 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
26 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | # the test suite runs the tests (headless, server+client) for multiple Meteor releases
2 | name: Test suite
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 |
9 | jobs:
10 | lint:
11 | name: Javascript standard lint
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: checkout
15 | uses: actions/checkout@v3
16 |
17 | - name: setup node
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version: 20
21 |
22 | - name: cache dependencies
23 | uses: actions/cache@v3
24 | with:
25 | path: ~/.npm
26 | key: ${{ runner.os }}-node-20-${{ hashFiles('**/package-lock.json') }}
27 | restore-keys: |
28 | ${{ runner.os }}-node-20-
29 |
30 | - run: |
31 | npm ci
32 | npm run lint
33 |
34 | test:
35 | name: Meteor package tests
36 | # needs: [lint]
37 | runs-on: ubuntu-latest
38 | strategy:
39 | matrix:
40 | meteorRelease:
41 | - '3.0.1'
42 | steps:
43 | - name: Checkout code
44 | uses: actions/checkout@v3
45 |
46 | - name: Install Node.js
47 | uses: actions/setup-node@v3
48 | with:
49 | node-version: 20
50 |
51 | - name: Setup meteor ${{ matrix.meteorRelease }}
52 | uses: meteorengineer/setup-meteor@v1
53 | with:
54 | meteor-release: ${{ matrix.meteorRelease }}
55 |
56 | - name: cache dependencies
57 | uses: actions/cache@v3
58 | with:
59 | path: ~/.npm
60 | key: ${{ runner.os }}-node-20-${{ hashFiles('**/package-lock.json') }}
61 | restore-keys: |
62 | ${{ runner.os }}-node-20-
63 |
64 | - name: Install test dependencies
65 | run: npm ci
66 |
67 |
68 | - name: Run tests
69 | run: meteor npm run test
70 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .npm
2 | .coverage
3 | packages
4 | node_modules
5 | local-packages.json
6 | .idea
7 | test
8 | someapp
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: required
2 | language: node_js
3 |
4 | addons:
5 | chrome: stable
6 |
7 | node_js:
8 | - "14"
9 |
10 | cache:
11 | directories:
12 | - $HOME/.meteor
13 | - $HOME/.npm
14 |
15 | before_cache:
16 | - rm -f $HOME/.meteor/log/*.log
17 |
18 | before_install:
19 | # Download Meteor - Keep in mind that you need
20 | # to remove your travis cache to get meteor updates
21 | - export PATH="$HOME/.meteor:$PATH:$(npm bin -g)"
22 | - npm install -g meteor
23 |
24 | # Install dependencies
25 | - npm install -g eslint coveralls codecov.io codacy-coverage
26 | - npm list -g
27 | - env
28 | - meteor-installer uninstall
29 | - meteor-installer install
30 | - meteor npm install
31 |
32 | services:
33 | - xvfb
34 |
35 | script:
36 | - ls "$HOME/.meteor"
37 | - meteor npm test
38 | - sed 's/packages\/meteor-coverage\///' someapp/.coverage/lcov.info | coveralls || true # ignore coveralls error
39 | - sed 's/packages\/meteor-coverage\///' someapp/.coverage/lcov.info | codecov || true # ignore codecov error
40 | - sed 's/packages\/meteor-coverage\///' someapp/.coverage/lcov.info | codacy-coverage || true # ignore codacy error
41 | - meteor npm run lint || true # ignore eslint error
42 |
--------------------------------------------------------------------------------
/.versions:
--------------------------------------------------------------------------------
1 | babel-compiler@7.2.3
2 | babel-runtime@1.3.0
3 | base64@1.0.11
4 | boilerplate-generator@1.6.0
5 | dynamic-import@0.5.1
6 | ecmascript@0.12.3
7 | ecmascript-runtime@0.7.0
8 | ecmascript-runtime-client@0.8.0
9 | ecmascript-runtime-server@0.7.1
10 | ejson@1.1.0
11 | fetch@0.1.0
12 | http@1.4.2
13 | inter-process-messaging@0.1.0
14 | lmieulet:meteor-coverage@3.0.0
15 | lmieulet:meteor-packages-coverage@0.2.0
16 | local-test:lmieulet:meteor-coverage@3.0.0
17 | logging@1.1.20
18 | meteor@1.9.2
19 | meteortesting:browser-tests@0.1.2
20 | meteortesting:mocha@0.4.4
21 | modern-browsers@0.1.3
22 | modules@0.13.0
23 | modules-runtime@0.10.3
24 | practicalmeteor:mocha-core@1.0.1
25 | promise@0.11.1
26 | routepolicy@1.1.0
27 | underscore@1.0.10
28 | url@1.2.0
29 | webapp@1.7.2
30 | webapp-hashing@1.0.9
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Leo Mieulet
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # meteor-coverage
2 |
3 | A meteor package that allows you to get the statement, line, function and branch coverage of Meteor project and package.
4 |
5 | This package uses the [istanbuljs](https://github.com/istanbuljs/istanbuljs) set of packages to generate reports. Starting from Meteor 1.8, this package does not instrument your code to get coverage as you can let babel do it using the [babel plugin istanbul](https://github.com/istanbuljs/babel-plugin-istanbul).
6 |
7 | It's a debug only package, so it does not affect your production build.
8 |
9 | ## CI Platforms supported
10 |
11 | | | Travis | Circle CI | Coveralls | Codecov | Codacy |
12 | | ------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------: | -------------------------------------------------------------------------------------------------------------------------------------------------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | -----------------------------------------------------------------------------------------------------------------------------------------------------------: | -----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
13 | | lmieulet:meteor-coverage | [](https://app.travis-ci.com/serut/meteor-coverage) | [](https://circleci.com/gh/serut/meteor-coverage) | [](https://coveralls.io/github/serut/meteor-coverage?branch=master) | [](https://codecov.io/gh/serut/meteor-coverage) | [](https://www.codacy.com/gh/serut/meteor-coverage/dashboard) [](https://www.codacy.com/app/l-mieulet/meteor-coverage) |
14 | | [meteor-coverage-app-exemple](https://github.com/serut/meteor-coverage-app-exemple) | [](https://travis-ci.org/serut/meteor-coverage-app-exemple) | [](https://circleci.com/gh/serut/meteor-coverage-app-exemple) | [](https://coveralls.io/github/serut/meteor-coverage-app-exemple?branch=master) | [](https://codecov.io/gh/serut/meteor-coverage-app-exemple) | [](https://www.codacy.com/app/l-mieulet/meteor-coverage-app-exemplee) |
15 |
16 |
17 | [](https://david-dm.org/serut/meteor-coverage)
18 | [](https://david-dm.org/serut/meteor-coverage?type=dev)
19 |
20 |
21 | ## Compatibility
22 |
23 | | meteor-coverage | Meteor | spacejam & practicalmeteor | meteortesting:mocha |
24 | | ------------- |:----------:|:----------:|:----------:|
25 | | 1.x | <1.6.0 | ✔ | ✘ |
26 | | [not supported](https://github.com/meteor/meteor/issues/9281) | 1.6.0 <1.6.1 | ✘ | ✘ |
27 | | 2.x | >=1.6.1 and < 1.8| ✘ | ✔ |
28 | | 3.x | >=1.8| ✘ | ✔ |
29 |
30 |
31 |
32 | **Table of Contents**
33 |
34 | - [Installation](#installation)
35 | - [Specific setup for Meteor apps](#specific-setup-for-meteor-apps)
36 | - [Specific setup for Meteor package](#specific-setup-for-meteor-package)
37 | - [Specific setup for Typescript](#specific-setup-for-Typescript)
38 | - [Advanced setup for CI](#advanced-setup-for-ci)
39 | - [Coveralls](#coveralls)
40 | - [Codecov](#codecov)
41 | - [Global environment variable](#global-environment-variable)
42 | - [Config file](#config-file)
43 | - [My files are missing from my app coverage report](#my-files-are-missing-from-my-app-coverage-report)
44 | - [Meteor ignored folders and files](#meteor-ignored-folders-and-files)
45 | - [How to use another test runner](#how-to-use-another-test-runner)
46 | - [I want my reports referred to my original source files](#i-want-my-reports-referred-to-my-original-source-files)
47 | - [Client API](#client-api)
48 | - [Meteor.sendCoverage(callback)](#meteorsendcoveragecallback)
49 | - [Meteor.exportCoverage(type, callback)](#meteorexportcoveragetype-callback)
50 | - [Meteor.importCoverage(callback)](#meteorimportcoveragecallback)
51 | - [Contributing](#contributing)
52 | - [Credits](#credits)
53 |
54 |
55 |
56 | ## Installation
57 |
58 | ### Specific setup for Meteor apps
59 |
60 | Ensure you use at least Meteor version `v1.8`. If you are using Typescript, jump [here](#specific-setup-for-Typescript)
61 |
62 | Then, run the following :
63 |
64 | ```txt
65 | meteor add lmieulet:meteor-coverage meteortesting:mocha
66 | meteor npm init # If the package.json file does not exist
67 | meteor npm install --save-dev babel-plugin-istanbul
68 | ```
69 |
70 | In order to instrument your code, you need to add the [`babel-plugin-istanbul`](https://github.com/istanbuljs/babel-plugin-istanbul) to your babel config. If you don't have a babel config file, edit your package.json file, or use [any other babel configuration file (see .babelrc.js)](https://babeljs.io/docs/en/config-files).
71 |
72 | ```json
73 | {
74 | "name": "my-package",
75 | "version": "1.0.0",
76 | "babel": {
77 | "env": {
78 | "COVERAGE": {
79 | "plugins": [
80 | "istanbul"
81 | ]
82 | }
83 | }
84 | }
85 | }
86 | ```
87 |
88 | You must wrap the istanbul plugin with the `env` setting to disable the file-instrumentation of your project when you are not running the test coverage script. Just keep in mind that if you follow the here under script, babel will use the `istanbul` package only when `BABEL_ENV=COVERAGE`.
89 |
90 | Now, to run the coverage process, just add these new scripts inside your `package.json` in the root folder of your app:
91 | ```json
92 | "scripts": {
93 | "coverage:unit": "BABEL_ENV=COVERAGE TEST_BROWSER_DRIVER=puppeteer COVERAGE=1 COVERAGE_OUT_HTML=1 COVERAGE_APP_FOLDER=$PWD/ meteor test --once --driver-package meteortesting:mocha",
94 | "coverage:watch": "BABEL_ENV=COVERAGE COVERAGE=1 COVERAGE_VERBOSE=1 COVERAGE_APP_FOLDER=$PWD/ TEST_WATCH=1 meteor test --driver-package meteortesting:mocha"
95 | }
96 | ```
97 |
98 | You can find more options on the [meteortesting readme](https://github.com/meteortesting/meteor-mocha#run-with-code-coverage). Let's try the watch mode :
99 |
100 | meteor npm run test:watch:coverage
101 |
102 | Now open your [browser test page localhost:3000/](http://localhost:3000/) and the page [localhost:3000/coverage](http://localhost:3000/coverage). You can notice the client coverage is completly missing but server one is there. A missing feature would be to save your client coverage with a widget. Instead, you need to enter this javascript in your browser console (in the page where tests are executed):
103 |
104 | Meteor.sendCoverage(function(stats,nbErr) {console.log(stats,nbErr);});
105 | # Reopen localhost:3000/coverage to see that client coverage have been saved on server
106 |
107 | # Creates an html export inside coverage_app_folder/output_folder/index.html
108 | Meteor.exportCoverage("html", function(err) {console.log(err)})
109 |
110 | Refresh the [localhost:3000/coverage](http://localhost:3000/coverage) in your browser to see there is client coverage now.
111 |
112 | ### Specific setup for Meteor package
113 |
114 | In a meteor package, you need to add inside the `package.js` file:
115 |
116 | ```js
117 | [...]
118 | Package.onTest(function (api) {
119 | api.use(['lmieulet:meteor-legacy-coverage@0.2.0', 'lmieulet:meteor-coverage@3.0.0','meteortesting:mocha']);
120 | [...]
121 | });
122 | ```
123 |
124 | Creating a Meteor package in 2018 is a nightmare, so please stay calm when you discover the following `package.json` that prevents so many issues :
125 | ```
126 | "scripts": {
127 | "setup-test": "rm -rf ./someapp && meteor create --bare someapp && cd someapp && cp ../.coverage.json . && meteor npm i --save puppeteer && mkdir packages && ln -s ../../ ./packages/meteor-coverage",
128 | "test": "meteor npm run setup-test && cd someapp && TEST_BROWSER_DRIVER=puppeteer COVERAGE_VERBOSE=1 COVERAGE=1 COVERAGE_OUT_LCOVONLY=1 COVERAGE_APP_FOLDER=$(pwd)/ meteor test-packages --once --driver-package meteortesting:mocha ./packages/meteor-coverage",
129 | "test:watch": "cd someapp && TEST_WATCH=1 COVERAGE=1 COVERAGE_APP_FOLDER=$(pwd)/ meteor test-packages --driver-package meteortesting:mocha ./packages/meteor-coverage"
130 | }
131 | ```
132 | The task `setup-test` is the cutting edge workaround that creates an empty meteor app that will run your test later.
133 |
134 | ### Specific setup for Typescript
135 |
136 | If you use Typescript, you cannot use babel to instrument your code, you need to rely on `lmieulet:meteor-legacy-coverage` (like packages).
137 | The installation is almost the same as normal Meteor application, but you don't need to install all the babel stuff, you just need to run the following :
138 |
139 | ```txt
140 | meteor add lmieulet:meteor-legacy-coverage
141 | ```
142 |
143 | You can look at the [deskoh/Meteor-React-Typescript-Starter](https://github.com/deskoh/Meteor-React-Typescript-Starter) if you need to see a working exemple.
144 |
145 | ## Advanced setup for CI
146 |
147 | ### Coveralls
148 |
149 | Install
150 |
151 | meteor npm i --save-dev coveralls
152 |
153 | Add this after tests execution:
154 |
155 | # Send coverage report
156 | cat .coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js || true # ignore coveralls error
157 |
158 | ### Codecov
159 |
160 | Install
161 |
162 | meteor npm i --save-dev codecov.io
163 |
164 | Add this after tests execution:
165 |
166 | cat .coverage/lcov.info | ./node_modules/codecov.io/bin/codecov.io.js || true # ignore codecov error
167 |
168 | ## Global environment variable
169 |
170 | **Deprecated**
171 | You can provides settings by setting these environment variables:
172 |
173 | - `COVERAGE=1` to enable coverage
174 | - `COVERAGE_APP_FOLDER=/path/to/your/meteor/app/`
175 | - Used to see if you have a customized `.coverage.json` file
176 | - Used by istanbul in reports if the file has source map
177 | - Needs to end with a trailing slash
178 | - Used when importing or exporting coverage reports
179 | - `COVERAGE_VERBOSE=1` to see logs (optional)
180 |
181 | ## Config file
182 |
183 | If you have packages used by your project (ex: aldeed:simple-schema) or libraries on your client side (ex: OpenLayers, Jquery), you can hide the coverage of these files from reports. You can specify which files will not be covered in a `.coverage.json` file inside the `COVERAGE_APP_FOLDER` folder.
184 |
185 | If you do not have this file, this package will use the default one (`conf/default-coverage.json`). If you do not define a key in the `.coverage.json` file, the default one will be used.
186 |
187 | Exemple:
188 |
189 | ```json{
190 | "--": "Meteor app does not require any specific configuration",
191 | "--": "If you want to instrument a package, you need to add the following",
192 | "remapFormat": ["html", "cobertura", "clover", "json", "json-summary", "lcovonly", "teamcity", "text", "text-summary"],
193 | "output": "./.coverage"
194 | }
195 | ```
196 |
197 | Details :
198 |
199 | - The glob syntax can be found [here](http://www.linuxjournal.com/content/bash-extended-globbing).
200 | - To create your custom config file, run the project with `COVERAGE_VERBOSE=1` env variable and use logs to see which filenames were hooked or hidden. PR welcome.
201 | - The output folder needs to starts with a dot to exclude that folder from Meteor build.
202 |
203 | ## My files are missing from my app coverage report
204 |
205 | If you have **internal packages** inside your app and you want to get their **server side** coverage. Open the file `.meteor/packages` and move the line `lmieulet:meteor-coverage` to be above these packages.
206 |
207 | ## Meteor ignored folders and files
208 |
209 | - hidden folders like .npm, .coverage or .meteor.
210 | - special folders like node_modules.
211 | - all meteor packages (bundled and/or manually installed ones) like meteor/underscore, meteor/accounts-password or aldeed:simple-schema.
212 | - all tests file(s) containing `spec?|test?|specs?|tests?|app-specs?|app-tests?` and all folder(s) named `specs?|tests?|app-specs?|app-tests?`
213 |
214 | ## How to use another test runner
215 |
216 | You can find [here](https://github.com/practicalmeteor/spacejam/compare/windows-suppport...serut:windows-suppport-rc4?diff=split&name=windows-suppport-rc4#diff-f388d8f4ed9765929079f40166396fdeR65) the diff between "spacejam without coverage" and "spacejam coverage", so you can build something else, with grunt for example, that exports your test. meteortesting:mocha did also the same.
217 |
218 | ## I want my reports referred to my original source files
219 |
220 | If you are using a language that compiles to JavaScript (there are [lots of them](https://github.com/jashkenas/coffeescript/wiki/list-of-languages-that-compile-to-js)), you may want to see your coverage reports referred to the original source files (prior to compilation).
221 |
222 | To remap your source files, you have to provide the report type `out_remap` explicitly when using `spacejam`: `spacejam-mocha --coverage out_remap`
223 |
224 | You'll get your remapped coverage reports at `./.coverage/.remap` (or `custom_output/.remap` if you're customized the output folder through the file `.coverage.json`).
225 |
226 | The coverage is remapped to **all the available reports** (listed in the following example) by default. If you only want some of them, you need to request them explicitly through the key `remap.format` in `.coverage.json` like this:
227 |
228 | ```json
229 | {
230 | "remap": {
231 | "format": ["html", "clover", "cobertura", "json", "json-summary", "lcovonly", "teamcity", "text", "text-summary"]
232 | }
233 | }
234 | ```
235 |
236 | If you want to remap the coverage with `Meteor.exportCoverage()`, then you must use the report type `remap`.
237 |
238 | This feature has only been tested with TypeScript, but it should work for any language compiled to JavaScript, just **make sure you generate source maps (\*.js.map) for all the compiled files and that source maps are located next to their respective compiled JavaScript file (\*.js)**, just like this:
239 |
240 | COVERAGE_APP_FOLDER
241 | ├── tsconfig.json
242 | ├── src
243 | | ├── my-file.ts
244 | | └── my-file.d.ts
245 | ├── build
246 | | ├── my-file.js
247 | | └── my-file.js.map
248 |
249 | * * *
250 |
251 | ## Client API
252 |
253 | #### Meteor.sendCoverage(callback)
254 |
255 | Run the following command in your browser and the client coverage will be saved into the server coverage report.
256 |
257 | ```js
258 | Meteor.sendCoverage(function(stats,nbErr) {console.log(stats,nbErr);});
259 | ```
260 |
261 | Why? When a browser opens the client side of your application, this package intercepts all queries matching `*.js` to respond the instrumented version of the original script, if they are not ignored by the configuration file. All these instrumented scripts are autonomous and they save the coverage in a global variable when you execute a line of a file. This global variable needs to be sent back to the server to create a full coverage report.
262 |
263 | #### Meteor.exportCoverage(type, callback)
264 |
265 | - type: the type of report you want to create inside your `COVERAGE_APP_FOLDER`
266 |
267 | - Default: `coverage`, used to dump the coverage object in a file because when there are several types of test, we want to merge results, and the server reloads between each one.
268 | - Allowed values: `coverage`, `html`, `json`, `json-summary`, `lcovonly`, `remap`, `text-summary`
269 | - **Not working values:** `clover`, `cobertura`, `lcov`, `teamcity`, `text`, `text-lcov`, PR welcome
270 | - Except for `coverage`, the file generation is handled by [istanbuljs/istanbul-reports](https://github.com/istanbuljs/istanbul-reports)
271 |
272 | ```js
273 | Meteor.exportCoverage(null, function(err) {console.log(err)})
274 | ```
275 |
276 | #### Meteor.importCoverage(callback)
277 |
278 | Import a `coverage` export.
279 |
280 | ```js
281 | Meteor.importCoverage(function(err) {console.log(err)})
282 | ```
283 |
284 | ## Contributing
285 |
286 | Anyone is welcome to contribute.
287 |
288 | # You should fork this repo first
289 | git clone https://github.com/serut/meteor-coverage
290 | # I have a preference to always execute npm action
291 | # throw the integrated meteor npm (npm --v !== meteor npm --v)
292 | meteor npm install
293 | # Edit the app_folder key to match your app folder, don't forget the ending slash
294 | nano settings.coverage.json
295 | # Then run mocha watch tests
296 | meteor npm run start
297 |
298 | ## Credits
299 |
300 | This package would not exist without the amazing work of:
301 |
302 | - [Contributors](https://github.com/serut/meteor-coverage/graphs/contributors) and testers for their help
303 | - [Xolv.io](http://xolv.io) and their work on the original [meteor-coverage](https://github.com/xolvio/meteor-coverage) package;
304 | - All contributors of [istanbuljs](https://github.com/istanbuljs/istanbuljs) project.
305 |
306 | All of them were very helpful in the development of this package. Merci !
307 |
--------------------------------------------------------------------------------
/assets/base.css:
--------------------------------------------------------------------------------
1 | body, html {
2 | margin:0; padding: 0;
3 | height: 100%;
4 | }
5 | body {
6 | font-family: Helvetica Neue, Helvetica, Arial;
7 | font-size: 14px;
8 | color:#333;
9 | }
10 | .small { font-size: 12px; }
11 | *, *:after, *:before {
12 | -webkit-box-sizing:border-box;
13 | -moz-box-sizing:border-box;
14 | box-sizing:border-box;
15 | }
16 | h1 { font-size: 20px; margin: 0;}
17 | h2 { font-size: 14px; }
18 | pre {
19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
20 | margin: 0;
21 | padding: 0;
22 | -moz-tab-size: 2;
23 | -o-tab-size: 2;
24 | tab-size: 2;
25 | }
26 | a { color:#0074D9; text-decoration:none; }
27 | a:hover { text-decoration:underline; }
28 | .strong { font-weight: bold; }
29 | .space-top1 { padding: 10px 0 0 0; }
30 | .pad2y { padding: 20px 0; }
31 | .pad1y { padding: 10px 0; }
32 | .pad2x { padding: 0 20px; }
33 | .pad2 { padding: 20px; }
34 | .pad1 { padding: 10px; }
35 | .space-left2 { padding-left:55px; }
36 | .space-right2 { padding-right:20px; }
37 | .center { text-align:center; }
38 | .clearfix { display:block; }
39 | .clearfix:after {
40 | content:'';
41 | display:block;
42 | height:0;
43 | clear:both;
44 | visibility:hidden;
45 | }
46 | .fl { float: left; }
47 | @media only screen and (max-width:640px) {
48 | .col3 { width:100%; max-width:100%; }
49 | .hide-mobile { display:none!important; }
50 | }
51 |
52 | .quiet {
53 | color: #7f7f7f;
54 | color: rgba(0,0,0,0.5);
55 | }
56 | .quiet a { opacity: 0.7; }
57 |
58 | .fraction {
59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
60 | font-size: 10px;
61 | color: #555;
62 | background: #E8E8E8;
63 | padding: 4px 5px;
64 | border-radius: 3px;
65 | vertical-align: middle;
66 | }
67 |
68 | div.path a:link, div.path a:visited { color: #333; }
69 | table.coverage {
70 | border-collapse: collapse;
71 | margin: 10px 0 0 0;
72 | padding: 0;
73 | }
74 |
75 | table.coverage td {
76 | margin: 0;
77 | padding: 0;
78 | vertical-align: top;
79 | }
80 | table.coverage td.line-count {
81 | text-align: right;
82 | padding: 0 5px 0 20px;
83 | }
84 | table.coverage td.line-coverage {
85 | text-align: right;
86 | padding-right: 10px;
87 | min-width:20px;
88 | }
89 |
90 | table.coverage td span.cline-any {
91 | display: inline-block;
92 | padding: 0 5px;
93 | width: 100%;
94 | }
95 | .missing-if-branch {
96 | display: inline-block;
97 | margin-right: 5px;
98 | border-radius: 3px;
99 | position: relative;
100 | padding: 0 4px;
101 | background: #333;
102 | color: yellow;
103 | }
104 |
105 | .skip-if-branch {
106 | display: none;
107 | margin-right: 10px;
108 | position: relative;
109 | padding: 0 4px;
110 | background: #ccc;
111 | color: white;
112 | }
113 | .missing-if-branch .typ, .skip-if-branch .typ {
114 | color: inherit !important;
115 | }
116 | .coverage-summary {
117 | border-collapse: collapse;
118 | width: 100%;
119 | }
120 | .coverage-summary tr { border-bottom: 1px solid #bbb; }
121 | .keyline-all { border: 1px solid #ddd; }
122 | .coverage-summary td, .coverage-summary th { padding: 10px; }
123 | .coverage-summary tbody { border: 1px solid #bbb; }
124 | .coverage-summary td { border-right: 1px solid #bbb; }
125 | .coverage-summary td:last-child { border-right: none; }
126 | .coverage-summary th {
127 | text-align: left;
128 | font-weight: normal;
129 | white-space: nowrap;
130 | }
131 | .coverage-summary th.file { border-right: none !important; }
132 | .coverage-summary th.pct { }
133 | .coverage-summary th.pic,
134 | .coverage-summary th.abs,
135 | .coverage-summary td.pct,
136 | .coverage-summary td.abs { text-align: right; }
137 | .coverage-summary td.file { white-space: nowrap; }
138 | .coverage-summary td.pic { min-width: 120px !important; }
139 | .coverage-summary tfoot td { }
140 |
141 | .coverage-summary .sorter {
142 | height: 10px;
143 | width: 7px;
144 | display: inline-block;
145 | margin-left: 0.5em;
146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
147 | }
148 | .coverage-summary .sorted .sorter {
149 | background-position: 0 -20px;
150 | }
151 | .coverage-summary .sorted-desc .sorter {
152 | background-position: 0 -10px;
153 | }
154 | .status-line { height: 10px; }
155 | /* yellow */
156 | .cbranch-no { background: yellow !important; color: #111; }
157 | /* dark red */
158 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 }
159 | .low .chart { border:1px solid #C21F39 }
160 | .highlighted,
161 | .highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{
162 | background: #C21F39 !important;
163 | }
164 | /* medium red */
165 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE }
166 | /* light red */
167 | .low, .cline-no { background:#FCE1E5 }
168 | /* light green */
169 | .high, .cline-yes { background:rgb(230,245,208) }
170 | /* medium green */
171 | .cstat-yes { background:rgb(161,215,106) }
172 | /* dark green */
173 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) }
174 | .high .chart { border:1px solid rgb(77,146,33) }
175 |
176 | .medium .chart { border:1px solid #666; }
177 | .medium .cover-fill { background: #666; }
178 |
179 | .cstat-skip { background: #ddd; color: #111; }
180 | .fstat-skip { background: #ddd; color: #111 !important; }
181 | .cbranch-skip { background: #ddd !important; color: #111; }
182 |
183 | span.cline-neutral { background: #eaeaea; }
184 | .medium { background: #eaeaea; }
185 |
186 | .coverage-summary td.empty {
187 | opacity: .5;
188 | padding-top: 4px;
189 | padding-bottom: 4px;
190 | line-height: 1;
191 | color: #888;
192 | }
193 |
194 | .cover-fill, .cover-empty {
195 | display:inline-block;
196 | height: 12px;
197 | }
198 | .chart {
199 | line-height: 0;
200 | }
201 | .cover-empty {
202 | background: white;
203 | }
204 | .cover-full {
205 | border-right: none !important;
206 | }
207 | pre.prettyprint {
208 | border: none !important;
209 | padding: 0 !important;
210 | margin: 0 !important;
211 | }
212 | .com { color: #999 !important; }
213 | .ignore-none { color: #999; font-weight: normal; }
214 |
215 | .wrapper {
216 | min-height: 100%;
217 | height: auto !important;
218 | height: 100%;
219 | margin: 0 auto -48px;
220 | }
221 | .footer, .push {
222 | height: 48px;
223 | }
224 |
--------------------------------------------------------------------------------
/assets/block-navigation.js:
--------------------------------------------------------------------------------
1 | var jumpToCode = (function init () {
2 | // Classes of code we would like to highlight
3 | var missingCoverageClasses = [ '.cbranch-no', '.cstat-no', '.fstat-no' ];
4 |
5 | // We don't want to select elements that are direct descendants of another match
6 | var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > `
7 |
8 | // Selecter that finds elements on the page to which we can jump
9 | var selector = notSelector + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b`
10 |
11 | // The NodeList of matching elements
12 | var missingCoverageElements = document.querySelectorAll(selector);
13 |
14 | var currentIndex;
15 |
16 | function toggleClass(index) {
17 | missingCoverageElements.item(currentIndex).classList.remove('highlighted');
18 | missingCoverageElements.item(index).classList.add('highlighted');
19 | }
20 |
21 | function makeCurrent(index) {
22 | toggleClass(index);
23 | currentIndex = index;
24 | missingCoverageElements.item(index)
25 | .scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'center' });
26 | }
27 |
28 | function goToPrevious() {
29 | var nextIndex = 0;
30 | if (typeof currentIndex !== 'number' || currentIndex === 0) {
31 | nextIndex = missingCoverageElements.length - 1;
32 | } else if (missingCoverageElements.length > 1) {
33 | nextIndex = currentIndex - 1;
34 | }
35 |
36 | makeCurrent(nextIndex);
37 | }
38 |
39 | function goToNext() {
40 | var nextIndex = 0;
41 |
42 | if (typeof currentIndex === 'number' && currentIndex < (missingCoverageElements.length - 1)) {
43 | nextIndex = currentIndex + 1;
44 | }
45 |
46 | makeCurrent(nextIndex);
47 | }
48 |
49 | return function jump(event) {
50 | switch (event.which) {
51 | case 78: // n
52 | case 74: // j
53 | goToNext();
54 | break;
55 | case 66: // b
56 | case 75: // k
57 | case 80: // p
58 | goToPrevious();
59 | break;
60 | }
61 | };
62 | }());
63 | window.addEventListener('keydown', jumpToCode);
64 |
--------------------------------------------------------------------------------
/assets/sort-arrow-sprite.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/serut/meteor-coverage/050d739f7c77279f7dc101ac6bb1dc45aab9f724/assets/sort-arrow-sprite.png
--------------------------------------------------------------------------------
/assets/sorter.js:
--------------------------------------------------------------------------------
1 | var addSorting = (function () {
2 | "use strict";
3 | var cols,
4 | currentSort = {
5 | index: 0,
6 | desc: false
7 | };
8 |
9 | // returns the summary table element
10 | function getTable() { return document.querySelector('.coverage-summary'); }
11 | // returns the thead element of the summary table
12 | function getTableHeader() { return getTable().querySelector('thead tr'); }
13 | // returns the tbody element of the summary table
14 | function getTableBody() { return getTable().querySelector('tbody'); }
15 | // returns the th element for nth column
16 | function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; }
17 |
18 | // loads all columns
19 | function loadColumns() {
20 | var colNodes = getTableHeader().querySelectorAll('th'),
21 | colNode,
22 | cols = [],
23 | col,
24 | i;
25 |
26 | for (i = 0; i < colNodes.length; i += 1) {
27 | colNode = colNodes[i];
28 | col = {
29 | key: colNode.getAttribute('data-col'),
30 | sortable: !colNode.getAttribute('data-nosort'),
31 | type: colNode.getAttribute('data-type') || 'string'
32 | };
33 | cols.push(col);
34 | if (col.sortable) {
35 | col.defaultDescSort = col.type === 'number';
36 | colNode.innerHTML = colNode.innerHTML + '';
37 | }
38 | }
39 | return cols;
40 | }
41 | // attaches a data attribute to every tr element with an object
42 | // of data values keyed by column name
43 | function loadRowData(tableRow) {
44 | var tableCols = tableRow.querySelectorAll('td'),
45 | colNode,
46 | col,
47 | data = {},
48 | i,
49 | val;
50 | for (i = 0; i < tableCols.length; i += 1) {
51 | colNode = tableCols[i];
52 | col = cols[i];
53 | val = colNode.getAttribute('data-value');
54 | if (col.type === 'number') {
55 | val = Number(val);
56 | }
57 | data[col.key] = val;
58 | }
59 | return data;
60 | }
61 | // loads all row data
62 | function loadData() {
63 | var rows = getTableBody().querySelectorAll('tr'),
64 | i;
65 |
66 | for (i = 0; i < rows.length; i += 1) {
67 | rows[i].data = loadRowData(rows[i]);
68 | }
69 | }
70 | // sorts the table using the data for the ith column
71 | function sortByIndex(index, desc) {
72 | var key = cols[index].key,
73 | sorter = function (a, b) {
74 | a = a.data[key];
75 | b = b.data[key];
76 | return a < b ? -1 : a > b ? 1 : 0;
77 | },
78 | finalSorter = sorter,
79 | tableBody = document.querySelector('.coverage-summary tbody'),
80 | rowNodes = tableBody.querySelectorAll('tr'),
81 | rows = [],
82 | i;
83 |
84 | if (desc) {
85 | finalSorter = function (a, b) {
86 | return -1 * sorter(a, b);
87 | };
88 | }
89 |
90 | for (i = 0; i < rowNodes.length; i += 1) {
91 | rows.push(rowNodes[i]);
92 | tableBody.removeChild(rowNodes[i]);
93 | }
94 |
95 | rows.sort(finalSorter);
96 |
97 | for (i = 0; i < rows.length; i += 1) {
98 | tableBody.appendChild(rows[i]);
99 | }
100 | }
101 | // removes sort indicators for current column being sorted
102 | function removeSortIndicators() {
103 | var col = getNthColumn(currentSort.index),
104 | cls = col.className;
105 |
106 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, '');
107 | col.className = cls;
108 | }
109 | // adds sort indicators for current column being sorted
110 | function addSortIndicators() {
111 | getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted';
112 | }
113 | // adds event listeners for all sorter widgets
114 | function enableUI() {
115 | var i,
116 | el,
117 | ithSorter = function ithSorter(i) {
118 | var col = cols[i];
119 |
120 | return function () {
121 | var desc = col.defaultDescSort;
122 |
123 | if (currentSort.index === i) {
124 | desc = !currentSort.desc;
125 | }
126 | sortByIndex(i, desc);
127 | removeSortIndicators();
128 | currentSort.index = i;
129 | currentSort.desc = desc;
130 | addSortIndicators();
131 | };
132 | };
133 | for (i =0 ; i < cols.length; i += 1) {
134 | if (cols[i].sortable) {
135 | // add the click event handler on the th so users
136 | // dont have to click on those tiny arrows
137 | el = getNthColumn(i).querySelector('.sorter').parentElement;
138 | if (el.addEventListener) {
139 | el.addEventListener('click', ithSorter(i));
140 | } else {
141 | el.attachEvent('onclick', ithSorter(i));
142 | }
143 | }
144 | }
145 | }
146 | // adds sorting functionality to the UI
147 | return function () {
148 | if (!getTable()) {
149 | return;
150 | }
151 | cols = loadColumns();
152 | loadData(cols);
153 | addSortIndicators();
154 | enableUI();
155 | };
156 | })();
157 |
158 | window.addEventListener('load', addSorting);
159 |
--------------------------------------------------------------------------------
/assets/vendor/prettify.css:
--------------------------------------------------------------------------------
1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee}
2 |
--------------------------------------------------------------------------------
/assets/vendor/prettify.js:
--------------------------------------------------------------------------------
1 | window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^