testrail-jest-reporter
Advanced tools
Comparing version 1.0.8 to 1.1.0
54
index.js
@@ -8,3 +8,3 @@ 'use strict'; | ||
const message = chalk.bold.green; | ||
const {baseUrl, regex, milestone, project_id, user, pass} = require(configPath); | ||
const {baseUrl, regex, milestone, project_id, suite_mode, user, pass} = require(configPath); | ||
const Utils = require('./src/utils'); | ||
@@ -14,4 +14,3 @@ const caller = require('./src/caller'); | ||
class CustomTestrailReporter { | ||
tests = null | ||
results = [] | ||
/** | ||
@@ -29,5 +28,8 @@ * constructor for the reporter | ||
this._options.project_id = _options && _options.project_id || project_id; | ||
this._options.suite_mode = _options && _options.suite_mode || suite_mode; | ||
this._options.run_update = (_options && _options.hasOwnProperty('publish_results')) ? _options.publish_results : true; | ||
this._options.auth = 'Basic ' + new Buffer.from(user + ':' + pass, 'utf-8').toString('base64'); | ||
caller.init(this._options); | ||
this._utils = new Utils({regex: regex || null, statuses: _options && _options.statuses}); | ||
this.results = [] | ||
} | ||
@@ -43,10 +45,9 @@ | ||
onRunStart(_results, _options) { | ||
if (this._options.project_id) { | ||
caller.get_tests() | ||
.then(_tests => this.tests = _tests); | ||
if (this._options.project_id && !isNaN(this._options.project_id) && this._options.milestone) { | ||
caller.get_milestone_id(); | ||
} | ||
else { | ||
console.log(error(`! Testrail Jest Reporter Error !`)); | ||
console.log(warning(`You must define "project_id" in jets configurations! | ||
\n Example: "reporters": [ ["testrail-jest-reporter", { "project_id": "1" }] ]`)); | ||
console.log(warning(`You must define "project_id" and "milestone" in jets configurations! | ||
\n Example: "reporters": [ ["testrail-jest-reporter", { "project_id": "1", "milestone": "Sprint 1" }] ]`)); | ||
} | ||
@@ -73,6 +74,10 @@ } | ||
onTestResult(_test, _testResults, _aggregatedResult) { | ||
if (this.tests) { | ||
if (caller._milestone_id) { | ||
_testResults.testResults.forEach((result) => { | ||
const testcases = this._utils.formatCase(result); | ||
if (testcases) this._accumulateResults(testcases); | ||
if (testcases) { | ||
for (let i=0, len = testcases.length; i<len; i++) { | ||
this.results.push(testcases[i]) | ||
} | ||
} | ||
}); | ||
@@ -90,7 +95,16 @@ } | ||
onRunComplete(_contexts, _results) { | ||
caller.add_results(this.results) | ||
.then(count => { | ||
if (count) console | ||
.log(message(`Testrail Jest Reporter updated ${count} tests in ${this.results.length} runs.`)); | ||
}); | ||
if (caller._milestone_id) { | ||
console.log(message('Testrail Jest Reporter is updating tests results...')); | ||
caller.get_tests() | ||
.then(() => { | ||
return caller.add_results(this.results) | ||
}) | ||
.then(({tests_count, runs_count}) => { | ||
if (tests_count) console | ||
.log(message(`\nTestrail Jest Reporter updated ${tests_count} tests in ${runs_count} runs.`)); | ||
}) | ||
.catch(e => { | ||
console.log(error(`! Testrail Jest Reporter Error !\n${e.stack}`)); | ||
}); | ||
} | ||
} | ||
@@ -103,14 +117,4 @@ | ||
} | ||
_accumulateResults(testcases_list) { | ||
for (let i=0, len = testcases_list.length; i<len; i++) { | ||
let index = -1; | ||
const test = this.tests.find(test => test.case_id === testcases_list[i].case_id); | ||
const run_id = test && test.run_id; | ||
if (run_id && !!this.results.length) index = this.results.findIndex(run => run.id === run_id); | ||
if (~index) this.results[index].results.push(testcases_list[i]); | ||
else if (run_id) this.results.push({id: run_id, "results": [testcases_list[i]]}); | ||
} | ||
} | ||
} | ||
module.exports = CustomTestrailReporter; |
{ | ||
"name": "testrail-jest-reporter", | ||
"version": "1.0.8", | ||
"version": "1.1.0", | ||
"description": "Custom Jest reporter for Testrail synchronization", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
@@ -22,2 +22,6 @@ [![TestRail v6.7](https://img.shields.io/badge/TestRail%20API-v2-green.svg)](http://docs.gurock.com/testrail-api2/start) [![NPM](https://img.shields.io/npm/l/testrail-jest-reporter)](https://github.com/AntonChaukin/testrail-jest-reporter/blob/main/LICENSE) [![NPM](https://img.shields.io/node/v/testrail-jest-reporter)](https://github.com/AntonChaukin/testrail-jest-reporter/blob/main/package.json) | ||
- Specify the TestRail Milestone name as parameter 'milestone' _(recommended)_. | ||
- Specify the TestRail **`suite mode`** id as parameter 'suite_mode' _(recommended)_. If that parameter is not specified, the Reporter will get this automatically. | ||
>single repository for all cases - `suite_mode:1`<br> | ||
single repository with baseline support - `suite_mode:2`<br> | ||
multiple test suites to manage cases - `suite_mode:3`<br> | ||
- There is no 'pending' or 'skipped' test result status in the TestRail results default <br>statuses. | ||
@@ -35,6 +39,7 @@ You can add your custom status to the TestRail and specify it id as parameter | ||
"jest-2-testrail", | ||
{ project_id: "1", | ||
{ project_id: 1, | ||
baseUrl: 'http://localhost', | ||
milestone: '<milestone_name>', | ||
statuses: {pending: "7"} | ||
suite_mode: 3, | ||
statuses: {pending: 7} | ||
}, | ||
@@ -58,2 +63,3 @@ ] | ||
"milestone": '<milestone_name>', | ||
"suite_mode": "3", | ||
"statuses": {"pending": "7"} | ||
@@ -100,5 +106,6 @@ } | ||
### Add TestRail tests Runs | ||
The first version of the Reporter requires you to add tests Runs with all tests you want to automate. | ||
The Reporter parse all TestRail tests Plans | ||
<br>and test Runs of the Milestone to collect testcases. | ||
You can add a TestRail tests Runs or tests Plan with all tests you want to automate.<br> | ||
If you don't, the Reporter will publish Jest tests results into the new TestRail test Run.<br> | ||
Each time the Jest runs tests the Reporter parse all TestRail tests Plans | ||
<br>and tests Runs of the Milestone to collect testcases. | ||
The Reporter collects only unique testcases, | ||
@@ -137,3 +144,3 @@ <br>if you have several tests Runs with one testcase | ||
**This version:** | ||
- Add new tests Run if there are testcases that are not present in any of the existing TestRail tests Runs. | ||
- ~~Add new tests Run if there are testcases that are not present in any of the existing TestRail tests Runs.~~ >> **Done in 1.1.0** | ||
- Add new test Runs if the Milestone not specified. | ||
@@ -140,0 +147,0 @@ - Add new TestRail Milestone if the specified Milestone not present in the Project. |
'use strict'; | ||
const chalk = require('chalk'), tr_api = require('./interface'), ReporterError = require('./error'); | ||
const chalk = require('chalk'), tr_api = require('../lib/interface'), ReporterError = require('../lib/error'), | ||
Utils = require('./utils'); | ||
const utils = new Utils(); | ||
const Ajv = require("ajv").default; | ||
const ajv = new Ajv({ | ||
strict: false, | ||
allErrors: true, | ||
}); | ||
const error = chalk.bold.red; | ||
module.exports = { | ||
init: init, | ||
add_results: add_results, | ||
get_tests: get_tests | ||
init, | ||
get_milestone_id, | ||
add_results, | ||
get_tests | ||
} | ||
@@ -15,2 +23,4 @@ | ||
this._project_id = _options.project_id; | ||
this._suite_mode = _options.suite_mode; | ||
this._run_update = _options.run_update; | ||
tr_api.defaults.headers['Authorization'] = _options.auth; | ||
@@ -20,37 +30,59 @@ tr_api.defaults.baseUrl = this._baseUrl + '/index.php?/api/v2/'; | ||
function add_results(testsResults) { | ||
return Promise.all( | ||
testsResults.map(run => { | ||
return tr_api.add_results_for_cases(run.id, {"results": run.results}); | ||
}) | ||
) | ||
.then(response => { | ||
let count = 0; | ||
response.map(run => { | ||
run.map((result) => { | ||
if (result && result.id) count++; | ||
}); | ||
/** | ||
* | ||
* @param {array<object>} testsResults | ||
* @return {Promise<number | boolean>} | ||
*/ | ||
async function add_results(testsResults) { | ||
const results = utils.groupCases(testsResults, this._tests); | ||
let runs = results.filter(result => result.hasOwnProperty('run_id')); | ||
const cases = results.filter(result => result.hasOwnProperty('case_id')); | ||
if (!!cases.length) { | ||
const updated_runs = await update_run.call(this, cases); | ||
runs = runs.concat(updated_runs); | ||
} | ||
return Promise.all( | ||
runs.map(run => { | ||
return tr_api.add_results_for_cases(run.run_id, {"results": run.results}); | ||
}) | ||
) | ||
.then(response => { | ||
let tests_count = 0; | ||
response.map(run => { | ||
run.map((result) => { | ||
if (result && result.id) tests_count++; | ||
}); | ||
return count; | ||
}) | ||
.catch((err) => { | ||
console.log(error(err)); | ||
return false; | ||
}); | ||
let runs_ids = runs.map(run => run.run_id).filter((id, i, arr) => !arr.includes(id,i+1)) | ||
return {tests_count, runs_count: runs_ids.length}; | ||
}) | ||
.catch((err) => { | ||
console.log(error(err)); | ||
return false; | ||
}); | ||
} | ||
function get_tests() { | ||
function get_milestone_id() { | ||
this._milestone_id = null; | ||
this._runs_ids = []; | ||
this._tests = []; | ||
return tr_api.get_milestones(this._project_id, {is_completed: 0}) | ||
return get_suite_mode.call(this) | ||
.then(() => tr_api.get_milestones(this._project_id, {is_completed: 0})) | ||
.then(res => { | ||
const _milestone = res.filter((milestone) => milestone.name === this._milestone_name); | ||
if (_milestone && !!_milestone.length) { | ||
this._milestone_id = _milestone[0].id; | ||
return tr_api.get_plans(this._project_id, {is_completed: 0, milestone_id: this._milestone_id}); | ||
} else { | ||
throw new ReporterError(`Can not find milestone with name ${this._milestone_name}! | ||
\nNo one tests results will be reported. | ||
\nPlease, check the "milestone" param you specified in congif and try again`) | ||
} | ||
return false; | ||
}) | ||
.catch(e => { | ||
console.log(error(e.stack)); | ||
}) | ||
} | ||
function get_tests() { | ||
this._runs_ids = []; | ||
this._tests = []; | ||
return tr_api.get_plans(this._project_id, {is_completed: 0, milestone_id: this._milestone_id}) | ||
.then(res => { | ||
@@ -71,3 +103,7 @@ if (res) { | ||
for (let j=0, len = plan.entries.length; j<len; j++) { | ||
plan.entries[j].runs.map(run => this._runs_ids.push(run.id)); | ||
plan.entries[j].runs | ||
.map(run => { | ||
this._runs_ids | ||
.push({id: run.id, suite_id: run.suite_id, plan_id: run.plan_id}) | ||
}); | ||
} | ||
@@ -88,7 +124,8 @@ } | ||
for (let i=0, len = res.length; i<len; i++) { | ||
if (res[i] && res[i].id) this._runs_ids.push(res[i].id); | ||
if (res[i] && res[i].id) this._runs_ids | ||
.push({id: res[i].id, suite_id: res[i].suite_id, plan_id: null}); | ||
} | ||
} | ||
if (!!this._runs_ids.length) { | ||
return Promise.all(this._runs_ids.map(id => tr_api.get_tests(id))); | ||
return Promise.all(this._runs_ids.map(run => tr_api.get_tests(run.id))); | ||
} | ||
@@ -102,4 +139,5 @@ return false; | ||
for(let j=0, t_len=tests.length; j<t_len; j++) { | ||
if (tests[j] && tests[j].case_id) this._tests | ||
.push({"case_id": tests[j].case_id, "run_id": tests[j].run_id}) | ||
if (tests[j] && tests[j].case_id) { | ||
this._tests.push({"case_id": tests[j].case_id, "run_id": tests[j].run_id}) | ||
} | ||
} | ||
@@ -111,11 +149,157 @@ } | ||
.filter((test, i, arr) => !arr.slice(i+1).find(t => t.case_id === test.case_id)); | ||
return this._tests; | ||
} | ||
throw new ReporterError(`There is no one Testrail testcase was finding in Project id=${this._project_id} | ||
by milestone "${this._milestone_name}"`); | ||
}) | ||
.catch((err) => { | ||
console.log(error(err.stack)); | ||
return false; | ||
}); | ||
} | ||
function get_suite_mode() { | ||
if (!this._suite_mode) { | ||
return tr_api.get_project(this._project_id) | ||
.then(resp => this._suite_mode = resp.suite_mode) | ||
.catch(e => { | ||
this._suite_mode = 2; | ||
console.log(error(`The trying to get TestRail Project was failed! | ||
\n The suite mode was defined as 2 by default. | ||
\n Context: ${e.stack}`)) | ||
}); | ||
} | ||
return Promise.resolve(); | ||
} | ||
/** | ||
* | ||
* @param {object} cases | ||
* @return {Promise<[]>} | ||
*/ | ||
async function update_run(cases) { | ||
const valid = ajv.validate({ | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"case_id": { | ||
"type": "integer" | ||
}, | ||
"result": { | ||
"type": "object", | ||
"properties": { | ||
"case_id": { | ||
"type": "integer" | ||
}, | ||
"status_id": { | ||
"type": "integer" | ||
}, | ||
"comment": { | ||
"type": "string" | ||
}, | ||
"elapsed": { | ||
"type": "string" | ||
}, | ||
"defects": { | ||
"type": "string" | ||
}, | ||
"version": { | ||
"type": "string" | ||
} | ||
}, | ||
} | ||
}, | ||
"required": ["case_id", "result"] | ||
} | ||
}, | ||
cases); | ||
if (!valid) { | ||
console.log(error(`\nCan not update cases of test JSON schema error | ||
\nContext: ${JSON.stringify(cases)}\n${JSON.stringify(ajv.errors, null, 2)}`)); | ||
return []; | ||
} | ||
let run_data; | ||
let suits = []; | ||
let runs = []; | ||
let _update = false; | ||
if(this._run_update && !!this._runs_ids.length) _update = true | ||
if (this._suite_mode === 1) { | ||
let case_ids = cases.map(c => c.case_id); | ||
let results = cases.map(c => c.result); | ||
suits.push({case_ids, results}) | ||
} else { | ||
for (let i=0, i_len=cases.length; i<i_len; i++) { | ||
let suite_id = null; | ||
try { | ||
const resp = await tr_api.get_case(cases[i].case_id); | ||
suite_id = resp.suite_id; | ||
} | ||
catch (e) { | ||
console.log(error(e.stack)); | ||
} | ||
const index = suits.findIndex(suite => suite.suite_id === suite_id); | ||
if (~index) { | ||
suits[index].case_ids.push(cases[i].case_id); | ||
suits[index].results.push(cases[i].result); | ||
} else if (suite_id) { | ||
suits.push({suite_id, case_ids: [cases[i].case_id], results: [cases[i].result]}) | ||
} | ||
} | ||
} | ||
for (let j=0, j_len=suits.length; j<j_len; j++) { | ||
let run = null; | ||
if (_update && suits[j].suite_id) { | ||
run = this._runs_ids.find(run => run.suite_id === suits[j].suite_id && !run.plan_id); | ||
} else if (_update) {run = this._runs_ids[0]} | ||
if (run) { | ||
for (let i=0, len=this._tests.length; i<len; i++) { | ||
if(this._tests[i].run_id === run.id) { | ||
suits[j].case_ids.push(this._tests[i].case_id) | ||
} | ||
} | ||
run_data = {"include_all": false, "case_ids": suits[j].case_ids, "milestone_id": this._milestone_id}; | ||
try { | ||
const {id} = await tr_api.update_run(run.id, run_data); | ||
runs.push({run_id: id, results: suits[j].results}); | ||
} | ||
catch (e) { | ||
console.log(error(e.stack)); | ||
} | ||
} else { | ||
const today = new Date; | ||
let suite_name = 'Automated Run'; | ||
if(suits[j].suite_id) { | ||
try { | ||
const {name} = await tr_api.get_suite(suits[j].suite_id); | ||
suite_name = name; | ||
} | ||
catch (e) { | ||
console.log(error(e.stack)); | ||
} | ||
run_data = {"suite_id": suits[j].suite_id, | ||
"name": suite_name + ` ${today.getDate()}.${today.getMonth() + 1}.${today.getFullYear()}`, | ||
"include_all": false, | ||
"case_ids": suits[j].case_ids, | ||
"milestone_id": this._milestone_id | ||
}; | ||
} | ||
else { | ||
run_data = {"name": suite_name + ` ${today.getDate()}.${today.getMonth()+1}.${today.getFullYear()}`, | ||
"include_all": false, | ||
"case_ids": suits[j].case_ids, | ||
"milestone_id": this._milestone_id | ||
}; | ||
} | ||
try { | ||
const {id} = await tr_api.add_run(this._project_id, run_data); | ||
runs.push({run_id: id, results: suits[j].results}) | ||
} | ||
catch (e) { | ||
console.log(error(e.stack)); | ||
} | ||
} | ||
} | ||
return runs; | ||
} |
167
src/utils.js
'use strict'; | ||
require('@babel/plugin-syntax-class-properties'); | ||
const chalk = require('chalk'), Ajv = require("ajv").default; | ||
const ajv = new Ajv({ | ||
strict: false, | ||
allErrors: true, | ||
}); | ||
const error = chalk.bold.red; | ||
@@ -66,3 +73,3 @@ class Utils { | ||
for (let i=0, len=_t.length; i<len; i++) { | ||
case_ids.push(_t[i].match(/[?\d]{3,6}/gm)) | ||
case_ids.push(_t[i].match(/\d+/gm)[0]) | ||
} | ||
@@ -96,70 +103,108 @@ return case_ids.length && case_ids; | ||
merge = merge | ||
isPlainObject = isPlainObject | ||
isArray = isArray | ||
} | ||
module.exports = Utils; | ||
/** | ||
* Accepts varargs expecting each argument to be an object, then | ||
* immutably merges the properties of each object and returns result. | ||
* | ||
* When multiple objects contain the same key the later object in | ||
* the arguments list will take precedence. | ||
* | ||
* Example: | ||
* | ||
* ```js | ||
* var result = merge({foo: 123}, {foo: 456}); | ||
* console.log(result.foo); // outputs 456 | ||
* ``` | ||
* | ||
* @param {Object} obj1 Object to merge | ||
* @returns {Object} Result of all merge properties | ||
*/ | ||
function merge(/* obj1, obj2, obj3, ... */) { | ||
let result = {}; | ||
function assignValue(val, key) { | ||
if (isPlainObject(result[key]) && isPlainObject(val)) { | ||
result[key] = merge(result[key], val); | ||
} else if (isPlainObject(val)) { | ||
result[key] = merge({}, val); | ||
} else if (isArray(val)) { | ||
result[key] = val.slice(); | ||
} else { | ||
result[key] = val; | ||
/** | ||
* Group jest test results in a testrail runs or a single cases | ||
* | ||
* @param {array} jest_result_list | ||
* @param {array} tr_tests | ||
* @return {[] || [{case_id, result}, {run_id, results}]} | ||
*/ | ||
groupCases(jest_result_list, tr_tests) { | ||
const valid_results = ajv.validate({ | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"case_id": { | ||
"type": "integer" | ||
}, | ||
"status_id": { | ||
"type": "integer" | ||
}, | ||
"comment": { | ||
"type": "string" | ||
}, | ||
"elapsed": { | ||
"type": "string" | ||
}, | ||
"defects": { | ||
"type": "string" | ||
}, | ||
"version": { | ||
"type": "string" | ||
} | ||
}, | ||
"required": ["case_id", "status_id", "comment", "elapsed"] | ||
} | ||
}, | ||
jest_result_list); | ||
if (!valid_results ) { | ||
console.log(error(`\nCan not update cases of test JSON schema error | ||
\nContext: ${JSON.stringify(ajv.errors, null, 2)}`)); | ||
return []; | ||
} | ||
const valid_tests_list = ajv.validate({ | ||
"type": "array", | ||
"items": { | ||
"type": "object", | ||
"properties": { | ||
"case_id": { | ||
"type": "integer" | ||
}, | ||
"run_id": { | ||
"type": "integer" | ||
} | ||
}, | ||
"required": ["case_id", "run_id"] | ||
} | ||
}, | ||
tr_tests); | ||
if (!valid_tests_list) { | ||
console.log(error(`\nCan not update cases of test JSON schema error | ||
\nContext: ${JSON.stringify(ajv.errors, null, 2)}`)); | ||
return []; | ||
} | ||
let results = []; | ||
for (let i=0, len = jest_result_list.length; i<len; i++) { | ||
const test = tr_tests ? | ||
tr_tests.find(test => test.case_id === jest_result_list[i].case_id) | ||
: null; | ||
if (test) { | ||
const index = results.findIndex(run => run.run_id === test.run_id); | ||
if (~index) results[index].results.push(jest_result_list[i]); | ||
else results.push({run_id: test.run_id, "results": [jest_result_list[i]]}); | ||
} else { | ||
results.push({case_id: jest_result_list[i].case_id, "result": jest_result_list[i]}) | ||
} | ||
} | ||
return results; | ||
} | ||
for (let i = 0, l = arguments.length; i < l; i++) { | ||
this.forEach(arguments[i], assignValue); | ||
/** | ||
* Determine if a value is a plain Object | ||
* | ||
* @param {Object} val The value to test | ||
* @return {boolean} True if value is a plain Object, otherwise false | ||
*/ | ||
isPlainObject(val) { | ||
if (toString.call(val) !== '[object Object]') { | ||
return false; | ||
} | ||
const prototype = Object.getPrototypeOf(val); | ||
return prototype === null || prototype === Object.prototype; | ||
} | ||
return result; | ||
} | ||
/** | ||
* Determine if a value is a plain Object | ||
* | ||
* @param {Object} val The value to test | ||
* @return {boolean} True if value is a plain Object, otherwise false | ||
*/ | ||
function isPlainObject(val) { | ||
if (toString.call(val) !== '[object Object]') { | ||
return false; | ||
/** | ||
* Determine if a value is an Array | ||
* | ||
* @param {Object} val The value to test | ||
* @returns {boolean} True if value is an Array, otherwise false | ||
*/ | ||
isArray(val) { | ||
return toString.call(val) === '[object Array]'; | ||
} | ||
const prototype = Object.getPrototypeOf(val); | ||
return prototype === null || prototype === Object.prototype; | ||
} | ||
/** | ||
* Determine if a value is an Array | ||
* | ||
* @param {Object} val The value to test | ||
* @returns {boolean} True if value is an Array, otherwise false | ||
*/ | ||
function isArray(val) { | ||
return toString.call(val) === '[object Array]'; | ||
} | ||
module.exports = Utils; | ||
@@ -13,2 +13,3 @@ const baseUrl = process.env.URL; | ||
//'milestone': '<milestone_name>' | ||
//'suite_mode': '<suite_mode>' | ||
} |
@@ -16,3 +16,4 @@ const faker = require('faker'), Utils = require('../src/utils'); | ||
tr_plan: tr_plan, | ||
tr_milestone: tr_milestone | ||
tr_milestone: tr_milestone, | ||
tr_get_project | ||
}; | ||
@@ -337,2 +338,22 @@ | ||
/** | ||
* | ||
* @param {object} options | ||
* @param {number || string} options.suite_mode | ||
* @return {{suite_mode: *, completed_on: null, name: string, id: number, is_completed: boolean, show_announcement: boolean, url: string, announcement: string}} | ||
*/ | ||
function tr_get_project(options) { | ||
const _suite_mode = options && options.suite_mode || faker.random.arrayElement([1,2,3]); | ||
return { | ||
"announcement": "..", | ||
"completed_on": null, | ||
"id": 1, | ||
"is_completed": false, | ||
"name": "Datahub", | ||
"show_announcement": true, | ||
"suite_mode": _suite_mode, | ||
"url": "http:///testrail/index.php?/projects/overview/1" | ||
} | ||
} | ||
function count(start) { | ||
@@ -339,0 +360,0 @@ let _count = start; |
Sorry, the diff of this file is not supported yet
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
142283
21
3186
159
1