diff --git a/package-lock.json b/package-lock.json index 926d72e..b6c365b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "mochawesome", - "version": "6.2.2", + "version": "7.0.0-alpha.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -451,6 +451,29 @@ "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", "dev": true }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@sinonjs/commons": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", @@ -492,12 +515,178 @@ "integrity": "sha1-HBJhu+qhCoBVu8XYq4S3sq/IRqA=", "dev": true }, + "@types/diff": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@types/diff/-/diff-5.0.1.tgz", + "integrity": "sha512-XIpxU6Qdvp1ZE6Kr3yrkv1qgUab0fyf4mHYvW8N3Bx3PCsbN6or1q9/q72cv5jIFWolaGH08U9XyYoLLIykyKQ==", + "dev": true + }, + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==" + }, + "@types/json-stringify-safe": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/json-stringify-safe/-/json-stringify-safe-5.0.0.tgz", + "integrity": "sha512-UUA1sH0RSRROdInuDOA1yoRzbi5xVFD1RHCoOvNRPTNwR8zBkJ/84PZ6NhKVDtKp0FTeIccJCdQz1X2aJPr4uw==", + "dev": true + }, + "@types/lodash": { + "version": "4.14.172", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.172.tgz", + "integrity": "sha512-/BHF5HAx3em7/KkzVKm3LrsD6HZAXuXO1AJZQ3cRRBZj4oHZDviWPYu0aEplAqDFNHZPW6d3G7KN+ONcCCC7pw==", + "dev": true + }, + "@types/mocha": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.0.0.tgz", + "integrity": "sha512-scN0hAWyLVAvLR9AyW7HoFF5sJZglyBsbPuHO4fv7JRvfmPBMfp1ozWqOf/e4wwPNxezBZXRfWzMb6iFLgEVRA==", + "dev": true + }, + "@types/node": { + "version": "16.7.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.7.1.tgz", + "integrity": "sha512-ncRdc45SoYJ2H4eWU9ReDfp3vtFqDYhjOsKlFFUDEn8V1Bgr2RjYal8YT5byfadWIRluhPFU6JiDOl0H6Sl87A==", + "dev": true + }, "@types/parse-json": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.29.3.tgz", + "integrity": "sha512-tBgfA3K/3TsZY46ROGvoRxQr1wBkclbVqRQep97MjVHJzcRBURRY3sNFqLk0/Xr//BY5hM9H2p/kp+6qim85SA==", + "requires": { + "@typescript-eslint/experimental-utils": "4.29.3", + "@typescript-eslint/scope-manager": "4.29.3", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.29.3.tgz", + "integrity": "sha512-ffIvbytTVWz+3keg+Sy94FG1QeOvmV9dP2YSdLFHw/ieLXWCa3U1TYu8IRCOpMv2/SPS8XqhM1+ou1YHsdzKrg==", + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.29.3", + "@typescript-eslint/types": "4.29.3", + "@typescript-eslint/typescript-estree": "4.29.3", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.29.3.tgz", + "integrity": "sha512-jrHOV5g2u8ROghmspKoW7pN8T/qUzk0+DITun0MELptvngtMrwUJ1tv5zMI04CYVEUsSrN4jV7AKSv+I0y0EfQ==", + "requires": { + "@typescript-eslint/scope-manager": "4.29.3", + "@typescript-eslint/types": "4.29.3", + "@typescript-eslint/typescript-estree": "4.29.3", + "debug": "^4.3.1" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + } + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.29.3.tgz", + "integrity": "sha512-x+w8BLXO7iWPkG5mEy9bA1iFRnk36p/goVlYobVWHyDw69YmaH9q6eA+Fgl7kYHmFvWlebUTUfhtIg4zbbl8PA==", + "requires": { + "@typescript-eslint/types": "4.29.3", + "@typescript-eslint/visitor-keys": "4.29.3" + } + }, + "@typescript-eslint/types": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.29.3.tgz", + "integrity": "sha512-s1eV1lKNgoIYLAl1JUba8NhULmf+jOmmeFO1G5MN/RBCyyzg4TIOfIOICVNC06lor+Xmy4FypIIhFiJXOknhIg==" + }, + "@typescript-eslint/typescript-estree": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.29.3.tgz", + "integrity": "sha512-45oQJA0bxna4O5TMwz55/TpgjX1YrAPOI/rb6kPgmdnemRZx/dB0rsx+Ku8jpDvqTxcE1C/qEbVHbS3h0hflag==", + "requires": { + "@typescript-eslint/types": "4.29.3", + "@typescript-eslint/visitor-keys": "4.29.3", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + }, + "dependencies": { + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "requires": { + "ms": "2.1.2" + } + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.29.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.29.3.tgz", + "integrity": "sha512-MGGfJvXT4asUTeVs0Q2m+sY63UsfnA+C/FDgBKV3itLBmM9H0u+URcneePtkd0at1YELmZK6HSolCqM4Fzs6yA==", + "requires": { + "@typescript-eslint/types": "4.29.3", + "eslint-visitor-keys": "^2.0.0" + } + }, "@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", @@ -575,9 +764,9 @@ } }, "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -608,6 +797,11 @@ "sprintf-js": "~1.0.2" } }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==" + }, "astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -640,7 +834,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -720,19 +913,30 @@ } }, "chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.2.tgz", + "integrity": "sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ==", "dev": true, "requires": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.3.1", - "glob-parent": "~5.1.0", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "clean-stack": { @@ -963,6 +1167,14 @@ "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "requires": { + "path-type": "^4.0.0" + } + }, "doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", @@ -1150,7 +1362,6 @@ "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, "requires": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -1176,8 +1387,7 @@ "eslint-visitor-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true + "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==" }, "espree": { "version": "7.3.1", @@ -1225,7 +1435,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, "requires": { "estraverse": "^5.2.0" }, @@ -1233,16 +1442,14 @@ "estraverse": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==" } } }, "estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" }, "esutils": { "version": "2.0.3", @@ -1273,6 +1480,42 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" + } + } + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -1285,6 +1528,14 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fastq": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.12.0.tgz", + "integrity": "sha512-VNX0QkHK3RsXVKr9KrlUv/FoTa0NdbYoHHl7uXHv2rzyHSlxjdNAKug2twd9luJxpcyNeAgf5iPPMutJO67Dfg==", + "requires": { + "reusify": "^1.0.4" + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -1317,7 +1568,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -1410,8 +1660,7 @@ "functional-red-black-tree": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" }, "gensync": { "version": "1.0.0-beta.1", @@ -1477,6 +1726,26 @@ "type-fest": "^0.8.1" } }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==" + } + } + }, "graceful-fs": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz", @@ -1589,8 +1858,7 @@ "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 + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" }, "is-fullwidth-code-point": { "version": "2.0.0", @@ -1601,7 +1869,6 @@ "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" } @@ -1609,8 +1876,7 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "is-obj": { "version": "1.0.1", @@ -1648,6 +1914,12 @@ "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", "dev": true }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, "is-windows": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", @@ -1989,10 +2261,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.flattendeep": { "version": "4.4.0", @@ -2006,26 +2277,11 @@ "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", "dev": true }, - "lodash.isempty": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.isempty/-/lodash.isempty-4.4.0.tgz", - "integrity": "sha1-b4bL7di+TsmHvpqvM8loTbGzHn4=" - }, "lodash.isfunction": { "version": "3.0.9", "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz", "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==" }, - "lodash.isobject": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/lodash.isobject/-/lodash.isobject-3.0.2.tgz", - "integrity": "sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=" - }, - "lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha1-1SfftUVuynzJu5XV2ur4i6VKVFE=" - }, "log-symbols": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", @@ -2119,7 +2375,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -2145,6 +2400,11 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" + }, "micromatch": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", @@ -2177,33 +2437,33 @@ "dev": true }, "mocha": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.3.0.tgz", - "integrity": "sha512-TQqyC89V1J/Vxx0DhJIXlq9gbbL9XFNdeLQ1+JsnZsVaSOV1z3tWfw0qZmQJGQRIfkvZcs7snQnZnOCKoldq1Q==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.1.0.tgz", + "integrity": "sha512-Kjg/XxYOFFUi0h/FwMOeb6RoroiZ+P1yOfya6NK7h3dNhahrJx1r2XIT3ge4ZQvJM86mdjNA+W5phqRQh7DwCg==", "dev": true, "requires": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", - "chokidar": "3.5.1", + "chokidar": "3.5.2", "debug": "4.3.1", "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.1.6", + "glob": "7.1.7", "growl": "1.10.5", "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", "minimatch": "3.0.4", "ms": "2.1.3", - "nanoid": "3.1.20", - "serialize-javascript": "5.0.1", + "nanoid": "3.1.23", + "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", "which": "2.0.2", "wide-align": "1.1.3", - "workerpool": "6.1.0", + "workerpool": "6.1.5", "yargs": "16.2.0", "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" @@ -2267,12 +2527,6 @@ } } }, - "diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true - }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2295,6 +2549,20 @@ "path-exists": "^4.0.0" } }, + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "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" + } + }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2308,9 +2576,9 @@ "dev": true }, "js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -2325,6 +2593,16 @@ "p-locate": "^5.0.0" } }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -2356,9 +2634,9 @@ "dev": true }, "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", + "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", "dev": true, "requires": { "emoji-regex": "^8.0.0", @@ -2387,9 +2665,9 @@ } }, "y18n": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.5.tgz", - "integrity": "sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg==", + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true }, "yargs": { @@ -2455,13 +2733,12 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", + "version": "3.1.23", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz", + "integrity": "sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw==", "dev": true }, "natural-compare": { @@ -2840,8 +3117,7 @@ "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==" }, "picomatch": { "version": "2.2.2", @@ -2967,6 +3243,11 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -2982,9 +3263,9 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { "picomatch": "^2.2.1" @@ -2993,8 +3274,7 @@ "regexpp": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==" }, "release-zalgo": { "version": "1.0.0", @@ -3046,6 +3326,11 @@ "signal-exit": "^3.0.2" } }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -3055,6 +3340,14 @@ "glob": "^7.1.3" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, "rxjs": { "version": "6.6.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", @@ -3083,9 +3376,9 @@ "dev": true }, "serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", "dev": true, "requires": { "randombytes": "^2.1.0" @@ -3208,6 +3501,11 @@ } } }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" + }, "slice-ansi": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", @@ -3465,7 +3763,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } @@ -3473,8 +3770,15 @@ "tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "requires": { + "tslib": "^1.8.1" + } }, "type-check": { "version": "0.4.0", @@ -3506,6 +3810,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", + "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "dev": true + }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", @@ -3593,9 +3903,9 @@ "dev": true }, "workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.5.tgz", + "integrity": "sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw==", "dev": true }, "wrap-ansi": { @@ -3649,8 +3959,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yaml": { "version": "1.10.0", diff --git a/package.json b/package.json index 2d181d6..fff6032 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,10 @@ { "name": "mochawesome", - "version": "6.2.2", + "version": "7.0.0-alpha.0", "description": "A gorgeous reporter for Mocha.js", "scripts": { "lint": "eslint src test*", + "dev": "mocha test-functional/simple --config test-functional/.mocharc.json", "test": "npm run lint && cross-env NODE_ENV=test nyc mocha --config test/.mocharc.json", "test:fn": "mocha test-functional --config test-functional/.mocharc.json", "test:par": "mocha test-parallel --parallel --config test-parallel/.mocharc.json", @@ -48,32 +49,37 @@ "branches": 90 }, "dependencies": { + "@typescript-eslint/eslint-plugin": "^4.29.3", + "@typescript-eslint/parser": "^4.29.3", "chalk": "^4.1.0", "diff": "^5.0.0", "json-stringify-safe": "^5.0.1", - "lodash.isempty": "^4.4.0", - "lodash.isfunction": "^3.0.9", - "lodash.isobject": "^3.0.2", - "lodash.isstring": "^4.0.1", + "lodash": "^4.17.21", "mochawesome-report-generator": "^5.2.0", "strip-ansi": "^6.0.0", "uuid": "^8.3.2" }, "peerDependencies": { - "mocha": ">=7" + "mocha": ">=9" }, "devDependencies": { + "@types/diff": "^5.0.1", + "@types/json-stringify-safe": "^5.0.0", + "@types/lodash": "^4.14.172", + "@types/mocha": "^9.0.0", + "@types/node": "^16.7.1", "cross-env": "^7.0.3", "eslint": "^7.20.0", "eslint-config-prettier": "^7.2.0", "husky": "^5.0.9", "lint-staged": "^10.5.4", - "mocha": "^8.3.0", + "mocha": "^9.1.0", "nyc": "^15.1.0", "prettier": "^2.2.1", "proxyquire": "^2.1.0", "should": "^13.2.3", - "sinon": "^9.2.4" + "sinon": "^9.2.4", + "typescript": "^4.3.5" }, "lint-staged": { "*.js": "eslint --cache --fix", diff --git a/src/addContext.js b/src/addContext.js deleted file mode 100644 index fa9ad22..0000000 --- a/src/addContext.js +++ /dev/null @@ -1,135 +0,0 @@ -const isObject = require('lodash.isobject'); -const isEmpty = require('lodash.isempty'); -const chalk = require('chalk'); -const stringify = require('json-stringify-safe'); - -const errorPrefix = 'Error adding context:'; -const ERRORS = { - INVALID_ARGS: `${errorPrefix} Invalid arguments.`, - INVALID_TEST: `${errorPrefix} Invalid test object.`, - INVALID_CONTEXT: ctx => { - const expected = - 'Expected a string or an object of shape { title: string, value: any } but saw:'; - return `${errorPrefix} ${expected}\n${stringify( - ctx, - (key, val) => (val === undefined ? 'undefined' : val), - 2 - )}`; - }, -}; - -/** - * HELPER FUNCTIONS - */ - -/* istanbul ignore next */ -function log(msg, level) { - const logMethod = console[level] || console.log; - let out = msg; - if (typeof msg === 'object') { - out = stringify(msg, null, 2); - } - logMethod(`[${chalk.gray('mochawesome')}] ${out}\n`); -} - -function _isValidContext(ctx) { - /* - * Context is valid if any of the following are true: - * 1. Type is string and it is not empty - * 2. Type is object and it has properties `title` and `value` and `title` is not empty - */ - if (!ctx) return false; - return ( - (typeof ctx === 'string' && !isEmpty(ctx)) || - (Object.hasOwnProperty.call(ctx, 'title') && - !isEmpty(ctx.title) && - Object.hasOwnProperty.call(ctx, 'value')) - ); -} - -/** - * Add context to the test object so it can - * be displayed in the mochawesome report - * - * @param {Object} test object - * @param {String|Object} context to add - * If context is an object, it must have the shape: - * { - * title: string that is used as context title in the report - * value: the context that is to be added - * } - * - * Usage: - * - * it('should test something', function () { - * someFunctionThatTestsCode(); - * - * addContext(this, 'some context to add'); - * - * addContext(this, { - * title: 'Expected number of something' - * value: 42 - * }); - * - * assert('something'); - * }); - * - */ - -const addContext = function (...args) { - // Check args to see if we should bother continuing - if (args.length !== 2 || !isObject(args[0])) { - log(ERRORS.INVALID_ARGS, 'error'); - return; - } - - const ctx = args[1]; - - // Ensure that context meets the requirements - if (!_isValidContext(ctx)) { - log(ERRORS.INVALID_CONTEXT(ctx), 'error'); - return; - } - - /* Context is valid, now get the test object - * If `addContext` is called from inside a hook the test object - * will be `.currentTest`, and the hook will be `.test`. - * Otherwise the test is just `.test` and `.currentTest` is undefined. - */ - const currentTest = args[0].currentTest; - const activeTest = args[0].test; - - /* For `before` and `after`, add the context to the hook, - * otherwise add it to the actual test. - */ - const isEachHook = - currentTest && /^"(?:before|after)\seach"/.test(activeTest.title); - const test = isEachHook ? currentTest : activeTest; - - if (!test) { - log(ERRORS.INVALID_TEST, 'error'); - return; - } - - /* If context is an object, and value is `undefined` - * change it to 'undefined' so it can be displayed - * correctly in the report - */ - if (ctx.title && ctx.value === undefined) { - ctx.value = 'undefined'; - } - - // Test doesn't already have context -> set it - if (!test.context) { - test.context = ctx; - } else if (Array.isArray(test.context)) { - // Test has context and context is an array -> push new context - test.context.push(ctx); - } else { - // Test has context and it is not an array -> make it an array, then push new context - test.context = [test.context]; - test.context.push(ctx); - } -}; - -module.exports = addContext; diff --git a/src/addContext.ts b/src/addContext.ts new file mode 100644 index 0000000..01f7d5a --- /dev/null +++ b/src/addContext.ts @@ -0,0 +1,117 @@ +import isObject from 'lodash/isobject'; +import jsonStringify from 'json-stringify-safe'; +import Logger from './logger'; + +const logger = new Logger(console); +const errorPrefix = 'Error adding context:'; +const ERRORS = { + INVALID_ARGS: `${errorPrefix} Invalid arguments.`, + INVALID_TEST: `${errorPrefix} Invalid test object.`, + INVALID_CONTEXT: (ctx: Mochawesome.ContextArg) => { + const expected = + 'Expected a string or an object of shape { title: string, value: any } but saw:'; + return `${errorPrefix} ${expected}\n${jsonStringify( + ctx, + (key, val) => (val === undefined ? 'undefined' : val), + 2 + )}`; + }, +}; + +function _normalizeContext( + context: Mochawesome.ContextArg +): Mochawesome.Context | undefined { + // Ensure that context meets the requirements + if ( + (typeof context === 'string' && !context.trim()) || + (typeof context === 'object' && context.title === undefined) + ) { + logger.error(ERRORS.INVALID_CONTEXT(context)); + return; + } + + if (typeof context === 'string') { + return { + title: '', + value: context, + }; + } + + if (context.value === undefined) { + return { + ...context, + value: 'undefined', + }; + } + + return context; +} + +/** + * Add context to the test object so it can + * be displayed in the mochawesome report + * + * Usage: + * + * it('should test something', function () { + * someFunctionThatTestsCode(); + * + * addContext(this, 'some context to add'); + * + * addContext(this, { + * title: 'Expected number of something' + * value: 42 + * }); + * + * assert('something'); + * }); + * + */ +const addContext = function ( + runnable: MochaRunnable, + _context: Mochawesome.ContextArg +) { + // Check args to see if we should bother continuing + if (!runnable || !_context || !isObject(runnable)) { + logger.error(ERRORS.INVALID_ARGS); + return; + } + + const context = _normalizeContext(_context); + if (!context) { + return; + } + + /* + * When `addContext` is called from inside a hook the test object + * will be `.currentTest`, and the hook will be `.test`. + * Otherwise the test is just `.test` and `.currentTest` is undefined. + */ + const { currentTest, test: activeTest } = runnable; + + /* + * For `beforeAll` and `afterAll`, add the context to the hook, + * otherwise add it to the actual test. + */ + const isEachHook = + currentTest && + activeTest && + /^"(?:before|after)\seach"/.test(activeTest.title); + + const theTest = isEachHook ? currentTest : activeTest; + + if (!theTest) { + logger.error(ERRORS.INVALID_TEST); + return; + } + + // Test doesn't already have context -> set it + if (!theTest.context) { + theTest.context = [context]; + } else if (Array.isArray(theTest.context)) { + // Test has context and context is an array -> push new context + theTest.context.push(context); + } +}; + +export = addContext; diff --git a/src/config.js b/src/config.js deleted file mode 100644 index 50e781a..0000000 --- a/src/config.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Retrieve the value of a user supplied option. - * Falls back to `defaultValue` - * Order of precedence - * 1. User-supplied option - * 2. Environment variable - * 3. Default value - * - * @param {string} optToGet Option name - * @param {object} options User supplied options object - * @param {boolean} isBool Treat option as Boolean - * @param {string|boolean} defaultValue Fallback value - * - * @return {string|boolean} Option value - */ -function _getOption(optToGet, options, isBool, defaultValue) { - const envVar = `MOCHAWESOME_${optToGet.toUpperCase()}`; - if (options && typeof options[optToGet] !== 'undefined') { - return isBool && typeof options[optToGet] === 'string' - ? options[optToGet] === 'true' - : options[optToGet]; - } - if (typeof process.env[envVar] !== 'undefined') { - return isBool ? process.env[envVar] === 'true' : process.env[envVar]; - } - return defaultValue; -} - -module.exports = function (opts) { - const reporterOpts = (opts && opts.reporterOptions) || {}; - const code = _getOption('code', reporterOpts, true, true); - const noCode = _getOption('no-code', reporterOpts, true, false); - - return { - quiet: _getOption('quiet', reporterOpts, true, false), - reportFilename: _getOption( - 'reportFilename', - reporterOpts, - false, - 'mochawesome' - ), - saveHtml: _getOption('html', reporterOpts, true, true), - saveJson: _getOption('json', reporterOpts, true, true), - consoleReporter: _getOption('consoleReporter', reporterOpts, false, 'spec'), - useInlineDiffs: !!opts.inlineDiffs, - code: noCode ? false : code, - }; -}; diff --git a/src/config.ts b/src/config.ts new file mode 100644 index 0000000..ce00452 --- /dev/null +++ b/src/config.ts @@ -0,0 +1,44 @@ +/** + * Retrieve the value of a user supplied option. + * Falls back to `defaultValue` + * Order of precedence + * 1. User-supplied option + * 2. Environment variable + * 3. Default value + */ +function get(optToGet: string, options: any, defaultValue: string | boolean) { + const optionValue = options && options[optToGet]; + const envVar = `MOCHAWESOME_${optToGet.toUpperCase()}`; + const envValue = process.env[envVar]; + const isBool = typeof defaultValue === 'boolean'; + + if (optionValue !== undefined) { + return isBool && typeof optionValue === 'string' + ? optionValue === 'true' + : optionValue; + } + + if (envValue !== undefined) { + return isBool ? envValue === 'true' : envValue; + } + + return defaultValue; +} + +function getMochawesomeConfig(opts: Mochawesome.Options): Mochawesome.Config { + const { reporterOptions } = opts || {}; + const code = get('code', reporterOptions, true); + const noCode = get('no-code', reporterOptions, false); + + return { + quiet: get('quiet', reporterOptions, false), + reportFilename: get('reportFilename', reporterOptions, 'mochawesome'), + saveHtml: get('html', reporterOptions, true), + saveJson: get('json', reporterOptions, true), + consoleReporter: get('consoleReporter', reporterOptions, 'spec'), + useInlineDiffs: !!opts.inlineDiffs, + code: noCode ? false : code, + }; +} + +export default getMochawesomeConfig; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..36a0202 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,230 @@ +import Mocha from 'mocha'; +import mochaPkg from 'mocha/package.json'; +import marge from 'mochawesome-report-generator'; +import margePkg from 'mochawesome-report-generator/package.json'; +import conf from './config'; +import RunProcessor from './processor'; +import Logger from './logger'; +import pkg from '../package.json'; + +const { + EVENT_RUN_BEGIN, + EVENT_HOOK_END, + EVENT_SUITE_BEGIN, + EVENT_TEST_PASS, + EVENT_TEST_FAIL, + EVENT_TEST_PENDING, + EVENT_SUITE_END, + EVENT_RUN_END, +} = Mocha.Runner.constants; + +/** + * Mochawesome Reporter + */ +class Mochawesome extends Mocha.reporters.Base { + config: Mochawesome.Config; + currentSuite: Mochawesome.Suite | undefined; + logger: Logger; + margeOptions: Mochawesome.MargeOptions; + meta: Mochawesome.OutputMeta; + options: Mochawesome.Options; + reporterStats: Mochawesome.Stats | null; + results: Mochawesome.Results; + totals: { registered: number; skipped: number }; + + constructor(runner: Mocha.Runner, options: Mochawesome.Options) { + // Call the Base mocha reporter + super(runner, options); + + // Save the options and get the reporter config + this.options = options; + this.config = conf(options); + this.logger = new Logger(console, this.config); + + // Save metadata about the run + this.meta = { + mocha: { + version: mochaPkg.version, + }, + mochawesome: { + options: this.config, + version: pkg.version, + }, + marge: { + options: options.reporterOptions, + version: margePkg.version, + }, + }; + + this.reporterStats = null; + this.results = { + suites: {}, + tests: {}, + hooks: {}, + }; + + // Ensure stats collector has been initialized + if (!runner.stats) { + const createStatsCollector = require('mocha/lib/stats-collector'); + createStatsCollector(runner); + } + + // Reporter options + this.margeOptions = { + ...options.reporterOptions, + reportFilename: this.config.reportFilename, + saveHtml: this.config.saveHtml, + saveJson: this.config.saveJson, + }; + + // Reset total tests counters + this.totals = { + registered: 0, + skipped: 0, + }; + + this.initConsoleReporter(); + + // Attach listener for run end event + runner.on(EVENT_RUN_END, () => { + try { + this.handleEndEvent(); + } catch (err) { + // required because thrown errors are not handled directly in the + // event emitter pattern and mocha does not have an "on error" + /* istanbul ignore next */ + this.logger.error(`Problem with mochawesome: ${(err as Error).stack}`); + } + }); + + // Handle events from workers in parallel mode + if (runner.constructor.name === 'ParallelBufferedRunner') { + this.attatchEventsForParallelMode(); + } + } + + /** + * Initialize a reporter to output to the console while mocha is running + * and before mochawesome generates its own report. + */ + initConsoleReporter() { + const { consoleReporter } = this.config; + if (consoleReporter !== 'none') { + let ConsoleReporter; + try { + ConsoleReporter = require(`mocha/lib/reporters/${consoleReporter}`); + } catch (e) { + this.logger.warn(`Unknown console reporter '${consoleReporter}'`); + } + if (ConsoleReporter) { + new ConsoleReporter(this.runner, this.options); // eslint-disable-line + } + } + } + + attatchEventsForParallelMode() { + const HookMap: { [key: string]: string } = { + '"before all" ': '_beforeAll', + '"before each" ': '_beforeEach', + '"after each" ': '_afterEach', + '"after all" ': '_afterAll', + }; + + this.runner.on(EVENT_RUN_BEGIN, () => { + this.currentSuite = undefined; + }); + + this.runner.on(EVENT_SUITE_BEGIN, suite => { + suite['_beforeAll'] = suite['_beforeAll'] || []; + suite['_beforeEach'] = suite['_beforeEach'] || []; + suite.suites = suite.suites || []; + suite.tests = suite.tests || []; + suite['_afterEach'] = suite['_afterEach'] || []; + suite['_afterAll'] = suite['_afterAll'] || []; + if (suite.root) { + suite = this.runner.suite; + } else if (this.currentSuite) { + this.currentSuite.suites.push(suite); + suite.parent = this.currentSuite; + } + this.currentSuite = suite as Mochawesome.Suite; + }); + + this.runner.on(EVENT_SUITE_END, () => { + if (this.currentSuite) { + this.currentSuite = this.currentSuite.parent as Mochawesome.Suite; + } + }); + + this.runner.on(EVENT_HOOK_END, hook => { + if (this.currentSuite) { + const hookType = HookMap[hook.title.split('hook')[0]]; + const hooks = this.currentSuite[hookType]; + // add only once, since it is attached to the Suite + if (hooks && hooks.every((it: Mocha.Hook) => it.title !== hook.title)) { + hook.parent = this.currentSuite; + hooks.push(hook); + } + } + }); + + [EVENT_TEST_PASS, EVENT_TEST_FAIL, EVENT_TEST_PENDING].forEach(type => { + this.runner.on(type, test => { + if (this.currentSuite) { + test.parent = this.currentSuite; + if (test.type === 'hook') { + const hookType = HookMap[test.title.split('hook')[0]]; + const hooks = this.currentSuite[hookType]; + hooks && hooks.push(test); + } else { + this.currentSuite.tests.push(test); + } + } + }); + }); + } + + handleEndEvent() { + const processor = new RunProcessor(this.runner.suite, this.config); + this.results = processor.run(); + + const { suites = 0, passes = 0, failures = 0, pending = 0, tests = 0 } = + this.runner?.stats || {}; + const failedHooks = passes + failures + pending - tests; + this.reporterStats = { + suites, + tests: this.totals.registered, + passed: passes, + failed: failures - failedHooks, + skipped: pending, + failedHooks, + }; + } + + // Done function will be called before mocha exits + // This is where we will save JSON and generate the HTML report + async done(failures: number, exit: (failures: number) => void) { + try { + const [htmlFile, jsonFile] = await marge.create( + { + meta: this.meta, + results: this.results, + stats: this.stats, + }, + this.margeOptions + ); + if (!htmlFile && !jsonFile) { + this.logger.warn('No files were generated'); + } else { + jsonFile && this.logger.log(`Report JSON saved to ${jsonFile}`); + htmlFile && this.logger.log(`Report HTML saved to ${htmlFile}`); + } + } catch (err) { + this.logger.error(err as Error); + } + + exit && exit(failures > 0 ? 1 : 0); + } +} + +export = Mochawesome; diff --git a/src/logger.ts b/src/logger.ts new file mode 100644 index 0000000..e7cc00d --- /dev/null +++ b/src/logger.ts @@ -0,0 +1,42 @@ +import chalk from 'chalk'; +import jsonStringify from 'json-stringify-safe'; + +type LogMessage = string | object; + +class Logger { + logger: Console; + config: Mochawesome.Config | undefined; + prefix: string; + + constructor(logger: Console, config?: Mochawesome.Config) { + this.logger = logger; + this.config = config; + this.prefix = `[${chalk.gray('mochawesome')}] `; //${out}\n` + } + + getMessage(message: LogMessage) { + const msg = + typeof message === 'object' ? jsonStringify(message, null, 2) : message; + return `${this.prefix}${msg}\n`; + } + + log(message: LogMessage) { + if (!this.config?.quiet) { + this.logger.log(this.getMessage(message)); + } + } + + error(message: LogMessage) { + if (!this.config?.quiet) { + this.logger.error(this.getMessage(message)); + } + } + + warn(message: LogMessage) { + if (!this.config?.quiet) { + this.logger.warn(this.getMessage(message)); + } + } +} + +export default Logger; diff --git a/src/mocha_utils.d.ts b/src/mocha_utils.d.ts new file mode 100644 index 0000000..df666e5 --- /dev/null +++ b/src/mocha_utils.d.ts @@ -0,0 +1,3 @@ +declare module 'mocha/lib/utils' { + export function stringify(value: any): string; +} diff --git a/src/mochawesome.d.ts b/src/mochawesome.d.ts new file mode 100644 index 0000000..852bd45 --- /dev/null +++ b/src/mochawesome.d.ts @@ -0,0 +1,163 @@ +// Type definitions for mochawesome 7.0 +// Project: https://github.com/adamgruber/mochawesome +// Definitions by: Adam Gruber + +declare namespace Mochawesome { + interface ReporterOptions { + 'no-code': boolean; + code: boolean; + consoleReporter: string; + html: boolean; + json: boolean; + quiet: boolean; + reportFilename: string; + } + + interface Options { + inlineDiffs?: boolean; + reporterOptions: Partial; + } + + interface Config { + code: boolean; + consoleReporter: string; + quiet: boolean; + reportFilename: string; + saveHtml: boolean; + saveJson: boolean; + useInlineDiffs: boolean; + } + + interface OutputMeta { + mocha: { + version: string; + }; + mochawesome: { + options: Config; + version: string; + }; + marge: { + options: Partial; + version: string; + }; + } + + interface Stats { + suites: number; + tests: number; + passed: number; + failed: number; + skipped: number; + failedHooks: number; + } + + type Results = { + suites: { + [key: string]: ProcessedSuite; + }; + tests: { + [key: string]: ProcessedTest; + }; + hooks: { + [key: string]: ProcessedTest; + }; + }; + + interface MargeOptions { + reportFilename: string; + saveHtml: boolean; + saveJson: boolean; + } + + interface Suite extends Mocha.Suite { + [key: string]: Mocha.Hook[]; + } + + interface ProcessedSuite { + id: string; + title: string; + fullFile: string | undefined; + file: string | undefined; + duration: number; + isRoot: boolean; + parent?: string; + } + + type Context = { + title: string; + value: string | number; + }; + + type ContextArg = string | Context; + + type TestType = + | 'test' + | 'beforeEach' + | 'beforeAll' + | 'afterEach' + | 'afterAll'; + + interface ProcessedTest { + id: string; + title: string; + fullTitle: string; + duration: number; + timeout: number; + timedOut: boolean; + retries?: number; + state?: 'failed' | 'passed' | 'pending' | 'didNotRun'; + speed?: 'slow' | 'medium' | 'fast'; + context?: string | Context; + code?: string; + err?: NormalizedError; + parent?: string; + type: TestType; + } +} + +// Create our own Mocha Types with properties used by the reporter +type MochaSuite = Omit< + Mocha.Suite, + | 'tests' + | 'suites' + | '_beforeEach' + | '_beforeAll' + | '_afterEach' + | '_afterAll' + | 'parent' +> & { + id: string; + tests: MochaTest[]; + suites: MochaSuite[]; + parent: MochaSuite | undefined; + _beforeEach: MochaHook[]; + _beforeAll: MochaHook[]; + _afterEach: MochaHook[]; + _afterAll: MochaHook[]; +}; + +type MochaTest = Omit & { + id: string; + context?: Mochawesome.Context[]; + parent?: MochaSuite; + type: 'test'; +}; + +type MochaHook = Omit & { + id: string; + context?: Mochawesome.Context[]; + parent?: MochaSuite; + err?: Error | undefined; + type: 'hook'; +}; + +type MochaRunnable = { + currentTest?: MochaTest | MochaHook; + test?: MochaTest | MochaHook; +}; + +type AssertionError = import('assert').AssertionError; + +type MochaError = Partial & { + showDiff?: boolean; +}; diff --git a/src/mochawesome.js b/src/mochawesome.js deleted file mode 100755 index b64830b..0000000 --- a/src/mochawesome.js +++ /dev/null @@ -1,257 +0,0 @@ -const Base = require('mocha/lib/reporters/base'); -const mochaPkg = require('mocha/package.json'); -const uuid = require('uuid'); -const marge = require('mochawesome-report-generator'); -const margePkg = require('mochawesome-report-generator/package.json'); -const conf = require('./config'); -const utils = require('./utils'); -const pkg = require('../package.json'); -const Mocha = require('mocha'); -const { - EVENT_RUN_BEGIN, - EVENT_HOOK_END, - EVENT_SUITE_BEGIN, - EVENT_TEST_PASS, - EVENT_TEST_FAIL, - EVENT_TEST_PENDING, - EVENT_SUITE_END, -} = Mocha.Runner.constants; - -// Import the utility functions -const { log, mapSuites } = utils; - -// Track the total number of tests registered/skipped -const testTotals = { - registered: 0, - skipped: 0 -} - -/** - * Done function gets called before mocha exits - * - * Creates and saves the report HTML and JSON files - * - * @param {Object} output Final report object - * @param {Object} options Options to pass to report generator - * @param {Object} config Reporter config object - * @param {Number} failures Number of reported failures - * @param {Function} exit - * - * @return {Promise} Resolves with successful report creation - */ -function done(output, options, config, failures, exit) { - return marge - .create(output, options) - .then(([htmlFile, jsonFile]) => { - if (!htmlFile && !jsonFile) { - log('No files were generated', 'warn', config); - } else { - jsonFile && log(`Report JSON saved to ${jsonFile}`, null, config); - htmlFile && log(`Report HTML saved to ${htmlFile}`, null, config); - } - }) - .catch(err => { - log(err, 'error', config); - }) - .then(() => { - exit && exit(failures > 0 ? 1 : 0); - }); -} - -/** - * Get the class of the configured console reporter. This reporter outputs - * test results to the console while mocha is running, and before - * mochawesome generates its own report. - * - * Defaults to 'spec'. - * - * @param {String} reporter Name of reporter to use for console output - * - * @return {Object} Reporter class object - */ -function consoleReporter(reporter) { - if (reporter) { - try { - return require(`mocha/lib/reporters/${reporter}`); - } catch (e) { - log(`Unknown console reporter '${reporter}', defaulting to spec`); - } - } - - return require('mocha/lib/reporters/spec'); -} - -/** - * Initialize a new reporter. - * - * @param {Runner} runner - * @api public - */ -function Mochawesome(runner, options) { - // Set the config options - this.config = conf(options); - - // Ensure stats collector has been initialized - if (!runner.stats) { - const createStatsCollector = require('mocha/lib/stats-collector'); - createStatsCollector(runner); - } - - // Reporter options - const reporterOptions = { - ...options.reporterOptions, - reportFilename: this.config.reportFilename, - saveHtml: this.config.saveHtml, - saveJson: this.config.saveJson, - }; - - // Done function will be called before mocha exits - // This is where we will save JSON and generate the HTML report - this.done = (failures, exit) => - done(this.output, reporterOptions, this.config, failures, exit); - - // Reset total tests counters - testTotals.registered = 0; - testTotals.skipped = 0; - - // Call the Base mocha reporter - Base.call(this, runner); - - const reporterName = reporterOptions.consoleReporter; - if (reporterName !== 'none') { - const ConsoleReporter = consoleReporter(reporterName); - new ConsoleReporter(runner); // eslint-disable-line - } - - let endCalled = false; - - // Add a unique identifier to each suite/test/hook - ['suite', 'test', 'hook', 'pending'].forEach(type => { - runner.on(type, item => { - item.uuid = uuid.v4(); - }); - }); - - // Handle events from workers in parallel mode - if (runner.constructor.name === 'ParallelBufferedRunner') { - let currentSuite; - - const HookMap = { - ['"before all" ']: '_beforeAll', - ['"before each" ']: '_beforeEach', - ['"after each" ']: '_afterEach', - ['"after all" ']: '_afterAll', - }; - - runner.on(EVENT_RUN_BEGIN, function () { - currentSuite = undefined; - }); - - runner.on(EVENT_SUITE_BEGIN, function (suite) { - suite._beforeAll = suite._beforeAll || []; - suite._beforeEach = suite._beforeEach || []; - suite.suites = suite.suites || []; - suite.tests = suite.tests || []; - suite._afterEach = suite._afterEach || []; - suite._afterAll = suite._afterAll || []; - if (suite.root) { - suite = runner.suite; - } else if (currentSuite) { - currentSuite.suites.push(suite); - suite.parent = currentSuite; - } - currentSuite = suite; - }); - - runner.on(EVENT_SUITE_END, function () { - if (currentSuite) { - currentSuite = currentSuite.parent; - } - }); - - runner.on(EVENT_HOOK_END, function (hook) { - if (currentSuite) { - const hooks = currentSuite[HookMap[hook.title.split('hook')[0]]]; - // add only once, since it is attached to the Suite - if (hooks && hooks.every(it => it.title !== hook.title)) { - hook.parent = currentSuite; - hooks.push(hook); - } - } - }); - - [EVENT_TEST_PASS, EVENT_TEST_FAIL, EVENT_TEST_PENDING].forEach(type => { - runner.on(type, function (test) { - if (currentSuite) { - test.parent = currentSuite; - if (test.type === 'hook') { - const hooks = currentSuite[HookMap[test.title.split('hook')[0]]]; - hooks && hooks.push(test); - } else { - currentSuite.tests.push(test); - } - } - }); - }); - } - - // Process the full suite - runner.on('end', () => { - try { - /* istanbul ignore else */ - if (!endCalled) { - // end gets called more than once for some reason - // so we ensure the suite is processed only once - endCalled = true; - - const rootSuite = mapSuites( - this.runner.suite, - testTotals, - this.config - ); - - const obj = { - stats: this.stats, - results: [rootSuite], - meta: { - mocha: { - version: mochaPkg.version, - }, - mochawesome: { - options: this.config, - version: pkg.version, - }, - marge: { - options: options.reporterOptions, - version: margePkg.version, - }, - }, - }; - - obj.stats.testsRegistered = testTotals.registered; - - const { passes, failures, pending, tests, testsRegistered } = obj.stats; - const passPercentage = (passes / (testsRegistered - pending)) * 100; - const pendingPercentage = (pending / testsRegistered) * 100; - - obj.stats.passPercent = passPercentage; - obj.stats.pendingPercent = pendingPercentage; - obj.stats.other = passes + failures + pending - tests; // Failed hooks - obj.stats.hasOther = obj.stats.other > 0; - obj.stats.skipped = testTotals.skipped; - obj.stats.hasSkipped = obj.stats.skipped > 0; - obj.stats.failures -= obj.stats.other; - - // Save the final output to be used in the done function - this.output = obj; - } - } catch (e) { - // required because thrown errors are not handled directly in the - // event emitter pattern and mocha does not have an "on error" - /* istanbul ignore next */ - log(`Problem with mochawesome: ${e.stack}`, 'error'); - } - }); -} - -module.exports = Mochawesome; diff --git a/src/processor.ts b/src/processor.ts new file mode 100644 index 0000000..14242be --- /dev/null +++ b/src/processor.ts @@ -0,0 +1,207 @@ +import isFunction from 'lodash/isfunction'; +import { stringify as mochaStringify } from 'mocha/lib/utils'; +import jsonStringify from 'json-stringify-safe'; +import { Change, createPatch, diffWordsWithSpace } from 'diff'; +import stripAnsi from 'strip-ansi'; +import stripFnStart from './stripFnStart'; + +class RunProcessor { + config: Mochawesome.Config; + processed: Mochawesome.Results; + rootSuite: MochaSuite; + + constructor(rootSuite: Mocha.Suite, config: Mochawesome.Config) { + this.config = config; + this.rootSuite = (rootSuite as unknown) as MochaSuite; + this.processed = { + suites: {}, + tests: {}, + hooks: {}, + }; + + this.processTest = this.processTest.bind(this); + this.processSuite = this.processSuite.bind(this); + } + + run() { + this.walkRunPostorder(); + return this.processed; + } + + /** + * Check that a / b have the same type. + */ + isSameType(a: any, b: any) { + const objToString = Object.prototype.toString; + return objToString.call(a) === objToString.call(b); + } + + createDiffFromError({ + actual, + expected, + showDiff, + }: MochaError): Change[] | string | undefined { + if ( + !showDiff || + !this.isSameType(actual, expected) || + actual === undefined || + expected === undefined + ) { + return; + } + + const actualStr = mochaStringify(actual); + const expectedStr = mochaStringify(expected); + return this.config.useInlineDiffs + ? diffWordsWithSpace(actualStr, expectedStr) + : createPatch('string', actualStr, expectedStr) + .split('\n') + .splice(4) + .map(line => { + if (line.match(/@@/)) { + return null; + } + if (line.match(/\\ No newline/)) { + return null; + } + return line.replace(/^(-|\+)/, '$1 '); + }) + .filter(line => typeof line !== 'undefined' && line !== null) + .join('\n'); + } + + /** + * Return a normalized error object + */ + normalizeErr(err?: MochaError) { + if (!err) { + return; + } + + const { name, message, stack } = err; + let errMessage: string | undefined; + + // Assertion libraries do not output consitent error objects so in order to + // get a consistent message object we need to create it ourselves + if (name && message) { + errMessage = `${name}: ${stripAnsi(message)}`; + } else if (stack) { + errMessage = stack.replace(/\n.*/g, ''); + } + + return { + message: errMessage, + estack: stack && stripAnsi(stack), + diff: this.createDiffFromError(err), + }; + } + + /** + * Strip the function definition from `str`, + * and re-indent for pre whitespace. + */ + cleanCode(code: string): string { + let cleaned = code + .replace(/\r\n|[\r\n\u2028\u2029]/g, '\n') // unify linebreaks + .replace(/^\uFEFF/, ''); // replace zero-width no-break space + + cleaned = stripFnStart(cleaned) // replace function declaration + .replace(/\)\s*\)\s*$/, ')') // replace closing paren + .replace(/\s*};?\s*$/, ''); // replace closing bracket + + // Preserve indentation by finding leading tabs/spaces + // and removing that amount of space from each line + const spacesMatch = cleaned.match(/^\n?( *)/); + const numSpaces = spacesMatch ? spacesMatch[1].length : 0; + const tabsMatch = cleaned.match(/^\n?(\t*)/); + const numTabs = tabsMatch ? tabsMatch[1].length : 0; + /* istanbul ignore next */ + const indentRegex = new RegExp( + `^\n?${numTabs ? '\t' : ' '}{${numTabs || numSpaces}}`, + 'gm' + ); + + cleaned = cleaned.replace(indentRegex, '').trim(); + return cleaned; + } + + processSuite(suite: MochaSuite): Mochawesome.ProcessedSuite { + let duration = 0; + + suite.tests?.forEach(test => { + const processed = this.processTest(test); + this.processed.tests[processed.id] = processed; + duration += processed.duration; + }); + + const processHook = (hook: MochaHook) => { + const processed = this.processTest(hook); + this.processed.hooks[processed.id] = processed; + }; + + suite._beforeAll?.forEach(processHook); + suite._beforeEach?.forEach(processHook); + suite._afterAll?.forEach(processHook); + suite._afterEach?.forEach(processHook); + + return { + id: suite.id, + title: stripAnsi(suite.title), + fullFile: suite.file, + file: suite.file?.replace(process.cwd(), ''), + duration, + isRoot: suite.root, + parent: suite.parent?.id, + }; + } + + processTest(test: MochaTest | MochaHook): Mochawesome.ProcessedTest { + const title = stripAnsi(test.title); + let testType: Mochawesome.TestType = 'test'; + if (test.type === 'hook') { + const hookTypes: { [key: string]: Mochawesome.TestType } = { + '"before each"': 'beforeEach', + '"before all"': 'beforeAll', + '"after each"': 'afterEach', + '"after all"': 'afterAll', + }; + Object.keys(hookTypes).forEach(prefix => { + if (title.startsWith(prefix)) { + testType = hookTypes[prefix]; + } + }); + } + + return { + id: test.id, + title, + fullTitle: isFunction(test.fullTitle) + ? stripAnsi(test.fullTitle()) + : title, + duration: test.duration || 0, + timeout: test.timeout(), + timedOut: test.timedOut, + retries: test.retries(), + state: + test.type === 'test' && !test.state && !test.pending + ? 'didNotRun' + : test.state, + speed: 'speed' in test ? test.speed : undefined, + context: jsonStringify(test.context, null, 2), + code: this.config.code ? this.cleanCode(test.body) : undefined, + err: this.normalizeErr(test.err), + parent: test.parent?.id, + type: testType, + }; + } + + walkRunPostorder(suite: MochaSuite = this.rootSuite) { + if (suite.suites.length) { + suite.suites.forEach(subSuite => this.walkRunPostorder(subSuite)); + } + const processed = this.processSuite(suite); + this.processed.suites[processed.id] = processed; + } +} + +export default RunProcessor; diff --git a/src/register.js b/src/register.js index 6c506ad..321fd45 100644 --- a/src/register.js +++ b/src/register.js @@ -4,7 +4,7 @@ const extendSerialize = (target, fields) => { const serialize = target.serialize; target.serialize = function (...args) { const result = serialize.call(this, ...args); - for (let field of fields) { + for (const field of fields) { result[field] = this[field]; } return result; @@ -14,5 +14,3 @@ const extendSerialize = (target, fields) => { extendSerialize(Mocha.Suite.prototype, ['file']); extendSerialize(Mocha.Hook.prototype, ['body', 'state']); extendSerialize(Mocha.Test.prototype, ['pending', 'context']); - -module.exports = {}; diff --git a/src/stripFnStart.js b/src/stripFnStart.ts similarity index 79% rename from src/stripFnStart.js rename to src/stripFnStart.ts index d3bd47d..b5744a8 100644 --- a/src/stripFnStart.js +++ b/src/stripFnStart.ts @@ -1,12 +1,7 @@ -/* eslint-disable consistent-return */ /** * Strip start of function - * - * @param {string} input - * - * @return {string} */ -function stripFunctionStart(input) { +function stripFunctionStart(input: string): string { const BEGIN = 1; const LBRACE = 2; const EQ = 4; @@ -15,9 +10,9 @@ function stripFunctionStart(input) { const ARROW_PAREN = 32; const DONE = 64; - const isWhitespace = ch => ch === ' ' || ch === '\t' || ch === '\n'; + const isWhitespace = (ch: string) => ch === ' ' || ch === '\t' || ch === '\n'; - const nextState = (state, c) => { + const nextState = (state: number, c: string) => { switch (state) { case BEGIN: switch (c) { @@ -49,6 +44,9 @@ function stripFunctionStart(input) { case ARROW_LBRACE: case ARROW_PAREN: return DONE; + + default: + return DONE; } }; @@ -61,4 +59,4 @@ function stripFunctionStart(input) { return state === DONE ? input.slice(pos - 1) : input; } -module.exports = stripFunctionStart; +export default stripFunctionStart; diff --git a/src/utils.js b/src/utils.js deleted file mode 100644 index 81eca0f..0000000 --- a/src/utils.js +++ /dev/null @@ -1,285 +0,0 @@ -const isString = require('lodash.isstring'); -const isFunction = require('lodash.isfunction'); -const isEmpty = require('lodash.isempty'); -const chalk = require('chalk'); -const uuid = require('uuid'); -const mochaUtils = require('mocha/lib/utils'); -const stringify = require('json-stringify-safe'); -const diff = require('diff'); -const stripAnsi = require('strip-ansi'); -const stripFnStart = require('./stripFnStart'); - -/** - * Return a classname based on percentage - * - * @param {String} msg - message to log - * @param {String} level - log level [log, info, warn, error] - * @param {Object} config - configuration object - */ -function log(msg, level, config) { - // Don't log messages in quiet mode - if (config && config.quiet) return; - const logMethod = console[level] || console.log; - let out = msg; - if (typeof msg === 'object') { - out = stringify(msg, null, 2); - } - logMethod(`[${chalk.gray('mochawesome')}] ${out}\n`); -} - -/** - * Strip the function definition from `str`, - * and re-indent for pre whitespace. - * - * @param {String} str - code in - * - * @return {String} cleaned code string - */ -function cleanCode(str) { - str = str - .replace(/\r\n|[\r\n\u2028\u2029]/g, '\n') // unify linebreaks - .replace(/^\uFEFF/, ''); // replace zero-width no-break space - - str = stripFnStart(str) // replace function declaration - .replace(/\)\s*\)\s*$/, ')') // replace closing paren - .replace(/\s*};?\s*$/, ''); // replace closing bracket - - // Preserve indentation by finding leading tabs/spaces - // and removing that amount of space from each line - const spaces = str.match(/^\n?( *)/)[1].length; - const tabs = str.match(/^\n?(\t*)/)[1].length; - /* istanbul ignore next */ - const indentRegex = new RegExp( - `^\n?${tabs ? '\t' : ' '}{${tabs || spaces}}`, - 'gm' - ); - - str = str.replace(indentRegex, '').trim(); - return str; -} - -/** - * Create a unified diff between two strings - * - * @param {Error} err Error object - * @param {string} err.actual Actual result returned - * @param {string} err.expected Result expected - * - * @return {string} diff - */ -function createUnifiedDiff({ actual, expected }) { - return diff - .createPatch('string', actual, expected) - .split('\n') - .splice(4) - .map(line => { - if (line.match(/@@/)) { - return null; - } - if (line.match(/\\ No newline/)) { - return null; - } - return line.replace(/^(-|\+)/, '$1 '); - }) - .filter(line => typeof line !== 'undefined' && line !== null) - .join('\n'); -} - -/** - * Create an inline diff between two strings - * - * @param {Error} err Error object - * @param {string} err.actual Actual result returned - * @param {string} err.expected Result expected - * - * @return {array} diff string objects - */ -function createInlineDiff({ actual, expected }) { - return diff.diffWordsWithSpace(actual, expected); -} - -/** - * Return a normalized error object - * - * @param {Error} err Error object - * - * @return {Object} normalized error - */ -function normalizeErr(err, config) { - const { name, message, actual, expected, stack, showDiff } = err; - let errMessage; - let errDiff; - - /** - * Check that a / b have the same type. - */ - function sameType(a, b) { - const objToString = Object.prototype.toString; - return objToString.call(a) === objToString.call(b); - } - - // Format actual/expected for creating diff - if ( - showDiff !== false && - sameType(actual, expected) && - expected !== undefined - ) { - /* istanbul ignore if */ - if (!(isString(actual) && isString(expected))) { - err.actual = mochaUtils.stringify(actual); - err.expected = mochaUtils.stringify(expected); - } - errDiff = config.useInlineDiffs - ? createInlineDiff(err) - : createUnifiedDiff(err); - } - - // Assertion libraries do not output consitent error objects so in order to - // get a consistent message object we need to create it ourselves - if (name && message) { - errMessage = `${name}: ${stripAnsi(message)}`; - } else if (stack) { - errMessage = stack.replace(/\n.*/g, ''); - } - - return { - message: errMessage, - estack: stack && stripAnsi(stack), - diff: errDiff, - }; -} - -/** - * Return a plain-object representation of `test` - * free of cyclic properties etc. - * - * @param {Object} test - * - * @return {Object} cleaned test - */ -function cleanTest(test, config) { - const code = config.code ? test.body || '' : ''; - - const fullTitle = isFunction(test.fullTitle) - ? stripAnsi(test.fullTitle()) - : stripAnsi(test.title); - - const cleaned = { - title: stripAnsi(test.title), - fullTitle, - timedOut: test.timedOut, - duration: test.duration || 0, - state: test.state, - speed: test.speed, - pass: test.state === 'passed', - fail: test.state === 'failed', - pending: test.pending, - context: stringify(test.context, null, 2), - code: code && cleanCode(code), - err: (test.err && normalizeErr(test.err, config)) || {}, - uuid: test.uuid || /* istanbul ignore next: default */ uuid.v4(), - parentUUID: test.parent && test.parent.uuid, - isHook: test.type === 'hook', - }; - - cleaned.skipped = - !cleaned.pass && !cleaned.fail && !cleaned.pending && !cleaned.isHook; - - return cleaned; -} - -/** - * Return a plain-object representation of `suite` with additional properties for rendering. - * - * @param {Object} suite - * @param {Object} testTotals Cumulative count of tests registered/skipped - * @param {Integer} testTotals.registered - * @param {Integer} testTotals.skipped - * - * @return {Object|boolean} cleaned suite or false if suite is empty - */ -function cleanSuite(suite, testTotals, config) { - let duration = 0; - const passingTests = []; - const failingTests = []; - const pendingTests = []; - const skippedTests = []; - - const beforeHooks = [] - .concat(suite._beforeAll, suite._beforeEach) - .map(test => cleanTest(test, config)); - - const afterHooks = [] - .concat(suite._afterAll, suite._afterEach) - .map(test => cleanTest(test, config)); - - const tests = suite.tests.map(test => { - const cleanedTest = cleanTest(test, config); - duration += test.duration || 0; - if (cleanedTest.state === 'passed') passingTests.push(cleanedTest.uuid); - if (cleanedTest.state === 'failed') failingTests.push(cleanedTest.uuid); - if (cleanedTest.pending) pendingTests.push(cleanedTest.uuid); - if (cleanedTest.skipped) skippedTests.push(cleanedTest.uuid); - return cleanedTest; - }); - - testTotals.registered += tests.length; - testTotals.skipped += skippedTests.length; - - const cleaned = { - uuid: suite.uuid || /* istanbul ignore next: default */ uuid.v4(), - title: stripAnsi(suite.title), - fullFile: suite.file || '', - file: suite.file ? suite.file.replace(process.cwd(), '') : '', - beforeHooks, - afterHooks, - tests, - suites: suite.suites, - passes: passingTests, - failures: failingTests, - pending: pendingTests, - skipped: skippedTests, - duration, - root: suite.root, - rootEmpty: suite.root && tests.length === 0, - _timeout: suite._timeout, - }; - - const isEmptySuite = - isEmpty(cleaned.suites) && - isEmpty(cleaned.tests) && - isEmpty(cleaned.beforeHooks) && - isEmpty(cleaned.afterHooks); - - return !isEmptySuite && cleaned; -} - -/** - * Map over a suite, returning a cleaned suite object - * and recursively cleaning any nested suites. - * - * @param {Object} suite Suite to map over - * @param {Object} testTotals Cumulative count of tests registered/skipped - * @param {Integer} testTotals.registered - * @param {Integer} testTotals.skipped - * @param {Object} config Reporter configuration - */ -function mapSuites(suite, testTotals, config) { - const suites = suite.suites.reduce((acc, subSuite) => { - const mappedSuites = mapSuites(subSuite, testTotals, config); - if (mappedSuites) { - acc.push(mappedSuites); - } - return acc; - }, []); - const toBeCleaned = { ...suite, suites }; - return cleanSuite(toBeCleaned, testTotals, config); -} - -module.exports = { - log, - cleanCode, - cleanTest, - cleanSuite, - mapSuites, -}; diff --git a/test-functional/.mocharc.json b/test-functional/.mocharc.json index 37ed19e..ada9a6a 100644 --- a/test-functional/.mocharc.json +++ b/test-functional/.mocharc.json @@ -1,5 +1,5 @@ { - "require": ["should", "./register"], - "reporter": "./src/mochawesome", + "require": ["should"], + "reporter": "./dist/src/index", "reporterOption": [] } diff --git a/test-functional/simple/simple-a.js b/test-functional/simple/simple-a.js new file mode 100644 index 0000000..8116ccf --- /dev/null +++ b/test-functional/simple/simple-a.js @@ -0,0 +1,24 @@ +describe('suite', function () { + before(() => { + console.log('before'); + // console.log(nope); + }); + // this.retries(1); + + it('is a simple test - A1', function () { + (1 + 1).should.equal(4); + }); + + it('is a simple test - A2', function () { + (1 + 1).should.equal(4); + }); + + describe('nested suite', function () { + beforeEach(() => { + console.log('beforeEach'); + }); + it('is a simple test - nested A3', function () { + (1 + 1).should.equal(4); + }); + }); +}); diff --git a/test-functional/simple/simple-b.js b/test-functional/simple/simple-b.js new file mode 100644 index 0000000..9633970 --- /dev/null +++ b/test-functional/simple/simple-b.js @@ -0,0 +1,3 @@ +it('is a simple test - B', function () { + (1 + 1).should.equal(2); +}); diff --git a/test/.mocharc.json b/test/.mocharc.json index 523d398..920a966 100644 --- a/test/.mocharc.json +++ b/test/.mocharc.json @@ -1,3 +1,3 @@ { - "require": ["should"] + "require": ["should", "ts-node/register"] } diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..869d487 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,15 @@ +{ + "include": ["src/**/*"], + "exclude": ["package.json"], + "compilerOptions": { + "lib": ["es6"], + "module": "commonjs", + "target": "es6", + "strict": true, + "allowJs": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "outDir": "dist" + } +}