Skip to content

Commit 095bbb1

Browse files
authored
Multi-Root Workspace Support (#173)
This enables multiple root workspaces to exist. There is more to do but this is a good first start. A separate test controller and pwsh instance are started for each workspace so they operate independently and don't clobber each other. PowerShell extension debugging currently all runs through the main terminal window, the goal however is to attach to a process in the future.
1 parent 7b21a03 commit 095bbb1

14 files changed

+437
-251
lines changed

.vscode/launch.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,33 @@
3232
"smartStep": true,
3333
"preLaunchTask": "build-watch"
3434
},
35+
{
36+
"name": "Run Extension Multi-Root Workspace",
37+
"type": "extensionHost",
38+
"request": "launch",
39+
"args": [
40+
"--extensionDevelopmentPath=${workspaceFolder}",
41+
"--profile=Debug-PesterExtension",
42+
"--trace-warnings",
43+
"${workspaceFolder}/sample/Tests.code-workspace"
44+
],
45+
"env": {
46+
"VSCODE_DEBUG_MODE": "true"
47+
},
48+
"outFiles": [
49+
"${workspaceFolder}/dist/**/*.js"
50+
],
51+
"sourceMaps": true,
52+
"autoAttachChildProcesses": true,
53+
"skipFiles": [
54+
"<node_internals>/**",
55+
"**/extensionHostProcess.js",
56+
"**/.vscode/extensions/**"
57+
],
58+
"showAsyncStacks": true,
59+
"smartStep": true,
60+
"preLaunchTask": "build-watch"
61+
},
3562
{
3663
"name": "Test Extension",
3764
"type": "node",

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,9 @@
117117
"readline-transform": "^1.0.0",
118118
"tslog": "^4.8.2"
119119
},
120+
"extensionDependencies": [
121+
"ms-vscode.powershell"
122+
],
120123
"icon": "images/pesterlogo.png",
121124
"badges": [
122125
{

sample/Tests.2/Empty.Tests.ps1

Whitespace-only changes.

sample/Tests.2/True.Tests.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Describe 'SimpleTest' {
2+
It 'True should be true' {
3+
$true | Should -Be $true
4+
}
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Context 'Test3 Unique Context' {
2+
It 'Test3UniqueIt' {
3+
$true
4+
}
5+
}

sample/Tests.3/True.Tests.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Describe 'SimpleTest' {
2+
It 'True should be true' {
3+
$true | Should -Be $true
4+
}
5+
}

sample/Tests.code-workspace

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"folders": [
3+
{
4+
"path": "Tests"
5+
},
6+
{
7+
"path": "Tests.2"
8+
},
9+
{
10+
"path": "Tests.3"
11+
}
12+
],
13+
"settings": {
14+
"git.openRepositoryInParentFolders": "never",
15+
"extensions.ignoreRecommendations": true,
16+
}
17+
}

src/extension.ts

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,81 @@
1-
import { type ExtensionContext, window, workspace, commands } from 'vscode'
2-
3-
import { PesterTestController } from './pesterTestController'
1+
import { type ExtensionContext, window, workspace, Disposable, WorkspaceConfiguration, Extension } from 'vscode'
42
import {
5-
getPowerShellExtension,
6-
PowerShellExtensionClient
3+
waitForPowerShellExtension,
4+
PowerShellExtensionClient,
5+
IPowerShellExtensionClient
76
} from './powershellExtensionClient'
7+
import { watchWorkspace } from './workspaceWatcher'
8+
import log, { VSCodeLogOutputChannelTransport } from './log'
89

910
export async function activate(context: ExtensionContext) {
10-
// PowerShell extension is a prerequisite, but we allow either preview or normal, which is why we do this instead of
11-
// leverage package.json dependencies
12-
const powershellExtension = getPowerShellExtension(context)
13-
14-
// Short circuit this activate call if we didn't find a PowerShell Extension. Another activate will be triggered once
15-
// the powershell extension is available
16-
if (powershellExtension === undefined) {
17-
return
11+
12+
log.attachTransport(new VSCodeLogOutputChannelTransport('Pester').transport)
13+
14+
subscriptions = context.subscriptions
15+
16+
// PowerShell extension is a prerequisite
17+
const powershellExtension = await waitForPowerShellExtension()
18+
19+
pesterExtensionContext = {
20+
extensionContext: context,
21+
powerShellExtension: powershellExtension,
22+
powershellExtensionPesterConfig: PowerShellExtensionClient.GetPesterSettings()
1823
}
1924

25+
promptForPSLegacyCodeLensDisable()
26+
27+
await watchWorkspace()
28+
29+
// TODO: Rig this up for multiple workspaces
30+
// const stopPowerShellCommand = commands.registerCommand('pester.stopPowershell', () => {
31+
// if (controller.stopPowerShell()) {
32+
// void window.showInformationMessage('PowerShell background process stopped.')
33+
// } else {
34+
// void window.showWarningMessage('No PowerShell background process was running !')
35+
// }
36+
// })
37+
38+
// context.subscriptions.push(
39+
// controller,
40+
// stopPowerShellCommand,
41+
// )
42+
43+
}
44+
45+
/** Register a Disposable with the extension so that it can be cleaned up if the extension is disabled */
46+
export function registerDisposable(disposable: Disposable) {
47+
if (subscriptions == undefined) {
48+
throw new Error('registerDisposable called before activate. This should never happen and is a bug.')
49+
}
50+
subscriptions.push(disposable)
51+
}
52+
53+
export function registerDisposables(disposables: Disposable[]) {
54+
subscriptions.push(Disposable.from(...disposables))
55+
}
56+
57+
let subscriptions: Disposable[]
58+
59+
type PesterExtensionContext = {
60+
extensionContext: ExtensionContext
61+
powerShellExtension: Extension<IPowerShellExtensionClient>
62+
powershellExtensionPesterConfig: WorkspaceConfiguration
63+
}
64+
65+
/** Get the activated extension context */
66+
export function getPesterExtensionContext() {
67+
if (pesterExtensionContext == undefined) {
68+
throw new Error('Pester Extension Context attempted to be fetched before activation. This should never happen and is a bug')
69+
}
70+
71+
return pesterExtensionContext
72+
}
73+
let pesterExtensionContext: PesterExtensionContext
74+
75+
function promptForPSLegacyCodeLensDisable() {
2076
// Disable PowerShell codelens setting if present
2177
const config = PowerShellExtensionClient.GetPesterSettings()
78+
2279
const psExtensionCodeLensSetting: boolean = config.codeLens
2380

2481
const suppressCodeLensNotice = workspace.getConfiguration('pester').get<boolean>('suppressCodeLensNotice') ?? false
@@ -50,18 +107,4 @@ export async function activate(context: ExtensionContext) {
50107
}
51108
})
52109
}
53-
54-
const controller = new PesterTestController(powershellExtension, context)
55-
const stopPowerShellCommand = commands.registerCommand('pester.stopPowershell', () => {
56-
if (controller.stopPowerShell()) {
57-
void window.showInformationMessage('PowerShell background process stopped.')
58-
} else {
59-
void window.showWarningMessage('No PowerShell background process was running !')
60-
}
61-
})
62-
63-
context.subscriptions.push(
64-
controller,
65-
stopPowerShellCommand,
66-
)
67110
}

src/log.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ type DefaultTSLogLevel =
1818
| "FATAL"
1919

2020
export class VSCodeLogOutputChannelTransport {
21-
// TODO: Make this lazy initialize so the window only starts once a log has been requested
2221
/** Used to ensure multiple registered transports that request the same name use the same output window. NOTE: You can still get duplicate windows if you register channels outside this transport */
2322
private static readonly channels = new Map<string, LogOutputChannel>()
2423
private readonly name: string
@@ -80,6 +79,9 @@ log.attachTransport(new VSCodeOutputChannelTransport('Pester'))
8079
const log = new Logger<DefaultLog>({
8180
name: 'default',
8281
type: 'pretty',
82+
prettyErrorLoggerNameDelimiter: "-",
83+
prettyErrorParentNamesSeparator: "-",
84+
stylePrettyLogs: true,
8385
argumentsArrayName: "args",
8486
overwrite: {
8587
transportFormatted: () => { return } // We want pretty formatting but no default output

0 commit comments

Comments
 (0)