MSIX.Tests/MSIX.ContextMenuPlacement.Tests.ps1
|
BeforeAll { Import-Module (Resolve-Path (Join-Path $PSScriptRoot '..\MSIX.psd1')) -Force # Minimal valid manifest used to exercise the manifest-mutation logic # entirely in-memory (no MakeAppx needed for these tests). $script:SampleXml = @' <?xml version="1.0" encoding="utf-8"?> <Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10" xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" IgnorableNamespaces="uap"> <Identity Name="Test.App" Publisher="CN=Test" Version="1.0.0.0" /> <Properties> <DisplayName>Test</DisplayName> <PublisherDisplayName>Test</PublisherDisplayName> <Logo>logo.png</Logo> </Properties> <Dependencies> <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.17763.0" MaxVersionTested="10.0.19041.0" /> </Dependencies> <Resources><Resource Language="en-us" /></Resources> <Applications> <Application Id="App" Executable="app.exe" EntryPoint="Windows.FullTrustApplication"> <uap:VisualElements DisplayName="Test" Description="Test" BackgroundColor="transparent" Square150x150Logo="l.png" Square44x44Logo="l.png" /> </Application> </Applications> </Package> '@ } AfterAll { Remove-Module MSIX -ErrorAction SilentlyContinue } Describe 'Shell-extension context menu placement (TMEditX-verified pattern)' -Tag 'ContextMenu' { # Working pattern, derived from a TMEditX-generated manifest that the # Windows shell actually wires up at runtime (Notepad++ NppShell case): # # Applications/Application/Extensions # <com:Extension Category="windows.comServer"> <- v10 com: # <com:ComServer> # <com:SurrogateServer DisplayName="..."> # <com:Class Id="b298d29a-..." Path="VFS\..\Shell.dll" .../> # </com:SurrogateServer> # </com:ComServer> # </com:Extension> # <desktop4:Extension Category="windows.fileExplorerContextMenus"> # <desktop4:FileExplorerContextMenus> # <desktop5:ItemType Type="*"> # <desktop5:Verb Id="ContextMenuHandlers" Clsid="b298d29a-..." /> # </desktop5:ItemType> # </desktop4:FileExplorerContextMenus> # </desktop4:Extension> # # Notes: # - desktop9:fileExplorerClassicContextMenuHandler turned out to be the # WRONG schema for COM-based shell extensions. desktop4 + desktop5 # drives both legacy IContextMenu and modern IExplorerCommand via # whichever interface(s) the CLSID's COM class implements. # - Everything lives at Application level. Package-level placement # either fails schema validation (com requires com4 at Package level # and com4 disallows SurrogateServer) or installs but isn't wired up # by Explorer. It 'Add-MsixLegacyContextMenu emits com:Extension at Application level (bare com)' { [xml]$xml = $script:SampleXml & (Get-Module MSIX) { param($m) Add-MsixManifestNamespace $m 'com' $app = Get-MsixManifestApplication -Manifest $m $appExt = $app.SelectSingleNode('*[local-name()="Extensions"]') if (-not $appExt) { $appExt = $m.CreateElement('Extensions', $m.Package.NamespaceURI) $null = $app.AppendChild($appExt) } $comUri = Get-MsixManifestNamespaceUri 'com' $comExt = $m.CreateElement('com:Extension', $comUri) $comExt.SetAttribute('Category', 'windows.comServer') $comServer = $m.CreateElement('com:ComServer', $comUri) $surrogate = $m.CreateElement('com:SurrogateServer', $comUri) $surrogate.SetAttribute('DisplayName', 'Test') $null = $comServer.AppendChild($surrogate) $null = $comExt.AppendChild($comServer) $null = $appExt.AppendChild($comExt) } $xml $appComServer = $xml.SelectNodes("//*[local-name()='Application']/*[local-name()='Extensions']/*[local-name()='Extension' and @Category='windows.comServer']") $pkgComServer = $xml.SelectNodes("/*[local-name()='Package']/*[local-name()='Extensions']/*[local-name()='Extension' and @Category='windows.comServer']") $appComServer.Count | Should -Be 1 $pkgComServer.Count | Should -Be 0 $appComServer[0].NamespaceURI | Should -Be 'http://schemas.microsoft.com/appx/manifest/com/windows10' } It 'ContextMenu.ps1 source uses bare com: for the SurrogateServer block' { $src = Get-Content (Resolve-Path (Join-Path $PSScriptRoot '..\MSIX.ContextMenu.ps1')) -Raw $src | Should -Match "CreateElement\('com:Extension'" $src | Should -Match "CreateElement\('com:ComServer'" $src | Should -Match "CreateElement\('com:SurrogateServer'" $src | Should -Match "CreateElement\('com:Class'" # Must NOT use com4 here — Surrogate is forbidden at Package level. $src | Should -Not -Match "CreateElement\('com4:Extension'" $src | Should -Not -Match "CreateElement\('com4:SurrogateServer'" } It 'ContextMenu.ps1 emits desktop4:Extension + desktop5:ItemType/Verb (NOT desktop9)' { $src = Get-Content (Resolve-Path (Join-Path $PSScriptRoot '..\MSIX.ContextMenu.ps1')) -Raw # The new working pattern uses desktop4 outer, desktop5 inner verbs. $src | Should -Match "CreateElement\('desktop4:Extension'" $src | Should -Match "CreateElement\('desktop4:FileExplorerContextMenus'" $src | Should -Match "CreateElement\('desktop5:ItemType'" $src | Should -Match "CreateElement\('desktop5:Verb'" # No CreateElement calls should reference the old desktop9 nodes. # (Docstring/comments may still mention the schema for historical # context — we only forbid actual element construction.) $src | Should -Not -Match "CreateElement\('desktop9:Extension'" $src | Should -Not -Match "CreateElement\('desktop9:ExtensionHandler'" $src | Should -Not -Match "CreateElement\('desktop9:FileExplorerClassicContextMenuHandler'" } It 'CLSID is lowercased in both Add-MsixLegacyContextMenu and Add-MsixFileExplorerContextMenu' { $src = Get-Content (Resolve-Path (Join-Path $PSScriptRoot '..\MSIX.ContextMenu.ps1')) -Raw # Both functions normalise via .ToLowerInvariant() after stripping braces. ($src | Select-String -Pattern '\.ToLowerInvariant\(\)' -AllMatches).Matches.Count | Should -BeGreaterOrEqual 2 } It 'Add-MsixComServerExtension (InProcessServer only) still uses com4 at Package level' { $src = Get-Content (Resolve-Path (Join-Path $PSScriptRoot '..\MSIX.ManifestExtensions.ps1')) -Raw $startIdx = $src.IndexOf('function Add-MsixComServerExtension') $nextIdx = $src.IndexOf("`nfunction ", $startIdx + 1) if ($nextIdx -lt 0) { $nextIdx = $src.Length } $body = $src.Substring($startIdx, $nextIdx - $startIdx) $body | Should -Match "CreateElement\('com4:Extension'" $body | Should -Match "CreateElement\('com4:InProcessServer'" $body | Should -Match '_MsixGetOrCreatePackageExtensions' # InProc only — no Surrogate at Package level. $body | Should -Not -Match 'SurrogateServer' } } |