UNPKG

@travetto/test

Version:

Declarative test framework

133 lines (119 loc) 3.96 kB
import type { RegistryAdapter } from '@travetto/registry'; import { AppError, asFull, type Class, describeFunction, Runtime, safeAssign } from '@travetto/runtime'; import { SchemaRegistryIndex } from '@travetto/schema'; import type { SuiteConfig } from '../model/suite.ts'; import type { TestConfig } from '../model/test.ts'; function combineClasses(baseConfig: SuiteConfig, ...subConfig: Partial<SuiteConfig>[]): SuiteConfig { for (const config of subConfig) { if (config.beforeAll) { baseConfig.beforeAll = [...baseConfig.beforeAll, ...config.beforeAll]; } if (config.beforeEach) { baseConfig.beforeEach = [...baseConfig.beforeEach, ...config.beforeEach]; } if (config.afterAll) { baseConfig.afterAll = [...baseConfig.afterAll, ...config.afterAll]; } if (config.afterEach) { baseConfig.afterEach = [...baseConfig.afterEach, ...config.afterEach]; } if (config.tags) { baseConfig.tags = [...baseConfig.tags ?? [], ...config.tags]; } baseConfig.skip = config.skip ?? baseConfig.skip; if (config.tests) { for (const [key, test] of Object.entries(config.tests ?? {})) { baseConfig.tests[key] = { ...test, sourceImport: Runtime.getImport(baseConfig.class), class: baseConfig.class, classId: baseConfig.classId, import: baseConfig.import, }; } } } return baseConfig; } function combineMethods(suite: SuiteConfig, baseConfig: TestConfig, ...subConfig: Partial<TestConfig>[]): TestConfig { baseConfig.classId = suite.classId; baseConfig.import = suite.import; for (const config of subConfig) { safeAssign(baseConfig, config, { tags: [ ...baseConfig.tags ?? [], ...config.tags ?? [] ] }); } return baseConfig; } export class SuiteRegistryAdapter implements RegistryAdapter<SuiteConfig> { #cls: Class; #config: SuiteConfig; constructor(cls: Class) { this.#cls = cls; } register(...data: Partial<SuiteConfig>[]): SuiteConfig { if (!this.#config) { const { lines, hash } = describeFunction(this.#cls) ?? {}; this.#config = asFull<SuiteConfig>({ class: this.#cls, classId: this.#cls.Ⲑid, tags: [], skip: false, import: Runtime.getImport(this.#cls), lineStart: lines?.[0], lineEnd: lines?.[1], sourceHash: hash, tests: {}, beforeAll: [], beforeEach: [], afterAll: [], afterEach: [] }); } combineClasses(this.#config, ...data); return this.#config; } registerTest(method: string, ...data: Partial<TestConfig>[]): TestConfig { const suite = this.register(); if (!(method in this.#config.tests)) { const { lines, hash } = describeFunction(this.#cls)?.methods?.[method] ?? {}; const config = asFull<TestConfig>({ class: this.#cls, tags: [], skip: false, import: Runtime.getImport(this.#cls), lineStart: lines?.[0], lineEnd: lines?.[1], lineBodyStart: lines?.[2], methodName: method, sourceHash: hash, }); this.#config.tests[method] = config; } const result = this.#config.tests[method]; combineMethods(suite, result, ...data); return result; } finalize(parent?: SuiteConfig): void { if (parent) { combineClasses(this.#config, parent); } for (const test of Object.values(this.#config.tests)) { test.tags = [...test.tags ?? [], ...this.#config.tags ?? []]; test.description ||= SchemaRegistryIndex.get(this.#cls).getMethod(test.methodName).description; } } get(): SuiteConfig { return this.#config; } getMethod(method: string): TestConfig { const test = this.#config.tests[method]; if (!test) { throw new AppError(`Test not registered: ${String(method)} on ${this.#cls.name}`); } return test; } }