mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-05 18:57:19 +02:00
CmdPal: Fix missing primary context command for late-bound items (#46131)
This PR does fix a bug where an item that starts with a null or empty primary command never adds that primary action to the context menu after the extension later provides a real command. - Creates the default primary context-menu item lazily when `Command` or `Command.Name` becomes available after `SlowInitializeProperties()` - Refreshes `AllCommands`, `SecondaryCommand`, and `HasMoreCommands` notifications for late command materialization and Show Details updates. - Adds unit tests to cover the fixed issue. <!-- Enter a brief description/summary of your PR here. What does it fix/what does it change/how was it tested (even manually, if necessary)? --> ## Summary of the Pull Request <!-- Please review the items on the PR checklist before submitting--> ## PR Checklist - [x] Closes: #46129 <!-- - [ ] Closes: #yyy (add separate lines for additional resolved issues) --> - [ ] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [ ] **Tests:** Added/updated and all pass - [ ] **Localization:** All end-user-facing strings can be localized - [ ] **Dev docs:** Added/updated - [ ] **New binaries:** Added on the required places - [ ] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [ ] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [ ] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [ ] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [ ] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx <!-- Provide a more detailed description of the PR, other things fixed, or any additional comments/features here --> ## Detailed Description of the Pull Request / Additional comments <!-- Describe how you validated the behavior. Add automated tests wherever possible, but list manual validation steps taken as well --> ## Validation Steps Performed
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.CmdPal.Common.Text;
|
||||
using Microsoft.CmdPal.UI.ViewModels.Models;
|
||||
using Microsoft.CommandPalette.Extensions.Toolkit;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
@@ -76,4 +77,64 @@ public class CommandItemViewModelTests
|
||||
Assert.IsNotNull(viewModel.SecondaryCommand);
|
||||
Assert.AreEqual("Secondary", viewModel.SecondaryCommand.Name);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void LatePrimaryCommandCreation_AddsPrimaryToAllCommands()
|
||||
{
|
||||
// Reproduces issue where SlowInitializeProperties runs before a real primary command exists.
|
||||
// The late-arriving command should still create the synthetic primary context item and prepend it to AllCommands.
|
||||
var pageContext = new TestPageContext();
|
||||
var item = new CommandItem()
|
||||
{
|
||||
Command = null,
|
||||
MoreCommands =
|
||||
[
|
||||
new CommandContextItem(new NoOpCommand { Name = "Secondary" }),
|
||||
],
|
||||
};
|
||||
|
||||
var viewModel = new CommandItemViewModel(new(item), new(pageContext), DefaultContextMenuFactory.Instance);
|
||||
viewModel.SlowInitializeProperties();
|
||||
|
||||
Assert.AreEqual(1, viewModel.AllCommands.Count);
|
||||
Assert.AreEqual("Secondary", ((CommandContextItemViewModel)viewModel.AllCommands[0]).Name);
|
||||
|
||||
item.Command = new NoOpCommand { Name = "Primary" };
|
||||
|
||||
Assert.AreEqual(2, viewModel.AllCommands.Count);
|
||||
Assert.AreEqual("Primary", ((CommandContextItemViewModel)viewModel.AllCommands[0]).Name);
|
||||
Assert.AreEqual("Secondary", ((CommandContextItemViewModel)viewModel.AllCommands[1]).Name);
|
||||
Assert.IsTrue(viewModel.HasMoreCommands);
|
||||
Assert.AreEqual("Secondary", viewModel.SecondaryCommand?.Name);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SyntheticPrimaryContextItem_UpdatesSubtitleAndCachedSubtitleTarget()
|
||||
{
|
||||
// The synthetic primary context item copies subtitle state from the parent CommandItemViewModel.
|
||||
// When subtitle changes later, both the exposed subtitle and its cached fuzzy-search target must refresh.
|
||||
var pageContext = new TestPageContext();
|
||||
var item = new CommandItem(new NoOpCommand { Name = "Primary" })
|
||||
{
|
||||
Subtitle = "before",
|
||||
MoreCommands =
|
||||
[
|
||||
new CommandContextItem(new NoOpCommand { Name = "Secondary" }),
|
||||
],
|
||||
};
|
||||
|
||||
var viewModel = new CommandItemViewModel(new(item), new(pageContext), DefaultContextMenuFactory.Instance);
|
||||
viewModel.SlowInitializeProperties();
|
||||
|
||||
var primaryContextItem = (CommandContextItemViewModel)viewModel.AllCommands[0];
|
||||
var matcher = new PrecomputedFuzzyMatcher(new PrecomputedFuzzyMatcherOptions());
|
||||
|
||||
Assert.AreEqual("before", primaryContextItem.Subtitle);
|
||||
Assert.AreEqual("before", primaryContextItem.GetSubtitleTarget(matcher).Original);
|
||||
|
||||
item.Subtitle = "after unique";
|
||||
|
||||
Assert.AreEqual("after unique", primaryContextItem.Subtitle);
|
||||
Assert.AreEqual("after unique", primaryContextItem.GetSubtitleTarget(matcher).Original);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user