├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── pull_request_template.md
├── .gitignore
├── .vscode
└── extensions.json
├── CONTRIBUTING.md
├── GitVersion.yml
├── LICENCE
├── README.md
├── azure-pipelines-pull-request.yml
├── azure-pipelines.yml
├── bindings
├── .editorconfig
├── Capgemini.PowerApps.SpecFlowBindings.sln
├── src
│ ├── Capgemini.PowerApps.SpecFlowBindings.MSBuild
│ │ ├── Capgemini.PowerApps.SpecFlowBindings.MSBuild.csproj
│ │ └── ExtendDataFiles.cs
│ └── Capgemini.PowerApps.SpecFlowBindings
│ │ ├── Capgemini.PowerApps.SpecFlowBindings.csproj
│ │ ├── Configuration
│ │ ├── BrowserCredentialsYamlTypeConverter.cs
│ │ ├── BrowserOptionsWithProfileSupport.cs
│ │ ├── ClientCredentials.cs
│ │ ├── ConfigHelper.cs
│ │ ├── TestConfiguration.cs
│ │ └── UserConfiguration.cs
│ │ ├── Extensions
│ │ ├── BrowserTypeExtensions.cs
│ │ ├── DirectoryInfoExtensions.cs
│ │ ├── DriverOptionsExtensions.cs
│ │ ├── EntityExtensions.cs
│ │ ├── FieldExtensions.cs
│ │ ├── GridExtensions.cs
│ │ ├── IListExtensions.cs
│ │ ├── MultiSelectOptionSetExtensions.cs
│ │ ├── StringExtensions.cs
│ │ └── SubGridExtensions.cs
│ │ ├── FileDataRepository.cs
│ │ ├── Hooks
│ │ ├── AfterScenarioHooks.cs
│ │ └── BeforeRunHooks.cs
│ │ ├── ITestDataRepository.cs
│ │ ├── ITestDriver.cs
│ │ ├── PowerAppsStepDefiner.cs
│ │ ├── Steps
│ │ ├── BusinessProcessFlowSteps.cs
│ │ ├── CommandBarSteps.cs
│ │ ├── DashboardSteps.cs
│ │ ├── DataSteps.cs
│ │ ├── DialogSteps.cs
│ │ ├── EntitySteps.cs
│ │ ├── EntitySubGridSteps.cs
│ │ ├── GlobalSearchSteps.cs
│ │ ├── GridSteps.cs
│ │ ├── LoginSteps.cs
│ │ ├── LookupDialogSteps.cs
│ │ ├── LookupSteps.cs
│ │ ├── NavigationSteps.cs
│ │ ├── QuickCreateSteps.cs
│ │ ├── RelatedGridSteps.cs
│ │ ├── TimelineSteps.cs
│ │ └── UtilitySteps.cs
│ │ ├── TestDriver.cs
│ │ ├── Transformations
│ │ ├── EasyReproTransformations.cs
│ │ └── UtilityTransformations.cs
│ │ ├── build
│ │ └── Capgemini.PowerApps.SpecFlowBindings.targets
│ │ ├── content
│ │ └── power-apps-bindings.yml
│ │ └── icon.png
└── tests
│ ├── Capgemini.PowerApps.SpecFlowBindings.UiTests
│ ├── .runsettings
│ ├── BusinessProcessFlowSteps.feature
│ ├── Capgemini.PowerApps.SpecFlowBindings.UiTests.csproj
│ ├── CodeCoverage.runsettings
│ ├── CommandBarSteps.feature
│ ├── DashboardSteps.feature
│ ├── Data
│ │ ├── a contact.json
│ │ ├── a contact
│ │ │ └── that has been extended with @extend.json
│ │ ├── a different team.json
│ │ ├── a record configured for global search.json
│ │ ├── a record referencing the record with an alias using @alias.bind.json
│ │ ├── a record with a business process flow.json
│ │ ├── a record with a subgrid and related records.json
│ │ ├── a record with a timeline on the main form.json
│ │ ├── a record with an alias.json
│ │ ├── a secondary mock record.json
│ │ ├── a team.json
│ │ └── data decorated with faker moustache syntax.json
│ ├── DataSteps.feature
│ ├── DialogSteps.feature
│ ├── EntitySteps.feature
│ ├── EntitySubGridSteps.feature
│ ├── GlobalSearchSteps.feature
│ ├── GridSteps.feature
│ ├── Hooks
│ │ ├── AfterScenarioHooks.cs
│ │ └── MockSolutionHooks.cs
│ ├── LoginSteps.feature
│ ├── LookupDialogSteps.feature
│ ├── LookupSteps.feature
│ ├── NavigationSteps.feature
│ ├── Properties
│ │ └── AssemblyInfo.cs
│ ├── QuickCreateSteps.feature
│ ├── RelatedGridSteps.feature
│ ├── Steps
│ │ └── TestEnvironmentConfigSteps.cs
│ ├── TimelineSteps.feature
│ ├── power-apps-bindings.yml
│ └── reqnroll.json
│ └── sb_PowerAppsSpecFlowBindings_Mock
│ ├── sb_PowerAppsSpecFlowBindings_Mock.cdsproj
│ └── src
│ ├── AppModuleSiteMaps
│ └── sb_MockApp
│ │ ├── AppModuleSiteMap.xml
│ │ └── AppModuleSiteMap_managed.xml
│ ├── AppModules
│ └── sb_MockApp
│ │ ├── AppModule.xml
│ │ └── AppModule_managed.xml
│ ├── Dashboards
│ ├── {50687e45-f839-eb11-a813-000d3a0b97ca}.xml
│ └── {6e43bd18-f839-eb11-a813-000d3a0b97ca}.xml
│ ├── Entities
│ ├── sb_MockRecord
│ │ ├── Entity.xml
│ │ ├── FormXml
│ │ │ ├── card
│ │ │ │ ├── {159912fd-c3fb-42c2-b058-922b3a8e3523}.xml
│ │ │ │ └── {159912fd-c3fb-42c2-b058-922b3a8e3523}_managed.xml
│ │ │ ├── main
│ │ │ │ ├── {974e0b5b-0d50-40fd-b607-61bb0812dda0}.xml
│ │ │ │ ├── {974e0b5b-0d50-40fd-b607-61bb0812dda0}_managed.xml
│ │ │ │ ├── {e75d54f4-033a-eb11-a813-000d3a0b97ca}.xml
│ │ │ │ └── {e75d54f4-033a-eb11-a813-000d3a0b97ca}_managed.xml
│ │ │ └── quick
│ │ │ │ ├── {6ac2352e-0322-4c77-b1e8-0f9ed141ef6b}.xml
│ │ │ │ └── {6ac2352e-0322-4c77-b1e8-0f9ed141ef6b}_managed.xml
│ │ ├── RibbonDiff.xml
│ │ └── SavedQueries
│ │ │ ├── {1f735b93-a07a-43bb-9df8-aa452cc8e44d}.xml
│ │ │ ├── {31f43b0f-0029-406a-aa44-253697a2e30a}.xml
│ │ │ ├── {3e00e84b-f203-4a23-a48b-0b1e13cf3cd9}.xml
│ │ │ ├── {8bcb3608-d091-44d6-837e-733a09b9f30f}.xml
│ │ │ ├── {8e4f2039-bded-4966-8bec-68c4994b931a}.xml
│ │ │ ├── {b2f87496-0a45-41b3-ba94-d54dc90afe00}.xml
│ │ │ └── {fe19a300-f739-eb11-bf68-000d3a0b851b}.xml
│ ├── sb_SecondaryMockRecord
│ │ ├── Entity.xml
│ │ ├── FormXml
│ │ │ ├── card
│ │ │ │ ├── {1b1eb19c-f074-41e8-96dd-2f4504cc23d7}.xml
│ │ │ │ └── {1b1eb19c-f074-41e8-96dd-2f4504cc23d7}_managed.xml
│ │ │ ├── main
│ │ │ │ ├── {64f94a07-433f-4a64-a290-16238d710ab3}.xml
│ │ │ │ └── {64f94a07-433f-4a64-a290-16238d710ab3}_managed.xml
│ │ │ ├── quick
│ │ │ │ ├── {80e3b3be-baa1-4abc-9ff3-1ed82ed2d5aa}.xml
│ │ │ │ └── {80e3b3be-baa1-4abc-9ff3-1ed82ed2d5aa}_managed.xml
│ │ │ └── quickCreate
│ │ │ │ ├── {5cdd3da8-0b3a-eb11-a813-000d3a0b97ca}.xml
│ │ │ │ └── {5cdd3da8-0b3a-eb11-a813-000d3a0b97ca}_managed.xml
│ │ ├── RibbonDiff.xml
│ │ └── SavedQueries
│ │ │ ├── {0f56be21-ac88-4347-b464-fd97bacfa84c}.xml
│ │ │ ├── {22ea6a27-f739-eb11-bf68-000d3a0b851b}.xml
│ │ │ ├── {42b8b4df-d57c-45fd-ba06-5f19234f35e1}.xml
│ │ │ ├── {be892914-e72f-446b-b351-8bff8684e42d}.xml
│ │ │ ├── {ca54c145-3bf1-4d00-99ae-3db3056f1372}.xml
│ │ │ ├── {cbf2061b-2054-4bc9-80ab-bffdade82ceb}.xml
│ │ │ └── {d81f44f7-26ec-484e-8435-dfd7649fac76}.xml
│ ├── sb_primarybusinessprocessflow
│ │ ├── Entity.xml
│ │ ├── FormXml
│ │ │ └── main
│ │ │ │ ├── {f5a00a01-01ad-42b4-a39a-aecb626c0fb9}.xml
│ │ │ │ └── {f5a00a01-01ad-42b4-a39a-aecb626c0fb9}_managed.xml
│ │ ├── Formulas
│ │ │ └── sb_primarybusinessprocessflow-bpf_duration.xaml
│ │ ├── RibbonDiff.xml
│ │ └── SavedQueries
│ │ │ ├── {16ad7b3f-073a-eb11-a813-000d3a0b97ca}.xml
│ │ │ ├── {19e7392f-cff5-4320-86d4-496f32b80583}.xml
│ │ │ ├── {2bef1fb0-8cab-448d-aa81-0f72c09824ad}.xml
│ │ │ ├── {48c12097-b8f9-4532-951d-dbb3d900b48c}.xml
│ │ │ ├── {522b22fd-1dbc-4843-8c17-4f51514cde84}.xml
│ │ │ ├── {61aa85f2-5063-4530-8ec1-251a4326ef01}.xml
│ │ │ ├── {9cfb31fe-0842-48e3-b34e-3cd7b7425666}.xml
│ │ │ ├── {ba760875-c5cd-4fe0-8e2f-17d9a1842656}.xml
│ │ │ ├── {d8cdfb4a-b8f1-493b-94af-c01778a00efd}.xml
│ │ │ └── {fa0636aa-9c5a-4f3c-ae17-2734cfdf336d}.xml
│ └── sb_secondarybusinessprocessflow
│ │ ├── Entity.xml
│ │ ├── FormXml
│ │ └── main
│ │ │ ├── {c6b374b2-7d8f-44e1-b2a5-641d2765d7ef}.xml
│ │ │ └── {c6b374b2-7d8f-44e1-b2a5-641d2765d7ef}_managed.xml
│ │ ├── Formulas
│ │ └── sb_secondarybusinessprocessflow-bpf_duration.xaml
│ │ ├── RibbonDiff.xml
│ │ └── SavedQueries
│ │ ├── {00f6e446-66c0-42b9-aa57-3eef587a8d35}.xml
│ │ ├── {1cf3fb71-073a-eb11-a813-000d3a0b97ca}.xml
│ │ ├── {21d48f05-fad9-4fab-9de5-f31c95f88bcb}.xml
│ │ ├── {282c6496-6c2a-4080-8235-701dee0cdeaf}.xml
│ │ ├── {34c0c9da-73fc-4548-aef8-5782e1ec1484}.xml
│ │ ├── {64057e18-a232-4fd2-8a6f-2cf8c99cd7ae}.xml
│ │ ├── {7a94bf0c-7f33-40cc-bc33-345a0653527e}.xml
│ │ ├── {7e8cb7b6-779f-4935-941b-11cc6c48e8e4}.xml
│ │ ├── {8acd2346-ea45-42ed-8cd5-be3e2b038b87}.xml
│ │ └── {e8ff9ab9-753d-4b5a-9a5c-2b8d567b9aef}.xml
│ ├── OptionSets
│ └── sb_choice.xml
│ ├── Other
│ ├── Customizations.xml
│ ├── Relationships.xml
│ ├── Relationships
│ │ ├── Account.xml
│ │ ├── BusinessUnit.xml
│ │ ├── Contact.xml
│ │ ├── Organization.xml
│ │ ├── Owner.xml
│ │ ├── ProcessStage.xml
│ │ ├── SystemUser.xml
│ │ ├── Team.xml
│ │ ├── TransactionCurrency.xml
│ │ ├── Workflow.xml
│ │ ├── sb_MockRecord.xml
│ │ └── sb_SecondaryMockRecord.xml
│ └── Solution.xml
│ ├── WebResources
│ └── sb_
│ │ └── js
│ │ ├── sb_mockrecord.form.js
│ │ ├── sb_mockrecord.form.js.data.xml
│ │ ├── sb_mockrecord.ribbon.js
│ │ └── sb_mockrecord.ribbon.js.data.xml
│ └── Workflows
│ ├── MockBusinessProcessError-A35908FE-0FCD-4EB7-826C-405286DE4D9D.xaml
│ ├── MockBusinessProcessError-A35908FE-0FCD-4EB7-826C-405286DE4D9D.xaml.data.xml
│ ├── PrimaryBusinessProcessFlow-35D6A39A-C720-4D5F-8F11-4497DFF2F9E3.xaml
│ ├── PrimaryBusinessProcessFlow-35D6A39A-C720-4D5F-8F11-4497DFF2F9E3.xaml.data.xml
│ ├── SecondaryBusinessProcessFlow-D21A6986-EB11-429C-B4B1-BB747EEB0991.xaml
│ └── SecondaryBusinessProcessFlow-D21A6986-EB11-429C-B4B1-BB747EEB0991.xaml.data.xml
├── driver
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── karma.conf.js
├── package-lock.json
├── package.json
├── src
│ ├── data
│ │ ├── createOptions.ts
│ │ ├── dataManager.ts
│ │ ├── deepInsertResponse.ts
│ │ ├── deepInsertService.ts
│ │ ├── fakerPreprocessor.ts
│ │ ├── index.ts
│ │ ├── preprocessor.ts
│ │ ├── record.ts
│ │ └── testRecord.ts
│ ├── driver.ts
│ ├── index.ts
│ ├── repositories
│ │ ├── authenticatedRecordRepository.ts
│ │ ├── currentUserRecordRepository.ts
│ │ ├── genericRecordRepository.ts
│ │ ├── index.ts
│ │ ├── metadataRepository.ts
│ │ └── recordRepository.ts
│ ├── requests
│ │ ├── associateRequest.ts
│ │ ├── index.ts
│ │ └── request.ts
│ └── types
│ │ └── xrm.d.ts
├── test
│ ├── data
│ │ ├── dataManager.spec.ts
│ │ ├── deepInsertService.spec.ts
│ │ └── fakerPreprocessor.spec.ts
│ ├── driver.spec.ts
│ ├── repositories
│ │ ├── authenticatedUserRecordRepository.spec.ts
│ │ ├── currentUserRecordRepository.spec.ts
│ │ └── metadataRepository.spec.ts
│ └── requests
│ │ └── associateRequest.spec.ts
├── tsconfig.json
└── webpack.config.js
├── scripts
├── Remove-InvalidSymbolsPackageFiles.ps1
├── Set-AllUserLocalesToUk.ps1
└── Sync-DataverseUsers.ps1
└── templates
├── build-and-test-job.yml
└── build-and-test-stages.yml
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Describe the bug
11 | _A clear and concise description of what the bug is._
12 |
13 | ## To reproduce
14 |
15 | _Steps to reproduce the behaviour._
16 |
17 | ## Expected behaviour
18 | _A clear and concise description of what you expected to happen._
19 |
20 | ## Screenshots
21 | _If applicable, add screenshots to help explain your problem._
22 |
23 | ## Environment (please complete the following information):
24 | - Test framework [e.g. NUnit/MSTest/xUnit]
25 | - Browser [e.g. chrome, safari]
26 | - Version [e.g. 22]
27 | - Power Apps environment version
28 |
29 | ## Additional context
30 | _Add any other context about the problem here._
31 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | ## Is your feature request related to a problem? Please describe
11 | _A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]_
12 |
13 | ## Describe the solution you'd like
14 | _A clear and concise description of what you want to happen._
15 |
16 | ## Additional context
17 | _Add any other context or screenshots about the feature request here._
18 |
--------------------------------------------------------------------------------
/.github/pull_request_template.md:
--------------------------------------------------------------------------------
1 | ## Purpose
2 | _Describe the problem or feature in addition to a link to the issue(s)._
3 |
4 | ## Approach
5 | _How does this change address the problem?_
6 |
7 | ## TODOs
8 | - [ ] Automated test coverage for new code
9 | - [ ] Documentation updated (if required)
10 | - [ ] Build and tests successful
11 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "dbaeumer.vscode-eslint"
4 | ]
5 | }
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Please first discuss the change you wish to make via an issue before making a change.
4 |
5 | ## Pull request process
6 |
7 | 1. Ensure that there are automated tests that cover any changes
8 | 1. Update the README.md with details of any significant changes to functionality
9 | 1. Ensure that your commit messages increment the version using [GitVersion syntax](https://gitversion.readthedocs.io/en/latest/input/docs/more-info/version-increments/). If no message is found then the patch version will be incremented by default.
10 | 1. You may merge the pull request once it meets all of the required checks. If you do not have permision, a reviewer will do it for you
--------------------------------------------------------------------------------
/GitVersion.yml:
--------------------------------------------------------------------------------
1 | mode: mainline
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | MIT Licence
2 |
3 | Copyright (c) 2020 Capgemini
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/azure-pipelines-pull-request.yml:
--------------------------------------------------------------------------------
1 | name: $(GITVERSION_FullSemVer)
2 |
3 | trigger: none
4 |
5 | pr:
6 | - master
7 |
8 | pool:
9 | vmImage: 'windows-latest'
10 |
11 | stages:
12 | - template: templates/build-and-test-stages.yml
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | name: $(GITVERSION_FullSemVer)
2 |
3 | trigger:
4 | batch: true
5 | branches:
6 | include:
7 | - master
8 |
9 | pr: none
10 |
11 | pool:
12 | vmImage: windows-latest
13 | stages:
14 | - template: templates/build-and-test-stages.yml
15 | - stage: Publish
16 | displayName: Publish
17 | jobs:
18 | - job: PublishJob
19 | displayName: Publish
20 | variables:
21 | SemVer: $[ stageDependencies.BuildAndTest.BuildAndTestJob.outputs['OutputSemVerTask.SemVer'] ]
22 | steps:
23 | - checkout: none
24 | - download: current
25 | displayName: Download NuGet package artifact
26 | artifact: Capgemini.PowerApps.SpecFlowBindings
27 | - task: NuGetCommand@2
28 | displayName: Push to NuGet.org
29 | inputs:
30 | command: push
31 | packagesToPush: '$(Pipeline.Workspace)/Capgemini.PowerApps.SpecFlowBindings/*.nupkg'
32 | nuGetFeedType: external
33 | publishFeedCredentials: Capgemini_UK
34 | - task: GitHubRelease@1
35 | displayName: Create GitHub releaes
36 | condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/master'))
37 | inputs:
38 | gitHubConnection: 'GitHub (ewingjm)'
39 | repositoryName: '$(Build.Repository.Name)'
40 | action: 'create'
41 | target: '$(Build.SourceVersion)'
42 | tagSource: 'userSpecifiedTag'
43 | tag: 'v$(SemVer)'
44 | releaseNotesSource: 'inline'
45 | assets: '$(Pipeline.Workspace)/Capgemini.PowerApps.SpecFlowBindings/*'
46 | isPreRelease: true
47 | changeLogCompareToRelease: 'lastNonDraftRelease'
48 | changeLogType: commitBased
49 |
--------------------------------------------------------------------------------
/bindings/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.cs]
2 |
3 | # SA1633: File should have header
4 | dotnet_diagnostic.SA1633.severity = none
5 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings.MSBuild/Capgemini.PowerApps.SpecFlowBindings.MSBuild.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | true
6 | true
7 |
8 |
9 |
10 |
11 |
12 |
13 | all
14 | runtime; build; native; contentfiles; analyzers; buildtransitive
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Configuration/BrowserCredentialsYamlTypeConverter.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Configuration
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Globalization;
6 | using Microsoft.Dynamics365.UIAutomation.Browser;
7 | using YamlDotNet.Core;
8 | using YamlDotNet.Core.Events;
9 | using YamlDotNet.Serialization;
10 |
11 | ///
12 | /// for .
13 | ///
14 | public sealed class BrowserCredentialsYamlTypeConverter : IYamlTypeConverter
15 | {
16 | ///
17 | public bool Accepts(Type type)
18 | {
19 | return type == typeof(BrowserCredentials);
20 | }
21 |
22 | ///
23 | public object ReadYaml(IParser parser, Type type)
24 | {
25 | var dictionary = new Dictionary();
26 |
27 | parser.Consume();
28 | while (!parser.TryConsume(out _))
29 | {
30 | dictionary.Add(parser.Consume().Value.ToLower(CultureInfo.CurrentCulture), parser.Consume().Value);
31 | }
32 |
33 | return new BrowserCredentials(dictionary["username"], dictionary["password"]);
34 | }
35 |
36 | ///
37 | public void WriteYaml(IEmitter emitter, object value, Type type)
38 | {
39 | throw new NotImplementedException();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Configuration/ClientCredentials.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Configuration
2 | {
3 | using YamlDotNet.Serialization;
4 |
5 | ///
6 | /// Configuration for the test application user.
7 | ///
8 | public class ClientCredentials
9 | {
10 | private string tenantId;
11 | private string clientId;
12 | private string clientSecret;
13 |
14 | ///
15 | /// Gets or sets the tenant ID of the test application user app registration.
16 | ///
17 | [YamlMember(Alias = "tenantId")]
18 | public string TenantId { get => ConfigHelper.GetEnvironmentVariableIfExists(this.tenantId); set => this.tenantId = value; }
19 |
20 | ///
21 | /// Gets or sets the client ID of the test application user app registration.
22 | ///
23 | [YamlMember(Alias = "clientId")]
24 | public string ClientId { get => ConfigHelper.GetEnvironmentVariableIfExists(this.clientId); set => this.clientId = value; }
25 |
26 | ///
27 | /// Gets or sets a client secret or the name of an environment variable containing the client secret of the test application user app registration.
28 | ///
29 | [YamlMember(Alias = "clientSecret")]
30 | public string ClientSecret { get => ConfigHelper.GetEnvironmentVariableIfExists(this.clientSecret); set => this.clientSecret = value; }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Configuration/ConfigHelper.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Configuration
2 | {
3 | using System;
4 |
5 | ///
6 | /// Helper methods for configuration classes.
7 | ///
8 | public static class ConfigHelper
9 | {
10 | ///
11 | /// Returns the value of an environment variable if it exists. Alternatively, returns the passed in value.
12 | ///
13 | /// The value which may be the name of an environment variable.
14 | /// The environment variable value (if found) or the passed in value.
15 | public static string GetEnvironmentVariableIfExists(string value)
16 | {
17 | if (string.IsNullOrEmpty(value))
18 | {
19 | return value;
20 | }
21 |
22 | var environmentVariableValue = Environment.GetEnvironmentVariable(value);
23 |
24 | if (!string.IsNullOrEmpty(environmentVariableValue))
25 | {
26 | return environmentVariableValue;
27 | }
28 |
29 | return value;
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Configuration/UserConfiguration.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Configuration
2 | {
3 | using YamlDotNet.Serialization;
4 |
5 | ///
6 | /// A user that tests can run as.
7 | ///
8 | public class UserConfiguration
9 | {
10 | private string username;
11 | private string password;
12 | private string otptoken;
13 |
14 | ///
15 | /// Gets or sets the username of the user.
16 | ///
17 | [YamlMember(Alias = "username")]
18 | public string Username { get => ConfigHelper.GetEnvironmentVariableIfExists(this.username); set => this.username = value; }
19 |
20 | ///
21 | /// Gets or sets the password of the user.
22 | ///
23 | [YamlMember(Alias = "password")]
24 | public string Password { get => ConfigHelper.GetEnvironmentVariableIfExists(this.password); set => this.password = value; }
25 |
26 | ///
27 | /// Gets or sets the alias of the user (used to retrieve from configuration).
28 | ///
29 | [YamlMember(Alias = "alias")]
30 | public string Alias { get; set; }
31 |
32 | ///
33 | /// Gets or sets the OTP token of the user.
34 | ///
35 | [YamlMember(Alias = "otptoken")]
36 | public string OtpToken { get => ConfigHelper.GetEnvironmentVariableIfExists(this.otptoken); set => this.otptoken = value; }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/BrowserTypeExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Extensions
2 | {
3 | using Microsoft.Dynamics365.UIAutomation.Browser;
4 |
5 | ///
6 | /// Provides extension methods on .
7 | ///
8 | public static class BrowserTypeExtensions
9 | {
10 | ///
11 | /// Determines if the given browser type supports profiles.
12 | ///
13 | /// The to check.
14 | /// true if the browser supports profiles otherwise false.
15 | public static bool SupportsProfiles(this BrowserType type)
16 | {
17 | switch (type)
18 | {
19 | case BrowserType.IE:
20 | return false;
21 | case BrowserType.Chrome:
22 | return true;
23 | case BrowserType.Firefox:
24 | return true;
25 | case BrowserType.Edge:
26 | return false;
27 | case BrowserType.Remote:
28 | return false;
29 | default:
30 | return false;
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/DirectoryInfoExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Extensions
2 | {
3 | using System.IO;
4 |
5 | ///
6 | /// Extensions to the class.
7 | ///
8 | public static class DirectoryInfoExtensions
9 | {
10 | ///
11 | /// Copies the directory recursively to the target directory.
12 | ///
13 | /// The source directory.
14 | /// The target directory.
15 | public static void CopyTo(this DirectoryInfo source, DirectoryInfo target)
16 | {
17 | Directory.CreateDirectory(target.FullName);
18 |
19 | foreach (var fileInfo in source.GetFiles())
20 | {
21 | fileInfo.CopyTo(Path.Combine(target.FullName, fileInfo.Name), true);
22 | }
23 |
24 | foreach (var subdirectory in source.GetDirectories())
25 | {
26 | subdirectory.CopyTo(target.CreateSubdirectory(subdirectory.Name));
27 | }
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/DriverOptionsExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Extensions
2 | {
3 | using OpenQA.Selenium;
4 | using OpenQA.Selenium.Chrome;
5 | using OpenQA.Selenium.Firefox;
6 | using OpenQA.Selenium.IE;
7 |
8 | ///
9 | /// Extensions to the class.
10 | ///
11 | public static class DriverOptionsExtensions
12 | {
13 | ///
14 | /// Adds a global capability to driver options.
15 | ///
16 | /// The driver options.
17 | /// The name of the capability.
18 | /// The value of the capability.
19 | internal static void AddGlobalCapability(this DriverOptions options, string name, object value)
20 | {
21 | switch (options)
22 | {
23 | case ChromeOptions chromeOptions:
24 | chromeOptions.AddAdditionalCapability(name, value, true);
25 | break;
26 | case FirefoxOptions firefoxOptions:
27 | firefoxOptions.AddAdditionalCapability(name, value, true);
28 | break;
29 | case InternetExplorerOptions internetExplorerOptions:
30 | internetExplorerOptions.AddAdditionalCapability(name, value, true);
31 | break;
32 | default:
33 | options.AddAdditionalCapability(name, value);
34 | break;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/EntityExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Extensions
2 | {
3 | using System;
4 | using Microsoft.Dynamics365.UIAutomation.Api.UCI;
5 | using OpenQA.Selenium;
6 |
7 | ///
8 | /// Extensions to the class.
9 | ///
10 | public static class EntityExtensions
11 | {
12 | ///
13 | /// This is required as throws a when getting a hidden field.
14 | ///
15 | /// The entity.
16 | /// The Selenium WebDriver.
17 | /// The name of the field.
18 | /// Whether the field is visible.
19 | public static bool IsFieldVisible(this Entity entity, IWebDriver driver, string fieldName)
20 | {
21 | driver = driver ?? throw new ArgumentNullException(nameof(driver));
22 |
23 | var elements = driver.FindElements(By.CssSelector($"div[data-id={fieldName}]"));
24 |
25 | return elements.Count > 0;
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/FieldExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Extensions
2 | {
3 | using System;
4 | using System.Collections.ObjectModel;
5 | using System.Linq;
6 | using Microsoft.Dynamics365.UIAutomation.Api.UCI;
7 | using OpenQA.Selenium;
8 |
9 | ///
10 | /// Extensions to the class.
11 | ///
12 | public static class FieldExtensions
13 | {
14 | ///
15 | /// This is required as the property does not work.
16 | ///
17 | /// The field.
18 | /// The Selenium WebDriver.
19 | /// Whether the field is read-only.
20 | public static bool IsReadOnly(this Field field, IWebDriver driver)
21 | {
22 | field = field ?? throw new ArgumentNullException(nameof(field));
23 |
24 | driver = driver ?? throw new ArgumentNullException(nameof(driver));
25 |
26 | ReadOnlyCollection lockedIcons = driver.FindElements(By.CssSelector($"div[data-id={field.Name}-locked-iconWrapper]"));
27 |
28 | return lockedIcons.Any();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/GridExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Extensions
2 | {
3 | using System;
4 | using System.Linq;
5 | using Microsoft.Dynamics365.UIAutomation.Api.UCI;
6 | using Microsoft.Dynamics365.UIAutomation.Browser;
7 | using OpenQA.Selenium;
8 |
9 | ///
10 | /// Extensions to the class.
11 | ///
12 | public static class GridExtensions
13 | {
14 | ///
15 | /// Gets the index of a record by ID.
16 | /// Accessing grid item IDs or URLs via EasyRepro is currently broken (see https://github.com/microsoft/EasyRepro/issues/800). Use this method instead.
17 | ///
18 | /// The Grid.
19 | /// The Selenium WebDriver.
20 | /// The ID of the record.
21 | /// The index of the record.
22 | public static int GetRecordIndexById(this Grid grid, IWebDriver driver, Guid recordId)
23 | {
24 | if (driver is null)
25 | {
26 | throw new ArgumentNullException(nameof(driver));
27 | }
28 |
29 | driver.WaitUntilAvailable(By.XPath(AppElements.Xpath[AppReference.Grid.Container]));
30 |
31 | var index = (long)driver.ExecuteScript($"return Object.keys(getCurrentXrmStatus().mainGrid._grid.getRows()._collection).indexOf(\"{recordId}\");");
32 | if (index == -1)
33 | {
34 | throw new Exception($"A record with an ID of {recordId} is not present in the grid");
35 | }
36 |
37 | return Convert.ToInt32(index);
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/IListExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Extensions
2 | {
3 | using System;
4 | using System.Collections.Generic;
5 |
6 | ///
7 | /// Extensions for .
8 | ///
9 | internal static class IListExtensions
10 | {
11 | private static readonly Random Rand = new Random();
12 |
13 | ///
14 | /// Randomises the order of a list.
15 | ///
16 | /// The type of list items.
17 | /// The list.
18 | /// The shuffled list.
19 | internal static IList Shuffle(this IList list)
20 | {
21 | var n = list.Count;
22 |
23 | while (n > 1)
24 | {
25 | n--;
26 | var k = Rand.Next(n + 1);
27 | T value = list[k];
28 | list[k] = list[n];
29 | list[n] = value;
30 | }
31 |
32 | return list;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Extensions
2 | {
3 | using System;
4 | using System.Globalization;
5 | using System.Text.RegularExpressions;
6 |
7 | ///
8 | /// Extensions to the class.
9 | ///
10 | public static class StringExtensions
11 | {
12 | ///
13 | /// Looks for templated text in string and replaces.
14 | ///
15 | /// The input string.
16 | /// The resulting string.
17 | public static string ReplaceTemplatedText(this string input)
18 | {
19 | input = input ?? throw new ArgumentNullException(nameof(input));
20 |
21 | MatchCollection matches = Regex.Matches(input, "{{.*}}");
22 | var random = new Random();
23 |
24 | foreach (Match match in matches)
25 | {
26 | string innerText = match.Value.Substring(2, match.Value.Length - 4);
27 | string[] templateSplit = innerText.Split(':');
28 |
29 | if (templateSplit[0].ToUpperInvariant() == "RANDNUM")
30 | {
31 | string[] randomBound = templateSplit[1].Split(',');
32 | int lowerBound = int.Parse(randomBound[0], CultureInfo.CurrentCulture);
33 | int upperBound = int.Parse(randomBound[1], CultureInfo.CurrentCulture);
34 |
35 | input = input
36 | .Remove(
37 | match.Index,
38 | match.Length)
39 | .Insert(
40 | match.Index,
41 | random
42 | .Next(lowerBound, upperBound)
43 | .ToString(CultureInfo.InvariantCulture));
44 | }
45 | else
46 | {
47 | throw new NotImplementedException($"ReplaceTemplatedText does not support {templateSplit[0]}.");
48 | }
49 | }
50 |
51 | return input;
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/FileDataRepository.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings
2 | {
3 | using System.IO;
4 | using System.Reflection;
5 |
6 | ///
7 | /// Reads test data from JSON files.
8 | ///
9 | public class FileDataRepository : ITestDataRepository
10 | {
11 | private const string FileDirectory = "data";
12 | private static readonly string RootDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
13 |
14 | ///
15 | public string GetTestData(string identifier)
16 | {
17 | return File.ReadAllText(Path.Combine(RootDirectory, FileDirectory, Path.GetExtension(identifier) == ".json" ? identifier : $"{identifier}.json"));
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Hooks/BeforeRunHooks.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Hooks
2 | {
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 | using Capgemini.PowerApps.SpecFlowBindings.Configuration;
8 | using Capgemini.PowerApps.SpecFlowBindings.Steps;
9 | using Microsoft.Dynamics365.UIAutomation.Api.UCI;
10 | using Microsoft.Dynamics365.UIAutomation.Browser;
11 | using Reqnroll;
12 |
13 | ///
14 | /// Hooks that run before the start of each run.
15 | ///
16 | [Binding]
17 | public class BeforeRunHooks : PowerAppsStepDefiner
18 | {
19 | ///
20 | /// Creates a new folder for the scenario and copies the session/cookies information from previous runs.
21 | ///
22 | [BeforeTestRun]
23 | public static void BaseProfileSetup()
24 | {
25 | if (!TestConfig.UseProfiles)
26 | {
27 | return;
28 | }
29 |
30 | Parallel.ForEach(UserProfileDirectories.Keys, (username) =>
31 | {
32 | var profileDirectory = UserProfileDirectories[username];
33 | var baseDirectory = Path.Combine(profileDirectory, "base");
34 |
35 | // SpecFlow isolation settings may run scenarios in different processes or AppDomains and [BeforeTestRun] runs per thread. Lock statement insufficient.
36 | using (var mutex = new Mutex(true, $"{nameof(BaseProfileSetup)}-{username}", out var createdNew))
37 | {
38 | if (!createdNew)
39 | {
40 | mutex.WaitOne();
41 | }
42 |
43 | if (Directory.Exists(baseDirectory))
44 | {
45 | mutex.ReleaseMutex();
46 | return;
47 | }
48 |
49 | try
50 | {
51 | Directory.CreateDirectory(baseDirectory);
52 |
53 | var userBrowserOptions = (BrowserOptionsWithProfileSupport)TestConfig.BrowserOptions.Clone();
54 | userBrowserOptions.ProfileDirectory = baseDirectory;
55 | userBrowserOptions.Headless = true;
56 |
57 | var webClient = new WebClient(userBrowserOptions);
58 | using (var app = new XrmApp(webClient))
59 | {
60 | var user = TestConfig.Users.First(u => u.Username == username);
61 | app.OnlineLogin.Login(TestConfig.GetTestUrl(), user.Username.ToSecureString(), user.Password.ToSecureString());
62 | }
63 | }
64 | finally
65 | {
66 | mutex.ReleaseMutex();
67 | }
68 | }
69 | });
70 | }
71 | }
72 | }
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/ITestDataRepository.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings
2 | {
3 | ///
4 | /// Provides an interface for a test data repository.
5 | ///
6 | public interface ITestDataRepository
7 | {
8 | ///
9 | /// Retrieves the test data for a given identifier.
10 | ///
11 | /// The identifier to use.
12 | /// A JSON string representing the test data.
13 | string GetTestData(string identifier);
14 | }
15 | }
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/ITestDriver.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings
2 | {
3 | using Microsoft.Xrm.Sdk;
4 |
5 | ///
6 | /// An interface for a test driver used to perform setup and teardown via the Power Apps Client APIs.
7 | ///
8 | public interface ITestDriver
9 | {
10 | ///
11 | /// Injects the driver onto the current page.
12 | ///
13 | /// The application user auth token (if configured).
14 | void InjectOnPage(string authToken);
15 |
16 | ///
17 | /// Loads scenario test data.
18 | ///
19 | /// The data to load.
20 | /// The username of the user to impersonate.
21 | void LoadTestDataAsUser(string data, string username);
22 |
23 | ///
24 | /// Loads scenario test data.
25 | ///
26 | /// The data to load.
27 | void LoadTestData(string data);
28 |
29 | ///
30 | /// Deletes scenario test data.
31 | ///
32 | void DeleteTestData();
33 |
34 | ///
35 | /// Open a test record.
36 | ///
37 | /// The alias of the record.
38 | void OpenTestRecord(string recordAlias);
39 |
40 | ///
41 | /// Gets an entity reference to a previously created test record.
42 | ///
43 | /// The alias of the test record.
44 | /// A reference to the created record.
45 | EntityReference GetTestRecordReference(string recordAlias);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/CommandBarSteps.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Steps
2 | {
3 | using Capgemini.PowerApps.SpecFlowBindings.Extensions;
4 | using FluentAssertions;
5 | using Reqnroll;
6 |
7 | ///
8 | /// Step bindings relating to the command bar.
9 | ///
10 | [Binding]
11 | public class CommandBarSteps : PowerAppsStepDefiner
12 | {
13 | ///
14 | /// Selects a command with the given label.
15 | ///
16 | /// The label of the command.
17 | [When("I select the '(.*)' command")]
18 | public static void WhenISelectTheCommand(string commandName)
19 | {
20 | XrmApp.CommandBar.ClickCommand(commandName);
21 | }
22 |
23 | ///
24 | /// Selects a command under a flyout with the given label.
25 | ///
26 | /// The label of the command.
27 | /// The label of the flyout.
28 | [When("I select the '([^']+)' command under the '([^']+)' flyout")]
29 | public static void WhenISelectTheCommandUnderTheFlyout(string commandName, string flyoutName)
30 | {
31 | XrmApp.CommandBar.ClickCommand(flyoutName, commandName);
32 | }
33 |
34 | ///
35 | /// Asserts that a command is available in the command bar.
36 | ///
37 | /// The label of the command.
38 | [Then("I can see the '(.*)' command")]
39 | public static void ThenICanSeeTheCommand(string commandName)
40 | {
41 | XrmApp.CommandBar.GetCommandValues(true).Value.Should().Contain(commandName);
42 | }
43 |
44 | ///
45 | /// Asserts that a command is available in the command bar.
46 | ///
47 | /// The label of the command.
48 | [Then("I can not see the '(.*)' command")]
49 | public static void ThenICanNotSeeTheCommand(string commandName)
50 | {
51 | XrmApp.CommandBar.GetCommandValues(true).Value.Should().NotContain(commandName);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/DashboardSteps.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Steps
2 | {
3 | using Reqnroll;
4 |
5 | ///
6 | /// Steps relating to dashboards.
7 | ///
8 | [Binding]
9 | public class DashboardSteps : PowerAppsStepDefiner
10 | {
11 | ///
12 | /// Selects a dashboard.
13 | ///
14 | /// Dashboard name.
15 | [When("I select the '(.*)' dashboard")]
16 | public static void WhenISelectTheDashboard(string dashboardName)
17 | {
18 | XrmApp.Dashboard.SelectDashboard(dashboardName);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/DataSteps.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Steps
2 | {
3 | using System.Configuration;
4 | using Capgemini.PowerApps.SpecFlowBindings;
5 | using Microsoft.Dynamics365.UIAutomation.Browser;
6 | using Microsoft.Identity.Client;
7 | using Reqnroll;
8 |
9 | ///
10 | /// Test setup step bindings for data.
11 | ///
12 | [Binding]
13 | public class DataSteps : PowerAppsStepDefiner
14 | {
15 | ///
16 | /// Opens a test record.
17 | ///
18 | /// The alias of the test record.
19 | [Given(@"I have opened '(.*)'")]
20 | public static void GivenIHaveOpened(string alias)
21 | {
22 | TestDriver.OpenTestRecord(alias);
23 |
24 | Driver.WaitForTransaction();
25 | }
26 |
27 | ///
28 | /// Creates a test record.
29 | ///
30 | /// The name of the file containing the test record.
31 | [Given(@"I have created '(.*)'")]
32 | public static void GivenIHaveCreated(string fileName)
33 | {
34 | TestDriver.LoadTestData(TestDataRepository.GetTestData(fileName));
35 | }
36 |
37 | ///
38 | /// Creates a test record as a given user.
39 | ///
40 | /// The user alias.
41 | /// The name of the file containing the test record.
42 | [Given(@"'(.*)' has created '(.*)'")]
43 | public static void GivenIHaveCreated(string alias, string fileName)
44 | {
45 | TestDriver.LoadTestDataAsUser(
46 | TestDataRepository.GetTestData(fileName),
47 | TestConfig.GetUser(alias).Username);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/GlobalSearchSteps.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Steps
2 | {
3 | using Reqnroll;
4 |
5 | ///
6 | /// Steps relating to global search.
7 | ///
8 | [Binding]
9 | public class GlobalSearchSteps : PowerAppsStepDefiner
10 | {
11 | ///
12 | /// Performs an advanced search using the filter attribute and a filter value.
13 | ///
14 | /// Attribute to filter by.
15 | /// Attribute filter value.
16 | [When("I apply a search filter using the filter '(.*)' with the filter value of '(.*)' group")]
17 | public static void WhenIPerformASearchAndFilterByValue(string filterBy, string filterValue)
18 | {
19 | XrmApp.GlobalSearch.Filter(filterBy, filterValue);
20 | }
21 |
22 | ///
23 | /// Performs a wildcard search using a particular filter value.
24 | ///
25 | /// Attribute filter value.
26 | [When("I apply a search filter using the filter '(.*)'")]
27 | public static void WhenIFilterWithValue(string filterValue)
28 | {
29 | XrmApp.GlobalSearch.FilterWith(filterValue);
30 | }
31 |
32 | ///
33 | /// Performs an advanced search using the filter attribute and a filter value.
34 | ///
35 | /// Attribute filter value.
36 | [When("I search globally using the filter '(.*)'")]
37 | public static void WhenIPerformASearch(string filterValue)
38 | {
39 | XrmApp.GlobalSearch.Search(filterValue);
40 | }
41 |
42 | ///
43 | /// Open a record from a global search at a certain row.
44 | ///
45 | /// Attribute to filter by.
46 | /// Attribute filter value.
47 | [When("I open a record from global search on the entity '(.*)' in the position of '(.*)'")]
48 | public static void WhenIOpenARecordFromSearchUsingIndex(string entityName, int recordIndex)
49 | {
50 | XrmApp.GlobalSearch.OpenRecord(entityName, recordIndex);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/LookupDialogSteps.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Steps
2 | {
3 | using Microsoft.Dynamics365.UIAutomation.Browser;
4 | using OpenQA.Selenium;
5 | using Reqnroll;
6 |
7 | ///
8 | /// Step bindings related to lookup dialogs.
9 | ///
10 | [Binding]
11 | public class LookupDialogSteps : PowerAppsStepDefiner
12 | {
13 | ///
14 | /// Searches and Selects the first matching result.
15 | ///
16 | /// The term to search for.
17 | [When("I select '([^']+)' in the lookup dialog")]
18 | public static void WhenISelectInTheLookupDialog(string searchTerm)
19 | {
20 | var fieldContainer = Driver.WaitUntilAvailable(By.CssSelector("div[id=\"lookupDialogContainer\"] div[id=\"lookupDialogLookup\"]"));
21 | var input = fieldContainer.FindElement(By.TagName("input"));
22 | input.Click();
23 | input.SendKeys(searchTerm);
24 | input.SendKeys(Keys.Enter);
25 |
26 | fieldContainer.WaitUntilAvailable(By.CssSelector("li[data-id*=\"LookupResultsPopup\"]"))
27 | .Click();
28 | }
29 |
30 | ///
31 | /// Clicks the Add button.
32 | ///
33 | [When("I click Add in the lookup dialog")]
34 | public static void WhenIClickAddInTheLookupDialog()
35 | {
36 | var container = Driver.WaitUntilAvailable(By.CssSelector("div[id=\"lookupDialogFooterContainer\"]"));
37 |
38 | container.FindElement(By.CssSelector("button[data-id*=\"lookupDialogSaveBtn\"]"))
39 | .Click();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Steps/UtilitySteps.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Steps
2 | {
3 | using Capgemini.PowerApps.SpecFlowBindings;
4 | using Reqnroll;
5 |
6 | ///
7 | /// Steps providing various utilities.
8 | ///
9 | [Binding]
10 | public class UtilitySteps : PowerAppsStepDefiner
11 | {
12 | ///
13 | /// Waits for a given number of seconds.
14 | ///
15 | /// The number of seconds to wait.
16 | [When(@"I wait up to '(.*)' seconds")]
17 | public static void WhenIWaitUpToSeconds(int seconds)
18 | {
19 | XrmApp.ThinkTime(seconds * 1000);
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Transformations/EasyReproTransformations.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Transformations
2 | {
3 | using Microsoft.Dynamics365.UIAutomation.Api.UCI;
4 | using Reqnroll;
5 |
6 | ///
7 | /// Transformations for EasyRepro types.
8 | ///
9 | [Binding]
10 | public static class EasyReproTransformations
11 | {
12 | ///
13 | /// Transforms a logical name into an option set.
14 | ///
15 | /// The logical name.
16 | /// The option set.
17 | [StepArgumentTransformation]
18 | public static OptionSet TransformFieldNameToOptionSet(string expression)
19 | {
20 | return new OptionSet { Name = expression };
21 | }
22 |
23 | ///
24 | /// Transforms a logical name into a multi-value option set.
25 | ///
26 | /// The logical name.
27 | /// The multi-value option set.
28 | [StepArgumentTransformation]
29 | public static MultiValueOptionSet TransformFieldNameToMultiValueOptionSet(string expression)
30 | {
31 | return new MultiValueOptionSet { Name = expression };
32 | }
33 |
34 | ///
35 | /// Transforms a logical name into an lookup item.
36 | ///
37 | /// The logical name.
38 | /// The lookup.
39 | [StepArgumentTransformation]
40 | public static LookupItem TransformFieldNameToLookupItem(string expression)
41 | {
42 | return new LookupItem { Name = expression };
43 | }
44 |
45 | ///
46 | /// Transforms a logical name into a datetime item.
47 | ///
48 | /// The logical name.
49 | /// The datetime.
50 | [StepArgumentTransformation]
51 | public static DateTimeControl TransformFieldNameToDateTimeItem(string expression)
52 | {
53 | return new DateTimeControl(expression);
54 | }
55 |
56 | ///
57 | /// Transforms a logical name into a boolean item.
58 | ///
59 | /// The logical name.
60 | /// The boolean item.
61 | [StepArgumentTransformation]
62 | public static BooleanItem TransformFieldNameToBooleanItem(string expression)
63 | {
64 | return new BooleanItem { Name = expression };
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/Transformations/UtilityTransformations.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Transformations
2 | {
3 | using System;
4 | using System.Linq;
5 | using System.Text.RegularExpressions;
6 | using Reqnroll;
7 |
8 | ///
9 | /// General utility transformations.
10 | ///
11 | [Binding]
12 | public static class UtilityTransformations
13 | {
14 | ///
15 | /// Transforms a comma-separated list to a string array.
16 | ///
17 | /// The comma-separated list.
18 | /// A string array.
19 | [StepArgumentTransformation]
20 | public static string[] TransformCommaSeparatedStringsToArray(string expression)
21 | {
22 | return expression?.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();
23 | }
24 |
25 | ///
26 | /// Transforms an ordinal number (e.g. 1st, 2nd, 3rd etc.) to a zero-based index.
27 | ///
28 | /// The ordinal number.
29 | /// The zero-based index.
30 | [StepArgumentTransformation(@"(\d+(?:(?:st)|(?:nd)|(?:rd)|(?:th)))")]
31 | public static int TransformOrdinalNumberToZeroBasedIndex(string expression)
32 | {
33 | return Convert.ToInt32(Regex.Match(expression, @"\d+").Value) - 1;
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/build/Capgemini.PowerApps.SpecFlowBindings.targets:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | driver.js
5 | PreserveNewest
6 | false
7 |
8 |
9 | power-apps-bindings.yml
10 | PreserveNewest
11 | false
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/content/power-apps-bindings.yml:
--------------------------------------------------------------------------------
1 | url:
2 | useProfiles: false
3 | browserOptions:
4 | browserType:
5 | users:
6 | - username:
7 | password:
8 | alias:
--------------------------------------------------------------------------------
/bindings/src/Capgemini.PowerApps.SpecFlowBindings/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Capgemini/powerapps-specflow-bindings/5a87748c3499c2343674032d2f9bc657f649a422/bindings/src/Capgemini.PowerApps.SpecFlowBindings/icon.png
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/.runsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 4
7 | MethodLevel
8 |
9 |
10 |
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/BusinessProcessFlowSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Business Process Flow Steps
2 | In order to automate business process flow interaction
3 | As a developer
4 | I want to use pre-existing business process flow bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 | And I have created 'a record with a business process flow'
9 | And I have opened 'the record'
10 |
11 | Scenario: Open and close business process flow
12 | When I select the 'First Stage' stage of the business process flow
13 | And I close the 'First Stage' stage of the business process flow
14 |
15 | Scenario: Set business process flow stage as active
16 | When I click next stage on the the 'First Stage' stage of the business process flow
17 | And I set the 'First Stage' stage of the business process flow as active
18 |
19 | Scenario: Click next stage on business process flow
20 | When I click next stage on the the 'First Stage' stage of the business process flow
21 |
22 | Scenario: Pin stage on business process flow
23 | When I pin the 'First Stage' stage of the business process flow
24 |
25 | Scenario Outline: Enter values on business process flow
26 | When I select the 'First Stage' stage of the business process flow
27 | When I enter '' into the '' field on the business process flow
28 | Then I can see a value of '' in the '' field on the business process flow
29 |
30 | Scenarios:
31 | | column | type | value |
32 | | sb_text | text | Some text |
33 | | sb_number | numeric | 10 |
34 | # | sb_yesno | boolean | false | Currently failing due to https://github.com/microsoft/EasyRepro/issues/1140
35 | #| sb_choice | optionset | Option A |
36 | # | sb_dateandtime | datetime | 1/1/2021 13:00 | Currently failing due to https://github.com/microsoft/EasyRepro/issues/1139
37 | #| sb_dateonly | datetime | 1/1/2021 |
38 | | sb_currency | currency | £10.00 |
39 |
40 | Scenario: Enter lookup value on business process flow
41 | Given I have created 'a secondary mock record'
42 | When I select the 'First Stage' stage of the business process flow
43 | And I enter 'A secondary mock record' into the 'sb_lookup' lookup field on the business process flow
44 | Then I can see a value of 'A secondary mock record' in the 'sb_lookup' lookup field on the business process flow
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/CodeCoverage.runsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 | .*\.SpecFlowBindings.dll
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/CommandBarSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Command Bar Steps
2 | In order to automate command bar interaction
3 | As a developer
4 | I want to use pre-existing command bar bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 | When I open the 'Mock Records' sub area of the 'Primary Group' group
9 |
10 | Scenario: Assert a command is visible
11 | Then I can see the 'New' command
12 |
13 | Scenario: Assert a command not visible
14 | Then I can not see the 'Missing' command
15 |
16 | Scenario: Select a command
17 | When I select the 'Refresh' command
18 |
19 | @ignore # EasyRepro issue: https://github.com/microsoft/EasyRepro/issues/1087
20 | Scenario: Select a command under a flyout
21 | When I select the 'No Options Available' command under the 'Run Report' flyout
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/DashboardSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Dashboard Steps
2 | In order to automate dashboard interaction
3 | As a developer
4 | I want to use pre-existing dashboard bindings
5 |
6 | Scenario: Select a dashboard
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 | When I select the 'Secondary Dashboard' dashboard
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a contact.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "contact",
3 | "@alias": "the contact",
4 | "firstname": "John",
5 | "lastname": "Smith"
6 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a contact/that has been extended with @extend.json:
--------------------------------------------------------------------------------
1 | {
2 | "@extends": "../a contact",
3 | "firstname": "Jane"
4 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a different team.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "team",
3 | "@alias": "the team",
4 | "name": "A different team"
5 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a record configured for global search.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "sb_mockrecord",
3 | "@alias": "the referenced record",
4 | "sb_name": "A record configured for global search"
5 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a record referencing the record with an alias using @alias.bind.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "sb_secondarymockrecord",
3 | "@alias": "the referencing record",
4 | "sb_name": "The referencing record",
5 | "sb_Parent@alias.bind": "the referenced record"
6 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a record with a business process flow.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "sb_mockrecord",
3 | "@alias": "the record",
4 | "sb_name": "A record with a business process flow",
5 | "sb_yesno": true
6 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a record with a subgrid and related records.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "sb_mockrecord",
3 | "@alias": "the record",
4 | "sb_name": "A record with a subgrid and related records",
5 | "sb_MockRecord_Parent_SecondaryMockRecord": [
6 | {
7 | "@alias": "the related record",
8 | "sb_name": "A related record",
9 | "sb_text": "Some text",
10 | "sb_number": 10,
11 | "sb_yesno": true,
12 | "sb_choice": 108550000,
13 | "sb_choices": "108550000, 108550001",
14 | "sb_dateandtime": "2021-01-01T13:00:00Z",
15 | "sb_dateonly": "2021-01-31",
16 | "sb_currency": 10.00
17 | },
18 | {
19 | "@alias": "the additional related record",
20 | "sb_name": "An additional related record"
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a record with a timeline on the main form.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "sb_mockrecord",
3 | "@alias": "the record",
4 | "sb_name": "A record with a timeline on the main form"
5 | }
6 |
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a record with an alias.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "sb_mockrecord",
3 | "@alias": "the referenced record",
4 | "sb_name": "The referenced record"
5 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a secondary mock record.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "sb_secondarymockrecord",
3 | "@alias": "the secondary mock record",
4 | "sb_name": "A secondary mock record"
5 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/a team.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "team",
3 | "@alias": "the team",
4 | "name": "A team"
5 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Data/data decorated with faker moustache syntax.json:
--------------------------------------------------------------------------------
1 | {
2 | "@logicalName": "sb_mockrecord",
3 | "@alias": "the faked record",
4 | "sb_name": "{{random.word}}",
5 | "sb_text": "{{random.words}}",
6 | "sb_number@faker.number": "{{random.number}}",
7 | "sb_currency@faker.number": "{{finance.amount}}",
8 | "sb_dateonly@faker.date": "{{date.past}}",
9 | "sb_dateandtime@faker.datetime": "{{date.future}}",
10 | "sb_Lookup": {
11 | "@alias": "a nested faked record",
12 | "sb_name": "{{random.word}}"
13 | }
14 | }
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/DataSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Data Steps
2 | In order to automate data creation
3 | As a developer
4 | I want to use pre-existing data creation steps
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 |
9 | Scenario: Set a lookup with an alias
10 | And I have created 'a record with an alias'
11 | And I have created 'a record referencing the record with an alias using @alias.bind'
12 | And I have opened 'the referencing record'
13 | Then I can see a value of 'The referenced record' in the 'sb_parent' lookup field
14 |
15 | Scenario: Generate data at run-time with faker
16 | And I have created 'data decorated with faker moustache syntax'
17 | And I have opened 'the faked record'
18 |
19 | Scenario: Generate data as a named user
20 | And 'an aliased user' has created 'a record with an alias'
21 |
22 | Scenario: Create data that extends from other data
23 | And I have created 'a contact that has been extended with @extend'
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/DialogSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Dialog Steps
2 | In order to automate dialog interaction
3 | As a developer
4 | I want to use pre-existing dialog bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 | And I have created 'a record with an alias'
9 | And I have opened 'the referenced record'
10 |
11 | Scenario: Confirm a confirmation dialog
12 | When I select the 'Show Confirmation Dialog' command
13 | And I confirm when presented with the confirmation dialog
14 |
15 | Scenario: Cancel a confirmation dialog
16 | When I select the 'Show Confirmation Dialog' command
17 | And I cancel when presented with the confirmation dialog
18 |
19 | Scenario: Assign to me on assign dialog
20 | When I select the 'Assign' command
21 | And I assign to me on the assign dialog
22 |
23 | Scenario: Assign to user on assign dialog
24 | When I select the 'Assign' command
25 | And I assign to a user named 'Power Apps Checker Application' on the assign dialog
26 |
27 | Scenario: Assign to team on assign dialog
28 | Given I have created 'a different team'
29 | When I select the 'Assign' command
30 | And I assign to a team named 'A different team' on the assign dialog
31 |
32 | Scenario: Close warning dialog
33 | When I select the 'Show Error Dialog' command
34 | And I close the warning dialog
35 |
36 | Scenario: Click OK on set state dialog
37 | When I select the 'Deactivate' command
38 | And I click ok on the set state dialog
39 |
40 | Scenario: Click cancel on set state dialog
41 | When I select the 'Deactivate' command
42 | And I click cancel on the set state dialog
43 | #TODO:
44 | #Scenario: Cancel publish dialog
45 | #Scenario: Confirm publish dialog
46 | #Scenario: Close opportunity as won
47 | #Scenario: Close opportunity as lost
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/GlobalSearchSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Global Search Steps
2 | In order to automate global search interaction
3 | As a developer
4 | I want to use pre-existing global search bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 | And I have created 'a record configured for global search'
9 | And the 'sb_mockrecord' entity is enabled for categorized search
10 |
11 | Scenario: Perform a global search
12 | When I search globally using the filter 'Record'
13 |
14 | Scenario: Apply a search filter
15 | When I search globally using the filter 'Record'
16 | And I apply a search filter using the filter 'Mock Record'
17 |
18 | Scenario: Open a record from global search results
19 | When I search globally using the filter 'A record configured for global search'
20 | And I open a record from global search on the entity 'Mock Records' in the position of '0'
21 | Then I am presented with a 'Information' form for the 'sb_mockrecord' entity
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/GridSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Grid Steps
2 | In order to automate grid interaction
3 | As a developer
4 | I want to use pre-existing grid bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 |
9 | Scenario: Switch grid view
10 | When I open the 'Mock Records' sub area of the 'Primary Group' group
11 | And I switch to the 'Inactive Mock Records' view in the grid
12 |
13 | Scenario: Open a grid record at a given position
14 | Given I have created 'a record with an alias'
15 | When I open the 'Mock Records' sub area of the 'Primary Group' group
16 | And I open the record at position '0' in the grid
17 | Then I am presented with a 'Information' form for the 'sb_mockrecord' entity
18 |
19 | Scenario: Perform grid search
20 | When I open the 'Mock Records' sub area of the 'Primary Group' group
21 | And I search for 'Some text' in the grid
22 |
23 | Scenario: Clear grid search
24 | When I open the 'Mock Records' sub area of the 'Primary Group' group
25 | And I search for 'Some text' in the grid
26 | And I clear the search in the grid
27 |
28 | Scenario: Sort a grid
29 | When I open the 'Mock Records' sub area of the 'Primary Group' group
30 | And I sort the 'Name' column in the grid using the 'Sort A to Z' option
31 |
32 | Scenario: Assert grid contains an aliased record
33 | Given I have created 'a record with an alias'
34 | When I open the 'Mock Records' sub area of the 'Primary Group' group
35 | Then the grid contains 'the referenced record'
36 |
37 | Scenario: Highlight multiple aliased records from a grid
38 | Given I have created 'a record with an alias'
39 | And I have created 'data decorated with faker moustache syntax'
40 | When I open the 'Mock Records' sub area of the 'Primary Group' group
41 | And I select the following records from the grid
42 | | Alias |
43 | | the referenced record |
44 | | the faked record |
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Hooks/AfterScenarioHooks.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.UiTests.Hooks
2 | {
3 | using System.IO;
4 | using System.Reflection;
5 | using Capgemini.PowerApps.SpecFlowBindings;
6 | using Microsoft.VisualStudio.TestTools.UnitTesting;
7 | using Reqnroll;
8 |
9 | ///
10 | /// After scenario hooks.
11 | ///
12 | [Binding]
13 | public class AfterScenarioHooks : PowerAppsStepDefiner
14 | {
15 | private readonly ScenarioContext scenarioContext;
16 | private readonly TestContext testContext;
17 |
18 | ///
19 | /// Initializes a new instance of the class.
20 | ///
21 | /// The scenario context.
22 | public AfterScenarioHooks(ScenarioContext scenarioContext, TestContext testContext)
23 | {
24 | this.scenarioContext = scenarioContext;
25 | this.testContext = testContext;
26 | }
27 |
28 | ///
29 | /// Publishes the screenshot of the browser when a test fails.
30 | ///
31 | [AfterScenario(Order = 100)]
32 | public void PublishScreenshotForFailedScenario()
33 | {
34 | if (this.scenarioContext.ScenarioExecutionStatus == ScenarioExecutionStatus.TestError)
35 | {
36 | var rootFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
37 | var screenshotsFolder = Path.Combine(rootFolder, "screenshots");
38 |
39 | var fileName = string.Concat(this.scenarioContext.ScenarioInfo.Title.Split(Path.GetInvalidFileNameChars()));
40 | this.testContext.AddResultFile(Path.Combine(screenshotsFolder, $"{fileName}.jpg"));
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/LoginSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Login Steps
2 | In order to automate login interaction
3 | As a developer
4 | I want to use pre-existing login bindings
5 |
6 | Scenario: Login to a given app as a given user
7 | Given I am logged in to the 'Mock App' app as 'an admin'
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/LookupDialogSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Lookup Dialog Steps
2 | In order to automate lookup dialog interaction
3 | As a developer
4 | I want to use pre-existing lookup dialog bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 | And I have created 'a record with a subgrid and related records'
9 | And I have created 'a secondary mock record'
10 | And I have opened 'the record'
11 | When I click the 'Add Existing Secondary Mock Record' command on the 'subgrid' subgrid
12 |
13 | Scenario: Select the first result for a search term in a lookup dialog
14 | When I select 'A secondary mock record' in the lookup dialog
15 |
16 | Scenario: Click add button in a lookup dialog
17 | When I select 'A secondary mock record' in the lookup dialog
18 | And I click Add in the lookup dialog
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/LookupSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Lookup Steps
2 | In order to automate Lookup interaction
3 | As a developer
4 | I want to use pre-existing lookup bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 | When I open the sub area 'Mock Records' under the 'Primary Group' area
9 | And I select the 'New' command
10 |
11 | Scenario: Click new button in lookup
12 | When I search for 'Some text' in the 'sb_lookup' lookup
13 | And I click the new button in the lookup
14 |
15 | Scenario: Open a record at a given position in a lookup
16 | Given I have created 'a secondary mock record'
17 | When I search for 'A secondary mock record' in the 'sb_lookup' lookup
18 | And I open the record at position '0' in the lookup
19 |
20 | Scenario: Perform a search in a lookup
21 | When I search for 'Some text' in the 'sb_lookup' lookup
22 |
23 | Scenario: Switch view in a lookup
24 | When I search for 'Some text' in the 'sb_lookup' lookup
25 | And I switch to the 'Inactive Secondary Mock Records' view in the lookup
26 |
27 | Scenario: Open advanced lookup
28 | When I search for 'Some text' in the 'sb_lookup' lookup
29 | And I click to perform an advanced lookup on 'sb_lookup' lookup
30 |
31 | @ignore # EasyRepro issue: https://github.com/microsoft/EasyRepro/issues/1311
32 | Scenario: Select a related entity in a lookup
33 | When I search for '*' in the 'sb_customer' lookup
34 | And I select the related 'Contacts' entity in the lookup
35 |
36 | Scenario: Assert lookup search results only contain records the given names
37 | Given I have created 'a secondary mock record'
38 | When I search for 'A secondary mock record' in the 'sb_lookup' lookup
39 | Then I can see only the following records in the 'sb_lookup' lookup
40 | | Name |
41 | | A secondary mock record |
42 |
43 | Scenario: Open a record set in a lookup
44 | Given I have created 'data decorated with faker moustache syntax'
45 | And I have opened 'the faked record'
46 | When I select a related 'sb_lookup' lookup field
47 | Then I am presented with a 'Information' form for the 'sb_secondarymockrecord' entity
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/NavigationSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Navigation Steps
2 | In order to automate navigation interaction
3 | As a developer
4 | I want to use pre-existing navigation bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 |
9 | Scenario: Open global search
10 | When I open global search
11 |
12 | Scenario: Open entity quick create
13 | When I open a quick create for the 'Secondary Mock Record' entity
14 |
15 | Scenario: Open a subarea
16 | When I open the 'Mock Records' sub area of the 'Primary Group' group
17 |
18 | Scenario: Open an area
19 | When I open the 'Secondary Area' area
20 |
21 | Scenario: Sign out
22 | When I sign out
23 |
24 | Scenario: Assert an area is visible
25 | Then I see the 'Primary Area' area
26 |
27 | Scenario: Assert a group is visible
28 | Then I see the 'Primary Group' group
29 |
30 | Scenario: Assert a subarea is visible
31 | Then I see the 'Mock Records' subarea
32 |
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.VisualStudio.TestTools.UnitTesting;
2 |
3 | [assembly: Parallelize(Workers = 0, Scope = ExecutionScope.ClassLevel)]
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/RelatedGridSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Related Grid Steps
2 | In order to automate related grid interaction
3 | As a developer
4 | I want to use pre-existing related grid bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 | And I have created 'a record with a subgrid and related records'
9 | And I have opened 'the record'
10 |
11 | @ignore # EasyRepo issue: https://github.com/microsoft/EasyRepro/issues/1310
12 | Scenario: Open record at a given position in a related grid
13 | When I open the related 'Secondary Mock Records' tab
14 | And I open the record at position '0' in the related grid
15 |
16 | Scenario: Open related tab
17 | When I open the related 'Activities' tab
18 |
19 | Scenario: Click command in a related grid
20 | When I open the related 'Activities' tab
21 | And I click the 'Add Existing Activity' button on the related grid
22 |
23 | Scenario: Assert a button is not visible on a flyout in a related grid
24 | When I open the related 'Activities' tab
25 | And I click the 'New Activity' button on the related grid
26 | Then I should not see a 'Missing' button in the flyout on the related grid
27 |
28 | Scenario: Assert a button is visible on a flyout in a related grid
29 | When I open the related 'Activities' tab
30 | And I click the 'New Activity' button on the related grid
31 | Then I should see a 'Task' button in the flyout on the related grid
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/Steps/TestEnvironmentConfigSteps.cs:
--------------------------------------------------------------------------------
1 | namespace Capgemini.PowerApps.SpecFlowBindings.Steps
2 | {
3 | using Microsoft.Xrm.Sdk;
4 | using Microsoft.Xrm.Tooling.Connector;
5 | using System.ServiceModel;
6 | using Reqnroll;
7 |
8 | ///
9 | /// Steps relating to configuring the test environment.
10 | ///
11 | [Binding]
12 | public class TestEnvironmentConfigSteps : PowerAppsStepDefiner
13 | {
14 | ///
15 | /// Enables categorized search for the given entity.
16 | ///
17 | /// The name of the entity.
18 | [Given("the '(.*)' entity is enabled for categorized search")]
19 | public static void GivenTheEntityIsEnabledForCategorizedSearch(string entityLogicalName)
20 | {
21 | using (var serviceClient = GetServiceClient())
22 | {
23 | var request = new OrganizationRequest("SaveEntityGroupConfiguration");
24 | request.Parameters["EntityGroupName"] = "Mobile Client Search";
25 | request.Parameters["EntityGroupConfiguration"] = new QuickFindConfigurationCollection { new QuickFindConfiguration(entityLogicalName) };
26 |
27 | try
28 | {
29 | serviceClient.Execute(request);
30 | }
31 | catch (FaultException ex)
32 | {
33 | // Invalid search entity
34 | if (ex.Detail.ErrorCode == -2147089919)
35 | {
36 | // Already configured as a search entity
37 | return;
38 | }
39 | }
40 | }
41 | }
42 |
43 | private static CrmServiceClient GetServiceClient()
44 | {
45 | var admin = TestConfig.GetUser("an admin");
46 |
47 | return new CrmServiceClient(
48 | $"AuthType=OAuth; " +
49 | $"Username={admin.Username}; " +
50 | $"Password={admin.Password}; " +
51 | $"Url={TestConfig.GetTestUrl()}; " +
52 | $"AppId=51f81489-12ee-4a9e-aaae-a2591f45987d; " +
53 | $"RedirectUri=app://58145B91-0C36-4500-8554-080854F2AC97; " +
54 | $"LoginPrompt=Auto");
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/TimelineSteps.feature:
--------------------------------------------------------------------------------
1 | Feature: Timeline Steps
2 | In order to automate timeline interaction
3 | As a developer
4 | I want to use pre-existing timeline bindings
5 |
6 | Background:
7 | Given I am logged in to the 'Mock App' app as 'an admin'
8 | And I have created 'a record with a timeline on the main form'
9 | And I have opened 'the record'
10 |
11 | Scenario: Add appointment to timeline
12 | When I add an appointment to the timeline with the subject 'A subject', the description 'A description', the duration '1', and the location 'A location'
13 |
14 | @ignore # EasyRepro issue: https://github.com/microsoft/EasyRepro/issues/1075
15 | Scenario: Add note to timeline
16 | When I add a note to the timeline with the title 'A note' and the body 'A note's body'
17 |
18 | Scenario: Add phone call to timeline
19 | When I add a phone call to the timeline with the subject 'A subject', the description 'A description', the duration '1', and the number '07123456789'
20 |
21 | #Requires D365 CE app in target instance
22 | #Scenario: Add post to timeline
23 | # When I post 'A post' to the timeline
24 | Scenario: Add task to timeline
25 | When I add a task to the timeline with the subject 'A subject', the description 'A description' and the duration '1'
26 |
27 | Scenario: Add email to timeline
28 | Given I have created 'a contact'
29 | When I add an email to the timeline with the subject 'A subject', the duration '1', and the following contacts
30 | | Type | Name |
31 | | To | John Smith |
32 |
33 | Scenario: Click create for appointment on the timeline
34 | Given I have created 'a contact'
35 | When I click create for an 'appointment' on the timeline
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/power-apps-bindings.yml:
--------------------------------------------------------------------------------
1 | url: POWERAPPS_SPECFLOW_BINDINGS_TEST_URL
2 | useProfiles: true
3 | browserOptions:
4 | browserType: Chrome
5 | headless: true
6 | width: 1920
7 | height: 1080
8 | startMaximized: false
9 | driversPath: ChromeWebDriver
10 | additionalCapabilities:
11 | capabilityName: capabilityVaue
12 | applicationUser:
13 | tenantId: POWERAPPS_SPECFLOW_BINDINGS_TEST_TENANTID
14 | clientId: POWERAPPS_SPECFLOW_BINDINGS_TEST_CLIENTID
15 | clientSecret: POWERAPPS_SPECFLOW_BINDINGS_TEST_CLIENTSECRET
16 | users:
17 | - username: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_USERNAME
18 | password: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_PASSWORD
19 | alias: an admin
20 | - username: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_USERNAME2
21 | password: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_PASSWORD2
22 | alias: an admin
23 | - username: POWERAPPS_SPECFLOW_BINDINGS_TEST_ADMIN_USERNAME
24 | alias: an aliased user
--------------------------------------------------------------------------------
/bindings/tests/Capgemini.PowerApps.SpecFlowBindings.UiTests/reqnroll.json:
--------------------------------------------------------------------------------
1 | {
2 | "bindingCulture": {
3 | "language": "en-gb"
4 | },
5 | "language": {
6 | "feature": "en-gb"
7 | },
8 | "bindingAssemblies": [
9 | { "assembly": "Capgemini.PowerApps.SpecFlowBindings" }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/sb_PowerAppsSpecFlowBindings_Mock.cdsproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | $(MSBuildExtensionsPath)\Microsoft\VisualStudio\v$(VisualStudioVersion)\PowerApps
5 |
6 |
7 |
8 |
9 |
10 |
11 | 854e57e1-86f3-4e78-9d5e-ae836992dc6a
12 | v4.6.2
13 |
14 | net462
15 | PackageReference
16 | src
17 |
18 |
19 |
20 |
21 | Managed
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | PreserveNewest
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/AppModules/sb_MockApp/AppModule.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | sb_MockApp
4 | 1.0.0.0
5 | 953b9fac-1e5e-e611-80d6-00155ded156f
6 |
7 | 0
8 | 1
9 | 1
10 | 4
11 | 0
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/AppModules/sb_MockApp/AppModule_managed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | sb_MockApp
4 | 1.0.0.0
5 | 953b9fac-1e5e-e611-80d6-00155ded156f
6 |
7 | 0
8 | 1
9 | 1
10 | 4
11 | 0
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_MockRecord/FormXml/quick/{6ac2352e-0322-4c77-b1e8-0f9ed141ef6b}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {6ac2352e-0322-4c77-b1e8-0f9ed141ef6b}
5 | 1.0
6 | 1
7 | 1
8 |
46 | 1
47 | 1
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_MockRecord/FormXml/quick/{6ac2352e-0322-4c77-b1e8-0f9ed141ef6b}_managed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {6ac2352e-0322-4c77-b1e8-0f9ed141ef6b}
5 | 1.0
6 | 1
7 | 1
8 |
46 | 1
47 | 1
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_MockRecord/SavedQueries/{1f735b93-a07a-43bb-9df8-aa452cc8e44d}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {1f735b93-a07a-43bb-9df8-aa452cc8e44d}
10 |
11 |
12 |
13 | |
14 | |
15 |
16 |
17 |
18 | 0
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 1.0
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_MockRecord/SavedQueries/{31f43b0f-0029-406a-aa44-253697a2e30a}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 1
7 | 0
8 | 1
9 | {31f43b0f-0029-406a-aa44-253697a2e30a}
10 |
11 |
12 |
13 | |
14 | |
15 |
16 |
17 |
18 | 4
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 1.0
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_MockRecord/SavedQueries/{3e00e84b-f203-4a23-a48b-0b1e13cf3cd9}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {3e00e84b-f203-4a23-a48b-0b1e13cf3cd9}
10 |
11 |
12 |
13 | |
14 | |
15 |
16 |
17 |
18 | 0
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 1.0
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_MockRecord/SavedQueries/{8bcb3608-d091-44d6-837e-733a09b9f30f}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {8bcb3608-d091-44d6-837e-733a09b9f30f}
10 |
11 |
12 |
13 | |
14 | |
15 |
16 |
17 |
18 | 64
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | 1.0
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_MockRecord/SavedQueries/{8e4f2039-bded-4966-8bec-68c4994b931a}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {8e4f2039-bded-4966-8bec-68c4994b931a}
10 |
11 |
12 |
13 | |
14 | |
15 |
16 |
17 |
18 | 2
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 1.0
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_MockRecord/SavedQueries/{b2f87496-0a45-41b3-ba94-d54dc90afe00}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {b2f87496-0a45-41b3-ba94-d54dc90afe00}
10 |
11 |
12 |
13 | |
14 | |
15 |
16 |
17 |
18 | 1
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | 1.0
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_MockRecord/SavedQueries/{fe19a300-f739-eb11-bf68-000d3a0b851b}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 1
6 | 0
7 | 0
8 | 1
9 | {fe19a300-f739-eb11-bf68-000d3a0b851b}
10 | 8192
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 1.0
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_SecondaryMockRecord/FormXml/quick/{80e3b3be-baa1-4abc-9ff3-1ed82ed2d5aa}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {80e3b3be-baa1-4abc-9ff3-1ed82ed2d5aa}
5 | 1.0
6 | 1
7 | 1
8 |
46 | 1
47 | 1
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_SecondaryMockRecord/FormXml/quick/{80e3b3be-baa1-4abc-9ff3-1ed82ed2d5aa}_managed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {80e3b3be-baa1-4abc-9ff3-1ed82ed2d5aa}
5 | 1.0
6 | 1
7 | 1
8 |
46 | 1
47 | 1
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_SecondaryMockRecord/SavedQueries/{0f56be21-ac88-4347-b464-fd97bacfa84c}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {0f56be21-ac88-4347-b464-fd97bacfa84c}
10 |
11 |
12 |
13 | |
14 | |
15 |
16 |
17 |
18 | 64
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | 1.0
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_SecondaryMockRecord/SavedQueries/{22ea6a27-f739-eb11-bf68-000d3a0b851b}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 1
6 | 0
7 | 0
8 | 1
9 | {22ea6a27-f739-eb11-bf68-000d3a0b851b}
10 | 8192
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | 1.0
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_SecondaryMockRecord/SavedQueries/{42b8b4df-d57c-45fd-ba06-5f19234f35e1}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {42b8b4df-d57c-45fd-ba06-5f19234f35e1}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 |
25 |
26 |
27 | 1
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 1.0
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_SecondaryMockRecord/SavedQueries/{be892914-e72f-446b-b351-8bff8684e42d}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 1
7 | 0
8 | 1
9 | {be892914-e72f-446b-b351-8bff8684e42d}
10 |
11 |
12 |
13 | |
14 | |
15 |
16 |
17 |
18 | 4
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 1.0
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_SecondaryMockRecord/SavedQueries/{ca54c145-3bf1-4d00-99ae-3db3056f1372}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {ca54c145-3bf1-4d00-99ae-3db3056f1372}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 |
25 |
26 |
27 | 2
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.0
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_SecondaryMockRecord/SavedQueries/{cbf2061b-2054-4bc9-80ab-bffdade82ceb}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {cbf2061b-2054-4bc9-80ab-bffdade82ceb}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 |
25 |
26 |
27 | 0
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.0
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_SecondaryMockRecord/SavedQueries/{d81f44f7-26ec-484e-8435-dfd7649fac76}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {d81f44f7-26ec-484e-8435-dfd7649fac76}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 |
25 |
26 |
27 | 0
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 | 1.0
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/RibbonDiff.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{16ad7b3f-073a-eb11-a813-000d3a0b97ca}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 1
6 | 0
7 | 0
8 | 1
9 | {16ad7b3f-073a-eb11-a813-000d3a0b97ca}
10 | 8192
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | 1.0
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{19e7392f-cff5-4320-86d4-496f32b80583}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {19e7392f-cff5-4320-86d4-496f32b80583}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 |
20 |
21 |
22 | 0
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 1.0
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{2bef1fb0-8cab-448d-aa81-0f72c09824ad}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {2bef1fb0-8cab-448d-aa81-0f72c09824ad}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 64
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 1.0
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{48c12097-b8f9-4532-951d-dbb3d900b48c}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {48c12097-b8f9-4532-951d-dbb3d900b48c}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 0
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 1.0
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{522b22fd-1dbc-4843-8c17-4f51514cde84}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {522b22fd-1dbc-4843-8c17-4f51514cde84}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 2
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 1.0
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{61aa85f2-5063-4530-8ec1-251a4326ef01}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {61aa85f2-5063-4530-8ec1-251a4326ef01}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 |
20 |
21 |
22 | 0
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 1.0
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{9cfb31fe-0842-48e3-b34e-3cd7b7425666}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {9cfb31fe-0842-48e3-b34e-3cd7b7425666}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 0
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 1.0
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{ba760875-c5cd-4fe0-8e2f-17d9a1842656}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 1
7 | 0
8 | 1
9 | {ba760875-c5cd-4fe0-8e2f-17d9a1842656}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 4
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 1.0
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{d8cdfb4a-b8f1-493b-94af-c01778a00efd}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {d8cdfb4a-b8f1-493b-94af-c01778a00efd}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 1
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | 1.0
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_primarybusinessprocessflow/SavedQueries/{fa0636aa-9c5a-4f3c-ae17-2734cfdf336d}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {fa0636aa-9c5a-4f3c-ae17-2734cfdf336d}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 0
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 1.0
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/FormXml/main/{c6b374b2-7d8f-44e1-b2a5-641d2765d7ef}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {c6b374b2-7d8f-44e1-b2a5-641d2765d7ef}
5 | 1.0
6 | 1
7 | 1
8 |
38 | 1
39 | 1
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/FormXml/main/{c6b374b2-7d8f-44e1-b2a5-641d2765d7ef}_managed.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {c6b374b2-7d8f-44e1-b2a5-641d2765d7ef}
5 | 1.0
6 | 1
7 | 1
8 |
38 | 1
39 | 1
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/RibbonDiff.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{00f6e446-66c0-42b9-aa57-3eef587a8d35}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {00f6e446-66c0-42b9-aa57-3eef587a8d35}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 64
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | 1.0
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{1cf3fb71-073a-eb11-a813-000d3a0b97ca}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 1
6 | 0
7 | 0
8 | 1
9 | {1cf3fb71-073a-eb11-a813-000d3a0b97ca}
10 | 8192
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | 1.0
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{21d48f05-fad9-4fab-9de5-f31c95f88bcb}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {21d48f05-fad9-4fab-9de5-f31c95f88bcb}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 0
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 1.0
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{282c6496-6c2a-4080-8235-701dee0cdeaf}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {282c6496-6c2a-4080-8235-701dee0cdeaf}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 0
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 1.0
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{34c0c9da-73fc-4548-aef8-5782e1ec1484}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {34c0c9da-73fc-4548-aef8-5782e1ec1484}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 2
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 1.0
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{64057e18-a232-4fd2-8a6f-2cf8c99cd7ae}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 1
7 | 0
8 | 1
9 | {64057e18-a232-4fd2-8a6f-2cf8c99cd7ae}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 4
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 1.0
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{7a94bf0c-7f33-40cc-bc33-345a0653527e}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 1
9 | {7a94bf0c-7f33-40cc-bc33-345a0653527e}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 1
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | 1.0
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{7e8cb7b6-779f-4935-941b-11cc6c48e8e4}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {7e8cb7b6-779f-4935-941b-11cc6c48e8e4}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 |
20 |
21 |
22 | 0
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | 1.0
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{8acd2346-ea45-42ed-8cd5-be3e2b038b87}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {8acd2346-ea45-42ed-8cd5-be3e2b038b87}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 |
19 |
20 |
21 | 0
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | 1.0
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Entities/sb_secondarybusinessprocessflow/SavedQueries/{e8ff9ab9-753d-4b5a-9a5c-2b8d567b9aef}.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 1
5 | 0
6 | 0
7 | 0
8 | 0
9 | {e8ff9ab9-753d-4b5a-9a5c-2b8d567b9aef}
10 |
11 |
12 |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 |
20 |
21 |
22 | 0
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 1.0
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/OptionSets/sb_choice.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | picklist
4 | 1
5 | 1.0.0.0
6 | 1
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
19 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Other/Customizations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | 1033
20 |
21 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Other/Relationships.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Other/Relationships/BusinessUnit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | OneToMany
5 | 1
6 | 1.0
7 | 0
8 | sb_MockRecord
9 | BusinessUnit
10 | NoCascade
11 | NoCascade
12 | NoCascade
13 | NoCascade
14 | NoCascade
15 | OwningBusinessUnit
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | OneToMany
24 | 1
25 | 1.0
26 | 0
27 | sb_SecondaryMockRecord
28 | BusinessUnit
29 | NoCascade
30 | NoCascade
31 | NoCascade
32 | NoCascade
33 | NoCascade
34 | OwningBusinessUnit
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Other/Relationships/Organization.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | OneToMany
5 | 1
6 | 1.0
7 | 0
8 | sb_primarybusinessprocessflow
9 | Organization
10 | NoCascade
11 | NoCascade
12 | NoCascade
13 | NoCascade
14 | NoCascade
15 | OrganizationId
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | OneToMany
24 | 1
25 | 1.0
26 | 0
27 | sb_secondarybusinessprocessflow
28 | Organization
29 | NoCascade
30 | NoCascade
31 | NoCascade
32 | NoCascade
33 | NoCascade
34 | OrganizationId
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Other/Relationships/Owner.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | OneToMany
5 | 1
6 | 1.0
7 | 0
8 | sb_MockRecord
9 | Owner
10 | NoCascade
11 | NoCascade
12 | NoCascade
13 | NoCascade
14 | NoCascade
15 | OwnerId
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | OneToMany
24 | 1
25 | 1.0
26 | 0
27 | sb_SecondaryMockRecord
28 | Owner
29 | NoCascade
30 | NoCascade
31 | NoCascade
32 | NoCascade
33 | NoCascade
34 | OwnerId
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Other/Relationships/ProcessStage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | OneToMany
5 | 1
6 | 1.0
7 | 0
8 | sb_primarybusinessprocessflow
9 | ProcessStage
10 | NoCascade
11 | NoCascade
12 | NoCascade
13 | NoCascade
14 | NoCascade
15 | ActiveStageId
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | OneToMany
24 | 1
25 | 1.0
26 | 0
27 | sb_secondarybusinessprocessflow
28 | ProcessStage
29 | NoCascade
30 | NoCascade
31 | NoCascade
32 | NoCascade
33 | NoCascade
34 | ActiveStageId
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Other/Relationships/Team.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | OneToMany
5 | 1
6 | 1.0
7 | 0
8 | sb_MockRecord
9 | Team
10 | NoCascade
11 | NoCascade
12 | NoCascade
13 | NoCascade
14 | NoCascade
15 | OwningTeam
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | OneToMany
24 | 1
25 | 1.0
26 | 0
27 | sb_SecondaryMockRecord
28 | Team
29 | NoCascade
30 | NoCascade
31 | NoCascade
32 | NoCascade
33 | NoCascade
34 | OwningTeam
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Other/Relationships/TransactionCurrency.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | OneToMany
5 | 1
6 | 1.0.0.0
7 | 0
8 | sb_MockRecord
9 | TransactionCurrency
10 | NoCascade
11 | Restrict
12 | NoCascade
13 | NoCascade
14 | NoCascade
15 | TransactionCurrencyId
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | OneToMany
24 | 1
25 | 1.0.0.0
26 | 0
27 | sb_SecondaryMockRecord
28 | TransactionCurrency
29 | NoCascade
30 | Restrict
31 | NoCascade
32 | NoCascade
33 | NoCascade
34 | TransactionCurrencyId
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Other/Relationships/Workflow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | OneToMany
5 | 1
6 | 1.0
7 | 0
8 | sb_primarybusinessprocessflow
9 | Workflow
10 | NoCascade
11 | NoCascade
12 | NoCascade
13 | NoCascade
14 | NoCascade
15 | ProcessId
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | OneToMany
24 | 1
25 | 1.0
26 | 0
27 | sb_secondarybusinessprocessflow
28 | Workflow
29 | NoCascade
30 | NoCascade
31 | NoCascade
32 | NoCascade
33 | NoCascade
34 | ProcessId
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/WebResources/sb_/js/sb_mockrecord.form.js:
--------------------------------------------------------------------------------
1 | function mockFormNotifications(context) {
2 | context.getFormContext().ui.setFormNotification("A mock info form notification", "INFO", "info_notification");
3 | context.getFormContext().ui.setFormNotification("A mock warning form notification", "WARNING", "warning_notification");
4 | context.getFormContext().ui.setFormNotification("A mock error form notification", "ERROR", "error_notification");
5 | }
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/WebResources/sb_/js/sb_mockrecord.form.js.data.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {6e19b941-083a-eb11-a813-000d3a0b97ca}
4 | sb_/js/sb_mockrecord.form.js
5 | sb_/js/sb_mockrecord.form.js
6 | 3
7 | 1.0.0.0
8 | 0
9 | 0
10 | <Dependencies><Dependency componentType="WebResource"/></Dependencies>
11 | 1
12 | 1
13 | 0
14 | /WebResources/sb_jssb_mockrecordformjs6E19B941-083A-EB11-A813-000D3A0B97CA
15 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/WebResources/sb_/js/sb_mockrecord.ribbon.js:
--------------------------------------------------------------------------------
1 | function mockLookupDialog(defaultEntityType, additionalEntityTypes, allowMultiSelect, defaultViewId, additionalViewIds) {
2 | var lookupOptions =
3 | {
4 | defaultEntityType,
5 | entityTypes: [defaultEntityType],
6 | allowMultiSelect,
7 | defaultViewId,
8 | viewIds: [defaultViewId],
9 | };
10 |
11 | if (additionalEntityTypes) {
12 | lookupOptions.entityTypes.push(...additionalEntityTypes.split(","))
13 | }
14 | if (additionalViewIds) {
15 | lookupOptions.viewIds.push(...additionalViewIds.split(","));
16 | }
17 |
18 | Xrm.Utility.lookupObjects(lookupOptions);
19 | }
20 |
21 | function mockErrorDialog() {
22 | Xrm.Navigation.openErrorDialog({ message: 'A mock error message.' })
23 | }
24 |
25 | function mockConfirmationDialog() {
26 | Xrm.Navigation.openConfirmDialog({ text: 'A mock confirmation message.' });
27 | }
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/WebResources/sb_/js/sb_mockrecord.ribbon.js.data.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | {8c50cecc-0e3a-eb11-a813-000d3a0b97ca}
4 | sb_/js/sb_mockrecord.ribbon.js
5 | sb_/js/sb_mockrecord.ribbon.js
6 | 3
7 | 1.0.0.0
8 | 0
9 | 0
10 | <Dependencies><Dependency componentType="WebResource"/></Dependencies>
11 | 1
12 | 1
13 | 0
14 | /WebResources/sb_jssb_mockrecordribbonjs8C50CECC-0E3A-EB11-A813-000D3A0B97CA
15 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Workflows/MockBusinessProcessError-A35908FE-0FCD-4EB7-826C-405286DE4D9D.xaml.data.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | /Workflows/MockBusinessProcessError-A35908FE-0FCD-4EB7-826C-405286DE4D9D.xaml
4 | 1
5 | 0
6 | 0
7 | 1
8 | 4
9 | 0
10 | sb_triggerbusinessprocesserror
11 | 1
12 | 0
13 | 0
14 | 1
15 | 1
16 | 2
17 | 40
18 | 40
19 | 1
20 | 1
21 | 1.0.0.2
22 | 1
23 | 1
24 | sb_MockRecord
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/bindings/tests/sb_PowerAppsSpecFlowBindings_Mock/src/Workflows/SecondaryBusinessProcessFlow-D21A6986-EB11-429C-B4B1-BB747EEB0991.xaml.data.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | /Workflows/SecondaryBusinessProcessFlow-D21A6986-EB11-429C-B4B1-BB747EEB0991.xaml
4 | 1
5 | 0
6 | 4
7 | 0
8 | 4
9 | 0
10 | 1
11 | 0
12 | 0
13 | 0
14 | 1
15 | 2
16 | 100
17 | 1
18 | sb_secondarybusinessprocessflow
19 | 1
20 | 1.0.0.0
21 | 1
22 | 0
23 | 1
24 | 1
25 | sb_MockRecord
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/driver/.eslintignore:
--------------------------------------------------------------------------------
1 | # don't ever lint node_modules
2 | node_modules
3 | # don't lint build output (make sure it's set to your correct build folder name)
4 | dist
5 | # don't lint nyc coverage output
6 | coverage
7 | # don't lint js
8 | **/*.js
--------------------------------------------------------------------------------
/driver/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: "@typescript-eslint/parser",
3 | parserOptions: {
4 | ecmaVersion: 2020,
5 | sourceType: "module",
6 | project: ['./tsconfig.json'],
7 | },
8 | plugins: [
9 | 'jasmine'
10 | ],
11 | extends: [
12 | 'plugin:jasmine/recommended',
13 | 'airbnb-typescript/base',
14 | ],
15 | env: {
16 | jasmine: true,
17 | },
18 | globals: {
19 | "Xrm": "readonly",
20 | "window": "readonly",
21 | "fetch": "readonly",
22 | },
23 | rules: {
24 | },
25 | };
--------------------------------------------------------------------------------
/driver/.gitattributes:
--------------------------------------------------------------------------------
1 | *.ts text eol=lf
2 |
--------------------------------------------------------------------------------
/driver/.gitignore:
--------------------------------------------------------------------------------
1 | # Node modules
2 | node_modules/
3 |
4 | # Build output
5 | /dist/
6 | /tests/
7 |
8 | # Test output
9 | test_results/
10 |
11 | # IDE settings
12 | /.vscode/
13 |
14 |
--------------------------------------------------------------------------------
/driver/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function (config) {
2 | config.set({
3 | frameworks: ['jasmine', 'karma-typescript'],
4 | files: [
5 | 'src/**/*.ts',
6 | 'test/**/*.ts'
7 | ],
8 | mime: {
9 | 'text/x-typescript': ['ts', 'tsx']
10 | },
11 | preprocessors: {
12 | '**/*.ts': 'karma-typescript'
13 | },
14 | junitReporter: {
15 | outputDir: 'test_results/reports',
16 | suite: 'powerapps-specflow-bindings',
17 | useBrowserName: true,
18 | },
19 | reporters: ['progress', 'karma-typescript', 'junit'],
20 | browsers: ['Chrome'],
21 | mime: {
22 | 'text/x-typescript': ['ts', 'tsx']
23 | },
24 | karmaTypescriptConfig: {
25 | reports:
26 | {
27 | html: {
28 | directory: 'test_results/coverage',
29 | subdirectory: 'html'
30 | },
31 | lcovonly: {
32 | directory: 'test_results/coverage',
33 | subdirectory: 'lcov',
34 | filename: 'lcov.info',
35 | },
36 | cobertura: {
37 | directory: 'test_results/coverage',
38 | subdirectory: 'cobertura',
39 | filename: 'cobertura.xml',
40 | }
41 | }
42 | }
43 | });
44 | };
--------------------------------------------------------------------------------
/driver/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "clean": "if exist dist rd dist /s /q",
4 | "lint": "npx eslint . --fix -o test_results/analysis/eslint.json -f json",
5 | "build": "npm install && npm run lint && webpack --config webpack.config.js",
6 | "build:watch": "webpack --config webpack.config.js --watch",
7 | "test": "karma start karma.conf.js",
8 | "test:ci": "karma start karma.conf.js --single-run"
9 | },
10 | "files": [
11 | "dist"
12 | ],
13 | "devDependencies": {
14 | "@types/faker": "^5.1.3",
15 | "@types/jasmine": "^3.5.10",
16 | "@types/xrm": "^9.0.27",
17 | "@typescript-eslint/eslint-plugin": "^4.2.0",
18 | "@typescript-eslint/parser": "^4.4.0",
19 | "eslint": "^7.11.0",
20 | "eslint-config-airbnb-typescript": "^11.0.0",
21 | "eslint-config-prettier": "^6.12.0",
22 | "eslint-plugin-import": "^2.22.0",
23 | "eslint-plugin-jasmine": "^4.1.1",
24 | "eslint-plugin-jsdoc": "^30.6.4",
25 | "eslint-plugin-prefer-arrow": "^1.2.2",
26 | "fetch-mock": "^9.10.7",
27 | "jasmine": "^3.5.0",
28 | "karma": "^6.3.16",
29 | "karma-chrome-launcher": "^3.1.0",
30 | "karma-jasmine": "^3.3.1",
31 | "karma-junit-reporter": "^2.0.1",
32 | "karma-typescript": "^5.5.3",
33 | "ts-loader": "^8.0.5",
34 | "typescript": "^4.0.3",
35 | "webpack": "^5.91.0",
36 | "webpack-cli": "^4.0.0"
37 | },
38 | "dependencies": {
39 | "faker": "^5.1.0"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/driver/src/data/createOptions.ts:
--------------------------------------------------------------------------------
1 | export interface CreateOptions {
2 | userToImpersonate: string
3 | }
4 |
--------------------------------------------------------------------------------
/driver/src/data/deepInsertResponse.ts:
--------------------------------------------------------------------------------
1 | export interface DeepInsertResponse {
2 | record: { reference: Xrm.LookupValue, alias?: string };
3 | associatedRecords: { reference: Xrm.LookupValue; alias?: string }[]
4 | }
5 |
--------------------------------------------------------------------------------
/driver/src/data/index.ts:
--------------------------------------------------------------------------------
1 | export { default as DataManager } from './dataManager';
2 | export { default as DeepInsertService } from './deepInsertService';
3 | export { DeepInsertResponse } from './deepInsertResponse';
4 | export { default as Record } from './record';
5 | export { TestRecord } from './testRecord';
6 | export { default as Preprocessor } from './preprocessor';
7 | export { default as FakerPreprocessor } from './fakerPreprocessor';
8 |
--------------------------------------------------------------------------------
/driver/src/data/preprocessor.ts:
--------------------------------------------------------------------------------
1 | import Record from './record';
2 |
3 | export default abstract class Preprocessor {
4 | abstract preprocess(data: Record): Record;
5 | }
6 |
--------------------------------------------------------------------------------
/driver/src/data/record.ts:
--------------------------------------------------------------------------------
1 | export default interface Record {
2 | [attribute: string]: number | string | unknown | unknown[];
3 | }
4 |
--------------------------------------------------------------------------------
/driver/src/data/testRecord.ts:
--------------------------------------------------------------------------------
1 | import Record from './record';
2 |
3 | export interface TestRecord extends Record {
4 | '@alias': string;
5 | '@logicalName': string;
6 | }
7 |
--------------------------------------------------------------------------------
/driver/src/driver.ts:
--------------------------------------------------------------------------------
1 | import { DataManager } from './data';
2 | import { TestRecord } from './data/testRecord';
3 |
4 | /**
5 | * Interacts with the Web API and Client API to assist test setup and teardown.
6 | *
7 | * @export
8 | * @class Driver
9 | */
10 | export default class Driver {
11 | private readonly dataManager: DataManager;
12 |
13 | /**
14 | * Creates an instance of Driver.
15 | * @param {TestDataManager} [dataManager] A test data manager.
16 | * @memberof Driver
17 | */
18 | constructor(dataManager: DataManager) {
19 | this.dataManager = dataManager;
20 | }
21 |
22 | /**
23 | * Loads test data into CDS from a JSON string. See Microsoft's docs on a 'Deep Insert'.
24 | * Should contain metadata to allow it to parse directly to an ITestRecord @see ITestRecord
25 | *
26 | * @param {string} json A JSON object.
27 | * @memberof Driver
28 | */
29 | public async loadTestData(json: string): Promise {
30 | const testRecord = JSON.parse(json) as TestRecord;
31 | const logicalName = testRecord['@logicalName'];
32 |
33 | return this.dataManager.createData(logicalName, testRecord);
34 | }
35 |
36 | /**
37 | *
38 | * @param json a JSON object.
39 | * @param userToImpersonate The username of the user to impersonate.
40 | */
41 | public async loadTestDataAsUser(
42 | json: string,
43 | userToImpersonate: string,
44 | ) {
45 | if (!userToImpersonate) {
46 | throw new Error('You have not provided the username of the user to impersonate.');
47 | }
48 |
49 | const testRecord = JSON.parse(json) as TestRecord;
50 | const logicalName = testRecord['@logicalName'];
51 |
52 | return this.dataManager.createData(logicalName, testRecord, { userToImpersonate });
53 | }
54 |
55 | /**
56 | * Deletes data that has been created as a result of any requests to load @see loadJsonData
57 | * @memberof Driver
58 | */
59 | public deleteTestData(): Promise<(Xrm.LookupValue | void)[]> {
60 | return this.dataManager.cleanup();
61 | }
62 |
63 | /**
64 | * Opens a test record.
65 | *
66 | * @param {string} alias The alias of the test record.
67 | * @returns {Xrm.Async.PromiseLike {
71 | if (this.dataManager.refsByAlias[alias] === undefined) {
72 | throw new Error(`Test record with alias '${alias}' does not exist`);
73 | }
74 |
75 | return Xrm.Navigation.openForm({
76 | entityId: this.dataManager.refsByAlias[alias].id,
77 | entityName: this.dataManager.refsByAlias[alias].entityType,
78 | });
79 | }
80 |
81 | /**
82 | * Gets a reference to a test record.
83 | * @param alias The alias of the test record.
84 | */
85 | public getRecordReference(alias: string): Xrm.LookupValue {
86 | return this.dataManager.refsByAlias[alias];
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/driver/src/index.ts:
--------------------------------------------------------------------------------
1 | export { default as Driver } from './driver';
2 | export { DataManager, DeepInsertService, FakerPreprocessor } from './data';
3 | export { CurrentUserRecordRepository, MetadataRepository, AuthenticatedRecordRepository } from './repositories';
4 |
--------------------------------------------------------------------------------
/driver/src/repositories/currentUserRecordRepository.ts:
--------------------------------------------------------------------------------
1 | import Record from '../data/record';
2 | import AssociateRequest from '../requests/associateRequest';
3 | import GenericRecordRepository from './genericRecordRepository';
4 |
5 | /**
6 | * Repository to handle CRUD operations for entities using the logged in user.
7 | *
8 | * @export
9 | * @class RecordRepository
10 | * @extends {Repository}
11 | */
12 | export default class CurrentUserRecordRepository extends GenericRecordRepository {
13 | private readonly webApi: Xrm.WebApiOnline;
14 |
15 | /**
16 | * Creates an instance of CurrentUserRecordRepository.
17 | * @param webApi The web API instance.
18 | */
19 | constructor(webApi: Xrm.WebApiOnline) {
20 | super();
21 |
22 | this.webApi = webApi;
23 | }
24 |
25 | /** @inheritdoc */
26 | public async retrieveRecord(logicalName: string, id: string, query?: string): Promise {
27 | return this.webApi.retrieveRecord(logicalName, id, query);
28 | }
29 |
30 | /** @inheritdoc */
31 | public async retrieveMultipleRecords(
32 | logicalName: string,
33 | query: string,
34 | ): Promise {
35 | return this.webApi.retrieveMultipleRecords(logicalName, query);
36 | }
37 |
38 | /** @inheritdoc */
39 | public async createRecord(logicalName: string, record: Record): Promise {
40 | return this.webApi.createRecord(logicalName, GenericRecordRepository.sanitiseRecord(record));
41 | }
42 |
43 | /** @inheritdoc */
44 | public async upsertRecord(logicalName: string, record: Record): Promise {
45 | if (!record['@key']) {
46 | return this.webApi.createRecord(
47 | logicalName, CurrentUserRecordRepository.sanitiseRecord(record),
48 | );
49 | }
50 |
51 | const retrieveResponse = await this.webApi.retrieveMultipleRecords(
52 | logicalName,
53 | `?$filter=${record['@key']} eq '${record[record['@key'] as string]}'&$select=${logicalName}id`,
54 | );
55 |
56 | if (retrieveResponse.entities.length > 0) {
57 | const id = retrieveResponse.entities[0][`${logicalName}id`];
58 | await this.webApi.updateRecord(
59 | logicalName, id, GenericRecordRepository.sanitiseRecord(record),
60 | );
61 |
62 | return { entityType: logicalName, id };
63 | }
64 |
65 | return this.webApi.createRecord(
66 | logicalName, GenericRecordRepository.sanitiseRecord(record),
67 | );
68 | }
69 |
70 | /** @inheritdoc */
71 | public async deleteRecord(ref: Xrm.LookupValue): Promise {
72 | return this.webApi.deleteRecord(ref.entityType, ref.id) as unknown as Xrm.LookupValue;
73 | }
74 |
75 | /** @inheritdoc */
76 | public async associateManyToManyRecords(
77 | primaryRecord: Xrm.LookupValue,
78 | relatedRecords: Xrm.LookupValue[],
79 | relationship: string,
80 | ): Promise {
81 | this.webApi.execute(new AssociateRequest(primaryRecord, relatedRecords, relationship));
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/driver/src/repositories/index.ts:
--------------------------------------------------------------------------------
1 | export { default as MetadataRepository } from './metadataRepository';
2 | export { default as RecordRepository } from './recordRepository';
3 | export { default as CurrentUserRecordRepository } from './currentUserRecordRepository';
4 | export { default as AuthenticatedRecordRepository } from './authenticatedRecordRepository';
5 |
--------------------------------------------------------------------------------
/driver/src/repositories/recordRepository.ts:
--------------------------------------------------------------------------------
1 | import Record from '../data/record';
2 |
3 | export default interface RecordRepository {
4 | retrieveRecord(logicalName: string, id:string, query?: string): Promise;
5 | retrieveMultipleRecords(logicalName: string, query?: string): Promise;
6 | createRecord(logicalName: string, record: Record): Promise;
7 | upsertRecord(logicalName: string, record: Record): Promise;
8 | deleteRecord(ref: Xrm.LookupValue): Promise;
9 | associateManyToManyRecords(
10 | primaryRecord: Xrm.LookupValue,
11 | relatedRecords: Xrm.LookupValue[],
12 | relationship: string,
13 | ): Promise;
14 | }
15 |
--------------------------------------------------------------------------------
/driver/src/requests/associateRequest.ts:
--------------------------------------------------------------------------------
1 | import { Request } from './request';
2 |
3 | export default class AssociateRequest implements Request {
4 | public target: Xrm.LookupValue;
5 |
6 | public relatedEntities: Xrm.LookupValue[];
7 |
8 | public relationship: string;
9 |
10 | constructor(target: Xrm.LookupValue, relatedEntities: Xrm.LookupValue[], relationship: string) {
11 | this.target = target;
12 | this.relatedEntities = relatedEntities;
13 | this.relationship = relationship;
14 | }
15 |
16 | public getMetadata(): Xrm.Metadata.ActionRequestMetadata {
17 | return {
18 | parameterTypes: {
19 | target: {
20 | typeName: `mscrm.${this.target.entityType}`,
21 | structuralProperty: 5,
22 | },
23 | relatedEntities: {
24 | typeName: 'Collection(mscrm.crmbaseentity)',
25 | structuralProperty: 4,
26 | },
27 | relationship: {
28 | typeName: 'Edm.String',
29 | structuralProperty: 1,
30 | },
31 | },
32 | operationType: 2,
33 | operationName: 'Associate',
34 | };
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/driver/src/requests/index.ts:
--------------------------------------------------------------------------------
1 | export { default as AssociateRequest } from './associateRequest';
2 | export { Request } from './request';
3 |
--------------------------------------------------------------------------------
/driver/src/requests/request.ts:
--------------------------------------------------------------------------------
1 | export interface Request {
2 | getMetadata(): Xrm.Metadata.ActionRequestMetadata
3 | }
4 |
--------------------------------------------------------------------------------
/driver/src/types/xrm.d.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-vars */
2 | declare namespace Xrm {
3 | namespace Metadata {
4 | type RelationshipMetadata =
5 | Xrm.Metadata.NToNRelationshipMetadata | Xrm.Metadata.OneToNRelationshipMetadata;
6 |
7 | interface NToNRelationshipMetadata {
8 | Entity1LogicalName: string;
9 | Entity2LogicalName: string;
10 | IntersectEntityName: string;
11 | Entity1IntersectAttribute: string;
12 | Entity2IntersectAttribute: string;
13 | Entity1NavigationPropertyName: string;
14 | Entity2NavigationPropertyName: string;
15 | IsCustomRelationship: boolean;
16 | IsValidForAdvancedFind: boolean;
17 | SchemaName: string;
18 | SecurityTypes: string;
19 | IsManaged: boolean;
20 | RelationshipType: string;
21 | IntroducedVersion: string;
22 | MetadataId: string;
23 | }
24 |
25 | interface OneToNRelationshipMetadata {
26 | ReferencedAttribute: string;
27 | ReferencedEntity: string;
28 | ReferencingAttribute: string;
29 | ReferencingEntity: string;
30 | IsHierarchical: boolean;
31 | ReferencedEntityNavigationPropertyName: string;
32 | ReferencingEntityNavigationPropertyName: string;
33 | RelationshipBehavior: number;
34 | IsCustomRelationship: boolean;
35 | IsValidForAdvancedFind: boolean;
36 | SchemaName: string;
37 | SecurityTypes: string;
38 | IsManaged: boolean;
39 | RelationshipType: string;
40 | IntroducedVersion: string;
41 | MetadataId: string;
42 | }
43 |
44 | interface ActionRequestMetadata {
45 | parameterTypes: { [parameter: string]: ParameterMetadata },
46 | operationType: number,
47 | operationName: string
48 | }
49 |
50 | interface ParameterMetadata {
51 | typeName: string,
52 | structuralProperty: number
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/driver/test/requests/associateRequest.spec.ts:
--------------------------------------------------------------------------------
1 | import AssociateRequest from '../../src/requests/associateRequest';
2 |
3 | let associateRequest: AssociateRequest;
4 |
5 | describe('AssociateRequest', () => {
6 | const target: Xrm.LookupValue = {
7 | entityType: 'contact',
8 | id: '',
9 | };
10 | const relatedEntities: Xrm.LookupValue[] = [
11 | {
12 | entityType: 'account',
13 | id: '',
14 | },
15 | {
16 | entityType: 'account',
17 | id: '',
18 | },
19 | ];
20 | const relationship = 'contact_account';
21 |
22 | beforeEach(() => {
23 | associateRequest = new AssociateRequest(target, relatedEntities, relationship);
24 | });
25 |
26 | describe('getMetadata', () => {
27 | it('replaces parameterTypes.target.typeName with target entity type', () => {
28 | expect(associateRequest.getMetadata().parameterTypes.target.typeName).toBe(`mscrm.${target.entityType}`);
29 | });
30 | });
31 | });
32 |
--------------------------------------------------------------------------------
/driver/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2015",
4 | "module": "ES2015",
5 | "moduleResolution": "node",
6 | "alwaysStrict": true,
7 | "strict": true,
8 | "sourceMap": true,
9 | "outDir": "dist",
10 | "allowSyntheticDefaultImports": true,
11 | },
12 | "include": [
13 | "src",
14 | "test",
15 | ],
16 | }
--------------------------------------------------------------------------------
/driver/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = {
4 | watch: false,
5 | entry: './src/index.ts',
6 | mode: 'development',
7 | devtool: 'inline-source-map',
8 | module: {
9 | rules: [
10 | {
11 | test: /\.tsx?$/,
12 | use: 'ts-loader',
13 | exclude: /node_modules/,
14 | },
15 | ]
16 | },
17 | resolve: {
18 | extensions: ['.ts', '.js'],
19 | },
20 | output: {
21 | filename: 'driver.js',
22 | path: path.resolve(__dirname, 'dist'),
23 | libraryTarget: 'var',
24 | library: 'PowerAppsSpecFlowBindings'
25 | },
26 | };
--------------------------------------------------------------------------------
/scripts/Remove-InvalidSymbolsPackageFiles.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | param (
3 | [Parameter(Mandatory = $true)]
4 | [String]
5 | $SymbolPackageDirectory
6 | )
7 | Add-Type -assembly System.IO.Compression.FileSystem
8 |
9 | Get-ChildItem "$SymbolPackageDirectory/*" -Include *.snupkg | ForEach-Object {
10 | Write-Host "Removing invalid files from $($_.Name)"
11 | $oldName = [IO.Path]::GetFileName($_.Name)
12 | $newName = [IO.Path]::GetFileName([IO.Path]::ChangeExtension($_.Name, "zip"))
13 | $zipFile = Rename-Item -Path $_.FullName -NewName $newName -PassThru
14 | $zip = [System.IO.Compression.ZipFile]::Open($zipFile.FullName, "Update")
15 |
16 | $allowedExtensions = @('.pdb', '.nuspec', '.xml', '.psmdcp', '.rels', '.p7s')
17 | ($zip.Entries | Where-Object { $allowedExtensions -notcontains [IO.Path]::GetExtension($_.Name) }) | ForEach-Object {
18 | Write-Host "Removing $($_.Name)"
19 | $_.Delete()
20 | }
21 |
22 | $zip.Dispose()
23 |
24 | Rename-Item -Path $zipFile.FullName -NewName $oldName
25 | }
26 |
--------------------------------------------------------------------------------
/scripts/Set-AllUserLocalesToUk.ps1:
--------------------------------------------------------------------------------
1 | Install-Module -Name Microsoft.Xrm.Tooling.CrmConnector.PowerShell -Force -Scope CurrentUser -AllowClobber
2 |
3 | Write-Host "Updating user settings to English (United Kingdom)"
4 |
5 | # $connectionString = "AuthType=OAuth; Username=$env:username; Password=$env:password; Url=$env:environmentUrl; AppId=51f81489-12ee-4a9e-aaae-a2591f45987d; RedirectUri=app://58145B91-0C36-4500-8554-080854F2AC97; LoginPrompt=Auto";
6 | $connectionString = "AuthType=ClientSecret; ClientId=$env:clientId; ClientSecret=`"$env:clientSecret`"; Url=$env:environmentUrl";
7 | Write-Host $connectionString
8 |
9 | $conn = Get-CrmConnection -ConnectionString $connectionString;
10 |
11 | $condition = [Microsoft.Xrm.Sdk.Query.ConditionExpression]::new();
12 | $condition.AttributeName = "localeid";
13 | $condition.Operator = [Microsoft.Xrm.Sdk.Query.ConditionOperator]::NotEqual;
14 | $condition.Values.Add(2057);
15 |
16 | $filter = [Microsoft.Xrm.Sdk.Query.FilterExpression]::new();
17 | $filter.Conditions.Add($condition);
18 |
19 | $query = [Microsoft.Xrm.Sdk.Query.QueryExpression]::new("usersettings");
20 | $query.ColumnSet.AddColumns("localeid");
21 | $query.Criteria.AddFilter($filter);
22 |
23 | $executeMultipleSettings = [Microsoft.Xrm.Sdk.ExecuteMultipleSettings]::new()
24 | $executeMultipleSettings.ContinueOnError = $true
25 | $executeMultipleSettings.ReturnResponses = $false
26 |
27 | $executeMultipleRequests = [Microsoft.Xrm.Sdk.OrganizationRequestCollection]::new()
28 |
29 | $usersettings = $conn.RetrieveMultiple($query).Entities
30 | $usersettings | ForEach-Object {
31 | $_.Attributes['localeid'] = 2057
32 | $updateRequest = [Microsoft.Xrm.Sdk.Messages.UpdateRequest]::new()
33 | $updateRequest.Target = $_
34 | $executeMultipleRequests.Add($updateRequest)
35 | }
36 |
37 | $executeMultipleRequest = [Microsoft.Xrm.Sdk.Messages.ExecuteMultipleRequest]::new()
38 | $executeMultipleRequest.Settings = $executeMultipleSettings
39 | $executeMultipleRequest.Requests = $executeMultipleRequests
40 | $conn.Execute($executeMultipleRequest)
--------------------------------------------------------------------------------
/scripts/Sync-DataverseUsers.ps1:
--------------------------------------------------------------------------------
1 | param (
2 | [Parameter(Mandatory)]
3 | [String]
4 | $EnvironmentName,
5 | [Parameter(Mandatory)]
6 | [String[]]
7 | $ObjectIds,
8 | [Parameter(Mandatory)]
9 | [String]
10 | $TenantId,
11 | [Parameter(Mandatory)]
12 | [String]
13 | $ClientId,
14 | [Parameter(Mandatory)]
15 | [String]
16 | $ClientSecret
17 | )
18 |
19 | Install-Module -Name Microsoft.PowerApps.Administration.PowerShell -Force -Scope CurrentUser -AllowClobber
20 |
21 | Write-Host "Authenticating as $ClientId."
22 | Add-PowerAppsAccount -TenantID $TenantId -ApplicationId $ClientId -ClientSecret $ClientSecret
23 |
24 | $ObjectIds | ForEach-Object {
25 | Write-Host "Syncing $_."
26 | Add-AdminPowerAppsSyncUser -EnvironmentName $EnvironmentName -PrincipalObjectId $_ | Out-Null
27 | }
--------------------------------------------------------------------------------
/templates/build-and-test-stages.yml:
--------------------------------------------------------------------------------
1 | stages:
2 |
3 | - stage: BuildAndTest
4 | displayName: Build and Test
5 | jobs:
6 | - template: build-and-test-job.yml
7 |
--------------------------------------------------------------------------------