gurkencheck 0.0.9 → 0.1.0
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 +30 -11
- package/dist/config-parser.d.ts +15 -1
- package/dist/config-parser.js +58 -12
- package/dist/config-verifier.d.ts +0 -1
- package/dist/config-verifier.js +71 -3
- package/dist/diagnostics.d.ts +0 -1
- package/dist/diagnostics.js +0 -1
- package/dist/exit-codes.d.ts +0 -1
- package/dist/exit-codes.js +0 -1
- package/dist/feature-finder.d.ts +0 -1
- package/dist/feature-finder.js +29 -11
- package/dist/formatters/index.d.ts +0 -1
- package/dist/formatters/index.js +2 -3
- package/dist/formatters/json.d.ts +0 -1
- package/dist/formatters/json.js +0 -1
- package/dist/formatters/junit.d.ts +1 -13
- package/dist/formatters/junit.js +36 -11
- package/dist/formatters/sarif.d.ts +0 -1
- package/dist/formatters/sarif.js +25 -7
- package/dist/formatters/stylish.d.ts +0 -1
- package/dist/formatters/stylish.js +0 -1
- package/dist/formatters/tap.d.ts +0 -1
- package/dist/formatters/tap.js +37 -5
- package/dist/gherkin/dialects.d.ts +0 -1
- package/dist/gherkin/dialects.js +0 -1
- package/dist/gherkin/keywords.d.ts +6 -3
- package/dist/gherkin/keywords.js +10 -4
- package/dist/gherkin/parse.d.ts +0 -1
- package/dist/gherkin/parse.js +19 -7
- package/dist/gherkin/traverse.d.ts +0 -1
- package/dist/gherkin/traverse.js +0 -1
- package/dist/gherkin/variables.d.ts +12 -1
- package/dist/gherkin/variables.js +24 -6
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/linter.d.ts +0 -1
- package/dist/linter.js +1 -2
- package/dist/logger.d.ts +0 -1
- package/dist/logger.js +0 -1
- package/dist/main.d.ts +0 -1
- package/dist/main.js +78 -27
- package/dist/presets.d.ts +2 -2
- package/dist/presets.js +0 -5
- package/dist/rules/allowed-tags.d.ts +0 -1
- package/dist/rules/allowed-tags.js +2 -1
- package/dist/rules/background-setup-only.d.ts +0 -1
- package/dist/rules/background-setup-only.js +0 -1
- package/dist/rules/file-name.d.ts +0 -1
- package/dist/rules/file-name.js +10 -1
- package/dist/rules/indentation.d.ts +0 -1
- package/dist/rules/indentation.js +24 -10
- package/dist/rules/index.d.ts +0 -1
- package/dist/rules/index.js +0 -1
- package/dist/rules/keywords-in-logical-order.d.ts +0 -1
- package/dist/rules/keywords-in-logical-order.js +0 -1
- package/dist/rules/max-scenarios-per-file.d.ts +0 -1
- package/dist/rules/max-scenarios-per-file.js +0 -1
- package/dist/rules/name-length.d.ts +0 -1
- package/dist/rules/name-length.js +5 -3
- package/dist/rules/new-line-at-eof.d.ts +0 -1
- package/dist/rules/new-line-at-eof.js +10 -2
- package/dist/rules/no-background-only-scenario.d.ts +0 -1
- package/dist/rules/no-background-only-scenario.js +0 -1
- package/dist/rules/no-dupe-feature-names.d.ts +0 -1
- package/dist/rules/no-dupe-feature-names.js +5 -1
- package/dist/rules/no-dupe-file-names.d.ts +0 -1
- package/dist/rules/no-dupe-file-names.js +0 -1
- package/dist/rules/no-dupe-scenario-names.d.ts +0 -1
- package/dist/rules/no-dupe-scenario-names.js +4 -1
- package/dist/rules/no-duplicate-tags.d.ts +0 -1
- package/dist/rules/no-duplicate-tags.js +0 -1
- package/dist/rules/no-empty-background.d.ts +0 -1
- package/dist/rules/no-empty-background.js +0 -1
- package/dist/rules/no-empty-file.d.ts +0 -1
- package/dist/rules/no-empty-file.js +0 -1
- package/dist/rules/no-examples-in-scenarios.d.ts +0 -1
- package/dist/rules/no-examples-in-scenarios.js +0 -1
- package/dist/rules/no-files-without-scenarios.d.ts +0 -1
- package/dist/rules/no-files-without-scenarios.js +0 -1
- package/dist/rules/no-homogenous-tags.d.ts +0 -1
- package/dist/rules/no-homogenous-tags.js +0 -1
- package/dist/rules/no-multiple-empty-lines.d.ts +0 -1
- package/dist/rules/no-multiple-empty-lines.js +2 -3
- package/dist/rules/no-partially-commented-tag-lines.d.ts +0 -1
- package/dist/rules/no-partially-commented-tag-lines.js +7 -5
- package/dist/rules/no-restricted-patterns.d.ts +0 -1
- package/dist/rules/no-restricted-patterns.js +64 -19
- package/dist/rules/no-restricted-tags.d.ts +0 -1
- package/dist/rules/no-restricted-tags.js +2 -1
- package/dist/rules/no-scenario-outlines-without-examples.d.ts +0 -1
- package/dist/rules/no-scenario-outlines-without-examples.js +0 -1
- package/dist/rules/no-scenarios-without-then.d.ts +0 -1
- package/dist/rules/no-scenarios-without-then.js +0 -1
- package/dist/rules/no-scenarios-without-when.d.ts +0 -1
- package/dist/rules/no-scenarios-without-when.js +0 -1
- package/dist/rules/no-superfluous-tags.d.ts +0 -1
- package/dist/rules/no-superfluous-tags.js +0 -1
- package/dist/rules/no-trailing-spaces.d.ts +0 -1
- package/dist/rules/no-trailing-spaces.js +13 -4
- package/dist/rules/no-undeclared-variables.d.ts +0 -1
- package/dist/rules/no-undeclared-variables.js +12 -11
- package/dist/rules/no-unnamed-features.d.ts +0 -1
- package/dist/rules/no-unnamed-features.js +0 -1
- package/dist/rules/no-unnamed-scenarios.d.ts +0 -1
- package/dist/rules/no-unnamed-scenarios.js +0 -1
- package/dist/rules/no-unused-variables.d.ts +0 -1
- package/dist/rules/no-unused-variables.js +0 -1
- package/dist/rules/one-space-between-tags.d.ts +0 -1
- package/dist/rules/one-space-between-tags.js +3 -2
- package/dist/rules/only-one-when.d.ts +0 -1
- package/dist/rules/only-one-when.js +5 -5
- package/dist/rules/required-tags.d.ts +0 -1
- package/dist/rules/required-tags.js +13 -3
- package/dist/rules/scenario-size.d.ts +0 -1
- package/dist/rules/scenario-size.js +0 -1
- package/dist/rules/use-and.d.ts +0 -1
- package/dist/rules/use-and.js +2 -2
- package/dist/rules.d.ts +0 -1
- package/dist/rules.js +49 -4
- package/dist/stats/collect.d.ts +0 -1
- package/dist/stats/collect.js +0 -1
- package/dist/stats/command.d.ts +0 -1
- package/dist/stats/command.js +24 -7
- package/dist/stats/format/index.d.ts +0 -1
- package/dist/stats/format/index.js +0 -1
- package/dist/stats/format/json.d.ts +0 -1
- package/dist/stats/format/json.js +0 -1
- package/dist/stats/format/markdown.d.ts +0 -1
- package/dist/stats/format/markdown.js +24 -6
- package/dist/stats/format/shared.d.ts +0 -1
- package/dist/stats/format/shared.js +0 -1
- package/dist/stats/format/text.d.ts +0 -1
- package/dist/stats/format/text.js +0 -1
- package/dist/stats/index.d.ts +0 -1
- package/dist/stats/index.js +0 -1
- package/dist/stats/normalise.d.ts +0 -1
- package/dist/stats/normalise.js +27 -14
- package/dist/stats/similar.d.ts +0 -1
- package/dist/stats/similar.js +0 -1
- package/dist/stats/types.d.ts +0 -1
- package/dist/stats/types.js +0 -1
- package/dist/suppressions.d.ts +6 -3
- package/dist/suppressions.js +36 -30
- package/dist/types.d.ts +7 -1
- package/dist/types.js +0 -1
- package/dist/util/case.d.ts +2 -2
- package/dist/util/case.js +17 -3
- package/dist/util/collections.d.ts +0 -1
- package/dist/util/collections.js +0 -1
- package/dist/util/glob.d.ts +9 -3
- package/dist/util/glob.js +115 -27
- package/dist/util/json.d.ts +0 -1
- package/dist/util/json.js +0 -1
- package/dist/util/lines.d.ts +6 -4
- package/dist/util/lines.js +22 -4
- package/dist/util/location.d.ts +8 -1
- package/dist/util/location.js +10 -1
- package/dist/util/patterns.d.ts +9 -0
- package/dist/util/patterns.js +21 -0
- package/dist/util/resolve.d.ts +6 -0
- package/dist/util/resolve.js +101 -0
- package/dist/util/stream.d.ts +0 -1
- package/dist/util/stream.js +3 -2
- package/dist/util/xml.d.ts +0 -1
- package/dist/util/xml.js +17 -3
- package/dist/version.d.ts +0 -1
- package/dist/version.js +0 -1
- package/dist/watch.d.ts +11 -1
- package/dist/watch.js +38 -3
- package/package.json +6 -2
- package/dist/config-parser.d.ts.map +0 -1
- package/dist/config-parser.js.map +0 -1
- package/dist/config-verifier.d.ts.map +0 -1
- package/dist/config-verifier.js.map +0 -1
- package/dist/diagnostics.d.ts.map +0 -1
- package/dist/diagnostics.js.map +0 -1
- package/dist/exit-codes.d.ts.map +0 -1
- package/dist/exit-codes.js.map +0 -1
- package/dist/feature-finder.d.ts.map +0 -1
- package/dist/feature-finder.js.map +0 -1
- package/dist/formatters/index.d.ts.map +0 -1
- package/dist/formatters/index.js.map +0 -1
- package/dist/formatters/json.d.ts.map +0 -1
- package/dist/formatters/json.js.map +0 -1
- package/dist/formatters/junit.d.ts.map +0 -1
- package/dist/formatters/junit.js.map +0 -1
- package/dist/formatters/sarif.d.ts.map +0 -1
- package/dist/formatters/sarif.js.map +0 -1
- package/dist/formatters/stylish.d.ts.map +0 -1
- package/dist/formatters/stylish.js.map +0 -1
- package/dist/formatters/tap.d.ts.map +0 -1
- package/dist/formatters/tap.js.map +0 -1
- package/dist/gherkin/dialects.d.ts.map +0 -1
- package/dist/gherkin/dialects.js.map +0 -1
- package/dist/gherkin/keywords.d.ts.map +0 -1
- package/dist/gherkin/keywords.js.map +0 -1
- package/dist/gherkin/parse.d.ts.map +0 -1
- package/dist/gherkin/parse.js.map +0 -1
- package/dist/gherkin/traverse.d.ts.map +0 -1
- package/dist/gherkin/traverse.js.map +0 -1
- package/dist/gherkin/variables.d.ts.map +0 -1
- package/dist/gherkin/variables.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/linter.d.ts.map +0 -1
- package/dist/linter.js.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/main.d.ts.map +0 -1
- package/dist/main.js.map +0 -1
- package/dist/presets.d.ts.map +0 -1
- package/dist/presets.js.map +0 -1
- package/dist/rules/allowed-tags.d.ts.map +0 -1
- package/dist/rules/allowed-tags.js.map +0 -1
- package/dist/rules/background-setup-only.d.ts.map +0 -1
- package/dist/rules/background-setup-only.js.map +0 -1
- package/dist/rules/file-name.d.ts.map +0 -1
- package/dist/rules/file-name.js.map +0 -1
- package/dist/rules/indentation.d.ts.map +0 -1
- package/dist/rules/indentation.js.map +0 -1
- package/dist/rules/index.d.ts.map +0 -1
- package/dist/rules/index.js.map +0 -1
- package/dist/rules/keywords-in-logical-order.d.ts.map +0 -1
- package/dist/rules/keywords-in-logical-order.js.map +0 -1
- package/dist/rules/max-scenarios-per-file.d.ts.map +0 -1
- package/dist/rules/max-scenarios-per-file.js.map +0 -1
- package/dist/rules/name-length.d.ts.map +0 -1
- package/dist/rules/name-length.js.map +0 -1
- package/dist/rules/new-line-at-eof.d.ts.map +0 -1
- package/dist/rules/new-line-at-eof.js.map +0 -1
- package/dist/rules/no-background-only-scenario.d.ts.map +0 -1
- package/dist/rules/no-background-only-scenario.js.map +0 -1
- package/dist/rules/no-dupe-feature-names.d.ts.map +0 -1
- package/dist/rules/no-dupe-feature-names.js.map +0 -1
- package/dist/rules/no-dupe-file-names.d.ts.map +0 -1
- package/dist/rules/no-dupe-file-names.js.map +0 -1
- package/dist/rules/no-dupe-scenario-names.d.ts.map +0 -1
- package/dist/rules/no-dupe-scenario-names.js.map +0 -1
- package/dist/rules/no-duplicate-tags.d.ts.map +0 -1
- package/dist/rules/no-duplicate-tags.js.map +0 -1
- package/dist/rules/no-empty-background.d.ts.map +0 -1
- package/dist/rules/no-empty-background.js.map +0 -1
- package/dist/rules/no-empty-file.d.ts.map +0 -1
- package/dist/rules/no-empty-file.js.map +0 -1
- package/dist/rules/no-examples-in-scenarios.d.ts.map +0 -1
- package/dist/rules/no-examples-in-scenarios.js.map +0 -1
- package/dist/rules/no-files-without-scenarios.d.ts.map +0 -1
- package/dist/rules/no-files-without-scenarios.js.map +0 -1
- package/dist/rules/no-homogenous-tags.d.ts.map +0 -1
- package/dist/rules/no-homogenous-tags.js.map +0 -1
- package/dist/rules/no-multiple-empty-lines.d.ts.map +0 -1
- package/dist/rules/no-multiple-empty-lines.js.map +0 -1
- package/dist/rules/no-partially-commented-tag-lines.d.ts.map +0 -1
- package/dist/rules/no-partially-commented-tag-lines.js.map +0 -1
- package/dist/rules/no-restricted-patterns.d.ts.map +0 -1
- package/dist/rules/no-restricted-patterns.js.map +0 -1
- package/dist/rules/no-restricted-tags.d.ts.map +0 -1
- package/dist/rules/no-restricted-tags.js.map +0 -1
- package/dist/rules/no-scenario-outlines-without-examples.d.ts.map +0 -1
- package/dist/rules/no-scenario-outlines-without-examples.js.map +0 -1
- package/dist/rules/no-scenarios-without-then.d.ts.map +0 -1
- package/dist/rules/no-scenarios-without-then.js.map +0 -1
- package/dist/rules/no-scenarios-without-when.d.ts.map +0 -1
- package/dist/rules/no-scenarios-without-when.js.map +0 -1
- package/dist/rules/no-superfluous-tags.d.ts.map +0 -1
- package/dist/rules/no-superfluous-tags.js.map +0 -1
- package/dist/rules/no-trailing-spaces.d.ts.map +0 -1
- package/dist/rules/no-trailing-spaces.js.map +0 -1
- package/dist/rules/no-undeclared-variables.d.ts.map +0 -1
- package/dist/rules/no-undeclared-variables.js.map +0 -1
- package/dist/rules/no-unnamed-features.d.ts.map +0 -1
- package/dist/rules/no-unnamed-features.js.map +0 -1
- package/dist/rules/no-unnamed-scenarios.d.ts.map +0 -1
- package/dist/rules/no-unnamed-scenarios.js.map +0 -1
- package/dist/rules/no-unused-variables.d.ts.map +0 -1
- package/dist/rules/no-unused-variables.js.map +0 -1
- package/dist/rules/one-space-between-tags.d.ts.map +0 -1
- package/dist/rules/one-space-between-tags.js.map +0 -1
- package/dist/rules/only-one-when.d.ts.map +0 -1
- package/dist/rules/only-one-when.js.map +0 -1
- package/dist/rules/required-tags.d.ts.map +0 -1
- package/dist/rules/required-tags.js.map +0 -1
- package/dist/rules/scenario-size.d.ts.map +0 -1
- package/dist/rules/scenario-size.js.map +0 -1
- package/dist/rules/use-and.d.ts.map +0 -1
- package/dist/rules/use-and.js.map +0 -1
- package/dist/rules.d.ts.map +0 -1
- package/dist/rules.js.map +0 -1
- package/dist/stats/collect.d.ts.map +0 -1
- package/dist/stats/collect.js.map +0 -1
- package/dist/stats/command.d.ts.map +0 -1
- package/dist/stats/command.js.map +0 -1
- package/dist/stats/format/index.d.ts.map +0 -1
- package/dist/stats/format/index.js.map +0 -1
- package/dist/stats/format/json.d.ts.map +0 -1
- package/dist/stats/format/json.js.map +0 -1
- package/dist/stats/format/markdown.d.ts.map +0 -1
- package/dist/stats/format/markdown.js.map +0 -1
- package/dist/stats/format/shared.d.ts.map +0 -1
- package/dist/stats/format/shared.js.map +0 -1
- package/dist/stats/format/text.d.ts.map +0 -1
- package/dist/stats/format/text.js.map +0 -1
- package/dist/stats/index.d.ts.map +0 -1
- package/dist/stats/index.js.map +0 -1
- package/dist/stats/normalise.d.ts.map +0 -1
- package/dist/stats/normalise.js.map +0 -1
- package/dist/stats/similar.d.ts.map +0 -1
- package/dist/stats/similar.js.map +0 -1
- package/dist/stats/types.d.ts.map +0 -1
- package/dist/stats/types.js.map +0 -1
- package/dist/suppressions.d.ts.map +0 -1
- package/dist/suppressions.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/util/case.d.ts.map +0 -1
- package/dist/util/case.js.map +0 -1
- package/dist/util/collections.d.ts.map +0 -1
- package/dist/util/collections.js.map +0 -1
- package/dist/util/glob.d.ts.map +0 -1
- package/dist/util/glob.js.map +0 -1
- package/dist/util/json.d.ts.map +0 -1
- package/dist/util/json.js.map +0 -1
- package/dist/util/lines.d.ts.map +0 -1
- package/dist/util/lines.js.map +0 -1
- package/dist/util/location.d.ts.map +0 -1
- package/dist/util/location.js.map +0 -1
- package/dist/util/stream.d.ts.map +0 -1
- package/dist/util/stream.js.map +0 -1
- package/dist/util/xml.d.ts.map +0 -1
- package/dist/util/xml.js.map +0 -1
- package/dist/version.d.ts.map +0 -1
- package/dist/version.js.map +0 -1
- package/dist/watch.d.ts.map +0 -1
- package/dist/watch.js.map +0 -1
|
@@ -2,11 +2,13 @@ import { getNodeType } from '../gherkin/keywords.js';
|
|
|
2
2
|
import { taggedNodesOf } from '../gherkin/traverse.js';
|
|
3
3
|
import { mergeDefaults } from '../util/collections.js';
|
|
4
4
|
import { at } from '../util/location.js';
|
|
5
|
+
import { invalidPatterns } from '../util/patterns.js';
|
|
5
6
|
const name = 'no-restricted-tags';
|
|
6
7
|
const availableConfigs = { tags: [], patterns: [] };
|
|
7
8
|
const rule = {
|
|
8
9
|
name,
|
|
9
10
|
availableConfigs,
|
|
11
|
+
verifySettings: (settings) => invalidPatterns('patterns', settings['patterns']),
|
|
10
12
|
run(feature, _file, configuration) {
|
|
11
13
|
if (feature === undefined) {
|
|
12
14
|
return [];
|
|
@@ -32,4 +34,3 @@ const rule = {
|
|
|
32
34
|
},
|
|
33
35
|
};
|
|
34
36
|
export default rule;
|
|
35
|
-
//# sourceMappingURL=no-restricted-tags.js.map
|
|
@@ -1,11 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { markDocStrings } from '../util/lines.js';
|
|
2
|
+
import { atLineColumn, columnOf } from '../util/location.js';
|
|
2
3
|
const name = 'no-trailing-spaces';
|
|
3
4
|
const TRAILING_WHITESPACE = /[\t ]+$/;
|
|
4
5
|
const rule = {
|
|
5
6
|
name,
|
|
6
|
-
run(
|
|
7
|
+
run(feature, file) {
|
|
7
8
|
const errors = [];
|
|
9
|
+
const inDocString = markDocStrings(file.lines, feature);
|
|
8
10
|
file.lines.forEach((line, index) => {
|
|
11
|
+
// The content of a doc string is data - expected output, a request
|
|
12
|
+
// body - where trailing spaces may be the point. Its delimiters are
|
|
13
|
+
// layout like any other line.
|
|
14
|
+
const trimmed = line.trim();
|
|
15
|
+
const isDelimiter = trimmed.startsWith('"""') || trimmed.startsWith('```');
|
|
16
|
+
if (inDocString[index] === true && !isDelimiter) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
9
19
|
const trailing = TRAILING_WHITESPACE.exec(line);
|
|
10
20
|
if (trailing !== null) {
|
|
11
21
|
errors.push({
|
|
@@ -13,7 +23,7 @@ const rule = {
|
|
|
13
23
|
rule: name,
|
|
14
24
|
// Point at the first character of the run, so an editor can
|
|
15
25
|
// underline exactly what has to go.
|
|
16
|
-
...atLineColumn(index + 1, trailing.index
|
|
26
|
+
...atLineColumn(index + 1, columnOf(line, trailing.index)),
|
|
17
27
|
});
|
|
18
28
|
}
|
|
19
29
|
});
|
|
@@ -21,4 +31,3 @@ const rule = {
|
|
|
21
31
|
},
|
|
22
32
|
};
|
|
23
33
|
export default rule;
|
|
24
|
-
//# sourceMappingURL=no-trailing-spaces.js.map
|
|
@@ -14,17 +14,19 @@ const rule = {
|
|
|
14
14
|
if (scenario.examples.length === 0) {
|
|
15
15
|
continue;
|
|
16
16
|
}
|
|
17
|
-
const { declared, used } = variablesOf(scenario);
|
|
17
|
+
const { declared, tables, used } = variablesOf(scenario);
|
|
18
18
|
for (const [variable, positions] of used) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
// Declared nowhere is one mistake; declared in some tables but not
|
|
20
|
+
// all is another, and the table leaving it out is worth naming.
|
|
21
|
+
const messages = !declared.has(variable)
|
|
22
|
+
? [`Step variable "${variable}" does not exist in the examples table`]
|
|
23
|
+
: tables
|
|
24
|
+
.filter((table) => !table.columns.has(variable))
|
|
25
|
+
.map((table) => `Step variable "${variable}" does not exist in the examples table on line ${table.line}`);
|
|
26
|
+
for (const message of messages) {
|
|
27
|
+
for (const position of positions) {
|
|
28
|
+
errors.push({ message, rule: name, ...position });
|
|
29
|
+
}
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
32
|
}
|
|
@@ -32,4 +34,3 @@ const rule = {
|
|
|
32
34
|
},
|
|
33
35
|
};
|
|
34
36
|
export default rule;
|
|
35
|
-
//# sourceMappingURL=no-undeclared-variables.js.map
|
|
@@ -8,7 +8,9 @@ function checkTags(tags, errors) {
|
|
|
8
8
|
for (let index = 0; index < ordered.length - 1; index++) {
|
|
9
9
|
const current = ordered[index];
|
|
10
10
|
const next = ordered[index + 1];
|
|
11
|
-
|
|
11
|
+
// Gherkin counts columns in characters, not in the UTF-16 units that
|
|
12
|
+
// .length counts, so a tag holding an emoji is measured the same way.
|
|
13
|
+
const endOfCurrent = (current.location.column ?? 0) + [...current.name].length;
|
|
12
14
|
if (endOfCurrent < (next.location.column ?? 0) - 1) {
|
|
13
15
|
errors.push({
|
|
14
16
|
message: `There is more than one space between the tags ${current.name} and ${next.name}`,
|
|
@@ -33,4 +35,3 @@ const rule = {
|
|
|
33
35
|
},
|
|
34
36
|
};
|
|
35
37
|
export default rule;
|
|
36
|
-
//# sourceMappingURL=one-space-between-tags.js.map
|
|
@@ -28,15 +28,16 @@ const rule = {
|
|
|
28
28
|
let firstViolation = { line: 0 };
|
|
29
29
|
for (const step of scenario.steps) {
|
|
30
30
|
const keyword = getNeutralKeyword(step, feature.language);
|
|
31
|
-
// An `And` carries on from whichever keyword came before it,
|
|
32
|
-
// many of them are chained together.
|
|
33
|
-
const
|
|
31
|
+
// An `And` or `*` carries on from whichever keyword came before it,
|
|
32
|
+
// however many of them are chained together.
|
|
33
|
+
const continues = keyword === 'and' || keyword === 'star';
|
|
34
|
+
const continuesWhen = continues && lastRealKeyword === 'when';
|
|
34
35
|
if (keyword !== 'when' && !continuesWhen) {
|
|
35
36
|
lastRealKeyword = keyword;
|
|
36
37
|
continue;
|
|
37
38
|
}
|
|
38
39
|
lastRealKeyword = 'when';
|
|
39
|
-
if (
|
|
40
|
+
if (continues && !countAnd) {
|
|
40
41
|
continue;
|
|
41
42
|
}
|
|
42
43
|
whenCount++;
|
|
@@ -56,4 +57,3 @@ const rule = {
|
|
|
56
57
|
},
|
|
57
58
|
};
|
|
58
59
|
export default rule;
|
|
59
|
-
//# sourceMappingURL=only-one-when.js.map
|
|
@@ -2,25 +2,36 @@ import { getNodeType } from '../gherkin/keywords.js';
|
|
|
2
2
|
import { scenariosOf } from '../gherkin/traverse.js';
|
|
3
3
|
import { mergeDefaults } from '../util/collections.js';
|
|
4
4
|
import { at } from '../util/location.js';
|
|
5
|
+
import { invalidPatterns } from '../util/patterns.js';
|
|
5
6
|
const name = 'required-tags';
|
|
6
7
|
const availableConfigs = { tags: [], ignoreUntagged: true };
|
|
7
8
|
const rule = {
|
|
8
9
|
name,
|
|
9
10
|
availableConfigs,
|
|
11
|
+
verifySettings: (settings) => invalidPatterns('tags', settings['tags']),
|
|
10
12
|
run(feature, _file, configuration) {
|
|
11
13
|
if (feature === undefined) {
|
|
12
14
|
return [];
|
|
13
15
|
}
|
|
14
16
|
const config = mergeDefaults(availableConfigs, configuration);
|
|
15
17
|
const errors = [];
|
|
16
|
-
for (const { scenario } of scenariosOf(feature)) {
|
|
18
|
+
for (const { scenario, rule: parent } of scenariosOf(feature)) {
|
|
17
19
|
if (config.ignoreUntagged && scenario.tags.length === 0) {
|
|
18
20
|
continue;
|
|
19
21
|
}
|
|
22
|
+
// A scenario runs with the tags of its Feature and Rule as well as its
|
|
23
|
+
// own, and each Examples table's rows with that table's tags on top.
|
|
24
|
+
// Asking for the tag to be repeated on the scenario would only have
|
|
25
|
+
// no-superfluous-tags complain about it.
|
|
26
|
+
const inherited = [...feature.tags, ...(parent?.tags ?? []), ...scenario.tags];
|
|
27
|
+
const tagSets = scenario.examples.length === 0
|
|
28
|
+
? [inherited]
|
|
29
|
+
: scenario.examples.map((examples) => [...inherited, ...examples.tags]);
|
|
20
30
|
const scenarioType = getNodeType(scenario, feature.language);
|
|
21
31
|
for (const required of config.tags) {
|
|
22
32
|
const pattern = new RegExp(required);
|
|
23
|
-
|
|
33
|
+
const found = tagSets.every((tags) => tags.some((tag) => pattern.test(tag.name)));
|
|
34
|
+
if (!found) {
|
|
24
35
|
errors.push({
|
|
25
36
|
message: `No tag found matching ${required} for ${scenarioType}`,
|
|
26
37
|
rule: name,
|
|
@@ -33,4 +44,3 @@ const rule = {
|
|
|
33
44
|
},
|
|
34
45
|
};
|
|
35
46
|
export default rule;
|
|
36
|
-
//# sourceMappingURL=required-tags.js.map
|
package/dist/rules/use-and.d.ts
CHANGED
package/dist/rules/use-and.js
CHANGED
|
@@ -13,7 +13,8 @@ const rule = {
|
|
|
13
13
|
let previousKeyword;
|
|
14
14
|
for (const step of node.steps) {
|
|
15
15
|
const keyword = getNeutralKeyword(step, feature.language);
|
|
16
|
-
|
|
16
|
+
// And and * already carry on from the step before them.
|
|
17
|
+
if (keyword === 'and' || keyword === 'star') {
|
|
17
18
|
continue;
|
|
18
19
|
}
|
|
19
20
|
if (keyword === previousKeyword) {
|
|
@@ -30,4 +31,3 @@ const rule = {
|
|
|
30
31
|
},
|
|
31
32
|
};
|
|
32
33
|
export default rule;
|
|
33
|
-
//# sourceMappingURL=use-and.js.map
|
package/dist/rules.d.ts
CHANGED
|
@@ -59,4 +59,3 @@ export declare function finishRun(rules: RuleRegistry, configuration: Configurat
|
|
|
59
59
|
* checking a single file wants.
|
|
60
60
|
*/
|
|
61
61
|
export declare function runEnabledRules(feature: Feature | undefined, file: FeatureFile, configuration: Configuration, rules: RuleRegistry, run?: Run): Promise<RuleError[]>;
|
|
62
|
-
//# sourceMappingURL=rules.d.ts.map
|
package/dist/rules.js
CHANGED
|
@@ -9,6 +9,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
|
|
|
9
9
|
/**
|
|
10
10
|
* Assembling the set of rules available to a run, and running them.
|
|
11
11
|
*/
|
|
12
|
+
import fs from 'node:fs';
|
|
12
13
|
import path from 'node:path';
|
|
13
14
|
import { pathToFileURL } from 'node:url';
|
|
14
15
|
import { BUILT_IN_RULES } from './rules/index.js';
|
|
@@ -35,7 +36,21 @@ function extractRule(loaded, source) {
|
|
|
35
36
|
}
|
|
36
37
|
async function loadRulesFrom(directory) {
|
|
37
38
|
const resolved = path.resolve(directory);
|
|
38
|
-
|
|
39
|
+
// A directory that is not there would otherwise contribute no rules and no
|
|
40
|
+
// complaint, and a run meant to include them would pass without them.
|
|
41
|
+
let stats;
|
|
42
|
+
try {
|
|
43
|
+
stats = fs.statSync(resolved);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
throw new Error(`Could not find the rules directory "${directory}".`);
|
|
47
|
+
}
|
|
48
|
+
if (!stats.isDirectory()) {
|
|
49
|
+
throw new Error(`The rules directory "${directory}" is not a directory.`);
|
|
50
|
+
}
|
|
51
|
+
// Declaration files sit beside TypeScript rules as a matter of course, and
|
|
52
|
+
// hold types, not code.
|
|
53
|
+
const files = globSync(`*.${RULE_EXTENSIONS}`, { cwd: resolved }).filter((file) => !/\.d\.[mc]?ts$/u.test(file));
|
|
39
54
|
const loaded = [];
|
|
40
55
|
for (const file of files) {
|
|
41
56
|
const absolute = path.join(resolved, file);
|
|
@@ -136,6 +151,28 @@ export function beginRun(rules, configuration) {
|
|
|
136
151
|
}
|
|
137
152
|
return run;
|
|
138
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* The line a finding names, or 0 - a finding about the whole file - when a
|
|
156
|
+
* custom rule gave none, or something that is not a line number. Passed on
|
|
157
|
+
* as it was, it would print as `undefined` where the line belongs.
|
|
158
|
+
*/
|
|
159
|
+
function lineOf(finding) {
|
|
160
|
+
const { line } = finding;
|
|
161
|
+
return typeof line === 'number' && Number.isInteger(line) && line >= 0 ? line : 0;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The finding reported in place of a rule's own when it hands back something
|
|
165
|
+
* other than a list. Iterating it anyway would throw somewhere far from the
|
|
166
|
+
* rule, and the message would name whatever happened to be running there.
|
|
167
|
+
*/
|
|
168
|
+
function notAList(ruleName, found) {
|
|
169
|
+
const what = found === null ? 'null' : typeof found;
|
|
170
|
+
return {
|
|
171
|
+
message: `Rule "${ruleName}" returned ${what} instead of an array of findings`,
|
|
172
|
+
rule: 'unexpected-error',
|
|
173
|
+
line: 0,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
139
176
|
/**
|
|
140
177
|
* Ends a run, collecting what could only be worked out once every file had
|
|
141
178
|
* been seen.
|
|
@@ -153,8 +190,12 @@ export async function finishRun(rules, configuration, run) {
|
|
|
153
190
|
const severity = getRuleSeverity(config);
|
|
154
191
|
try {
|
|
155
192
|
const found = await rule.onRunEnd(getRuleSettings(config), run.contextFor(rule.name));
|
|
193
|
+
if (!Array.isArray(found)) {
|
|
194
|
+
findings.push(notAList(rule.name, found));
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
156
197
|
for (const finding of found) {
|
|
157
|
-
findings.push({ severity, ...finding });
|
|
198
|
+
findings.push({ severity, ...finding, line: lineOf(finding) });
|
|
158
199
|
}
|
|
159
200
|
}
|
|
160
201
|
catch (thrown) {
|
|
@@ -190,6 +231,7 @@ export async function runEnabledRules(feature, file, configuration, rules, run =
|
|
|
190
231
|
continue;
|
|
191
232
|
}
|
|
192
233
|
const severity = getRuleSeverity(config);
|
|
234
|
+
// Typed as what a custom rule could really hand back, not what it should.
|
|
193
235
|
let found;
|
|
194
236
|
try {
|
|
195
237
|
found = await rule.run(feature, file, getRuleSettings(config), run.contextFor(rule.name));
|
|
@@ -206,12 +248,15 @@ export async function runEnabledRules(feature, file, configuration, rules, run =
|
|
|
206
248
|
});
|
|
207
249
|
continue;
|
|
208
250
|
}
|
|
251
|
+
if (!Array.isArray(found)) {
|
|
252
|
+
errors.push(notAList(rule.name, found));
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
209
255
|
for (const error of found) {
|
|
210
256
|
// The configuration decides how loudly a rule reports, so a custom rule
|
|
211
257
|
// needs no say in it - but one that sets a severity itself is respected.
|
|
212
|
-
errors.push({ severity, ...error });
|
|
258
|
+
errors.push({ severity, ...error, line: lineOf(error) });
|
|
213
259
|
}
|
|
214
260
|
}
|
|
215
261
|
return errors;
|
|
216
262
|
}
|
|
217
|
-
//# sourceMappingURL=rules.js.map
|
package/dist/stats/collect.d.ts
CHANGED
|
@@ -28,4 +28,3 @@ export declare function collectStatistics(parsed: readonly ParseResult[], option
|
|
|
28
28
|
* suite of any size costs the same as the report it produces.
|
|
29
29
|
*/
|
|
30
30
|
export declare function collectStatisticsFrom(parsed: Sequence<ParseResult>, options?: CollectOptions): Promise<Statistics>;
|
|
31
|
-
//# sourceMappingURL=collect.d.ts.map
|
package/dist/stats/collect.js
CHANGED
package/dist/stats/command.d.ts
CHANGED
package/dist/stats/command.js
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
* ever be the reason a build goes red. Only being unable to run at all -
|
|
7
7
|
* an option that makes no sense, a path that names nothing - fails.
|
|
8
8
|
*/
|
|
9
|
+
import fs from 'node:fs';
|
|
9
10
|
import { parseArgs } from 'node:util';
|
|
11
|
+
import { DEFAULT_CONFIG_FILE_NAME, readConfiguredLanguage } from '../config-parser.js';
|
|
10
12
|
import { EXIT_OK, EXIT_USAGE } from '../exit-codes.js';
|
|
11
13
|
import { DEFAULT_IGNORE_FILE_NAME, findFeatureFileStream } from '../feature-finder.js';
|
|
12
14
|
import { isKnownLanguage } from '../gherkin/dialects.js';
|
|
@@ -24,7 +26,10 @@ export function statsUsage() {
|
|
|
24
26
|
'Options:',
|
|
25
27
|
` -f, --format <format> output format: ${Object.keys(STATS_FORMATTERS).join(', ')}`,
|
|
26
28
|
` (default: ${DEFAULT_STATS_FORMAT})`,
|
|
27
|
-
` -
|
|
29
|
+
` -c, --config <path> configuration file to take the language from`,
|
|
30
|
+
` (default: ${DEFAULT_CONFIG_FILE_NAME})`,
|
|
31
|
+
` -i, --ignore <globs> comma separated globs to skip, overriding ${DEFAULT_IGNORE_FILE_NAME};`,
|
|
32
|
+
' may be given more than once',
|
|
28
33
|
' -l, --language <code> dialect for files with no "# language:" header',
|
|
29
34
|
` --top <n> how many entries each list shows (default: ${DEFAULT_TOP})`,
|
|
30
35
|
' -h, --help show this message',
|
|
@@ -41,7 +46,8 @@ export async function runStats(argv, diagnostics = SILENT) {
|
|
|
41
46
|
args: [...argv],
|
|
42
47
|
options: {
|
|
43
48
|
format: { type: 'string', short: 'f' },
|
|
44
|
-
|
|
49
|
+
config: { type: 'string', short: 'c' },
|
|
50
|
+
ignore: { type: 'string', short: 'i', multiple: true },
|
|
45
51
|
language: { type: 'string', short: 'l' },
|
|
46
52
|
top: { type: 'string' },
|
|
47
53
|
help: { type: 'boolean', short: 'h' },
|
|
@@ -72,8 +78,10 @@ export async function runStats(argv, diagnostics = SILENT) {
|
|
|
72
78
|
}
|
|
73
79
|
let top = DEFAULT_TOP;
|
|
74
80
|
if (values.top !== undefined) {
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
// Digits only: Number() would also take 0x10, 1e3 and ' 3', none of which
|
|
82
|
+
// anybody means as a count.
|
|
83
|
+
top = /^\d+$/u.test(values.top) ? Number(values.top) : Number.NaN;
|
|
84
|
+
if (!Number.isSafeInteger(top) || top < 1) {
|
|
77
85
|
diagnostics.report({
|
|
78
86
|
level: 'error',
|
|
79
87
|
message: `--top needs a whole number of at least 1, not "${values.top}".`,
|
|
@@ -81,7 +89,16 @@ export async function runStats(argv, diagnostics = SILENT) {
|
|
|
81
89
|
return EXIT_USAGE;
|
|
82
90
|
}
|
|
83
91
|
}
|
|
84
|
-
|
|
92
|
+
if (values.config !== undefined && !fs.existsSync(values.config)) {
|
|
93
|
+
diagnostics.report({
|
|
94
|
+
level: 'error',
|
|
95
|
+
message: `Could not find specified config file "${values.config}"`,
|
|
96
|
+
});
|
|
97
|
+
return EXIT_USAGE;
|
|
98
|
+
}
|
|
99
|
+
// The same dialect the linter would read the files in: --language, or else
|
|
100
|
+
// the configuration's. Nothing else in the configuration matters here.
|
|
101
|
+
const language = values.language ?? (await readConfiguredLanguage(values.config));
|
|
85
102
|
if (language !== undefined && !isKnownLanguage(language)) {
|
|
86
103
|
diagnostics.report({
|
|
87
104
|
level: 'error',
|
|
@@ -89,7 +106,8 @@ export async function runStats(argv, diagnostics = SILENT) {
|
|
|
89
106
|
});
|
|
90
107
|
return EXIT_USAGE;
|
|
91
108
|
}
|
|
92
|
-
|
|
109
|
+
// Given more than once, the lists add up rather than the last one winning.
|
|
110
|
+
const ignore = values.ignore?.flatMap((list) => list.split(',').map((pattern) => pattern.trim()));
|
|
93
111
|
const { files, invalidPatterns } = findFeatureFileStream(positionals, ignore);
|
|
94
112
|
if (invalidPatterns.length > 0) {
|
|
95
113
|
for (const pattern of invalidPatterns) {
|
|
@@ -110,4 +128,3 @@ export async function runStats(argv, diagnostics = SILENT) {
|
|
|
110
128
|
console.log(formatter(statistics, { top }));
|
|
111
129
|
return EXIT_OK;
|
|
112
130
|
}
|
|
113
|
-
//# sourceMappingURL=command.js.map
|
|
@@ -5,4 +5,3 @@ import type { Statistics } from '../types.ts';
|
|
|
5
5
|
import type { StatsFormatOptions } from './shared.ts';
|
|
6
6
|
/** The report, as Markdown. */
|
|
7
7
|
export declare function toMarkdown(statistics: Statistics, options: StatsFormatOptions): string;
|
|
8
|
-
//# sourceMappingURL=markdown.d.ts.map
|
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import { andMore, head, location, percent, plural, summarise } from './shared.js';
|
|
2
2
|
/** A pipe inside a cell would start a column of its own. */
|
|
3
|
-
function
|
|
3
|
+
function inCell(text) {
|
|
4
4
|
return text.replace(/\|/gu, '\\|');
|
|
5
5
|
}
|
|
6
|
-
/**
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Text written into a table cell as it is: Markdown's own characters escaped,
|
|
8
|
+
* so a scenario called `*important*` is not shown in italics, and a line
|
|
9
|
+
* break, which would end the row, made a space.
|
|
10
|
+
*/
|
|
11
|
+
function cell(text) {
|
|
12
|
+
return inCell(text.replace(/[\\`*_{}[\]<>#!~]/gu, '\\$&').replace(/\r?\n/gu, ' '));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* ``` `text` ```, so step text keeps its quotes and angle brackets.
|
|
16
|
+
*
|
|
17
|
+
* The fence is one backtick longer than the longest run inside the text, so a
|
|
18
|
+
* backtick in the step cannot close it early; a space pads a text starting or
|
|
19
|
+
* ending with one, as CommonMark asks. Pipes are only escaped inside a table,
|
|
20
|
+
* because anywhere else the backslash would be shown.
|
|
21
|
+
*/
|
|
22
|
+
function code(text, inTable = true) {
|
|
23
|
+
const longest = Math.max(0, ...(text.match(/`+/gu) ?? []).map((run) => run.length));
|
|
24
|
+
const fence = '`'.repeat(longest + 1);
|
|
25
|
+
const padding = text.startsWith('`') || text.endsWith('`') ? ' ' : '';
|
|
26
|
+
const span = `${fence}${padding}${text}${padding}${fence}`;
|
|
27
|
+
return inTable ? inCell(span) : span;
|
|
9
28
|
}
|
|
10
29
|
function table(headings, alignments, rows) {
|
|
11
30
|
return [
|
|
@@ -106,7 +125,7 @@ export function toMarkdown(statistics, options) {
|
|
|
106
125
|
}
|
|
107
126
|
if (tags.usedOnce.length > 0) {
|
|
108
127
|
const tagsOnce = head(tags.usedOnce, options.top);
|
|
109
|
-
lines.push('', '### Tags written exactly once', '', tagsOnce.shown.map(code).join(', '), ...more(tagsOnce.hidden, 'tag'));
|
|
128
|
+
lines.push('', '### Tags written exactly once', '', tagsOnce.shown.map((tag) => code(tag, false)).join(', '), ...more(tagsOnce.hidden, 'tag'));
|
|
110
129
|
}
|
|
111
130
|
if (languages.length > 1) {
|
|
112
131
|
lines.push('', '## Languages', '', ...table(['Dialect', 'Files'], ['left', 'right'], languages.map((language) => [code(language.code), String(language.files)])));
|
|
@@ -120,4 +139,3 @@ export function toMarkdown(statistics, options) {
|
|
|
120
139
|
}
|
|
121
140
|
return lines.join('\n');
|
|
122
141
|
}
|
|
123
|
-
//# sourceMappingURL=markdown.js.map
|
|
@@ -2,4 +2,3 @@ import type { Statistics } from '../types.ts';
|
|
|
2
2
|
import type { StatsFormatOptions } from './shared.ts';
|
|
3
3
|
/** The report, as a block of text. */
|
|
4
4
|
export declare function toText(statistics: Statistics, options: StatsFormatOptions): string;
|
|
5
|
-
//# sourceMappingURL=text.d.ts.map
|