From 9e4e6f6692fbae4d322e2a62d987e3c9acfcee29 Mon Sep 17 00:00:00 2001 From: Max Ewing Date: Thu, 11 Mar 2021 13:20:38 +0700 Subject: [PATCH] fix: customer fields not supported by data steps (#69) BREAKING CHANGE: removes `V2` extension methods used as temporary workarounds for EasyRepro issues. --- ...apgemini.PowerApps.SpecFlowBindings.csproj | 2 +- .../Extensions/WebClientExtensions.cs | 207 ------------------ .../Steps/CommandBarSteps.cs | 8 +- .../Steps/EntitySubGridSteps.cs | 12 +- .../Steps/LoginSteps.cs | 3 +- .../Hooks/EasyReproSelectorFixHooks.cs | 33 --- driver/src/data/deepInsertService.ts | 25 ++- driver/src/repositories/metadataRepository.ts | 6 +- driver/test/data/deepInsertService.spec.ts | 64 +++++- .../authenticatedUserRecordRepository.spec.ts | 2 +- .../repositories/metadataRepository.spec.ts | 20 +- templates/include-build-and-test-steps.yml | 2 +- 12 files changed, 115 insertions(+), 269 deletions(-) delete mode 100644 bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/WebClientExtensions.cs delete mode 100644 bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Hooks/EasyReproSelectorFixHooks.cs diff --git a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Capgemini.PowerApps.SpecFlowBindings.csproj b/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Capgemini.PowerApps.SpecFlowBindings.csproj index d0bb25f..3f44608 100644 --- a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Capgemini.PowerApps.SpecFlowBindings.csproj +++ b/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Capgemini.PowerApps.SpecFlowBindings.csproj @@ -45,7 +45,7 @@ - + all diff --git a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/WebClientExtensions.cs b/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/WebClientExtensions.cs deleted file mode 100644 index 3b81284..0000000 --- a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/WebClientExtensions.cs +++ /dev/null @@ -1,207 +0,0 @@ -namespace Capgemini.PowerApps.SpecFlowBindings.Extensions -{ - using System; - using System.Linq; - using Microsoft.Dynamics365.UIAutomation.Api.UCI; - using Microsoft.Dynamics365.UIAutomation.Browser; - using OpenQA.Selenium; - - /// - /// Extensions to the class. - /// - public static class WebClientExtensions - { - /// - /// Temporary workaround until https://github.com/microsoft/EasyRepro/issues/1087 is resolved. - /// - /// The . - /// The name of the subgrid. - /// The name of the command. - /// The name of the sub-command. - /// The name of the second sub-command. - /// The . - public static BrowserCommandResult ClickSubGridCommandV2(this WebClient webClient, string subGridName, string name, string subName = null, string subSecondName = null) - { - return webClient.Execute(GetOptions("Click SubGrid Command"), driver => - { - var subGrid = driver.FindElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridContents].Replace("[NAME]", subGridName))); - - if (subGrid.TryFindElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridCommandBar].Replace("[NAME]", subGridName)), out var subGridCommandBar)) - { - var items = subGridCommandBar.FindElements(By.TagName("button")); - if (items.Any(x => x.GetAttribute("aria-label").Equals(name, StringComparison.OrdinalIgnoreCase))) - { - items.FirstOrDefault(x => x.GetAttribute("aria-label").Equals(name, StringComparison.OrdinalIgnoreCase)).Click(true); - driver.WaitForTransaction(); - } - else - { - if (items.Any(x => x.GetAttribute("aria-label").Contains("More Commands", StringComparison.OrdinalIgnoreCase))) - { - items.FirstOrDefault(x => x.GetAttribute("aria-label").Contains("More Commands", StringComparison.OrdinalIgnoreCase)).Click(true); - driver.WaitForTransaction(); - - var overflowContainer = driver.FindElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowContainer])); - - if (overflowContainer.HasElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", name)))) - { - overflowContainer.FindElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", name))).Click(true); - driver.WaitForTransaction(); - } - else - { - throw new InvalidOperationException($"No command with the name '{name}' exists inside of {subGridName} Commandbar."); - } - } - else - { - throw new InvalidOperationException($"No command with the name '{name}' exists inside of {subGridName} CommandBar."); - } - } - - if (subName != null) - { - var overflowContainer = driver.FindElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowContainer])); - - if (overflowContainer.HasElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", subName)))) - { - overflowContainer.FindElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", subName))).Click(true); - driver.WaitForTransaction(); - } - else - { - throw new InvalidOperationException($"No command with the name '{subName}' exists under the {name} command inside of {subGridName} Commandbar."); - } - - if (subSecondName != null) - { - overflowContainer = driver.FindElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowContainer])); - - if (overflowContainer.HasElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", subSecondName)))) - { - overflowContainer.FindElement(By.XPath(AppElements.Xpath[AppReference.Entity.SubGridOverflowButton].Replace("[NAME]", subSecondName))).Click(true); - driver.WaitForTransaction(); - } - else - { - throw new InvalidOperationException($"No command with the name '{subSecondName}' exists under the {subName} command inside of {name} on the {subGridName} SubGrid Commandbar."); - } - } - } - } - else - { - throw new InvalidOperationException($"Unable to locate the Commandbar for the {subGrid} SubGrid."); - } - - return true; - }); - } - - /// - /// Temporary workaround until https://github.com/microsoft/EasyRepro/issues/1087 is resolved. - /// - /// The . - /// The name of the command. - /// The name of the sub-command. - /// The name of the second sub-command. - /// The think time. - /// The . - public static BrowserCommandResult ClickCommandV2(this WebClient webClient, string name, string subname = null, string subSecondName = null, int thinkTime = Constants.DefaultThinkTime) - { - return webClient.Execute(GetOptions($"Click Command"), driver => - { - var ribbon = driver.WaitUntilAvailable( - By.XPath(AppElements.Xpath[AppReference.CommandBar.Container]), - TimeSpan.FromSeconds(5)); - - if (ribbon == null) - { - ribbon = driver.WaitUntilAvailable( - By.XPath(AppElements.Xpath[AppReference.CommandBar.ContainerGrid]), - TimeSpan.FromSeconds(5), - "Unable to find the ribbon."); - } - - var items = ribbon.FindElements(By.TagName("button")); - - if (items.Any(x => x.GetAttribute("aria-label").Equals(name, StringComparison.OrdinalIgnoreCase))) - { - items.FirstOrDefault(x => x.GetAttribute("aria-label").Equals(name, StringComparison.OrdinalIgnoreCase)).Click(true); - driver.WaitForTransaction(); - } - else - { - if (items.Any(x => x.GetAttribute("aria-label").Contains("More Commands", StringComparison.OrdinalIgnoreCase))) - { - items.FirstOrDefault(x => x.GetAttribute("aria-label").Contains("More Commands", StringComparison.OrdinalIgnoreCase)).Click(true); - driver.WaitForTransaction(); - - if (driver.HasElement(By.XPath(AppElements.Xpath[AppReference.CommandBar.Button].Replace("[NAME]", name)))) - { - driver.FindElement(By.XPath(AppElements.Xpath[AppReference.CommandBar.Button].Replace("[NAME]", name))).Click(true); - driver.WaitForTransaction(); - } - else - { - throw new InvalidOperationException($"No command with the name '{name}' exists inside of Commandbar."); - } - } - else - { - throw new InvalidOperationException($"No command with the name '{name}' exists inside of Commandbar."); - } - } - - if (!string.IsNullOrEmpty(subname)) - { - var submenu = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.CommandBar.MoreCommandsMenu])); - - var subbutton = submenu.FindElements(By.TagName("button")).FirstOrDefault(x => x.Text == subname); - - if (subbutton != null) - { - subbutton.Click(true); - } - else - { - throw new InvalidOperationException($"No sub command with the name '{subname}' exists inside of Commandbar."); - } - - if (!string.IsNullOrEmpty(subSecondName)) - { - var subSecondmenu = driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.CommandBar.MoreCommandsMenu])); - - var subSecondbutton = subSecondmenu.FindElements(By.TagName("button")).FirstOrDefault(x => x.Text == subSecondName); - - if (subSecondbutton != null) - { - subSecondbutton.Click(true); - } - else - { - throw new InvalidOperationException($"No sub command with the name '{subSecondName}' exists inside of Commandbar."); - } - } - } - - driver.WaitForTransaction(); - - return true; - }); - } - - private static BrowserCommandOptions GetOptions(string commandName) - { - return new BrowserCommandOptions( - Constants.DefaultTraceSource, - commandName, - Constants.DefaultRetryAttempts, - Constants.DefaultRetryDelay, - null, - true, - typeof(NoSuchElementException), - typeof(StaleElementReferenceException)); - } - } -} \ No newline at end of file diff --git a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/CommandBarSteps.cs b/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/CommandBarSteps.cs index 9eeceef..4f42cc4 100644 --- a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/CommandBarSteps.cs +++ b/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/CommandBarSteps.cs @@ -17,9 +17,7 @@ public class CommandBarSteps : PowerAppsStepDefiner [When("I select the '(.*)' command")] public static void WhenISelectTheCommand(string commandName) { - // TODO: Replace with commented out code when new EasyRepro version available. - // XrmApp.CommandBar.ClickCommand(commandName); - Client.ClickCommandV2(commandName); + XrmApp.CommandBar.ClickCommand(commandName); } /// @@ -30,9 +28,7 @@ public static void WhenISelectTheCommand(string commandName) [When("I select the '([^']+)' command under the '([^']+)' flyout")] public static void WhenISelectTheCommandUnderTheFlyout(string commandName, string flyoutName) { - // TODO: Replace with commented out code when new EasyRepro version available. - // XrmApp.CommandBar.ClickCommand(flyoutName, commandName); - Client.ClickCommandV2(flyoutName, commandName); + XrmApp.CommandBar.ClickCommand(flyoutName, commandName); } /// diff --git a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/EntitySubGridSteps.cs b/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/EntitySubGridSteps.cs index b4ede6c..82aa64a 100644 --- a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/EntitySubGridSteps.cs +++ b/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/EntitySubGridSteps.cs @@ -28,9 +28,7 @@ public static void WhenISelectTheCommandOnTheSubgrid(string commandName, string Driver.WaitUntilVisible( By.CssSelector($"div#dataSetRoot_{subGridName} button[aria-label=\"{commandName}\"]")); - // TODO: Replace with commented out code when new EasyRepro version available. - // XrmApp.Entity.SubGrid.ClickCommand(subGridName, commandName); - Client.ClickSubGridCommandV2(subGridName, commandName); + XrmApp.Entity.SubGrid.ClickCommand(subGridName, commandName); } /// @@ -238,9 +236,7 @@ public static void WhenIClickTheFlyoutOnTheSubgrid(string flyoutName, string sub { Driver.WaitUntilVisible(By.CssSelector($"div#dataSetRoot_{subGridName} li[aria-label=\"{flyoutName}\"]")); - // TODO: Replace with commented out code when new EasyRepro version available. - // XrmApp.Entity.SubGrid.ClickCommand(subGridName, flyoutName); - Client.ClickSubGridCommandV2(subGridName, flyoutName); + XrmApp.Entity.SubGrid.ClickCommand(subGridName, flyoutName); } /// @@ -295,9 +291,7 @@ public static void ThenICanNotSeeTheCommandOnTheFlyoutOfTheSubgrid(string comman [When(@"I click the '([^']+)' command under the '([^']+)' flyout on the '([^']+)' subgrid")] public static void WhenIClickTheCommandUnderTheFlyoutOnTheSubgrid(string commandName, string flyoutName, string subGridName) { - // TODO: Replace with commented out code when new EasyRepro version available. - // XrmApp.Entity.SubGrid.ClickCommand(subGridName, flyoutName, commandName); - Client.ClickSubGridCommandV2(subGridName, flyoutName, commandName); + XrmApp.Entity.SubGrid.ClickCommand(subGridName, flyoutName, commandName); } /// diff --git a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/LoginSteps.cs b/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/LoginSteps.cs index 5c77b50..f962108 100644 --- a/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/LoginSteps.cs +++ b/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/LoginSteps.cs @@ -23,7 +23,8 @@ public static void GivenIAmLoggedInToTheAppAs(string appName, string userAlias) XrmApp.OnlineLogin.Login( TestConfig.GetTestUrl(), user.Username.ToSecureString(), - user.Password.ToSecureString()); + user.Password.ToSecureString(), + string.Empty.ToSecureString()); XrmApp.Navigation.OpenApp(appName); diff --git a/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Hooks/EasyReproSelectorFixHooks.cs b/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Hooks/EasyReproSelectorFixHooks.cs deleted file mode 100644 index 1169725..0000000 --- a/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Hooks/EasyReproSelectorFixHooks.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Microsoft.Dynamics365.UIAutomation.Api.UCI; -using Microsoft.Xrm.Tooling.Connector; -using System.IO; -using System.Reflection; -using TechTalk.SpecFlow; - -namespace Capgemini.PowerApps.SpecFlowBindings.UiTests.Hooks -{ - /// - /// Temporary hooks related to fixing broken EasyRepro selectors. - /// - [Binding] - public class EasyReproSelectorFixHooks : PowerAppsStepDefiner - { - [BeforeTestRun] - public static void FixQuickCreateMenuItemSelector() - { - AppElements.Xpath[AppReference.Navigation.QuickCreateMenuItems] = "//button[@role='menuitem']"; - } - - [BeforeTestRun] - public static void FixEntitySubGridOverflowButtonSelector() - { - AppElements.Xpath[AppReference.Entity.SubGridOverflowButton] = ".//button[contains(@aria-label, '[NAME]')]"; - } - - [BeforeTestRun] - public static void FixRelatedCommandBarButtonSelector() - { - AppElements.Xpath[AppReference.Related.CommandBarButton] = ".//button[contains(@aria-label, '[NAME]') and contains(@id,'SubGrid')]"; - } - } -} \ No newline at end of file diff --git a/driver/src/data/deepInsertService.ts b/driver/src/data/deepInsertService.ts index 745d95e..69ee44e 100644 --- a/driver/src/data/deepInsertService.ts +++ b/driver/src/data/deepInsertService.ts @@ -135,9 +135,32 @@ export default class DeepInsertService { const record = entity; delete record[singleNavProp]; - const entityName = await this.metadataRepository.getEntityForLookupProperty( + let entityName: string | null = null; + const lookupEntity = navigationPropertyMap[singleNavProp]; + + const targets = await this.metadataRepository.getTargetsForLookupProperty( logicalName, singleNavProp, ); + + if (!targets && (singleNavProp.endsWith('_account') || singleNavProp.endsWith('_contact'))) { + // Possibly a customer field + const fieldWithoutSuffix = singleNavProp.replace('_account', '').replace('_contact', ''); + const customerTargets = await this.metadataRepository.getTargetsForLookupProperty( + logicalName, fieldWithoutSuffix, + ); + if (customerTargets && customerTargets.length === 2) { + entityName = singleNavProp.endsWith('account') ? 'account' : 'contact'; + } + } else if (targets && targets.length === 1) { + [entityName] = targets; + } else if (lookupEntity['@logicalName']) { + entityName = lookupEntity['@logicalName'] as string; + } + + if (!entityName) { + throw new Error(`Unable to determine target entity for ${singleNavProp}.`); + } + const deepInsertResponse = await this.deepInsert( entityName, navigationPropertyMap[singleNavProp], createdRecordsByAlias, repository, ); diff --git a/driver/src/repositories/metadataRepository.ts b/driver/src/repositories/metadataRepository.ts index f1afbe0..5de2721 100644 --- a/driver/src/repositories/metadataRepository.ts +++ b/driver/src/repositories/metadataRepository.ts @@ -40,8 +40,8 @@ export default class MetadataRepository { * @returns {Promise} A promise which contains the logical name of the related entity. * @memberof MetadataRepository */ - public async getEntityForLookupProperty(logicalName: string, navigationProperty: string) - : Promise { + public async getTargetsForLookupProperty(logicalName: string, navigationProperty: string) + : Promise { const response = await fetch( 'api/data/v9.1/' + `${MetadataRepository.EntityMetadataSet}(LogicalName='${logicalName}')/Attributes/Microsoft.Dynamics.CRM.LookupAttributeMetadata?$filter=LogicalName eq '${navigationProperty.toLowerCase()}'&$select=Targets`, @@ -49,7 +49,7 @@ export default class MetadataRepository { ); const result = await response.json(); - return result.value[0].Targets[0]; + return result.value[0] ? result.value[0].Targets : null; } /** diff --git a/driver/test/data/deepInsertService.spec.ts b/driver/test/data/deepInsertService.spec.ts index 8c19582..7b88e39 100644 --- a/driver/test/data/deepInsertService.spec.ts +++ b/driver/test/data/deepInsertService.spec.ts @@ -7,6 +7,7 @@ function mockRecord( ): Record { metadataRepo.getLookupPropertyForCollectionProperty.and.resolveTo('customerid_account'); metadataRepo.getRelationshipMetadata.and.resolveTo({ RelationshipType: 'OneToManyRelationship' } as Xrm.Metadata.OneToNRelationshipMetadata); + metadataRepo.getTargetsForLookupProperty.and.resolveTo(['contact']); recordRepo.upsertRecord .and.returnValues( Promise.resolve({ id: '', entityType: 'account' }), @@ -48,7 +49,7 @@ describe('DeepInsertService', () => { metadataRepo = jasmine.createSpyObj('MetadataRepository', [ 'getEntitySetForEntity', - 'getEntityForLookupProperty', + 'getTargetsForLookupProperty', 'getLookupPropertyForCollectionProperty', 'getRelationshipMetadata', ]); @@ -68,6 +69,7 @@ describe('DeepInsertService', () => { it('replaces nested objects with @odata.bind associations', async () => { const entitySetName = 'contacts'; const contactId = ''; + metadataRepo.getTargetsForLookupProperty.and.resolveTo(['contact']); metadataRepo.getEntitySetForEntity.and.resolveTo(entitySetName); recordRepo.upsertRecord.and.returnValues( Promise.resolve({ id: contactId, entityType: 'contact' }), @@ -191,6 +193,7 @@ describe('DeepInsertService', () => { }); it("queries for nested object's entity set name", async () => { + metadataRepo.getTargetsForLookupProperty.and.resolveTo(['contact']); metadataRepo.getEntitySetForEntity.and .returnValues(Promise.resolve('contacts'), Promise.resolve('accounts')); recordRepo.upsertRecord @@ -214,6 +217,7 @@ describe('DeepInsertService', () => { }); it('returns an entity reference for each created record', async () => { + metadataRepo.getTargetsForLookupProperty.and.resolveTo(['contact']); const expectedEntityReference = { id: '', entityType: 'contact' }; const createResponses = [ Promise.resolve({ id: '', entityType: 'account' }), @@ -244,5 +248,63 @@ describe('DeepInsertService', () => { expect(newRecordRepo.upsertRecord.calls.count()).toBe(4); }); + + it('throws when the target entity can not be determined for a lookup record', async () => { + metadataRepo.getTargetsForLookupProperty.and.resolveTo(['contact', 'account']); + + expectAsync(deepInsertService.deepInsert('account', + { + primarycontactid: + { + firstname: 'John', + lastname: 'Smith', + }, + }, + {})).toBeRejectedWithError('Unable to determine target entity for primarycontactid'); + }); + + it('correctly determines the target entity for customer fields', async () => { + metadataRepo.getTargetsForLookupProperty.withArgs('account', 'customer_contact').and.resolveTo(null); + metadataRepo.getTargetsForLookupProperty.withArgs('account', 'customer').and.resolveTo(['contact', 'account']); + recordRepo.upsertRecord + .and.returnValues( + Promise.resolve({ id: '', entityType: 'contact' }), + Promise.resolve({ id: '', entityType: 'account' }), + ); + + await deepInsertService.deepInsert('account', + { + customer_contact: + { + firstname: 'John', + lastname: 'Smith', + }, + }, + {}); + + expect(recordRepo.upsertRecord.calls.first().args[0]).toBe('contact'); + }); + + it('fallsback to @logicalName if targets are not found', async () => { + metadataRepo.getTargetsForLookupProperty.and.resolveTo(['contact']); + recordRepo.upsertRecord + .and.returnValues( + Promise.resolve({ id: '', entityType: 'contact' }), + Promise.resolve({ id: '', entityType: 'account' }), + ); + + await deepInsertService.deepInsert('account', + { + primarycontactid: + { + '@logicalName': 'contact', + firstname: 'John', + lastname: 'Smith', + }, + }, + {}); + + expect(recordRepo.upsertRecord.calls.count()).toBe(2); + }); }); }); diff --git a/driver/test/repositories/authenticatedUserRecordRepository.spec.ts b/driver/test/repositories/authenticatedUserRecordRepository.spec.ts index f753f3e..3b65db2 100644 --- a/driver/test/repositories/authenticatedUserRecordRepository.spec.ts +++ b/driver/test/repositories/authenticatedUserRecordRepository.spec.ts @@ -11,7 +11,7 @@ describe('CurrentUserRecordRepository', () => { metadataRepo = jasmine.createSpyObj('MetadataRepository', [ 'getEntitySetForEntity', - 'getEntityForLookupProperty', + 'getTargetsForLookupProperty', 'getLookupPropertyForCollectionProperty', 'getRelationshipMetadata', ]); diff --git a/driver/test/repositories/metadataRepository.spec.ts b/driver/test/repositories/metadataRepository.spec.ts index d9b3e25..b9bb709 100644 --- a/driver/test/repositories/metadataRepository.spec.ts +++ b/driver/test/repositories/metadataRepository.spec.ts @@ -25,16 +25,26 @@ describe('MetadataRepository', () => { }); }); - describe('getEntityForLookupProperty(logicalName, navigationProperty)', () => { + describe('getTargetsForLookupProperty(logicalName, navigationProperty)', () => { + const entityLogicalName = 'contact'; + const targetEntityName = 'account'; + it('returns the target entity for a lookup attribute', () => { - const entityLogicalName = 'contact'; - const targetEntityName = 'account'; const navigationProperty = 'accountid'; const mockResponse = { value: [{ Targets: [targetEntityName] }] }; fetchMock.mock('*', { body: mockResponse, sendAsJson: true }); - expectAsync(metadataRepo.getEntityForLookupProperty(entityLogicalName, navigationProperty)) - .toBeResolvedTo(targetEntityName); + expectAsync(metadataRepo.getTargetsForLookupProperty(entityLogicalName, navigationProperty)) + .toBeResolvedTo(mockResponse.value[0].Targets); + }); + + it('returns null if the attribute is not found', () => { + const navigationProperty = 'accountid'; + const mockResponse = { value: [] }; + fetchMock.mock('*', { body: mockResponse, sendAsJson: true }); + + expectAsync(metadataRepo.getTargetsForLookupProperty(entityLogicalName, navigationProperty)) + .toBeResolvedTo(null); }); }); diff --git a/templates/include-build-and-test-steps.yml b/templates/include-build-and-test-steps.yml index 0169cf1..ee2f992 100644 --- a/templates/include-build-and-test-steps.yml +++ b/templates/include-build-and-test-steps.yml @@ -107,7 +107,7 @@ jobs: displayName: Pack NuGet package inputs: command: pack - packagesToPack: bindings/src/**/*.csproj + packagesToPack: bindings/src/Capgemini.PowerApps.SpecFlowBindings/Capgemini.PowerApps.SpecFlowBindings.csproj modifyOutputPath: true versioningScheme: byEnvVar versionEnvVar: GitVersion.NuGetVersionV2