@syncerauk/test-rules 1.0.3 → 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/package.json +1 -1
- package/test-rules.mjs +11 -2
package/README.md
CHANGED
|
@@ -49,7 +49,9 @@ Both in the repo's `scripts/test-rules/`:
|
|
|
49
49
|
|
|
50
50
|
- `config.json`: what differs between repos. `fail` (false is report only), `tests` and
|
|
51
51
|
`ignore` (which files are test files), `fixtures` (folders a test may read), `wording` (the
|
|
52
|
-
one list of what a person reads), `
|
|
52
|
+
one list of what a person reads), `style_contract` (`{ "<test file>": ["<.css or .scss path or
|
|
53
|
+
glob>"] }`: the one test that may read those stylesheets; style rules are checked there, once),
|
|
54
|
+
`reads` (the reads register: `"comment"`, or
|
|
53
55
|
`{ "json": "<path>" }` for the map under `"tests"` in that file, `"key": "<name>"` for another
|
|
54
56
|
key, `"key": null` when the file itself is the map). The defaults are `CONFIG_DEFAULTS` in the
|
|
55
57
|
script.
|
|
@@ -67,6 +69,10 @@ A repo starts with `"fail": false`: breaches are printed and the exit is 0. One
|
|
|
67
69
|
|
|
68
70
|
## Versions
|
|
69
71
|
|
|
72
|
+
- **1.0.4** — `style_contract`: a repo may name one test that reads its stylesheets (`.css`,
|
|
73
|
+
`.scss`), the way wording is read once from the wording list. Those files are not a
|
|
74
|
+
`T4:source-text` breach in that test, and only in that test. Nothing changes for a repo that
|
|
75
|
+
does not set it.
|
|
70
76
|
- **1.0.3** — A JSON reads register no longer turns off the scan of the test's code. A source
|
|
71
77
|
file the test builds a path to from its own place, in a test that reads files, is a
|
|
72
78
|
`T4:source-text` breach unless the test declares it `// runs:`. A repo with a JSON register
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@syncerauk/test-rules",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.4",
|
|
4
4
|
"description": "The static check of Syncera's test-writing rules (T1, T4, T6 and the line limit of R7). One file, no dependency. Published to npm by the orchestrator's publish-test-rules workflow; each repo pins an exact version.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|
package/test-rules.mjs
CHANGED
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
// reads the file system and declares it neither `// reads:`, `// runs:` nor
|
|
40
40
|
// in its JSON register entry (a file that is only run is declared `// runs:`).
|
|
41
41
|
// A JSON register is what a test says it reads; it does not stop the code
|
|
42
|
-
// being read for what it does read (1.0.3).
|
|
42
|
+
// being read for what it does read (1.0.3). A stylesheet named for a test in
|
|
43
|
+
// `style_contract` is not a breach in that test, and only in that test (1.0.4).
|
|
43
44
|
// T6:timer setTimeout( / setInterval( / sleep( / timers/promises with no fake timers in the file
|
|
44
45
|
// T6:clock Date.now( / new Date() / performance.now( with no fake timers in the file
|
|
45
46
|
// T6:outside homedir(), process.env.HOME, a relative path that climbs out of the repo,
|
|
@@ -71,6 +72,7 @@ export const CONFIG_DEFAULTS = Object.freeze({
|
|
|
71
72
|
ignore: [], // test files the rules do not judge (a real-time tier 3 suite)
|
|
72
73
|
fixtures: [], // folders a test may read: `a/fixtures/`
|
|
73
74
|
wording: [], // the one list of what a person reads (a built page, a legal text): files, globs, folders
|
|
75
|
+
style_contract: {}, // { "<test file>": ["<.css/.scss path or glob>", …] }: the one test that may read those stylesheets
|
|
74
76
|
reads: null, // the reads register: null, "comment" (`// reads:` lines) or { json: "<path>", key: "tests" } (key null: the file itself is the map)
|
|
75
77
|
source_ext: ['mjs', 'cjs', 'js', 'jsx', 'ts', 'tsx', 'mts', 'cts', 'sql', 'css', 'scss', 'html', 'sh', 'yml', 'yaml', 'md', 'mdx'],
|
|
76
78
|
max_lines: 1500,
|
|
@@ -273,7 +275,9 @@ function envLine({ code, bare }, allowed) {
|
|
|
273
275
|
/** T4: the line of the first read of a source file as text, or 0. */
|
|
274
276
|
function sourceTextLine({ path, text, code, bare, strings }, { config, register, tracked }) {
|
|
275
277
|
const isSource = (p) => config.source_ext.includes(String(p).split('.').pop().toLowerCase());
|
|
276
|
-
|
|
278
|
+
// A style contract is the one test that may read the stylesheets it names: style rules are
|
|
279
|
+
// checked once, there, the way wording is checked once from the wording list.
|
|
280
|
+
const allowed = (p) => under(config.fixtures, p) || under(config.wording, p) || under(config.style_contract?.[path], p);
|
|
277
281
|
const isFolderOrGlob = (p) => (/[*?]/.test(p) ? !/\.[A-Za-z0-9]+$/.test(p) || isSource(p) : String(p).endsWith('/'));
|
|
278
282
|
const offends = (p) => !allowed(p) && (isFolderOrGlob(p) || isSource(p));
|
|
279
283
|
|
|
@@ -397,6 +401,11 @@ export function withDefaults(raw) {
|
|
|
397
401
|
for (const k of ['tests', 'ignore', 'fixtures', 'wording', 'source_ext', 'task_names', 'fake_timers', 'env_allowed']) {
|
|
398
402
|
if (!Array.isArray(config[k]) || config[k].some((x) => typeof x !== 'string')) throw fail(EXIT.FAILED, `${CONFIG_FILE}: ${k} must be a list of strings`);
|
|
399
403
|
}
|
|
404
|
+
const sc = config.style_contract;
|
|
405
|
+
const styleOnly = (g) => typeof g === 'string' && /\.s?css$/.test(g);
|
|
406
|
+
if (!sc || typeof sc !== 'object' || Array.isArray(sc) || Object.values(sc).some((v) => !Array.isArray(v) || !v.length || !v.every(styleOnly))) {
|
|
407
|
+
throw fail(EXIT.FAILED, `${CONFIG_FILE}: style_contract must be { "<test file>": ["<path or glob ending .css or .scss>", …] }`);
|
|
408
|
+
}
|
|
400
409
|
const r = config.reads;
|
|
401
410
|
const json = r && typeof r === 'object' && !Array.isArray(r) && typeof r.json === 'string'
|
|
402
411
|
&& Object.keys(r).every((k) => k === 'json' || k === 'key') && (r.key === undefined || r.key === null || typeof r.key === 'string');
|