UNPKG

@qaflag/core

Version:

Base requirements for the QA Flag library

600 lines (496 loc) 13.8 kB
import { LoggerInterface } from '../types/log-provider.interface'; import { ValueInterface, PrimitiveValueInterface, } from '../value/value.interface'; import { toType } from '../utils/to-type'; import { Test, test } from '../assertions/test'; import is from '@sindresorhus/is'; import { NumberMust } from '../assertions/number.interface'; import { StringMust } from '../assertions/string.interface'; import { BooleanMust } from '../assertions/boolean.interface'; import { DateMust } from '../assertions/date.interface'; import { ArrayMust } from '../assertions/array.interface'; import { ordinal } from '../utils/helpers'; import { ContextInterface } from '../context/context.interface'; import { SuiteInterface } from '../suite/suite.interface'; import { ScenarioInterface } from '../scenario/scenario.interface'; export interface ValueOpts { name: string; context: ContextInterface; } export abstract class ValueAbstract<InputType> implements ValueInterface<InputType> { protected _nameOverride: string | undefined = undefined; public readonly context: ContextInterface; constructor(public readonly input: InputType, protected opts: ValueOpts) { this.context = opts.context; } public get scenario(): ScenarioInterface { return this.context.scenario; } public get suite(): SuiteInterface { return this.context.scenario.suite; } public get logger(): LoggerInterface { return this.context.logger; } public get $(): InputType { return this.input; } public get name(): string { return this._nameOverride || this.opts.name; } public get length(): NumericValue { return new NumericValue(this.input['length'] ? this.input['length'] : 0, { name: `Length of ${this.name}`, context: this.context, }); } public abstract must: any; public abstract should: any; public abstract could: any; public get type() { return new StringValue(toType(this.input), { ...this.opts, name: `Type of ${this.name}`, }); } public as(newName: string) { this._nameOverride = newName; return this; } public alias(name: string) { return this.context.set(name, this); } public get number() { return new NumericValue(this.toNumber(), this.opts); } public get string() { return new StringValue(this.toString(), this.opts); } public get boolean() { return new BooleanValue(this.isTruthy(), this.opts); } public get array() { return new ArrayValue(this.toArray(), this.opts); } public get date() { return new DateValue(this.toDate(), this.opts); } public get generic() { return new GenericValue(this, this.opts); } protected isArray(): boolean { return Array.isArray(this.input); } protected isTruthy(): boolean { return !!this.input; } protected isUndefined(): boolean { return this.input === undefined; } protected isNull(): boolean { return this.input === null; } protected isString(): boolean { return typeof this.input === 'string'; } protected isNumber(): boolean { return typeof this.input === 'number'; } protected isBoolean(): boolean { return typeof this.input === 'boolean'; } protected isPrimitive(): boolean { return ['string', 'number', 'boolean'].includes(typeof this.input); } protected toString(): string { return typeof this.input == 'string' ? this.input : JSON.stringify(this.input); } protected toNumber(): number { return Number(this.input); } protected toArray(): any[] { return Array.isArray(this.input) ? this.input : [this.input]; } protected toDate() { return new Date(this.toString()); } protected createGeneric(data: any, opts?: Partial<ValueOpts>) { return new GenericValue(data, { context: this.context, name: this.name, ...opts, }); } protected createString(str: string, opts?: Partial<ValueOpts>) { return new StringValue(str, { context: this.context, name: this.name, ...opts, }); } protected createNumber(num: number, opts?: Partial<ValueOpts>) { return new NumericValue(num, { context: this.context, name: this.name, ...opts, }); } protected createBoolean(bool: boolean, opts?: Partial<ValueOpts>) { return new BooleanValue(bool, { context: this.context, name: this.name, ...opts, }); } protected createArray<T>(data: T[], opts?: Partial<ValueOpts>) { return new ArrayValue<T>(data, { context: this.context, name: this.name, ...opts, }); } public valueOf() { return this.isPrimitive() ? this.input : this.toString(); } } export abstract class PrimitiveValueAbstract<InputType> extends ValueAbstract<InputType> implements PrimitiveValueInterface<InputType> { public get number() { return new NumericValue(this.toNumber(), this.opts); } public get date() { return new DateValue(this.toDate(), this.opts); } public get keys() { return new ArrayValue(Object.keys(this.toArrayOrObject()), this.opts); } public get values() { return new ArrayValue(Object.values(this.toArrayOrObject()), this.opts); } public get entries() { return new ArrayValue(Object.entries(this.toArrayOrObject()), this.opts); } public toObject(): object { return Object(this.$); } public toArray(): unknown[] { return Array.isArray(this.$) ? this.$ : [this.$]; } public toNumber(): number { return Number(this.input); } public toDate(): Date { try { return is.date(this.input) ? this.input : new Date(this.toString()); } catch { throw `Could not convert ${this.name} (${this.input}) to date.`; } } public valueOf() { return this.input; } public split(separator: string) { return new ArrayValue(this.toString().split(separator), this.opts); } public join(separator: string) { return new StringValue(this.toArray().join(separator), this.opts); } private toArrayOrObject() { return Array.isArray(this.$) ? this.$ : Object(this.$); } } export class GenericValue extends PrimitiveValueAbstract<any> { public get must(): Test<any> { return test(this, 'must'); } public get should(): Test<any> { return test(this, 'should'); } public get could(): Test<any> { return test(this, 'could'); } } export class BooleanValue extends PrimitiveValueAbstract<boolean> { public get must(): BooleanMust { return test(this, 'must'); } public get should(): BooleanMust { return test(this, 'should'); } public get could(): BooleanMust { return test(this, 'could'); } } export class ArrayValue<T = any> extends PrimitiveValueAbstract<T[]> { public get must(): ArrayMust<ArrayValue> { return test(this, 'must'); } public get should(): ArrayMust<ArrayValue> { return test(this, 'should'); } public get could(): ArrayMust<ArrayValue> { return test(this, 'could'); } public get first() { return new GenericValue(this.$[0], { ...this.opts, name: `First in ${this.name}`, }); } public get length(): NumericValue { return new NumericValue(this.input.length, { name: `Length of ${this.name}`, context: this.context, }); } public get last() { return new GenericValue(this.$.slice(-1), { ...this.opts, name: `Last in ${this.name}`, }); } public every(name: string, callback: (item: T) => boolean) { return new BooleanValue(this.$.every(callback), { ...this.opts, name }); } public some(name: string, callback: (item: T) => boolean) { return new BooleanValue(this.$.some(callback), { ...this.opts, name }); } public map<Output>(callback: (item: T) => Output) { return new ArrayValue(this.$.map(callback), { ...this.opts, name: `Mapped ${this.name}`, }); } public filter(callback: (item: T) => boolean): ArrayValue<T> { return new ArrayValue(this.$.filter(callback), { ...this.opts, name: `Filtered ${this.name}`, }); } public forEach( callback: (item: GenericValue, i?: number) => void, ): ArrayValue<T> { this.$.forEach((input, i) => { const opts = { name: `${ordinal(i + 1)} item in ${this.name}`, context: this.context, }; callback(new GenericValue(input, opts), i); }); return this; } public pluck(propertyName: string): GenericValue[] { return this.$.map((item, i) => { const opts = { name: `${ordinal(i + 1)} item in ${this.name}`, context: this.context, }; return new GenericValue(item[propertyName], opts); }); } } export class NumericValue extends PrimitiveValueAbstract<number> { public get must(): NumberMust { return test(this, 'must'); } public get should(): NumberMust { return test(this, 'should'); } public get could(): NumberMust { return test(this, 'could'); } } export class StringValue extends PrimitiveValueAbstract<string> { public get must(): StringMust { return test(this, 'must'); } public get should(): StringMust { return test(this, 'should'); } public get could(): StringMust { return test(this, 'could'); } public nthLine(n: number) { const lines = this.$.split('\n'); return new StringValue(lines[n - 1], { ...this.opts, name: `${ordinal(n)} line of ${this.name}`, }); } public get firstLine() { const lines = this.$.split('\n'); return new StringValue(lines[0], { ...this.opts, name: `first line of ${this.name}`, }); } public get lastLine() { const lines = this.$.split('\n'); return new StringValue(lines[lines.length - 1], { ...this.opts, name: `last line of ${this.name}`, }); } public get lines(): StringValue[] { const lines = this.$.split('\n'); return lines.map( (line, i) => new StringValue(line, { ...this.opts, name: `${ordinal(i + 1)} line of ${this.name}`, }), ); } public get lineCount(): NumericValue { const lines = this.$.split('\n'); return new NumericValue(lines.length, { ...this.opts, name: `Number of lines in ${this.name}`, }); } public get trim() { return new StringValue(this.$.trim(), { ...this.opts, name: `Trimmed ${this.name}`, }); } public get length(): NumericValue { return new NumericValue(this.$.length, { ...this.opts, name: `Length of ${this.name}`, }); } } export class DateValue extends PrimitiveValueAbstract<Date> { public get must(): DateMust { return test(this, 'must'); } public get should(): DateMust { return test(this, 'should'); } public get could(): DateMust { return test(this, 'could'); } public get unixTime() { return new NumericValue(Math.floor(this.$.getTime() / 1000), { ...this.opts, name: `${this.name} from UNIX Time`, }); } public get hours() { return new NumericValue(this.$.getHours(), { ...this.opts, name: `Hours from ${this.name}`, }); } public get minutes() { return new NumericValue(this.$.getMinutes(), { ...this.opts, name: `Minutes from ${this.name}`, }); } public get seconds() { return new NumericValue(this.$.getSeconds(), { ...this.opts, name: `Seconds from ${this.name}`, }); } public get month() { return new NumericValue(this.$.getMonth() + 1, { ...this.opts, name: `Month from ${this.name}`, }); } public get dayOfMonth() { return new NumericValue(this.$.getDate(), { ...this.opts, name: `Day of Month from ${this.name}`, }); } public get year() { return new NumericValue(this.$.getFullYear(), { ...this.opts, name: `Year from ${this.name}`, }); } public get dayOfWeek() { return new NumericValue(this.$.getDay(), { ...this.opts, name: `Day of Week from ${this.name}`, }); } public get utcHours() { return new NumericValue(this.$.getUTCHours(), { ...this.opts, name: `UTC Hours from ${this.name}`, }); } public get utcMinutes() { return new NumericValue(this.$.getUTCMinutes(), { ...this.opts, name: `UTC Minutes from ${this.name}`, }); } public get utcSeconds() { return new NumericValue(this.$.getUTCSeconds(), { ...this.opts, name: `UTC Seconds from ${this.name}`, }); } public get utcMonth() { return new NumericValue(this.$.getUTCMonth() + 1, { ...this.opts, name: `UTC Month from ${this.name}`, }); } public get utcDayOfMonth() { return new NumericValue(this.$.getUTCDate(), { ...this.opts, name: `UTC Day of Month from ${this.name}`, }); } public get utcYear() { return new NumericValue(this.$.getUTCFullYear(), { ...this.opts, name: `UTC Year from ${this.name}`, }); } public get utcDayOfWeek() { return new NumericValue(this.$.getUTCDay(), { ...this.opts, name: `UTC Day of Week from ${this.name}`, }); } public get dateString() { return new StringValue(this.$.toDateString(), { ...this.opts, name: `Date String from ${this.name}`, }); } public get isoDateString() { return new StringValue(this.$.toISOString(), { ...this.opts, name: `ISO Date String from ${this.name}`, }); } public get timeString() { return new StringValue(this.$.toTimeString(), { ...this.opts, name: `Time String from ${this.name}`, }); } public get utcString() { return new StringValue(this.$.toUTCString(), { ...this.opts, name: `UTC String from ${this.name}`, }); } }