diff --git a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs index b19d75e7ced..c0055b8dab6 100644 --- a/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs +++ b/src/System.Management.Automation/engine/CommandCompletion/CompletionCompleters.cs @@ -5919,7 +5919,20 @@ private static string GetNamespaceToRemove(CompletionContext context, TypeComple internal static List CompleteHelpTopics(CompletionContext context) { var results = new List(); - var dirPath = Utils.GetApplicationBase(Utils.DefaultPowerShellShellID) + StringLiterals.DefaultPathSeparator + CultureInfo.CurrentCulture.Name; + var searchPaths = new List(); + var currentCulture = CultureInfo.CurrentCulture.Name; + + // Add the user scope path first, since it is searched in order. + var userHelpRoot = Path.Combine(HelpUtils.GetUserHomeHelpSearchPath(), currentCulture); + + if(Directory.Exists(userHelpRoot)) + { + searchPaths.Add(userHelpRoot); + } + + var dirPath = Path.Combine(Utils.GetApplicationBase(Utils.DefaultPowerShellShellID), currentCulture); + searchPaths.Add(dirPath); + var wordToComplete = context.WordToComplete + "*"; var topicPattern = WildcardPattern.Get("about_*.help.txt", WildcardOptions.IgnoreCase); List files = new List(); @@ -5928,11 +5941,14 @@ internal static List CompleteHelpTopics(CompletionContext cont { var wildcardPattern = WildcardPattern.Get(wordToComplete, WildcardOptions.IgnoreCase); - foreach(var file in Directory.GetFiles(dirPath)) + foreach (var dir in searchPaths) { - if(wildcardPattern.IsMatch(Path.GetFileName(file))) + foreach (var file in Directory.GetFiles(dir)) { - files.Add(file); + if (wildcardPattern.IsMatch(Path.GetFileName(file))) + { + files.Add(file); + } } } } diff --git a/src/System.Management.Automation/help/CabinetNativeApi.cs b/src/System.Management.Automation/help/CabinetNativeApi.cs index 309dfb59b56..2176be675f6 100644 --- a/src/System.Management.Automation/help/CabinetNativeApi.cs +++ b/src/System.Management.Automation/help/CabinetNativeApi.cs @@ -225,9 +225,19 @@ internal delegate IntPtr FdiOpenDelegate( internal static IntPtr FdiOpen(string filename, int oflag, int pmode) { FileMode mode = CabinetNativeApi.ConvertOpflagToFileMode(oflag); + FileAccess access = CabinetNativeApi.ConvertPermissionModeToFileAccess(pmode); FileShare share = CabinetNativeApi.ConvertPermissionModeToFileShare(pmode); + // This method is used for opening the cab file as well as saving the extracted files. + // When we are opening the cab file we only need read permissions. + // We force read permissions so that non-elevated users can extract cab files. + if(mode == FileMode.Open || mode == FileMode.OpenOrCreate) + { + access = FileAccess.Read; + share = FileShare.Read; + } + try { FileStream stream = new FileStream(filename, mode, access, share); diff --git a/src/System.Management.Automation/help/CommandHelpProvider.cs b/src/System.Management.Automation/help/CommandHelpProvider.cs index 1de757edce3..dfb3b8bb8f1 100644 --- a/src/System.Management.Automation/help/CommandHelpProvider.cs +++ b/src/System.Management.Automation/help/CommandHelpProvider.cs @@ -317,7 +317,8 @@ private HelpInfo GetHelpInfo(CommandInfo commandInfo, bool reportErrors, bool se // and the nested module that implements the command GetModulePaths(commandInfo, out moduleName, out moduleDir, out nestedModulePath); - Collection searchPaths = new Collection(); + Collection searchPaths = new Collection(){ HelpUtils.GetUserHomeHelpSearchPath() }; + if (!String.IsNullOrEmpty(moduleDir)) { searchPaths.Add(moduleDir); @@ -511,14 +512,18 @@ private string GetHelpFile(string helpFile, CmdletInfo cmdletInfo) // we have to search only in the application base for a mshsnapin... // if you create an absolute path for helpfile, then MUIFileSearcher // will look only in that path. - helpFileToLoad = Path.Combine(mshSnapInInfo.ApplicationBase, helpFile); + + searchPaths.Add(HelpUtils.GetUserHomeHelpSearchPath()); + searchPaths.Add(mshSnapInInfo.ApplicationBase); } else if (cmdletInfo.Module != null && !string.IsNullOrEmpty(cmdletInfo.Module.Path)) { - helpFileToLoad = Path.Combine(cmdletInfo.Module.ModuleBase, helpFile); + searchPaths.Add(HelpUtils.GetModuleBaseForUserHelp(cmdletInfo.Module.ModuleBase, cmdletInfo.Module.Name)); + searchPaths.Add(cmdletInfo.Module.ModuleBase); } else { + searchPaths.Add(HelpUtils.GetUserHomeHelpSearchPath()); searchPaths.Add(GetDefaultShellSearchPath()); searchPaths.Add(GetCmdletAssemblyPath(cmdletInfo)); } diff --git a/src/System.Management.Automation/help/HelpFileHelpProvider.cs b/src/System.Management.Automation/help/HelpFileHelpProvider.cs index da282fa7f07..a70100bdfe5 100644 --- a/src/System.Management.Automation/help/HelpFileHelpProvider.cs +++ b/src/System.Management.Automation/help/HelpFileHelpProvider.cs @@ -164,6 +164,28 @@ private Collection FilterToLatestModuleVersion(Collection filesM } } + // Deduplicate by filename to compensate for two sources, currentuser scope and allusers scope. + // This is done after the version check filtering to ensure we do not remove later version files. + HashSet fileNameHash = new HashSet(); + + foreach(var file in filesMatched) + { + string fileName = Path.GetFileName(file); + + if(!fileNameHash.Contains(fileName)) + { + fileNameHash.Add(fileName); + } + else + { + // If the file need to be removed, add it to matchedFilesToRemove, if not already present. + if(!matchedFilesToRemove.Contains(file)) + { + matchedFilesToRemove.Add(file); + } + } + } + return matchedFilesToRemove; } @@ -323,6 +345,7 @@ internal Collection GetExtendedSearchPaths() // Add $pshome at the top of the list String defaultShellSearchPath = GetDefaultShellSearchPath(); + int index = searchPaths.IndexOf(defaultShellSearchPath); if (index != 0) { @@ -333,6 +356,9 @@ internal Collection GetExtendedSearchPaths() searchPaths.Insert(0, defaultShellSearchPath); } + // Add the CurrentUser help path. + searchPaths.Add(HelpUtils.GetUserHomeHelpSearchPath()); + // Add modules that are not loaded. Since using 'get-module -listavailable' is very expensive, // we load all the directories (which are not empty) under the module path. foreach (string psModulePath in ModuleIntrinsics.GetModulePath(false, this.HelpSystem.ExecutionContext)) @@ -366,6 +392,7 @@ internal Collection GetExtendedSearchPaths() catch (System.Security.SecurityException) { } } } + return searchPaths; } diff --git a/src/System.Management.Automation/help/HelpUtils.cs b/src/System.Management.Automation/help/HelpUtils.cs new file mode 100644 index 00000000000..b84e7e1cba0 --- /dev/null +++ b/src/System.Management.Automation/help/HelpUtils.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Help; +using Microsoft.PowerShell.Commands; + +namespace System.Management.Automation +{ + internal class HelpUtils + { + private static string userHomeHelpPath = null; + + /// + /// Get the path to $HOME + /// + internal static string GetUserHomeHelpSearchPath() + { + if (userHomeHelpPath == null) + { +#if UNIX + var userModuleFolder = Platform.SelectProductNameForDirectory(Platform.XDG_Type.USER_MODULES); + string userScopeRootPath = System.IO.Path.GetDirectoryName(userModuleFolder); +#else + string userScopeRootPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "PowerShell"); +#endif + userHomeHelpPath = Path.Combine(userScopeRootPath, "Help"); + } + + return userHomeHelpPath; + } + + internal static string GetModuleBaseForUserHelp(string moduleBase, string moduleName) + { + string newModuleBase = moduleBase; + + // In case of inbox modules, the help is put under $PSHOME/, + // since the dlls are not published under individual module folders, but under $PSHome. + // In case of other modules, the help is under moduleBase/ or + // under moduleBase//. + // The code below creates a similar layout for CurrentUser scope. + // If the the scope is AllUsers, then the help goes under moduleBase. + + var userHelpPath = GetUserHomeHelpSearchPath(); + string moduleBaseParent = Directory.GetParent(moduleBase).Name; + + if (moduleBase.EndsWith(moduleName, StringComparison.OrdinalIgnoreCase)) + { + //This module is not an inbox module, so help goes under / + newModuleBase = Path.Combine(userHelpPath, moduleName); + } + else if (String.Equals(moduleBaseParent, moduleName, StringComparison.OrdinalIgnoreCase)) + { + //This module has version folder. + var moduleVersion = Path.GetFileName(moduleBase); + newModuleBase = Path.Combine(userHelpPath, moduleName, moduleVersion); + } + else + { + //This module is inbox module, help should be under + newModuleBase = userHelpPath; + } + + return newModuleBase; + } + } +} diff --git a/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs b/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs index 9e08244eb99..eddf4c3fd56 100644 --- a/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs +++ b/src/System.Management.Automation/help/UpdatableHelpCommandBase.cs @@ -115,6 +115,16 @@ public SwitchParameter Force } internal bool _force; + /// + /// Sets the scope to which help is saved. + /// + [Parameter(Mandatory = false, ValueFromPipelineByPropertyName = true)] + public UpdateHelpScope Scope + { + get; + set; + } + #endregion #region Events @@ -862,4 +872,19 @@ internal void ProcessException(string moduleName, string culture, Exception e) #endregion } + + /// + /// Scope to which the help should be saved. + /// + public enum UpdateHelpScope + { + /// + /// Save the help content to the user directory. + CurrentUser, + + /// + /// Save the help content to the module directory. This is the default behavior. + /// + AllUsers + } } diff --git a/src/System.Management.Automation/help/UpdateHelpCommand.cs b/src/System.Management.Automation/help/UpdateHelpCommand.cs index 1f36f4fb875..4a97b0236bf 100644 --- a/src/System.Management.Automation/help/UpdateHelpCommand.cs +++ b/src/System.Management.Automation/help/UpdateHelpCommand.cs @@ -212,10 +212,17 @@ internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, UpdatableHelpInfo newHelpInfo = null; string helpInfoUri = null; + string moduleBase = module.ModuleBase; + + if(this.Scope == UpdateHelpScope.CurrentUser) + { + moduleBase = HelpUtils.GetModuleBaseForUserHelp(moduleBase, module.ModuleName); + } + // reading the xml file even if force is specified // Reason: we need the current version for ShouldProcess string xml = UpdatableHelpSystem.LoadStringFromPath(this, - SessionState.Path.Combine(module.ModuleBase, module.GetHelpInfoName()), + SessionState.Path.Combine(moduleBase, module.GetHelpInfoName()), null); if (xml != null) @@ -230,7 +237,7 @@ internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, } // Don't update too frequently - if (!_alreadyCheckedOncePerDayPerModule && !CheckOncePerDayPerModule(module.ModuleName, module.ModuleBase, module.GetHelpInfoName(), DateTime.UtcNow, _force)) + if (!_alreadyCheckedOncePerDayPerModule && !CheckOncePerDayPerModule(module.ModuleName, moduleBase, module.GetHelpInfoName(), DateTime.UtcNow, _force)) { return true; } @@ -347,7 +354,7 @@ internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, continue; } - if (Utils.IsUnderProductFolder(module.ModuleBase) && (!Utils.IsAdministrator())) + if (Utils.IsUnderProductFolder(moduleBase) && (!Utils.IsAdministrator())) { string message = StringUtil.Format(HelpErrors.UpdatableHelpRequiresElevation); ProcessException(module.ModuleName, null, new UpdatableHelpSystemException("UpdatableHelpSystemRequiresElevation", @@ -375,7 +382,12 @@ internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, // Gather destination paths Collection destPaths = new Collection(); - destPaths.Add(module.ModuleBase); + if(!Directory.Exists(moduleBase)) + { + Directory.CreateDirectory(moduleBase); + } + + destPaths.Add(moduleBase); #if !CORECLR // Side-By-Side directories are not present in OneCore environments. if (IsSystemModule(module.ModuleName) && Environment.Is64BitOperatingSystem) @@ -442,7 +454,7 @@ internal override bool ProcessModuleWithCulture(UpdatableHelpModuleInfo module, } _helpSystem.GenerateHelpInfo(module.ModuleName, module.ModuleGuid, newHelpInfo.UnresolvedUri, contentUri.Culture.Name, newHelpInfo.GetCultureVersion(contentUri.Culture), - module.ModuleBase, module.GetHelpInfoName(), _force); + moduleBase, module.GetHelpInfoName(), _force); foreach (string fileInstalled in filesInstalled) { diff --git a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 index a6fdaef785b..f0270b14778 100644 --- a/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 +++ b/test/powershell/Host/TabCompletion/TabCompletion.Tests.ps1 @@ -938,22 +938,29 @@ dir -Recurse ` $completionOptions | Should -Be ([string]::Join("", $expected)) } } -} -Describe "Tab completion help test" -Tags @('RequireAdminOnWindows', 'CI') { - It 'Should complete about help topic' { + Context "Tab completion help test" { + BeforeAll { + if ([System.Management.Automation.Platform]::IsWindows) { + $userHelpRoot = Join-Path $HOME "Documents/PowerShell/Help/" + } else { + $userModulesRoot = [System.Management.Automation.Platform]::SelectProductNameForDirectory([System.Management.Automation.Platform+XDG_Type]::USER_MODULES) + $userHelpRoot = Join-Path $userModulesRoot -ChildPath ".." -AdditionalChildPath "Help" + } + } - $aboutHelpPath = Join-Path $PSHOME (Get-Culture).Name + It 'Should complete about help topic' { + $aboutHelpPath = Join-Path $userHelpRoot (Get-Culture).Name - ## If help content does not exist, tab completion will not work. So update it first. - if (-not (Test-Path (Join-Path $aboutHelpPath "about_Splatting.help.txt"))) - { - Update-Help -Force -ErrorAction SilentlyContinue - } + ## If help content does not exist, tab completion will not work. So update it first. + if (-not (Test-Path (Join-Path $aboutHelpPath "about_Splatting.help.txt"))) { + Update-Help -Force -ErrorAction SilentlyContinue -Scope 'CurrentUser' + } - $res = TabExpansion2 -inputScript 'get-help about_spla' -cursorColumn 'get-help about_spla'.Length - $res.CompletionMatches.Count | Should -Be 1 - $res.CompletionMatches[0].CompletionText | Should -BeExactly 'about_Splatting' + $res = TabExpansion2 -inputScript 'get-help about_spla' -cursorColumn 'get-help about_spla'.Length + $res.CompletionMatches.Count | Should -Be 1 + $res.CompletionMatches[0].CompletionText | Should -BeExactly 'about_Splatting' + } } } diff --git a/test/powershell/engine/Help/HelpSystem.Tests.ps1 b/test/powershell/engine/Help/HelpSystem.Tests.ps1 index ed0bb72ab43..fb358a9197f 100644 --- a/test/powershell/engine/Help/HelpSystem.Tests.ps1 +++ b/test/powershell/engine/Help/HelpSystem.Tests.ps1 @@ -3,67 +3,38 @@ # # Validates Get-Help for cmdlets in Microsoft.PowerShell.Core. -function UpdateHelpFromLocalContentPath -{ - param ([string]$ModuleName, [string]$Tag = 'CI') - - if ($Tag -eq 'CI') - { - $helpContentPath = Join-Path $PSScriptRoot "assets" - $helpFiles = @(Get-ChildItem "$helpContentPath\*" -ea SilentlyContinue) - - if ($helpFiles.Count -eq 0) - { - throw "Unable to find help content at '$helpContentPath'" - } - - Update-Help -Module $ModuleName -SourcePath $helpContentPath -Force -ErrorAction Stop +$script:cmdletsToSkip = @( + "Get-PSHostProcessInfo", + "Out-Default", + "Register-ArgumentCompleter", + "New-PSRoleCapabilityFile", + "Get-PSSessionCapability", + "Disable-PSRemoting", # Content not available: Issue # https://github.com/PowerShell/PowerShell-Docs/issues/1790 + "Enable-PSRemoting" +) + +function UpdateHelpFromLocalContentPath { + param ([string]$ModuleName, [string] $Scope = 'CurrentUser') + + $helpContentPath = Join-Path $PSScriptRoot "assets" + $helpFiles = @(Get-ChildItem "$helpContentPath\*" -ErrorAction SilentlyContinue) + + if ($helpFiles.Count -eq 0) { + throw "Unable to find help content at '$helpContentPath'" } - else - { - Update-Help -Module $ModuleName -Force -ErrorAction Stop - } -} - -function RunTestCase -{ - param ([string]$tag = "CI") - - $moduleName = "Microsoft.PowerShell.Core" - - UpdateHelpFromLocalContentPath $moduleName $tag - $cmdlets = get-command -module $moduleName - - $cmdletsToSkip = @( - "Get-PSHostProcessInfo", - "Out-Default", - "Register-ArgumentCompleter", - "New-PSRoleCapabilityFile", - "Get-PSSessionCapability", - "Disable-PSRemoting", # Content not available: Issue # https://github.com/PowerShell/PowerShell-Docs/issues/1790 - "Enable-PSRemoting" - ) - - foreach ($cmdletName in $cmdlets) - { - if ($cmdletsToSkip -notcontains $cmdletName) - { - It "Validate -Description and -Examples sections in help content. Run 'Get-help -name $cmdletName'" { - - $help = get-help -name $cmdletName - $help.Description | Out-String | Should -Match $cmdletName - $help.Examples | Out-String | Should -Match $cmdletName - } + Update-Help -Module $ModuleName -SourcePath $helpContentPath -Force -ErrorAction Stop -Scope $Scope +} - if ($tag -eq "CI") - { - # For a CI test run, we are only interested in validating one cmdlet to ensure that - # get-help works. - break - } - } +function GetCurrentUserHelpRoot { + if ([System.Management.Automation.Platform]::IsWindows) { + $userHelpRoot = Join-Path $HOME "Documents/PowerShell/Help/" + } else { + $userModulesRoot = [System.Management.Automation.Platform]::SelectProductNameForDirectory([System.Management.Automation.Platform+XDG_Type]::USER_MODULES) + $userHelpRoot = Join-Path $userModulesRoot -ChildPath ".." -AdditionalChildPath "Help" } + + return $userHelpRoot } Describe "Validate that //default.help.txt is present" -Tags @('CI') { @@ -77,34 +48,68 @@ Describe "Validate that //default.help.txt is present" -Tags @( } } -Describe "Validate that get-help works" -Tags @('CI', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { +Describe "Validate that get-help works for CurrentUserScope" -Tags @('CI') { BeforeAll { $SavedProgressPreference = $ProgressPreference $ProgressPreference = "SilentlyContinue" + $moduleName = "Microsoft.PowerShell.Core" } AfterAll { $ProgressPreference = $SavedProgressPreference } - RunTestCase -tag "CI" + + Context "for module : $moduleName" { + + BeforeAll { + UpdateHelpFromLocalContentPath $moduleName -Scope 'CurrentUser' + $cmdlets = Get-Command -Module $moduleName + } + + $testCases = @() + + ## Just testing first 3 in CI, Feature tests validate full list. + $cmdlets | Where-Object { $script:cmdletsToSkip -notcontains $_ } | Select-Object -First 3 | ForEach-Object { $testCases += @{ cmdletName = $_.Name }} + + It "Validate -Description and -Examples sections in help content. Run 'Get-help -name " -TestCases $testCases { + param($cmdletName) + $help = get-help -name $cmdletName + $help.Description | Out-String | Should Match $cmdletName + $help.Examples | Out-String | Should Match $cmdletName + } + } } -Describe "Validate Get-Help for all cmdlets in 'Microsoft.PowerShell.Core'" -Tags @('Feature', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { +Describe "Validate that get-help works for AllUsers Scope" -Tags @('Feature','RequireAdminOnWindows', 'RequireSudoOnUnix') { BeforeAll { $SavedProgressPreference = $ProgressPreference $ProgressPreference = "SilentlyContinue" + $moduleName = "Microsoft.PowerShell.Core" } AfterAll { $ProgressPreference = $SavedProgressPreference } - RunTestCase -tag "Feature" + Context "for module : $moduleName" { + + BeforeAll { + UpdateHelpFromLocalContentPath $moduleName -Scope 'AllUsers' + $cmdlets = Get-Command -Module $moduleName + } + + $testCases = @() + $cmdlets | Where-Object { $cmdletsToSkip -notcontains $_ } | ForEach-Object { $testCases += @{ cmdletName = $_.Name }} + + It "Validate -Description and -Examples sections in help content. Run 'Get-help -name " -TestCases $testCases { + param($cmdletName) + $help = get-help -name $cmdletName + $help.Description | Out-String | Should Match $cmdletName + $help.Examples | Out-String | Should Match $cmdletName + } + } } -Describe "Validate that Get-Help returns provider-specific help" -Tags @('CI', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { +Describe "Validate that get-help works for provider specific help" -Tags @('CI') { BeforeAll { - $SavedProgressPreference = $ProgressPreference - $ProgressPreference = "SilentlyContinue" - $namespaces = @{ command = 'http://schemas.microsoft.com/maml/dev/command/2004/10' dev = 'http://schemas.microsoft.com/maml/dev/2004/10' @@ -112,75 +117,67 @@ Describe "Validate that Get-Help returns provider-specific help" -Tags @('CI', ' msh = 'http://msh' } + $helpFileRoot = Join-Path (GetCurrentUserHelpRoot) ([Globalization.CultureInfo]::CurrentUICulture) + # Currently these test cases are verified only on Windows, because # - WSMan:\ and Cert:\ providers are not yet supported on non-Windows platforms. $testCases = @( @{ - helpFile = "$PSHOME\$([Globalization.CultureInfo]::CurrentUICulture)\System.Management.Automation.dll-help.xml" - path = "$PSHOME" + helpFile = "$helpFileRoot\System.Management.Automation.dll-help.xml" + path = "$userHelpRoot" helpContext = "[@id='FileSystem' or @ID='FileSystem']" verb = 'Add' noun = 'Content' - pending = !$IsWindows } ) - if ($IsWindows) - { + if ($IsWindows) { $testCases += @( @{ - helpFile = "$PSHOME\$([Globalization.CultureInfo]::CurrentUICulture)\Microsoft.WSMan.Management.dll-help.xml" + helpFile = "$helpFileRoot\Microsoft.WSMan.Management.dll-help.xml" path = 'WSMan:\localhost\ClientCertificate' helpContext = "[@id='ClientCertificate' or @ID='ClientCertificate']" - verb = 'New' - noun = 'Item' - pending = $false + cmdlet = 'New-Item' } , @{ - helpFile = "$PSHOME\$([Globalization.CultureInfo]::CurrentUICulture)\Microsoft.PowerShell.Security.dll-help.xml" + helpFile = "$helpFileRoot\Microsoft.PowerShell.Security.dll-help.xml" path = 'Cert:\' helpContext = $null # CertificateProvider uses only verb and noun in XPath query verb = 'New' noun = 'Item' - pending = $false } ) - UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.WSMan.Management' -Tag 'CI' - UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Security' -Tag 'CI' + + UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.WSMan.Management' -Scope 'CurrentUser' + UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Security' -Scope 'CurrentUser' } - UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Core' -Tag 'CI' + UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Core' -Scope 'CurrentUser' } - AfterAll { - $ProgressPreference = $SavedProgressPreference - } - - foreach ($helptest in $testCases) - { - $helpFile = $helptest.helpFile - $path = $helptest.path - $helpContext = $helptest.helpContext - $verb = $helptest.verb - $noun = $helptest.noun - $pending = $helptest.pending + ## The tests are marked as pending since provider specific help content in not available in PS v6.* + It "Shows contextual help when Get-Help is invoked for provider-specific path (Get-Help -Name - -Path )" -TestCases $testCases -Pending { - It -Pending:$pending "Shows contextual help when Get-Help is invoked for provider-specific path (Get-Help -Name $verb-$noun -Path $path)" { + param( + $helpFile, + $path, + $verb, + $noun + ) - # Path should exist or else Get-Help will fallback to default help text - $path | Should -Exist + # Path should exist or else Get-Help will fallback to default help text + $path | Should -Exist - $xpath = "/msh:helpItems/msh:providerHelp/msh:CmdletHelpPaths/msh:CmdletHelpPath$helpContext/command:command/command:details[command:verb='$verb' and command:noun='$noun']" - $helpXmlNode = Select-Xml -Path $helpFile -XPath $xpath -Namespace $namespaces | Select-Object -ExpandProperty Node + $xpath = "/msh:helpItems/msh:providerHelp/msh:CmdletHelpPaths/msh:CmdletHelpPath$helpContext/command:command/command:details[command:verb='$verb' and command:noun='$noun']" + $helpXmlNode = Select-Xml -Path $helpFile -XPath $xpath -Namespace $namespaces | Select-Object -ExpandProperty Node - # Synopsis comes from command:command/command:details/maml:description - $expected = Get-Help -Name "$verb-$noun" -Path $path | Select-Object -ExpandProperty Synopsis + # Synopsis comes from command:command/command:details/maml:description + $expected = Get-Help -Name "$verb-$noun" -Path $path | Select-Object -ExpandProperty Synopsis - # System.Management.Automation.ProviderContext.GetProviderSpecificHelpInfo ignores extra whitespace, line breaks and - # comments when loading help XML, but Select-Xml can not; use BeLikeExactly operator to omit trailing line breaks: - $helpXmlNode.description.para -clike "$expected*" | Should -BeTrue - } + # System.Management.Automation.ProviderContext.GetProviderSpecificHelpInfo ignores extra whitespace, line breaks and + # comments when loading help XML, but Select-Xml can not; use BeLikeExactly operator to omit trailing line breaks: + $helpXmlNode.description.para -clike "$expected*" | Should -BeTrue } } @@ -192,19 +189,18 @@ Describe "Validate about_help.txt under culture specific folder works" -Tags @(' Set-Content -Path $modulePath\test.psm1 -Value "function foo{}" Set-Content -Path $modulePath\en-US\about_testhelp.help.txt -Value "Hello" -NoNewline - $aboutHelpPath = Join-Path $PSHOME (Get-Culture).Name + $aboutHelpPath = Join-Path (GetCurrentUserHelpRoot) (Get-Culture).Name ## If help content does not exist, update it first. - if (-not (Test-Path (Join-Path $aboutHelpPath "about_Variables.help.txt"))) - { - Update-Help -Force -ErrorAction SilentlyContinue + if (-not (Test-Path (Join-Path $aboutHelpPath "about_Variables.help.txt"))) { + UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Core' -Scope 'CurrentUser' } } AfterAll { Remove-Item $modulePath -Recurse -Force # Remove all the help content. - Get-ChildItem -Path $PSHOME -Include @('about_*.txt', "*help.xml") -Recurse | Remove-Item -Force -ErrorAction SilentlyContinue + Get-ChildItem -Path $aboutHelpPath -Include @('about_*.txt', "*help.xml") -Recurse | Remove-Item -Force -ErrorAction SilentlyContinue } It "Get-Help should return help text and not multiple HelpInfo objects when help is under `$pshome path" { @@ -220,26 +216,42 @@ Describe "Validate about_help.txt under culture specific folder works" -Tags @(' } } -Describe "Get-Help should find help info within help files" -Tags @('CI', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { +Describe "About help files can be found in AllUsers scope" -Tags @('Feature', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { + BeforeAll { + $aboutHelpPath = Join-Path $PSHOME (Get-Culture).Name + + ##clean up any CurrentUser if exists. + + $userHelpRoot = GetCurrentUserHelpRoot + + if (Test-Path $userHelpRoot) { + Remove-Item $userHelpRoot -Force -Recurse -ErrorAction Stop + } + + UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Core' -Scope 'AllUsers' + } + + It "Get-Help for about_Variable should return only one help object" { + $help = Get-Help about_Variables + $help.count | Should Be 1 + } +} + +Describe "Get-Help should find help info within help files" -Tags @('CI') { It "Get-Help should find help files under pshome" { $helpFile = "about_testCase.help.txt" - $culture = (Get-Culture).Name - $helpFolderPath = Join-Path $PSHOME $culture + $helpFolderPath = Join-Path (GetCurrentUserHelpRoot) (Get-Culture).Name $helpFilePath = Join-Path $helpFolderPath $helpFile - if (!(Test-Path $helpFolderPath)) - { + if (!(Test-Path $helpFolderPath)) { $null = New-Item -ItemType Directory -Path $helpFolderPath -ErrorAction SilentlyContinue } - try - { + try { $null = New-Item -ItemType File -Path $helpFilePath -Value "about_test" -ErrorAction SilentlyContinue $helpContent = Get-Help about_testCase $helpContent | Should -Match "about_test" - } - finally - { + } finally { Remove-Item $helpFilePath -Force -ErrorAction SilentlyContinue } } @@ -251,34 +263,33 @@ Describe "Get-Help should find pattern help files" -Tags "CI" { # This occurs even if Unix system just returns "Directory.GetFiles(path, pattern);" as the windows' code does. # Since there's currently no way to get the vm from Travis CI and the test PASSES locally on both Ubuntu and MacOS, excluding pattern test under Unix system. - BeforeAll { - $helpFile1 = "about_testCase1.help.txt" - $helpFile2 = "about_testCase.2.help.txt" - $culture = (Get-Culture).Name - $helpFolderPath = Join-Path $PSHOME $culture - $helpFilePath1 = Join-Path $helpFolderPath $helpFile1 - $helpFilePath2 = Join-Path $helpFolderPath $helpFile2 - $null = New-Item -ItemType Directory -Path $helpFolderPath -ErrorAction SilentlyContinue -Force - # Create at least one help file matches "about*" pattern - $null = New-Item -ItemType File -Path $helpFilePath1 -Value "about_test1" -ErrorAction SilentlyContinue - $null = New-Item -ItemType File -Path $helpFilePath2 -Value "about_test2" -ErrorAction SilentlyContinue - } - - # Remove the test files - AfterAll { - Remove-Item $helpFilePath1 -Force -ErrorAction SilentlyContinue - Remove-Item $helpFilePath2 -Force -ErrorAction SilentlyContinue - } - - $testcases = @( - @{command = {Get-Help about_testCas?1}; testname = "test ? pattern"; result = "about_test1"} - @{command = {Get-Help about_testCase.?}; testname = "test ? pattern with dot"; result = "about_test2"} - @{command = {(Get-Help about_testCase*).Count}; testname = "test * pattern"; result = "2"} - @{command = {Get-Help about_testCas?.2*}; testname = "test ?, * pattern with dot"; result = "about_test2"} - ) - - It "Get-Help should find pattern help files - " -TestCases $testcases -Pending: (-not $IsWindows){ - param ( + BeforeAll { + $helpFile1 = "about_testCase1.help.txt" + $helpFile2 = "about_testCase.2.help.txt" + $helpFolderPath = Join-Path (GetCurrentUserHelpRoot) (Get-Culture).Name + $helpFilePath1 = Join-Path $helpFolderPath $helpFile1 + $helpFilePath2 = Join-Path $helpFolderPath $helpFile2 + $null = New-Item -ItemType Directory -Path $helpFolderPath -ErrorAction SilentlyContinue -Force + # Create at least one help file matches "about*" pattern + $null = New-Item -ItemType File -Path $helpFilePath1 -Value "about_test1" -ErrorAction SilentlyContinue + $null = New-Item -ItemType File -Path $helpFilePath2 -Value "about_test2" -ErrorAction SilentlyContinue + } + + # Remove the test files + AfterAll { + Remove-Item $helpFilePath1 -Force -ErrorAction SilentlyContinue + Remove-Item $helpFilePath2 -Force -ErrorAction SilentlyContinue + } + + $testcases = @( + @{command = {Get-Help about_testCas?1}; testname = "test ? pattern"; result = "about_test1"} + @{command = {Get-Help about_testCase.?}; testname = "test ? pattern with dot"; result = "about_test2"} + @{command = {(Get-Help about_testCase*).Count}; testname = "test * pattern"; result = "2"} + @{command = {Get-Help about_testCas?.2*}; testname = "test ?, * pattern with dot"; result = "about_test2"} + ) + + It "Get-Help should find pattern help files - " -TestCases $testcases -Pending: (-not $IsWindows) { + param ( $command, $result ) @@ -293,20 +304,20 @@ Describe "Get-Help should find pattern alias" -Tags "CI" { } It "Get-Help should find alias as command" { - (Get-Help where).Name | Should -BeExactly "Where-Object" + (Get-Help where).Name | Should -BeExactly "Where-Object" } It "Get-Help should find alias with ? pattern" { - $help = Get-Help wher? - $help.Category | Should -BeExactly "Alias" - $help.Synopsis | Should -BeExactly "Where-Object" + $help = Get-Help wher? + $help.Category | Should -BeExactly "Alias" + $help.Synopsis | Should -BeExactly "Where-Object" } It "Get-Help should find alias with * pattern" { - Set-Alias -Name testAlias1 -Value Where-Object - $help = Get-Help testAlias1* - $help.Category | Should -BeExactly "Alias" - $help.Synopsis | Should -BeExactly "Where-Object" + Set-Alias -Name testAlias1 -Value Where-Object + $help = Get-Help testAlias1* + $help.Category | Should -BeExactly "Alias" + $help.Synopsis | Should -BeExactly "Where-Object" } } @@ -326,3 +337,94 @@ Describe "help function uses full view by default" -Tags "CI" { $gpsHelp | Where-Object {$_ -cmatch '^PARAMETERS'} | Should -BeNullOrEmpty } } + +Describe 'help can be found for CurrentUser Scope' -Tags 'CI' { + BeforeAll { + $userHelpRoot = GetCurrentUserHelpRoot + + ## Clear all help from user scope. + Remove-Item $userHelpRoot -Force -ErrorAction SilentlyContinue -Recurse + UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Core' -Scope 'CurrentUser' + UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Management' -Scope 'CurrentUser' + UpdateHelpFromLocalContentPath -ModuleName 'PSReadLine' -Scope CurrentUser -Force + UpdateHelpFromLocalContentPath -ModuleName 'PackageManagement' -Scope CurrentUser -Force + + ## Delete help from global scope if it exists. + $currentCulture = (Get-Culture).Name + + $managementHelpFilePath = Join-Path $PSHOME -ChildPath $currentCulture -AdditionalChildPath 'Microsoft.PowerShell.Commands.Management.dll-Help.xml' + if (Test-Path $managementHelpFilePath) { + Remove-Item $managementHelpFilePath -Force -ErrorAction SilentlyContinue + } + + $coreHelpFilePath = Join-Path $PSHOME -ChildPath $currentCulture -AdditionalChildPath 'System.Management.Automation.dll-Help.xml' + if (Test-Path $coreHelpFilePath) { + Remove-Item $coreHelpFilePath -Force -ErrorAction SilentlyContinue + } + + $psreadlineHelpFilePath = Join-Path (Get-Module PSReadLine -ListAvailable).ModuleBase -ChildPath $currentCulture -AdditionalChildPath 'Microsoft.PowerShell.PSReadLine.dll-Help.xml' + if (Test-Path $psreadlineHelpFilePath) { + Remove-Item $psreadlineHelpFilePath -Force -ErrorAction SilentlyContinue + } + + $TestCases = @( + @{TestName = 'module under $PSHOME'; CmdletName = 'Add-Content'} + @{TestName = 'module is a PSSnapin'; CmdletName = 'Get-Command' } + @{TestName = 'module is under $PSHOME\Modules'; CmdletName = 'Get-PSReadlineOption' } + @{TestName = 'module has a version folder'; CmdletName = 'Find-Package' } + ) + } + + It 'help in user scope be found for ' -TestCases $TestCases { + param($CmdletName) + + $helpObj = Get-Help -Name $CmdletName -Full + $helpObj.description | Out-String | Should -Match $CmdletName + } +} + +Describe 'help can be found for AllUsers Scope' -Tags @('Feature', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { + BeforeAll { + $userHelpRoot = GetCurrentUserHelpRoot + + ## Clear all help from user scope. + Remove-Item $userHelpRoot -Force -ErrorAction SilentlyContinue -Recurse + + ## Delete help from global scope if it exists. + $currentCulture = (Get-Culture).Name + + $managementHelpFilePath = Join-Path $PSHOME -ChildPath $currentCulture -AdditionalChildPath 'Microsoft.PowerShell.Commands.Management.dll-Help.xml' + if (Test-Path $managementHelpFilePath) { + Remove-Item $managementHelpFilePath -Force -ErrorAction SilentlyContinue + } + + $coreHelpFilePath = Join-Path $PSHOME -ChildPath $currentCulture -AdditionalChildPath 'System.Management.Automation.dll-Help.xml' + if (Test-Path $coreHelpFilePath) { + Remove-Item $coreHelpFilePath -Force -ErrorAction SilentlyContinue + } + + $psreadlineHelpFilePath = Join-Path (Get-Module PSReadLine -ListAvailable).ModuleBase -ChildPath $currentCulture -AdditionalChildPath 'Microsoft.PowerShell.PSReadLine.dll-Help.xml' + if (Test-Path $psreadlineHelpFilePath) { + Remove-Item $psreadlineHelpFilePath -Force -ErrorAction SilentlyContinue + } + + UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Core' -Scope 'AllUsers' + UpdateHelpFromLocalContentPath -ModuleName 'Microsoft.PowerShell.Management' -Scope 'AllUsers' + UpdateHelpFromLocalContentPath -ModuleName 'PSReadLine' -Scope 'AllUsers' -Force + UpdateHelpFromLocalContentPath -ModuleName 'PackageManagement' -Scope 'AllUsers' -Force + + $TestCases = @( + @{TestName = 'module under $PSHOME'; CmdletName = 'Add-Content'} + @{TestName = 'module is a PSSnapin'; CmdletName = 'Get-Command' } + @{TestName = 'module is under $PSHOME\Modules'; CmdletName = 'Get-PSReadlineOption' } + @{TestName = 'module has a version folder'; CmdletName = 'Find-Package' } + ) + } + + It 'help in user scope be found for ' -TestCases $TestCases { + param($CmdletName) + + $helpObj = Get-Help -Name $CmdletName -Full + $helpObj.description | Out-String | Should -Match $CmdletName + } +} diff --git a/test/powershell/engine/Help/UpdatableHelpSystem.Tests.ps1 b/test/powershell/engine/Help/UpdatableHelpSystem.Tests.ps1 index 1db5045a751..39af4273f38 100644 --- a/test/powershell/engine/Help/UpdatableHelpSystem.Tests.ps1 +++ b/test/powershell/engine/Help/UpdatableHelpSystem.Tests.ps1 @@ -32,6 +32,16 @@ if ([System.Management.Automation.Platform]::IsWindows) $extension = ".cab" } +if([System.Management.Automation.Platform]::IsWindows) +{ + $userHelpRoot = Join-Path $HOME "Documents/PowerShell/Help/" +} +else +{ + $userModulesRoot = [System.Management.Automation.Platform]::SelectProductNameForDirectory([System.Management.Automation.Platform+XDG_Type]::USER_MODULES) + $userHelpRoot = Join-Path $userModulesRoot -ChildPath ".." -AdditionalChildPath "Help" +} + # This is the list of test cases -- each test case represents a PowerShell Core module. $testCases = @{ @@ -40,6 +50,7 @@ $testCases = @{ HelpInfoFiles = "CimCmdlets_fb6cc51d-c096-4b38-b78d-0fed6277096a_HelpInfo.xml" CompressedFiles = "CimCmdlets_fb6cc51d-c096-4b38-b78d-0fed6277096a_en-US_HelpContent$extension" HelpInstallationPath = "$pshome\Modules\CimCmdlets\en-US" + HelpInstallationPathHome = "$userHelpRoot\CimCmdlets\en-US" } <# @@ -58,6 +69,7 @@ $testCases = @{ HelpInfoFiles = "Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_HelpInfo.xml" CompressedFiles = "Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_en-US_HelpContent$extension" HelpInstallationPath = "$pshome\en-US" + HelpInstallationPathHome = "$userHelpRoot\en-US" } "Microsoft.PowerShell.Diagnostics" = @{ @@ -65,6 +77,7 @@ $testCases = @{ HelpInfoFiles = "Microsoft.PowerShell.Diagnostics_ca046f10-ca64-4740-8ff9-2565dba61a4f_HelpInfo.xml" CompressedFiles = "Microsoft.PowerShell.Diagnostics_ca046f10-ca64-4740-8ff9-2565dba61a4f_en-US_helpcontent$extension" HelpInstallationPath = "$pshome\en-US" + HelpInstallationPathHome = "$userHelpRoot\en-US" } "Microsoft.PowerShell.Host" = @{ @@ -72,6 +85,7 @@ $testCases = @{ HelpInfoFiles = "Microsoft.PowerShell.Host_56d66100-99a0-4ffc-a12d-eee9a6718aef_HelpInfo.xml" CompressedFiles = "Microsoft.PowerShell.Host_56d66100-99a0-4ffc-a12d-eee9a6718aef_en-US_helpcontent$extension" HelpInstallationPath = "$pshome\en-US" + HelpInstallationPathHome = "$userHelpRoot\en-US" } "Microsoft.PowerShell.LocalAccounts" = @{ @@ -79,6 +93,7 @@ $testCases = @{ HelpInfoFiles = "Microsoft.PowerShell.LocalAccounts_8e362604-2c0b-448f-a414-a6a690a644e2_HelpInfo.xml" CompressedFiles = "Microsoft.PowerShell.LocalAccounts_8e362604-2c0b-448f-a414-a6a690a644e2_en-US_HelpContent$extension" HelpInstallationPath = "$pshome\Modules\Microsoft.PowerShell.LocalAccounts\en-US" + HelpInstallationPathHome = "$userHelpRoot\Microsoft.PowerShell.LocalAccounts\en-US" } "Microsoft.PowerShell.Management" = @{ @@ -86,6 +101,7 @@ $testCases = @{ HelpInfoFiles = "Microsoft.PowerShell.Management_eefcb906-b326-4e99-9f54-8b4bb6ef3c6d_HelpInfo.xml" CompressedFiles = "Microsoft.PowerShell.Management_eefcb906-b326-4e99-9f54-8b4bb6ef3c6d_en-US_helpcontent$extension" HelpInstallationPath = "$pshome\en-US" + HelpInstallationPathHome = "$userHelpRoot\en-US" } "Microsoft.PowerShell.Security" = @{ @@ -93,6 +109,7 @@ $testCases = @{ HelpInfoFiles = "Microsoft.PowerShell.Security_a94c8c7e-9810-47c0-b8af-65089c13a35a_HelpInfo.xml" CompressedFiles = "Microsoft.PowerShell.Security_a94c8c7e-9810-47c0-b8af-65089c13a35a_en-US_helpcontent$extension" HelpInstallationPath = "$pshome\en-US" + HelpInstallationPathHome = "$userHelpRoot\en-US" } "Microsoft.PowerShell.Utility" = @{ @@ -100,6 +117,7 @@ $testCases = @{ HelpInfoFiles = "Microsoft.PowerShell.Utility_1da87e53-152b-403e-98dc-74d7b4d63d59_HelpInfo.xml" CompressedFiles = "Microsoft.PowerShell.Utility_1da87e53-152b-403e-98dc-74d7b4d63d59_en-US_helpcontent$extension" HelpInstallationPath = "$pshome\en-US" + HelpInstallationPathHome = "$userHelpRoot\en-US" } "Microsoft.WSMan.Management" = @{ @@ -107,6 +125,7 @@ $testCases = @{ HelpInfoFiles = "Microsoft.WsMan.Management_766204A6-330E-4263-A7AB-46C87AFC366C_HelpInfo.xml" CompressedFiles = "Microsoft.WsMan.Management_766204A6-330E-4263-A7AB-46C87AFC366C_en-US_helpcontent$extension" HelpInstallationPath = "$pshome\en-US" + HelpInstallationPathHome = "$userHelpRoot\en-US" } "PackageManagement" = @{ @@ -114,6 +133,7 @@ $testCases = @{ HelpInfoFiles = "PackageManagement_4ae9fd46-338a-459c-8186-07f910774cb8_HelpInfo.xml" CompressedFiles = "PackageManagement_4ae9fd46-338a-459c-8186-07f910774cb8_en-US_helpcontent$extension" HelpInstallationPath = "$pshome\Modules\PackageManagement\*\en-US" + HelpInstallationPathHome = "$userHelpRoot\PackageManagement\*\en-US" } "PowershellGet" = @{ @@ -121,6 +141,7 @@ $testCases = @{ HelpInfoFiles = "PowershellGet_1d73a601-4a6c-43c5-ba3f-619b18bbb404_HelpInfo.xml" CompressedFiles = "PowershellGet_1d73a601-4a6c-43c5-ba3f-619b18bbb404_en-US_helpcontent$extension" HelpInstallationPath = "$pshome\Modules\PowershellGet\*\en-US" + HelpInstallationPathHome = "$userHelpRoot\PackageManagement\*\en-US" } "PSReadline" = @{ @@ -128,6 +149,7 @@ $testCases = @{ HelpInfoFiles = "PSReadline_5714753b-2afd-4492-a5fd-01d9e2cff8b5_HelpInfo.xml" CompressedFiles = "PSReadline_5714753b-2afd-4492-a5fd-01d9e2cff8b5_en-US_helpcontent$extension" HelpInstallationPath = "$pshome\Modules\PSReadLine\en-US" + HelpInstallationPathHome = "$userHelpRoot\PSReadLine\en-US" } } @@ -151,10 +173,21 @@ function ValidateInstalledHelpContent { param ( [ValidateNotNullOrEmpty()] - [string]$moduleName + [string]$moduleName, + [switch]$UserScope ) - $helpFilesInstalled = @(GetFiles -path $testCases[$moduleName].HelpInstallationPath | ForEach-Object {Split-Path $_ -Leaf}) + if($UserScope) + { + $params = @{ Path = $testCases[$moduleName].HelpInstallationPathHome } + } + else + { + $params = @{ Path = $testCases[$moduleName].HelpInstallationPath } + } + + $helpFilesInstalled = @(GetFiles @params | ForEach-Object {Split-Path $_ -Leaf}) + $expectedHelpFiles = @($testCases[$moduleName].HelpFiles) $helpFilesInstalled.Count | Should -Be $expectedHelpFiles.Count @@ -168,7 +201,8 @@ function RunUpdateHelpTests { param ( [string]$tag = "CI", - [switch]$useSourcePath + [switch]$useSourcePath, + [switch]$userscope ) foreach ($moduleName in $modulesInBox) @@ -176,36 +210,62 @@ function RunUpdateHelpTests if ($powershellCoreModules -contains $moduleName) { - It "Validate Update-Help for module '$moduleName'" { + It "Validate Update-Help for module '$moduleName' with scope as '$userscope'" { + + if($userscope) + { + $params = @{Path = $testCases[$moduleName].HelpInstallationPathHome} + $updateScope = @{Scope = 'CurrentUser'} + } + else + { + $params = @{Path = $testCases[$moduleName].HelpInstallationPath} + $updateScope = @{Scope = 'AllUsers'} + } + + $commonParam = @{ + Include = @("*help.xml") + Recurse = $true + ErrorAction = 'SilentlyContinue' + } + + $params += $commonParam # If the help file is already installed, delete it. - Get-ChildItem $testCases[$moduleName].HelpInstallationPath -Include @("*help.xml") -Recurse -ea SilentlyContinue | + Get-ChildItem @params | Remove-Item -Force -ErrorAction SilentlyContinue if ((Get-UICulture).Name -ne "en-Us") { if ($useSourcePath) { - Update-Help -Module $moduleName -Force -UICulture en-US -SourcePath "$PSScriptRoot\assets" + Update-Help -Module $moduleName -Force -UICulture en-US -SourcePath "$PSScriptRoot\assets" @updateScope } else { - Update-Help -Module $moduleName -Force -UICulture en-US + Update-Help -Module $moduleName -Force -UICulture en-US @updateScope } } else { if ($useSourcePath) { - Update-Help -Module $moduleName -Force -SourcePath "$PSScriptRoot\assets" + Update-Help -Module $moduleName -Force -SourcePath "$PSScriptRoot\assets" @updateScope } else { - Update-Help -Module $moduleName -Force + Update-Help -Module $moduleName -Force @updateScope } } - ValidateInstalledHelpContent -moduleName $moduleName + if($userscope) + { + ValidateInstalledHelpContent -moduleName $moduleName -UserScope + } + else + { + ValidateInstalledHelpContent -moduleName $moduleName + } } if ($tag -eq "CI") @@ -296,7 +356,19 @@ Describe "Validate Update-Help from the Web for one PowerShell Core module." -Ta $ProgressPreference = $SavedProgressPreference } - RunUpdateHelpTests -tag "CI" + RunUpdateHelpTests -tag "CI" -Scope 'AllUsers' +} + +Describe "Validate Update-Help from the Web for one PowerShell Core module for user scope." -Tags @('CI', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { + BeforeAll { + $SavedProgressPreference = $ProgressPreference + $ProgressPreference = "SilentlyContinue" + } + AfterAll { + $ProgressPreference = $SavedProgressPreference + } + + RunUpdateHelpTests -tag "CI" -Scope 'CurrentUser' } Describe "Validate Update-Help from the Web for all PowerShell Core modules." -Tags @('Feature', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { @@ -308,7 +380,19 @@ Describe "Validate Update-Help from the Web for all PowerShell Core modules." -T $ProgressPreference = $SavedProgressPreference } - RunUpdateHelpTests -tag "Feature" + RunUpdateHelpTests -tag "Feature" -Scope 'AllUsers' +} + +Describe "Validate Update-Help from the Web for all PowerShell Core modules for user scope." -Tags @('Feature', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { + BeforeAll { + $SavedProgressPreference = $ProgressPreference + $ProgressPreference = "SilentlyContinue" + } + AfterAll { + $ProgressPreference = $SavedProgressPreference + } + + RunUpdateHelpTests -tag "Feature" -Scope 'CurrentUser' } Describe "Validate Update-Help -SourcePath for one PowerShell Core module." -Tags @('CI', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { @@ -320,7 +404,19 @@ Describe "Validate Update-Help -SourcePath for one PowerShell Core module." -Tag $ProgressPreference = $SavedProgressPreference } - RunUpdateHelpTests -tag "CI" -useSourcePath + RunUpdateHelpTests -tag "CI" -useSourcePath -Scope 'AllUsers' +} + +Describe "Validate Update-Help -SourcePath for one PowerShell Core module for user scope." -Tags @('CI', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { + BeforeAll { + $SavedProgressPreference = $ProgressPreference + $ProgressPreference = "SilentlyContinue" + } + AfterAll { + $ProgressPreference = $SavedProgressPreference + } + + RunUpdateHelpTests -tag "CI" -useSourcePath -Scope 'CurrentUser' } Describe "Validate Update-Help -SourcePath for all PowerShell Core modules." -Tags @('Feature', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { @@ -332,7 +428,19 @@ Describe "Validate Update-Help -SourcePath for all PowerShell Core modules." -Ta $ProgressPreference = $SavedProgressPreference } - RunUpdateHelpTests -tag "Feature" -useSourcePath + RunUpdateHelpTests -tag "Feature" -useSourcePath -Scope 'AllUsers' +} + +Describe "Validate Update-Help -SourcePath for all PowerShell Core modules for user scope." -Tags @('Feature', 'RequireAdminOnWindows', 'RequireSudoOnUnix') { + BeforeAll { + $SavedProgressPreference = $ProgressPreference + $ProgressPreference = "SilentlyContinue" + } + AfterAll { + $ProgressPreference = $SavedProgressPreference + } + + RunUpdateHelpTests -tag "Feature" -useSourcePath -Scope 'CurrentUser' } Describe "Validate 'Save-Help -DestinationPath for one PowerShell Core modules." -Tags @('CI', 'RequireAdminOnWindows') { diff --git a/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_HelpInfo.xml b/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_HelpInfo.xml index de97c328064..0bde5a496c9 100644 --- a/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_HelpInfo.xml +++ b/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_HelpInfo.xml @@ -1,10 +1,10 @@  - https://go.microsoft.com/fwlink/?linkid=390782 + https://go.microsoft.com/fwlink/?linkid=855953 en-US - 5.0.7.0 + 6.0 \ No newline at end of file diff --git a/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_en-US_HelpContent.cab b/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_en-US_HelpContent.cab index 2509a6b4a35..354f5fc7dcb 100644 Binary files a/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_en-US_HelpContent.cab and b/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_en-US_HelpContent.cab differ diff --git a/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_en-US_HelpContent.zip b/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_en-US_HelpContent.zip index f0f4558e102..8d14ce3df67 100644 Binary files a/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_en-US_HelpContent.zip and b/test/powershell/engine/Help/assets/Microsoft.PowerShell.Core_00000000-0000-0000-0000-000000000000_en-US_HelpContent.zip differ