// Valide des fichiers .feature avec l'analyseur Gherkin officiel. // Usage : node validate.mjs fichier.feature… // Sort en erreur si un fichier n'est pas du Gherkin valide, n'est pas en // français (# language: fr), ne contient aucun scénario, ou a une étape égarée // dans une description (signe d'un mot-clé mal orthographié, que Gherkin // accepte alors comme texte libre). import { readFileSync } from 'node:fs' import { AstBuilder, GherkinClassicTokenMatcher, Parser } from '@cucumber/gherkin' import { IdGenerator } from '@cucumber/messages' const STEP = /^\s*(Étant donné|Etant donné|Quand|Alors|Et|Mais)\b/ function strayStep(description) { return description.split('\n').find((line) => STEP.test(line)) } let errors = 0 for (const file of process.argv.slice(2)) { const parser = new Parser(new AstBuilder(IdGenerator.uuid()), new GherkinClassicTokenMatcher()) try { const { feature } = parser.parse(readFileSync(file, 'utf8')) if (!feature) { throw new Error('aucune fonctionnalité') } if (feature.language !== 'fr') { throw new Error(`langue « ${feature.language} » au lieu de « fr »`) } const scenarios = feature.children.filter((child) => child.scenario).map((child) => child.scenario) if (scenarios.length === 0) { throw new Error('aucun scénario') } for (const description of [feature.description, ...scenarios.map((scenario) => scenario.description)]) { const line = strayStep(description) if (line) { throw new Error(`étape hors d'un scénario (mot-clé mal orthographié ?) : ${line.trim()}`) } } } catch (error) { console.log(`${file} : ${error.message}`) errors++ } } process.exit(errors === 0 ? 0 : 1)