@sprucelabs/spruce-cli
Version:
Command line interface for building Spruce skills.
191 lines (158 loc) • 5.99 kB
text/typescript
import { diskUtil, namesUtil } from '@sprucelabs/spruce-skill-utils'
import { test, assert, errorAssert, generateId } from '@sprucelabs/test-utils'
import CreateAppAction from '../../../features/view/actions/CreateAppAction'
import AbstractSkillTest from '../../../tests/AbstractSkillTest'
import testUtil from '../../../tests/utilities/test.utility'
export default class CreatingAnAppControllerTest extends AbstractSkillTest {
protected static skillCacheKey = 'views'
private static acPath: string
private static skillNamespace = generateId().replace(/\d/g, '')
private static action: CreateAppAction
private static lastExecution: {
actionCode: string
featureCode: string
}[] = []
protected static async beforeAll(): Promise<void> {
await super.beforeAll()
this.acPath = this.resolvePath('src/App.ac.ts')
const pgk = this.Service('pkg')
pgk.set({ path: 'skill.namespace', value: this.skillNamespace })
}
protected static async beforeEach(): Promise<void> {
await super.beforeEach()
this.action = this.Action('view', 'createApp')
await this.cli.on('feature.will-execute', (payload) => {
this.lastExecution.push(payload)
})
}
()
protected static async generatesExpectedFile() {
const results = await this.execute()
assert.isFalsy(results.errors)
testUtil.assertFileByPathInGeneratedFiles(this.acPath, results.files)
assert.doesInclude(results.files, {
action: 'generated',
name: 'App.ac.ts',
path: this.acPath,
})
assert.doesInclude(results.files, {
action: 'generated',
name: 'views.ts',
path: this.combinedViewsPath,
})
}
()
protected static async syncsViewsOnCreation() {
assert.doesInclude(this.lastExecution, {
actionCode: 'sync',
featureCode: 'view',
})
assert.isEqual(this.lastExecution[1].actionCode, 'sync')
assert.isTrue(
diskUtil.doesFileExist(this.combinedViewsPath),
'views.ts does not exist'
)
}
()
protected static async throwsIfAppAlreadyExists() {
await this.executeAndAssertThrowsAvcExists()
}
()
protected static async acIncludesExpectedContents() {
const contents = diskUtil.readFile(this.acPath)
assert.doesInclude(
contents,
`export default class ${this.className} extends AbstractAppController`
)
assert.doesInclude(
contents,
'public async load(_options: AppControllerLoadOptions)'
)
}
()
protected static async generatedFilesAreValid() {
await this.Service('typeChecker').check(this.acPath)
await this.assertCombinedViewsFileIsValid()
}
()
protected static async doesNotDropAppIntoSkillViewTypes() {
const expected = `interface ViewControllerMap {}`
this.assertCombinedViewsContains(expected)
}
()
protected static async callsHeartwoodImportFunctionAsExpected() {
this.assertCombinedViewsContains(
`heartwood({ vcs, pluginsByName, App: ${this.className} })`
)
}
()
protected static async combineViewsTypesAsExpected() {
const expected = `interface AppControllerMap {
'${this.skillNamespace}' : ${this.className}
}`
this.assertCombinedViewsContains(expected)
}
()
protected static async throwsEvenIfAppMoves() {
const destination = this.resolvePath(`src/${generateId()}/App.ac.ts`)
diskUtil.moveFile(this.acPath, destination)
await this.executeAndAssertThrowsAvcExists()
this.acPath = destination
}
()
protected static async canRenameAppAndItUpdatesCombinedViews() {
const contents = diskUtil.readFile(this.acPath)
const updatedContents = contents.replace(
this.className,
'NewAppControllerImpl'
)
diskUtil.writeFile(this.acPath, updatedContents)
await this.Action('view', 'sync').execute({})
this.assertCombinedViewsContains(
'heartwood({ vcs, pluginsByName, App: NewAppControllerImpl })'
)
await this.assertCombinedViewsFileIsValid()
}
()
protected static async importsAppFromCombinedViews() {
const testScript =
`import { App } from '#` +
`spruce/views/views'
const app = new App({} as any)
console.log(app)
if (App.id !== '${this.skillNamespace}') {
throw new Error('App.id is not correct')
}`
const testScriptPath = this.resolvePath('src/test.ts')
diskUtil.writeFile(testScriptPath, testScript)
await this.Service('typeChecker').check(testScriptPath)
}
private static async assertCombinedViewsFileIsValid() {
await this.Service('typeChecker').check(this.combinedViewsPath)
}
private static assertCombinedViewsContains(expected: string) {
const contents = diskUtil.readFile(this.combinedViewsPath)
assert.doesInclude(
contents.replace(/\s/g, ''),
expected.replace(/\s/g, ''),
`The combined views file does not include the expected contents.`
)
}
private static get combinedViewsPath() {
return this.resolveHashSprucePath('views', 'views.ts')
}
private static get className() {
return `${namesUtil.toPascal(this.skillNamespace)}AppController`
}
private static async executeAndAssertThrowsAvcExists() {
const results = await this.execute()
assert.isTruthy(results.errors)
errorAssert.assertError(
results.errors[0],
'APP_CONTROLLER_ALREADY_EXISTS'
)
}
private static async execute() {
return await this.action.execute()
}
}