Unit Test Case for Typescript Webresources

In our project, we got a requirement to write unit test cases for typescript webresources. In this blog, I am going to explain step by step process to write unit test case for one scenario.

Scenario: Set the Country field on Account Form. The data should be copied from logged in user profile.

I used the following frameworks to write unit test case.

Process: Follow the below steps

  1. Create a “Blank Node.js Web Application” project.
  2. Install the following node packages.
    • mocha@7.2.0
    • sinon@9.0.2
    • xrm-mock@3.4.18
    • @types/xrm@9.0.18
  3. Add “src” and “test” folders in the project.
  4. Add “Account.ts” file under “src” folder and “AccountTest.ts” file under “test” folder.
  5. Add the below code in Account.ts
export default class Account {
    
    public static setCountryField(): Promise<void> {
        let loggedInUserId = Xrm.Page.context.getUserId().replace("{", "").replace("}", "");
        if (loggedInUserId != null) {
            return new Promise((resolve, reject) => {
                Xrm.WebApi.retrieveRecord("systemuser", loggedInUserId, "?$select=_new_countryid_value").then((result) => {
                    resolve(
                        Xrm.Page.getAttribute("new_defaultcountryid").setValue([{
                            entityType: result["_new_countryid_value@Microsoft.Dynamics.CRM.lookuplogicalname"],
                            id: result["_new_countryid_value"],
                            name: result["_new_countryid_value@OData.Community.Display.V1.FormattedValue"]
                        }]));
                }).catch((error) => {
                    reject(error);
                });
            });
        }
    }

  1. Add the below code in AccountTest.ts
import { beforeEach, describe, it } from "mocha";
import Account from "../src/Account";
import assert = require('assert');
import { XrmMockGenerator } from "xrm-mock";
import * as sinon from "sinon"

describe("SetCountryTest", () => {
    beforeEach(() => {
        XrmMockGenerator.initialise();
        XrmMockGenerator.Attribute.createLookup("new_defaultcountryid", null);
    });

    it("Get logged in user country and set it on Account Form", () => {
        sinon.stub(Xrm.WebApi, "retrieveRecord").resolves({
            "_new_countryid_value@Microsoft.Dynamics.CRM.lookuplogicalname": "new_country",
            "_new_countryid_value": "{00000000-0000-0000-0000-000000000002}",
            "_new_countryid_value@OData.Community.Display.V1.FormattedValue": "Germany"
        });
        return Account.setCountryField().then(() => {
            let countryId = Xrm.Page.getAttribute("new_defaultcountryid").getValue();
            assert.equal(countryId[0].id, "{00000000-0000-0000-0000-000000000002}");
        });
    });
});
  1. Build Solution (You have to build project/solution before Run Tests).
  2. Open the Test Explorer (Test –> Test Explorer).
  3. Run the tests by clicking the Run All link in Test Explorer. Or, you can run tests by selecting one or more tests or groups, right-clicking, and selecting Run Selected Tests from the shortcut menu.
  4. Here is the result from Test Explorer.
  1. You can also debug selected tests by selecting Debug Selected Tests.
  2. Final folder structure looks like below

Hope it helps…

Using Data Spawner component (SSIS) to generate sample data in Dynamics 365

Nishant Rana's Weblog

At times we need to generate sample data for our entities in Dynamics 365 for various reasons, performance testing is one of them.

Data Spawner component which is part of KingswaySoft’s
SSIS Productivity Pack provides us the most efficient way of doing so.

Download the component here –

https://www.kingswaysoft.com/solutions/ssis-data-generation-anonymization-components/data-spawner-component

Let us generate the sample data for Contact Entity.

Add the Data Spawner component to the Data Flow along with the CDS Destination component in the integration service project.

Double click the Data Spawner to open the editor.

Click on Add + button to specify the columns, here we have specified four different columns.

We have kept the name for each of the columns, same as the schema name so that it is easy to map them in CDS Destination.

For the First Name column, we have specified Data Type as nvarchar and Spawn Type as the First Name, which will…

View original post 180 more words

PluginType not found in PluginAssembly which has a total of [0] plugin/workflow activity types.

Today while updating one of my plugin assembly in plugin registration tool, I got the below error.

Unhandled Exception: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]: PluginType  not found in PluginAssembly which has a total of [0] plugin/workflow activity types.
Detail: <OrganizationServiceFault xmlns="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <ActivityId>2b06487c-19ac-4d04-9b0f-0d8c95aa2eb3</ActivityId>
  <ErrorCode>-2147204725</ErrorCode>
  <ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
    <KeyValuePairOfstringanyType>
      <a:key>ApiExceptionSourceKey</a:key>
      <a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">Plugin/Microsoft.Crm.ObjectModel.PluginAssemblyService</a:value>
    </KeyValuePairOfstringanyType>
    <KeyValuePairOfstringanyType>
      <a:key>ApiOriginalExceptionKey</a:key>
      <a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">Microsoft.Crm.CrmException: PluginType not found in PluginAssembly  which has a total of [0] plugin/workflow activity types. ---&gt; Microsoft.Crm.CrmException: PluginType not found in PluginAssembly which has a total of [0] plugin/workflow activity types.

