The remainder of this walkthrough focuses on EventRegistrationFlowDetailsControllerSpec.js. The full code is below.

EventRegistrationFlowDetailsControllerSpec.js

const chai = require('chai');
const expect = chai.expect;
const sinon = require('sinon');
const sinonChai = require("sinon-chai");
chai.use(sinonChai);
const _ = require('lodash')

require('mocha-aura/ensurePath')();

const {
    auraFactory,
    eventFactory,
    componentFactory,
    useComponentAdapters,
    apexCallFactory,
    apexSuccessResult,
    apexErrorResult
} = require('mocha-aura/aura')

const { frameworkAdapters } = require('mocha-fonteva-adapters');
useComponentAdapters(frameworkAdapters);

const {
    useDataGenerators,
    LTEAttendee,
    LTESpeaker,
    LTESite,
    LTEEvent,
    LTEStore,
    LTEUser,
    LTESalesOrder,
    LTETicket,
    } = require('mocha-fonteva-data-generator')

useDataGenerators();

import { ClassyDecorators } from 'mocha-fonteva-data-generator';
import { classyRegister } from 'mocha-fonteva-data-generator';

const { isTest, testMethod, inject, runBeforeEach, runBefore, runAfter, runAfterEach, } = ClassyDecorators;

const controller = require('aura/EventRegistrationFlowDetails/EventRegistrationFlowDetailsController');
const helper = require('aura/EventRegistrationFlowDetails/EventRegistrationFlowDetailsHelper');

@isTest
class EventRegistrationFlowDetailsController {
    @runBefore
    before() {
        global.$A = auraFactory({
            'e.c:EventCancelRegistrationEvent': eventFactory()
        });
        this.sandbox = sinon.sandbox.create();
    }
    @runAfter
    after() {
        global.$A = auraFactory()
    }

    @runBeforeEach
    beforeEach() {
        this.sandbox.stub(helper)
        global.document = {
            title: '',
            body: {
                classList: {
                    add: this.sandbox.spy()
                }
            }
        };
    }

    @runAfterEach
    afterEach() {
        this.sandbox.restore()
        delete global.document;
    }

    @testMethod('doInit should set ticket properties when isModal true')
    @inject(LTEEvent, LTEUser, LTETicket, LTESalesOrder, true, true)
    @testMethod('doInit should set ticket properties and isModal false')
    @inject(LTEEvent, LTEUser, LTETicket, LTESalesOrder, false, false)

    doInit(eventObj, usr, ticket, salesOrderObj, isModal, isPhone) {
        global.$A.set("$Browser.isPhone", isPhone);
        const component = componentFactory(
            {
                eventObj,
                usr,
                ticket,
                salesOrderObj,
                isModal : isModal

            });
        controller.doInit(component, {}, helper);
        expect(helper.setupFormDataAndBuildSOLEntries).to.have.been.calledWith(component);
        if (isModal) {
            expect(helper.openModal).to.have.been.calledWith(component);
        }
    }

    @testMethod('should call close modal')
    closeModal() {
        const component = componentFactory();
        controller.closeModal(component, {}, helper);
        expect(helper.deleteUnSavedChanges).to.have.been.calledWith(component);
    }

    @testMethod('should call addtickettype')
    @inject(LTEEvent)
    addNewAttendee(eventObj) {
        let button = componentFactory({}, 'Framework:Button');
        const component = componentFactory({
            eventObj,
            findMap : {
                cancelAttendeeSetup : button,
                continueAttendeeSetup : button
            }
        });
        controller.addNewAttendee(component, {}, helper);
        expect(helper.addTicketType).to.have.been.calledWith(component);
    }

    @testMethod('shwoCancelModal - yeah shwo')
    shwoCancelModal() {
        const component = componentFactory({
            findMap: {
                cancelAttendeeSetup: 'Framework:Button',
                cancelAddTickets: 'Framework:Button',
            }
        });

        component.find('cancelAddTickets').showModal = sinon.spy();

        controller.shwoCancelModal(component);

        expect(component.find('cancelAttendeeSetup').stopIndicator).to.have.been.called;
    }

