@travetto/test
Advanced tools
Comparing version 0.0.29 to 0.0.30
@@ -31,3 +31,3 @@ { | ||
}, | ||
"version": "0.0.29" | ||
"version": "0.0.30" | ||
} |
@@ -26,4 +26,4 @@ import { AllSuitesResult, TestResult, SuiteResult, TestEvent } from '../model'; | ||
this.summary.skip += src.skip; | ||
this.summary.total += src.total; | ||
this.summary.total += (src.fail + src.success + src.skip); | ||
} | ||
} |
@@ -5,8 +5,15 @@ import { Class } from '@travetto/registry'; | ||
export function Test(description?: string, extra?: Partial<TestConfig>) { | ||
return (inst: any, prop: string, descriptor: PropertyDescriptor) => { | ||
export function Test(): MethodDecorator; | ||
export function Test(description: string, extra?: Partial<TestConfig>): MethodDecorator; | ||
export function Test(extra: Partial<TestConfig>): MethodDecorator; | ||
export function Test(description?: string | Partial<TestConfig>, extra?: Partial<TestConfig>): MethodDecorator { | ||
if (description && typeof description !== 'string') { | ||
extra = extra ? {...description, ...extra} : extra; | ||
description = extra.description || undefined; | ||
} | ||
return (inst: any, prop: string | symbol, descriptor: PropertyDescriptor) => { | ||
TestRegistry.registerMethod(inst.constructor, descriptor.value, { | ||
...(extra || {}), | ||
file: inst.constructor.__filename, | ||
description | ||
description: description as string | ||
}); | ||
@@ -13,0 +20,0 @@ return descriptor; |
@@ -10,4 +10,5 @@ import { Class } from '@travetto/registry/src/model/types'; | ||
methodName: string; | ||
shouldError: string | RegExp | Function; | ||
shouldError?: string | RegExp | Function; | ||
skip: boolean; | ||
timeout?: number; | ||
} | ||
@@ -14,0 +15,0 @@ |
@@ -1,2 +0,3 @@ | ||
import { LocalExecution, ChildExecution, serializeError, deserializeError, ExecutionPool } from '@travetto/exec'; | ||
import { LocalExecution, ChildExecution, serializeError, deserializeError } from '@travetto/exec'; | ||
import { ConcurrentPool, IdleManager } from '@travetto/pool'; | ||
import * as startup from '@travetto/base/src/startup'; | ||
@@ -56,4 +57,10 @@ import { Consumer } from '../consumer'; | ||
// Die if no communication within 120 seconds | ||
const idle = new IdleManager(parseInt(process.env.IDLE_TIMEOUT || '120000', 10)); | ||
idle.extend(); | ||
worker.listen(async (data: Event) => { | ||
console.log('on message', data); | ||
idle.extend(); // Extend | ||
if (data.type === Events.INIT) { | ||
@@ -113,3 +120,3 @@ console.debug('Init'); | ||
export function client() { | ||
return new ExecutionPool(async () => { | ||
return new ConcurrentPool(async () => { | ||
const worker = new ChildExecution(require.resolve('../../bin/travetto-test.js'), true); | ||
@@ -116,0 +123,0 @@ worker.init(); |
@@ -14,25 +14,62 @@ import * as fs from 'fs'; | ||
export const BREAKOUT = Symbol('breakout'); | ||
export const TIMEOUT = Symbol('timeout'); | ||
export class ExecuteUtil { | ||
static timeout = 5000; | ||
static timeout = parseInt(process.env.DEFAULT_TIMEOUT || '5000', 10); | ||
static async affixProcess(suite: SuiteConfig, result: SuiteResult, phase: SuitePhase) { | ||
static asyncTimeout(duration?: number) { | ||
return new Promise((_, reject) => setTimeout(() => reject(TIMEOUT), duration || this.timeout).unref()); | ||
} | ||
static async generateSuiteError(consumer: Consumer, suite: SuiteConfig, description: string, error: Error) { | ||
const { line, file } = AssertUtil.readFilePosition(error, suite.file); | ||
const badAssert: Assertion = { | ||
line, | ||
file, | ||
error, | ||
message: error.message, | ||
text: '(outer)', | ||
operator: 'throws' | ||
}; | ||
const badTest: TestResult = { | ||
status: 'fail', | ||
className: suite.className, | ||
methodName: description, | ||
description, | ||
lines: { start: line, end: line }, | ||
file, | ||
error, | ||
assertions: [badAssert], | ||
output: {} | ||
}; | ||
const badTestConfig: TestConfig = { | ||
class: suite.class, | ||
className: badTest.className, | ||
file: badTest.file, | ||
lines: badTest.lines, | ||
methodName: badTest.methodName, | ||
description: badTest.description, | ||
skip: false | ||
}; | ||
consumer.onEvent({ type: 'test', phase: 'before', test: badTestConfig }); | ||
consumer.onEvent({ type: 'assertion', phase: 'after', assertion: badAssert }); | ||
consumer.onEvent({ type: 'test', phase: 'after', test: badTest }); | ||
return badTest; | ||
} | ||
static async affixProcess(consumer: Consumer, phase: SuitePhase, suite: SuiteConfig, result: SuiteResult) { | ||
try { | ||
for (const fn of suite[phase]) { | ||
await fn.call(suite.instance); | ||
await Promise.race([this.asyncTimeout(), fn.call(suite.instance)]); | ||
} | ||
} catch (error) { | ||
const { line, file } = AssertUtil.readFilePosition(error, suite.file); | ||
result.tests.push({ | ||
status: 'fail', | ||
className: suite.className, | ||
methodName: phase, | ||
description: phase, | ||
lines: { start: line, end: line }, | ||
file, | ||
error, | ||
assertions: [], | ||
output: {} | ||
} as TestResult); | ||
if (error === TIMEOUT) { | ||
error = new Error(`${suite.className}: ${phase} timed out`);; | ||
} | ||
const res = await this.generateSuiteError(consumer, suite, phase, error); | ||
result.tests.push(res); | ||
throw BREAKOUT; | ||
@@ -42,38 +79,2 @@ } | ||
static async stubSuiteFailure(suite: SuiteConfig, e: Error, consumer?: Consumer) { | ||
if (!consumer) { | ||
return; | ||
} | ||
const test = { | ||
className: suite.className, | ||
lines: { ...suite.lines }, | ||
status: 'fail', | ||
methodName: 'all', | ||
error: e, | ||
output: { error: e.stack }, | ||
assertions: [{ | ||
file: suite.file, | ||
line: suite.lines.start, | ||
text: '(init)', | ||
error: e, | ||
message: e.message, | ||
operator: 'throws' | ||
}], | ||
class: suite.class.name, | ||
description: '', | ||
file: suite.file | ||
} as TestResult; | ||
consumer.onEvent({ phase: 'after', type: 'test', test }); | ||
consumer.onEvent({ | ||
phase: 'after', type: 'suite', suite: { | ||
success: 0, | ||
fail: 1, | ||
skip: 0, | ||
total: 1 | ||
} as SuiteResult | ||
}); | ||
} | ||
static isTest(file: string) { | ||
@@ -146,7 +147,11 @@ return new Promise<boolean>((resolve, reject) => { | ||
const timeout = new Promise((_, reject) => setTimeout(reject, this.timeout).unref()); | ||
const timeout = this.asyncTimeout(test.timeout); | ||
const res = await Promise.race([suite.instance[test.methodName](), timeout]); | ||
result.status = 'success'; | ||
} catch (err) { | ||
err = this.checkError(test, err); | ||
if (err === TIMEOUT) { | ||
err = new Error('Operation timed out'); | ||
} else { | ||
err = this.checkError(test, err); | ||
} | ||
if (!err) { | ||
@@ -180,29 +185,26 @@ result.status = 'success'; | ||
static async executeSuiteTest(consumer: Consumer, suite: SuiteConfig, test: TestConfig) { | ||
const result: SuiteResult = { | ||
success: 0, | ||
fail: 0, | ||
skip: 0, | ||
total: 0, | ||
lines: { ...suite.lines }, | ||
file: suite.file, | ||
className: suite.className, | ||
tests: [] | ||
}; | ||
try { | ||
const result: SuiteResult = { | ||
success: 0, | ||
fail: 0, | ||
skip: 0, | ||
total: 0, | ||
lines: { ...suite.lines }, | ||
file: suite.file, | ||
className: suite.className, | ||
tests: [] | ||
}; | ||
try { | ||
await this.affixProcess(suite, result, 'beforeAll'); | ||
await this.affixProcess(suite, result, 'beforeEach'); | ||
await this.executeTest(consumer, test); | ||
await this.affixProcess(suite, result, 'afterEach'); | ||
await this.affixProcess(suite, result, 'afterAll'); | ||
} catch (e) { | ||
if (e.message === 'breakout') { | ||
// Done | ||
} else { | ||
throw e; | ||
} | ||
await this.affixProcess(consumer, 'beforeAll', suite, result); | ||
await this.affixProcess(consumer, 'beforeEach', suite, result); | ||
await this.executeTest(consumer, test); | ||
await this.affixProcess(consumer, 'afterEach', suite, result); | ||
await this.affixProcess(consumer, 'afterAll', suite, result); | ||
} catch (e) { | ||
if (e === BREAKOUT) { | ||
// Done | ||
} else { | ||
const res = await this.generateSuiteError(consumer, suite, 'all', e); | ||
result.tests.push(res); | ||
} | ||
} catch (e) { | ||
this.stubSuiteFailure(suite, e, consumer); | ||
} | ||
@@ -212,46 +214,43 @@ } | ||
static async executeSuite(consumer: Consumer, suite: SuiteConfig) { | ||
try { | ||
const result: SuiteResult = { | ||
success: 0, | ||
fail: 0, | ||
skip: 0, | ||
total: 0, | ||
lines: { ...suite.lines }, | ||
file: suite.file, | ||
className: suite.className, | ||
tests: [] | ||
}; | ||
const result: SuiteResult = { | ||
success: 0, | ||
fail: 0, | ||
skip: 0, | ||
total: 0, | ||
lines: { ...suite.lines }, | ||
file: suite.file, | ||
className: suite.className, | ||
tests: [] | ||
}; | ||
consumer.onEvent({ phase: 'before', type: 'suite', suite }); | ||
consumer.onEvent({ phase: 'before', type: 'suite', suite }); | ||
try { | ||
await this.affixProcess(suite, result, 'beforeAll'); | ||
try { | ||
await this.affixProcess(consumer, 'beforeAll', suite, result); | ||
for (const testConfig of suite.tests) { | ||
await this.affixProcess(suite, result, 'beforeEach'); | ||
for (const testConfig of suite.tests) { | ||
await this.affixProcess(consumer, 'beforeEach', suite, result); | ||
const ret = await this.executeTest(consumer, testConfig); | ||
result[ret.status]++; | ||
result.tests.push(ret); | ||
const ret = await this.executeTest(consumer, testConfig); | ||
result[ret.status]++; | ||
result.tests.push(ret); | ||
await this.affixProcess(suite, result, 'afterEach'); | ||
} | ||
await this.affixProcess(consumer, 'afterEach', suite, result); | ||
} | ||
await this.affixProcess(suite, result, 'afterAll'); | ||
} catch (e) { | ||
if (e.message === 'breakout') { | ||
// Done | ||
} else { | ||
throw e; | ||
} | ||
await this.affixProcess(consumer, 'afterAll', suite, result); | ||
} catch (e) { | ||
if (e === BREAKOUT) { | ||
// Done | ||
} else { | ||
const res = await this.generateSuiteError(consumer, suite, 'all', e); | ||
result.tests.push(res); | ||
} | ||
} | ||
consumer.onEvent({ phase: 'after', type: 'suite', suite: result }); | ||
consumer.onEvent({ phase: 'after', type: 'suite', suite: result }); | ||
result.total = result.success + result.fail; | ||
result.total = result.success + result.fail; | ||
return result as SuiteResult; | ||
} catch (e) { | ||
this.stubSuiteFailure(suite, e, consumer); | ||
} | ||
return result as SuiteResult; | ||
} | ||
@@ -258,0 +257,0 @@ |
@@ -6,3 +6,4 @@ import * as minimist from 'minimist'; | ||
import { ArrayDataSource, deserializeError } from '@travetto/exec'; | ||
import { ArrayDataSource } from '@travetto/pool'; | ||
import { deserializeError } from '@travetto/exec'; | ||
import { Class } from '@travetto/registry'; | ||
@@ -9,0 +10,0 @@ import { bulkRequire } from '@travetto/base'; |
@@ -20,3 +20,3 @@ import { MetadataRegistry, Class, ChangeEvent } from '@travetto/registry'; | ||
class: cls, | ||
method: fn.name | ||
methodName: fn.name | ||
} | ||
@@ -23,0 +23,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
45951
1306