@amiceli/vitest-cucumber
Version:
vitest tools to use Gherkin feature in unit tests
262 lines (261 loc) • 7.22 kB
JavaScript
import parsecurrency from 'parsecurrency';
import { InvalidCurrencyParameterError, InvalidDateParameterError, InvalidUrlParameterError, } from '../../errors/errors';
import symbolToCode from './symbolToCode.json';
export class ExpressionRegex {
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() { }
}
export class BooleanRegex 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';
}
}
export class WordRegex extends ExpressionRegex {
constructor() {
super({
keyword: `{word}`,
groupName: `word`,
keywordRegex: /{word}/g,
});
}
getRegex(index) {
return `\\b(?<word${index}>\\w+)\\b`;
}
getValue(str) {
return str;
}
}
export class CharRegex extends ExpressionRegex {
constructor() {
super({
keyword: `{char}`,
groupName: `char`,
keywordRegex: /{char}/g,
});
}
getRegex(index) {
return `(?<char${index}>\\w)`;
}
getValue(str) {
return str;
}
}
export class StringRegex extends ExpressionRegex {
constructor() {
super({
keyword: `{string}`,
groupName: `string`,
keywordRegex: /{string}/g,
});
}
getRegex(index) {
return `(?<string${index}>"[^"]*"|'[^']*')`;
}
getValue(str) {
return str.replace(/^["']|["']$/g, ``);
}
}
export class EmailRegex 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, ``);
}
}
export class UrlRegex 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);
}
}
}
export class IntRegex extends ExpressionRegex {
constructor() {
super({
keyword: `{int}`,
groupName: `int`,
keywordRegex: /{int}/g,
});
}
getRegex(index) {
return `(?<int${index}>-?\\d+)`;
}
getValue(str) {
return Number(str);
}
}
export class NumberRegex extends ExpressionRegex {
constructor() {
super({
keyword: `{number}`,
groupName: `number`,
keywordRegex: /{number}/g,
});
}
getRegex(index) {
return `(?<number${index}>-?\\d+(\\.\\d+)?)`;
}
getValue(str) {
return Number(str);
}
}
export class DateRegex 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;
}
}
export class CurrencyRegex extends ExpressionRegex {
constructor() {
super({
keyword: `{currency}`,
groupName: `currency`,
keywordRegex: /{currency}/g,
});
}
getRegex(index) {
const currencyRegex = `(?:([-+]{1}) ?)?(?:([A-Z]{3}) ?)?(?:([^\\d ]+?) ?)?(((?:\\d{1,3}([,. ’'\\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[value.symbol],
};
}
}
export class ListRegex 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());
}
}
export class AnyRegex extends ExpressionRegex {
constructor() {
super({
keyword: `{any}`,
groupName: `any`,
keywordRegex: /{any}/g,
});
}
getRegex(index) {
return `(?<any${index}>.+)`;
}
getValue(str) {
return str;
}
}