@qaflag/core
Version:
Base requirements for the QA Flag library
600 lines (496 loc) • 13.8 kB
text/typescript
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}`,
});
}
}