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]
|
||||
public async Task FetchExtensionsAsync_ParsesDocumentedJsonRpcGalleryExample()
|
||||
{
|
||||
// This JSON is the gallery feed example published in
|
||||
// doc/json-rpc-spec/04-manifest-packaging.md. It is fed through the production parser so
|
||||
// the documentation and the real gallery model stay in sync. The example omits iconUrl
|
||||
// so the test stays offline.
|
||||
// This copy validates compatibility with the documented JSON shape, but it does not read
|
||||
// the docs file. If that example changes, this fixture must be updated by hand.
|
||||
var feedDirectory = CreateTempDirectory("feed");
|
||||
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)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -218,6 +218,29 @@ describe('form identity and routing', () => {
|
||||
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 () => {
|
||||
// Mirrors the comments sample: submitting a reply mutates the tree, and the
|
||||
// next serialization must still route the same stable formId back to its
|
||||
|
||||
Reference in New Issue
Block a user