Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e5fc2b4
fix(test): Rewrite run method to better handle function timeouts.. Up…
ElijahKotyluk Apr 14, 2021
3af00c5
test(snapshot): update snapshots.
ElijahKotyluk Apr 14, 2021
008ee7e
chore(merge): pull master.
ElijahKotyluk Apr 14, 2021
9e5440c
style(test): lint fix.
ElijahKotyluk Apr 14, 2021
7471f92
style(interface): remove non-null assertion.
ElijahKotyluk Apr 14, 2021
e42b184
test(suite): update snapshots.
ElijahKotyluk Apr 14, 2021
650bfe0
test(interface): fix possibly null prop.
ElijahKotyluk Apr 14, 2021
94514f4
refactor(suite): clean up suite class. Remove skip from bail test.
ElijahKotyluk Apr 14, 2021
bebf8f1
fix(test): clear timeout in wait promise chain.
ElijahKotyluk Apr 14, 2021
d9f2fb4
style(test): lint fix.
ElijahKotyluk Apr 14, 2021
e6a4ebf
test(suite): split bail tests into two, one for sequential, one for n…
ElijahKotyluk Apr 15, 2021
efa4c18
chore(suite): fix spacing.
ElijahKotyluk May 4, 2021
7175f70
Merge branch 'master' into refactor/core-v1-alpha
ElijahKotyluk May 4, 2021
bcc4f4a
test(test): clear timers at end of timeout test.
ElijahKotyluk May 22, 2021
34d860d
chore(test-spec): update comment for timeout test.
ElijahKotyluk May 22, 2021
bbfa7a0
fix(test): remove await from fn when racing promise against timeout.
ElijahKotyluk May 22, 2021
74f1c54
refactor(runnable): make runnable an abstract class, remove events an…
ElijahKotyluk May 26, 2021
6893b76
test(runnable): start testing abstract class.
ElijahKotyluk May 26, 2021
403cd68
refactor(runnable): update runnable class and tests. Update result ty…
ElijahKotyluk Jun 4, 2021
a3b3c94
refactor(runnable): make doFail always take an error.
ElijahKotyluk Jun 5, 2021
a540717
Merge branch 'master' into refactor/core-v1-alpha
ElijahKotyluk Jan 4, 2022
df63892
chore(core): misc changes.
ElijahKotyluk Jan 4, 2022
7e3b2c4
Merge branch 'refactor/core-v1-alpha' of github.com:onyxjs/onyx into …
ElijahKotyluk Jan 4, 2022
77e357c
chore(configs): update ts and jest.
ElijahKotyluk Jan 10, 2022
fb7e5a0
refactor(test): rewrite test class, runnable class, and tests. Commen…
ElijahKotyluk Jan 10, 2022
a8d7757
test(core): skip files that have not been refactored.
ElijahKotyluk Jan 10, 2022
3bb9ff9
refactor(test): rewrite test class.
ElijahKotyluk Jan 11, 2022
4eda7c3
refactor(suite): rewrite run method.
ElijahKotyluk Jan 11, 2022
097457c
chore(core): remove utils.ts and types.ts.
ElijahKotyluk Jan 11, 2022
554c493
feat(utils): add utils.
ElijahKotyluk Jan 11, 2022
844363c
feat(errors): add internal error classes.
ElijahKotyluk Jan 11, 2022
1abd6bd
feat(types): add types directory.
ElijahKotyluk Jan 11, 2022
3fbd310
chore(snapshots): remove old snapshots.
ElijahKotyluk Jan 11, 2022
384b73b
chore(core): WIP, circular dependencies from core class imports.
ElijahKotyluk Jan 11, 2022
d5fecdc
fix(suite): export isSuite and rootSymbol.
ElijahKotyluk Jan 11, 2022
0200e16
test(Test): fix should fail.
ElijahKotyluk Jan 11, 2022
6085412
feat(runnable): add isStatus method.
ElijahKotyluk Jan 11, 2022
45ec3b3
refactor(result): remove properties, update addErrors method.
ElijahKotyluk Jan 11, 2022
aeff41d
refactor(runnable): add isPending method, update concatenated return …
ElijahKotyluk Jan 12, 2022
da35643
refactor(result): make result type generic based on test type. add cr…
ElijahKotyluk Jan 12, 2022
e4e5e68
test(result): reintroduce result class tests.
ElijahKotyluk Jan 12, 2022
41e63a8
Merge branch 'master' into refactor/core-v1-alpha
ElijahKotyluk Jan 12, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/core/jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['test/'],
testEnvironment: 'jsdom',
testMatch: ['**/test/**/*.[jt]s?(x)', '**/?(*.)+(spec).[jt]s?(x)'],
};
10 changes: 10 additions & 0 deletions packages/core/src/BailError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class BailError extends Error {
constructor(message: string) {
super(message)
this.name = 'BailError'
}
}

function createBailError(message: string): BailError {
return new BailError(message)
}
10 changes: 10 additions & 0 deletions packages/core/src/TimeoutError.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export class TimeoutError extends Error {
public constructor(message: string) {
super(message)
this.name = 'TimeoutError'
}
}

function createTimeoutError(message: string): TimeoutError {
return new TimeoutError(message)
}
8 changes: 4 additions & 4 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export {
beforeEach,
afterEach,
} from './interface'
export { Status, default as Result } from './result'
export { default as Runnable, isRunnable, RunnableTypes } from './runnable'
export { /*Status,*/ default as Result } from './result'
export { default as Runnable, isRunnable, /*RunnableTypes*/ } from './runnable'
export { default as Test, isTest } from './test'
export { default as Suite, isSuite, SuiteStats, rootSymbol, BailError } from './suite'
export { default as Runner, RunOptions, runnerDefaults } from './runner'
export { default as Suite, isSuite, /*SuiteStats,*/ rootSymbol/*, BailError*/ } from './suite'
// export { RunOptions } from './runner'
export { Hook, HookName, HookFn, Hooks } from './hooks'
88 changes: 63 additions & 25 deletions packages/core/src/result.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,99 @@
export enum Status {
Pending = 'pending',
Running = 'running',
Passed = 'passed',
Failed = 'failed',
Skipped = 'skipped',
Todo = 'todo',
// import { RunnableTypes } from "."\


// export enum Status {
// Pending = 'pending',
// Running = 'running',
// Passed = 'passed',
// Failed = 'failed',
// Skipped = 'skipped',
// Todo = 'todo',
// }

// Types
import { BailError } from './BailError'
import { TimeoutError } from './TimeoutError'
import { RunnableTypes, Status } from './types'
type ResultOptions<T extends RunnableTypes> = {
description: string
time: number
type: T
}

type RunnableError = Error | TimeoutError | BailError

/**
* @todo Delete messages.
*/
export default class Result {
private internalStatus: Status
private internalMessages: string[]
class Result<T extends RunnableTypes> {
private _internalErrors: Error[]
private _internalStatus: Status
private _internalMessages: string[]

constructor(status?: Status, messages: string | string[] = []) {
this.internalStatus = status || Status.Pending
if (!Array.isArray(messages)) {
messages = [ messages ]
}
this.internalMessages = messages
public time: number
public description: string
public type: T

constructor(messages: string | string[] = [], options: ResultOptions<T>, errors: RunnableError[] | RunnableError = [], status?: Status) {
this._internalErrors = !Array.isArray(errors) ? [ errors ] : errors
this._internalStatus = status || Status.Pending
this._internalMessages = !Array.isArray(messages) ? [ messages ] : messages

this.description = options.description
this.time = options.time
this.type = options.type
}

/**
* @description Checks if the internal status is 'Pending' or 'Running'.
*/
public isDone() {
return this.internalStatus !== Status.Pending && this.internalStatus !== Status.Running
public isDone(): boolean {
return this._internalStatus !== Status.Pending && this._internalStatus !== Status.Running
}

public get errors(): Array<Error> {
return this._internalErrors
}

/**
* @description Gets the internal status on the current `Result` instance.
*/
public get status() {
return this.internalStatus
public get status(): Status {
return this._internalStatus
}

/**
* @description Sets the internal status on the current `Result` instance.
*/
public set status(v: Status) {
if (this.isDone()) { return }
this.internalStatus = v
this._internalStatus = v
}

/**
* @description Gets the internal messages on the current `Result` instance.
*/
public get messages() {
return this.internalMessages
public get messages(): string[] {
return this._internalMessages
}

/**
* @description Adds messages to the internal messages if the `Runnable` has not completed.
*/
public addMessages(...messages: string[]) {
public addMessages(...messages: string[]): void {
if (this.isDone()) { return }
this.internalMessages.push(...messages)
this._internalMessages = [...this._internalMessages, ...messages]
}

public addErrors(...errors: Error[]): void {
if (this.isDone()) { return }
this.addMessages(...errors.map((e) => e.message))
this._internalErrors = [...this._internalErrors, ...errors]
}
}

export function createResult<T extends RunnableTypes>(options: ResultOptions<T>, messages: string | string[] = [], errors: RunnableError[] | RunnableError = [], status?: Status): Result<T> {
return new Result(messages, options, errors, status)
}

export default Result
129 changes: 77 additions & 52 deletions packages/core/src/runnable.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,57 @@
import { EventEmitter } from 'events'
import { performance } from 'perf_hooks'
import Result, { Status } from './result'
import Suite from './suite'
import type { Hooks } from './hooks'
// import { RunStatus/*, BaseResult*/ } from './newResult'
// import Result, { Status } from './result'
import type Suite from './suite'
import type Result from './result'
import type Test from './test'

// Types
import { RunnableOptions, RunnableResult, RunStatus, RunnableTypes, RunOptions } from './types'

export const runnableSymbol = Symbol('isRunnable')

/**
* @description Checks if passed value is an instance of `Runnable`.
*/
export const isRunnable = (v: unknown): v is Runnable => {
if (typeof v === 'object' && v === null) { return false }
return (v as Runnable)[runnableSymbol]
}

export enum RunnableTypes {
Runnable = 'runnable',
Suite = 'suite',
Test = 'test',
if (v && (v as Runnable)[runnableSymbol]) { return true }
else { return false }
}

export interface RunnableOptions {
skip: boolean
todo: boolean
// export const isRunnable2 = <R extends Runnable | Test | Suite>(v: unknown): v is R => {
// return (v instanceof Runnable) || (v instanceof Test) || (v instanceof Suite)
// }

// export enum RunnableTypes {
// Runnable = 'runnable',
// Suite = 'suite',
// Test = 'test',
// }

// export interface RunnableOptions {
// skip: boolean
// todo: boolean
// }

// export interface RunnableResult extends BaseResult {
// id: string
// description: string
// time: number
// }

const DEFAULT_RESULT: RunnableResult = {
id: '',
description: '',
messages: [],
failures: [],
hooks: {} as Hooks,
status: RunStatus.PENDING,
time: 0,
fullDescription: '',
}

export default class Runnable extends EventEmitter {
export default abstract class Runnable {

/**
* @description Normalize passed options object with `Runnable` default options.
Expand All @@ -37,49 +64,53 @@ export default class Runnable extends EventEmitter {
}
}
public description: string
public result: Result
public options: RunnableOptions
public parent: Suite | null
public result: RunnableResult
public status: RunStatus
public type: RunnableTypes = RunnableTypes.Runnable
public [runnableSymbol] = true

public time = 0
private start = 0
public start = 0

/* istanbul ignore next */
constructor(description: string, options: Partial<RunnableOptions> = {}, parent: Suite | null) {
super()
this.description = description
this.result = new Result()
this.options = Runnable.normalizeOptions(options)
this.parent = parent
this.result = { ...DEFAULT_RESULT, description, fullDescription: this.getFullDescription() }
this.status = RunStatus.PENDING
}

/**
* @description Run a `Runnable` instance.
*/
public abstract run(options: Partial<RunOptions>): Promise<RunnableResult | Result>


/**
* @description Sets result status to `Running` and emits a `start` event with the `Runnable` instance and timestamp.
*/
public doStart(): void {
this.result.status = Status.Running
this.emit('start', this)
public doStart(): RunnableResult {
this.result.status = RunStatus.RUNNING
this.start = performance.now()
return this.result
}

/**
* @description Emits an `end` event with the completed `Runnable` instance and the time taken to complete.
*/
public doEnd() {
if (this.result.status !== Status.Skipped && this.result.status !== Status.Todo) {
this.time = performance.now() - this.start
if (this.result.status !== RunStatus.SKIPPED && this.result.status !== RunStatus.TODO) {
this.result.time = performance.now() - this.start
}
this.emit('end', this, this.time)
}

/**
* @description Emits a `pass` event with the passing `Runnable` instance.
*/
public doPass(): Result {
this.result.status = Status.Passed
this.emit('pass', this)
public doPass() {
this.result.status = RunStatus.PASSED
this.doEnd()

return this.result
Expand All @@ -88,12 +119,10 @@ export default class Runnable extends EventEmitter {
/**
* @description Emits a `fail` event with the failed `Runnable` instance and passed error.
*/
public doFail(error?: Error | string): Result {
if (error) {
this.result.addMessages(String(error))
}
this.result.status = Status.Failed
this.emit('fail', this, error)
public doFail(error: Error) {
this.result.failures = [...this.result.failures, error]
this.result.messages = [...this.result.messages, error.message]
this.result.status = RunStatus.FAILED
this.doEnd()

return this.result
Expand All @@ -102,41 +131,37 @@ export default class Runnable extends EventEmitter {
/**
* @description Emits `skip` event with the skipped `Runnable` instance.
*/
public doSkip(todo = false): Result {
this.result.status = todo ? Status.Todo : Status.Skipped
this.emit('skip', this, todo)
public doSkip(skipOrTodo: RunStatus.SKIPPED | RunStatus.TODO) {
this.result.status = skipOrTodo
this.doEnd()

return this.result
}

/**
* @description Run a `Runnable` instance.
* @description Check that `Runnable` has completed.
*/
// istanbul ignore next unimplemented
public async run(): Promise<Result> {
if (this.options.skip || this.options.todo) {
return this.doSkip(this.options.todo)
}

this.doStart()

return this.doSkip() // To be replaced with real run function
public isDone() {
return this.result.status !== RunStatus.PENDING && this.result.status !== RunStatus.RUNNING
}

/**
* @description Check that `Runnable` has completed.
* @description Check if the current status is the same as the status argument passed in.
*/
public isDone() {
return this.result.isDone()
public isStatus(status: RunStatus): boolean {
return this.result.status === status
}

public isPending(): boolean {
return this.result.status === RunStatus.PENDING || (this.parent !== null && this.parent.isStatus(RunStatus.PENDING))
}

/**
* @description Concatenate the Parent's description and the current `Runnable`'s description.
*/
public getFullDescription(): string {
if (this.parent && !this.parent.isRoot()) {
return `${this.parent.getFullDescription()} -> ${this.description}`
return `${this.parent.getFullDescription()} ${this.description}`
}
return this.description
}
Expand Down
Loading