add: grunt task 'local_coverage' (#2095)

* Add: 'js-beautify' and 'c8' in package.json

And update other package to the latest version.

* eslint must ignore generated "schema.json.translated.to.js"

* .gitignore must ignore src/temp

This directory will be autogenerated by make file "coverage"

* add: grunt task 'local_coverage'

This task will can be called via the makefile
And generate a HTML report files stored in 'src/temp/coverage/report/index.html'
This commit is contained in:
Gerry Ferdinandus
2022-02-20 19:26:17 +01:00
committed by GitHub
parent 879c52c6eb
commit cbfcc17f80
6 changed files with 1217 additions and 608 deletions

1
.gitignore vendored
View File

@@ -126,6 +126,7 @@ ClientBin/
*.pfx
*.publishsettings
.DS_Store
src/temp
# RIA/Silverlight projects
Generated_Code/

View File

@@ -1,4 +1,4 @@
.PHONY: build remote
.PHONY: build remote maintenance coverage
build: # Run the local build process
cd src && \
@@ -18,3 +18,12 @@ maintenance: # Run the maintenance check
cd src && \
npm install && \
npm run maintenance
# For a specific schema, generate a coverage report in src/temp/coverage/report/index.html
# Example: via 'make' to generate coverage report for schema-catalog.json
# make coverage schema=schema-catalog.json
coverage: # generate HTML coverage report
cd src && \
npx c8 --temp-directory temp/coverage -x 'Gruntfile.js' grunt local_coverage --SchemaName=$(schema) && \
npx c8 --temp-directory temp/coverage report -r html -o temp/coverage/report -x 'Gruntfile.js' && \
echo "Full HTML report files stored in 'src/temp/coverage/report/index.html'"

View File

@@ -1,14 +1,17 @@
{
"env": {
"es2020": true,
"node": true
},
"extends": [
"standard"
],
"parserOptions": {
"ecmaVersion": 11
},
"rules": {
}
"env": {
"es2020": true,
"node": true
},
"extends": [
"standard"
],
"parserOptions": {
"ecmaVersion": 11
},
"rules": {
},
"ignorePatterns": [
"schema.json.translated.to.js"
]
}

View File

@@ -7,6 +7,7 @@ const Ajv2019 = require('ajv/dist/2019')
const Ajv2020 = require('ajv/dist/2020')
const pt = require('path')
const fs = require('fs')
const temporaryCoverageDir = 'temp'
const schemaDir = 'schemas/json'
const testPositiveDir = 'test'
const testNegativeDir = 'negative_test'
@@ -26,7 +27,6 @@ const countSchemasType = [
{ schemaName: 'draft-03', schemaStr: 'json-schema.org/draft-03/schema', totalCount: 0, active: false },
{ schemaName: 'draft without version', schemaStr: 'json-schema.org/schema', totalCount: 0, active: false }
]
module.exports = function (grunt) {
'use strict'
@@ -129,7 +129,8 @@ module.exports = function (grunt) {
{
fullScanAllFiles = false,
calledByTV4Validator = false,
skipReadFile = true
skipReadFile = true,
processOnlyThisOneSchemaFile = undefined
} = {}) {
/**
* @summary Check if the present json schema file must be tested or not
@@ -162,6 +163,9 @@ module.exports = function (grunt) {
}
// Process all the schema files one by one via callback.
schemasToBeTested.forEach((schemaFileName) => {
if (processOnlyThisOneSchemaFile) {
if (schemaFileName !== processOnlyThisOneSchemaFile) return
}
const schemaFullPathName = pt.join(schemaDir, schemaFileName)
// Some schema files must be ignored.
@@ -332,9 +336,11 @@ module.exports = function (grunt) {
* @param {string} schemaName
* @param {string[]} unknownFormatsList
* @param {boolean} fullStrictMode
* @param {boolean} standAloneCode
* @param {string[]} standAloneCodeWithMultipleSchema
* @returns {Object}
*/
function factoryAJV (schemaName, unknownFormatsList = [], fullStrictMode = true) {
function factoryAJV ({ schemaName, unknownFormatsList = [], fullStrictMode = true, standAloneCode = false, standAloneCodeWithMultipleSchema = [] } = {}) {
// some AJV default setting are [true, false or log]
// Some options are default: 'log'
// 'log' will generate a lot of noise in the build log. So make it true or false.
@@ -350,6 +356,13 @@ module.exports = function (grunt) {
}
const ajvOptions = fullStrictMode ? ajvOptionsStrictMode : ajvOptionsNotStrictMode
// Stand-alone code need some special options parameters
if (standAloneCode) {
ajvOptions.code = { source: true }
if (standAloneCodeWithMultipleSchema.length) {
ajvOptions.schemas = standAloneCodeWithMultipleSchema
}
}
let ajvSelected
// There are multiple AJV version for each $schema version.
// Create the correct one.
@@ -455,7 +468,11 @@ module.exports = function (grunt) {
versionObj = schemaVersion.getObj(schemaJson)
// Get the correct AJV version
ajvSelected = factoryAJV(versionObj?.schemaName, unknownFormatsList, fullStrictMode)
ajvSelected = factoryAJV({
schemaName: versionObj?.schemaName,
unknownFormatsList: unknownFormatsList,
fullStrictMode: fullStrictMode
})
// AJV must ignore these keywords
unknownKeywordsList?.forEach((x) => {
@@ -597,7 +614,7 @@ module.exports = function (grunt) {
grunt.registerTask('local_catalog', 'Catalog validation', function () {
const catalogSchema = require(pt.resolve('.', schemaDir, 'schema-catalog.json'))
const ajvInstance = factoryAJV('draft-04', [])
const ajvInstance = factoryAJV({ schemaName: 'draft-04' })
if (ajvInstance.validate(catalogSchema, catalog)) {
grunt.log.ok('catalog.json OK')
} else {
@@ -790,7 +807,11 @@ module.exports = function (grunt) {
const validateViaAjv = (schemaJson, schemaName, option) => {
try {
const ajvSelected = factoryAJV(schemaName, option.unknownFormatsList, false)
const ajvSelected = factoryAJV({
schemaName: schemaName,
unknownFormatsList: option.unknownFormatsList,
fullStrictMode: false
})
// AJV must ignore these keywords
option.unknownKeywordsList?.forEach((x) => {
@@ -1068,6 +1089,118 @@ module.exports = function (grunt) {
grunt.log.ok(`Total schema-validation.json items check: ${countSchemaValidationItems}`)
})
grunt.registerTask('local_coverage', 'Run one selected schema in coverage mode', function () {
const javaScriptCoverageName = 'schema.json.translated.to.js'
const javaScriptCoverageNameWithPath = pt.join(__dirname, `${temporaryCoverageDir}/${javaScriptCoverageName}`)
/**
* Translate one JSON schema file to javascript via AJV validator.
* And run the positive and negative test files with it.
* @param {string} processOnlyThisOneSchemaFile The schema file that need to process
*/
const generateCoverage = (processOnlyThisOneSchemaFile) => {
const standaloneCode = require('ajv/dist/standalone').default
const schemaVersion = showSchemaVersions()
let jsonName
let mainSchema
let mainSchemaJsonId
let isThisWithExternalSchema
let validations
// Compile JSON schema to javascript and write it to disk.
const processSchemaFile = (callbackParameter) => {
jsonName = callbackParameter.jsonName
// Get possible options define in schema-validation.json
const {
unknownFormatsList,
unknownKeywordsList,
externalSchemaWithPathList
} = getOption(callbackParameter.jsonName)
// select the correct AJV object for this schema
mainSchema = JSON.parse(callbackParameter.rawFile)
const versionObj = schemaVersion.getObj(mainSchema)
// External schema present to be included?
const multipleSchema = []
isThisWithExternalSchema = externalSchemaWithPathList.length > 0
if (isThisWithExternalSchema) {
// There is an external schema that need to be included.
externalSchemaWithPathList.forEach((x) => {
multipleSchema.push(require(x.toString()))
})
// Also add the 'root' schema
multipleSchema.push(mainSchema)
}
// Get the correct AJV version
const ajvSelected = factoryAJV({
schemaName: versionObj?.schemaName,
unknownFormatsList: unknownFormatsList,
fullStrictMode: schemaValidation.ajvFullStrictMode.includes(jsonName),
standAloneCode: true,
standAloneCodeWithMultipleSchema: multipleSchema
})
// AJV must ignore these keywords
unknownKeywordsList?.forEach((x) => {
ajvSelected.addKeyword(x)
})
let moduleCode
if (isThisWithExternalSchema) {
// Multiple schemas are combine to one JavaScript file.
// Must use the root $id/id to call the correct 'main' schema in JavaScript code
mainSchemaJsonId = schemaVersion.getObj(mainSchema).schemaName === 'draft-04' ? mainSchema.id : mainSchema.$id
if (!mainSchemaJsonId) {
throwWithErrorText([`Missing $id or id in ${jsonName}`])
}
moduleCode = standaloneCode(ajvSelected)
} else { // Single schema
mainSchemaJsonId = undefined
moduleCode = standaloneCode(ajvSelected, ajvSelected.compile(mainSchema))
}
// Write the javascript module code to file + 'js-beautify' it
fs.writeFileSync(javaScriptCoverageNameWithPath, require('js-beautify').js(moduleCode, { indent_size: 2 }))
// Now use this JavaScript as validation in the positive and negative test
validations = require(javaScriptCoverageNameWithPath)
}
// Load the Javascript file from the disk and run it with the JSON test file.
// This will generate the NodeJS coverage data in the background.
const processTestFile = (callbackParameter) => {
// Test only for the code coverage. Not for the validity of the test.
if (isThisWithExternalSchema) {
// Must use the root $id/id to call the correct schema JavaScript code
const validateRootSchema = validations[mainSchemaJsonId]
validateRootSchema?.(JSON.parse(callbackParameter.rawFile))
} else {
// Single schema does not need $id
validations(JSON.parse(callbackParameter.rawFile))
}
}
localSchemaFileAndTestFile({
schemaForTestScan: processSchemaFile,
positiveTestScan: processTestFile,
negativeTestScan: processTestFile
}, { skipReadFile: false, processOnlyThisOneSchemaFile: processOnlyThisOneSchemaFile })
}
// Generate the schema via option parameter 'SchemaName'
const schemaNameToBeCoverage = grunt.option('SchemaName')
if (!schemaNameToBeCoverage) {
throwWithErrorText(['Must start "make" file with schema name parameter.'])
}
// Not for tv4 schema files
if (schemaValidation.tv4test.includes(schemaNameToBeCoverage)) {
throwWithErrorText([`Coverage is not possible for tv4-validator schema file :${schemaNameToBeCoverage}`])
}
generateCoverage(schemaNameToBeCoverage)
grunt.log.ok('OK')
})
grunt.registerTask('local_test',
[
'local_check_duplicate_list_in_schema-validation.json',

1628
src/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,6 +13,7 @@
"type": "git",
"url": "https://github.com/schemastore/SchemaStore"
},
"type": "commonjs",
"scripts": {
"build": "npm run eslint_check && grunt",
"eslint_check": "eslint Gruntfile.js",
@@ -21,18 +22,20 @@
"maintenance": "grunt local_maintenance"
},
"devDependencies": {
"ajv": "^8.9.0",
"ajv-draft-04": "^0.1.0",
"ajv": "^8.10.0",
"ajv-draft-04": "^1.0.0",
"ajv-formats": "^2.1.1",
"ajv-formats-draft2019": "^1.6.1",
"eslint": "^7.32.0",
"c8": "^7.11.0",
"eslint": "^8.9.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-promise": "^5.2.0",
"eslint-plugin-promise": "^6.0.0",
"find-duplicated-property-keys": "^1.2.7",
"got": "^11.8.3",
"grunt": "^1.4.1",
"grunt-tv4": "^5.0.1"
"grunt-tv4": "^5.0.1",
"js-beautify": "^1.14.0"
}
}