diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Common.UnitTests/ExtensionGallery/Services/ExtensionGalleryServiceTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Common.UnitTests/ExtensionGallery/Services/ExtensionGalleryServiceTests.cs index 7ac8e47043..db9a2b9773 100644 --- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.Common.UnitTests/ExtensionGallery/Services/ExtensionGalleryServiceTests.cs +++ b/src/modules/cmdpal/Tests/Microsoft.CmdPal.Common.UnitTests/ExtensionGallery/Services/ExtensionGalleryServiceTests.cs @@ -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"); diff --git a/src/modules/cmdpal/Tests/Microsoft.CmdPal.UI.ViewModels.UnitTests/JSExtensionManifestTests.cs b/src/modules/cmdpal/Tests/Microsoft.CmdPal.UI.ViewModels.UnitTests/JSExtensionManifestTests.cs index bbfc353530..7c9197453e 100644 --- a/src/modules/cmdpal/Tests/Microsoft.CmdPal.UI.ViewModels.UnitTests/JSExtensionManifestTests.cs +++ b/src/modules/cmdpal/Tests/Microsoft.CmdPal.UI.ViewModels.UnitTests/JSExtensionManifestTests.cs @@ -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 diff --git a/src/modules/cmdpal/ts-sdk/test/forms.test.ts b/src/modules/cmdpal/ts-sdk/test/forms.test.ts index c947ab3082..9b62103505 100644 --- a/src/modules/cmdpal/ts-sdk/test/forms.test.ts +++ b/src/modules/cmdpal/ts-sdk/test/forms.test.ts @@ -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>; + 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