From c7863af9844a5f7cc444409b60cd1ddca97a3e8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Blanca=20Robledo=20D=C3=ADaz?= <48318470+blrobl@users.noreply.github.com> Date: Tue, 12 Mar 2024 10:11:12 +0100 Subject: [PATCH] [releases/24.0] AppSource Gallery bug fixes and test refactoring (#697) #### Summary Cherry-picks of the following AppSource Gallery-related PR's. #610 #657 #675 #### Work Item(s) Fixes [AB#504560](https://dynamicssmb2.visualstudio.com/1fcb79e7-ab07-432a-a3c6-6cf5a88ba4a5/_workitems/edit/504560) --------- Co-authored-by: Steffen Balslev Co-authored-by: Alexander Holstrup <117829001+aholstrup1@users.noreply.github.com> --- .../App/AppSource Gallery/app.json | 6 +- .../src/AppSourceJsonUtilities.Codeunit.al | 2 +- .../src/AppSourceProductDetails.Page.al | 19 +- .../src/AppSourceProductList.Page.al | 5 +- .../src/AppSourceProductManager.Codeunit.al | 74 +-- .../src/AppSrcProductDepsProvider.Codeunit.al | 73 +++ .../src/ExtensionManagement.Page.al | 14 + .../Test Library/AppSource Gallery/app.json | 44 ++ .../src/AppSourceMockDepsProvider.Codeunit.al | 118 +++++ .../src/AppSrcProductMgrTestImpl.Codeunit.al | 97 ++++ .../Test/AppSource Gallery/app.json | 50 +- .../src/AppSourceGalleryTest.Codeunit.al | 266 +++++++++++ .../AppSourceProductManagerTest.Codeunit.al | 439 ------------------ 13 files changed, 650 insertions(+), 557 deletions(-) create mode 100644 src/System Application/App/AppSource Gallery/src/AppSrcProductDepsProvider.Codeunit.al create mode 100644 src/System Application/Test Library/AppSource Gallery/app.json create mode 100644 src/System Application/Test Library/AppSource Gallery/src/AppSourceMockDepsProvider.Codeunit.al create mode 100644 src/System Application/Test Library/AppSource Gallery/src/AppSrcProductMgrTestImpl.Codeunit.al create mode 100644 src/System Application/Test/AppSource Gallery/src/AppSourceGalleryTest.Codeunit.al delete mode 100644 src/System Application/Test/AppSource Gallery/src/AppSourceProductManagerTest.Codeunit.al diff --git a/src/System Application/App/AppSource Gallery/app.json b/src/System Application/App/AppSource Gallery/app.json index 88b3ee20e4..99b8681335 100644 --- a/src/System Application/App/AppSource Gallery/app.json +++ b/src/System Application/App/AppSource Gallery/app.json @@ -63,8 +63,8 @@ ], "internalsVisibleTo": [ { - "name": "AppSource Product Gallery Test", - "id": "c1830429-77f6-4469-837a-7bfd4705ff41", + "id": "ba17b564-d600-44d5-be0b-ca7ff7ac28fc", + "name": "AppSource Gallery Test Library", "publisher": "Microsoft" } ], @@ -73,7 +73,7 @@ "idRanges": [ { "from": 2515, - "to": 2517 + "to": 2518 } ], "target": "OnPrem" diff --git a/src/System Application/App/AppSource Gallery/src/AppSourceJsonUtilities.Codeunit.al b/src/System Application/App/AppSource Gallery/src/AppSourceJsonUtilities.Codeunit.al index 6db7cfae85..5255c3ddac 100644 --- a/src/System Application/App/AppSource Gallery/src/AppSourceJsonUtilities.Codeunit.al +++ b/src/System Application/App/AppSource Gallery/src/AppSourceJsonUtilities.Codeunit.al @@ -5,7 +5,7 @@ namespace System.Apps.AppSource; /// -/// Library for managing AppSource product retrival and usage. +/// Library for managing AppSource product retrieval and usage. /// codeunit 2516 "AppSource Json Utilities" { diff --git a/src/System Application/App/AppSource Gallery/src/AppSourceProductDetails.Page.al b/src/System Application/App/AppSource Gallery/src/AppSourceProductDetails.Page.al index ba94fcb7d4..9f6d407f58 100644 --- a/src/System Application/App/AppSource Gallery/src/AppSourceProductDetails.Page.al +++ b/src/System Application/App/AppSource Gallery/src/AppSourceProductDetails.Page.al @@ -147,6 +147,7 @@ page 2516 "AppSource Product Details" { actionref(Open_Promoted; OpenInAppSource) { } actionref(Install_Promoted; Install) { } + actionref(InstallFromAppSource_Promoted; InstallFromAppSource) { } actionref(Uninstall_Promoted; Uninstall) { } } @@ -169,7 +170,8 @@ page 2516 "AppSource Product Details" { Caption = 'Install App'; Scope = Page; - Enabled = CurrentRecordCanBeInstalled; + Enabled = (not CurrentRecordCanBeUninstalled) and CurrentRecordCanBeInstalled; + Visible = (not CurrentRecordCanBeUninstalled) and CurrentRecordCanBeInstalled; Image = Insert; ToolTip = 'Installs the app.'; @@ -184,6 +186,21 @@ page 2516 "AppSource Product Details" end; } + action(InstallFromAppSource) + { + Caption = 'Install From AppSource'; + Scope = Page; + Image = Insert; + ToolTip = 'Installs the app from Microsoft AppSource.'; + Enabled = (not CurrentRecordCanBeUninstalled) and (not CurrentRecordCanBeInstalled); + Visible = (not CurrentRecordCanBeUninstalled) and (not CurrentRecordCanBeInstalled); + + trigger OnAction() + begin + AppSourceProductManager.OpenAppInAppSource(UniqueProductID); + end; + } + action(Uninstall) { Caption = 'Uninstall App'; diff --git a/src/System Application/App/AppSource Gallery/src/AppSourceProductList.Page.al b/src/System Application/App/AppSource Gallery/src/AppSourceProductList.Page.al index 62395bd92c..9ea1263b85 100644 --- a/src/System Application/App/AppSource Gallery/src/AppSourceProductList.Page.al +++ b/src/System Application/App/AppSource Gallery/src/AppSourceProductList.Page.al @@ -186,8 +186,8 @@ page 2515 "AppSource Product List" trigger OnOpenPage() begin - ReloadAllProducts(); Rec.SetCurrentKey(DisplayName); + ReloadAllProducts(); end; trigger OnAfterGetCurrRecord() @@ -201,8 +201,9 @@ page 2515 "AppSource Product List" var AppSourceProductTemp: Record "AppSource Product"; begin + AppSourceProductTemp.Copy(Rec); AppSourceProductManager.GetProductsAndPopulateRecord(AppSourceProductTemp); - AppSourceProductTemp.CopyFilters(Rec); Rec.Copy(AppSourceProductTemp, true); + Rec.FindFirst(); end; } diff --git a/src/System Application/App/AppSource Gallery/src/AppSourceProductManager.Codeunit.al b/src/System Application/App/AppSource Gallery/src/AppSourceProductManager.Codeunit.al index 48ea663a28..61d6ecacb2 100644 --- a/src/System Application/App/AppSource Gallery/src/AppSourceProductManager.Codeunit.al +++ b/src/System Application/App/AppSource Gallery/src/AppSourceProductManager.Codeunit.al @@ -8,14 +8,13 @@ using System.Environment.Configuration; using System.Globalization; using System.Azure.Identity; using System.Utilities; -using System.Environment; using System.Azure.KeyVault; using System.RestClient; /// -/// Library for managing AppSource product retrival and usage. +/// Library for managing AppSource product retrieval and usage. /// -codeunit 2515 "AppSource Product Manager" implements "AppSource Product Manager Dependencies" +codeunit 2515 "AppSource Product Manager" { Access = Internal; InherentEntitlements = X; @@ -39,66 +38,6 @@ codeunit 2515 "AppSource Product Manager" implements "AppSource Product Manager UnsupportedLanguageNotificationLbl: Label 'Language %1 is not supported by AppSource. Defaulting to "en". Change the language in the user profile to use another language.', Comment = '%1=Language ID, such as en'; UnsupportedMarketNotificationLbl: Label 'Market %1 is not supported by AppSource. Defaulting to "us". Change the region in the user profile to use another market.', Comment = '%1=Market ID, such as "us"'; - #region Dependency Interface implementation - procedure GetCountryLetterCode(): Code[2] - var - AzureAdTenant: Codeunit "Azure AD Tenant"; - begin - exit(AzureAdTenant.GetCountryLetterCode()); - end; - - procedure GetPreferredLanguage(): Text - var - AzureAdTenant: Codeunit "Azure AD Tenant"; - begin - exit(AzureAdTenant.GetPreferredLanguage()); - end; - - procedure GetApplicationFamily(): Text - var - EnvironmentInformation: Codeunit "Environment Information"; - begin - exit(EnvironmentInformation.GetApplicationFamily()); - end; - - procedure IsSaas(): boolean - var - EnvironmentInformation: Codeunit "Environment Information"; - begin - exit(EnvironmentInformation.IsSaas()); - end; - - procedure GetFormatRegionOrDefault(FormatRegion: Text[80]): Text - var - Language: Codeunit Language; - begin - exit(Language.GetFormatRegionOrDefault(FormatRegion)); - end; - - procedure GetAsJSon(var RestClient: Codeunit "Rest Client"; RequestUri: Text): JsonToken - begin - exit(RestClient.GetAsJSon(RequestUri)); - end; - - procedure GetUserSettings(UserSecurityId: Guid; var TempUserSettingsRecord: record "User Settings" temporary) - var - UserSettings: Codeunit "User Settings"; - begin - UserSettings.GetUserSettings(Database.UserSecurityID(), TempUserSettingsRecord); - end; - - procedure ShouldSetCommonHeaders(): Boolean - begin - exit(true); - end; - - #endregion - procedure SetDependencies(SpecificDependencies: Interface "AppSource Product Manager Dependencies") - begin - AppSourceProductManagerDependencies := SpecificDependencies; - IsDependenciesInterfaceSet := true; - end; - #region Product helpers /// /// Opens Microsoft AppSource web page for the region is specified in the UserSessionSettings or 'en-us' by default. @@ -494,9 +433,14 @@ codeunit 2515 "AppSource Product Manager" implements "AppSource Product Manager local procedure SetDefaultDependencyImplementation() var - AppSourceProductManager: Codeunit "AppSource Product Manager"; + AppSrcProductDepsProvider: Codeunit "AppSrc Product Deps. Provider"; + begin + SetDependencies(AppSrcProductDepsProvider); + end; + + internal procedure SetDependencies(AppSourceProductManagerDependencyProvider: Interface "AppSource Product Manager Dependencies") begin - SetDependencies(AppSourceProductManager); + AppSourceProductManagerDependencies := AppSourceProductManagerDependencyProvider; IsDependenciesInterfaceSet := true; end; } diff --git a/src/System Application/App/AppSource Gallery/src/AppSrcProductDepsProvider.Codeunit.al b/src/System Application/App/AppSource Gallery/src/AppSrcProductDepsProvider.Codeunit.al new file mode 100644 index 0000000000..ef7a1b2cf1 --- /dev/null +++ b/src/System Application/App/AppSource Gallery/src/AppSrcProductDepsProvider.Codeunit.al @@ -0,0 +1,73 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.Apps.AppSource; + +using System.Environment.Configuration; +using System.Globalization; +using System.Azure.Identity; +using System.Environment; +using System.RestClient; + +/// +/// Provides the dependencies used by the AppSource Gallery module. +/// +codeunit 2518 "AppSrc Product Deps. Provider" implements "AppSource Product Manager Dependencies" +{ + Access = Internal; + InherentEntitlements = X; + InherentPermissions = X; + + procedure GetCountryLetterCode(): Code[2] + var + AzureAdTenant: Codeunit "Azure AD Tenant"; + begin + exit(AzureAdTenant.GetCountryLetterCode()); + end; + + procedure GetPreferredLanguage(): Text + var + AzureAdTenant: Codeunit "Azure AD Tenant"; + begin + exit(AzureAdTenant.GetPreferredLanguage()); + end; + + procedure GetApplicationFamily(): Text + var + EnvironmentInformation: Codeunit "Environment Information"; + begin + exit(EnvironmentInformation.GetApplicationFamily()); + end; + + procedure IsSaas(): boolean + var + EnvironmentInformation: Codeunit "Environment Information"; + begin + exit(EnvironmentInformation.IsSaas()); + end; + + procedure GetFormatRegionOrDefault(FormatRegion: Text[80]): Text + var + Language: Codeunit Language; + begin + exit(Language.GetFormatRegionOrDefault(FormatRegion)); + end; + + procedure GetAsJSon(var RestClient: Codeunit "Rest Client"; RequestUri: Text): JsonToken + begin + exit(RestClient.GetAsJSon(RequestUri)); + end; + + procedure GetUserSettings(UserSecurityId: Guid; var TempUserSettingsRecord: record "User Settings" temporary) + var + UserSettings: Codeunit "User Settings"; + begin + UserSettings.GetUserSettings(Database.UserSecurityID(), TempUserSettingsRecord); + end; + + procedure ShouldSetCommonHeaders(): Boolean + begin + exit(true); + end; +} \ No newline at end of file diff --git a/src/System Application/App/Extension Management/src/ExtensionManagement.Page.al b/src/System Application/App/Extension Management/src/ExtensionManagement.Page.al index 59fffe7263..af60d6b3c1 100644 --- a/src/System Application/App/Extension Management/src/ExtensionManagement.Page.al +++ b/src/System Application/App/Extension Management/src/ExtensionManagement.Page.al @@ -226,6 +226,20 @@ page 2500 "Extension Management" end; #endif } + action("Microsoft AppSource Gallery") + { + Caption = 'AppSource Gallery'; + Enabled = IsSaaS; + Image = NewItem; + ToolTip = 'Browse the Microsoft AppSource Gallery for new extensions to install.'; + Visible = not IsOnPremDisplay; + RunPageMode = View; + + trigger OnAction() + begin + Page.Run(2515); + end; + } action("Upload Extension") { Caption = 'Upload Extension'; diff --git a/src/System Application/Test Library/AppSource Gallery/app.json b/src/System Application/Test Library/AppSource Gallery/app.json new file mode 100644 index 0000000000..90f4001507 --- /dev/null +++ b/src/System Application/Test Library/AppSource Gallery/app.json @@ -0,0 +1,44 @@ +{ + "id": "ba17b564-d600-44d5-be0b-ca7ff7ac28fc", + "name": "AppSource Gallery Test Library", + "publisher": "Microsoft", + "brief": "Test objects for the AppSource Gallery module.", + "description": "Test objects for the AppSource Gallery module.", + "version": "24.0.0.0", + "privacyStatement": "https://go.microsoft.com/fwlink/?LinkId=724009", + "EULA": "https://go.microsoft.com/fwlink/?linkid=2009120", + "help": "", + "url": "https://go.microsoft.com/fwlink/?LinkId=724011", + "logo": "", + "dependencies": [ + { + "id": "79952567-63d7-4586-8b47-ba13a11a8a18", + "name": "AppSource Product Gallery", + "publisher": "Microsoft", + "version": "24.0.0.0" + }, + { + "id": "812b339d-a9db-4a6e-84e4-fe35cbef0c44", + "name": "Rest Client", + "publisher": "Microsoft", + "version": "24.0.0.0" + }, + { + "id": "7b9b59f5-a68d-4271-b11a-0d3b9c0938dd", + "name": "User Settings", + "publisher": "Microsoft", + "version": "24.0.0.0" + } + ], + "screenshots": [ + + ], + "idRanges": [ + { + "from": 132935, + "to": 132936 + } + ], + "platform": "24.0.0.0", + "target": "OnPrem" +} \ No newline at end of file diff --git a/src/System Application/Test Library/AppSource Gallery/src/AppSourceMockDepsProvider.Codeunit.al b/src/System Application/Test Library/AppSource Gallery/src/AppSourceMockDepsProvider.Codeunit.al new file mode 100644 index 0000000000..3947260f1f --- /dev/null +++ b/src/System Application/Test Library/AppSource Gallery/src/AppSourceMockDepsProvider.Codeunit.al @@ -0,0 +1,118 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.TestLibraries.Apps.AppSource; + +using System.Environment.Configuration; +using System.RestClient; +using System.Apps.AppSource; + +/// +/// Library for providing mock dependencies for the AppSource product manager. +/// +codeunit 132936 "AppSource Mock Deps. Provider" implements "AppSource Product Manager Dependencies" +{ + InherentEntitlements = X; + InherentPermissions = X; + + var + FormatRegionStore: Text[80]; + CountryLetterCode: Code[2]; + PreferredLanguage: Text; + LanguageID: Variant; + IsInSaas: Boolean; + JsonRequests: JsonArray; + ApplicationFamily: Text; + + // Dependency to Azure AD Tenant + procedure GetCountryLetterCode(): Code[2] + begin + exit(CountryLetterCode) + end; + + procedure SetCountryLetterCode(InputCountryLetterCode: Code[2]) + begin + CountryLetterCode := InputCountryLetterCode; + end; + + procedure GetPreferredLanguage(): Text + begin + exit(PreferredLanguage); + end; + + procedure SetPreferredLanguage(InputPreferredLanguage: Text) + begin + PreferredLanguage := InputPreferredLanguage; + end; + + // Dependency to Environment Information + procedure GetApplicationFamily(): Text + begin + exit(ApplicationFamily); + end; + + procedure SetApplicationFamily(InputApplicationFamily: Text) + begin + ApplicationFamily := InputApplicationFamily; + end; + + procedure IsSaas(): Boolean + begin + exit(IsInSaas); + end; + + procedure SetIsSaas(InputIsSaas: Boolean) + begin + IsInSaas := InputIsSaas; + end; + + // Dependency to Language + procedure GetFormatRegionOrDefault(InputFormatRegion: Text[80]): Text + begin + if (InputFormatRegion <> '') then + exit(InputFormatRegion); + exit(FormatRegionStore); + end; + + procedure SetFormatRegionStore(InputFormatRegion: Text[80]) + begin + FormatRegionStore := InputFormatRegion; + end; + + // Rest client override + procedure GetAsJSon(var RestClient: Codeunit "Rest Client"; RequestUri: Text): JsonToken + var + Json: JsonToken; + begin + JsonRequests.Get(0, Json); + JsonRequests.RemoveAt(0); + exit(Json); + end; + + procedure SetJSon(JsonText: Text) + var + Json: JsonToken; + begin + Json.ReadFrom(JsonText); + JsonRequests.Add(Json); + end; + + procedure ShouldSetCommonHeaders(): Boolean + begin + exit(false); + end; + + // Dependency to User Settings + procedure GetUserSettings(UserSecurityID: Guid; var TempUserSettingsRecord: Record "User Settings" temporary) + begin + TempUserSettingsRecord.Init(); + TempUserSettingsRecord."User Security ID" := UserSecurityID; + TempUserSettingsRecord."Language ID" := LanguageID; + end; + + procedure SetUserSettings(InputLanguageId: Variant) + begin + LanguageID := InputLanguageId; + end; +} \ No newline at end of file diff --git a/src/System Application/Test Library/AppSource Gallery/src/AppSrcProductMgrTestImpl.Codeunit.al b/src/System Application/Test Library/AppSource Gallery/src/AppSrcProductMgrTestImpl.Codeunit.al new file mode 100644 index 0000000000..fe54403190 --- /dev/null +++ b/src/System Application/Test Library/AppSource Gallery/src/AppSrcProductMgrTestImpl.Codeunit.al @@ -0,0 +1,97 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace System.TestLibraries.Apps.AppSource; + +using System.Apps.AppSource; + +/// +/// Library for mocking AppSource product retrieval and usage. +/// +codeunit 132935 "AppSrc Product Mgr. Test Impl." +{ + InherentEntitlements = X; + InherentPermissions = X; + + var + TempAppSourceProduct: Record "AppSource Product" temporary; + AppSourceProductManager: Codeunit "AppSource Product Manager"; + + /// + /// Opens Microsoft AppSource web page for the region is specified in the UserSessionSettings or 'en-us' by default. + /// + procedure OpenAppSource() + begin + AppSourceProductManager.OpenAppSource(); + end; + + /// + /// Opens the AppSource product page in Microsoft AppSource, for the specified unique product ID. + /// + /// The Unique Product ID of the product to show in MicrosoftAppSource + procedure OpenAppInAppSource(UniqueProductIDValue: Text) + begin + AppSourceProductManager.OpenAppInAppSource(UniqueProductIDValue); + end; + + /// + /// Extracts the AppID from the Unique Product ID. + /// + /// The Unique Product ID of the product as defined in MicrosoftAppSource + /// AppID found in the Product ID + /// The AppSource unique product ID is specific to AppSource and combines different features while always ending with PAPID. and extension app id. Example: PUBID.mdcc1667400477212|AID.bc_converttemp_sample|PAPPID.9d314b3e-ffd3-41fd-8755-7744a6a790df + procedure ExtractAppIDFromUniqueProductID(UniqueProductIDValue: Text): Guid + begin + exit(AppSourceProductManager.ExtractAppIDFromUniqueProductID(UniqueProductIDValue)) + end; + + procedure CanInstallProductWithPlans(Plans: JsonArray): Boolean + begin + exit(AppSourceProductManager.CanInstallProductWithPlans(Plans)); + end; + + #region Record handling functions + + /// + /// Get all products from a remote server and adds them to the AppSource Product table. + /// + procedure GetProductsAndPopulateRecord(): Text + begin + exit(AppSourceProductManager.GetProductsAndPopulateRecord(TempAppSourceProduct)); + end; + + /// + /// Returns true if a product with the given display name is present in the AppSource Product table. + /// + procedure IsRecordWithDisplayNameinProductTable(DisplayName: Text): Boolean + begin + TempAppSourceProduct.Reset(); + TempAppSourceProduct.SetRange(DisplayName, DisplayName); + exit(TempAppSourceProduct.FindFirst()); + end; + + /// + /// Gets the number of records in the AppSource Product table. + /// + procedure GetProductTableCount(): Integer + begin + TempAppSourceProduct.Reset(); + exit(TempAppSourceProduct.Count()); + end; + #endregion + + // Dependencies + procedure SetDependencies(AppSourceMockDepsProvider: Codeunit "AppSource Mock Deps. Provider") + begin + AppSourceProductManager.SetDependencies(AppSourceMockDepsProvider); + end; + + procedure ResetDependencies() + var + AppSourceMockDepsProvider: Codeunit "AppSource Mock Deps. Provider"; + begin + AppSourceProductManager.SetDependencies(AppSourceMockDepsProvider); + TempAppSourceProduct.DeleteAll(); + end; +} \ No newline at end of file diff --git a/src/System Application/Test/AppSource Gallery/app.json b/src/System Application/Test/AppSource Gallery/app.json index 278fdf6cf6..9643071d2b 100644 --- a/src/System Application/Test/AppSource Gallery/app.json +++ b/src/System Application/Test/AppSource Gallery/app.json @@ -12,51 +12,9 @@ "logo": "", "dependencies": [ { - "id": "1b2efb4b-8c44-4d74-a56f-60646645bb21", - "name": "URI", - "publisher": "Microsoft", - "version": "24.0.0.0" - }, - { - "id": "3a56c7d2-a594-4682-bd90-b10bfb177620", - "name": "Azure Key Vault", - "publisher": "Microsoft", - "version": "24.0.0.0" - }, - { - "id": "812b339d-a9db-4a6e-84e4-fe35cbef0c44", - "name": "Rest Client", - "publisher": "Microsoft", - "version": "24.0.0.0" - }, - { - "id": "79952567-63d7-4586-8b47-ba13a11a8a18", - "name": "AppSource Product Gallery", - "publisher": "Microsoft", - "version": "24.0.0.0" - }, - { - "id": "95025170-61fc-4808-9505-4ba1fe1d05d9", - "name": "Azure AD Tenant", - "publisher": "Microsoft", - "version": "24.0.0.0" - }, - { - "id": "5c36f279-480c-451b-b513-c1af8cfb0744", - "name": "Language", - "publisher": "Microsoft", - "version": "24.0.0.0" - }, - { - "id": "7b9b59f5-a68d-4271-b11a-0d3b9c0938dd", - "name": "User Settings", - "publisher": "Microsoft", - "version": "24.0.0.0" - }, - { - "id": "2673d810-273e-402f-9093-2eaef7e03b83", - "name": "Environment Information", - "publisher": "Microsoft", + "id": "ba17b564-d600-44d5-be0b-ca7ff7ac28fc", + "name": "AppSource Gallery Test Library", + "publisher": "Microsoft", "version": "24.0.0.0" }, { @@ -81,4 +39,4 @@ } ], "target": "OnPrem" -} \ No newline at end of file +} diff --git a/src/System Application/Test/AppSource Gallery/src/AppSourceGalleryTest.Codeunit.al b/src/System Application/Test/AppSource Gallery/src/AppSourceGalleryTest.Codeunit.al new file mode 100644 index 0000000000..0f79dc74d0 --- /dev/null +++ b/src/System Application/Test/AppSource Gallery/src/AppSourceGalleryTest.Codeunit.al @@ -0,0 +1,266 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ + +namespace System.Apps.AppSource.Test; + +using System.TestLibraries.Apps.AppSource; +using System.TestLibraries.Utilities; + +codeunit 135074 "AppSource Gallery Test" +{ + Subtype = Test; + + var + AppSrcProductMgrTestImpl: Codeunit "AppSrc Product Mgr. Test Impl."; + LibraryAssert: Codeunit "Library Assert"; + HyperlinkStorage: Codeunit "Library - Variable Storage"; + + [Test] + procedure TestExtractAppIDFromUniqueProductIDReturnsExpectedAppId() + var + UniqueId: Text; + AppId: Guid; + ExpectedAppId: Guid; + begin + // Given + UniqueId := 'PUBID.nav24spzoo1579516366010%7CAID.n24_test_transactability%7CPAPPID.0984da34-5ec1-4ac1-9575-b73fb2212327'; + ExpectedAppId := '0984da34-5ec1-4ac1-9575-b73fb2212327'; + + // When + AppId := AppSrcProductMgrTestImpl.ExtractAppIDFromUniqueProductID(UniqueId); + + // Then + LibraryAssert.AreEqual(ExpectedAppId, AppId, 'Expected AppId to be extracted from UniqueId'); + end; + + [Test] + procedure TestExtractAppIDFromUniqueProductIDReturnsEmptyAppIdWhenNotValid() + var + UniqueId: Text; + AppId: Guid; + begin + // Given + UniqueId := 'articentgroupllc1635512619530.ackee-ubuntu-18-04-minimal'; + + // When + AppId := AppSrcProductMgrTestImpl.ExtractAppIDFromUniqueProductID(UniqueId); + + // Then + LibraryAssert.IsTrue(IsNullGuid(AppId), 'Expected AppId to be empty when not present in the UniqueId'); + end; + + [Test] + [HandlerFunctions('HyperlinkHandler')] + procedure TestOpenAppSourceHyperlink() + var + AppSourceMockDepsProvider: Codeunit "AppSource Mock Deps. Provider"; + begin + // Given + AppSourceMockDepsProvider.SetFormatRegionStore('da-DK'); + AppSrcProductMgrTestImpl.SetDependencies(AppSourceMockDepsProvider); + + // With handler expectation + HyperlinkStorage.Enqueue('https://appsource.microsoft.com/da-DK/marketplace/apps?product=dynamics-365-business-central'); + + // When + AppSrcProductMgrTestImpl.OpenAppSource(); + + // Then + // Asserted in handler + AssertCleanedUp(); + AppSrcProductMgrTestImpl.ResetDependencies(); + end; + + [Test] + [HandlerFunctions('HyperlinkHandler')] + procedure TestOpenInAppSourceHyperlink() + var + AppSourceMockDepsProvider: Codeunit "AppSource Mock Deps. Provider"; + UniqueId: Text; + begin + // Given + AppSourceMockDepsProvider.SetUserSettings(3082); //es-ES + // Override dependencies + AppSrcProductMgrTestImpl.SetDependencies(AppSourceMockDepsProvider); + + UniqueId := 'PUBID.nav24spzoo1579516366010%7CAID.n24_test_transactability%7CPAPPID.0984da34-5ec1-4ac1-9575-b73fb2212327'; + + // With handler expectation + HyperlinkStorage.Enqueue('https://appsource.microsoft.com/es-ES/product/dynamics-365-business-central/PUBID.nav24spzoo1579516366010%7CAID.n24_test_transactability%7CPAPPID.0984da34-5ec1-4ac1-9575-b73fb2212327'); + + // When + AppSrcProductMgrTestImpl.OpenAppInAppSource(UniqueId); + + // Then + // Asserted in handler + AssertCleanedUp(); + AppSrcProductMgrTestImpl.ResetDependencies(); + end; + + [Test] + procedure TestLoadProductNotAllowedOnPremises() + var + AppSourceMockDepsProvider: Codeunit "AppSource Mock Deps. Provider"; + begin + // Given + AppSourceMockDepsProvider.SetIsSaas(false); + AppSourceMockDepsProvider.SetUserSettings(3082); //es-ES + AppSourceMockDepsProvider.SetApplicationFamily('W1'); + AppSourceMockDepsProvider.SetCountryLetterCode('dk'); + AppSrcProductMgrTestImpl.SetDependencies(AppSourceMockDepsProvider); + + // When + asserterror AppSrcProductMgrTestImpl.GetProductsAndPopulateRecord(); + + // Then + LibraryAssert.ExpectedError('Not supported on premises.'); + + AppSrcProductMgrTestImpl.ResetDependencies(); + end; + + [Test] + procedure TestLoadProduct() + var + AppSourceMockDepsProvider: Codeunit "AppSource Mock Deps. Provider"; + begin + + // Given + AppSourceMockDepsProvider.SetIsSaas(true); + AppSourceMockDepsProvider.SetApplicationFamily('W1'); + AppSourceMockDepsProvider.SetUserSettings(3082); //es-ES + AppSourceMockDepsProvider.SetCountryLetterCode('us'); + AppSourceMockDepsProvider.SetJson('{"items": [{"uniqueProductId": "PUBID.pbsi_software|AID.247timetracker|PAPPID.9a12247e-8564-4b90-b80b-cd5f4b64217e","displayName": "Dynamics 365 Business Central","publisherId": "pbsi_software","publisherDisplayName": "David Boehm, CPA and Company Inc.","publisherType": "ThirdParty","ratingAverage": 5.0,"ratingCount": 2,"productType": "DynamicsBC","popularity": 7.729569120865367,"privacyPolicyUri": "https://pbsisoftware.com/24-7-tt-privacy-statement","lastModifiedDateTime": "2023-09-03T11:08:28.5348241+00:00"}]}'); + AppSrcProductMgrTestImpl.SetDependencies(AppSourceMockDepsProvider); + + // When + AppSrcProductMgrTestImpl.GetProductsAndPopulateRecord(); + + // Then + LibraryAssert.AreEqual(AppSrcProductMgrTestImpl.GetProductTableCount(), 1, 'The number of products is incorrect.'); + LibraryAssert.IsTrue(AppSrcProductMgrTestImpl.IsRecordWithDisplayNameinProductTable('Dynamics 365 Business Central'), 'The product name is incorrect.'); + + AppSrcProductMgrTestImpl.ResetDependencies(); + end; + + [Test] + procedure TestLoadProductWithNextPageLink() + var + AppSourceMockDepsProvider: Codeunit "AppSource Mock Deps. Provider"; + begin + // Given + AppSourceMockDepsProvider.SetIsSaas(true); + AppSourceMockDepsProvider.SetApplicationFamily('W1'); + AppSourceMockDepsProvider.SetUserSettings(3082); //es-ES + AppSourceMockDepsProvider.SetCountryLetterCode('dk'); + + // Push items + AppSourceMockDepsProvider.SetJson('{"items": [{"uniqueProductId": "PUBID.advania|AID.advania_approvals|PAPPID.603d81ef-542b-46ae-9cb5-17dc16fa3842","displayName": "Dynamics 365 Business Central - First","publisherId": "advania","publisherDisplayName": "Advania","publisherType": "ThirdParty","ratingAverage": 0.0,"ratingCount": 0,"productType": "DynamicsBC","popularity": 7.729569120865367,"privacyPolicyUri": "https://privacy.d365bc.is/","lastModifiedDateTime": "2024-01-19T03:23:15.4319343+00:00"}],"nextPageLink": "next page uri"}'); + // Push second without next page link + AppSourceMockDepsProvider.SetJson('{"items": [{"uniqueProductId": "PUBID.pbsi_software|AID.247timetracker|PAPPID.9a12247e-8564-4b90-b80b-cd5f4b64217e","displayName": "Dynamics 365 Business Central - Second","publisherId": "pbsi_software","publisherDisplayName": "David Boehm, CPA and Company Inc.","publisherType": "ThirdParty","ratingAverage": 5.0,"ratingCount": 2,"productType": "DynamicsBC","popularity": 7.729569120865367,"privacyPolicyUri": "https://pbsisoftware.com/24-7-tt-privacy-statement","lastModifiedDateTime": "2023-09-03T11:08:28.5348241+00:00"}]}'); + AppSrcProductMgrTestImpl.SetDependencies(AppSourceMockDepsProvider); + + // When + AppSrcProductMgrTestImpl.GetProductsAndPopulateRecord(); + + //Then + LibraryAssert.AreEqual(2, AppSrcProductMgrTestImpl.GetProductTableCount(), 'The number of products is incorrect.'); + LibraryAssert.IsTrue(AppSrcProductMgrTestImpl.IsRecordWithDisplayNameinProductTable('Dynamics 365 Business Central - First'), 'The first product name is incorrect.'); + LibraryAssert.IsTrue(AppSrcProductMgrTestImpl.IsRecordWithDisplayNameinProductTable('Dynamics 365 Business Central - Second'), 'The second product name is incorrect.'); + + AppSrcProductMgrTestImpl.ResetDependencies(); + end; + + [Test] + // In AppSource this shows up as having the Buy Now button enabled + procedure TestCanInstallProduct_BuyNow() + var + PlansList: JsonArray; + CanInstall: Boolean; + begin + // Given + // Hello world sample: PUBID.microsoftdynsmb%7CAID.helloworld%7CPAPPID.8e315acc-413d-46d5-abb9-c16912d3f3e3 + PlansList.ReadFrom('[{"id": "0002","availabilities": [{"id": "DZH318Z0BMGT","actions": ["Browse","Curate","Details","License","Purchase"],"meter": null,"pricingAudience": "DirectCommercial","terms": [{"termDescriptionParameters": null,"termId": "bh3541oe15ry","termUnit": "P1M","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Month Trial to 1 Year Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.0,"msrp": 0.0},"renewTermId": "qdp73gtwa5dy","renewTermUnits": "P1Y","isAutorenewable": true},{"termDescriptionParameters": null,"termId": "njspcsugneyy","termUnit": "P1M","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Month Trial to 1 Month Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.0,"msrp": 0.0},"renewTermId": "usrac41besqy","renewTermUnits": "P1M","isAutorenewable": true}],"hasFreeTrials": true,"consumptionUnitType": "DAY","displayRank": 0},{"id": "DZH318Z0BMGP","actions": ["Browse","Curate","Details","License","Purchase","Renew"],"meter": null,"pricingAudience": "DirectCommercial","terms": [{"termDescriptionParameters": null,"termId": "qdp73gtwa5dy","termUnit": "P1Y","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Year Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.02,"msrp": 0.02},"renewTermId": "qdp73gtwa5dy","renewTermUnits": "P1Y","isAutorenewable": true},{"termDescriptionParameters": null,"termId": "usrac41besqy","termUnit": "P1M","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Month Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.01,"msrp": 0.01},"renewTermId": "usrac41besqy","renewTermUnits": "P1M","isAutorenewable": true}],"hasFreeTrials": false,"consumptionUnitType": "DAY","displayRank": 1}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RW15J0G","isHidden": false,"isStopSell": false,"cspState": "OptOut","minQuantity": 1,"maxQuantity": 10,"isQuantifiable": true,"purchaseDurationDiscounts": [],"planId": "transactableplan1","uniquePlanId": "microsoftdynsmb.helloworldtransactableplan1","displayName": "First 10 users","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": ["FreeTrial","Payg"],"description": "Test plan to test first 10 users configurations","skuId": "0002","planType": "DynamicsBC","displayRank": "2147483647","isPrivate": false},{"id": "0003","availabilities": [{"id": "DZH318Z0BMGW","actions": ["Browse","Curate","Details","License","Purchase","Renew"],"meter": null,"pricingAudience": "DirectCommercial","terms": [{"termDescriptionParameters": null,"termId": "qdp73gtwa5dy","termUnit": "P1Y","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Year Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.03,"msrp": 0.03},"renewTermId": "qdp73gtwa5dy","renewTermUnits": "P1Y","isAutorenewable": true},{"termDescriptionParameters": null,"termId": "usrac41besqy","termUnit": "P1M","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Month Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.02,"msrp": 0.02},"renewTermId": "usrac41besqy","renewTermUnits": "P1M","isAutorenewable": true}],"hasFreeTrials": false,"consumptionUnitType": "DAY","displayRank": 0}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RW15J0H","isHidden": false,"isStopSell": false,"cspState": "OptOut","minQuantity": 10,"maxQuantity": 100,"isQuantifiable": true,"purchaseDurationDiscounts": [],"planId": "transactableplan2","uniquePlanId": "microsoftdynsmb.helloworldtransactableplan2","displayName": "Ten to Hundred plan","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": ["Payg"],"description": "Test 10 - 100 User plan","skuId": "0003","planType": "DynamicsBC","displayRank": "2147483647","isPrivate": false}]'); + + // When + CanInstall := AppSrcProductMgrTestImpl.CanInstallProductWithPlans(PlansList); + + // Then + LibraryAssert.IsTrue(CanInstall, 'The product should be installable.'); + end; + + [Test] + // In AppSource this shows up as having the Get It Now button enabled + procedure TestCanInstallProduct_GetItNow() + var + PlansList: JsonArray; + CanInstall: Boolean; + begin + // Given + // Hello world too sample: PUBID.microsoftdynsmb%7CAID.helloworldtoo%7CPAPPID.37447a59-b131-4e9c-83a3-a7856bfc30ff + PlansList.ReadFrom('[{"id": "0001","availabilities": [{"id": "DZH318Z0BMTW","actions": ["Browse","Curate","Details"],"meter": null,"pricingAudience": "DirectCommercial","terms": null,"hasFreeTrials": false,"displayRank": 0}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RE5c9A4","isHidden": false,"isStopSell": false,"isQuantifiable": false,"purchaseDurationDiscounts": [],"planId": "69fe2a5e-cb01-43ea-9af3-2417d0c843f2","uniquePlanId": "microsoftdynsmb.helloworldtoo69fe2a5e-cb01-43ea-9af3-2417d0c843f2","displayName": "HelloWorldToo","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": [],"description": "
desc
","skuId": "0001","planType": "DynamicsBC","isPrivate": false}]'); + + // When + CanInstall := AppSrcProductMgrTestImpl.CanInstallProductWithPlans(PlansList); + + // Then + if (CanInstall) then + LibraryAssert.Fail('Test now produces expected outcome and should be update.'); + end; + + + [Test] + // In AppSource this shows up as having the Free Trial button enabled + procedure TestCanInstallProduct_FreeTrial() + var + PlansList: JsonArray; + CanInstall: Boolean; + begin + // Given + // Manufacturing Central by Intech: PUBID.intechsystems%7CAID.manufacturing_central%7CPAPPID.cea8e27e-050e-4880-840c-954ceb2e3f13 + PlansList.ReadFrom('[{"id": "0001","availabilities": [{"id": "DZH318Z0BMV3","actions": ["Browse","Curate","Details"],"meter": null,"pricingAudience": "DirectCommercial","terms": null,"hasFreeTrials": false,"displayRank": 0}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RW1fI7S","isHidden": false,"isStopSell": false,"isQuantifiable": false,"purchaseDurationDiscounts": [],"planId": "77447b7c-9737-4132-8191-b9ce21d35a0e","uniquePlanId": "intechsystems.manufacturing_central77447b7c-9737-4132-8191-b9ce21d35a0e","displayName": "Manufacturing Central","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": [],"description": "

Hey there

","skuId": "0001","planType": "DynamicsBC","isPrivate": false}]'); + + // When + CanInstall := AppSrcProductMgrTestImpl.CanInstallProductWithPlans(PlansList); + + // Then + if (CanInstall) then + LibraryAssert.Fail('Test now produces expected outcome and should be update.'); + end; + + [Test] + + // In AppSource this shows up as having the Contact Me button enabled + procedure TestCanInstallProduct_ContactMe() + var + PlansList: JsonArray; + CanInstall: Boolean; + begin + // Given + // Salesforce Integration by Celigo Inc: PUBID.celigoinc-causa1621285384596%7CAID.salesforce-integration-celigo%7CPAPPID.0595b87b-7670-4bd2-91e2-bd98a7fe2f5a + PlansList.ReadFrom('[{"id": "0001","availabilities": [{"id": "DZH318Z0BMV7","actions": ["Browse","Curate","Details"],"meter": null,"pricingAudience": "DirectCommercial","terms": null,"hasFreeTrials": false,"displayRank": 0}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RE5fzzR","isHidden": false,"isStopSell": false,"isQuantifiable": false,"purchaseDurationDiscounts": [],"planId": "4b4a5c29-40ad-4169-b23c-1ea9a51fd176","uniquePlanId": "celigoinc-causa1621285384596.salesforce-integration-celigo4b4a5c29-40ad-4169-b23c-1ea9a51fd176","displayName": "Salesforce Integration for Dynamics 365 Business Central","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": [],"description": "
Celigo
","skuId": "0001","planType": "DynamicsBC","isPrivate": false}]'); + + // When + CanInstall := AppSrcProductMgrTestImpl.CanInstallProductWithPlans(PlansList); + + // Then + LibraryAssert.IsFalse(CanInstall, 'The product NOT should be installable.'); + end; + + [HyperlinkHandler] + procedure HyperlinkHandler(Message: Text[1024]) + begin + LibraryAssert.AreEqual(HyperlinkStorage.DequeueText(), Message, 'The hyperlink is incorrect.'); + end; + + internal procedure Initialize() + begin + HyperlinkStorage.Clear(); + end; + + internal procedure AssertCleanedUp() + begin + HyperlinkStorage.AssertEmpty(); + end; +} \ No newline at end of file diff --git a/src/System Application/Test/AppSource Gallery/src/AppSourceProductManagerTest.Codeunit.al b/src/System Application/Test/AppSource Gallery/src/AppSourceProductManagerTest.Codeunit.al deleted file mode 100644 index 5bae511f6e..0000000000 --- a/src/System Application/Test/AppSource Gallery/src/AppSourceProductManagerTest.Codeunit.al +++ /dev/null @@ -1,439 +0,0 @@ -// ------------------------------------------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. -// ------------------------------------------------------------------------------------------------ - -namespace System.Apps.AppSource.Test; - -using System.Apps.AppSource; -using System.RestClient; -using System.Environment.Configuration; -using System.TestLibraries.Utilities; - -codeunit 135074 "AppSource Product Manager Test" implements "AppSource Product Manager Dependencies" -{ - Subtype = Test; - - var - Assert: Codeunit "Library Assert"; - HyperlinkStorage: Codeunit "Library - Variable Storage"; - FormatRegionStore: Codeunit "Library - Variable Storage"; - UserSettingsLanguageIDStore: Codeunit "Library - Variable Storage"; - ApplicationFamilyStore: Codeunit "Library - Variable Storage"; - IsSaasStore: Codeunit "Library - Variable Storage"; - RestClientGetJsonStore: Codeunit "Library - Variable Storage"; - CountryLetterCodeStore: Codeunit "Library - Variable Storage"; - - [Test] - procedure TestExtractAppIDFromUniqueProductIDReturnsExpectedAppId() - var - AppSourceProductManager: codeunit "AppSource Product Manager"; - UniqueId: Text; - AppId: Guid; - ExpectedAppId: Guid; - begin - // Given - UniqueId := 'PUBID.nav24spzoo1579516366010%7CAID.n24_test_transactability%7CPAPPID.0984da34-5ec1-4ac1-9575-b73fb2212327'; - ExpectedAppId := '0984da34-5ec1-4ac1-9575-b73fb2212327'; - - // When - AppId := AppSourceProductManager.ExtractAppIDFromUniqueProductID(UniqueId); - - // Then - Assert.AreEqual(ExpectedAppId, AppId, 'Expected AppId to be extracted from UniqueId'); - end; - - [Test] - procedure TestExtractAppIDFromUniqueProductIDReturnsEmptyAppIdWhenNotValid() - var - AppSourceProductManager: codeunit "AppSource Product Manager"; - UniqueId: Text; - AppId: Guid; - begin - // Given - UniqueId := 'articentgroupllc1635512619530.ackee-ubuntu-18-04-minimal'; - - // When - AppId := AppSourceProductManager.ExtractAppIDFromUniqueProductID(UniqueId); - - // Then - Assert.IsTrue(IsNullGuid(AppId), 'Expected AppId to be empty when not present in the UniqueId'); - end; - - [Test] - [HandlerFunctions('HyperlinkHandler')] - procedure TestOpenAppSourceHyperlink() - var - AppSourceProductManager: codeunit "AppSource Product Manager"; - AppSourceProductManagerTest: Codeunit "AppSource Product Manager Test"; - begin - Initialize(); - // Initialize this copy - AppSourceProductManagerTest.Initialize(); - // Override dependencies - AppSourceProductManager.SetDependencies(AppSourceProductManagerTest); - - // Given - AppSourceProductManagerTest.AddToFormatRegionStore('da-DK'); - - // With handler expectation - HyperlinkStorage.Enqueue('https://appsource.microsoft.com/da-DK/marketplace/apps?product=dynamics-365-business-central'); - - // When - AppSourceProductManager.OpenAppSource(); - - // Then - // Asserted in handler - AppSourceProductManagerTest.AssertCleanedUp(); - AssertCleanedUp(); - end; - - [Test] - [HandlerFunctions('HyperlinkHandler')] - procedure TestOpenInAppSourceHyperlink() - var - AppSourceProductManager: codeunit "AppSource Product Manager"; - AppSourceProductManagerTest: Codeunit "AppSource Product Manager Test"; - UniqueId: Text; - begin - Initialize(); - // Initialize this copy - AppSourceProductManagerTest.Initialize(); - // Override dependencies - AppSourceProductManager.SetDependencies(AppSourceProductManagerTest); - - // Given - AppSourceProductManagerTest.AddToUserSettingsLanguageIDStore(3082); //es-ES - - UniqueId := 'PUBID.nav24spzoo1579516366010%7CAID.n24_test_transactability%7CPAPPID.0984da34-5ec1-4ac1-9575-b73fb2212327'; - - // With handler expectation - HyperlinkStorage.Enqueue('https://appsource.microsoft.com/es-ES/product/dynamics-365-business-central/PUBID.nav24spzoo1579516366010%7CAID.n24_test_transactability%7CPAPPID.0984da34-5ec1-4ac1-9575-b73fb2212327'); - - // When - AppSourceProductManager.OpenAppInAppSource(UniqueId); - - // Then - // Asserted in handler - AppSourceProductManagerTest.AssertCleanedUp(); - AssertCleanedUp(); - end; - - [Test] - procedure TestLoadProductNotAllowedOnPremises() - var - TempProduct: Record "AppSource Product" temporary; - AppSourceProductManager: codeunit "AppSource Product Manager"; - AppSourceProductManagerTest: Codeunit "AppSource Product Manager Test"; - begin - Initialize(); - // Initialize this copy - AppSourceProductManagerTest.Initialize(); - // Override dependencies - AppSourceProductManager.SetDependencies(AppSourceProductManagerTest); - - // Given - AppSourceProductManagerTest.AddToIsSaasStore(false); - AppSourceProductManagerTest.AddToUserSettingsLanguageIDStore(3082); //es-ES - AppSourceProductManagerTest.AddToApplicationFamilyStore('W1'); - AppSourceProductManagerTest.AddToCountryLetterCodeStore('dk'); - - // When - asserterror AppSourceProductManager.GetProductsAndPopulateRecord(TempProduct); - - // Then - Assert.ExpectedError('Not supported on premises.'); - - AppSourceProductManagerTest.AssertCleanedUp(); - AssertCleanedUp(); - end; - - [Test] - procedure TestLoadProduct() - var - TempAppSourceProduct: Record "AppSource Product" temporary; - AppSourceProductManager: codeunit "AppSource Product Manager"; - AppSourceProductManagerTest: Codeunit "AppSource Product Manager Test"; - begin - Initialize(); - - // Initialize this copy - AppSourceProductManagerTest.Initialize(); - // Override dependencies - AppSourceProductManager.SetDependencies(AppSourceProductManagerTest); - - // Given - AppSourceProductManagerTest.AddToIsSaasStore(true); - AppSourceProductManagerTest.AddToApplicationFamilyStore('W1'); - AppSourceProductManagerTest.AddToUserSettingsLanguageIDStore(3082); //es-ES - AppSourceProductManagerTest.AddToCountryLetterCodeStore('us'); - AppSourceProductManagerTest.AddToRestClientGetJsonStore('{"items": [{"uniqueProductId": "PUBID.pbsi_software|AID.247timetracker|PAPPID.9a12247e-8564-4b90-b80b-cd5f4b64217e","displayName": "Dynamics 365 Business Central","publisherId": "pbsi_software","publisherDisplayName": "David Boehm, CPA and Company Inc.","publisherType": "ThirdParty","ratingAverage": 5.0,"ratingCount": 2,"productType": "DynamicsBC","popularity": 7.729569120865367,"privacyPolicyUri": "https://pbsisoftware.com/24-7-tt-privacy-statement","lastModifiedDateTime": "2023-09-03T11:08:28.5348241+00:00"}]}'); - // When - AppSourceProductManager.GetProductsAndPopulateRecord(TempAppSourceProduct); - - // Then - Assert.AreEqual(TempAppSourceProduct.Count, 1, 'The number of products is incorrect.'); - Assert.AreEqual('Dynamics 365 Business Central', TempAppSourceProduct.DisplayName, 'The product name is incorrect.'); - AppSourceProductManagerTest.AssertCleanedUp(); - AssertCleanedUp(); - end; - - [Test] - procedure TestLoadProductWithNextPageLink() - var - TempAppSourceProduct: Record "AppSource Product" temporary; - AppSourceProductManager: codeunit "AppSource Product Manager"; - AppSourceProductManagerTest: Codeunit "AppSource Product Manager Test"; - begin - Initialize(); - // Initialize this copy - AppSourceProductManagerTest.Initialize(); - // Override dependencies - AppSourceProductManager.SetDependencies(AppSourceProductManagerTest); - - // Given - AppSourceProductManagerTest.AddToIsSaasStore(true); - AppSourceProductManagerTest.AddToApplicationFamilyStore('W1'); - AppSourceProductManagerTest.AddToUserSettingsLanguageIDStore(3082); //es-ES - AppSourceProductManagerTest.AddToCountryLetterCodeStore('dk'); - - // Push first with next page link - AppSourceProductManagerTest.AddToRestClientGetJsonStore('{"items": [{"uniqueProductId": "PUBID.advania|AID.advania_approvals|PAPPID.603d81ef-542b-46ae-9cb5-17dc16fa3842","displayName": "Dynamics 365 Business Central - First","publisherId": "advania","publisherDisplayName": "Advania","publisherType": "ThirdParty","ratingAverage": 0.0,"ratingCount": 0,"productType": "DynamicsBC","popularity": 7.729569120865367,"privacyPolicyUri": "https://privacy.d365bc.is/","lastModifiedDateTime": "2024-01-19T03:23:15.4319343+00:00"}],"nextPageLink": "next page uri"}'); - // Push second without next page link - AppSourceProductManagerTest.AddToRestClientGetJsonStore('{"items": [{"uniqueProductId": "PUBID.pbsi_software|AID.247timetracker|PAPPID.9a12247e-8564-4b90-b80b-cd5f4b64217e","displayName": "Dynamics 365 Business Central - Second","publisherId": "pbsi_software","publisherDisplayName": "David Boehm, CPA and Company Inc.","publisherType": "ThirdParty","ratingAverage": 5.0,"ratingCount": 2,"productType": "DynamicsBC","popularity": 7.729569120865367,"privacyPolicyUri": "https://pbsisoftware.com/24-7-tt-privacy-statement","lastModifiedDateTime": "2023-09-03T11:08:28.5348241+00:00"}]}'); - - // When - AppSourceProductManager.GetProductsAndPopulateRecord(TempAppSourceProduct); - - // Then - Assert.AreEqual(2, TempAppSourceProduct.Count, 'The number of products is incorrect.'); - TempAppSourceProduct.FindSet(); - Assert.AreEqual('Dynamics 365 Business Central - First', TempAppSourceProduct.DisplayName, 'The first product name is incorrect.'); - TempAppSourceProduct.Next(); - Assert.AreEqual('Dynamics 365 Business Central - Second', TempAppSourceProduct.DisplayName, 'The second product name is incorrect.'); - - AppSourceProductManagerTest.AssertCleanedUp(); - AssertCleanedUp(); - end; - - [Test] - // In AppSource this shows up as having the Buy Now button enabled - procedure TestCanInstallProduct_BuyNow() - var - AppSourceProductManager: codeunit "AppSource Product Manager"; - PlansList: JsonArray; - CanInstall: Boolean; - begin - // Given - // Hello world sample: PUBID.microsoftdynsmb%7CAID.helloworld%7CPAPPID.8e315acc-413d-46d5-abb9-c16912d3f3e3 - PlansList.ReadFrom('[{"id": "0002","availabilities": [{"id": "DZH318Z0BMGT","actions": ["Browse","Curate","Details","License","Purchase"],"meter": null,"pricingAudience": "DirectCommercial","terms": [{"termDescriptionParameters": null,"termId": "bh3541oe15ry","termUnit": "P1M","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Month Trial to 1 Year Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.0,"msrp": 0.0},"renewTermId": "qdp73gtwa5dy","renewTermUnits": "P1Y","isAutorenewable": true},{"termDescriptionParameters": null,"termId": "njspcsugneyy","termUnit": "P1M","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Month Trial to 1 Month Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.0,"msrp": 0.0},"renewTermId": "usrac41besqy","renewTermUnits": "P1M","isAutorenewable": true}],"hasFreeTrials": true,"consumptionUnitType": "DAY","displayRank": 0},{"id": "DZH318Z0BMGP","actions": ["Browse","Curate","Details","License","Purchase","Renew"],"meter": null,"pricingAudience": "DirectCommercial","terms": [{"termDescriptionParameters": null,"termId": "qdp73gtwa5dy","termUnit": "P1Y","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Year Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.02,"msrp": 0.02},"renewTermId": "qdp73gtwa5dy","renewTermUnits": "P1Y","isAutorenewable": true},{"termDescriptionParameters": null,"termId": "usrac41besqy","termUnit": "P1M","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Month Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.01,"msrp": 0.01},"renewTermId": "usrac41besqy","renewTermUnits": "P1M","isAutorenewable": true}],"hasFreeTrials": false,"consumptionUnitType": "DAY","displayRank": 1}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RW15J0G","isHidden": false,"isStopSell": false,"cspState": "OptOut","minQuantity": 1,"maxQuantity": 10,"isQuantifiable": true,"purchaseDurationDiscounts": [],"planId": "transactableplan1","uniquePlanId": "microsoftdynsmb.helloworldtransactableplan1","displayName": "First 10 users","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": ["FreeTrial","Payg"],"description": "Test plan to test first 10 users configurations","skuId": "0002","planType": "DynamicsBC","displayRank": "2147483647","isPrivate": false},{"id": "0003","availabilities": [{"id": "DZH318Z0BMGW","actions": ["Browse","Curate","Details","License","Purchase","Renew"],"meter": null,"pricingAudience": "DirectCommercial","terms": [{"termDescriptionParameters": null,"termId": "qdp73gtwa5dy","termUnit": "P1Y","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Year Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.03,"msrp": 0.03},"renewTermId": "qdp73gtwa5dy","renewTermUnits": "P1Y","isAutorenewable": true},{"termDescriptionParameters": null,"termId": "usrac41besqy","termUnit": "P1M","prorationPolicy": {"minimumProratedUnits": "P1D"},"termDescription": "1 Month Subscription","price": {"currencyCode": "USD","isPIRequired": true,"listPrice": 0.02,"msrp": 0.02},"renewTermId": "usrac41besqy","renewTermUnits": "P1M","isAutorenewable": true}],"hasFreeTrials": false,"consumptionUnitType": "DAY","displayRank": 0}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RW15J0H","isHidden": false,"isStopSell": false,"cspState": "OptOut","minQuantity": 10,"maxQuantity": 100,"isQuantifiable": true,"purchaseDurationDiscounts": [],"planId": "transactableplan2","uniquePlanId": "microsoftdynsmb.helloworldtransactableplan2","displayName": "Ten to Hundred plan","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": ["Payg"],"description": "Test 10 - 100 User plan","skuId": "0003","planType": "DynamicsBC","displayRank": "2147483647","isPrivate": false}]'); - - // When - CanInstall := AppSourceProductManager.CanInstallProductWithPlans(PlansList); - - // Then - Assert.IsTrue(CanInstall, 'The product should be installable.'); - end; - - [Test] - // In AppSource this shows up as having the Get It Now button enabled - procedure TestCanInstallProduct_GetItNow() - var - AppSourceProductManager: codeunit "AppSource Product Manager"; - PlansList: JsonArray; - CanInstall: Boolean; - begin - // Given - // Hello world too sample: PUBID.microsoftdynsmb%7CAID.helloworldtoo%7CPAPPID.37447a59-b131-4e9c-83a3-a7856bfc30ff - PlansList.ReadFrom('[{"id": "0001","availabilities": [{"id": "DZH318Z0BMTW","actions": ["Browse","Curate","Details"],"meter": null,"pricingAudience": "DirectCommercial","terms": null,"hasFreeTrials": false,"displayRank": 0}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RE5c9A4","isHidden": false,"isStopSell": false,"isQuantifiable": false,"purchaseDurationDiscounts": [],"planId": "69fe2a5e-cb01-43ea-9af3-2417d0c843f2","uniquePlanId": "microsoftdynsmb.helloworldtoo69fe2a5e-cb01-43ea-9af3-2417d0c843f2","displayName": "HelloWorldToo","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": [],"description": "
desc
","skuId": "0001","planType": "DynamicsBC","isPrivate": false}]'); - - // When - CanInstall := AppSourceProductManager.CanInstallProductWithPlans(PlansList); - - // Then - if (CanInstall) then - Assert.Fail('Test now produces expected outcome and should be update.'); - // Assert.IsTrue(CanInstall, 'The product should be installable.'); - end; - - - [Test] - // In AppSource this shows up as having the Free Trial button enabled - procedure TestCanInstallProduct_FreeTrial() - var - AppSourceProductManager: codeunit "AppSource Product Manager"; - PlansList: JsonArray; - CanInstall: Boolean; - begin - // Given - // Manufacturing Central by Intech: PUBID.intechsystems%7CAID.manufacturing_central%7CPAPPID.cea8e27e-050e-4880-840c-954ceb2e3f13 - PlansList.ReadFrom('[{"id": "0001","availabilities": [{"id": "DZH318Z0BMV3","actions": ["Browse","Curate","Details"],"meter": null,"pricingAudience": "DirectCommercial","terms": null,"hasFreeTrials": false,"displayRank": 0}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RW1fI7S","isHidden": false,"isStopSell": false,"isQuantifiable": false,"purchaseDurationDiscounts": [],"planId": "77447b7c-9737-4132-8191-b9ce21d35a0e","uniquePlanId": "intechsystems.manufacturing_central77447b7c-9737-4132-8191-b9ce21d35a0e","displayName": "Manufacturing Central","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": [],"description": "

Hey there

","skuId": "0001","planType": "DynamicsBC","isPrivate": false}]'); - - // When - CanInstall := AppSourceProductManager.CanInstallProductWithPlans(PlansList); - - // Then - if (CanInstall) then - Assert.Fail('Test now produces expected outcome and should be update.'); - // Assert.IsTrue(CanInstall, 'The product should be installable.'); - end; - - [Test] - - // In AppSource this shows up as having the Contact Me button enabled - procedure TestCanInstallProduct_ContactMe() - var - AppSourceProductManager: codeunit "AppSource Product Manager"; - PlansList: JsonArray; - CanInstall: Boolean; - begin - // Given - // Salesforce Integration by Celigo Inc: PUBID.celigoinc-causa1621285384596%7CAID.salesforce-integration-celigo%7CPAPPID.0595b87b-7670-4bd2-91e2-bd98a7fe2f5a - PlansList.ReadFrom('[{"id": "0001","availabilities": [{"id": "DZH318Z0BMV7","actions": ["Browse","Curate","Details"],"meter": null,"pricingAudience": "DirectCommercial","terms": null,"hasFreeTrials": false,"displayRank": 0}],"uiDefinitionUri": "https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RE5fzzR","isHidden": false,"isStopSell": false,"isQuantifiable": false,"purchaseDurationDiscounts": [],"planId": "4b4a5c29-40ad-4169-b23c-1ea9a51fd176","uniquePlanId": "celigoinc-causa1621285384596.salesforce-integration-celigo4b4a5c29-40ad-4169-b23c-1ea9a51fd176","displayName": "Salesforce Integration for Dynamics 365 Business Central","metadata": {"generation": null,"altStackReference": null},"categoryIds": [],"pricingTypes": [],"description": "
Celigo
","skuId": "0001","planType": "DynamicsBC","isPrivate": false}]'); - - // When - CanInstall := AppSourceProductManager.CanInstallProductWithPlans(PlansList); - - // Then - Assert.IsFalse(CanInstall, 'The product NOT should be installable.'); - end; - - [HyperlinkHandler] - procedure HyperlinkHandler(Message: Text[1024]) - begin - Assert.AreEqual(HyperlinkStorage.DequeueText(), Message, 'The hyperlink is incorrect.'); - end; - - internal procedure Initialize() - begin - HyperlinkStorage.Clear(); - FormatRegionStore.Clear(); - UserSettingsLanguageIDStore.Clear(); - ApplicationFamilyStore.Clear(); - IsSaasStore.Clear(); - RestClientGetJsonStore.Clear(); - CountryLetterCodeStore.Clear(); - end; - - internal procedure AssertCleanedUp() - begin - HyperlinkStorage.AssertEmpty(); - FormatRegionStore.AssertEmpty(); - UserSettingsLanguageIDStore.AssertEmpty(); - ApplicationFamilyStore.AssertEmpty(); - IsSaasStore.AssertEmpty(); - RestClientGetJsonStore.AssertEmpty(); - CountryLetterCodeStore.AssertEmpty(); - end; - - #region this helpers - internal procedure AddToFormatRegionStore(FormatRegion: Text[80]) - begin - FormatRegionStore.Enqueue(FormatRegion); - end; - - internal procedure AddToUserSettingsLanguageIDStore(LanguageId: Integer) - begin - UserSettingsLanguageIDStore.Enqueue(LanguageId); - end; - - internal procedure AddToApplicationFamilyStore(ApplicationFamily: Text) - begin - ApplicationFamilyStore.Enqueue(ApplicationFamily); - end; - - internal procedure AddToIsSaasStore(IsSaasValue: Boolean) - begin - IsSaasStore.Enqueue(IsSaasValue); - end; - - internal procedure AddToRestClientGetJsonStore(JsonText: Text) - var - JsonToken: JsonToken; - begin - JsonToken.ReadFrom(JsonText); - RestClientGetJsonStore.Enqueue(JsonToken); - end; - - internal procedure AddToCountryLetterCodeStore(CountryLetterCode: Text[2]) - begin - CountryLetterCodeStore.Enqueue(CountryLetterCode); - end; - - #endregion - - #region dependencies implementation - - procedure ShouldSetCommonHeaders(): Boolean - begin - exit(false); - end; - - procedure GetCountryLetterCode(): Code[2] - begin - if (CountryLetterCodeStore.Length() > 0) then - exit(CopyStr(CountryLetterCodeStore.DequeueText(), 1, 2)); - - Assert.Fail('GetCountryLetterCode should not be called'); - end; - - procedure GetPreferredLanguage(): Text - begin - Assert.Fail('GetPreferredLanguage should not be called'); - end; - - // Dependency to Environment Information - procedure GetApplicationFamily(): Text - begin - if (ApplicationFamilyStore.Length() > 0) then - exit(ApplicationFamilyStore.DequeueText()); - - Assert.Fail('GetApplicationFamily should not be called'); - end; - - procedure IsSaas(): boolean - begin - if (IsSaasStore.Length() > 0) then - exit(IsSaasStore.DequeueBoolean()); - - Assert.Fail('IsSaas should not be called'); - end; - - // Dependency to Language - procedure GetFormatRegionOrDefault(FormatRegion: Text[80]): Text - begin - if (FormatRegionStore.Length() > 0) then - exit(FormatRegionStore.DequeueText()); - - Assert.Fail('GetFormatRegionOrDefault should not be called'); - end; - - procedure GetAsJSon(var RestClient: Codeunit "Rest Client"; RequestUri: Text): JsonToken - var - ValueVariant: Variant; - begin - if (RestClientGetJsonStore.Length() > 0) then begin - RestClientGetJsonStore.Dequeue(ValueVariant); - exit(ValueVariant); - end; - - Assert.Fail('GetAsJSon should not be called'); - end; - - // Dependency to User Settings - procedure GetUserSettings(UserSecurityID: Guid; var TempUserSettingsRecord: Record "User Settings" temporary) - var - LanguageID: Variant; - begin - if (UserSettingsLanguageIDStore.Length() > 0) then begin - TempUserSettingsRecord.Init(); - TempUserSettingsRecord."User Security ID" := UserSecurityID; - UserSettingsLanguageIDStore.Dequeue(LanguageID); - TempUserSettingsRecord."Language ID" := LanguageID; - exit; - end; - Assert.Fail('GetUserSettings should not be called'); - end; - #endregion - -} \ No newline at end of file