UNPKG

@amiceli/vitest-cucumber

Version:

vitest tools to use Gherkin feature in unit tests

262 lines (261 loc) 7.22 kB
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; } }