After a bit of research, I found that I forgot to change the “Copy Local” property to false of “Microsoft.Xrm.Sdk” and “Microsoft.Crm.Sdk.Proxy” dlls. In our case, we are using ILMerge to merge different dlls but these Microsoft dlls should not be part of the Merged dll. After setting the “Copy Local” property to “false”, Plugin assembly updated sucessfully.

Hope it helps…..

Get Transaction currency name for logged in user in Dynamics 365

Debajit's Dynamic CRM Blog

Recently Microsoft have released quite a few updates to it’s client API and one such is the update to API for getting the currency name of the logged in user.

All this time, Microsoft had an API to get the transaction currency id of the logged in user using the API – . You needed to run a separate query to fetch the currency name based on currency id. However it is being deprecated now and now the replacement API is

This wonderful api returns the name of the currency as well as the id and entityType.

Below is the sample output

Wonderful isn’t it?

Hope this helps!

Debajit Dutta

(Dynamics MVP)

For consultation/ corporate training visit www.xrmforyou.com or reach out to us at info@xrmforyou.com

Our product offerings:

Role based views for Dynamics 365 (http://www.xrmforyou.com/role-based-views.html)

CRM-Sharepoint Attachment uploader and metadata manager (http://www.xrmforyou.com/sharepoint-integrator.html)

Record Cloner for Dynamics…

View original post 4 more words

Nested Editable Grids in D365 CE v9 UCI

D365 Demystified

Nested Grids – as the name suggests is a grid-within-a-grid (or rather, Grid-ception!). Nested Grids will let you expand a sub-grid entry to look at another grid of the expanded record. This depends on how you configure it.

Be aware, Nested Grids work with Editable Grids and only for Tablet, Phones and Unified Interface. This is not available for the classic Web UI.

Configuring Nested Editable Grid

Here’s my entity structure – I have Account, having multiple Contacts and each Contact, having Opportunities under them. Like in the below diagram –

relationships

Now, I have a Contact grid on my Account form (just like we usually do).

  1. I now will have to choose the grid to be a Editable Grid Control as follows. Also, click on the Nested grid view control as pointed
    editableGridControl
  2. On clicking the Nested grid view pencil, the next dialog box will let you select what entity…

View original post 186 more words

All about Power Studio App (Flow Studio)

Power studio app (previously flow studio) is a great app for Flow makers and administrators. It is offering wide range of options to manage Flows as an administrator. You can use the following functionalities with this app.

  • Advanced Discovery
  • Sort Everything
  • Flexible Tagging
  • Migrate and Deploy
  • Improved Administration
  • Run Sparklines
  • Maker Superpowers
  • Version Snapshots
  • Full Text Search
  • Export your data

How to use it?

  1. Open this link and sign in with your CDS credentials.
  2. You can see list of Flows with multiple operations like Export Excel, Flow Diagram, Sort, Filter and etc.,
  3. Click on (…) next to any flow for more operations. You can see all operations that you can perform on each Flow in the below screen shot.

They are offering one month trail version, so I suggest everyone to subscribe for trail version and explore all the options. I like the Flow Diagram and Snapshots options the most.

Advanced Filtering from Home Grid View is now available with 2020 Release Wave 1

While exploring the new features with 2020 Release wave 1, I found the advanced filtering option from Home Grid View. You can find this filter button before the search box on Home Grid View.

Advanced filter looks like below. I applied two more extra filters to the existing “My Active Accounts” view. You can add conditions from the related entity also. Click on “Apply” button to filter the records.

Here is the result after these conditions applied. You can remove the filters by clicking on the below highlighted button.

You can also create a personal view with these conditions from the ribbon button.

Scratching your head over why your custom ribbon buttons are not visible or not working as expected–The wonderful Command checker tool from Microsoft might just save your day.

Debajit's Dynamic CRM Blog

If you have ever worked in Dynamics 365 implementation then it is almost certain that you must have played around with ribbons. And more often than not you must have faced scenario where you have multiple enable and display rules on the form and not sure why the ribbon button is showing up on the form when it is supposed not to show up or vice versa.

And what we do to resolve it. Well, we play around with the ribbon customizations which takes much more time than other customizations in Dynamics 365. And if it is some custom rule, we would attach debuggers to our custom code and debug and find what is going wrong.

What if I tell you that Microsoft just released something which shall help you identify which of your ribbon rule is evaluating to what in a matter of few mins so that you can…

View original post 441 more words

Performance issue while migrating activities

In this post I am going to explain how did I solved the performance issue while migrating activities using SSIS packages. My approach to migrate the data is, export the CRM data to Staging DB by using “Data Export” services and then to CRM by using SSIS packages. We all know that we have to migrate the party list (from,to,cc,bcc etc.,) data as part of activities migration and we need to write either SQL functions / script component to migrate party list data. I opted for SQL functions and I used those functions in SQL view. For ex, in email view I have to call 4 functions for each record to transform the data in party list fields (from, to, cc, bcc). So it leads to huge performance issue while fetching the data from staging table.

After a bit of research, I realised that I don’t have index for my intermediate tables. So I immediately created the following indexes.

  1. Index for “activityid” and “participationtype” fields on “activityparty” table
  2. Index for “activityid” on “email” table

and here is my SQL Function

CREATE function [dbo].[GetPartyListForEmail] (@activityid uniqueidentifier, @partyType int)
RETURNS nvarchar(max)
AS
BEGIN
	DECLARE @partyList nvarchar(max);
	DECLARE @partyid as uniqueidentifier;
	DECLARE @logicalname as nvarchar(100);
	DECLARE @getPartyList nvarchar(max);
	 
	DECLARE db_cursor CURSOR FOR SELECT ap.PartyId,ap.partyid_entitytype 
	FROM email em 
	LEFT JOIN activityparty ap ON ap.ActivityId = em.ActivityId
	WHERE ap.ParticipationTypeMask = @partyType AND em.ActivityId = @activityid;

	OPEN db_cursor
	FETCH NEXT FROM db_cursor INTO @partyid, @logicalname
		WHILE @@FETCH_STATUS = 0
			BEGIN
				IF @logicalname = 'systemuser'
				BEGIN
					SET @partyid = (SELECT TargetUserId FROM mapping_systemuser WHERE SourceUserId = @partyid)
				END

				IF @partyid IS NOT NULL
				BEGIN
					SET @partyList = Concat(@partyList, @logicalname);
					SET @partyList = Concat(@partyList, ':');
					SET @partyList = Concat(@partyList, @partyid);
					SET @partyList = Concat(@partyList, ';');
				END
				FETCH NEXT FROM db_cursor INTO @partyid, @logicalname
			END

	IF (LEN(@partyList)) > 0  
	BEGIN
		SET @partyList= left (@partyList, len(@partyList)-1)
	END

	RETURN @partyList;
END

After adding the indexes, I experienced huge improvement and processed 230k records in 2 hours.

Hope it helps…..

ReviseQuoteRequest: The quote cannot be revised because it is not in closed state

I used “ReviseQuoteRequest” SDK method in one of the plugin to revise the quote. When you revise a quote from UI, first it will close the existing quote and then create a revised version of this quote. So I expected that “ReviseQuoteRequest” also do the same, but I got the “The quote cannot be revised because it is not in closed state” error when I am using the SDK method. After a bit of research, I understood that we have to Close the quote before revise it. So I closed the quote by using “CloseQuoteRequest” and then “ReviseQuoteRequest” which solved my issue. But my suggestion that SDK method should handle the close quote request also internally. Please let me know your thoughts on it.