Skip to content

Commit

Permalink
🎉 Add Test AutoRun and fix up test change detection (#63)
Browse files Browse the repository at this point in the history
This enables test autorun by default, you can disable it in settings if you prefer.
  • Loading branch information
JustinGrote authored Sep 16, 2021
1 parent 3f72f50 commit 321ec30
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 22 deletions.
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
"pester.hideSkippedBecauseMessages": {
"type": "boolean",
"markdownDescription": "If a skipped test is inconclusive or `Set-ItResult` had the `-Because` parameter specified, those are surfaced as test errors for easy viewing. Enable this setting if you prefer these to be displayed simply as skipped. **NOTE:** If you do this then you cannot see any BECAUSE messages without looking directly at the Pester output."
},
"pester.autoRunOnSave": {
"type": "boolean",
"default": true,
"markdownDescription": "Automatically runs Pester test files upon changes being detected on save. Uncheck this box to disable this behavior."
}
}
}
Expand Down
7 changes: 0 additions & 7 deletions src/dotnetNamedPipeServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,6 @@ import { Disposable, EventEmitter } from 'vscode'

/** Provides a simple server listener to a .NET named pipe. This is useful as a IPC method to child processes like a PowerShell Script */
export class DotnetNamedPipeServer implements Disposable {
// We will use this emitter to notify any subscribers of new objects to process
// TODO: Tighten up the types here
// TODO: Optionally skip the json processing?
// TODO: Make this not depend on vscode and use a general eventEmitter, then make an inherited class that is vscode specific

// FIXME: One socket per PSIC invocation and graceful cleanup

private readonly server: Server
constructor(
public name: string = 'NodeNamedPipe-' + Math.random().toString(36)
Expand Down
54 changes: 39 additions & 15 deletions src/pesterTestController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
TestItem,
TestMessage,
TestRun,
TestRunProfile,
TestRunProfileKind,
TestRunRequest,
tests,
Expand All @@ -35,13 +36,14 @@ import {
} from './powershellExtensionClient'
import { findTestItem } from './testItemUtils'
import debounce = require('debounce-promise')

/** A wrapper for the vscode TestController API specific to PowerShell Pester Test Suite.
* This should only be instantiated once in the extension activate method.
*/
export class PesterTestController implements Disposable {
private ps: PowerShell | undefined
private powerShellExtensionClient: PowerShellExtensionClient | undefined
private readonly runProfile: TestRunProfile
private readonly debugProfile: TestRunProfile
constructor(
private readonly powershellExtension: Extension<IPowerShellExtensionClient>,
private readonly context: ExtensionContext,
Expand All @@ -54,13 +56,13 @@ export class PesterTestController implements Disposable {
// wire up our custom handlers to the managed instance
// HACK: https://github.com/microsoft/vscode/issues/107467#issuecomment-869261078
testController.resolveHandler = testItem => this.resolveHandler(testItem)
testController.createRunProfile(
this.runProfile = testController.createRunProfile(
'Run',
TestRunProfileKind.Run,
this.testHandler.bind(this),
true
)
testController.createRunProfile(
this.debugProfile = testController.createRunProfile(
'Debug',
TestRunProfileKind.Debug,
this.testHandler.bind(this),
Expand All @@ -73,15 +75,23 @@ export class PesterTestController implements Disposable {

/** The test controller API calls this whenever it needs to get the resolveChildrenHandler
* for Pester, this is only relevant to TestFiles as this is pester's lowest level of test resolution
* @param testItem - The test item to get the resolveChildrenHandler for
* @param force - If true, force the test to be re-resolved
*/
private async resolveHandler(testItem: TestItem | undefined) {
private async resolveHandler(
testItem: TestItem | undefined,
force?: boolean
): Promise<void> {
// If testitem is undefined, this is a signal to initialize the controller
if (testItem === undefined) {
log.info(
'Initializing Pester Test Controller and watching for Pester Files'
)
Promise.all([this.watchWorkspaces(), this.returnServer.listen()])
await this.watchWorkspaces()
return
} else {
// Reset any errors previously reported.
testItem.error = undefined
}

const testItemData = TestData.get(testItem)
Expand Down Expand Up @@ -119,10 +129,10 @@ export class PesterTestController implements Disposable {
this.testController.items.get(testDef.parent)
if (parent === undefined && testDef.error === undefined) {
log.fatal(
`Test Item ${testDef.label} does not have a parent. This is a bug and should not happen`
`Test Item ${testDef.label} does not have a TestFile parent or its parent was not sent by PesterInterface first. This is a bug and should not happen`
)
throw new Error(
`Test Item ${testDef.label} does not have a parent. This is a bug and should not happen`
`Test Item ${testDef.label} does not have a TestFile parent or its parent was not sent by PesterInterface first. This is a bug and should not happen`
)
}
const newTestItem = this.testController.createTestItem(
Expand All @@ -145,9 +155,10 @@ export class PesterTestController implements Disposable {
}

if (
testItemData instanceof TestFile &&
!testItemData.testsDiscovered &&
!testItem.busy
(testItemData instanceof TestFile &&
!testItemData.testsDiscovered &&
!testItem.busy) ||
(testItemData instanceof TestFile && force)
) {
// Indicate the start of a discovery, will cause the UI to show a spinner
testItem.busy = true
Expand All @@ -158,7 +169,6 @@ export class PesterTestController implements Disposable {
// For discovery we don't care about the terminal output, thats why no assignment to var here
await this.startTestDiscovery(testItemDiscoveryHandler)
testItem.busy = false
testItemData.testsDiscovered = true
} else {
log.info(
`Resolve for ${testItem.label} requested but it is already resolving/resolved`
Expand Down Expand Up @@ -454,12 +464,26 @@ export class PesterTestController implements Disposable {
const pattern = new RelativePattern(workspaceFolder, pathToWatchItem)
const testWatcher = workspace.createFileSystemWatcher(pattern)
const tests = this.testController.items
testWatcher.onDidCreate(uri =>
testWatcher.onDidCreate(uri => {
log.info(`File created: ${uri.toString()}`)
tests.add(TestFile.getOrCreate(testController, uri))
)
testWatcher.onDidDelete(uri => tests.delete(uri.toString()))
})
testWatcher.onDidDelete(uri => {
log.info(`File deleted: ${uri.toString()}`)
tests.delete(TestFile.getOrCreate(testController, uri).id)
})
testWatcher.onDidChange(uri => {
this.resolveHandler(TestFile.getOrCreate(testController, uri))
log.info(`File saved: ${uri.toString()}`)
const savedFile = TestFile.getOrCreate(testController, uri)
this.resolveHandler(savedFile, true).then(() => {
if (
workspace.getConfiguration('pester').get<boolean>('autoRunOnSave')
) {
this.testHandler(
new TestRunRequest([savedFile], undefined, this.runProfile)
)
}
})
})

// TODO: Fix this for non-file based pester tests and
Expand Down

0 comments on commit 321ec30

Please sign in to comment.