diff --git a/webui/.eslintrc.js b/webui/.eslintrc.js
index cea5781eb..f8fee91f8 100644
--- a/webui/.eslintrc.js
+++ b/webui/.eslintrc.js
@@ -7,19 +7,22 @@ module.exports = {
},
env: {
- browser: true
+ browser: true,
+ mocha: true
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
- '@vue/standard'
+ '@vue/standard',
+ 'plugin:mocha/recommended'
],
// required to lint *.vue files
plugins: [
- 'vue'
+ 'vue',
+ 'mocha'
],
globals: {
diff --git a/webui/package-lock.json b/webui/package-lock.json
index 909e47be8..1956acaa2 100644
--- a/webui/package-lock.json
+++ b/webui/package-lock.json
@@ -2602,6 +2602,16 @@
"integrity": "sha512-8VCoJeeH8tCkzhkpfOkt+abALQkS11OIHhte5MBzYaKMTqK0A3ZAKEUVAffsOklhEv7t0yrQt696Opnu9oAx+w==",
"dev": true
},
+ "@vue/test-utils": {
+ "version": "1.0.0-beta.29",
+ "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.0.0-beta.29.tgz",
+ "integrity": "sha512-yX4sxEIHh4M9yAbLA/ikpEnGKMNBCnoX98xE1RwxfhQVcn0MaXNSj1Qmac+ZydTj6VBSEVukchBogXBTwc+9iA==",
+ "dev": true,
+ "requires": {
+ "dom-event-types": "^1.0.0",
+ "lodash": "^4.17.4"
+ }
+ },
"@webassemblyjs/ast": {
"version": "1.8.5",
"resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.8.5.tgz",
@@ -3060,6 +3070,12 @@
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
"dev": true
},
+ "assertion-error": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
+ "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
+ "dev": true
+ },
"assign-symbols": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
@@ -3538,6 +3554,12 @@
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=",
"dev": true
},
+ "browser-stdout": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+ "dev": true
+ },
"browserify-aes": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz",
@@ -3782,6 +3804,20 @@
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
"dev": true
},
+ "chai": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz",
+ "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==",
+ "dev": true,
+ "requires": {
+ "assertion-error": "^1.1.0",
+ "check-error": "^1.0.2",
+ "deep-eql": "^3.0.1",
+ "get-func-name": "^2.0.0",
+ "pathval": "^1.1.0",
+ "type-detect": "^4.0.5"
+ }
+ },
"chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@@ -3832,6 +3868,12 @@
"color-name": "^1.0.0"
}
},
+ "check-error": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
+ "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
+ "dev": true
+ },
"check-types": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/check-types/-/check-types-8.0.3.tgz",
@@ -4832,6 +4874,15 @@
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
"dev": true
},
+ "deep-eql": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
+ "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
+ "dev": true,
+ "requires": {
+ "type-detect": "^4.0.0"
+ }
+ },
"deep-equal": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
@@ -5002,6 +5053,12 @@
"integrity": "sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==",
"dev": true
},
+ "diff": {
+ "version": "3.5.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz",
+ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==",
+ "dev": true
+ },
"diffie-hellman": {
"version": "5.0.3",
"resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz",
@@ -5066,6 +5123,12 @@
"utila": "~0.4"
}
},
+ "dom-event-types": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/dom-event-types/-/dom-event-types-1.0.0.tgz",
+ "integrity": "sha512-2G2Vwi2zXTHBGqXHsJ4+ak/iP0N8Ar+G8a7LiD2oup5o4sQWytwqqrZu/O6hIMV0KMID2PL69OhpshLO0n7UJQ==",
+ "dev": true
+ },
"dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@@ -5585,6 +5648,15 @@
}
}
},
+ "eslint-plugin-mocha": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.2.1.tgz",
+ "integrity": "sha512-o3Ibhpczi5MjUVpnlnrpC/+oJYGoHKB5m4bQdRnaAOeFCN3HRkqBisQ2/h0hEuCR4lPxyHP1Qzyjpna8MsOdlA==",
+ "dev": true,
+ "requires": {
+ "ramda": "^0.26.1"
+ }
+ },
"eslint-plugin-node": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-8.0.1.tgz",
@@ -6185,6 +6257,23 @@
}
}
},
+ "flat": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
+ "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
+ "dev": true,
+ "requires": {
+ "is-buffer": "~2.0.3"
+ },
+ "dependencies": {
+ "is-buffer": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz",
+ "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==",
+ "dev": true
+ }
+ }
+ },
"flat-cache": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
@@ -6473,6 +6562,12 @@
"integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==",
"dev": true
},
+ "get-func-name": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
+ "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
+ "dev": true
+ },
"get-own-enumerable-property-symbols": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.1.tgz",
@@ -6594,6 +6689,12 @@
"integrity": "sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg==",
"dev": true
},
+ "growl": {
+ "version": "1.10.5",
+ "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz",
+ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
+ "dev": true
+ },
"gzip-size": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-5.1.1.tgz",
@@ -7292,6 +7393,12 @@
"ipaddr.js": "^1.9.0"
}
},
+ "interpret": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
+ "integrity": "sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==",
+ "dev": true
+ },
"invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -7969,7 +8076,8 @@
"lodash": {
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
- "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+ "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
+ "dev": true
},
"lodash._reinterpolate": {
"version": "3.0.0",
@@ -8001,6 +8109,11 @@
"integrity": "sha1-gwWOkDtRy7dZ0JzPVG3qPqOcRxg=",
"dev": true
},
+ "lodash.isequal": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
+ "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
+ },
"lodash.kebabcase": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
@@ -8056,6 +8169,15 @@
"integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=",
"dev": true
},
+ "log-symbols": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
+ "integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.0.1"
+ }
+ },
"log-update": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/log-update/-/log-update-3.3.0.tgz",
@@ -8580,6 +8702,1022 @@
}
}
},
+ "mocha": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.2.2.tgz",
+ "integrity": "sha512-FgDS9Re79yU1xz5d+C4rv1G7QagNGHZ+iXF81hO8zY35YZZcLEsJVfFolfsqKFWunATEvNzMK0r/CwWd/szO9A==",
+ "dev": true,
+ "requires": {
+ "ansi-colors": "3.2.3",
+ "browser-stdout": "1.3.1",
+ "debug": "3.2.6",
+ "diff": "3.5.0",
+ "escape-string-regexp": "1.0.5",
+ "find-up": "3.0.0",
+ "glob": "7.1.3",
+ "growl": "1.10.5",
+ "he": "1.2.0",
+ "js-yaml": "3.13.1",
+ "log-symbols": "2.2.0",
+ "minimatch": "3.0.4",
+ "mkdirp": "0.5.1",
+ "ms": "2.1.1",
+ "node-environment-flags": "1.0.5",
+ "object.assign": "4.1.0",
+ "strip-json-comments": "2.0.1",
+ "supports-color": "6.0.0",
+ "which": "1.3.1",
+ "wide-align": "1.1.3",
+ "yargs": "13.3.0",
+ "yargs-parser": "13.1.1",
+ "yargs-unparser": "1.6.0"
+ },
+ "dependencies": {
+ "ansi-colors": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz",
+ "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==",
+ "dev": true
+ },
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "debug": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
+ "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "glob": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+ "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+ "dev": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+ "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "supports-color": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz",
+ "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==",
+ "dev": true,
+ "requires": {
+ "has-flag": "^3.0.0"
+ }
+ },
+ "yargs": {
+ "version": "13.3.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
+ "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
+ "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
+ },
+ "mocha-webpack": {
+ "version": "2.0.0-beta.0",
+ "resolved": "https://registry.npmjs.org/mocha-webpack/-/mocha-webpack-2.0.0-beta.0.tgz",
+ "integrity": "sha512-2ezbW0h5cYWr874F/hzytQCqINxk+GVelMY4xWTSHwwH1LrPAOzjlUljZ+/PhpaP6QeqYbL5x5vK/bnaXqkfEw==",
+ "dev": true,
+ "requires": {
+ "babel-runtime": "^6.18.0",
+ "chalk": "^2.3.0",
+ "chokidar": "^2.0.2",
+ "glob-parent": "^3.1.0",
+ "globby": "^7.1.1",
+ "interpret": "^1.0.1",
+ "is-glob": "^4.0.0",
+ "loader-utils": "^1.1.0",
+ "lodash": "^4.3.0",
+ "memory-fs": "^0.4.1",
+ "nodent-runtime": "^3.0.3",
+ "normalize-path": "^2.0.1",
+ "progress": "^2.0.0",
+ "source-map-support": "^0.5.0",
+ "strip-ansi": "^4.0.0",
+ "toposort": "^1.0.0",
+ "yargs": "^11.0.0"
+ },
+ "dependencies": {
+ "ansi-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+ "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+ "dev": true
+ },
+ "anymatch": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz",
+ "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==",
+ "dev": true,
+ "requires": {
+ "micromatch": "^3.1.4",
+ "normalize-path": "^2.1.1"
+ }
+ },
+ "binary-extensions": {
+ "version": "1.13.1",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
+ "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==",
+ "dev": true
+ },
+ "braces": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "dev": true,
+ "requires": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "camelcase": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
+ "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=",
+ "dev": true
+ },
+ "chokidar": {
+ "version": "2.1.8",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz",
+ "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==",
+ "dev": true,
+ "requires": {
+ "anymatch": "^2.0.0",
+ "async-each": "^1.0.1",
+ "braces": "^2.3.2",
+ "fsevents": "^1.2.7",
+ "glob-parent": "^3.1.0",
+ "inherits": "^2.0.3",
+ "is-binary-path": "^1.0.0",
+ "is-glob": "^4.0.0",
+ "normalize-path": "^3.0.0",
+ "path-is-absolute": "^1.0.0",
+ "readdirp": "^2.2.1",
+ "upath": "^1.1.1"
+ },
+ "dependencies": {
+ "normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true
+ }
+ }
+ },
+ "fill-range": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
+ "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+ "dev": true,
+ "requires": {
+ "extend-shallow": "^2.0.1",
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1",
+ "to-regex-range": "^2.1.0"
+ },
+ "dependencies": {
+ "extend-shallow": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "dev": true,
+ "requires": {
+ "is-extendable": "^0.1.0"
+ }
+ }
+ }
+ },
+ "find-up": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+ "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+ "dev": true,
+ "requires": {
+ "locate-path": "^2.0.0"
+ }
+ },
+ "fsevents": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
+ "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "nan": "^2.12.1",
+ "node-pre-gyp": "^0.12.0"
+ },
+ "dependencies": {
+ "abbrev": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "ansi-regex": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true
+ },
+ "aproba": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "are-we-there-yet": {
+ "version": "1.1.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "delegates": "^1.0.0",
+ "readable-stream": "^2.0.6"
+ }
+ },
+ "balanced-match": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true
+ },
+ "brace-expansion": {
+ "version": "1.1.11",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "chownr": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "code-point-at": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "concat-map": {
+ "version": "0.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "console-control-strings": {
+ "version": "1.1.0",
+ "bundled": true,
+ "dev": true
+ },
+ "core-util-is": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "debug": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "deep-extend": {
+ "version": "0.6.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "delegates": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "detect-libc": {
+ "version": "1.0.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "fs-minipass": {
+ "version": "1.2.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "fs.realpath": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "gauge": {
+ "version": "2.7.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "aproba": "^1.0.3",
+ "console-control-strings": "^1.0.0",
+ "has-unicode": "^2.0.0",
+ "object-assign": "^4.1.0",
+ "signal-exit": "^3.0.0",
+ "string-width": "^1.0.1",
+ "strip-ansi": "^3.0.1",
+ "wide-align": "^1.1.0"
+ }
+ },
+ "glob": {
+ "version": "7.1.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "fs.realpath": "^1.0.0",
+ "inflight": "^1.0.4",
+ "inherits": "2",
+ "minimatch": "^3.0.4",
+ "once": "^1.3.0",
+ "path-is-absolute": "^1.0.0"
+ }
+ },
+ "has-unicode": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "iconv-lite": {
+ "version": "0.4.24",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ }
+ },
+ "ignore-walk": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minimatch": "^3.0.4"
+ }
+ },
+ "inflight": {
+ "version": "1.0.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "inherits": {
+ "version": "2.0.3",
+ "bundled": true,
+ "dev": true
+ },
+ "ini": {
+ "version": "1.3.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "is-fullwidth-code-point": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "number-is-nan": "^1.0.0"
+ }
+ },
+ "isarray": {
+ "version": "1.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "minimatch": {
+ "version": "3.0.4",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "brace-expansion": "^1.1.7"
+ }
+ },
+ "minimist": {
+ "version": "0.0.8",
+ "bundled": true,
+ "dev": true
+ },
+ "minipass": {
+ "version": "2.3.5",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.0"
+ }
+ },
+ "minizlib": {
+ "version": "1.2.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "minipass": "^2.2.1"
+ }
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "ms": {
+ "version": "2.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "needle": {
+ "version": "2.3.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "debug": "^4.1.0",
+ "iconv-lite": "^0.4.4",
+ "sax": "^1.2.4"
+ }
+ },
+ "node-pre-gyp": {
+ "version": "0.12.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "detect-libc": "^1.0.2",
+ "mkdirp": "^0.5.1",
+ "needle": "^2.2.1",
+ "nopt": "^4.0.1",
+ "npm-packlist": "^1.1.6",
+ "npmlog": "^4.0.2",
+ "rc": "^1.2.7",
+ "rimraf": "^2.6.1",
+ "semver": "^5.3.0",
+ "tar": "^4"
+ }
+ },
+ "nopt": {
+ "version": "4.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "abbrev": "1",
+ "osenv": "^0.1.4"
+ }
+ },
+ "npm-bundled": {
+ "version": "1.0.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "npm-packlist": {
+ "version": "1.4.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "ignore-walk": "^3.0.1",
+ "npm-bundled": "^1.0.1"
+ }
+ },
+ "npmlog": {
+ "version": "4.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "are-we-there-yet": "~1.1.2",
+ "console-control-strings": "~1.1.0",
+ "gauge": "~2.7.3",
+ "set-blocking": "~2.0.0"
+ }
+ },
+ "number-is-nan": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true
+ },
+ "object-assign": {
+ "version": "4.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "once": {
+ "version": "1.4.0",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "wrappy": "1"
+ }
+ },
+ "os-homedir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "os-tmpdir": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "osenv": {
+ "version": "0.1.5",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "os-homedir": "^1.0.0",
+ "os-tmpdir": "^1.0.0"
+ }
+ },
+ "path-is-absolute": {
+ "version": "1.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "process-nextick-args": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "rc": {
+ "version": "1.2.8",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "deep-extend": "^0.6.0",
+ "ini": "~1.3.0",
+ "minimist": "^1.2.0",
+ "strip-json-comments": "~2.0.1"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ }
+ }
+ },
+ "readable-stream": {
+ "version": "2.3.6",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "rimraf": {
+ "version": "2.6.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "glob": "^7.1.3"
+ }
+ },
+ "safe-buffer": {
+ "version": "5.1.2",
+ "bundled": true,
+ "dev": true
+ },
+ "safer-buffer": {
+ "version": "2.1.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "sax": {
+ "version": "1.2.4",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "semver": {
+ "version": "5.7.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "set-blocking": {
+ "version": "2.0.0",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "signal-exit": {
+ "version": "3.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "string-width": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "code-point-at": "^1.0.0",
+ "is-fullwidth-code-point": "^1.0.0",
+ "strip-ansi": "^3.0.0"
+ }
+ },
+ "string_decoder": {
+ "version": "1.1.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "strip-ansi": {
+ "version": "3.0.1",
+ "bundled": true,
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^2.0.0"
+ }
+ },
+ "strip-json-comments": {
+ "version": "2.0.1",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "tar": {
+ "version": "4.4.8",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "chownr": "^1.1.1",
+ "fs-minipass": "^1.2.5",
+ "minipass": "^2.3.4",
+ "minizlib": "^1.1.1",
+ "mkdirp": "^0.5.0",
+ "safe-buffer": "^5.1.2",
+ "yallist": "^3.0.2"
+ }
+ },
+ "util-deprecate": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true,
+ "optional": true
+ },
+ "wide-align": {
+ "version": "1.1.3",
+ "bundled": true,
+ "dev": true,
+ "optional": true,
+ "requires": {
+ "string-width": "^1.0.2 || 2"
+ }
+ },
+ "wrappy": {
+ "version": "1.0.2",
+ "bundled": true,
+ "dev": true
+ },
+ "yallist": {
+ "version": "3.0.3",
+ "bundled": true,
+ "dev": true
+ }
+ }
+ },
+ "glob-parent": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz",
+ "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=",
+ "dev": true,
+ "requires": {
+ "is-glob": "^3.1.0",
+ "path-dirname": "^1.0.0"
+ },
+ "dependencies": {
+ "is-glob": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz",
+ "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.0"
+ }
+ }
+ }
+ },
+ "is-binary-path": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz",
+ "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=",
+ "dev": true,
+ "requires": {
+ "binary-extensions": "^1.0.0"
+ }
+ },
+ "is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=",
+ "dev": true
+ },
+ "is-glob": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz",
+ "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==",
+ "dev": true,
+ "requires": {
+ "is-extglob": "^2.1.1"
+ }
+ },
+ "is-number": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz",
+ "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=",
+ "dev": true,
+ "requires": {
+ "kind-of": "^3.0.2"
+ },
+ "dependencies": {
+ "kind-of": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
+ "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=",
+ "dev": true,
+ "requires": {
+ "is-buffer": "^1.1.5"
+ }
+ }
+ }
+ },
+ "locate-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
+ "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
+ "dev": true,
+ "requires": {
+ "p-locate": "^2.0.0",
+ "path-exists": "^3.0.0"
+ }
+ },
+ "memory-fs": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz",
+ "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=",
+ "dev": true,
+ "requires": {
+ "errno": "^0.1.3",
+ "readable-stream": "^2.0.1"
+ }
+ },
+ "micromatch": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz",
+ "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==",
+ "dev": true,
+ "requires": {
+ "arr-diff": "^4.0.0",
+ "array-unique": "^0.3.2",
+ "braces": "^2.3.1",
+ "define-property": "^2.0.2",
+ "extend-shallow": "^3.0.2",
+ "extglob": "^2.0.4",
+ "fragment-cache": "^0.2.1",
+ "kind-of": "^6.0.2",
+ "nanomatch": "^1.2.9",
+ "object.pick": "^1.3.0",
+ "regex-not": "^1.0.0",
+ "snapdragon": "^0.8.1",
+ "to-regex": "^3.0.2"
+ }
+ },
+ "normalize-path": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz",
+ "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=",
+ "dev": true,
+ "requires": {
+ "remove-trailing-separator": "^1.0.1"
+ }
+ },
+ "p-limit": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
+ "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
+ "dev": true,
+ "requires": {
+ "p-try": "^1.0.0"
+ }
+ },
+ "p-locate": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
+ "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
+ "dev": true,
+ "requires": {
+ "p-limit": "^1.1.0"
+ }
+ },
+ "p-try": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
+ "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
+ "dev": true
+ },
+ "readdirp": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
+ "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.11",
+ "micromatch": "^3.1.10",
+ "readable-stream": "^2.0.2"
+ }
+ },
+ "strip-ansi": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+ "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+ "dev": true,
+ "requires": {
+ "ansi-regex": "^3.0.0"
+ }
+ },
+ "to-regex-range": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz",
+ "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=",
+ "dev": true,
+ "requires": {
+ "is-number": "^3.0.0",
+ "repeat-string": "^1.6.1"
+ }
+ },
+ "y18n": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+ "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE=",
+ "dev": true
+ },
+ "yargs": {
+ "version": "11.1.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-11.1.1.tgz",
+ "integrity": "sha512-PRU7gJrJaXv3q3yQZ/+/X6KBswZiaQ+zOmdprZcouPYtQgvNU35i+68M4b1ZHLZtYFT5QObFLV+ZkmJYcwKdiw==",
+ "dev": true,
+ "requires": {
+ "cliui": "^4.0.0",
+ "decamelize": "^1.1.1",
+ "find-up": "^2.1.0",
+ "get-caller-file": "^1.0.1",
+ "os-locale": "^3.1.0",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^1.0.1",
+ "set-blocking": "^2.0.0",
+ "string-width": "^2.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^3.2.1",
+ "yargs-parser": "^9.0.2"
+ }
+ },
+ "yargs-parser": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-9.0.2.tgz",
+ "integrity": "sha1-nM9qQ0YP5O1Aqbto9I1DuKaMwHc=",
+ "dev": true,
+ "requires": {
+ "camelcase": "^4.1.0"
+ }
+ }
+ }
+ },
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
@@ -8685,6 +9823,24 @@
"lower-case": "^1.1.1"
}
},
+ "node-environment-flags": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz",
+ "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==",
+ "dev": true,
+ "requires": {
+ "object.getownpropertydescriptors": "^2.0.3",
+ "semver": "^5.7.0"
+ },
+ "dependencies": {
+ "semver": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
+ "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
+ "dev": true
+ }
+ }
+ },
"node-forge": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.9.0.tgz",
@@ -8860,6 +10016,12 @@
}
}
},
+ "nodent-runtime": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/nodent-runtime/-/nodent-runtime-3.2.1.tgz",
+ "integrity": "sha512-7Ws63oC+215smeKJQCxzrK21VFVlCFBkwl0MOObt0HOpVQXs3u483sAmtkF33nNqZ5rSOQjB76fgyPBmAUrtCA==",
+ "dev": true
+ },
"nopt": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz",
@@ -9421,6 +10583,12 @@
}
}
},
+ "pathval": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
+ "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
+ "dev": true
+ },
"pbkdf2": {
"version": "3.0.17",
"resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz",
@@ -10390,6 +11558,12 @@
"integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==",
"dev": true
},
+ "ramda": {
+ "version": "0.26.1",
+ "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz",
+ "integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==",
+ "dev": true
+ },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -12575,6 +13749,12 @@
"prelude-ls": "~1.1.2"
}
},
+ "type-detect": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz",
+ "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==",
+ "dev": true
+ },
"type-fest": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz",
@@ -15305,6 +16485,81 @@
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
+ },
+ "yargs-unparser": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz",
+ "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==",
+ "dev": true,
+ "requires": {
+ "flat": "^4.1.0",
+ "lodash": "^4.17.15",
+ "yargs": "^13.3.0"
+ },
+ "dependencies": {
+ "cliui": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz",
+ "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==",
+ "dev": true,
+ "requires": {
+ "string-width": "^3.1.0",
+ "strip-ansi": "^5.2.0",
+ "wrap-ansi": "^5.1.0"
+ }
+ },
+ "get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true
+ },
+ "require-main-filename": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
+ "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
+ "dev": true
+ },
+ "string-width": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz",
+ "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==",
+ "dev": true,
+ "requires": {
+ "emoji-regex": "^7.0.1",
+ "is-fullwidth-code-point": "^2.0.0",
+ "strip-ansi": "^5.1.0"
+ }
+ },
+ "yargs": {
+ "version": "13.3.0",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
+ "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
+ "dev": true,
+ "requires": {
+ "cliui": "^5.0.0",
+ "find-up": "^3.0.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^3.0.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^13.1.1"
+ }
+ },
+ "yargs-parser": {
+ "version": "13.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz",
+ "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==",
+ "dev": true,
+ "requires": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ }
+ }
+ }
}
}
}
diff --git a/webui/package.json b/webui/package.json
index 9280222b6..423cc8e66 100644
--- a/webui/package.json
+++ b/webui/package.json
@@ -8,7 +8,7 @@
"scripts": {
"transfer": "node dev/scripts/transfer.js",
"lint": "eslint --ext .js,.vue src",
- "test": "echo \"No test specified\" && exit 0",
+ "test-unit": "mocha-webpack --mode=production './src/**/*.spec.js'",
"dev": "export APP_ENV='development' && quasar dev -m pwa",
"build-quasar": "quasar build -m pwa",
"build-staging": "export NODE_ENV='production' && export APP_ENV='development' && npm run build-quasar",
@@ -20,8 +20,8 @@
"axios": "^0.19.0",
"bowser": "^2.5.2",
"chart.js": "^2.8.0",
- "dot-prop": "5.2.0",
- "lodash": "^4.17.15",
+ "dot-prop": "^5.2.0",
+ "lodash.isequal": "4.5.0",
"moment": "^2.24.0",
"quasar": "^1.4.4",
"vh-check": "^2.0.5",
@@ -31,11 +31,16 @@
"devDependencies": {
"@quasar/app": "^1.2.4",
"@vue/eslint-config-standard": "^4.0.0",
+ "@vue/test-utils": "^1.0.0-beta.29",
"babel-eslint": "^10.0.1",
+ "chai": "4.2.0",
"eslint": "^5.10.0",
"eslint-loader": "^2.1.1",
"eslint-plugin-prettier": "3.1.1",
+ "eslint-plugin-mocha": "6.2.1",
"eslint-plugin-vue": "^5.0.0",
+ "mocha": "^6.2.2",
+ "mocha-webpack": "^2.0.0-beta.0",
"node-sass": "^4.12.0",
"prettier": "1.19.1",
"sass-loader": "^7.1.0"
diff --git a/webui/quasar.conf.js b/webui/quasar.conf.js
index 1be691cf4..abe9a24ff 100644
--- a/webui/quasar.conf.js
+++ b/webui/quasar.conf.js
@@ -7,7 +7,6 @@ module.exports = function (ctx) {
// --> boot files are part of "main.js"
boot: [
'_globals',
- 'lodash',
'api',
'_hacks',
'_init'
diff --git a/webui/src/_helpers/Errors.js b/webui/src/_helpers/Errors.js
index d1bc43f1e..749885076 100644
--- a/webui/src/_helpers/Errors.js
+++ b/webui/src/_helpers/Errors.js
@@ -1,5 +1,4 @@
import { Notify } from 'quasar'
-import { APP } from './APP'
class Errors {
// Getters
@@ -12,7 +11,7 @@ class Errors {
// ------------------------------------------------------------------------
static showError (body) {
- body = APP._.isString(body) ? JSON.parse(body) : body
+ body = typeof body === 'string' ? JSON.parse(body) : body
Notify.create({
color: 'negative',
position: 'top',
diff --git a/webui/src/_helpers/Helps.js b/webui/src/_helpers/Helps.js
index 0b378efa4..b8ba03d9e 100644
--- a/webui/src/_helpers/Helps.js
+++ b/webui/src/_helpers/Helps.js
@@ -1,4 +1,4 @@
-import { APP } from './APP'
+import { get } from 'dot-prop'
class Helps {
// Getters
@@ -11,7 +11,7 @@ class Helps {
// ------------------------------------------------------------------------
static get (obj, prop, def = undefined) {
- return APP._.get(obj, prop, def)
+ return get(obj, prop, def)
}
static hasIn (obj, prop) {
diff --git a/webui/src/_helpers/Mutations.js b/webui/src/_helpers/Mutations.js
new file mode 100644
index 000000000..71faeec30
--- /dev/null
+++ b/webui/src/_helpers/Mutations.js
@@ -0,0 +1,44 @@
+import { set, get } from 'dot-prop'
+
+export const withPagination = (type, opts = {}) => (state, data) => {
+ const { isSameContext, statePath } = opts
+ const currentState = get(state, statePath)
+
+ let newState
+
+ switch (type) {
+ case 'request':
+ newState = {
+ ...currentState,
+ loading: true
+ }
+ break
+ case 'success':
+ const { body, page } = data
+ newState = {
+ ...currentState,
+ items: [
+ ...(isSameContext && currentState.items && page !== 1 ? currentState.items : []),
+ ...(body.data || [])
+ ],
+ currentPage: page,
+ total: isSameContext && currentState.items && page !== 1
+ ? body.total + currentState.total
+ : body.total,
+ loading: false
+ }
+ break
+ case 'failure':
+ newState = {
+ ...currentState,
+ loading: false,
+ error: data,
+ endReached: data.message.includes('invalid request: page:')
+ }
+ break
+ }
+
+ if (newState) {
+ set(state, statePath, newState)
+ }
+}
diff --git a/webui/src/_mixins/Pagination.js b/webui/src/_mixins/Pagination.js
new file mode 100644
index 000000000..4c2091626
--- /dev/null
+++ b/webui/src/_mixins/Pagination.js
@@ -0,0 +1,74 @@
+import { get } from 'dot-prop'
+
+export default function PaginationMixin (opts = {}) {
+ const { pollingIntervalTime, rowsPerPage = 10 } = opts
+ let listLength = 0
+ let currentPage = 1
+ let currentLimit = rowsPerPage
+
+ return {
+ methods: {
+ fetchWithInterval () {
+ this.initFetch({ limit: listLength })
+ this.pollingInterval = setInterval(
+ () => {
+ this.fetchMore({
+ limit: Math.ceil(listLength / rowsPerPage) * rowsPerPage, // round up to multiple of rowsPerPage
+ refresh: true
+ })
+ },
+ pollingIntervalTime
+ )
+ },
+ fetchMore ({ page = 1, limit = rowsPerPage, refresh, ...params } = {}) {
+ if (page === currentPage && limit === currentLimit && !refresh) {
+ return Promise.resolve()
+ }
+
+ currentPage = page
+ currentLimit = limit || rowsPerPage
+
+ const fetchMethod = get(this, opts.fetchMethod)
+
+ return fetchMethod({
+ ...params,
+ page,
+ limit: limit || rowsPerPage
+ }).then(res => {
+ listLength = page > 1
+ ? listLength += res.data.length
+ : res.data.length
+ })
+ },
+ initFetch (params) {
+ const scrollerRef = get(this.$refs, opts.scrollerRef)
+
+ if (scrollerRef) {
+ scrollerRef.stop()
+ scrollerRef.reset()
+ }
+
+ return this.fetchMore({
+ page: 1,
+ refresh: true,
+ ...params
+ }).then(() => {
+ if (scrollerRef) {
+ scrollerRef.resume()
+ scrollerRef.poll()
+ }
+ })
+ }
+ },
+ mounted () {
+ if (pollingIntervalTime) {
+ this.fetchWithInterval()
+ } else {
+ this.fetchMore()
+ }
+ },
+ beforeDestroy () {
+ clearInterval(this.pollingInterval)
+ }
+ }
+}
diff --git a/webui/src/boot/lodash.js b/webui/src/boot/lodash.js
deleted file mode 100644
index 4cf69bdd0..000000000
--- a/webui/src/boot/lodash.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import lodash from 'lodash'
-
-import { APP } from '../_helpers/APP'
-
-export default async ({ app, Vue }) => {
- Vue.prototype.$_ = app._ = APP._ = lodash
-}
diff --git a/webui/src/components/_commons/ChartDoughnut.vue b/webui/src/components/_commons/ChartDoughnut.vue
index 060ca41f8..24b8f1f60 100644
--- a/webui/src/components/_commons/ChartDoughnut.vue
+++ b/webui/src/components/_commons/ChartDoughnut.vue
@@ -1,5 +1,6 @@
diff --git a/webui/src/pages/tcp/Services.vue b/webui/src/pages/tcp/Services.vue
index 593c90c9f..e904f5e2e 100644
--- a/webui/src/pages/tcp/Services.vue
+++ b/webui/src/pages/tcp/Services.vue
@@ -27,13 +27,21 @@
diff --git a/webui/src/store/http/mutations.js b/webui/src/store/http/mutations.js
index b4c7b589f..484a90d4a 100644
--- a/webui/src/store/http/mutations.js
+++ b/webui/src/store/http/mutations.js
@@ -1,37 +1,33 @@
+import { withPagination } from '../../_helpers/Mutations'
+
// ----------------------------
// Get All Routers
// ----------------------------
export function getAllRoutersRequest (state) {
- state.allRouters.loading = true
+ withPagination('request', { statePath: 'allRouters' })(state)
}
export function getAllRoutersSuccess (state, data) {
- const { body, query = '', status = '', page } = data
+ const { query = '', status = '' } = data
const currentState = state.allRouters
+ const { currentQuery = '', currentStatus = '' } = currentState
- const isSameContext = currentState.currentQuery === query && currentState.currentStatus === status
+ const isSameContext = currentQuery === query && currentStatus === status
state.allRouters = {
...state.allRouters,
- items: [
- ...(isSameContext && currentState.items && page !== 1 ? currentState.items : []),
- ...(body.data || [])
- ],
- currentPage: page,
- total: body.total,
- loading: false,
currentQuery: query,
currentStatus: status
}
+
+ withPagination('success', {
+ isSameContext,
+ statePath: 'allRouters'
+ })(state, data)
}
export function getAllRoutersFailure (state, error) {
- state.allRouters = {
- ...state.allRouters,
- loading: false,
- error,
- endReached: error.message.includes('invalid request: page:')
- }
+ withPagination('failure', { statePath: 'allRouters' })(state, error)
}
export function getAllRoutersClear (state) {
@@ -61,36 +57,29 @@ export function getRouterByNameClear (state) {
// Get All Services
// ----------------------------
export function getAllServicesRequest (state) {
- state.allServices.loading = true
+ withPagination('request', { statePath: 'allServices' })(state)
}
export function getAllServicesSuccess (state, data) {
- const { body, query = '', status = '', page } = data
+ const { query = '', status = '' } = data
const currentState = state.allServices
const isSameContext = currentState.currentQuery === query && currentState.currentStatus === status
state.allServices = {
...state.allServices,
- items: [
- ...(isSameContext && currentState.items && page !== 1 ? currentState.items : []),
- ...(body.data || [])
- ],
- currentPage: page,
- total: body.total,
- loading: false,
currentQuery: query,
currentStatus: status
}
+
+ withPagination('success', {
+ isSameContext,
+ statePath: 'allServices'
+ })(state, data)
}
export function getAllServicesFailure (state, error) {
- state.allServices = {
- ...state.allServices,
- loading: false,
- error,
- endReached: error.message.includes('invalid request: page:')
- }
+ withPagination('failure', { statePath: 'allServices' })(state, error)
}
export function getAllServicesClear (state) {
@@ -120,36 +109,29 @@ export function getServiceByNameClear (state) {
// Get All Middlewares
// ----------------------------
export function getAllMiddlewaresRequest (state) {
- state.allMiddlewares.loading = true
+ withPagination('request', { statePath: 'allMiddlewares' })(state)
}
export function getAllMiddlewaresSuccess (state, data) {
- const { body, query = '', status = '', page } = data
+ const { query = '', status = '' } = data
const currentState = state.allMiddlewares
const isSameContext = currentState.currentQuery === query && currentState.currentStatus === status
state.allMiddlewares = {
...state.allMiddlewares,
- items: [
- ...(isSameContext && currentState.items && page !== 1 ? currentState.items : []),
- ...(body.data || [])
- ],
- currentPage: page,
- total: body.total,
- loading: false,
currentQuery: query,
currentStatus: status
}
+
+ withPagination('success', {
+ isSameContext,
+ statePath: 'allMiddlewares'
+ })(state, data)
}
export function getAllMiddlewaresFailure (state, error) {
- state.allMiddlewares = {
- ...state.allMiddlewares,
- loading: false,
- error,
- endReached: error.message.includes('invalid request: page:')
- }
+ withPagination('failure', { statePath: 'allMiddlewares' })(state, error)
}
export function getAllMiddlewaresClear (state) {
diff --git a/webui/src/store/http/mutations.spec.js b/webui/src/store/http/mutations.spec.js
new file mode 100644
index 000000000..890fd1406
--- /dev/null
+++ b/webui/src/store/http/mutations.spec.js
@@ -0,0 +1,292 @@
+import { expect } from 'chai'
+import store from './index.js'
+
+const {
+ getAllRoutersRequest,
+ getAllRoutersSuccess,
+ getAllRoutersFailure,
+ getAllServicesRequest,
+ getAllServicesSuccess,
+ getAllServicesFailure,
+ getAllMiddlewaresRequest,
+ getAllMiddlewaresSuccess,
+ getAllMiddlewaresFailure
+} = store.mutations
+
+describe('http mutations', function () {
+ /* Routers */
+ describe('http routers mutations', function () {
+ it('getAllRoutersRequest', function () {
+ const state = {
+ allRouters: {
+ items: [{}, {}, {}]
+ }
+ }
+
+ getAllRoutersRequest(state)
+
+ expect(state.allRouters.loading).to.equal(true)
+ expect(state.allRouters.items.length).to.equal(3)
+ })
+
+ it('getAllRoutersSuccess page 1', function () {
+ const state = {
+ allRouters: {
+ loading: true
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{}, {}, {}],
+ total: 3
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 1
+ }
+
+ getAllRoutersSuccess(state, data)
+
+ expect(state.allRouters.loading).to.equal(false)
+ expect(state.allRouters.total).to.equal(3)
+ expect(state.allRouters.items.length).to.equal(3)
+ expect(state.allRouters.currentPage).to.equal(1)
+ expect(state.allRouters.currentQuery).to.equal('test query')
+ expect(state.allRouters.currentStatus).to.equal('warning')
+ })
+
+ it('getAllRoutersSuccess page 2', function () {
+ const state = {
+ allRouters: {
+ loading: false,
+ items: [{ id: 1 }, { id: 2 }, { id: 3 }],
+ total: 3,
+ currentPage: 1,
+ currentQuery: 'test query',
+ currentStatus: 'warning'
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{ id: 4 }, { id: 5 }, { id: 6 }, { id: 7 }],
+ total: 4
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 2
+ }
+
+ getAllRoutersSuccess(state, data)
+
+ expect(state.allRouters.loading).to.equal(false)
+ expect(state.allRouters.total).to.equal(7)
+ expect(state.allRouters.items.length).to.equal(7)
+ expect(state.allRouters.currentPage).to.equal(2)
+ expect(state.allRouters.currentQuery).to.equal('test query')
+ expect(state.allRouters.currentStatus).to.equal('warning')
+ })
+
+ it('getAllRoutersFailing', function () {
+ const state = {
+ allRouters: {
+ items: [{}, {}, {}],
+ loading: true
+ }
+ }
+
+ const error = { message: 'invalid request: page: 3, per_page: 10' }
+
+ getAllRoutersFailure(state, error)
+
+ expect(state.allRouters.loading).to.equal(false)
+ expect(state.allRouters.endReached).to.equal(true)
+ expect(state.allRouters.items.length).to.equal(3)
+ })
+ })
+
+ /* Services */
+ describe('http services mutations', function () {
+ it('getAllServicesRequest', function () {
+ const state = {
+ allServices: {
+ items: [{}, {}, {}]
+ }
+ }
+
+ getAllServicesRequest(state)
+
+ expect(state.allServices.loading).to.equal(true)
+ expect(state.allServices.items.length).to.equal(3)
+ })
+
+ it('getAllServicesSuccess page 1', function () {
+ const state = {
+ allServices: {
+ loading: true
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{}, {}, {}],
+ total: 3
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 1
+ }
+
+ getAllServicesSuccess(state, data)
+
+ expect(state.allServices.loading).to.equal(false)
+ expect(state.allServices.total).to.equal(3)
+ expect(state.allServices.items.length).to.equal(3)
+ expect(state.allServices.currentPage).to.equal(1)
+ expect(state.allServices.currentQuery).to.equal('test query')
+ expect(state.allServices.currentStatus).to.equal('warning')
+ })
+
+ it('getAllServicesSuccess page 2', function () {
+ const state = {
+ allServices: {
+ loading: false,
+ items: [{ id: 1 }, { id: 2 }, { id: 3 }],
+ total: 3,
+ currentPage: 1,
+ currentQuery: 'test query',
+ currentStatus: 'warning'
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{ id: 4 }, { id: 5 }, { id: 6 }, { id: 7 }],
+ total: 4
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 2
+ }
+
+ getAllServicesSuccess(state, data)
+
+ expect(state.allServices.loading).to.equal(false)
+ expect(state.allServices.total).to.equal(7)
+ expect(state.allServices.items.length).to.equal(7)
+ expect(state.allServices.currentPage).to.equal(2)
+ expect(state.allServices.currentQuery).to.equal('test query')
+ expect(state.allServices.currentStatus).to.equal('warning')
+ })
+
+ it('getAllServicesFailing', function () {
+ const state = {
+ allServices: {
+ items: [{}, {}, {}],
+ loading: true
+ }
+ }
+
+ const error = { message: 'invalid request: page: 3, per_page: 10' }
+
+ getAllServicesFailure(state, error)
+
+ expect(state.allServices.loading).to.equal(false)
+ expect(state.allServices.endReached).to.equal(true)
+ expect(state.allServices.items.length).to.equal(3)
+ })
+ })
+
+ /* Middlewares */
+ describe('http middlewares mutations', function () {
+ it('getAllMiddlewaresRequest', function () {
+ const state = {
+ allMiddlewares: {
+ items: [{}, {}, {}]
+ }
+ }
+
+ getAllMiddlewaresRequest(state)
+
+ expect(state.allMiddlewares.loading).to.equal(true)
+ expect(state.allMiddlewares.items.length).to.equal(3)
+ })
+
+ it('getAllMiddlewaresSuccess page 1', function () {
+ const state = {
+ allMiddlewares: {
+ loading: true
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{}, {}, {}],
+ total: 3
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 1
+ }
+
+ getAllMiddlewaresSuccess(state, data)
+
+ expect(state.allMiddlewares.loading).to.equal(false)
+ expect(state.allMiddlewares.total).to.equal(3)
+ expect(state.allMiddlewares.items.length).to.equal(3)
+ expect(state.allMiddlewares.currentPage).to.equal(1)
+ expect(state.allMiddlewares.currentQuery).to.equal('test query')
+ expect(state.allMiddlewares.currentStatus).to.equal('warning')
+ })
+
+ it('getAllMiddlewaresSuccess page 2', function () {
+ const state = {
+ allMiddlewares: {
+ loading: false,
+ items: [{ id: 1 }, { id: 2 }, { id: 3 }],
+ total: 3,
+ currentPage: 1,
+ currentQuery: 'test query',
+ currentStatus: 'warning'
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{ id: 4 }, { id: 5 }, { id: 6 }, { id: 7 }],
+ total: 4
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 2
+ }
+
+ getAllMiddlewaresSuccess(state, data)
+
+ expect(state.allMiddlewares.loading).to.equal(false)
+ expect(state.allMiddlewares.total).to.equal(7)
+ expect(state.allMiddlewares.items.length).to.equal(7)
+ expect(state.allMiddlewares.currentPage).to.equal(2)
+ expect(state.allMiddlewares.currentQuery).to.equal('test query')
+ expect(state.allMiddlewares.currentStatus).to.equal('warning')
+ })
+
+ it('getAllMiddlewaresFailing', function () {
+ const state = {
+ allMiddlewares: {
+ items: [{}, {}, {}],
+ loading: true
+ }
+ }
+
+ const error = { message: 'invalid request: page: 3, per_page: 10' }
+
+ getAllMiddlewaresFailure(state, error)
+
+ expect(state.allMiddlewares.loading).to.equal(false)
+ expect(state.allMiddlewares.endReached).to.equal(true)
+ expect(state.allMiddlewares.items.length).to.equal(3)
+ })
+ })
+})
diff --git a/webui/src/store/tcp/mutations.js b/webui/src/store/tcp/mutations.js
index 4ad95df19..69df42013 100644
--- a/webui/src/store/tcp/mutations.js
+++ b/webui/src/store/tcp/mutations.js
@@ -1,37 +1,32 @@
+import { withPagination } from '../../_helpers/Mutations'
+
// ----------------------------
// Get All Routers
// ----------------------------
export function getAllRoutersRequest (state) {
- state.allRouters.loading = true
+ withPagination('request', { statePath: 'allRouters' })(state)
}
export function getAllRoutersSuccess (state, data) {
- const { body, query = '', status = '', page } = data
+ const { query = '', status = '' } = data
const currentState = state.allRouters
const isSameContext = currentState.currentQuery === query && currentState.currentStatus === status
state.allRouters = {
...state.allRouters,
- items: [
- ...(isSameContext && currentState.items && page !== 1 ? currentState.items : []),
- ...(body.data || [])
- ],
- currentPage: page,
- total: body.total,
- loading: false,
currentQuery: query,
currentStatus: status
}
+
+ withPagination('success', {
+ isSameContext,
+ statePath: 'allRouters'
+ })(state, data)
}
export function getAllRoutersFailure (state, error) {
- state.allRouters = {
- ...state.allRouters,
- loading: false,
- error,
- endReached: error.message.includes('invalid request: page:')
- }
+ withPagination('failure', { statePath: 'allRouters' })(state, error)
}
export function getAllRoutersClear (state) {
@@ -61,36 +56,29 @@ export function getRouterByNameClear (state) {
// Get All Services
// ----------------------------
export function getAllServicesRequest (state) {
- state.allServices.loading = true
+ withPagination('request', { statePath: 'allServices' })(state)
}
export function getAllServicesSuccess (state, data) {
- const { body, query = '', status = '', page } = data
+ const { query = '', status = '' } = data
const currentState = state.allServices
const isSameContext = currentState.currentQuery === query && currentState.currentStatus === status
state.allServices = {
...state.allServices,
- items: [
- ...(isSameContext && currentState.items && page !== 1 ? currentState.items : []),
- ...(body.data || [])
- ],
- currentPage: page,
- total: body.total,
- loading: false,
currentQuery: query,
currentStatus: status
}
+
+ withPagination('success', {
+ isSameContext,
+ statePath: 'allServices'
+ })(state, data)
}
export function getAllServicesFailure (state, error) {
- state.allServices = {
- ...state.allServices,
- loading: false,
- error,
- endReached: error.message.includes('invalid request: page:')
- }
+ withPagination('failure', { statePath: 'allServices' })(state, error)
}
export function getAllServicesClear (state) {
diff --git a/webui/src/store/tcp/mutations.spec.js b/webui/src/store/tcp/mutations.spec.js
new file mode 100644
index 000000000..cd955b806
--- /dev/null
+++ b/webui/src/store/tcp/mutations.spec.js
@@ -0,0 +1,197 @@
+import { expect } from 'chai'
+import store from './index.js'
+
+const {
+ getAllRoutersRequest,
+ getAllRoutersSuccess,
+ getAllRoutersFailure,
+ getAllServicesRequest,
+ getAllServicesSuccess,
+ getAllServicesFailure
+} = store.mutations
+
+describe('tcp mutations', function () {
+ /* Routers */
+ describe('tcp routers mutations', function () {
+ it('getAllRoutersRequest', function () {
+ const state = {
+ allRouters: {
+ items: [{}, {}, {}]
+ }
+ }
+
+ getAllRoutersRequest(state)
+
+ expect(state.allRouters.loading).to.equal(true)
+ expect(state.allRouters.items.length).to.equal(3)
+ })
+
+ it('getAllRoutersSuccess page 1', function () {
+ const state = {
+ allRouters: {
+ loading: true
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{}, {}, {}],
+ total: 3
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 1
+ }
+
+ getAllRoutersSuccess(state, data)
+
+ expect(state.allRouters.loading).to.equal(false)
+ expect(state.allRouters.total).to.equal(3)
+ expect(state.allRouters.items.length).to.equal(3)
+ expect(state.allRouters.currentPage).to.equal(1)
+ expect(state.allRouters.currentQuery).to.equal('test query')
+ expect(state.allRouters.currentStatus).to.equal('warning')
+ })
+
+ it('getAllRoutersSuccess page 2', function () {
+ const state = {
+ allRouters: {
+ loading: false,
+ items: [{ id: 1 }, { id: 2 }, { id: 3 }],
+ total: 3,
+ currentPage: 1,
+ currentQuery: 'test query',
+ currentStatus: 'warning'
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{ id: 4 }, { id: 5 }, { id: 6 }, { id: 7 }],
+ total: 4
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 2
+ }
+
+ getAllRoutersSuccess(state, data)
+
+ expect(state.allRouters.loading).to.equal(false)
+ expect(state.allRouters.total).to.equal(7)
+ expect(state.allRouters.items.length).to.equal(7)
+ expect(state.allRouters.currentPage).to.equal(2)
+ expect(state.allRouters.currentQuery).to.equal('test query')
+ expect(state.allRouters.currentStatus).to.equal('warning')
+ })
+
+ it('getAllRoutersFailing', function () {
+ const state = {
+ allRouters: {
+ items: [{}, {}, {}],
+ loading: true
+ }
+ }
+
+ const error = { message: 'invalid request: page: 3, per_page: 10' }
+
+ getAllRoutersFailure(state, error)
+
+ expect(state.allRouters.loading).to.equal(false)
+ expect(state.allRouters.endReached).to.equal(true)
+ expect(state.allRouters.items.length).to.equal(3)
+ })
+ })
+
+ /* Services */
+ describe('tcp services mutations', function () {
+ it('getAllServicesRequest', function () {
+ const state = {
+ allServices: {
+ items: [{}, {}, {}]
+ }
+ }
+
+ getAllServicesRequest(state)
+
+ expect(state.allServices.loading).to.equal(true)
+ expect(state.allServices.items.length).to.equal(3)
+ })
+
+ it('getAllServicesSuccess page 1', function () {
+ const state = {
+ allServices: {
+ loading: true
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{}, {}, {}],
+ total: 3
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 1
+ }
+
+ getAllServicesSuccess(state, data)
+
+ expect(state.allServices.loading).to.equal(false)
+ expect(state.allServices.total).to.equal(3)
+ expect(state.allServices.items.length).to.equal(3)
+ expect(state.allServices.currentPage).to.equal(1)
+ expect(state.allServices.currentQuery).to.equal('test query')
+ expect(state.allServices.currentStatus).to.equal('warning')
+ })
+
+ it('getAllServicesSuccess page 2', function () {
+ const state = {
+ allServices: {
+ loading: false,
+ items: [{ id: 1 }, { id: 2 }, { id: 3 }],
+ total: 3,
+ currentPage: 1,
+ currentQuery: 'test query',
+ currentStatus: 'warning'
+ }
+ }
+
+ const data = {
+ body: {
+ data: [{ id: 4 }, { id: 5 }, { id: 6 }, { id: 7 }],
+ total: 4
+ },
+ query: 'test query',
+ status: 'warning',
+ page: 2
+ }
+
+ getAllServicesSuccess(state, data)
+
+ expect(state.allServices.loading).to.equal(false)
+ expect(state.allServices.total).to.equal(7)
+ expect(state.allServices.items.length).to.equal(7)
+ expect(state.allServices.currentPage).to.equal(2)
+ expect(state.allServices.currentQuery).to.equal('test query')
+ expect(state.allServices.currentStatus).to.equal('warning')
+ })
+
+ it('getAllServicesFailing', function () {
+ const state = {
+ allServices: {
+ items: [{}, {}, {}],
+ loading: true
+ }
+ }
+
+ const error = { message: 'invalid request: page: 3, per_page: 10' }
+
+ getAllServicesFailure(state, error)
+
+ expect(state.allServices.loading).to.equal(false)
+ expect(state.allServices.endReached).to.equal(true)
+ expect(state.allServices.items.length).to.equal(3)
+ })
+ })
+})