Dynamics CRM Model-Driven Apps: Implement Niam.Xrm.Client and Niam.Xrm.Client.Test

Now we go to the last part of the tutorial for Front-end Customization. We will implement Niam.Xrm.Client and Niam.Xrm.Client.Tests on the project we set up earlier. Niam.Xrm.Client is a framework wrapper for Xrm object that focuses on make customization easier, while Niam.Xrm.Client.Tests will mainly focus on Xrm.webApi mock objects and testing related helpers. Both packages are in beta versions but already sufficient for our scenario that we will cover in this post.

Installing

We will install those packages using this command:

npm install --save-dev @insurgo/niam.xrm.client @insurgo/niam.xrm.client.test uuid

Usage

In the last post, we have a scenario when on a load of the new record, we want to set the default values for attributes new_name to be “Form TypeScript UI” and new_documentdate to be today date. We will set another attribute called new_submittedby with a default account whose name equal to “A Datum Corporation”.

First, we will add a new class in src/new_customdocument/entity.ts (This class holds our definition (metadata) of the entity):

export type entity  = {
    new_name: string;
    new_documentdate: Date;
    new_submittedby: Xrm.LookupValue[]
};

Then in src/new_customdocument/form.spec.ts, we will refactor the code to this:

import { expect } from 'chai';
import { XrmMockGenerator } from 'xrm-mock';
import { initFormCreate, setSubmittedBy } from './form';
import { TestApiContext } from '@insurgo/niam.xrm.client.test';
import * as sinon from 'sinon';

describe('new_customdocument form tests', () => {
    beforeEach(() => {
        XrmMockGenerator.initialise();
        XrmMockGenerator.Attribute.createString('new_name');
        XrmMockGenerator.Attribute.createDate('new_documentdate');
        XrmMockGenerator.Attribute.createLookup('new_submittedby', []);
    });

    describe('on form update', () => {
        beforeEach(() => {
            sinon.stub(XrmMockGenerator.getFormContext().ui, 'getFormType').
                returns(XrmEnum.FormType.Update);
        });

        it('initFormCreate skip set', () => {
            initFormCreate(XrmMockGenerator.getEventContext());

            var formContext = XrmMockGenerator.getFormContext();

            expect(formContext.getAttribute('new_name').getValue()).to.empty;
            expect(formContext.getAttribute('new_documentdate').getValue()).to.undefined;
        });
    });

    describe('on form create', () => {
        let testContext: TestApiContext;
        beforeEach(() => {
            sinon.stub(XrmMockGenerator.getFormContext().ui, 'getFormType').
                returns(XrmEnum.FormType.Create);

            testContext = new TestApiContext();
            sinon.replace(Xrm, 'WebApi', testContext.webApi);
        });

        it('initFormCreate set name and document date', () => {
            initFormCreate(XrmMockGenerator.getEventContext());

            var formContext = XrmMockGenerator.getFormContext();

            expect(formContext.getAttribute('new_name').getValue()).to.equal('Form TypeScript UI');
            expect(formContext.getAttribute('new_documentdate').getValue()).to.not.null;
        });

        it('initFormCreate set submittedby', async () => {
            const contact = {
                id: 'account-id',
                logicalName: 'account',
                name: 'A Datum Corporation',
                accountid: 'account-id'
            };

            testContext.init([contact]);
            await setSubmittedBy(XrmMockGenerator.getEventContext());

            const formContext = XrmMockGenerator.getFormContext();
            const submittedByRef = formContext.getAttribute('new_submittedby').getValue();
            expect(submittedByRef[0].id).to.equal('account-id');
        });
    });
});

On that code, you need to see how TestApiContext works. we create a new instance of it in every test. Then on “initFormCreate set submittedby” case, we add a contact to be inserted on testContext.init method. This means that we want to add the contact to our in-memory database.

Then this is the implementation code in src/new_customdocument/form.ts:

import { entity } from './entity';
import { Fx } from '@insurgo/niam.xrm.client';

export function formLoaded(context: Xrm.Events.EventContext) {
    initFormCreate(context);
}

export function initFormCreate(context: Xrm.Events.EventContext) {
    const fx = new Fx<entity>(context);
    if (fx.formContext.ui.getFormType() !== XrmEnum.FormType.Create) return;

    fx.set('new_name', 'Form TypeScript UI');
    fx.set('new_documentdate', new Date());

    setSubmittedBy(context);
}

export async function setSubmittedBy(context: Xrm.Events.EventContext) {
    const fx = new Fx<entity>(context);

    const data = await Xrm.WebApi.retrieveMultipleRecords('account', 
        "?$filter=name eq 'A Datum Corporation'");
    const valid = data && data.entities && data.entities.length > 0;
    if (!valid) return;
    
    var accountRef: Xrm.LookupValue[] = [
        {
            entityType: 'account',
            id: data.entities[0].accountid,
            name: data.entities[0].name
        }
    ];
    fx.set('new_submittedby', accountRef);
}

After you enable the Niam.Xrm.Client, this is the IntelliSense response when we want to set/get the attribute (more strict + easy = happier):

IntelliSense to the rescue

If you run the test using this command:

npm run test

This is the result of the test:

Test result

After you build it and deploy it to your environment, this is the result:

Result on load of the new record

You can get the source code here.

Summary

  • Niam.Xrm.Client has a feature like early-bound in plugin development. Using this way, you can be more strict in the front-end as well.
  • Niam.Xrm.Client.Test has a feature to enable the in-memory database for Xrm.webApi. You need to replace Xrm.WebApi and replace it webApi object in TestApiContext.

Related Post

3 thoughts on “Dynamics CRM Model-Driven Apps: Implement Niam.Xrm.Client and Niam.Xrm.Client.Test

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.