mochawesome
Advanced tools
Comparing version 6.1.1 to 6.2.0
/* | ||
* Export addContext function | ||
* Export `addContext` function | ||
* | ||
*/ | ||
module.exports = require('./src/addContext'); |
@@ -5,2 +5,13 @@ # mochawesome changelog | ||
## [6.2.0] - 2020-11-01 | ||
### Added | ||
- Support mocha `--parallel` mode | ||
### Changed | ||
- Update dev dependencies | ||
- Remove airbnb-extended `eslint` config. Use `eslint:recommended` instead | ||
- Add `husky`, `lint-staged`, and `prettier` | ||
- Format all files with `prettier` | ||
- Remove travis-ci config | ||
## [6.1.1] - 2020-04-27 | ||
@@ -258,3 +269,4 @@ ### Fixed | ||
[Unreleased]: https://github.com/adamgruber/mochawesome/compare/6.1.1...HEAD | ||
[Unreleased]: https://github.com/adamgruber/mochawesome/compare/6.2.0...HEAD | ||
[6.2.0]: https://github.com/adamgruber/mochawesome/compare/6.1.1...6.2.0 | ||
[6.1.1]: https://github.com/adamgruber/mochawesome/compare/6.1.0...6.1.1 | ||
@@ -261,0 +273,0 @@ [6.1.0]: https://github.com/adamgruber/mochawesome/compare/6.0.0...6.1.0 |
{ | ||
"name": "mochawesome", | ||
"version": "6.1.1", | ||
"version": "6.2.0", | ||
"description": "A gorgeous reporter for Mocha.js", | ||
"scripts": { | ||
"lint": "eslint src test", | ||
"lint": "eslint src test*", | ||
"test": "npm run lint && cross-env NODE_ENV=test nyc mocha --config test/.mocharc.json", | ||
"test:fn": "mocha test-functional/test.js --config test-functional/.mocharc.json", | ||
"test:fn": "mocha test-functional --config test-functional/.mocharc.json", | ||
"test:par": "mocha test-parallel --parallel --config test-parallel/.mocharc.json", | ||
"test:prog": "node ./test-programmatic", | ||
@@ -65,11 +66,43 @@ "test:mem": "mocha test-functional/mem-test.js --config test-functional/.mocharc.json", | ||
"cross-env": "^7.0.2", | ||
"eslint": "^6.5.1", | ||
"eslint-config-airbnb-base": "^14.1.0", | ||
"eslint-plugin-import": "^2.20.2", | ||
"mocha": "^7.1.1", | ||
"nyc": "^15.0.1", | ||
"eslint": "^7.10.0", | ||
"eslint-config-prettier": "^6.12.0", | ||
"husky": "^4.3.0", | ||
"lint-staged": "^10.4.0", | ||
"mocha": "^8.1.3", | ||
"nyc": "^15.1.0", | ||
"prettier": "^2.1.2", | ||
"proxyquire": "^2.1.0", | ||
"should": "^13.2.3", | ||
"sinon": "^9.0.2" | ||
"sinon": "^9.0.3" | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
} | ||
}, | ||
"lint-staged": { | ||
"*.js": "eslint --cache --fix", | ||
"*.{js,css,md}": "prettier --write" | ||
}, | ||
"prettier": { | ||
"arrowParens": "avoid", | ||
"semi": true, | ||
"singleQuote": true, | ||
"tabWidth": 2, | ||
"trailingComma": "es5" | ||
}, | ||
"eslintConfig": { | ||
"extends": [ | ||
"eslint:recommended", | ||
"prettier" | ||
], | ||
"env": { | ||
"es6": true, | ||
"mocha": true, | ||
"node": true | ||
}, | ||
"parserOptions": { | ||
"ecmaVersion": 9 | ||
} | ||
} | ||
} |
@@ -1,3 +0,3 @@ | ||
mochawesome | ||
=========== | ||
# mochawesome | ||
[![npm](https://img.shields.io/npm/v/mochawesome.svg?style=flat-square)](http://www.npmjs.com/package/mochawesome) ![Node.js CI](https://github.com/adamgruber/mochawesome/workflows/Node.js%20CI/badge.svg) [![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg?style=flat-square)](https://gitter.im/mochawesome/general) | ||
@@ -7,3 +7,2 @@ | ||
## Features | ||
@@ -23,2 +22,3 @@ | ||
- Offline viewing | ||
- Supports `parallel` mode | ||
@@ -29,18 +29,28 @@ ## Usage | ||
`npm install --save-dev mochawesome` | ||
`npm install --save-dev mochawesome` | ||
2. Tell mocha to use the Mochawesome reporter: | ||
`mocha testfile.js --reporter mochawesome` | ||
`mocha testfile.js --reporter mochawesome` | ||
3. If using mocha programatically: | ||
```js | ||
var mocha = new Mocha({ | ||
reporter: 'mochawesome' | ||
}); | ||
``` | ||
```js | ||
var mocha = new Mocha({ | ||
reporter: 'mochawesome', | ||
}); | ||
``` | ||
### Parallel Mode | ||
Since `mocha@8` test files can be run in parallel using the `--parallel` flag. In order for mochawesome to work properly it needs to be registered as a hook. | ||
`mocha tests --reporter mochawesome --require mochawesome/register` | ||
> Due to differences in how parallel tests are processed, statistics may differ between sequential and parallel test runs. Mocha does not provide information about skipped tests in parallel mode. For more information, see https://mochajs.org/#parallel-tests. | ||
### Output | ||
Mochawesome generates the following inside your project directory: | ||
``` | ||
@@ -69,18 +79,24 @@ mochawesome-report/ | ||
### Options | ||
### Options | ||
Options can be passed to the reporter in two ways. | ||
#### Environment variables | ||
The reporter will try to read environment variables that begin with `MOCHAWESOME_`. | ||
```bash | ||
$ export MOCHAWESOME_REPORTFILENAME=customReportFilename | ||
``` | ||
*Note that environment variables must be in uppercase.* | ||
_Note that environment variables must be in uppercase._ | ||
#### Mocha reporter-options | ||
You can pass comma-separated options to the reporter via mocha's `--reporter-options` flag. Options passed this way will take precedence over environment variables. | ||
```bash | ||
$ mocha test.js --reporter mochawesome --reporter-options reportDir=customReportDir,reportFilename=customReportFilename | ||
``` | ||
Alternately, `reporter-options` can be passed in programatically: | ||
@@ -93,4 +109,4 @@ | ||
reportFilename: 'customReportFilename', | ||
quiet: true | ||
} | ||
quiet: true, | ||
}, | ||
}); | ||
@@ -103,12 +119,12 @@ ``` | ||
Option Name | Type | Default | Description | ||
:---------- | :--- | :------ | :---------- | ||
`quiet` | boolean | false | Silence console messages | ||
`reportFilename` | string | mochawesome | Filename of saved report <br> *Applies to the generated html and json files.* | ||
`html` | boolean | true | Save the HTML output for the test run | ||
`json` | boolean | true | Save the JSON output for the test run | ||
`consoleReporter` | string | spec | Name of mocha reporter to use for console output, or `none` to disable console report output entirely | ||
| Option Name | Type | Default | Description | | ||
| :---------------- | :------ | :---------- | :---------------------------------------------------------------------------------------------------- | | ||
| `quiet` | boolean | false | Silence console messages | | ||
| `reportFilename` | string | mochawesome | Filename of saved report <br> _Applies to the generated html and json files._ | | ||
| `html` | boolean | true | Save the HTML output for the test run | | ||
| `json` | boolean | true | Save the JSON output for the test run | | ||
| `consoleReporter` | string | spec | Name of mocha reporter to use for console output, or `none` to disable console report output entirely | | ||
### Adding Test Context | ||
### Adding Test Context | ||
Mochawesome ships with an `addContext` helper method that can be used to associate additional information with a test. This information will then be displayed inside the report. | ||
@@ -120,6 +136,6 @@ | ||
param | type | description | ||
:---- | :--- | :---------- | ||
testObj | object | The test object | ||
context | string\|object | The context to be added to the test | ||
| param | type | description | | ||
| :------ | :------------- | :---------------------------------- | | ||
| testObj | object | The test object | | ||
| context | string\|object | The context to be added to the test | | ||
@@ -133,6 +149,8 @@ **Context as a string** | ||
Context passed as an object must adhere to the following shape: | ||
```js | ||
{ | ||
title: 'some title' // must be a string | ||
value: {} // can be anything | ||
title: 'some title'; // must be a string | ||
value: { | ||
} // can be anything | ||
} | ||
@@ -144,2 +162,3 @@ ``` | ||
Be sure to use ES5 functions and not ES6 arrow functions when using `addContext` to ensure `this` references the test object. | ||
```js | ||
@@ -165,6 +184,6 @@ const addContext = require('mochawesome/addContext'); | ||
b: '2', | ||
c: 'd' | ||
} | ||
c: 'd', | ||
}, | ||
}); | ||
}) | ||
}); | ||
}); | ||
@@ -174,6 +193,7 @@ ``` | ||
It is also possible to use `addContext` from within a `beforeEach` or `afterEach` test hook. | ||
```js | ||
describe('test suite', () => { | ||
beforeEach(function () { | ||
addContext(this, 'some context') | ||
addContext(this, 'some context'); | ||
}); | ||
@@ -184,3 +204,3 @@ | ||
title: 'afterEach context', | ||
value: { a: 1 } | ||
value: { a: 1 }, | ||
}); | ||
@@ -206,3 +226,3 @@ }); | ||
[marge-options]: https://github.com/adamgruber/mochawesome-report-generator#options | ||
[CHANGELOG]: CHANGELOG.md | ||
[changelog]: CHANGELOG.md | ||
[license]: LICENSE.md |
@@ -11,5 +11,10 @@ const isObject = require('lodash.isobject'); | ||
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)}`; | ||
} | ||
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 | ||
)}`; | ||
}, | ||
}; | ||
@@ -38,4 +43,8 @@ | ||
if (!ctx) return false; | ||
return ((typeof ctx === 'string') && !isEmpty(ctx)) | ||
|| (Object.hasOwnProperty.call(ctx, 'title') && !isEmpty(ctx.title) && Object.hasOwnProperty.call(ctx, 'value')); | ||
return ( | ||
(typeof ctx === 'string' && !isEmpty(ctx)) || | ||
(Object.hasOwnProperty.call(ctx, 'title') && | ||
!isEmpty(ctx.title) && | ||
Object.hasOwnProperty.call(ctx, 'value')) | ||
); | ||
} | ||
@@ -74,3 +83,3 @@ | ||
// Check args to see if we should bother continuing | ||
if ((args.length !== 2) || !isObject(args[0])) { | ||
if (args.length !== 2 || !isObject(args[0])) { | ||
log(ERRORS.INVALID_ARGS, 'error'); | ||
@@ -99,4 +108,4 @@ return; | ||
*/ | ||
const isEachHook = currentTest | ||
&& /^"(?:before|after)\seach"/.test(activeTest.title); | ||
const isEachHook = | ||
currentTest && /^"(?:before|after)\seach"/.test(activeTest.title); | ||
const test = isEachHook ? currentTest : activeTest; | ||
@@ -125,3 +134,3 @@ | ||
// Test has context and it is not an array -> make it an array, then push new context | ||
test.context = [ test.context ]; | ||
test.context = [test.context]; | ||
test.context.push(ctx); | ||
@@ -128,0 +137,0 @@ } |
@@ -19,3 +19,3 @@ /** | ||
if (options && typeof options[optToGet] !== 'undefined') { | ||
return (isBool && typeof options[optToGet] === 'string') | ||
return isBool && typeof options[optToGet] === 'string' | ||
? options[optToGet] === 'true' | ||
@@ -25,5 +25,3 @@ : options[optToGet]; | ||
if (typeof process.env[envVar] !== 'undefined') { | ||
return isBool | ||
? process.env[envVar] === 'true' | ||
: process.env[envVar]; | ||
return isBool ? process.env[envVar] === 'true' : process.env[envVar]; | ||
} | ||
@@ -40,3 +38,8 @@ return defaultValue; | ||
quiet: _getOption('quiet', reporterOpts, true, false), | ||
reportFilename: _getOption('reportFilename', reporterOpts, false, 'mochawesome'), | ||
reportFilename: _getOption( | ||
'reportFilename', | ||
reporterOpts, | ||
false, | ||
'mochawesome' | ||
), | ||
saveHtml: _getOption('html', reporterOpts, true, true), | ||
@@ -46,4 +49,4 @@ saveJson: _getOption('json', reporterOpts, true, true), | ||
useInlineDiffs: !!opts.inlineDiffs, | ||
code: noCode ? false : code | ||
code: noCode ? false : code, | ||
}; | ||
}; |
@@ -9,8 +9,15 @@ const Base = require('mocha/lib/reporters/base'); | ||
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; | ||
const { log, mapSuites } = utils; | ||
@@ -34,4 +41,5 @@ // Track the total number of tests registered | ||
function done(output, options, config, failures, exit) { | ||
return marge.create(output, options) | ||
.then(([ htmlFile, jsonFile ]) => { | ||
return marge | ||
.create(output, options) | ||
.then(([htmlFile, jsonFile]) => { | ||
if (!htmlFile && !jsonFile) { | ||
@@ -66,3 +74,2 @@ log('No files were generated', 'warn', config); | ||
try { | ||
// eslint-disable-next-line import/no-dynamic-require | ||
return require(`mocha/lib/reporters/${reporter}`); | ||
@@ -98,3 +105,3 @@ } catch (e) { | ||
saveHtml: this.config.saveHtml, | ||
saveJson: this.config.saveJson | ||
saveJson: this.config.saveJson, | ||
}; | ||
@@ -104,9 +111,4 @@ | ||
// This is where we will save JSON and generate the HTML report | ||
this.done = (failures, exit) => done( | ||
this.output, | ||
reporterOptions, | ||
this.config, | ||
failures, | ||
exit | ||
); | ||
this.done = (failures, exit) => | ||
done(this.output, reporterOptions, this.config, failures, exit); | ||
@@ -128,3 +130,3 @@ // Reset total tests counter | ||
// Add a unique identifier to each suite/test/hook | ||
[ 'suite', 'test', 'hook', 'pending' ].forEach(type => { | ||
['suite', 'test', 'hook', 'pending'].forEach(type => { | ||
runner.on(type, item => { | ||
@@ -135,2 +137,65 @@ 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 | ||
@@ -145,20 +210,24 @@ runner.on('end', () => { | ||
const rootSuite = mapSuites(this.runner.suite, totalTestsRegistered, this.config); | ||
const rootSuite = mapSuites( | ||
this.runner.suite, | ||
totalTestsRegistered, | ||
this.config | ||
); | ||
const obj = { | ||
stats: this.stats, | ||
results: [ rootSuite ], | ||
results: [rootSuite], | ||
meta: { | ||
mocha: { | ||
version: mochaPkg.version | ||
version: mochaPkg.version, | ||
}, | ||
mochawesome: { | ||
options: this.config, | ||
version: pkg.version | ||
version: pkg.version, | ||
}, | ||
marge: { | ||
options: options.reporterOptions, | ||
version: margePkg.version | ||
} | ||
} | ||
version: margePkg.version, | ||
}, | ||
}, | ||
}; | ||
@@ -174,3 +243,3 @@ | ||
obj.stats.pendingPercent = pendingPercentage; | ||
obj.stats.other = (passes + failures + pending) - tests; // Failed hooks | ||
obj.stats.other = passes + failures + pending - tests; // Failed hooks | ||
obj.stats.hasOther = obj.stats.other > 0; | ||
@@ -177,0 +246,0 @@ obj.stats.skipped = testsRegistered - tests; |
@@ -22,26 +22,32 @@ /* eslint-disable consistent-return */ | ||
switch (state) { | ||
case BEGIN: | ||
switch (c) { | ||
case '{': return LBRACE; | ||
case '=': return EQ; | ||
default: return BEGIN; | ||
} | ||
case BEGIN: | ||
switch (c) { | ||
case '{': | ||
return LBRACE; | ||
case '=': | ||
return EQ; | ||
default: | ||
return BEGIN; | ||
} | ||
case LBRACE: | ||
return c === ' ' ? LBRACE : DONE; | ||
case LBRACE: | ||
return c === ' ' ? LBRACE : DONE; | ||
case EQ: | ||
return c === '>' ? ARROW : BEGIN; | ||
case EQ: | ||
return c === '>' ? ARROW : BEGIN; | ||
case ARROW: | ||
if (isWhitespace(c)) return ARROW; | ||
switch (c) { | ||
case '{': return ARROW_LBRACE; | ||
case '(': return ARROW_PAREN; | ||
default: return DONE; | ||
} | ||
case ARROW: | ||
if (isWhitespace(c)) return ARROW; | ||
switch (c) { | ||
case '{': | ||
return ARROW_LBRACE; | ||
case '(': | ||
return ARROW_PAREN; | ||
default: | ||
return DONE; | ||
} | ||
case ARROW_LBRACE: | ||
case ARROW_PAREN: | ||
return DONE; | ||
case ARROW_LBRACE: | ||
case ARROW_PAREN: | ||
return DONE; | ||
} | ||
@@ -48,0 +54,0 @@ }; |
@@ -52,3 +52,6 @@ const isString = require('lodash.isstring'); | ||
/* istanbul ignore next */ | ||
const indentRegex = new RegExp(`^\n?${tabs ? '\t' : ' '}{${tabs || spaces}}`, 'gm'); | ||
const indentRegex = new RegExp( | ||
`^\n?${tabs ? '\t' : ' '}{${tabs || spaces}}`, | ||
'gm' | ||
); | ||
@@ -69,3 +72,4 @@ str = str.replace(indentRegex, '').trim(); | ||
function createUnifiedDiff({ actual, expected }) { | ||
return diff.createPatch('string', actual, expected) | ||
return diff | ||
.createPatch('string', actual, expected) | ||
.split('\n') | ||
@@ -120,3 +124,7 @@ .splice(4) | ||
// Format actual/expected for creating diff | ||
if (showDiff !== false && sameType(actual, expected) && expected !== undefined) { | ||
if ( | ||
showDiff !== false && | ||
sameType(actual, expected) && | ||
expected !== undefined | ||
) { | ||
/* istanbul ignore if */ | ||
@@ -127,3 +135,5 @@ if (!(isString(actual) && isString(expected))) { | ||
} | ||
errDiff = config.useInlineDiffs ? createInlineDiff(err) : createUnifiedDiff(err); | ||
errDiff = config.useInlineDiffs | ||
? createInlineDiff(err) | ||
: createUnifiedDiff(err); | ||
} | ||
@@ -142,3 +152,3 @@ | ||
estack: stack && stripAnsi(stack), | ||
diff: errDiff | ||
diff: errDiff, | ||
}; | ||
@@ -156,3 +166,3 @@ } | ||
function cleanTest(test, config) { | ||
const code = config.code ? (test.body || '') : ''; | ||
const code = config.code ? test.body || '' : ''; | ||
@@ -176,8 +186,9 @@ const fullTitle = isFunction(test.fullTitle) | ||
err: (test.err && normalizeErr(test.err, config)) || {}, | ||
uuid: test.uuid || /* istanbul ignore next: default */uuid.v4(), | ||
uuid: test.uuid || /* istanbul ignore next: default */ uuid.v4(), | ||
parentUUID: test.parent && test.parent.uuid, | ||
isHook: test.type === 'hook' | ||
isHook: test.type === 'hook', | ||
}; | ||
cleaned.skipped = (!cleaned.pass && !cleaned.fail && !cleaned.pending && !cleaned.isHook); | ||
cleaned.skipped = | ||
!cleaned.pass && !cleaned.fail && !cleaned.pending && !cleaned.isHook; | ||
@@ -203,9 +214,9 @@ return cleaned; | ||
const beforeHooks = [].concat( | ||
suite._beforeAll, suite._beforeEach | ||
).map(test => cleanTest(test, config)); | ||
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 afterHooks = [] | ||
.concat(suite._afterAll, suite._afterEach) | ||
.map(test => cleanTest(test, config)); | ||
@@ -225,3 +236,3 @@ const tests = suite.tests.map(test => { | ||
const cleaned = { | ||
uuid: suite.uuid || /* istanbul ignore next: default */uuid.v4(), | ||
uuid: suite.uuid || /* istanbul ignore next: default */ uuid.v4(), | ||
title: stripAnsi(suite.title), | ||
@@ -241,9 +252,10 @@ fullFile: suite.file || '', | ||
rootEmpty: suite.root && tests.length === 0, | ||
_timeout: suite._timeout | ||
_timeout: suite._timeout, | ||
}; | ||
const isEmptySuite = isEmpty(cleaned.suites) | ||
&& isEmpty(cleaned.tests) | ||
&& isEmpty(cleaned.beforeHooks) | ||
&& isEmpty(cleaned.afterHooks); | ||
const isEmptySuite = | ||
isEmpty(cleaned.suites) && | ||
isEmpty(cleaned.tests) && | ||
isEmpty(cleaned.beforeHooks) && | ||
isEmpty(cleaned.afterHooks); | ||
@@ -279,3 +291,3 @@ return !isEmptySuite && cleaned; | ||
cleanSuite, | ||
mapSuites | ||
mapSuites, | ||
}; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
46525
11
722
216
11