@amiceli/vitest-cucumber
Version:
vitest tools to use Gherkin feature in unit tests
2,035 lines (2,013 loc) • 125 kB
JavaScript
// src/vitest/configuration.ts
function getDefaultConfiguration() {
return {
language: "en",
includeTags: [],
excludeTags: ["ignore"],
onStepError: ({ error }) => {
}
};
}
var globalConfiguration = {};
var getVitestCucumberConfiguration = (options) => {
const defaultConfiguration = getDefaultConfiguration();
if (typeof window !== "undefined") {
defaultConfiguration.includeTags?.push(
...import.meta.env.VITEST_INCLUDE_TAGS?.split(" ") || []
);
defaultConfiguration.excludeTags?.push(
...import.meta.env.VITEST_EXCLUDE_TAGS?.split(" ") || []
);
} else {
defaultConfiguration.includeTags?.push(
...process.env.VITEST_INCLUDE_TAGS?.split(" ") || []
);
defaultConfiguration.excludeTags?.push(
...process.env.VITEST_EXCLUDE_TAGS?.split(" ") || []
);
}
const mergedOptions = {
...defaultConfiguration,
...globalConfiguration,
...options || {}
};
return mergedOptions;
};
var setVitestCucumberConfiguration = (options) => {
globalConfiguration = options;
};
// src/vitest/describe-feature.ts
import { afterAll as afterAll3, beforeAll as beforeAll3 } from "vitest";
// src/vitest/export-vitest.ts
var describeFn;
var testFn;
var gThis = globalThis;
if (typeof gThis.describe !== "undefined" && typeof globalThis.test !== "undefined") {
describeFn = gThis.describe;
testFn = gThis.test;
} else {
const vitest = await import("vitest");
describeFn = vitest.describe;
testFn = vitest.test;
}
// src/vitest/describe/describeBackground.ts
import { onTestFailed, test } from "vitest";
// src/errors/errors.ts
var VitestsCucumberError = class extends Error {
constructor(message, name) {
super(message);
this.stack = ``;
this.name = name || this.constructor.name;
}
};
var NotScenarioOutlineError = class extends VitestsCucumberError {
constructor(scenario) {
super(`${scenario.getTitle()} is not a ScenarioOutline`);
}
};
var IsScenarioOutlineError = class extends VitestsCucumberError {
constructor(scenario) {
super(`${scenario.getTitle()} is a ScenarioOutline`);
}
};
var BackgroundNotCalledError = class extends VitestsCucumberError {
constructor(background) {
super(`${background.getTitle()} was not called`);
}
};
var ScenarioNotCalledError = class extends VitestsCucumberError {
constructor(scenario) {
super(`${scenario.getTitle()} was not called`);
}
};
var ScenarioOutlineVariableNotCalledInStepsError = class extends VitestsCucumberError {
constructor(scenario, variableName) {
super(
`${scenario.getTitle()}
${variableName} was not called in steps`
);
}
};
var ScenarioOulineWithoutExamplesError = class extends VitestsCucumberError {
constructor(scenario) {
super(`${scenario.getTitle()}
has an empty Examples`);
}
};
var ScenarioOutlineVariablesDeclaredWithoutExamplesError = class extends VitestsCucumberError {
constructor(scenario) {
super(`${scenario.getTitle()}
variables declared without Examples`);
}
};
var MissingScenarioOutlineVariableValueError = class extends VitestsCucumberError {
constructor(scenario, variableName) {
super(
`${scenario.getTitle()}
missing ${variableName} value in Examples`
);
}
};
var FeatureUknowScenarioError = class extends VitestsCucumberError {
constructor(feature, scenario) {
super(
`${scenario.getTitle()} does not exist in
${feature.getTitle()}`
);
}
};
var StepAbleUnknowStepError = class extends VitestsCucumberError {
constructor(stepable, step) {
super(
`${stepable.getTitle()}
${step.type} ${step.details} does not exist`
);
}
};
var StepAbleStepExpressionError = class extends VitestsCucumberError {
constructor(stepable, step) {
super(
[
`No step match with this expression`,
` ${stepable.getTitle()}`,
` ${step.getTitle()} \u274C`
].join(`
`)
);
}
};
var StepAbleStepsNotCalledError = class extends VitestsCucumberError {
constructor(stepable, step) {
super(
[
``,
` ${stepable.getTitle()}`,
` ${step.getTitle()} \u274C`
].join(`
`),
`Missing steps in Scenario`
);
}
};
var RuleNotCalledError = class extends VitestsCucumberError {
constructor(rule) {
super(`${rule.getTitle()} was not called`);
}
};
var FeatureUknowRuleError = class extends VitestsCucumberError {
constructor(feature, rule) {
super(
`${rule.getTitle()} does not exist in
Feature: ${feature.name}`
);
}
};
var FeatureFileNotFoundError = class extends VitestsCucumberError {
constructor(path) {
super(`feature file ${path} does not exist`);
}
};
var NotAllowedBackgroundStepTypeError = class extends VitestsCucumberError {
constructor(type) {
super(`${type} step is not allowed in Background`);
}
};
var TwiceBackgroundError = class extends VitestsCucumberError {
constructor() {
super(`A background already exist`);
}
};
var BackgroundNotExistsError = class extends VitestsCucumberError {
constructor(parent) {
super(`${parent.getTitle()} has no background`);
}
};
var OnlyOneFeatureError = class extends VitestsCucumberError {
constructor() {
super(`Gherkin rule: only one Feature per file`);
}
};
var StepExpressionMatchError = class extends VitestsCucumberError {
constructor(step, expression) {
super(`${expression} no match with ${step.details}`);
}
};
var MissingFeature = class extends VitestsCucumberError {
constructor(line) {
super(
[
`Missing Feature before add Scenario, Rule or Background`,
` ${line.trim()} \u274C`
].join("\n")
);
}
};
var MissingSteppableError = class extends VitestsCucumberError {
constructor(line) {
super(
[
`Missing Scenario, ScenarioOutline or Background before add step`,
` ${line.trim()} \u274C`
].join("\n")
);
}
};
var MissingScnearioOutlineError = class extends VitestsCucumberError {
constructor(line) {
super(
[
`Missing ScenarioOutline before add Examples`,
` ${line.trim()} \u274C`
].join("\n")
);
}
};
var MissingExamplesError = class extends VitestsCucumberError {
constructor(line) {
super(
[`Missing Examples before add value`, ` ${line.trim()} \u274C`].join(
"\n"
)
);
}
};
var SpokenKeywordError = class extends VitestsCucumberError {
constructor(line, keywords) {
super(
[
`No keywords match for: ${line}`,
` Available keywords : ${keywords.join(", ")}`
].join("\n")
);
}
};
var ParentWithoutScenario = class extends VitestsCucumberError {
constructor(feature) {
super(`${feature.getTitle()} must have at least one scenario`);
}
};
var InvalidUrlParameterError = class extends VitestsCucumberError {
constructor(arg) {
super(`String '${arg}' was not recognized as a valid Url`);
}
};
var InvalidDateParameterError = class extends VitestsCucumberError {
constructor(arg) {
super(`String '${arg}' was not recognized as a valid Date`);
}
};
var InvalidCurrencyParameterError = class extends VitestsCucumberError {
constructor(arg) {
super(`String '${arg}' was not recognized as a valid currency`);
}
};
var BuiltinParameterExpressionAlreadyExistsError = class extends VitestsCucumberError {
constructor(expressionName) {
super(`You cannot redefine the built-in expression '${expressionName}'`);
}
};
var CustomParameterExpressionAlreadyExistsError = class extends VitestsCucumberError {
constructor(expressionName) {
super(`The custom expression '${expressionName}' already exists`);
}
};
var ItemAlreadyExistsError = class extends VitestsCucumberError {
constructor(parent, child) {
super(`${parent.getTitle()} already has ${child.getTitle()}`);
}
};
var RequiredTitleError = class extends VitestsCucumberError {
constructor(line, keyword) {
super([`${line} \u274C`, ` ${keyword} required a title`].join("\n"));
}
};
// src/parser/expression/regexes.ts
import parsecurrency from "parsecurrency";
// src/parser/expression/symbolToCode.json
var symbolToCode_default = {
$: "USD",
$CAD: "CAD",
$U: "UYU",
A$: "AUD",
AED: "AED",
ALL: "ALL",
AMD: "AMD",
AR$: "ARS",
AU$: "AUD",
Af: "AFN",
"B/.": "PAB",
BD: "BHD",
BGN: "BGN",
BN$: "BND",
BWP: "BWP",
BZ$: "BZD",
Bs: "BOB",
"Bs.F.": "VEF",
C$: "NIO",
CA$: "CAD",
CDF: "CDF",
CF: "KMF",
CFA: "XOF",
CFPF: "XPF",
CHF: "CHF",
CL$: "CLP",
"CN\xA5": "CNY",
CO$: "COP",
CV$: "CVE",
DA: "DZD",
DT: "TND",
Dkr: "DKK",
EGP: "EGP",
Ekr: "EEK",
FBu: "BIF",
FCFA: "XAF",
FG: "GNF",
FJ$: "FJD",
Fdj: "DJF",
Ft: "HUF",
GEL: "GEL",
"GH\u20B5": "GHS",
GTQ: "GTQ",
HK$: "HKD",
HNL: "HNL",
IQD: "IQD",
IRR: "IRR",
Ikr: "ISK",
J$: "JMD",
JD: "JOD",
"JP\xA5": "JPY",
KD: "KWD",
KHR: "KHR",
KM: "BAM",
KZT: "KZT",
Ksh: "KES",
K\u010D: "CZK",
L: "SZL",
"LB\xA3": "LBP",
LD: "LYD",
Ls: "LVL",
Lt: "LTL",
MAD: "MAD",
MDL: "MDL",
MGA: "MGA",
MKD: "MKD",
MMK: "MMK",
MOP$: "MOP",
MTn: "MZN",
MURs: "MUR",
MX$: "MXN",
N$: "NAD",
NPRs: "NPR",
NT$: "TWD",
NZ$: "NZD",
Nfk: "ERN",
Nkr: "NOK",
OMR: "OMR",
PKRs: "PKR",
QR: "QAR",
R: "ZAR",
R$: "BRL",
RD$: "DOP",
RM: "MYR",
RON: "RON",
RUB: "RUB",
RWF: "RWF",
Rp: "IDR",
Rs: "INR",
S$: "SGD",
"S/.": "PEN",
SDG: "SDG",
SLRs: "LKR",
SR: "SAR",
"SY\xA3": "SYP",
Skr: "SEK",
Ssh: "SOS",
T$: "TOP",
TL: "TRY",
TSh: "TZS",
TT$: "TTD",
Tk: "BDT",
US$: "USD",
USh: "UGX",
UZS: "UZS",
VT: "VUV",
YR: "YER",
ZK: "ZMW",
ZWL$: "ZWL",
"din.": "RSD",
kn: "HRK",
"man.": "AZN",
z\u0142: "PLN",
"\xA3": "GBP",
"\xA5": "JPY",
"\u0E3F": "THB",
"\u20A1": "CRC",
"\u20A3": "XPF",
"\u20A6": "NGN",
"\u20A9": "KRW",
"\u20AA": "ILS",
"\u20AB": "VND",
"\u20AC": "EUR",
"\u20B1": "PHP",
"\u20B2": "PYG",
"\u20B4": "UAH",
"\u20B9": "INR"
};
// src/parser/expression/regexes.ts
var ExpressionRegex = class {
keyword;
keywordRegex;
groupName;
constructor(options) {
this.keyword = options.keyword;
this.keywordRegex = options.keywordRegex;
this.groupName = options.groupName;
}
matchGroupName(str) {
return str.startsWith(this.groupName);
}
get cloneKeywordRegex() {
return new RegExp(this.keywordRegex.source, this.keywordRegex.flags);
}
resetExpressionStates() {
}
};
var BooleanRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{boolean}`,
groupName: `boolean`,
keywordRegex: /{boolean}/g
});
}
getRegex(index) {
return `\\b(?<boolean${index}>(true|false))\\b`;
}
getValue(str) {
return str === "true";
}
};
var WordRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{word}`,
groupName: `word`,
keywordRegex: /{word}/g
});
}
getRegex(index) {
return `\\b(?<word${index}>\\w+)\\b`;
}
getValue(str) {
return str;
}
};
var CharRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{char}`,
groupName: `char`,
keywordRegex: /{char}/g
});
}
getRegex(index) {
return `(?<char${index}>\\w)`;
}
getValue(str) {
return str;
}
};
var StringRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{string}`,
groupName: `string`,
keywordRegex: /{string}/g
});
}
getRegex(index) {
return `(?<string${index}>"[^"]*"|'[^']*')`;
}
getValue(str) {
return str.replace(/^["']|["']$/g, ``);
}
};
var EmailRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{email}`,
groupName: `email`,
keywordRegex: /{email}/g
});
}
getRegex(index) {
const emailRegex = `[^\\s@]+@[^\\s@]+\\.[^\\s@]+`;
return `\\b(?<email${index}>${emailRegex})\\b`;
}
getValue(str) {
return str.replace(/^["']|["']$/g, ``);
}
};
var UrlRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{url}`,
groupName: `url`,
keywordRegex: /{url}/g
});
}
getRegex(index) {
const urlRegex = `(https?://)?([^\\s$.?#].[^\\s]*)`;
return `\\b(?<url${index}>${urlRegex})\\b`;
}
getValue(str) {
try {
return new URL(str);
} catch {
throw new InvalidUrlParameterError(str);
}
}
};
var IntRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{int}`,
groupName: `int`,
keywordRegex: /{int}/g
});
}
getRegex(index) {
return `(?<int${index}>-?\\d+)`;
}
getValue(str) {
return Number(str);
}
};
var NumberRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{number}`,
groupName: `number`,
keywordRegex: /{number}/g
});
}
getRegex(index) {
return `(?<number${index}>-?\\d+(\\.\\d+)?)`;
}
getValue(str) {
return Number(str);
}
};
var DateRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{date}`,
groupName: `date`,
keywordRegex: /{date}/g
});
}
getRegex(index) {
const dateRegex = `[0-9]{1,2}/[0-9]{1,2}/[0-9]{4}(?: [0-9]{2}:[0-9]{2}:[0-9]{2})?`;
const isoDateRegex = `(?:\\d{4})-(?:\\d{2})-(?:\\d{2})`;
const isoDatetimeRegex = `(?:\\d{4})-(?:\\d{2})-(?:\\d{2})T(?:\\d{2}):(?:\\d{2}):(?:\\d{2}(?:\\.\\d*)?)(?:(?:[-|+](?:\\d{2}):(?:\\d{2})|Z)?)`;
const shortMonths = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
];
const longMonths = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
];
const shortOrLongMonthsRegex = shortMonths.concat(longMonths).join("|");
const shortOrLongDateRegex = `\\d{1,2},? (:?${shortOrLongMonthsRegex}),? \\d{4}`;
const altShortOrLongDateRegex = `(:?${shortOrLongMonthsRegex}),? \\d{1,2},? \\d{4}`;
return `\\b(?<date${index}>(${dateRegex})|(${isoDateRegex})|(${isoDatetimeRegex})|(${shortOrLongDateRegex})|(${altShortOrLongDateRegex}))\\b`;
}
getValue(str) {
const value = new Date(str);
if (Number.isNaN(value.getTime())) {
throw new InvalidDateParameterError(str);
}
return value;
}
};
var CurrencyRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{currency}`,
groupName: `currency`,
keywordRegex: /{currency}/g
});
}
getRegex(index) {
const currencyRegex = `(?:([-+]{1}) ?)?(?:([A-Z]{3}) ?)?(?:([^\\d ]+?) ?)?(((?:\\d{1,3}([,. \u2019'\\u00A0\\u202F]))*?\\d{1,})(([,.])\\d{1,2})?)(?: ?([^\\d]+?))??(?: ?([A-Z]{3}))?`;
return `(?<!\\S)(?<currency${index}>(${currencyRegex}))(?!\\S)`;
}
getValue(str) {
const value = parsecurrency(str);
if (!value) {
throw new InvalidCurrencyParameterError(str);
}
return {
raw: value.raw,
value: value.value,
currency: value.currency || symbolToCode_default[value.symbol]
};
}
};
var ListRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{list}`,
groupName: `list`,
keywordRegex: /{list(?::(["'])([^"']?)\1)?\}/g
});
}
regexMatchSeparators = [];
resetExpressionStates() {
this.regexMatchSeparators = [];
}
getRegex(index, originalRegex) {
const separator = this.cloneKeywordRegex.exec(originalRegex);
const s = separator?.at(2) || ",";
this.regexMatchSeparators.push(s);
return `(?<list${index}>[a-zA-Z]+(?:${s} ?[a-zA-Z]+)*)`;
}
getValue(str, index) {
return str.split(this.regexMatchSeparators[index]).map((t) => t.trim());
}
};
var AnyRegex = class extends ExpressionRegex {
constructor() {
super({
keyword: `{any}`,
groupName: `any`,
keywordRegex: /{any}/g
});
}
getRegex(index) {
return `(?<any${index}>.+)`;
}
getValue(str) {
return str;
}
};
// src/parser/expression/custom.ts
var CustomExpressionRegex = class extends ExpressionRegex {
constructor(args) {
super({
keyword: `{${args.name}}`,
groupName: args.name,
keywordRegex: new RegExp(`{${args.name}}`, `g`)
});
this.args = args;
}
getRegex(index) {
return `(?<${this.groupName}${index}>${this.args.regexp.source})`;
}
getValue(str) {
return this.args.transformer(str);
}
};
var customExpressionRegEx = [];
var defineParameterExpression = (args) => {
if (customExpressionRegEx.some((r) => r.groupName === args.name)) {
throw new CustomParameterExpressionAlreadyExistsError(args.name);
}
if (builtInExpressionRegEx.some((r) => r.groupName === args.name)) {
throw new BuiltinParameterExpressionAlreadyExistsError(args.name);
}
customExpressionRegEx.push(new CustomExpressionRegex(args));
};
// src/parser/expression/ExpressionStep.ts
var builtInExpressionRegEx = [
new BooleanRegex(),
new WordRegex(),
new CharRegex(),
new StringRegex(),
new EmailRegex(),
new UrlRegex(),
new IntRegex(),
new NumberRegex(),
new DateRegex(),
new CurrencyRegex(),
new ListRegex(),
new AnyRegex()
];
var ExpressionStep = class {
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
static matchStep(step, stepExpression) {
const allExpressionRegEx = builtInExpressionRegEx.concat(
customExpressionRegEx
);
let regexString = stepExpression;
const groupCount = {};
regexString = regexString.replace(/[?]/g, `\\$&`);
for (const r of allExpressionRegEx) {
r.resetExpressionStates();
groupCount[r.groupName] = 0;
}
for (const r of allExpressionRegEx) {
regexString = regexString.replace(
r.keywordRegex,
(originalRegex) => {
groupCount[r.groupName] += 1;
return r.getRegex(groupCount[r.groupName], originalRegex);
}
);
}
regexString = `^${regexString}$`;
const regex = new RegExp(regexString, `g`);
const matches = [...step.details.matchAll(regex)];
const result = matches.map((match) => {
const res = [];
if (!match.groups) {
return;
}
Object.keys(match.groups).forEach((key, index) => {
const matchRegex = allExpressionRegEx.find(
(r) => r.matchGroupName(key)
);
if (matchRegex) {
res.push({
index,
// biome-ignore lint/style/noNonNullAssertion: <explanation>
value: matchRegex.getValue(match.groups[key], index)
});
} else {
res.push({
index,
// biome-ignore lint/style/noNonNullAssertion: <explanation>
value: new StringRegex().getValue(match.groups[key])
});
}
});
return res;
});
const allValues = result.flat().filter((t) => t !== void 0).map((r) => r.value);
const hasRegex = allExpressionRegEx.some((r) => {
return stepExpression.includes(r.keyword);
});
if (hasRegex && allValues.length === 0) {
throw new StepExpressionMatchError(step, stepExpression);
}
return allValues;
}
static stepContainsRegex(expression) {
const allExpressionRegEx = builtInExpressionRegEx.concat(
customExpressionRegEx
);
return allExpressionRegEx.some((r) => {
return expression.includes(r.keyword);
});
}
};
// src/vitest/describe/describeBackground.ts
function createBackgroundDescribeHandler({
background,
backgroundCallback
}) {
const backgroundStepsToRun = [];
const config = getVitestCucumberConfiguration();
const createScenarioStepCallback = (stepType) => {
return (stepDetails, scenarioStepCallback) => {
const foundStep = background.checkIfStepExists(
stepType,
stepDetails
);
const params = ExpressionStep.matchStep(
foundStep,
stepDetails
);
foundStep.isCalled = true;
backgroundStepsToRun.push({
key: foundStep.getTitle(),
fn: scenarioStepCallback,
step: foundStep,
params: [
...params,
foundStep.dataTables.length > 0 ? foundStep.dataTables : null,
foundStep.docStrings
].filter((p) => p !== null)
});
};
};
const scenarioStepsCallback = {
Given: createScenarioStepCallback(`Given`),
And: createScenarioStepCallback(`And`)
};
backgroundCallback(scenarioStepsCallback);
background.checkIfStepWasCalled();
return function backgroundDescribe() {
test.for(
backgroundStepsToRun.map((s) => {
return [s.key, s];
})
)(`%s`, async ([, scenarioStep], ctx) => {
onTestFailed((e) => {
const message = e.errors?.at(0)?.message;
config.onStepError({
error: new Error(
message || `${scenarioStep.step.details} failed`
),
ctx,
step: scenarioStep.step
});
});
await scenarioStep.fn(ctx, ...scenarioStep.params);
});
};
}
// src/vitest/describe/describeScenario.ts
import { afterAll, beforeAll, onTestFailed as onTestFailed2, test as test2 } from "vitest";
function createScenarioDescribeHandler({
scenario,
scenarioTestCallback,
afterEachScenarioHook,
beforeEachScenarioHook
}) {
const scenarioStepsToRun = [];
const config = getVitestCucumberConfiguration();
const createScenarioStepCallback = (stepType) => {
return (stepDetails, scenarioStepCallback) => {
const foundStep = scenario.checkIfStepExists(stepType, stepDetails);
const params = ExpressionStep.matchStep(
foundStep,
stepDetails
);
foundStep.isCalled = true;
scenarioStepsToRun.push({
key: foundStep.getTitle(),
fn: scenarioStepCallback,
step: foundStep,
params: [
...params,
foundStep.dataTables.length > 0 ? foundStep.dataTables : null,
foundStep.docStrings
].filter((p) => p !== null)
});
};
};
const scenarioStepsCallback = {
Given: createScenarioStepCallback(`Given`),
When: createScenarioStepCallback(`When`),
And: createScenarioStepCallback(`And`),
Then: createScenarioStepCallback(`Then`),
But: createScenarioStepCallback(`But`)
};
scenarioTestCallback(scenarioStepsCallback);
scenario.checkIfStepWasCalled();
return function scenarioDescribe() {
beforeAll(async () => {
await beforeEachScenarioHook();
});
afterAll(async () => {
await afterEachScenarioHook();
});
test2.for(
scenarioStepsToRun.map((s) => {
return [s.key, s];
})
)(`%s`, async ([, scenarioStep], ctx) => {
onTestFailed2((e) => {
const message = e.errors?.at(0)?.message;
config.onStepError({
error: new Error(
message || `${scenarioStep.step.details} failed`
),
ctx,
step: scenarioStep.step
});
});
await scenarioStep.fn(ctx, ...scenarioStep.params);
});
};
}
// src/vitest/describe/describeScenarioOutline.ts
import { afterAll as afterAll2, beforeAll as beforeAll2, onTestFailed as onTestFailed3, test as test3 } from "vitest";
function createScenarioOutlineDescribeHandler({
scenario,
scenarioTestCallback,
afterEachScenarioHook,
beforeEachScenarioHook
}) {
let scenarioStepsToRun = [];
const config = getVitestCucumberConfiguration();
const createScenarioStepCallback = (stepType) => {
return (stepDetails, scenarioStepCallback) => {
const foundStep = scenario.checkIfStepExists(stepType, stepDetails);
const params = ExpressionStep.matchStep(
foundStep,
stepDetails
);
foundStep.isCalled = true;
scenarioStepsToRun.push({
key: foundStep.getTitle(),
fn: scenarioStepCallback,
step: foundStep,
params: [
...params,
foundStep.dataTables.length > 0 ? foundStep.dataTables : null,
foundStep.docStrings
].filter((p) => p !== null)
});
};
};
const scenarioStepsCallback = {
Given: createScenarioStepCallback(`Given`),
When: createScenarioStepCallback(`When`),
And: createScenarioStepCallback(`And`),
Then: createScenarioStepCallback(`Then`),
But: createScenarioStepCallback(`But`)
};
const example = scenario.examples;
if (example) {
return example?.map((exampleVariables) => {
scenarioStepsToRun = [];
scenarioTestCallback(scenarioStepsCallback, exampleVariables);
scenario.checkIfStepWasCalled();
return /* @__PURE__ */ ((steps) => function scenarioOutlineDescribe() {
beforeAll2(async () => {
await beforeEachScenarioHook();
});
afterAll2(async () => {
await afterEachScenarioHook();
});
test3.for(
steps.map((s) => {
return [
scenario.getStepTitle(s.step, exampleVariables),
s
];
})
)(`%s`, async ([, scenarioStep], ctx) => {
if (scenarioStep.step.docStrings) {
scenarioStep.params[scenarioStep.params.length - 1] = scenario.getStepDocStrings(
scenarioStep.step,
exampleVariables
);
}
onTestFailed3((e) => {
const message = e.errors?.at(0)?.message;
config.onStepError({
error: new Error(
message || `${scenarioStep.step.details} failed`
),
ctx,
step: scenarioStep.step
});
});
await scenarioStep.fn(ctx, ...scenarioStep.params);
});
})([...scenarioStepsToRun]);
});
}
return [];
}
// src/parser/models/Taggable.ts
var matchFilter = (filterItem, tags) => {
if (Array.isArray(filterItem)) {
return filterItem.every((item) => tags.has(item));
}
return tags.has(filterItem);
};
var Taggable = class {
tags = /* @__PURE__ */ new Set();
/**
* Simple matching filter mostly following the cucumber expression tag rules,
* e.g. `[["alpha", "beta"], "vitests", "another"]`
* will be equivalent to:
* (`@alpha` and `@beta`) or `@vitests` or `@another`
*/
matchTags(filterItems) {
return filterItems.some(
(filterItem) => matchFilter(filterItem, this.tags)
);
}
shouldBeCalled(options) {
return (options.includeTags.length <= 0 || this.matchTags(options.includeTags) === true) && this.matchTags(options.excludeTags) === false;
}
};
// src/parser/models/step.ts
var Step = class {
type;
details;
docStrings = null;
isCalled;
dataTables = [];
title;
constructor(type, details, title) {
this.details = details;
this.type = type;
this.isCalled = false;
this.title = title || `${type}`;
}
getTitle() {
return `${this.title} ${this.details}`;
}
setDocStrings(docStrings) {
this.docStrings = docStrings;
}
};
// src/parser/models/Stepable.ts
var StepAble = class extends Taggable {
isCalled;
_steps;
title;
constructor(title) {
super();
this.title = title;
this.isCalled = false;
this._steps = [];
}
stepFailedExpressionMatch = {};
findStepByTypeAndDetails(type, details) {
this.stepFailedExpressionMatch[details] = 0;
return this._steps.find((step) => {
try {
const sameType = step.type === type;
const sameDetails = step.details === details;
if (ExpressionStep.stepContainsRegex(details)) {
const params = ExpressionStep.matchStep(step, details);
return sameType && (sameDetails || params.length >= 0);
}
return sameType && sameDetails;
} catch (e) {
this.stepFailedExpressionMatch[details] += 1;
return false;
}
});
}
hasUnCalledSteps() {
return this.getNoCalledStep() !== void 0;
}
getNoCalledStep() {
return this._steps.find((s) => s.isCalled === false);
}
addStep(newStep) {
const duplicatedStep = this._steps.find((step) => {
return step.getTitle() === newStep.getTitle();
});
if (duplicatedStep) {
throw new ItemAlreadyExistsError(this, newStep);
}
this._steps.push(newStep);
}
checkIfStepWasCalled() {
const step = this.getNoCalledStep();
if (step) {
throw new StepAbleStepsNotCalledError(this, step);
}
}
checkIfStepExists(stepType, stepDetails) {
const foundStep = this.findStepByTypeAndDetails(stepType, stepDetails);
if (!foundStep) {
if (this.stepFailedExpressionMatch[stepDetails] === this._steps.length) {
throw new StepAbleStepExpressionError(
this,
new Step(stepType, stepDetails)
);
}
throw new StepAbleUnknowStepError(
this,
new Step(stepType, stepDetails)
);
}
return foundStep;
}
get lastStep() {
return this._steps[this._steps.length - 1];
}
get steps() {
return this._steps;
}
};
// src/parser/models/scenario.ts
var Scenario = class extends StepAble {
description;
constructor(description, title = "Scenario") {
super(title);
this.description = description;
}
getTitle() {
return `${this.title}: ${this.description}`;
}
};
var ScenarioOutline = class extends Scenario {
examples = [];
missingExamplesKeyword = false;
constructor(description, title = "Scenario Outline") {
super(description, title);
}
getStepTitle(step, example) {
let stepTitle = step.getTitle();
const exampleKeys = Object.keys(example);
for (const key of exampleKeys) {
stepTitle = stepTitle.replace(`<${key}>`, example[key]);
}
return stepTitle;
}
getStepDocStrings(step, example) {
if (step.docStrings) {
let docStrings = `${step.docStrings}`;
for (const key in example) {
docStrings = docStrings.replace(`<${key}>`, example[key]);
}
return docStrings;
}
return null;
}
};
// src/vitest/state-detectors/ScenarioStateDetector.ts
var ScenarioStateDetector = class _ScenarioStateDetector {
scenario;
constructor(scenario) {
this.scenario = scenario;
}
static forScenario(scenario) {
return new _ScenarioStateDetector(scenario);
}
checkIfScenarioHasNoExample() {
const { examples } = this.scenario;
if (examples.length === 0) {
throw new ScenarioOulineWithoutExamplesError(
this.scenario
);
}
}
detectMissingVariableInSteps() {
const { examples } = this.scenario;
const examplesKeys = Object.keys(examples[0]);
const missingVariable = examplesKeys.find((v) => {
return this.scenario.steps.map((s) => s.details).join(``).includes(`<${v}>`) === false;
});
if (missingVariable) {
throw new ScenarioOutlineVariableNotCalledInStepsError(
this.scenario,
missingVariable
);
}
}
detectMissingVariableValue() {
const { examples } = this.scenario;
const examplesKeys = Object.keys(examples[0]);
const missingVariable = examplesKeys.find((v) => {
return examples.filter((values) => {
return values[v] === void 0 || values[v] === null;
}).length > 0;
});
if (missingVariable) {
throw new MissingScenarioOutlineVariableValueError(
this.scenario,
missingVariable
);
}
}
checkExemples() {
if (this.scenario instanceof ScenarioOutline) {
if (this.scenario.missingExamplesKeyword) {
throw new ScenarioOutlineVariablesDeclaredWithoutExamplesError(
this.scenario
);
}
this.checkIfScenarioHasNoExample();
this.detectMissingVariableInSteps();
this.detectMissingVariableValue();
}
}
};
// src/vitest/describe-feature.ts
var extractTagFilters = (filterItems) => {
return filterItems.map((filterItem) => {
if (Array.isArray(filterItem)) {
return extractTagFilters(filterItem);
}
if (filterItem.startsWith("@")) {
return filterItem.replace("@", "");
}
return filterItem;
});
};
function defineRuleScenarioToRun(options) {
const describeToRun = options.describes.filter((d) => !d.skipped);
const describeToSkip = options.describes.filter((d) => d.skipped);
const finalDescribesToRun = [];
for (const toRun of describeToRun) {
if (options.featureBackground && !options.featureBackground.skipped) {
finalDescribesToRun.push(options.featureBackground);
}
if (options.ruleBackground && !options.ruleBackground.skipped) {
finalDescribesToRun.push(options.ruleBackground);
}
finalDescribesToRun.push(toRun);
}
return {
describeToRun: finalDescribesToRun,
describeToSkip
};
}
function defineScenarioToRun(options) {
const describeToRun = options.describes.filter((d) => !d.skipped);
const describeToSkip = options.describes.filter((d) => d.skipped);
const finalDescribesToRun = [];
for (const toRun of describeToRun) {
if (options.featureBackground && !options.featureBackground.skipped) {
finalDescribesToRun.push(options.featureBackground);
}
finalDescribesToRun.push(toRun);
}
describeToSkip.push(...options.describeRules.filter((s) => s.skipped));
finalDescribesToRun.push(...options.describeRules.filter((s) => !s.skipped));
return {
describeToRun: finalDescribesToRun,
describeToSkip
};
}
function describeFeature(feature, describeFeatureCallback, describeFeatureOptions) {
let beforeAllScenarioHook = () => {
};
let beforeEachScenarioHook = () => {
};
let afterAllScenarioHook = () => {
};
let afterEachScenarioHook = () => {
};
const configuration = getVitestCucumberConfiguration();
const options = {
includeTags: extractTagFilters(
describeFeatureOptions?.includeTags || configuration.includeTags
),
excludeTags: extractTagFilters(
describeFeatureOptions?.excludeTags || configuration.excludeTags
)
};
const describeScenarios = [];
const describeRules = [];
let describeBackground = null;
const descibeFeatureParams = {
Background: (backgroundCallback) => {
const background = feature.getBackground();
background.isCalled = true;
describeBackground = {
skipped: !background.shouldBeCalled(options),
describeTitle: background.getTitle(),
describeHandler: createBackgroundDescribeHandler({
background,
backgroundCallback
})
};
},
Scenario: (scenarioDescription, scenarioTestCallback) => {
const scenario = feature.getScenario(scenarioDescription);
scenario.isCalled = true;
describeScenarios.push({
skipped: !scenario.shouldBeCalled(options),
describeTitle: scenario.getTitle(),
describeHandler: createScenarioDescribeHandler({
scenario,
scenarioTestCallback,
beforeEachScenarioHook,
afterEachScenarioHook
})
});
},
ScenarioOutline: (scenarioDescription, scenarioTestCallback) => {
const scenario = feature.getScenarioOutline(scenarioDescription);
ScenarioStateDetector.forScenario(scenario).checkExemples();
scenario.isCalled = true;
describeScenarios.push(
...createScenarioOutlineDescribeHandler({
scenario,
scenarioTestCallback,
beforeEachScenarioHook,
afterEachScenarioHook
}).map((t) => ({
skipped: !scenario.shouldBeCalled(options),
describeTitle: scenario.getTitle(),
describeHandler: t
}))
);
},
Rule: (ruleName, describeRuleCallback) => {
const describeRuleScenarios = [];
const currentRule = feature.checkIfRuleExists(ruleName);
currentRule.isCalled = true;
let describeRuleBackground = null;
describeRuleCallback({
RuleBackground: (backgroundCallback) => {
const background = currentRule.getBackground();
background.isCalled = true;
describeRuleBackground = {
skipped: !background.shouldBeCalled(options),
describeTitle: background.getTitle(),
describeHandler: createBackgroundDescribeHandler({
background,
backgroundCallback
})
};
},
RuleScenario: (scenarioDescription, scenarioTestCallback) => {
const scenario = currentRule.getScenario(scenarioDescription);
scenario.isCalled = true;
describeRuleScenarios.push({
describeTitle: scenario.getTitle(),
skipped: !scenario.shouldBeCalled(options),
describeHandler: createScenarioDescribeHandler({
scenario,
scenarioTestCallback,
beforeEachScenarioHook,
afterEachScenarioHook
})
});
},
RuleScenarioOutline: (scenarioDescription, scenarioTestCallback) => {
const scenario = currentRule.getScenarioOutline(scenarioDescription);
ScenarioStateDetector.forScenario(scenario).checkExemples();
scenario.isCalled = true;
describeRuleScenarios.push(
...createScenarioOutlineDescribeHandler({
scenario,
scenarioTestCallback,
beforeEachScenarioHook,
afterEachScenarioHook
}).map((t) => ({
skipped: !scenario.shouldBeCalled(options),
describeTitle: scenario.getTitle(),
describeHandler: t
}))
);
}
});
currentRule.checkUncalledScenario(options).checkUncalledBackground(options);
describeRules.push({
skipped: !currentRule.shouldBeCalled(options),
describeTitle: currentRule.getTitle(),
describeHandler: function describeRule() {
beforeAll3(async () => {
await beforeAllScenarioHook();
});
afterAll3(async () => {
await afterAllScenarioHook();
});
const { describeToRun, describeToSkip } = defineRuleScenarioToRun({
describes: describeRuleScenarios,
ruleBackground: describeRuleBackground,
featureBackground: describeBackground
});
describeFn.skip.each(
describeToSkip.map((s) => {
return [s.describeTitle, s];
})
)(`%s`, (_, { describeHandler }) => {
describeHandler();
});
describeFn.each(
describeToRun.map((s) => {
return [s.describeTitle, s];
})
)(`%s`, (_, { describeHandler }) => {
describeHandler();
});
}
});
},
BeforeEachScenario: (fn) => {
beforeEachScenarioHook = fn;
},
BeforeAllScenarios: (fn) => {
beforeAllScenarioHook = fn;
},
AfterAllScenarios: (fn) => {
afterAllScenarioHook = fn;
},
AfterEachScenario: (fn) => {
afterEachScenarioHook = fn;
}
};
describeFeatureCallback(descibeFeatureParams);
feature.checkUncalledRule(options).checkUncalledScenario(options).checkUncalledBackground(options);
describeFn(feature.getTitle(), async () => {
beforeAll3(async () => {
await beforeAllScenarioHook();
});
afterAll3(async () => {
await afterAllScenarioHook();
});
const { describeToRun, describeToSkip } = defineScenarioToRun({
describes: describeScenarios,
featureBackground: describeBackground,
describeRules
});
describeFn.skip.each(
describeToSkip.map((s) => {
return [s.describeTitle, s];
})
)(`%s`, (_, { describeHandler }) => {
describeHandler();
});
describeFn.each(
describeToRun.map((s) => {
return [s.describeTitle, s];
})
)(`%s`, (_, { describeHandler }) => {
describeHandler();
});
});
}
// src/vitest/load-feature.ts
import { dirname } from "node:path";
import callsites from "callsites";
// src/parser/readfile.ts
import fs from "node:fs";
import readline from "node:readline";
// src/parser/lang/lang.json
var lang_default = {
af: {
and: [
"* ",
"En "
],
background: [
"Agtergrond"
],
but: [
"Maar "
],
examples: [
"Voorbeelde"
],
feature: [
"Funksie",
"Besigheid Behoefte",
"Vermo\xEB"
],
given: [
"Gegewe "
],
name: "Afrikaans",
native: "Afrikaans",
rule: [
"Regel"
],
scenario: [
"Voorbeeld",
"Situasie"
],
scenarioOutline: [
"Situasie Uiteensetting"
],
then: [
"Dan "
],
when: [
"Wanneer "
]
},
am: {
and: [
"* ",
"\u0535\u057E "
],
background: [
"\u053F\u0578\u0576\u057F\u0565\u0584\u057D\u057F"
],
but: [
"\u0532\u0561\u0575\u0581 "
],
examples: [
"\u0555\u0580\u056B\u0576\u0561\u056F\u0576\u0565\u0580"
],
feature: [
"\u0556\u0578\u0582\u0576\u056F\u0581\u056B\u0578\u0576\u0561\u056C\u0578\u0582\u0569\u0575\u0578\u0582\u0576",
"\u0540\u0561\u057F\u056F\u0578\u0582\u0569\u0575\u0578\u0582\u0576"
],
given: [
"\u0534\u056B\u0581\u0578\u0582\u0584 "
],
name: "Armenian",
native: "\u0570\u0561\u0575\u0565\u0580\u0565\u0576",
rule: [
"Rule"
],
scenario: [
"\u0555\u0580\u056B\u0576\u0561\u056F",
"\u054D\u0581\u0565\u0576\u0561\u0580"
],
scenarioOutline: [
"\u054D\u0581\u0565\u0576\u0561\u0580\u056B \u056F\u0561\u057C\u0578\u0582\u0581\u057E\u0561\u0581\u0584\u0568"
],
then: [
"\u0531\u057A\u0561 "
],
when: [
"\u0535\u0569\u0565 ",
"\u0535\u0580\u0562 "
]
},
an: {
and: [
"* ",
"Y ",
"E "
],
background: [
"Antecedents"
],
but: [
"Pero "
],
examples: [
"Eixemplos"
],
feature: [
"Caracteristica"
],
given: [
"Dau ",
"Dada ",
"Daus ",
"Dadas "
],
name: "Aragonese",
native: "Aragon\xE9s",
rule: [
"Rule"
],
scenario: [
"Eixemplo",
"Caso"
],
scenarioOutline: [
"Esquema del caso"
],
then: [
"Alavez ",
"Allora ",
"Antonces "
],
when: [
"Cuan "
]
},
ar: {
and: [
"* ",
"\u0648 "
],
background: [
"\u0627\u0644\u062E\u0644\u0641\u064A\u0629"
],
but: [
"\u0644\u0643\u0646 "
],
examples: [
"\u0627\u0645\u062B\u0644\u0629"
],
feature: [
"\u062E\u0627\u0635\u064A\u0629"
],
given: [
"\u0628\u0641\u0631\u0636 "
],
name: "Arabic",
native: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629",
rule: [
"Rule"
],
scenario: [
"\u0645\u062B\u0627\u0644",
"\u0633\u064A\u0646\u0627\u0631\u064A\u0648"
],
scenarioOutline: [
"\u0633\u064A\u0646\u0627\u0631\u064A\u0648 \u0645\u062E\u0637\u0637"
],
then: [
"\u0627\u0630\u0627\u064B ",
"\u062B\u0645 "
],
when: [
"\u0645\u062A\u0649 ",
"\u0639\u0646\u062F\u0645\u0627 "
]
},
ast: {
and: [
"* ",
"Y ",
"Ya "
],
background: [
"Antecedentes"
],
but: [
"Peru "
],
examples: [
"Exemplos"
],
feature: [
"Carauter\xEDstica"
],
given: [
"D\xE1u ",
"Dada ",
"Daos ",
"Daes "
],
name: "Asturian",
native: "asturianu",
rule: [
"Rule"
],
scenario: [
"Exemplo",
"Casu"
],
scenarioOutline: [
"Esbozu del casu"
],
then: [
"Ent\xF3s "
],
when: [
"Cuando "
]
},
az: {
and: [
"* ",
"V\u0259 ",
"H\u0259m "
],
background: [
"Ke\xE7mi\u015F",
"Kontekst"
],
but: [
"Amma ",
"Ancaq "
],
examples: [
"N\xFCmun\u0259l\u0259r"
],
feature: [
"\xD6z\u0259llik"
],
given: [
"Tutaq ki ",
"Verilir "
],
name: "Azerbaijani",
native: "Az\u0259rbaycanca",
rule: [
"Rule"
],
scenario: [
"N\xFCmun\u0259",
"Ssenari"
],
scenarioOutline: [
"Ssenarinin strukturu"
],
then: [
"O halda "
],
when: [
"\u018Fg\u0259r ",
"N\u0259 vaxt ki "
]
},
be: {
and: [
"* ",
"I ",
"\u0414\u044B ",
"\u0422\u0430\u043A\u0441\u0430\u043C\u0430 "
],
background: [
"\u041A\u0430\u043D\u0442\u044D\u043A\u0441\u0442"
],
but: [
"\u0410\u043B\u0435 ",
"\u0406\u043D\u0430\u043A\u0448 "
],
examples: [
"\u041F\u0440\u044B\u043A\u043B\u0430\u0434\u044B"
],
feature: [
"\u0424\u0443\u043D\u043A\u0446\u044B\u044F\u043D\u0430\u043B\u044C\u043D\u0430\u0441\u0446\u044C",
"\u0424\u0456\u0447\u0430"
],
given: [
"\u041D\u044F\u0445\u0430\u0439 ",
"\u0414\u0430\u0434\u0437\u0435\u043D\u0430 "
],
name: "Belarusian",
native: "\u0411\u0435\u043B\u0430\u0440\u0443\u0441\u043A\u0430\u044F",
rule: [
"\u041F\u0440\u0430\u0432\u0456\u043B\u044B"
],
scenario: [
"\u0421\u0446\u044D\u043D\u0430\u0440\u044B\u0439",
"C\u0446\u044D\u043D\u0430\u0440"
],
scenarioOutline: [
"\u0428\u0430\u0431\u043B\u043E\u043D \u0441\u0446\u044D\u043D\u0430\u0440\u044B\u044F",
"\u0423\u0437\u043E\u0440 \u0441\u0446\u044D\u043D\u0430\u0440\u0430"
],
then: [
"\u0422\u0430\u0434\u044B "
],
when: [
"\u041A\u0430\u043B\u0456 "
]
},
bg: {
and: [
"* ",
"\u0418 "
],
background: [
"\u041F\u0440\u0435\u0434\u0438\u0441\u0442\u043E\u0440\u0438\u044F"
],
but: [
"\u041D\u043E "
],
examples: [
"\u041F\u0440\u0438\u043C\u0435\u0440\u0438"
],
feature: [
"\u0424\u0443\u043D\u043A\u0446\u0438\u043E\u043D\u0430\u043B\u043D\u043E\u0441\u0442"
],
given: [
"\u0414\u0430\u0434\u0435\u043D\u043E "
],
name: "Bulgarian",
native: "\u0431\u044A\u043B\u0433\u0430\u0440\u0441\u043A\u0438",
rule: [
"\u041F\u0440\u0430\u0432\u0438\u043B\u043E"
],
scenario: [
"\u041F\u0440\u0438\u043C\u0435\u0440",
"\u0421\u0446\u0435\u043D\u0430\u0440\u0438\u0439"
],
scenarioOutline: [
"\u0420\u0430\u043C\u043A\u0430 \u043D\u0430 \u0441\u0446\u0435\u043D\u0430\u0440\u0438\u0439"
],
then: [
"\u0422\u043E "
],
when: [
"\u041A\u043E\u0433\u0430\u0442\u043E "
]
},
bm: {
and: [
"* ",
"Dan "
],
background: [
"Latar Belakang"
],
but: [
"Tetapi ",
"Tapi "
],
examples: [
"Contoh"
],
feature: [
"Fungsi"
],
given: [
"Diberi ",
"Bagi "
],
name: "Malay",
native: "Bahasa Melayu",
rule: [
"Rule"
],
scenario: [
"Senario",
"Situasi",
"Keadaan"
],
scenarioOutline: [
"Kerangka Senario",
"Kerangka Situasi",
"Kerangka Keadaan",
"Garis Panduan Senario"
],
then: [
"Maka ",
"Kemudian "
],
when: [
"Apabila "
]
},
bs: {
and: [
"* ",
"I ",
"A "
],
background: [
"Pozadina"
],
but: [
"Ali "
],
examples: [
"Primjeri"
],
feature: [
"Karakteristika"
],
given: [
"Dato "
],
name: "Bosnian",
native: "Bosanski",
rule: [
"Rule"
],
scenario: [
"Primjer",
"Scenariju",
"Scenario"
],
scenarioOutline: [
"Scenariju-obris",
"Scenario-outline"
],
then: [
"Zatim "
],
when: [
"Kada "
]
},
ca: {
and: [
"* ",
"I "
],
background: [
"Rerefons",
"Antecedents"
],
but: [
"Per\xF2 "
],
examples: [
"Exemples"
],
feature: [
"Caracter\xEDstica",
"Funcionalitat"
],
given: [
"Donat ",
"Donada ",
"At\xE8s ",
"Atesa "
],
name: "Catalan",
native: "catal\xE0",
rule: [
"Rule"
],
scenario: [
"Exemple",
"Escenari"
],
scenarioOutline: [
"Esquema de l'escenari"
],
then: [
"Aleshores ",
"Cal "
],
when: [
"Quan "
]
},
cs: {
and: [
"* ",
"A tak\xE9 ",
"A "
],
background: [
"Pozad\xED",
"Kontext"
],
but: [
"Ale "
],
examples: [
"P\u0159\xEDklady"
],
feature: [
"Po\u017Eadavek"
],
given: [
"Pokud ",
"Za p\u0159edpokladu "
],
name: "Czech",
native: "\u010Cesky",
rule: [
"Pravidlo"
],
scenario: [
"P\u0159\xEDklad",
"Sc\xE9n\xE1\u0159"
],
scenarioOutline: [
"N\xE1\u010Drt Sc\xE9n\xE1\u0159e",
"Osnova sc\xE9n\xE1\u0159e"
],
then: [
"Pak "
],
when: [
"Kdy\u017E "
]
},
"cy-GB": {
and: [
"* ",
"A "
],
background: [
"Cefndir"
],
but: [
"Ond "
],
examples: [
"Enghreifftiau"
],
feature: [
"Arwedd"
],
given: [
"Anrhegedig a "
],
name: "Welsh",
native: "Cymraeg",
rule: [
"Rule"
],
scenario: [
"Enghraifft",
"Scenario"
],
scenarioOutline: [
"Scenario Amlinellol"
],
then: [
"Yna "
],
when: [
"Pryd "
]
},
da: {
and: [
"* ",
"Og "
],
background: [
"Baggrund"
],
but: [
"Men "
],
examples: [
"Eksempler"
],
feature: