mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-08-29 10:09:43 +02:00
test(cmdpal): clean up Phase 6 ownership
Restore the inherited manifest test surface, pin generated form IDs, and clarify what the copied gallery fixture covers. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 62a92164-1ef1-4c78-9678-26fd596e2ff8
This commit is contained in:
@@ -287,10 +287,8 @@ public class ExtensionGalleryServiceTests
|
|||||||
[TestMethod]
|
[TestMethod]
|
||||||
public async Task FetchExtensionsAsync_ParsesDocumentedJsonRpcGalleryExample()
|
public async Task FetchExtensionsAsync_ParsesDocumentedJsonRpcGalleryExample()
|
||||||
{
|
{
|
||||||
// This JSON is the gallery feed example published in
|
// This copy validates compatibility with the documented JSON shape, but it does not read
|
||||||
// doc/json-rpc-spec/04-manifest-packaging.md. It is fed through the production parser so
|
// the docs file. If that example changes, this fixture must be updated by hand.
|
||||||
// the documentation and the real gallery model stay in sync. The example omits iconUrl
|
|
||||||
// so the test stays offline.
|
|
||||||
var feedDirectory = CreateTempDirectory("feed");
|
var feedDirectory = CreateTempDirectory("feed");
|
||||||
var cacheDirectory = CreateTempDirectory("cache");
|
var cacheDirectory = CreateTempDirectory("cache");
|
||||||
|
|
||||||
|
|||||||
@@ -866,162 +866,6 @@ public class JSExtensionManifestTests
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TryParse_RelativeIcon_ResolvesToContainedAbsolutePath()
|
|
||||||
{
|
|
||||||
CreateEntryPoint("dist/index.js");
|
|
||||||
CreateEntryPoint("assets/icon.png");
|
|
||||||
const string Json = """
|
|
||||||
{
|
|
||||||
"name": "relative-icon",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"cmdpal": { "icon": "assets/icon.png" }
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
var result = JSExtensionManifest.TryParse(Json, _testDirectory);
|
|
||||||
|
|
||||||
Assert.IsTrue(result.IsValid, result.FailureReason);
|
|
||||||
var expected = Path.GetFullPath(Path.Combine(_testDirectory, "assets", "icon.png"));
|
|
||||||
Assert.AreEqual(expected, result.Manifest!.IconPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TryParse_RootDirectory_IsResolvedToPackageRoot()
|
|
||||||
{
|
|
||||||
CreateEntryPoint("dist/index.js");
|
|
||||||
const string Json = """
|
|
||||||
{
|
|
||||||
"name": "root-directory",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"cmdpal": {}
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
var result = JSExtensionManifest.TryParse(Json, _testDirectory);
|
|
||||||
|
|
||||||
Assert.IsTrue(result.IsValid, result.FailureReason);
|
|
||||||
var expected = Path.TrimEndingDirectorySeparator(Path.GetFullPath(_testDirectory));
|
|
||||||
Assert.AreEqual(expected, result.Manifest!.RootDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TryParse_RelativeIcon_ThatEscapesPackage_ResolvesToEmpty()
|
|
||||||
{
|
|
||||||
CreateEntryPoint("dist/index.js");
|
|
||||||
const string Json = """
|
|
||||||
{
|
|
||||||
"name": "escaping-icon",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"cmdpal": { "icon": "../outside-icon.png" }
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
var result = JSExtensionManifest.TryParse(Json, _testDirectory);
|
|
||||||
|
|
||||||
Assert.IsTrue(result.IsValid, result.FailureReason);
|
|
||||||
Assert.AreEqual(string.Empty, result.Manifest!.IconPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TryParse_RelativeIcon_ThatDoesNotExist_ResolvesToEmpty()
|
|
||||||
{
|
|
||||||
CreateEntryPoint("dist/index.js");
|
|
||||||
const string Json = """
|
|
||||||
{
|
|
||||||
"name": "missing-icon",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"cmdpal": { "icon": "assets/missing.png" }
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
var result = JSExtensionManifest.TryParse(Json, _testDirectory);
|
|
||||||
|
|
||||||
Assert.IsTrue(result.IsValid, result.FailureReason);
|
|
||||||
Assert.AreEqual(string.Empty, result.Manifest!.IconPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TryParse_GlyphIcon_IsPreservedUnchanged()
|
|
||||||
{
|
|
||||||
CreateEntryPoint("dist/index.js");
|
|
||||||
const string Json = """
|
|
||||||
{
|
|
||||||
"name": "glyph-icon",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"cmdpal": { "icon": "\uE700" }
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
var result = JSExtensionManifest.TryParse(Json, _testDirectory);
|
|
||||||
|
|
||||||
Assert.IsTrue(result.IsValid, result.FailureReason);
|
|
||||||
Assert.AreEqual("\uE700", result.Manifest!.IconPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TryParse_UriIcon_IsPreservedUnchanged()
|
|
||||||
{
|
|
||||||
CreateEntryPoint("dist/index.js");
|
|
||||||
const string Json = """
|
|
||||||
{
|
|
||||||
"name": "uri-icon",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"cmdpal": { "icon": "https://example.com/icon.png" }
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
var result = JSExtensionManifest.TryParse(Json, _testDirectory);
|
|
||||||
|
|
||||||
Assert.IsTrue(result.IsValid, result.FailureReason);
|
|
||||||
Assert.AreEqual("https://example.com/icon.png", result.Manifest!.IconPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void TryParse_IconThroughJunction_ResolvesToEmpty()
|
|
||||||
{
|
|
||||||
// An icon whose lexical path stays inside the package but traverses a junction that
|
|
||||||
// redirects outside the package must resolve to empty rather than load the outside file.
|
|
||||||
CreateEntryPoint("dist/index.js");
|
|
||||||
|
|
||||||
var outsideDirectory = Path.Combine(Path.GetTempPath(), $"JSExtensionIconJunctionTarget_{Guid.NewGuid():N}");
|
|
||||||
Directory.CreateDirectory(outsideDirectory);
|
|
||||||
File.WriteAllText(Path.Combine(outsideDirectory, "icon.png"), "// icon bytes");
|
|
||||||
|
|
||||||
var junctionPath = Path.Combine(_testDirectory, "linked-assets");
|
|
||||||
if (!TryCreateJunction(junctionPath, outsideDirectory))
|
|
||||||
{
|
|
||||||
Directory.Delete(outsideDirectory, recursive: true);
|
|
||||||
Assert.Inconclusive("A directory junction could not be created in this environment.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
const string Json = """
|
|
||||||
{
|
|
||||||
"name": "junction-icon",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"cmdpal": { "icon": "linked-assets/icon.png" }
|
|
||||||
}
|
|
||||||
""";
|
|
||||||
|
|
||||||
var result = JSExtensionManifest.TryParse(Json, _testDirectory);
|
|
||||||
|
|
||||||
Assert.IsTrue(result.IsValid, result.FailureReason);
|
|
||||||
Assert.AreEqual(string.Empty, result.Manifest!.IconPath);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (Directory.Exists(junctionPath))
|
|
||||||
{
|
|
||||||
Directory.Delete(junctionPath, recursive: false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Directory.Delete(outsideDirectory, recursive: true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool TryCreateJunction(string junctionPath, string targetPath)
|
private static bool TryCreateJunction(string junctionPath, string targetPath)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -218,6 +218,29 @@ describe('form identity and routing', () => {
|
|||||||
expect(responseFor(sent, 4)?.result).toEqual({ kind: 2 });
|
expect(responseFor(sent, 4)?.result).toEqual({ kind: 2 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('assigns a deterministic formId when the author omits one', async () => {
|
||||||
|
const page: IContentPage = {
|
||||||
|
id: 'page',
|
||||||
|
name: 'Page',
|
||||||
|
title: 'Page',
|
||||||
|
getContent(): Content[] {
|
||||||
|
return [formContent(undefined, () => ({ kind: 'goHome' }))];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const { runtime, sent } = createHarness();
|
||||||
|
runtime.setProvider(providerWith(page));
|
||||||
|
|
||||||
|
await runtime.handleRequest({
|
||||||
|
jsonrpc: JSONRPC_VERSION,
|
||||||
|
id: 1,
|
||||||
|
method: 'contentPage/getContent',
|
||||||
|
params: { pageId: 'page' },
|
||||||
|
});
|
||||||
|
|
||||||
|
const content = responseFor(sent, 1)?.result as Array<Record<string, unknown>>;
|
||||||
|
expect(content[0]?.formId).toBe('form-0');
|
||||||
|
});
|
||||||
|
|
||||||
it('keeps routing a nested form by its stable id after the tree grows', async () => {
|
it('keeps routing a nested form by its stable id after the tree grows', async () => {
|
||||||
// Mirrors the comments sample: submitting a reply mutates the tree, and the
|
// Mirrors the comments sample: submitting a reply mutates the tree, and the
|
||||||
// next serialization must still route the same stable formId back to its
|
// next serialization must still route the same stable formId back to its
|
||||||
|
|||||||
Reference in New Issue
Block a user