UNPKG

@amiceli/vitest-cucumber

Version:

vitest tools to use Gherkin feature in unit tests

311 lines (310 loc) 11.9 kB
import { MissingExamplesError, MissingFeature, MissingScnearioOutlineError, MissingSteppableError, OnlyOneFeatureError, TwiceBackgroundError, } from '../errors/errors'; import { SpokenParserFactory } from './lang/SpokenParser'; import { Background } from './models/Background'; import { Rule } from './models/Rule'; import { Feature } from './models/feature'; import { Scenario, ScenarioOutline } from './models/scenario'; import { Step } from './models/step'; var FeatureActions; (function (FeatureActions) { FeatureActions["FEATURE"] = "Feature"; FeatureActions["SCENARIO"] = "Scenario"; FeatureActions["BACKGROUND"] = "Background"; FeatureActions["STEP"] = "Step"; FeatureActions["RULE"] = "Rule"; FeatureActions["EXAMPLES"] = "Examples"; })(FeatureActions || (FeatureActions = {})); export class GherkinParser { spokenParser; features = []; currentFeatureIndex = -1; currentScenarioIndex = -1; currentRulenIndex = -1; lastScenarioOutline = null; currentExample = null; currentExampleLine = -1; exampleKeys = []; currentDataTable = []; currentStepDataTableLine = -1; dataTanleKeys = []; lastTags = []; lastSteppableTag = null; currentDocStrings = []; parsingDocStrings = false; previousAction = null; lastStep = null; resetStepDataTable() { this.currentDataTable = []; this.currentStepDataTableLine = -1; this.dataTanleKeys = []; this.lastStep = null; } constructor(options) { this.spokenParser = SpokenParserFactory.fromLang(options.language); } hasFeature(line) { if (!this.currentFeature) { throw new MissingFeature(line); } return true; } hasSteppable(line) { if (!(this.currentBackground || this.currentScenario)) { throw new MissingSteppableError(line); } return true; } hasScenarioOutline(line) { if (!this.lastScenarioOutline) { throw new MissingScnearioOutlineError(line); } return true; } previousActionIs(value) { return this.previousAction === value; } addLine(line) { if (line.trim().startsWith(`#`)) { return; } if (this.previousActionIs(FeatureActions.STEP)) { if (this.lastStep) { this.lastStep.dataTables = this.currentDataTable; } } if (this.parsingDocStrings && !this.isDocStrings(line)) { this.currentDocStrings.push(line.trim()); } else if (this.spokenParser.isFeature(line)) { this.previousAction = FeatureActions.FEATURE; this.resetStepDataTable(); if (this.features.length > 0) { throw new OnlyOneFeatureError(); } this.currentFeatureIndex++; this.currentScenarioIndex = -1; this.currentRulenIndex = -1; this.currentExampleLine = -1; const { title, keyword } = this.spokenParser.getFeatureName(line); const feature = new Feature(title, keyword); this.features.push(feature); this.addTagToParent(feature); } else if (this.spokenParser.isRule(line) && this.hasFeature(line)) { this.previousAction = FeatureActions.RULE; this.resetStepDataTable(); this.currentExampleLine = -1; this.currentScenarioIndex = -1; this.currentRulenIndex++; const { title, keyword } = this.spokenParser.getRuleName(line); const rule = new Rule(title, keyword); this.addTagToParent(rule); this.currentFeature.addRule(rule); } else if (this.spokenParser.isScenarioOutline(line) && this.hasFeature(line)) { this.previousAction = FeatureActions.SCENARIO; this.resetStepDataTable(); this.currentScenarioIndex++; const { title, keyword } = this.spokenParser.getScenarioOutlineName(line); const scenario = new ScenarioOutline(title, keyword); this.lastScenarioOutline = scenario; this.lastSteppableTag = `ScenarioOutline`; this.addScenarioToParent(scenario); this.addTagToParent(scenario); } else if (this.spokenParser.isExamples(line) && this.hasScenarioOutline(line)) { this.previousAction = FeatureActions.EXAMPLES; this.currentExample = []; this.resetStepDataTable(); } else if (line.trim().startsWith(`|`)) { if (this.previousActionIs(FeatureActions.EXAMPLES) && this.currentExample) { this.detectMissingExamplesKeyword(); this.updateScenarioExamples(line); } else if (this.previousActionIs(FeatureActions.STEP)) { this.updateStepDataTable(line); } else { throw new MissingExamplesError(line); } } else if (this.spokenParser.isScenario(line) && this.hasFeature(line)) { this.previousAction = FeatureActions.SCENARIO; this.resetStepDataTable(); this.currentScenarioIndex++; const { title, keyword } = this.spokenParser.getScenarioName(line); const scenario = new Scenario(title, keyword); this.lastSteppableTag = `Scenario`; this.addScenarioToParent(scenario); this.addTagToParent(scenario); } else if (this.spokenParser.isBackground(line) && this.hasFeature(line)) { this.previousAction = FeatureActions.BACKGROUND; this.resetStepDataTable(); if (this.currentBackground) { throw new TwiceBackgroundError(); } const background = new Background(this.spokenParser.getBackgroundKeyWord(line)); this.lastSteppableTag = `Background`; this.addBackgroundToParent(background); this.addTagToParent(background); } else if (line.trim().startsWith(`@`)) { this.lastTags.push(...line .split(` `) .filter((l) => l.startsWith(`@`)) .map((l) => l.replace(`@`, ``))); } else if (this.isDocStrings(line)) { if (this.parsingDocStrings) { this.currentScenario.lastStep.docStrings = this.currentDocStrings.join(`\n`); this.parsingDocStrings = false; this.currentDocStrings.splice(0, this.currentDocStrings.length); } else { this.parsingDocStrings = true; } } else if (!this.parsingDocStrings && this.spokenParser.isStep(line) && this.hasSteppable(line)) { this.previousAction = FeatureActions.STEP; this.resetStepDataTable(); const stepType = this.spokenParser.getStepType(line); const { keyword, title } = this.spokenParser.getStepDetails(line, stepType); const newStep = new Step(stepType, title, keyword); this.lastStep = newStep; this.currentScenario.addStep(newStep); } else if (this.currentExample !== null) { if (this.currentExample.length === 0) { this.currentExample.push(this.getEmptyExamplesValues()); } if (this.lastScenarioOutline) { this.lastScenarioOutline.examples = JSON.parse(JSON.stringify(this.currentExample)); } this.currentExample = null; this.currentExampleLine = -1; } } addBackgroundToParent(background) { if (this.currentRule) { this.currentRule.background = background; } else { this.currentFeature.background = background; } } addScenarioToParent(scenario) { if (this.currentRule) { this.currentRule.addScenario(scenario); } else { this.currentFeature.addScenario(scenario); } } finish() { if (this.lastScenarioOutline && this.currentExample) { if (this.currentExample.length === 0) { this.currentExample.push(this.getEmptyExamplesValues()); } this.lastScenarioOutline.examples = JSON.parse(JSON.stringify(this.currentExample)); } this.currentFeature?.mustHaveScenarioOrRules(); return this.features; } getVariablesFromLine(line) { const exampleVariables = line .trim() .split(`|`) .filter((n) => n.length > 0) .map((n) => n.trim()); return exampleVariables; } // biome-ignore lint/suspicious/noExplicitAny: <explanation> getObjectWithValuesFromLine(line, keys) { const exampleVariables = this.getVariablesFromLine(line); // biome-ignore lint/suspicious/noExplicitAny: <explanation> const res = exampleVariables.reduce((acc, cur, index) => { // biome-ignore lint/suspicious/noAssignInExpressions: <explanation> // biome-ignore lint/style/noCommaOperator: <explanation> return (acc[keys[index]] = cur), acc; }, {}); return res; } addTagToParent(parent) { if (this.lastTags.length > 0) { for (const tag of this.lastTags) { parent.tags.add(tag); } this.lastTags = []; } } getEmptyExamplesValues() { // biome-ignore lint/suspicious/noExplicitAny: <explanation> const res = this.exampleKeys.reduce((acc, cur, index) => { // biome-ignore lint/suspicious/noAssignInExpressions: <explanation> // biome-ignore lint/style/noCommaOperator: <explanation> return (acc[this.exampleKeys[index]] = null), acc; }, {}); return res; } detectMissingExamplesKeyword() { if (this.currentExample === null && this.lastScenarioOutline) { this.lastScenarioOutline.missingExamplesKeyword = true; } } updateStepDataTable(line) { if (this.currentDataTable) { this.currentStepDataTableLine++; if (this.currentStepDataTableLine === 0) { this.dataTanleKeys = this.getVariablesFromLine(line); } else { this.currentDataTable.push(this.getObjectWithValuesFromLine(line, this.dataTanleKeys)); } } } updateScenarioExamples(line) { if (this.currentExample) { this.currentExampleLine++; if (this.currentExampleLine === 0) { this.exampleKeys = this.getVariablesFromLine(line); } else { this.currentExample.push(this.getObjectWithValuesFromLine(line, this.exampleKeys)); } } } isDocStrings(line) { return line.trim().startsWith(`"""`) || line.trim().startsWith(`\`\`\``); } get currentBackground() { if (this.currentRule) { return this.currentRule.background; } return this.currentFeature.background; } get currentRule() { return this.currentFeature.rules[this.currentRulenIndex]; } get currentFeature() { return this.features[this.currentFeatureIndex]; } get currentScenario() { if (this.lastSteppableTag === `Background` && this.currentBackground) { return this.currentBackground; } if (this.currentRule) { return this.currentRule.scenarii[this.currentScenarioIndex]; } return this.currentFeature.scenarii[this.currentScenarioIndex]; } }