Skip to content

Commit 6efcbc0

Browse files
fix: handle empty PowerShell profiles (#29)
1 parent 1614d8a commit 6efcbc0

2 files changed

Lines changed: 108 additions & 6 deletions

File tree

installer/ProfileIntegration.ps1

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,11 @@ $startMarker = '# >>> DevNav >>>'
1515
$endMarker = '# <<< DevNav <<<'
1616
$importLine = "Import-Module '$($ModulePath.Replace("'", "''"))'"
1717
$block = @($startMarker, $importLine, $endMarker)
18-
$content = if (Test-Path -LiteralPath $profilePath -PathType Leaf) {
19-
@(Get-Content -LiteralPath $profilePath)
20-
}
21-
else {
22-
@()
23-
}
18+
$content = @(
19+
if (Test-Path -LiteralPath $profilePath -PathType Leaf) {
20+
Get-Content -LiteralPath $profilePath
21+
}
22+
)
2423

2524
$start = [Array]::IndexOf($content, $startMarker)
2625
$end = [Array]::IndexOf($content, $endMarker)
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
BeforeAll {
2+
$repositoryRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot)
3+
$scriptPath = Join-Path $repositoryRoot 'installer\ProfileIntegration.ps1'
4+
$realProfilePath = $PROFILE.CurrentUserCurrentHost
5+
$realProfileSnapshot = if (Test-Path -LiteralPath $realProfilePath -PathType Leaf) {
6+
[pscustomobject]@{ Exists = $true; Hash = (Get-FileHash -LiteralPath $realProfilePath -Algorithm SHA256).Hash }
7+
}
8+
else {
9+
[pscustomobject]@{ Exists = $false; Hash = $null }
10+
}
11+
Set-Item Function:\global:Invoke-ProfileIntegrationRegression {
12+
param([switch] $Install, [string] $ModulePath)
13+
$arguments = @{ Install = $Install; ModulePath = $ModulePath }
14+
& $global:profileIntegrationScriptPath @arguments
15+
[pscustomobject]@{
16+
Content = @($global:profileSetContent)
17+
Encoding = $global:profileSetEncoding
18+
NewItemPaths = @($global:profileNewItemPaths)
19+
}
20+
}
21+
}
22+
23+
AfterAll {
24+
$currentSnapshot = if (Test-Path -LiteralPath $realProfilePath -PathType Leaf) {
25+
[pscustomobject]@{ Exists = $true; Hash = (Get-FileHash -LiteralPath $realProfilePath -Algorithm SHA256).Hash }
26+
}
27+
else {
28+
[pscustomobject]@{ Exists = $false; Hash = $null }
29+
}
30+
$currentSnapshot.Exists | Should -Be $realProfileSnapshot.Exists
31+
$currentSnapshot.Hash | Should -Be $realProfileSnapshot.Hash
32+
Remove-Item Function:\global:Invoke-ProfileIntegrationRegression -ErrorAction SilentlyContinue
33+
}
34+
35+
Describe 'ProfileIntegration.ps1 regression behavior' {
36+
BeforeEach {
37+
$global:profileIntegrationScriptPath = $scriptPath
38+
$global:profileExists = $true
39+
$global:profileExistingContent = @()
40+
$global:profileSetContent = $null
41+
$global:profileSetEncoding = $null
42+
$global:profileNewItemPaths = [System.Collections.Generic.List[string]]::new()
43+
44+
Mock Test-Path {
45+
param($LiteralPath, $PathType)
46+
if ($LiteralPath -eq $PROFILE.CurrentUserCurrentHost) { return $global:profileExists }
47+
return $false
48+
}
49+
Mock Get-Content {
50+
param($LiteralPath)
51+
if ($LiteralPath -eq $PROFILE.CurrentUserCurrentHost) { return $global:profileExistingContent }
52+
throw "Unexpected Get-Content path: $LiteralPath"
53+
}
54+
Mock New-Item {
55+
param($Path)
56+
[void] $global:profileNewItemPaths.Add($Path)
57+
[pscustomobject]@{ FullName = $Path }
58+
}
59+
Mock Set-Content {
60+
param($LiteralPath, $Value, $Encoding)
61+
$global:profileSetContent = @($Value | ForEach-Object { $_ })
62+
$global:profileSetEncoding = $Encoding
63+
}
64+
Mock Remove-Item { param($LiteralPath) }
65+
}
66+
67+
It 'installs successfully when the profile does not exist' {
68+
$global:profileExists = $false
69+
$modulePath = 'C:\Program Files\DevNav\DevNav.psm1'
70+
$result = Invoke-ProfileIntegrationRegression -Install -ModulePath $modulePath
71+
72+
$result.Encoding | Should -BeOfType ([System.Text.UTF8Encoding])
73+
$result.Encoding.GetPreamble().Length | Should -Be 0
74+
$result.Content | Should -Contain '# >>> DevNav >>>'
75+
$result.Content | Should -Contain "Import-Module '$modulePath'"
76+
$result.Content | Should -Contain '# <<< DevNav <<<'
77+
@($result.Content | Where-Object { $_ -eq '# >>> DevNav >>>' }).Count | Should -Be 1
78+
@($result.Content | Where-Object { $_ -eq '# <<< DevNav <<<' }).Count | Should -Be 1
79+
Should -Invoke New-Item -Times 2 -Scope It
80+
Should -Invoke Set-Content -Times 1 -Scope It
81+
}
82+
83+
It 'preserves a single-line profile and appends one DevNav block' {
84+
$global:profileExistingContent = @('Set-Alias foo bar')
85+
$modulePath = 'C:\Program Files\DevNav\DevNav.psm1'
86+
$result = Invoke-ProfileIntegrationRegression -Install -ModulePath $modulePath
87+
88+
$result.Content | Should -Contain 'Set-Alias foo bar'
89+
@($result.Content | Where-Object { $_ -eq '# >>> DevNav >>>' }).Count | Should -Be 1
90+
@($result.Content | Where-Object { $_ -eq '# <<< DevNav <<<' }).Count | Should -Be 1
91+
$result.Content | Should -Contain "Import-Module '$modulePath'"
92+
}
93+
94+
It 'preserves a small multi-line profile and appends one DevNav block' {
95+
$global:profileExistingContent = @('line before', 'line after')
96+
$result = Invoke-ProfileIntegrationRegression -Install -ModulePath 'C:\Program Files\DevNav\DevNav.psm1'
97+
98+
$result.Content | Should -Contain 'line before'
99+
$result.Content | Should -Contain 'line after'
100+
@($result.Content | Where-Object { $_ -eq '# >>> DevNav >>>' }).Count | Should -Be 1
101+
@($result.Content | Where-Object { $_ -eq '# <<< DevNav <<<' }).Count | Should -Be 1
102+
}
103+
}

0 commit comments

Comments
 (0)