@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
156 lines (133 loc) • 4.86 kB
text/typescript
import { eventResponseUtil } from '@sprucelabs/spruce-event-utils'
import { diskUtil } from '@sprucelabs/spruce-skill-utils'
import SpruceError from '../../errors/SpruceError'
import PkgService from '../../services/PkgService'
import { NpmPackage } from '../../types/cli.types'
import tsConfigUtil from '../../utilities/tsConfig.utility'
import AbstractFeature, { FeatureDependency } from '../AbstractFeature'
import ParentTestFinder from '../error/ParentTestFinder'
import { FeatureCode } from '../features.types'
export interface ParentClassCandidate {
name: string
label: string
path?: string
import?: string
isDefaultExport: boolean
featureCode?: FeatureCode
}
export default class TestFeature extends AbstractFeature {
public nameReadable = 'Testing'
public description = 'Test first. Test everything! 💪'
public code: FeatureCode = 'test'
public dependencies: FeatureDependency[] = [
{ code: 'skill', isRequired: false },
{ code: 'node', isRequired: true },
]
public packageDependencies: NpmPackage[] = [
{ name: '@sprucelabs/test@latest', isDev: true },
{ name: '@sprucelabs/test-utils@latest', isDev: true },
{ name: '@sprucelabs/jest-json-reporter@latest', isDev: true },
{ name: 'jest-circus', isDev: true },
{ name: 'jest', isDev: true },
]
public actionsDir = diskUtil.resolvePath(__dirname, 'actions')
public async afterPackageInstall() {
const service = this.Service('pkg')
this.configureJest(service)
this.configureScripts(service)
this.configureTsConfig()
return {}
}
private configureTsConfig() {
try {
const tsConfig = tsConfigUtil.readConfig(this.cwd)
if (!tsConfig.compilerOptions.experimentalDecorators) {
tsConfig.compilerOptions.experimentalDecorators = true
tsConfigUtil.setCompilerOption(
this.cwd,
'experimentalDecorators',
true
)
}
} catch (err) {
//@ts-ignore
}
}
private configureScripts(service: PkgService) {
let scripts = service.get('scripts') as Record<string, any> | undefined
if (!scripts) {
scripts = {}
}
if (!scripts.test) {
scripts.test = 'jest'
scripts['watch.tests'] = 'jest --watch'
service.set({ path: 'scripts', value: scripts })
}
}
private configureJest(service: PkgService) {
const jestConfig = {
testRunner: 'jest-circus/runner',
maxWorkers: 4,
testTimeout: 120000,
testEnvironment: 'node',
testPathIgnorePatterns: [
'<rootDir>/tmp/',
'<rootDir>/src/',
'<rootDir>/node_modules/',
'<rootDir>/build/__tests__/testDirsAndFiles/',
],
testMatch: ['**/__tests__/**/*.test.js?(x)'],
moduleNameMapper: {
'^#spruce/(.*)$': '<rootDir>/build/.spruce/$1',
},
}
const existingJest = service.get('jest') as Record<string, any>
if (!existingJest) {
service.set({ path: 'jest', value: jestConfig })
}
}
public async buildParentClassCandidates(): Promise<ParentClassCandidate[]> {
const parentFinder = new ParentTestFinder(this.cwd)
const candidates: ParentClassCandidate[] = (
await parentFinder.findAbstractTests()
).map((a) => ({ ...a, label: a.name }))
const results = await this.emitter.emit(
'test.register-abstract-test-classes'
)
const { payloads } = eventResponseUtil.getAllResponsePayloadsAndErrors(
results,
SpruceError
)
for (const payload of payloads) {
const { abstractClasses } = payload
for (const ac of abstractClasses) {
const a = { ...ac, isDefaultExport: false }
if (ac.featureCode) {
const isInstalled = await this.features.isInstalled(
ac.featureCode as any
)
if (!isInstalled) {
a.label = `${a.label} (requires install)`
}
}
candidates.push(a as any)
}
}
candidates.sort((a, b) => {
if (a.label > b.label) {
return 1
} else {
return -1
}
})
return candidates
}
}
declare module '../../features/features.types' {
interface FeatureMap {
test: TestFeature
}
interface FeatureOptionsMap {
test: undefined
}
}