1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | const assert = require('assert');
|
13 | const {
|
14 | root, heading, paragraph, text, link, table, tableRow, tableCell, inlineCode,
|
15 | } = require('mdast-builder');
|
16 | const unified = require('unified');
|
17 | const stringify = require('remark-stringify');
|
18 | const gfm = require('remark-gfm');
|
19 | const fs = require('fs-extra');
|
20 | const { report } = require('../lib/keywords');
|
21 |
|
22 | const expected = [
|
23 | 'examples',
|
24 | 'allOf',
|
25 | 'json-pointer',
|
26 | 'relative-json-pointer',
|
27 | 'regex',
|
28 | 'uri-template',
|
29 | 'default',
|
30 | ];
|
31 |
|
32 | const allkeywords = {
|
33 | 'The JSON Schema Core Vocabulary, https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.8.1': [
|
34 | '$schema',
|
35 | '$vocabulary',
|
36 | '$id',
|
37 | '$anchor',
|
38 | '$ref',
|
39 | '$recursiveRef',
|
40 | '$recursiveAnchor',
|
41 | '$defs',
|
42 | '$comment',
|
43 | ],
|
44 | 'A Vocabulary for Applying Subschemas, https://json-schema.org/draft/2019-09/json-schema-core.html#rfc.section.9': [
|
45 | 'additionalProperties',
|
46 | 'properties',
|
47 | 'patternProperties',
|
48 | 'unevaluatedProperties',
|
49 | 'additionalItems',
|
50 | 'items',
|
51 | 'unevaluatedItems',
|
52 | 'allOf',
|
53 | 'anyOf',
|
54 | 'oneOf',
|
55 | 'not',
|
56 | 'if',
|
57 | 'then',
|
58 | 'else',
|
59 | 'dependentSchemas',
|
60 | 'contains',
|
61 | 'propertyNames',
|
62 | ],
|
63 | 'Validation Keywords for Any Instance Type, https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.6.1': [
|
64 | 'type',
|
65 | 'enum',
|
66 | 'const',
|
67 | ],
|
68 | 'Validation Keywords for Numeric Instances, https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.6.2': [
|
69 | 'multipleOf',
|
70 | 'maximum',
|
71 | 'exclusiveMaximum',
|
72 | 'minimum',
|
73 | 'exclusiveMinimum',
|
74 | ],
|
75 | 'Validation Keywords for Strings, https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.6.3': [
|
76 | 'maxLength',
|
77 | 'minLength',
|
78 | 'pattern',
|
79 | ],
|
80 | 'Validation Keywords for Arrays, https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.6.4': [
|
81 | 'maxItems',
|
82 | 'minItems',
|
83 | 'uniqueItems',
|
84 | 'maxContains',
|
85 | 'minContains',
|
86 | ],
|
87 | 'Validation Keywords for Objects, https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.6.5': [
|
88 | 'maxProperties',
|
89 | 'minProperties',
|
90 | 'required',
|
91 | 'dependentRequired',
|
92 | ],
|
93 | 'Defined Formats, https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.7.3': [
|
94 | 'date-time',
|
95 | 'date',
|
96 | 'time',
|
97 | 'duration',
|
98 | 'email',
|
99 | 'idn-email',
|
100 | 'hostname',
|
101 | 'idn-hostname',
|
102 | 'ipv4',
|
103 | 'ipv6',
|
104 | 'uri',
|
105 | 'uri-reference',
|
106 | 'iri',
|
107 | 'iri-reference',
|
108 | 'uuid',
|
109 | 'uri-template',
|
110 | 'json-pointer',
|
111 | 'relative-json-pointer',
|
112 | 'regex',
|
113 | ],
|
114 | 'A Vocabulary for the Contents of String-Encoded Data, https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.8': [
|
115 | 'contentEncoding',
|
116 | 'contentMediaType',
|
117 | 'contentSchema',
|
118 | ],
|
119 | 'A Vocabulary for Basic Meta-Data Annotations, https://json-schema.org/draft/2019-09/json-schema-validation.html#rfc.section.9': [
|
120 | 'title',
|
121 | 'description',
|
122 | 'default',
|
123 | 'deprecated',
|
124 | 'readOnly',
|
125 | 'writeOnly',
|
126 | 'examples',
|
127 | ],
|
128 | };
|
129 |
|
130 | after('Generating Schema Coverage Report', () => {
|
131 | const allkeywordsplain = Array.from(new Set(Object
|
132 | .values(allkeywords)
|
133 | .reduce((p, v) => [...p, ...v], [])));
|
134 | const allkeywordssupported = allkeywordsplain
|
135 | .filter((keyword) => report().has(keyword));
|
136 |
|
137 | const overall = Math.floor((100 * allkeywordssupported.length) / allkeywordsplain.length);
|
138 |
|
139 | const sections = Object.entries(allkeywords).map(([name, keywords]) => {
|
140 | const [label, url] = name.split(', ');
|
141 | const coverage = Math.floor(
|
142 | (100
|
143 | * keywords.filter((keyword) => report().has(keyword)).length)
|
144 | / keywords.length,
|
145 | );
|
146 | return [
|
147 | heading(2, text(label)),
|
148 | paragraph([text('Coverage for '), link(url, '', text(label)), text(` is ${coverage}%.`)]),
|
149 | table('left', [
|
150 | tableRow([
|
151 | tableCell(text('Keyword')),
|
152 | tableCell(text('Supported')),
|
153 | ]),
|
154 | ...keywords.sort().map((keyword) => tableRow([
|
155 | tableCell(inlineCode(keyword)),
|
156 | tableCell(text(report().has(keyword) ? 'Yes' : 'No')),
|
157 | ])),
|
158 | ]),
|
159 | ];
|
160 | }).reduce((p, v) => [...p, ...v], []);
|
161 |
|
162 | const mdast = root([
|
163 | heading(1, text('JSON Schema Spec Coverage Report')),
|
164 | paragraph(text(`This report lists the keywords of the JSON Schema spec that are covered in the tests. The overall coverage is ${overall}%`)),
|
165 | ...sections,
|
166 | ]);
|
167 |
|
168 | const processor = unified()
|
169 | .use(gfm)
|
170 | .use(stringify);
|
171 |
|
172 | const output = processor.stringify(mdast);
|
173 |
|
174 | fs.writeFileSync('schemasupport.md', output);
|
175 |
|
176 | const lowerlimit = 50;
|
177 | assert.ok(overall > lowerlimit, `Expected minumum spec coverage of ${lowerlimit}%, got only ${overall}%`);
|
178 | });
|
179 |
|
180 | describe('Checking JSON Schema Spec coverage', () => {
|
181 | expected.forEach((keyword) => {
|
182 | it(`keyword ${keyword}`, () => {
|
183 | assert.ok(report().has(keyword), `keyword ${keyword} not used in tests`);
|
184 | });
|
185 | });
|
186 | });
|