gurkencheck 0.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/LICENSE +13 -0
- package/README.md +327 -0
- package/dist/config-parser.d.ts +21 -0
- package/dist/config-parser.d.ts.map +1 -0
- package/dist/config-parser.js +171 -0
- package/dist/config-parser.js.map +1 -0
- package/dist/config-verifier.d.ts +4 -0
- package/dist/config-verifier.d.ts.map +1 -0
- package/dist/config-verifier.js +81 -0
- package/dist/config-verifier.js.map +1 -0
- package/dist/feature-finder.d.ts +18 -0
- package/dist/feature-finder.d.ts.map +1 -0
- package/dist/feature-finder.js +70 -0
- package/dist/feature-finder.js.map +1 -0
- package/dist/formatters/index.d.ts +23 -0
- package/dist/formatters/index.d.ts.map +1 -0
- package/dist/formatters/index.js +79 -0
- package/dist/formatters/index.js.map +1 -0
- package/dist/formatters/json.d.ts +31 -0
- package/dist/formatters/json.d.ts.map +1 -0
- package/dist/formatters/json.js +31 -0
- package/dist/formatters/json.js.map +1 -0
- package/dist/formatters/junit.d.ts +17 -0
- package/dist/formatters/junit.d.ts.map +1 -0
- package/dist/formatters/junit.js +61 -0
- package/dist/formatters/junit.js.map +1 -0
- package/dist/formatters/sarif.d.ts +6 -0
- package/dist/formatters/sarif.d.ts.map +1 -0
- package/dist/formatters/sarif.js +81 -0
- package/dist/formatters/sarif.js.map +1 -0
- package/dist/formatters/stylish.d.ts +9 -0
- package/dist/formatters/stylish.d.ts.map +1 -0
- package/dist/formatters/stylish.js +66 -0
- package/dist/formatters/stylish.js.map +1 -0
- package/dist/formatters/tap.d.ts +14 -0
- package/dist/formatters/tap.d.ts.map +1 -0
- package/dist/formatters/tap.js +45 -0
- package/dist/formatters/tap.js.map +1 -0
- package/dist/gherkin/dialects.d.ts +31 -0
- package/dist/gherkin/dialects.d.ts.map +1 -0
- package/dist/gherkin/dialects.js +40 -0
- package/dist/gherkin/dialects.js.map +1 -0
- package/dist/gherkin/keywords.d.ts +21 -0
- package/dist/gherkin/keywords.d.ts.map +1 -0
- package/dist/gherkin/keywords.js +53 -0
- package/dist/gherkin/keywords.js.map +1 -0
- package/dist/gherkin/parse.d.ts +32 -0
- package/dist/gherkin/parse.d.ts.map +1 -0
- package/dist/gherkin/parse.js +236 -0
- package/dist/gherkin/parse.js.map +1 -0
- package/dist/gherkin/traverse.d.ts +45 -0
- package/dist/gherkin/traverse.d.ts.map +1 -0
- package/dist/gherkin/traverse.js +89 -0
- package/dist/gherkin/traverse.js.map +1 -0
- package/dist/gherkin/variables.d.ts +26 -0
- package/dist/gherkin/variables.d.ts.map +1 -0
- package/dist/gherkin/variables.js +51 -0
- package/dist/gherkin/variables.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/linter.d.ts +23 -0
- package/dist/linter.d.ts.map +1 -0
- package/dist/linter.js +57 -0
- package/dist/linter.js.map +1 -0
- package/dist/logger.d.ts +16 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +28 -0
- package/dist/logger.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +144 -0
- package/dist/main.js.map +1 -0
- package/dist/presets.d.ts +19 -0
- package/dist/presets.d.ts.map +1 -0
- package/dist/presets.js +24 -0
- package/dist/presets.js.map +1 -0
- package/dist/rules/allowed-tags.d.ts +4 -0
- package/dist/rules/allowed-tags.d.ts.map +1 -0
- package/dist/rules/allowed-tags.js +34 -0
- package/dist/rules/allowed-tags.js.map +1 -0
- package/dist/rules/file-name.d.ts +4 -0
- package/dist/rules/file-name.d.ts.map +1 -0
- package/dist/rules/file-name.js +41 -0
- package/dist/rules/file-name.js.map +1 -0
- package/dist/rules/indentation.d.ts +4 -0
- package/dist/rules/indentation.d.ts.map +1 -0
- package/dist/rules/indentation.js +153 -0
- package/dist/rules/indentation.js.map +1 -0
- package/dist/rules/index.d.ts +13 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +70 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/keywords-in-logical-order.d.ts +4 -0
- package/dist/rules/keywords-in-logical-order.d.ts.map +1 -0
- package/dist/rules/keywords-in-logical-order.js +37 -0
- package/dist/rules/keywords-in-logical-order.js.map +1 -0
- package/dist/rules/max-scenarios-per-file.d.ts +4 -0
- package/dist/rules/max-scenarios-per-file.d.ts.map +1 -0
- package/dist/rules/max-scenarios-per-file.js +42 -0
- package/dist/rules/max-scenarios-per-file.js.map +1 -0
- package/dist/rules/name-length.d.ts +4 -0
- package/dist/rules/name-length.d.ts.map +1 -0
- package/dist/rules/name-length.js +50 -0
- package/dist/rules/name-length.js.map +1 -0
- package/dist/rules/new-line-at-eof.d.ts +4 -0
- package/dist/rules/new-line-at-eof.d.ts.map +1 -0
- package/dist/rules/new-line-at-eof.js +30 -0
- package/dist/rules/new-line-at-eof.js.map +1 -0
- package/dist/rules/no-background-only-scenario.d.ts +4 -0
- package/dist/rules/no-background-only-scenario.d.ts.map +1 -0
- package/dist/rules/no-background-only-scenario.js +43 -0
- package/dist/rules/no-background-only-scenario.js.map +1 -0
- package/dist/rules/no-dupe-feature-names.d.ts +4 -0
- package/dist/rules/no-dupe-feature-names.d.ts.map +1 -0
- package/dist/rules/no-dupe-feature-names.js +25 -0
- package/dist/rules/no-dupe-feature-names.js.map +1 -0
- package/dist/rules/no-dupe-file-names.d.ts +4 -0
- package/dist/rules/no-dupe-file-names.d.ts.map +1 -0
- package/dist/rules/no-dupe-file-names.js +28 -0
- package/dist/rules/no-dupe-file-names.js.map +1 -0
- package/dist/rules/no-dupe-scenario-names.d.ts +4 -0
- package/dist/rules/no-dupe-scenario-names.d.ts.map +1 -0
- package/dist/rules/no-dupe-scenario-names.js +41 -0
- package/dist/rules/no-dupe-scenario-names.js.map +1 -0
- package/dist/rules/no-duplicate-tags.d.ts +4 -0
- package/dist/rules/no-duplicate-tags.d.ts.map +1 -0
- package/dist/rules/no-duplicate-tags.js +38 -0
- package/dist/rules/no-duplicate-tags.js.map +1 -0
- package/dist/rules/no-empty-background.d.ts +4 -0
- package/dist/rules/no-empty-background.d.ts.map +1 -0
- package/dist/rules/no-empty-background.js +24 -0
- package/dist/rules/no-empty-background.js.map +1 -0
- package/dist/rules/no-empty-file.d.ts +4 -0
- package/dist/rules/no-empty-file.d.ts.map +1 -0
- package/dist/rules/no-empty-file.js +12 -0
- package/dist/rules/no-empty-file.js.map +1 -0
- package/dist/rules/no-examples-in-scenarios.d.ts +4 -0
- package/dist/rules/no-examples-in-scenarios.d.ts.map +1 -0
- package/dist/rules/no-examples-in-scenarios.js +25 -0
- package/dist/rules/no-examples-in-scenarios.js.map +1 -0
- package/dist/rules/no-files-without-scenarios.d.ts +4 -0
- package/dist/rules/no-files-without-scenarios.d.ts.map +1 -0
- package/dist/rules/no-files-without-scenarios.js +18 -0
- package/dist/rules/no-files-without-scenarios.js.map +1 -0
- package/dist/rules/no-homogenous-tags.d.ts +4 -0
- package/dist/rules/no-homogenous-tags.d.ts.map +1 -0
- package/dist/rules/no-homogenous-tags.js +54 -0
- package/dist/rules/no-homogenous-tags.js.map +1 -0
- package/dist/rules/no-multiple-empty-lines.d.ts +4 -0
- package/dist/rules/no-multiple-empty-lines.d.ts.map +1 -0
- package/dist/rules/no-multiple-empty-lines.js +44 -0
- package/dist/rules/no-multiple-empty-lines.js.map +1 -0
- package/dist/rules/no-partially-commented-tag-lines.d.ts +4 -0
- package/dist/rules/no-partially-commented-tag-lines.d.ts.map +1 -0
- package/dist/rules/no-partially-commented-tag-lines.js +44 -0
- package/dist/rules/no-partially-commented-tag-lines.js.map +1 -0
- package/dist/rules/no-restricted-patterns.d.ts +4 -0
- package/dist/rules/no-restricted-patterns.d.ts.map +1 -0
- package/dist/rules/no-restricted-patterns.js +101 -0
- package/dist/rules/no-restricted-patterns.js.map +1 -0
- package/dist/rules/no-restricted-tags.d.ts +4 -0
- package/dist/rules/no-restricted-tags.d.ts.map +1 -0
- package/dist/rules/no-restricted-tags.js +35 -0
- package/dist/rules/no-restricted-tags.js.map +1 -0
- package/dist/rules/no-scenario-outlines-without-examples.d.ts +4 -0
- package/dist/rules/no-scenario-outlines-without-examples.d.ts.map +1 -0
- package/dist/rules/no-scenario-outlines-without-examples.js +29 -0
- package/dist/rules/no-scenario-outlines-without-examples.js.map +1 -0
- package/dist/rules/no-superfluous-tags.d.ts +4 -0
- package/dist/rules/no-superfluous-tags.d.ts.map +1 -0
- package/dist/rules/no-superfluous-tags.js +40 -0
- package/dist/rules/no-superfluous-tags.js.map +1 -0
- package/dist/rules/no-trailing-spaces.d.ts +4 -0
- package/dist/rules/no-trailing-spaces.d.ts.map +1 -0
- package/dist/rules/no-trailing-spaces.js +24 -0
- package/dist/rules/no-trailing-spaces.js.map +1 -0
- package/dist/rules/no-undeclared-variables.d.ts +4 -0
- package/dist/rules/no-undeclared-variables.d.ts.map +1 -0
- package/dist/rules/no-undeclared-variables.js +35 -0
- package/dist/rules/no-undeclared-variables.js.map +1 -0
- package/dist/rules/no-unnamed-features.d.ts +4 -0
- package/dist/rules/no-unnamed-features.d.ts.map +1 -0
- package/dist/rules/no-unnamed-features.js +19 -0
- package/dist/rules/no-unnamed-features.js.map +1 -0
- package/dist/rules/no-unnamed-scenarios.d.ts +4 -0
- package/dist/rules/no-unnamed-scenarios.d.ts.map +1 -0
- package/dist/rules/no-unnamed-scenarios.js +24 -0
- package/dist/rules/no-unnamed-scenarios.js.map +1 -0
- package/dist/rules/no-unused-variables.d.ts +4 -0
- package/dist/rules/no-unused-variables.d.ts.map +1 -0
- package/dist/rules/no-unused-variables.js +36 -0
- package/dist/rules/no-unused-variables.js.map +1 -0
- package/dist/rules/one-space-between-tags.d.ts +4 -0
- package/dist/rules/one-space-between-tags.d.ts.map +1 -0
- package/dist/rules/one-space-between-tags.js +36 -0
- package/dist/rules/one-space-between-tags.js.map +1 -0
- package/dist/rules/only-one-when.d.ts +4 -0
- package/dist/rules/only-one-when.d.ts.map +1 -0
- package/dist/rules/only-one-when.js +59 -0
- package/dist/rules/only-one-when.js.map +1 -0
- package/dist/rules/required-tags.d.ts +4 -0
- package/dist/rules/required-tags.d.ts.map +1 -0
- package/dist/rules/required-tags.js +36 -0
- package/dist/rules/required-tags.js.map +1 -0
- package/dist/rules/scenario-size.d.ts +4 -0
- package/dist/rules/scenario-size.d.ts.map +1 -0
- package/dist/rules/scenario-size.js +37 -0
- package/dist/rules/scenario-size.js.map +1 -0
- package/dist/rules/use-and.d.ts +4 -0
- package/dist/rules/use-and.d.ts.map +1 -0
- package/dist/rules/use-and.js +33 -0
- package/dist/rules/use-and.js.map +1 -0
- package/dist/rules.d.ts +24 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +109 -0
- package/dist/rules.js.map +1 -0
- package/dist/suppressions.d.ts +33 -0
- package/dist/suppressions.d.ts.map +1 -0
- package/dist/suppressions.js +84 -0
- package/dist/suppressions.js.map +1 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/util/case.d.ts +21 -0
- package/dist/util/case.d.ts.map +1 -0
- package/dist/util/case.js +42 -0
- package/dist/util/case.js.map +1 -0
- package/dist/util/collections.d.ts +30 -0
- package/dist/util/collections.d.ts.map +1 -0
- package/dist/util/collections.js +87 -0
- package/dist/util/collections.js.map +1 -0
- package/dist/util/glob.d.ts +20 -0
- package/dist/util/glob.d.ts.map +1 -0
- package/dist/util/glob.js +203 -0
- package/dist/util/glob.js.map +1 -0
- package/dist/util/json.d.ts +14 -0
- package/dist/util/json.d.ts.map +1 -0
- package/dist/util/json.js +63 -0
- package/dist/util/json.js.map +1 -0
- package/dist/util/lines.d.ts +24 -0
- package/dist/util/lines.d.ts.map +1 -0
- package/dist/util/lines.js +45 -0
- package/dist/util/lines.js.map +1 -0
- package/dist/util/location.d.ts +15 -0
- package/dist/util/location.d.ts.map +1 -0
- package/dist/util/location.js +11 -0
- package/dist/util/location.js.map +1 -0
- package/dist/util/xml.d.ts +16 -0
- package/dist/util/xml.d.ts.map +1 -0
- package/dist/util/xml.js +36 -0
- package/dist/util/xml.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +24 -0
- package/dist/version.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/** The keyword lists for one language. */
|
|
2
|
+
export interface Dialect {
|
|
3
|
+
name: string;
|
|
4
|
+
native: string;
|
|
5
|
+
feature: readonly string[];
|
|
6
|
+
rule: readonly string[];
|
|
7
|
+
background: readonly string[];
|
|
8
|
+
scenario: readonly string[];
|
|
9
|
+
scenarioOutline: readonly string[];
|
|
10
|
+
examples: readonly string[];
|
|
11
|
+
given: readonly string[];
|
|
12
|
+
when: readonly string[];
|
|
13
|
+
then: readonly string[];
|
|
14
|
+
and: readonly string[];
|
|
15
|
+
but: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
export declare const DEFAULT_LANGUAGE = "en";
|
|
18
|
+
/** The dialect for a language code, falling back to English. */
|
|
19
|
+
export declare function getDialect(language: string | undefined): Dialect;
|
|
20
|
+
/** True when `language` is one Gherkin knows about. */
|
|
21
|
+
export declare function isKnownLanguage(language: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Reads the `# language: xx` header from the top of a feature file. Only the
|
|
24
|
+
* leading comment and blank lines are inspected, which is where Gherkin
|
|
25
|
+
* itself expects the header to be.
|
|
26
|
+
*
|
|
27
|
+
* A file with no header is read in `fallback`, which lets a project written
|
|
28
|
+
* entirely in one language set it once instead of in every file.
|
|
29
|
+
*/
|
|
30
|
+
export declare function detectLanguage(lines: readonly string[], fallback?: string): string;
|
|
31
|
+
//# sourceMappingURL=dialects.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialects.d.ts","sourceRoot":"","sources":["../../src/gherkin/dialects.ts"],"names":[],"mappings":"AAKA,0CAA0C;AAC1C,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9B,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,eAAe,EAAE,SAAS,MAAM,EAAE,CAAC;IACnC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,KAAK,EAAE,SAAS,MAAM,EAAE,CAAC;IACzB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;IACvB,GAAG,EAAE,SAAS,MAAM,EAAE,CAAC;CACxB;AAID,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEhE;AAED,uDAAuD;AACvD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEzD;AAID;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,QAAQ,GAAE,MAAyB,GAClC,MAAM,CAeR"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Access to Gherkin's localised keywords.
|
|
3
|
+
*/
|
|
4
|
+
import { dialects } from '@cucumber/gherkin';
|
|
5
|
+
const ALL_DIALECTS = dialects;
|
|
6
|
+
export const DEFAULT_LANGUAGE = 'en';
|
|
7
|
+
/** The dialect for a language code, falling back to English. */
|
|
8
|
+
export function getDialect(language) {
|
|
9
|
+
return ALL_DIALECTS[language ?? DEFAULT_LANGUAGE] ?? ALL_DIALECTS[DEFAULT_LANGUAGE];
|
|
10
|
+
}
|
|
11
|
+
/** True when `language` is one Gherkin knows about. */
|
|
12
|
+
export function isKnownLanguage(language) {
|
|
13
|
+
return language in ALL_DIALECTS;
|
|
14
|
+
}
|
|
15
|
+
const LANGUAGE_HEADER = /^\s*#\s*language\s*:\s*([\w-]+)\s*$/;
|
|
16
|
+
/**
|
|
17
|
+
* Reads the `# language: xx` header from the top of a feature file. Only the
|
|
18
|
+
* leading comment and blank lines are inspected, which is where Gherkin
|
|
19
|
+
* itself expects the header to be.
|
|
20
|
+
*
|
|
21
|
+
* A file with no header is read in `fallback`, which lets a project written
|
|
22
|
+
* entirely in one language set it once instead of in every file.
|
|
23
|
+
*/
|
|
24
|
+
export function detectLanguage(lines, fallback = DEFAULT_LANGUAGE) {
|
|
25
|
+
for (const line of lines) {
|
|
26
|
+
const trimmed = line.trim();
|
|
27
|
+
if (trimmed === '') {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
if (!trimmed.startsWith('#')) {
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
const header = LANGUAGE_HEADER.exec(trimmed);
|
|
34
|
+
if (header?.[1] !== undefined && isKnownLanguage(header[1])) {
|
|
35
|
+
return header[1];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return isKnownLanguage(fallback) ? fallback : DEFAULT_LANGUAGE;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=dialects.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialects.js","sourceRoot":"","sources":["../../src/gherkin/dialects.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAC,QAAQ,EAAC,MAAM,mBAAmB,CAAC;AAmB3C,MAAM,YAAY,GAAG,QAA8C,CAAC;AAEpE,MAAM,CAAC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAErC,gEAAgE;AAChE,MAAM,UAAU,UAAU,CAAC,QAA4B;IACrD,OAAO,YAAY,CAAC,QAAQ,IAAI,gBAAgB,CAAC,IAAI,YAAY,CAAC,gBAAgB,CAAE,CAAC;AACvF,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,QAAQ,IAAI,YAAY,CAAC;AAClC,CAAC;AAED,MAAM,eAAe,GAAG,qCAAqC,CAAC;AAE9D;;;;;;;GAOG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAwB,EACxB,QAAQ,GAAW,gBAAgB;IAEnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5B,IAAI,OAAO,KAAK,EAAE,EAAE,CAAC;YACnB,SAAS;QACX,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM;QACR,CAAC;QACD,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,MAAM,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5D,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning a node's localised keyword back into a language-neutral one.
|
|
3
|
+
*
|
|
4
|
+
* The keyword is the only way to tell a Scenario from a Scenario Outline,
|
|
5
|
+
* because both arrive as a `scenario` node in the parsed document.
|
|
6
|
+
*/
|
|
7
|
+
import type { Background, Examples, Feature, Rule, Scenario, Step } from '@cucumber/messages';
|
|
8
|
+
/** Any node that carries a Gherkin keyword. */
|
|
9
|
+
export type KeywordNode = Feature | Rule | Background | Scenario | Examples | Step;
|
|
10
|
+
/** The keyword categories used across the rules. */
|
|
11
|
+
export type NeutralKeyword = 'feature' | 'rule' | 'background' | 'scenario' | 'scenariooutline' | 'examples' | 'given' | 'when' | 'then' | 'and' | 'but' | '';
|
|
12
|
+
/**
|
|
13
|
+
* Maps a node's localised keyword (`Szenario`) onto the language-neutral name
|
|
14
|
+
* (`scenario`). Returns an empty string for keywords the dialect does not know.
|
|
15
|
+
*/
|
|
16
|
+
export declare function getNeutralKeyword(node: Pick<KeywordNode, 'keyword'>, language: string | undefined): NeutralKeyword;
|
|
17
|
+
/** True when the keyword is a Given/When/Then/And/But step keyword. */
|
|
18
|
+
export declare function isStepKeyword(keyword: NeutralKeyword): boolean;
|
|
19
|
+
/** The display name for a node, as used in error messages. */
|
|
20
|
+
export declare function getNodeType(node: Pick<KeywordNode, 'keyword'>, language: string | undefined): string;
|
|
21
|
+
//# sourceMappingURL=keywords.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keywords.d.ts","sourceRoot":"","sources":["../../src/gherkin/keywords.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAC,MAAM,oBAAoB,CAAC;AAG5F,+CAA+C;AAC/C,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,IAAI,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;AAEnF,oDAAoD;AACpD,MAAM,MAAM,cAAc,GACtB,SAAS,GACT,MAAM,GACN,YAAY,GACZ,UAAU,GACV,iBAAiB,GACjB,UAAU,GACV,OAAO,GACP,MAAM,GACN,MAAM,GACN,KAAK,GACL,KAAK,GACL,EAAE,CAAC;AAkBP;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,EAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,cAAc,CAQhB;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAE9D;AAED,8DAA8D;AAC9D,wBAAgB,WAAW,CACzB,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,EAClC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAC3B,MAAM,CAkBR"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getDialect } from './dialects.js';
|
|
2
|
+
const KEYWORD_FIELDS = [
|
|
3
|
+
'feature',
|
|
4
|
+
'rule',
|
|
5
|
+
'background',
|
|
6
|
+
'scenario',
|
|
7
|
+
'scenarioOutline',
|
|
8
|
+
'examples',
|
|
9
|
+
'given',
|
|
10
|
+
'when',
|
|
11
|
+
'then',
|
|
12
|
+
'and',
|
|
13
|
+
'but',
|
|
14
|
+
];
|
|
15
|
+
const STEP_KEYWORDS = new Set(['given', 'when', 'then', 'and', 'but']);
|
|
16
|
+
/**
|
|
17
|
+
* Maps a node's localised keyword (`Szenario`) onto the language-neutral name
|
|
18
|
+
* (`scenario`). Returns an empty string for keywords the dialect does not know.
|
|
19
|
+
*/
|
|
20
|
+
export function getNeutralKeyword(node, language) {
|
|
21
|
+
const dialect = getDialect(language);
|
|
22
|
+
for (const field of KEYWORD_FIELDS) {
|
|
23
|
+
if (dialect[field].includes(node.keyword)) {
|
|
24
|
+
return field.toLowerCase();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
/** True when the keyword is a Given/When/Then/And/But step keyword. */
|
|
30
|
+
export function isStepKeyword(keyword) {
|
|
31
|
+
return STEP_KEYWORDS.has(keyword);
|
|
32
|
+
}
|
|
33
|
+
/** The display name for a node, as used in error messages. */
|
|
34
|
+
export function getNodeType(node, language) {
|
|
35
|
+
const keyword = getNeutralKeyword(node, language);
|
|
36
|
+
switch (keyword) {
|
|
37
|
+
case 'feature':
|
|
38
|
+
return 'Feature';
|
|
39
|
+
case 'rule':
|
|
40
|
+
return 'Rule';
|
|
41
|
+
case 'background':
|
|
42
|
+
return 'Background';
|
|
43
|
+
case 'scenario':
|
|
44
|
+
return 'Scenario';
|
|
45
|
+
case 'scenariooutline':
|
|
46
|
+
return 'Scenario Outline';
|
|
47
|
+
case 'examples':
|
|
48
|
+
return 'Examples';
|
|
49
|
+
default:
|
|
50
|
+
return isStepKeyword(keyword) ? 'Step' : '';
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=keywords.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keywords.js","sourceRoot":"","sources":["../../src/gherkin/keywords.ts"],"names":[],"mappings":"AAOA,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAoBzC,MAAM,cAAc,GAAG;IACrB,SAAS;IACT,MAAM;IACN,YAAY;IACZ,UAAU;IACV,iBAAiB;IACjB,UAAU;IACV,OAAO;IACP,MAAM;IACN,MAAM;IACN,KAAK;IACL,KAAK;CACG,CAAC;AAEX,MAAM,aAAa,GAAG,IAAI,GAAG,CAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAEvF;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAAkC,EAClC,QAA4B;IAE5B,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,CAAC;IACrC,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;QACnC,IAAI,OAAO,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1C,OAAO,KAAK,CAAC,WAAW,EAAoB,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,uEAAuE;AACvE,MAAM,UAAU,aAAa,CAAC,OAAuB;IACnD,OAAO,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,WAAW,CACzB,IAAkC,EAClC,QAA4B;IAE5B,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAClD,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,SAAS;YACZ,OAAO,SAAS,CAAC;QACnB,KAAK,MAAM;YACT,OAAO,MAAM,CAAC;QAChB,KAAK,YAAY;YACf,OAAO,YAAY,CAAC;QACtB,KAAK,UAAU;YACb,OAAO,UAAU,CAAC;QACpB,KAAK,iBAAiB;YACpB,OAAO,kBAAkB,CAAC;QAC5B,KAAK,UAAU;YACb,OAAO,UAAU,CAAC;QACpB;YACE,OAAO,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAChD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Feature } from '@cucumber/messages';
|
|
2
|
+
import type { FeatureFile, RuleError } from '../types.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Rules the parser enforces on every file. They cannot be turned off, but
|
|
5
|
+
* they may be named in a configuration file, because they are documented
|
|
6
|
+
* alongside the rules that can.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ALWAYS_ON_RULES: readonly ['no-tags-on-backgrounds', 'one-feature-per-file', 'up-to-one-background-per-file', 'background-before-scenarios', 'no-multiline-steps'];
|
|
9
|
+
/** The rule names parse errors are reported under. */
|
|
10
|
+
export declare const PARSER_RULES: readonly ["no-tags-on-backgrounds", "one-feature-per-file", "up-to-one-background-per-file", "background-before-scenarios", "no-multiline-steps", "unexpected-error"];
|
|
11
|
+
/** The outcome of reading and parsing one feature file. */
|
|
12
|
+
export interface ParseResult {
|
|
13
|
+
file: FeatureFile;
|
|
14
|
+
/** The parsed feature, or `undefined` when parsing failed or the file is empty. */
|
|
15
|
+
feature: Feature | undefined;
|
|
16
|
+
/** Fatal parse errors. When this is non-empty, no rules are run on the file. */
|
|
17
|
+
errors: RuleError[];
|
|
18
|
+
}
|
|
19
|
+
/** Splits source text into lines, accepting any of the three line endings. */
|
|
20
|
+
export declare function toLines(source: string): string[];
|
|
21
|
+
/**
|
|
22
|
+
* Parses feature file source. When the parser rejects the file, the returned
|
|
23
|
+
* `feature` is `undefined` and `errors` describes what went wrong.
|
|
24
|
+
*
|
|
25
|
+
* `defaultLanguage` is the dialect to read a file in when it carries no
|
|
26
|
+
* `# language:` header of its own, for projects written entirely in one
|
|
27
|
+
* language.
|
|
28
|
+
*/
|
|
29
|
+
export declare function parseFeature(relativePath: string, source: string, defaultLanguage?: string): ParseResult;
|
|
30
|
+
/** Reads a feature file from disk and parses it. */
|
|
31
|
+
export declare function readAndParseFile(relativePath: string, defaultLanguage?: string): Promise<ParseResult>;
|
|
32
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/gherkin/parse.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAEhD,OAAO,KAAK,EAAC,WAAW,EAAE,SAAS,EAAC,MAAM,aAAa,CAAC;AAIxD;;;;GAIG;AACH,eAAO,MAAM,eAAe,YAC1B,wBAAwB,EACxB,sBAAsB,EACtB,+BAA+B,EAC/B,6BAA6B,EAC7B,oBAAoB,CACZ,CAAC;AAEX,sDAAsD;AACtD,eAAO,MAAM,YAAY,uKAAoD,CAAC;AAE9E,2DAA2D;AAC3D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAC;IAClB,mFAAmF;IACnF,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC;IAC7B,gFAAgF;IAChF,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAmMD,8EAA8E;AAC9E,wBAAgB,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAEhD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,eAAe,GAAE,MAAyB,GACzC,WAAW,CA8Cb;AAED,oDAAoD;AACpD,wBAAsB,gBAAgB,CACpC,YAAY,EAAE,MAAM,EACpB,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,WAAW,CAAC,CAGtB"}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing feature files, and turning the parser's complaints into lint errors.
|
|
3
|
+
*
|
|
4
|
+
* A handful of Gherkin constructs are rejected by the parser itself rather
|
|
5
|
+
* than by a rule: a second Feature, a second Background, a tag on a
|
|
6
|
+
* Background, and a step continued onto the next line. Those show up here as
|
|
7
|
+
* parse failures, and this module maps them onto the rule names users see.
|
|
8
|
+
* Because the parser refuses to build a document for such a file, these
|
|
9
|
+
* checks cannot be switched off.
|
|
10
|
+
*/
|
|
11
|
+
import { AstBuilder, GherkinClassicTokenMatcher, Parser } from '@cucumber/gherkin';
|
|
12
|
+
import { IdGenerator } from '@cucumber/messages';
|
|
13
|
+
import { readFile } from 'node:fs/promises';
|
|
14
|
+
import { DEFAULT_LANGUAGE, detectLanguage, getDialect } from './dialects.js';
|
|
15
|
+
/**
|
|
16
|
+
* Rules the parser enforces on every file. They cannot be turned off, but
|
|
17
|
+
* they may be named in a configuration file, because they are documented
|
|
18
|
+
* alongside the rules that can.
|
|
19
|
+
*/
|
|
20
|
+
export const ALWAYS_ON_RULES = [
|
|
21
|
+
'no-tags-on-backgrounds',
|
|
22
|
+
'one-feature-per-file',
|
|
23
|
+
'up-to-one-background-per-file',
|
|
24
|
+
'background-before-scenarios',
|
|
25
|
+
'no-multiline-steps',
|
|
26
|
+
];
|
|
27
|
+
/** The rule names parse errors are reported under. */
|
|
28
|
+
export const PARSER_RULES = [...ALWAYS_ON_RULES, 'unexpected-error'];
|
|
29
|
+
const UNEXPECTED_TOKEN = /^\((\d+):(\d+)\): expected: (.*), got '([\s\S]*)'$/;
|
|
30
|
+
const UNEXPECTED_EOF = /^\((\d+):(\d+)\): unexpected end of file, expected: (.*)$/;
|
|
31
|
+
/** How many times a tag-on-background may be stripped before giving up. */
|
|
32
|
+
const MAX_TAG_RECOVERIES = 20;
|
|
33
|
+
function newParser(defaultLanguage) {
|
|
34
|
+
return new Parser(new AstBuilder(IdGenerator.uuid()), new GherkinClassicTokenMatcher(defaultLanguage));
|
|
35
|
+
}
|
|
36
|
+
function toComplaint(error) {
|
|
37
|
+
const raw = error instanceof Error ? error.message : String(error);
|
|
38
|
+
const location = error.location;
|
|
39
|
+
const unexpectedToken = UNEXPECTED_TOKEN.exec(raw);
|
|
40
|
+
if (unexpectedToken) {
|
|
41
|
+
return {
|
|
42
|
+
line: Number(unexpectedToken[1]),
|
|
43
|
+
column: Number(unexpectedToken[2]),
|
|
44
|
+
expected: unexpectedToken[3].split(', '),
|
|
45
|
+
got: unexpectedToken[4],
|
|
46
|
+
raw,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const unexpectedEof = UNEXPECTED_EOF.exec(raw);
|
|
50
|
+
if (unexpectedEof) {
|
|
51
|
+
return {
|
|
52
|
+
line: Number(unexpectedEof[1]),
|
|
53
|
+
column: Number(unexpectedEof[2]),
|
|
54
|
+
expected: unexpectedEof[3].split(', '),
|
|
55
|
+
got: undefined,
|
|
56
|
+
raw,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
line: location?.line ?? 0,
|
|
61
|
+
column: location?.column ?? 0,
|
|
62
|
+
expected: [],
|
|
63
|
+
got: undefined,
|
|
64
|
+
raw,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function parseSource(source, defaultLanguage) {
|
|
68
|
+
try {
|
|
69
|
+
const document = newParser(defaultLanguage).parse(source);
|
|
70
|
+
return { feature: document.feature ?? undefined, complaints: [] };
|
|
71
|
+
}
|
|
72
|
+
catch (thrown) {
|
|
73
|
+
const nested = thrown.errors;
|
|
74
|
+
const errors = Array.isArray(nested) && nested.length > 0 ? nested : [thrown];
|
|
75
|
+
return { feature: undefined, complaints: errors.map(toComplaint) };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** True when the offending text opens a node of one of the given keywords. */
|
|
79
|
+
function startsWithKeyword(text, keywords) {
|
|
80
|
+
return keywords.some((keyword) => text.startsWith(`${keyword.trim()}:`));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* True when a Background is already open above `line`.
|
|
84
|
+
*
|
|
85
|
+
* The parser rejects a second Background and a Background written below a
|
|
86
|
+
* Scenario in exactly the same way, but they are different mistakes with
|
|
87
|
+
* different fixes, so the lines above are checked to tell them apart.
|
|
88
|
+
*/
|
|
89
|
+
function hasEarlierBackground(lines, line, dialect) {
|
|
90
|
+
return lines
|
|
91
|
+
.slice(0, Math.max(0, line - 1))
|
|
92
|
+
.some((text) => startsWithKeyword(text.trim(), dialect.background));
|
|
93
|
+
}
|
|
94
|
+
/** The step keywords of a dialect, without the `*` wildcard, for messages. */
|
|
95
|
+
function stepKeywordsFor(dialect) {
|
|
96
|
+
return [dialect.given, dialect.when, dialect.then, dialect.and, dialect.but]
|
|
97
|
+
.map((keywords) => keywords.map((keyword) => keyword.trim()).find((keyword) => keyword !== '*'))
|
|
98
|
+
.filter((keyword) => keyword !== undefined);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* A tag written directly above a Background. The parser reports this as
|
|
102
|
+
* "expected a tag line or a rule line" - it is still waiting for something a
|
|
103
|
+
* tag can attach to, and a Background is not one of those things.
|
|
104
|
+
*/
|
|
105
|
+
function isTagOnBackground(complaint, dialect) {
|
|
106
|
+
return (complaint.got !== undefined &&
|
|
107
|
+
complaint.expected.includes('#TagLine') &&
|
|
108
|
+
!complaint.expected.includes('#ScenarioLine') &&
|
|
109
|
+
!complaint.expected.includes('#StepLine') &&
|
|
110
|
+
startsWithKeyword(complaint.got, dialect.background));
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Blanks the tag lines sitting above `line`, so the file can be parsed again
|
|
114
|
+
* to find the problems hiding behind the tag. Lines are blanked rather than
|
|
115
|
+
* removed so that every other line number stays correct.
|
|
116
|
+
*
|
|
117
|
+
* Returns `undefined` when there was no tag line to blank.
|
|
118
|
+
*/
|
|
119
|
+
function withoutTagsAbove(lines, line) {
|
|
120
|
+
const result = [...lines];
|
|
121
|
+
let index = line - 2;
|
|
122
|
+
let blanked = false;
|
|
123
|
+
while (index >= 0) {
|
|
124
|
+
const trimmed = result[index].trim();
|
|
125
|
+
if (trimmed.startsWith('@')) {
|
|
126
|
+
result[index] = '';
|
|
127
|
+
blanked = true;
|
|
128
|
+
}
|
|
129
|
+
else if (trimmed !== '' && !trimmed.startsWith('#')) {
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
index--;
|
|
133
|
+
}
|
|
134
|
+
return blanked ? result : undefined;
|
|
135
|
+
}
|
|
136
|
+
function classify(complaint, dialect, lines) {
|
|
137
|
+
if (complaint.got !== undefined) {
|
|
138
|
+
if (startsWithKeyword(complaint.got, dialect.background)) {
|
|
139
|
+
return hasEarlierBackground(lines, complaint.line, dialect)
|
|
140
|
+
? {
|
|
141
|
+
message: 'Multiple "Background" definitions in the same file are disallowed',
|
|
142
|
+
rule: 'up-to-one-background-per-file',
|
|
143
|
+
line: complaint.line,
|
|
144
|
+
column: complaint.column,
|
|
145
|
+
}
|
|
146
|
+
: {
|
|
147
|
+
message: 'A "Background" must come before the Scenarios it applies to',
|
|
148
|
+
rule: 'background-before-scenarios',
|
|
149
|
+
line: complaint.line,
|
|
150
|
+
column: complaint.column,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
if (startsWithKeyword(complaint.got, dialect.feature)) {
|
|
154
|
+
return {
|
|
155
|
+
message: 'Multiple "Feature" definitions in the same file are disallowed',
|
|
156
|
+
rule: 'one-feature-per-file',
|
|
157
|
+
line: complaint.line,
|
|
158
|
+
column: complaint.column,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (complaint.expected.includes('#StepLine') && complaint.expected.includes('#DocStringSeparator')) {
|
|
162
|
+
const keywords = stepKeywordsFor(dialect);
|
|
163
|
+
const last = keywords.pop();
|
|
164
|
+
const list = `${keywords.map((keyword) => `"${keyword}"`).join(', ')} or "${last}"`;
|
|
165
|
+
return {
|
|
166
|
+
message: `Steps should begin with ${list}. Multiline steps are disallowed`,
|
|
167
|
+
rule: 'no-multiline-steps',
|
|
168
|
+
line: complaint.line,
|
|
169
|
+
column: complaint.column,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return {
|
|
174
|
+
message: complaint.raw,
|
|
175
|
+
rule: 'unexpected-error',
|
|
176
|
+
line: complaint.line,
|
|
177
|
+
column: complaint.column,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/** Splits source text into lines, accepting any of the three line endings. */
|
|
181
|
+
export function toLines(source) {
|
|
182
|
+
return source.split(/\r\n|\r|\n/);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Parses feature file source. When the parser rejects the file, the returned
|
|
186
|
+
* `feature` is `undefined` and `errors` describes what went wrong.
|
|
187
|
+
*
|
|
188
|
+
* `defaultLanguage` is the dialect to read a file in when it carries no
|
|
189
|
+
* `# language:` header of its own, for projects written entirely in one
|
|
190
|
+
* language.
|
|
191
|
+
*/
|
|
192
|
+
export function parseFeature(relativePath, source, defaultLanguage = DEFAULT_LANGUAGE) {
|
|
193
|
+
const lines = toLines(source);
|
|
194
|
+
const file = { relativePath, lines };
|
|
195
|
+
const dialect = getDialect(detectLanguage(lines, defaultLanguage));
|
|
196
|
+
const errors = [];
|
|
197
|
+
let workingLines = lines;
|
|
198
|
+
for (let attempt = 0; attempt <= MAX_TAG_RECOVERIES; attempt++) {
|
|
199
|
+
const { feature, complaints } = parseSource(workingLines.join('\n'), defaultLanguage);
|
|
200
|
+
if (complaints.length === 0) {
|
|
201
|
+
return { file, feature: errors.length > 0 ? undefined : feature, errors };
|
|
202
|
+
}
|
|
203
|
+
const first = complaints[0];
|
|
204
|
+
if (isTagOnBackground(first, dialect)) {
|
|
205
|
+
const recovered = withoutTagsAbove(workingLines, first.line);
|
|
206
|
+
if (recovered !== undefined) {
|
|
207
|
+
errors.push({
|
|
208
|
+
message: 'Tags on Backgrounds are disallowed',
|
|
209
|
+
rule: 'no-tags-on-backgrounds',
|
|
210
|
+
line: first.line,
|
|
211
|
+
column: first.column,
|
|
212
|
+
});
|
|
213
|
+
workingLines = recovered;
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const classified = complaints.map((complaint) => classify(complaint, dialect, workingLines));
|
|
218
|
+
// A Background in the wrong place derails everything after it: each
|
|
219
|
+
// following line is rejected in turn, and none of those complaints tells
|
|
220
|
+
// the reader anything the first one did not. Report the cause on its own.
|
|
221
|
+
const misplacedBackground = classified[0];
|
|
222
|
+
if (misplacedBackground?.rule === 'background-before-scenarios') {
|
|
223
|
+
errors.push(misplacedBackground);
|
|
224
|
+
return { file, feature: undefined, errors };
|
|
225
|
+
}
|
|
226
|
+
errors.push(...classified);
|
|
227
|
+
return { file, feature: undefined, errors };
|
|
228
|
+
}
|
|
229
|
+
return { file, feature: undefined, errors };
|
|
230
|
+
}
|
|
231
|
+
/** Reads a feature file from disk and parses it. */
|
|
232
|
+
export async function readAndParseFile(relativePath, defaultLanguage) {
|
|
233
|
+
const source = await readFile(relativePath, 'utf8');
|
|
234
|
+
return parseFeature(relativePath, source, defaultLanguage);
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/gherkin/parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Walking a parsed feature.
|
|
3
|
+
*
|
|
4
|
+
* A Feature's children are Backgrounds, Scenarios and Rules, and a Rule has
|
|
5
|
+
* Backgrounds and Scenarios of its own. These helpers flatten that nesting so
|
|
6
|
+
* rules do not each have to remember that Rules exist - several of the
|
|
7
|
+
* original rules did not, and crashed on any feature file using `Rule:`.
|
|
8
|
+
*/
|
|
9
|
+
import type { Background, Examples, Feature, Rule, Scenario } from '@cucumber/messages';
|
|
10
|
+
/** A Background or Scenario, together with the Rule containing it, if any. */
|
|
11
|
+
export interface StepContainer {
|
|
12
|
+
node: Background | Scenario;
|
|
13
|
+
rule: Rule | undefined;
|
|
14
|
+
}
|
|
15
|
+
/** Every Rule in the feature. */
|
|
16
|
+
export declare function rulesOf(feature: Feature): Generator<Rule>;
|
|
17
|
+
/** Every Scenario and Scenario Outline, including those inside Rules. */
|
|
18
|
+
export declare function scenariosOf(feature: Feature): Generator<{
|
|
19
|
+
scenario: Scenario;
|
|
20
|
+
rule: Rule | undefined;
|
|
21
|
+
}>;
|
|
22
|
+
/** Every Background, including those inside Rules. */
|
|
23
|
+
export declare function backgroundsOf(feature: Feature): Generator<{
|
|
24
|
+
background: Background;
|
|
25
|
+
rule: Rule | undefined;
|
|
26
|
+
}>;
|
|
27
|
+
/** Every node that holds steps: Backgrounds and Scenarios, Rules included. */
|
|
28
|
+
export declare function stepContainersOf(feature: Feature): Generator<StepContainer>;
|
|
29
|
+
/** Anything a tag may be written on. */
|
|
30
|
+
export type TaggableNode = Feature | Rule | Scenario | Examples;
|
|
31
|
+
/** A tagged node, with the nodes it sits inside, nearest ancestor first. */
|
|
32
|
+
export interface TaggedNode {
|
|
33
|
+
node: TaggableNode;
|
|
34
|
+
/** Feature, and the Rule and Scenario Outline containing it, if any. */
|
|
35
|
+
ancestors: TaggableNode[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Every node that can carry tags: the Feature, its Rules, every Scenario -
|
|
39
|
+
* inside a Rule or not - and every Examples table.
|
|
40
|
+
*
|
|
41
|
+
* Each one comes with its ancestors, so that a rule comparing a node's tags
|
|
42
|
+
* with the ones it inherits does not have to walk the tree itself.
|
|
43
|
+
*/
|
|
44
|
+
export declare function taggedNodesOf(feature: Feature): Generator<TaggedNode>;
|
|
45
|
+
//# sourceMappingURL=traverse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traverse.d.ts","sourceRoot":"","sources":["../../src/gherkin/traverse.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAC,MAAM,oBAAoB,CAAC;AAEtF,8EAA8E;AAC9E,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,IAAI,GAAG,SAAS,CAAC;CACxB;AAED,iCAAiC;AACjC,wBAAiB,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAM1D;AAED,yEAAyE;AACzE,wBAAiB,WAAW,CAC1B,OAAO,EAAE,OAAO,GACf,SAAS,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAA;CAAC,CAAC,CAazD;AAED,sDAAsD;AACtD,wBAAiB,aAAa,CAC5B,OAAO,EAAE,OAAO,GACf,SAAS,CAAC;IAAC,UAAU,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,SAAS,CAAA;CAAC,CAAC,CAa7D;AAED,8EAA8E;AAC9E,wBAAiB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC,aAAa,CAAC,CAmB5E;AAED,wCAAwC;AACxC,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,IAAI,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEhE,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,YAAY,CAAC;IACnB,wEAAwE;IACxE,SAAS,EAAE,YAAY,EAAE,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAiB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,CAAC,UAAU,CAAC,CAuBtE"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/** Every Rule in the feature. */
|
|
2
|
+
export function* rulesOf(feature) {
|
|
3
|
+
for (const child of feature.children) {
|
|
4
|
+
if (child.rule !== undefined) {
|
|
5
|
+
yield child.rule;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/** Every Scenario and Scenario Outline, including those inside Rules. */
|
|
10
|
+
export function* scenariosOf(feature) {
|
|
11
|
+
for (const child of feature.children) {
|
|
12
|
+
if (child.scenario !== undefined) {
|
|
13
|
+
yield { scenario: child.scenario, rule: undefined };
|
|
14
|
+
}
|
|
15
|
+
if (child.rule !== undefined) {
|
|
16
|
+
for (const grandchild of child.rule.children) {
|
|
17
|
+
if (grandchild.scenario !== undefined) {
|
|
18
|
+
yield { scenario: grandchild.scenario, rule: child.rule };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Every Background, including those inside Rules. */
|
|
25
|
+
export function* backgroundsOf(feature) {
|
|
26
|
+
for (const child of feature.children) {
|
|
27
|
+
if (child.background !== undefined) {
|
|
28
|
+
yield { background: child.background, rule: undefined };
|
|
29
|
+
}
|
|
30
|
+
if (child.rule !== undefined) {
|
|
31
|
+
for (const grandchild of child.rule.children) {
|
|
32
|
+
if (grandchild.background !== undefined) {
|
|
33
|
+
yield { background: grandchild.background, rule: child.rule };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Every node that holds steps: Backgrounds and Scenarios, Rules included. */
|
|
40
|
+
export function* stepContainersOf(feature) {
|
|
41
|
+
for (const child of feature.children) {
|
|
42
|
+
if (child.background !== undefined) {
|
|
43
|
+
yield { node: child.background, rule: undefined };
|
|
44
|
+
}
|
|
45
|
+
if (child.scenario !== undefined) {
|
|
46
|
+
yield { node: child.scenario, rule: undefined };
|
|
47
|
+
}
|
|
48
|
+
if (child.rule !== undefined) {
|
|
49
|
+
for (const grandchild of child.rule.children) {
|
|
50
|
+
if (grandchild.background !== undefined) {
|
|
51
|
+
yield { node: grandchild.background, rule: child.rule };
|
|
52
|
+
}
|
|
53
|
+
if (grandchild.scenario !== undefined) {
|
|
54
|
+
yield { node: grandchild.scenario, rule: child.rule };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Every node that can carry tags: the Feature, its Rules, every Scenario -
|
|
62
|
+
* inside a Rule or not - and every Examples table.
|
|
63
|
+
*
|
|
64
|
+
* Each one comes with its ancestors, so that a rule comparing a node's tags
|
|
65
|
+
* with the ones it inherits does not have to walk the tree itself.
|
|
66
|
+
*/
|
|
67
|
+
export function* taggedNodesOf(feature) {
|
|
68
|
+
yield { node: feature, ancestors: [] };
|
|
69
|
+
function* scenarios(scenario, ancestors) {
|
|
70
|
+
yield { node: scenario, ancestors };
|
|
71
|
+
for (const examples of scenario.examples) {
|
|
72
|
+
yield { node: examples, ancestors: [scenario, ...ancestors] };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
for (const child of feature.children) {
|
|
76
|
+
if (child.scenario !== undefined) {
|
|
77
|
+
yield* scenarios(child.scenario, [feature]);
|
|
78
|
+
}
|
|
79
|
+
if (child.rule !== undefined) {
|
|
80
|
+
yield { node: child.rule, ancestors: [feature] };
|
|
81
|
+
for (const grandchild of child.rule.children) {
|
|
82
|
+
if (grandchild.scenario !== undefined) {
|
|
83
|
+
yield* scenarios(grandchild.scenario, [child.rule, feature]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=traverse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traverse.js","sourceRoot":"","sources":["../../src/gherkin/traverse.ts"],"names":[],"mappings":"AAgBA,iCAAiC;AACjC,MAAM,SAAS,CAAC,CAAC,OAAO,CAAC,OAAgB;IACvC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,KAAK,CAAC,IAAI,CAAC;QACnB,CAAC;IACH,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,SAAS,CAAC,CAAC,WAAW,CAC1B,OAAgB;IAEhB,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,EAAC,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAC,CAAC;QACpD,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC7C,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;oBACtC,MAAM,EAAC,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAC,CAAC;gBAC1D,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,sDAAsD;AACtD,MAAM,SAAS,CAAC,CAAC,aAAa,CAC5B,OAAgB;IAEhB,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACnC,MAAM,EAAC,UAAU,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,EAAC,CAAC;QACxD,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC7C,IAAI,UAAU,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;oBACxC,MAAM,EAAC,UAAU,EAAE,UAAU,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAC,CAAC;gBAC9D,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,8EAA8E;AAC9E,MAAM,SAAS,CAAC,CAAC,gBAAgB,CAAC,OAAgB;IAChD,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;YACnC,MAAM,EAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,EAAC,CAAC;QAClD,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjC,MAAM,EAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAC,CAAC;QAChD,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC7C,IAAI,UAAU,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;oBACxC,MAAM,EAAC,IAAI,EAAE,UAAU,CAAC,UAAU,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAC,CAAC;gBACxD,CAAC;gBACD,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;oBACtC,MAAM,EAAC,IAAI,EAAE,UAAU,CAAC,QAAQ,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAC,CAAC;gBACtD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAYD;;;;;;GAMG;AACH,MAAM,SAAS,CAAC,CAAC,aAAa,CAAC,OAAgB;IAC7C,MAAM,EAAC,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAC,CAAC;IAErC,QAAQ,CAAC,CAAC,SAAS,CAAC,QAAkB,EAAE,SAAyB;QAC/D,MAAM,EAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAC,CAAC;QAClC,KAAK,MAAM,QAAQ,IAAI,QAAQ,CAAC,QAAQ,EAAE,CAAC;YACzC,MAAM,EAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,QAAQ,EAAE,GAAG,SAAS,CAAC,EAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrC,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;YACjC,KAAK,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,EAAC,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC,OAAO,CAAC,EAAC,CAAC;YAC/C,KAAK,MAAM,UAAU,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;gBAC7C,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;oBACtC,KAAK,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;gBAC/D,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding the `<placeholder>` variables of a Scenario Outline.
|
|
3
|
+
*
|
|
4
|
+
* A Scenario Outline declares its variables as the columns of an Examples
|
|
5
|
+
* table, and uses them in step text, step arguments and its own name. Two
|
|
6
|
+
* rules care about the difference between the two sets, so the gathering is
|
|
7
|
+
* done once here.
|
|
8
|
+
*/
|
|
9
|
+
import type { Scenario } from '@cucumber/messages';
|
|
10
|
+
/** Where a variable was seen: the node holding it. */
|
|
11
|
+
export interface Position {
|
|
12
|
+
line: number;
|
|
13
|
+
column?: number;
|
|
14
|
+
}
|
|
15
|
+
/** Variable name -> every place it appears. */
|
|
16
|
+
export type Sightings = Map<string, Position[]>;
|
|
17
|
+
/** What a Scenario Outline declares, and what it actually uses. */
|
|
18
|
+
export interface ScenarioVariables {
|
|
19
|
+
/** Column names, against the Examples header cells declaring them. */
|
|
20
|
+
declared: Sightings;
|
|
21
|
+
/** Variable names, against every node using them. */
|
|
22
|
+
used: Sightings;
|
|
23
|
+
}
|
|
24
|
+
/** Gathers the variables a Scenario Outline declares and the ones it uses. */
|
|
25
|
+
export declare function variablesOf(scenario: Scenario): ScenarioVariables;
|
|
26
|
+
//# sourceMappingURL=variables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"variables.d.ts","sourceRoot":"","sources":["../../src/gherkin/variables.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,oBAAoB,CAAC;AAMjD,sDAAsD;AACtD,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,+CAA+C;AAC/C,MAAM,MAAM,SAAS,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AAEhD,mEAAmE;AACnE,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,QAAQ,EAAE,SAAS,CAAC;IACpB,qDAAqD;IACrD,IAAI,EAAE,SAAS,CAAC;CACjB;AAwBD,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,QAAQ,EAAE,QAAQ,GAAG,iBAAiB,CA8BjE"}
|