    @testMethod('handleDisableButtonEvent')
    handleDisableButtonEvent() {
        const component = componentFactory({
            findMap: {
                cancelAttendeeSetup: 'Framework:Button',
                addAttendeeBtn: 'Framework:Button',
            }
        });
        controller.handleDisableButtonEvent(component, eventFactory(), helper);
        expect(component.find('cancelAttendeeSetup').set).to.have.been.calledWith('v.disable',  true);
        expect(component.find('addAttendeeBtn').set).to.have.been.calledWith('v.disable',  true);
    }
}

classyRegister(EventRegistrationFlowDetailsController);
JS

Example Controller File

Here is an annotated example of EventRegistrationFlowDetailsControllerSpec.js. Everything red-marked below will be discussed in the following sections.


Boilerplate: 

  • chai – An assertion library that can be paired with any javascript testing framework (http://www.chaijs.com/).
  • chai.expect – Assertion library (http://www.chaijs.com/api/bdd/).
  • sinon – Standalone test spies, stubs and mocks for JavaScript. This works with any unit testing framework.
  • sinonChai – Provides a set of custom assertions for using the Sinon.JS spy, stub, and mocking framework with the Chai assertion library.
  • lodashMakes JavaScript easier by simplifying working with arrays, numbers, objects, strings, etc. (https://lodash.com/).
  • mocha-aura – Allows testing of SalesForce components. Salesforce aura component controller and helper files do not export anything and cannot be directly required by nodejs. mocha-aura modifies the standard nodejs loader for Salesforce Aura Components and exports the containing object. In conjunction with istanbul, this allows you to export Salesforce Aura Controller and helper files before instrumenting it and have code coverage metrics.

Constants:

  • auraFactory Allows you to mock the $A global variable.
  • eventFactory – Allows you to generate the event object that Lightning provides.
  • componentFactory – Generates a mock of the Lightning Component object.

    • component – Generated using componentFactory (line 13 of EventRegistrationFlowDetailsControllerSpec.js).
    • event – Generated using eventFactory (line 12 of EventRegistrationFlowDetailsControllerSpec.js).
    • helper – Referenced on line 43 of EventRegistrationFlowDetailsControllerSpec.js.

      handleFieldChangeEvent : function(component, event, helper)
      JS
  • useComponentAdapters Loads Fonteva's adapters into memory.

  • apexCallFactory – Enables you to mock out calling the server.
  • apexSuccessResult – Returns a success result based on the outcome of apexCallFactory. Call mimics results to see if your code can handle it.
  • apexErrorResult – Returns an error result based on the outcome of apexCallFactory. Call mimics results to see if your code can handle it.

    For example, to return a success result, apexSuccessResult() (line 2 below) calls line 4 in the screenshot just below.
    To return an error result, replace apexSuccessResult() with apexErrorResult().

    helper.js

    const result = apexSuccessResult(goodPRData);
    const findAccounts = apexCallFactory(result);
    const component = componentFactory({
       findAccounts,
       value : {
          account : 'Test Account'
       },
       queryFilter : 'WHERE OwnerId = "123456789123456789"',
       storeObj : {
          accountSearchResultFields : 'Id, Name, OwnerId'
       },
    });
    helper.findAccounts(component);
    CODE

        

Controller and Helper Files

In the following code, you require the controller file and the helper file, and you provide the paths to these files (paths are relative to your project's root).

EventRegistrationFlowDetailsControllerSpec.js

const controller = require('aura/EventRegistrationFlowDetails/EventRegistrationFlowDetailsController');
const helper = require('aura/EventRegistrationFlowDetails/EventRegistrationFlowDetailsHelper');
CODE

       (From EventRegistrationFlowDetailsControllerSpec.js.)

  • isTest - (line 45) - Annotation that means this is a test class that’s going to run the specified number of tests.
  • runBefore - (line 47) - Runs before any of the tests are run.
  • runAfter - (line 54) - Runs after all of the tests are run.
  • runBeforeEach - (line 59) - Run before each test.   
  • runAfterEach - (line 72) - Run after each test.

See http://sinonjs.org/. Everything that can be done with sinon.js can be used in your Fonteva code.

 

Previous: Directory Structure and Files     |     Next: Stubbing Out