@serenity-js/core
Version:
The core Serenity/JS framework, providing the Screenplay Pattern interfaces, as well as the test reporting and integration infrastructure
87 lines (71 loc) • 2.92 kB
text/typescript
import { ConfigurationError } from '../errors';
import { CapabilityTag, FeatureTag, Tag, ThemeTag } from '../model';
import type { FileSystem } from './FileSystem';
import { Path } from './Path';
export class RequirementsHierarchy {
private root: Path;
private static readonly specDirectoryCandidates = [
`features`,
`specs`,
`spec`,
`tests`,
`test`,
`src`,
];
constructor(
private readonly fileSystem: FileSystem,
private readonly userDefinedSpecDirectory?: Path,
) {
}
requirementTagsFor(pathToSpec: Path, featureName?: string): Tag[] {
const [ fileBasedFeatureName, capabilityName, ...themeNames ] = this.hierarchyFor(pathToSpec).reverse().filter(segment => !['.', '..'].includes(segment));
const themeTags = themeNames.reverse().map(themeName => Tag.humanReadable(ThemeTag, themeName));
const capabilityTag = capabilityName && Tag.humanReadable(CapabilityTag, capabilityName);
const featureTag = featureName
? new FeatureTag(featureName)
: Tag.humanReadable(FeatureTag, fileBasedFeatureName)
return [
...themeTags,
capabilityTag,
featureTag
].filter(Boolean);
}
hierarchyFor(pathToSpec: Path): string[] {
const relative = this.rootDirectory().relative(pathToSpec);
return relative.split().map((segment, i, segments) => {
// return all the segments as-is, except for the last one
if (i < segments.length - 1) {
return segment;
}
// Strip the extension, like `.feature` or `.spec.ts`
const firstDotIndex = segment.indexOf('.');
return firstDotIndex === -1
? segment
: segment.slice(0, firstDotIndex);
});
}
rootDirectory(): Path {
if (!this.root) {
this.root = this.userDefinedSpecDirectory
? this.resolve(this.userDefinedSpecDirectory)
: this.guessRootDirectory();
}
return this.root;
}
private guessRootDirectory(): Path {
for (const candidate of RequirementsHierarchy.specDirectoryCandidates) {
const candidateSpecDirectory = Path.from(candidate);
if (this.fileSystem.exists(Path.from(candidate))) {
return this.fileSystem.resolve(candidateSpecDirectory);
}
}
// default to current working directory
return this.fileSystem.resolve(Path.from('.'));
}
private resolve(userDefinedRootDirectory: Path): Path {
if (!this.fileSystem.exists(userDefinedRootDirectory)) {
throw new ConfigurationError(`Configured specDirectory \`${ userDefinedRootDirectory }\` does not exist`);
}
return this.fileSystem.resolve(userDefinedRootDirectory);
}
}