jest-allure2-reporter
Version:
Idiomatic Jest reporter for Allure Framework
144 lines (113 loc) • 5.51 kB
text/typescript
import type { AllureTestItemMetadata, AllureTestStepMetadata } from 'jest-allure2-reporter';
import { weakMemoize } from '../../utils';
export type MetadataSelectorOptions<Metadata, Result> = {
empty(): Result;
getMetadata(metadata: Metadata): Result | undefined;
getDocblock(metadata: Metadata): Result | undefined;
mergeUnsafe(a: Result, b: Result | undefined): Result;
};
export interface HasParent<Metadata> {
readonly parent?: LikeDescribeBlock<Metadata>;
}
export type LikeDescribeBlock<Metadata> = Metadata & HasParent<Metadata>;
export type LikeTestDefinition<Metadata> = Metadata & {
readonly describeBlock: LikeDescribeBlock<Metadata>;
};
export type LikeStepInvocation<Metadata> = Metadata & {
readonly definition: Metadata;
};
export type LikeTestFile<Metadata> = Metadata & {
readonly globalMetadata: Metadata;
};
export type LikeTestInvocation<Metadata> = Metadata & {
allInvocations(): Iterable<Metadata>;
readonly file: LikeTestFile<Metadata>;
readonly beforeAll: Iterable<LikeStepInvocation<Metadata>>;
readonly beforeEach: Iterable<LikeStepInvocation<Metadata>>;
readonly afterEach: Iterable<LikeStepInvocation<Metadata>>;
readonly afterAll: Iterable<LikeStepInvocation<Metadata>>;
readonly definition: LikeTestDefinition<Metadata>;
readonly fn?: Metadata;
};
export class MetadataSelector<Metadata, T extends AllureTestItemMetadata> {
constructor(protected readonly _options: MetadataSelectorOptions<Metadata, T>) {
this.belowTestInvocation = weakMemoize(this.belowTestInvocation.bind(this));
this.testInvocationAndBelow = weakMemoize(this.testInvocationAndBelow.bind(this));
this.testInvocationAndBelowDirect = weakMemoize(this.testInvocationAndBelowDirect.bind(this));
this.testDefinitionAndBelow = weakMemoize(this.testDefinitionAndBelow.bind(this));
}
protected readonly _getDocblock = (metadata: Metadata | undefined): T | undefined =>
metadata ? this._options.getDocblock(metadata) : undefined;
protected readonly _getMetadataUnsafe = (metadata: Metadata | undefined): T | undefined =>
metadata ? this._options.getMetadata(metadata) : undefined;
readonly getMetadataWithDocblock = (metadata: Metadata | undefined): T =>
this._options.mergeUnsafe(
this._options.mergeUnsafe(this._options.empty(), this._getDocblock(metadata)),
this._getMetadataUnsafe(metadata),
);
protected _describeAncestors = weakMemoize(
(metadata: LikeDescribeBlock<Metadata> | undefined): T =>
metadata
? this._options.mergeUnsafe(
this._describeAncestors(metadata.parent),
this._getMetadataUnsafe(metadata),
)
: this._options.empty(),
);
protected _ancestors(metadata: LikeTestInvocation<Metadata>): T {
return this.merge(
this.globalAndTestFile(metadata.file),
this._describeAncestors(metadata.definition.describeBlock),
);
}
protected _hookMetadata = (metadata: LikeStepInvocation<Metadata>): T => {
const definitionMetadata = this.getMetadataWithDocblock(metadata.definition);
return this.merge(definitionMetadata, this._getMetadataUnsafe(metadata));
};
public readonly merge = (a: T | undefined, b: T | undefined): T =>
this._options.mergeUnsafe(this._options.mergeUnsafe(this._options.empty(), a), b);
testInvocation(metadata: Metadata): T {
return this.getMetadataWithDocblock(metadata);
}
testDefinition(metadata: LikeTestInvocation<Metadata>): T {
return this.getMetadataWithDocblock(metadata.definition);
}
belowTestInvocation(metadata: LikeTestInvocation<Metadata>): T {
return [...metadata.allInvocations()]
.map(this._getMetadataUnsafe)
.reduce(this._options.mergeUnsafe, this._options.empty());
}
testInvocationAndBelow(metadata: LikeTestInvocation<Metadata>): T {
return this.merge(this.testInvocation(metadata), this.belowTestInvocation(metadata));
}
testInvocationAndBelowDirect(metadata: LikeTestInvocation<Metadata>): T {
return this.merge(this.testInvocation(metadata), this.getMetadataWithDocblock(metadata.fn));
}
testDefinitionAndBelow(metadata: LikeTestInvocation<Metadata>): T {
return this.merge(this.testDefinition(metadata), this.testInvocationAndBelow(metadata));
}
testDefinitionAndBelowDirect(metadata: LikeTestInvocation<Metadata>): T {
return this.merge(this.testDefinition(metadata), this.testInvocationAndBelowDirect(metadata));
}
testVertical(metadata: LikeTestInvocation<Metadata>): T {
const ancestors = this._ancestors(metadata);
const test_definition_and_below = this.testDefinitionAndBelow(metadata);
return this.merge(ancestors, test_definition_and_below);
}
steps(metadata: LikeTestInvocation<Metadata>): AllureTestStepMetadata[] {
const before = [...metadata.beforeAll, ...metadata.beforeEach].map(this._hookMetadata);
const after = [...metadata.afterEach, ...metadata.afterAll].map(this._hookMetadata);
const testFunctionSteps =
(metadata.fn && this.getMetadataWithDocblock(metadata.fn).steps) || [];
return [...before, ...testFunctionSteps, ...after];
}
globalAndTestFile(metadata: LikeTestFile<Metadata>): T {
return this.merge(
this._getMetadataUnsafe(metadata.globalMetadata),
this.getMetadataWithDocblock(metadata),
);
}
globalAndTestFileAndTestInvocation(metadata: LikeTestInvocation<Metadata>): T {
return this.merge(this.globalAndTestFile(metadata.file), this._getMetadataUnsafe(metadata));
}
}