diff --git a/src/tiPS/Private/TipsAlreadyShownFunctions.Tests.ps1 b/src/tiPS/Private/TipsAlreadyShownFunctions.Tests.ps1 index a2056f3..0902164 100644 --- a/src/tiPS/Private/TipsAlreadyShownFunctions.Tests.ps1 +++ b/src/tiPS/Private/TipsAlreadyShownFunctions.Tests.ps1 @@ -144,4 +144,49 @@ InModuleScope -ModuleName tiPS { # Must use InModuleScope to call private functi } } } + + Describe 'Calling GetLastShownTipId' { + BeforeEach { + # Use a temp configuration data directory instead of reading/overwriting the current user's configuration. + Mock -CommandName Get-TiPSDataDirectoryPath -MockWith { + [string] $directoryPath = "$TestDrive/tiPS" # Use $TestDrive variable so .NET methods can resolve the path. + if (-not (Test-Path -Path $directoryPath -PathType Container)) + { + New-Item -Path $directoryPath -ItemType Directory -Force > $null + } + return $directoryPath + } + + ClearTipIdsAlreadyShown + } + + Context 'When no tips have been shown' { + It 'Should return null' { + $lastShownTipId = GetLastShownTipId + $lastShownTipId | Should -BeNullOrEmpty + } + } + + Context 'When tips have been shown' { + It 'Should return the last shown tip ID' { + AppendTipIdToTipIdsAlreadyShown -TipId 'Tip1' + AppendTipIdToTipIdsAlreadyShown -TipId 'Tip2' + AppendTipIdToTipIdsAlreadyShown -TipId 'Tip3' + + $lastShownTipId = GetLastShownTipId + + $lastShownTipId | Should -Be 'Tip3' + } + } + + Context 'When only one tip has been shown' { + It 'Should return that tip ID' { + AppendTipIdToTipIdsAlreadyShown -TipId 'OnlyTip' + + $lastShownTipId = GetLastShownTipId + + $lastShownTipId | Should -Be 'OnlyTip' + } + } + } } diff --git a/src/tiPS/Private/TipsAlreadyShownFunctions.ps1 b/src/tiPS/Private/TipsAlreadyShownFunctions.ps1 index e604f10..cc409aa 100644 --- a/src/tiPS/Private/TipsAlreadyShownFunctions.ps1 +++ b/src/tiPS/Private/TipsAlreadyShownFunctions.ps1 @@ -64,6 +64,27 @@ function ClearTipIdsAlreadyShown [System.IO.File]::WriteAllText($tipIdsAlreadyShownFilePath, [string]::Empty) } +function GetLastShownTipId +{ + [CmdletBinding()] + [OutputType([string])] + Param() + + [string[]] $tipIdsAlreadyShown = ReadTipIdsAlreadyShownOrDefault + if ($tipIdsAlreadyShown.Count -eq 0) + { + return $null + } + + [string] $lastShownTipId = $tipIdsAlreadyShown | Select-Object -Last 1 + if ([string]::IsNullOrWhiteSpace($lastShownTipId)) + { + return $null + } + + return $lastShownTipId +} + function GetTipIdsAlreadyShownFilePath { [CmdletBinding()] diff --git a/src/tiPS/Public/Get-PowerShellTip.Tests.ps1 b/src/tiPS/Public/Get-PowerShellTip.Tests.ps1 index 11b2ec4..424b1af 100644 --- a/src/tiPS/Public/Get-PowerShellTip.Tests.ps1 +++ b/src/tiPS/Public/Get-PowerShellTip.Tests.ps1 @@ -140,6 +140,60 @@ Describe 'Get-PowerShellTip' { $tip.CreatedDate | Should -Be $oldestTipDate } } + + Context 'Given the Previous switch' { + BeforeEach { + # Use a temp configuration data directory instead of reading/overwriting the current user's configuration. + Mock -ModuleName $ModuleName -CommandName Get-TiPSDataDirectoryPath -MockWith { + [string] $directoryPath = "$TestDrive/tiPS" # Use $TestDrive variable so .NET methods can resolve the path. + if (-not (Test-Path -Path $directoryPath -PathType Container)) + { + New-Item -Path $directoryPath -ItemType Directory -Force > $null + } + return $directoryPath + } + } + + It 'Should return the last shown tip' { + InModuleScope -ModuleName $ModuleName { + # Show a tip to populate the last shown tip ID + AppendTipIdToTipIdsAlreadyShown -TipId '2023-07-16-powershell-is-open-source' + } + + $tip = Get-PowerShellTip -Previous + + $tip.Id | Should -Be '2023-07-16-powershell-is-open-source' + } + + It 'Should write an error when no tips have been shown yet' { + InModuleScope -ModuleName $ModuleName { + # Clear all shown tips + ClearTipIdsAlreadyShown + } + + Get-PowerShellTip -Previous -ErrorVariable error -ErrorAction SilentlyContinue > $null + $error | Should -Not -BeNullOrEmpty + } + + It 'Should not mark the tip as shown again' { + InModuleScope -ModuleName $ModuleName { + # Show a tip to populate the last shown tip ID + AppendTipIdToTipIdsAlreadyShown -TipId '2023-07-16-powershell-is-open-source' + + # Get count of shown tips before calling -Previous + [string[]] $tipIdsBeforePrevious = ReadTipIdsAlreadyShownOrDefault + + # Get the previous tip + Get-PowerShellTip -Previous > $null + + # Get count of shown tips after calling -Previous + [string[]] $tipIdsAfterPrevious = ReadTipIdsAlreadyShownOrDefault + + # The count should be the same + $tipIdsAfterPrevious.Count | Should -Be $tipIdsBeforePrevious.Count + } + } + } } InModuleScope -ModuleName tiPS { # Must use InModuleScope to access script-level variables of the module. diff --git a/src/tiPS/Public/Get-PowerShellTip.ps1 b/src/tiPS/Public/Get-PowerShellTip.ps1 index 82f6be8..35b3894 100644 --- a/src/tiPS/Public/Get-PowerShellTip.ps1 +++ b/src/tiPS/Public/Get-PowerShellTip.ps1 @@ -20,6 +20,11 @@ function Get-PowerShellTip When this parameter is used, the list of tips shown is not updated. + .PARAMETER Previous + Return the last tip that was shown. + + When this parameter is used, the list of tips shown is not updated. + .INPUTS You can pipe a [string] of the ID of the tip to retrieve, or a PSCustomObject with a [string] 'Id' property. @@ -43,6 +48,11 @@ function Get-PowerShellTip Get all tips. + .EXAMPLE + Get-PowerShellTip -Previous + + Get the last tip that was shown. + .EXAMPLE '2023-07-16-powershell-is-open-source' | Get-PowerShellTip @@ -56,6 +66,7 @@ function Get-PowerShellTip [CmdletBinding(DefaultParameterSetName = 'Default')] [OutputType([tiPS.PowerShellTip], ParameterSetName = 'Default')] + [OutputType([tiPS.PowerShellTip], ParameterSetName = 'Previous')] [OutputType([System.Collections.Specialized.OrderedDictionary], ParameterSetName = 'AllTips')] Param ( @@ -64,8 +75,11 @@ function Get-PowerShellTip HelpMessage = 'The ID of the tip to retrieve. If not supplied, a random tip will be returned.')] [string] $Id, - [Parameter(ParameterSetName = 'AllTips', Mandatory = $false, HelpMessage = 'Return all tips.')] - [switch] $AllTips + [Parameter(ParameterSetName = 'AllTips', HelpMessage = 'Return all tips.')] + [switch] $AllTips, + + [Parameter(ParameterSetName = 'Previous', HelpMessage = 'Return the last tip that was shown.')] + [switch] $Previous ) Process @@ -75,6 +89,27 @@ function Get-PowerShellTip return ReadAllPowerShellTipsFromJsonFile } + if ($Previous) + { + $lastShownTipId = GetLastShownTipId + if ($null -eq $lastShownTipId) + { + Write-Error "No tips have been shown yet." + return + } + + [hashtable] $allTips = ReadAllPowerShellTipsFromJsonFile + [bool] $tipIdDoesNotExist = (-not $allTips.Contains($lastShownTipId)) + if ($tipIdDoesNotExist) + { + Write-Error "The last shown tip with ID '$lastShownTipId' no longer exists." + return + } + + [tiPS.PowerShellTip] $tip = $allTips[$lastShownTipId] + return $tip + } + [bool] $allTipsHaveBeenShown = $script:UnshownTips.Count -eq 0 if ($allTipsHaveBeenShown) { diff --git a/src/tiPS/Public/Write-PowerShellTip.Tests.ps1 b/src/tiPS/Public/Write-PowerShellTip.Tests.ps1 index 42d54d5..78bc711 100644 --- a/src/tiPS/Public/Write-PowerShellTip.Tests.ps1 +++ b/src/tiPS/Public/Write-PowerShellTip.Tests.ps1 @@ -56,4 +56,40 @@ Describe 'Write-PowerShellTip' { Should -InvokeVerifiable # Verify that the Write-Host mock was called. } } + + Context 'Given the Previous switch' { + BeforeEach { + # Use a temp configuration data directory instead of reading/overwriting the current user's configuration. + Mock -ModuleName $ModuleName -CommandName Get-TiPSDataDirectoryPath -MockWith { + [string] $directoryPath = "$TestDrive/tiPS" # Use $TestDrive variable so .NET methods can resolve the path. + if (-not (Test-Path -Path $directoryPath -PathType Container)) + { + New-Item -Path $directoryPath -ItemType Directory -Force > $null + } + return $directoryPath + } + } + + It 'Should write the last shown tip without error' { + InModuleScope -ModuleName $ModuleName { + # Show a tip to populate the last shown tip ID + AppendTipIdToTipIdsAlreadyShown -TipId '2023-07-16-powershell-is-open-source' + } + + $Error.Clear() + { Write-PowerShellTip -Previous } | Should -Not -Throw + Should -InvokeVerifiable # Verify that the Write-Host mock was called. + $Error[0] | Should -BeNullOrEmpty + } + + It 'Should write an error when no tips have been shown yet' { + InModuleScope -ModuleName $ModuleName { + # Clear all shown tips + ClearTipIdsAlreadyShown + } + + Write-PowerShellTip -Previous -ErrorVariable tipError -ErrorAction SilentlyContinue > $null + $tipError | Should -Not -BeNullOrEmpty + } + } } diff --git a/src/tiPS/Public/Write-PowerShellTip.ps1 b/src/tiPS/Public/Write-PowerShellTip.ps1 index ff8bac2..45fa093 100644 --- a/src/tiPS/Public/Write-PowerShellTip.ps1 +++ b/src/tiPS/Public/Write-PowerShellTip.ps1 @@ -14,6 +14,10 @@ function Write-PowerShellTip The ID of the tip to write. If not supplied, a random tip will be written. If no tip with the specified ID exists, an error is written. + .PARAMETER Previous + Write the last tip that was shown instead of a new tip. + The tip is not marked as shown again when using this parameter. + .INPUTS You can pipe a [string] of the ID of the tip to write, or a PSCustomObject with a [string] 'Id' property. @@ -29,20 +33,36 @@ function Write-PowerShellTip Write-PowerShellTip -Id '2023-07-16-powershell-is-open-source' Write the tip with the specified ID. + + .EXAMPLE + Write-PowerShellTip -Previous + + Write the last tip that was shown. #> - [CmdletBinding()] + [CmdletBinding(DefaultParameterSetName = 'Default')] [Alias('Tips')] [OutputType([void])] Param ( - [Parameter(Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, + [Parameter(ParameterSetName = 'Default', Mandatory = $false, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, HelpMessage = 'The ID of the tip to write. If not supplied, a random tip will be written.')] - [string] $Id + [string] $Id, + + [Parameter(ParameterSetName = 'Previous', HelpMessage = 'Write the last tip that was shown.')] + [switch] $Previous ) Process { - [tiPS.PowerShellTip] $tip = Get-PowerShellTip -Id $Id + if ($Previous) + { + [tiPS.PowerShellTip] $tip = Get-PowerShellTip -Previous + } + else + { + [tiPS.PowerShellTip] $tip = Get-PowerShellTip -Id $Id + } + if ($null -ne $tip) { WritePowerShellTipToTerminal -Tip $tip