Friday, 15 December 2017

Santa Force is Coming to Town

Santa Force is Coming to Town

Santa

Introduction

This post comes all the way from Lapland, from the workshop of Santa Force. a long time Salesforce user. This Salesforce instance has received some enhancements to help with the unique problems of this unique non-profit, which we’ll take a closer look at.

Customisations

There are a few additional fields on user, which don’t necessarily make a lot of sense when viewed in isolation:

Screen Shot 2017 12 15 at 09 59 47

However, they are vital for a formula field :

 

Screen Shot 2017 12 15 at 10 01 33

 

So as you can see, you’d better watch out, not pout and not cry. This might seem an odd requirement, but the help text tells you why:

 

Screen Shot 2017 12 15 at 10 02 06

 

 

On to Santa Force now, he’s making a list view. The elves created one a few days ago, but there’s some issues with it - the name doesn’t look right and there’s a few fields missing.

 

Screen Shot 2017 12 15 at 10 37 42

 

Santa Force clones the list view, renames it and adds the required fields:

 

Screen Shot 2017 12 15 at 10 36 55

 

This is much better - he’s making a list view, he’s checked it twice and can now see who has been naughty or nice.

 

There’s also a process builder that works off another custom field on the contact record - a checkbox field labelled Asleep?

 

Screen Shot 2017 12 15 at 10 41 39

 

So Santa Force knows if you are sleeping, and knows if you are awake, because it is posted to his chatter feed:

Screen Shot 2017 12 15 at 10 17 35

 

 

Finally, there’s one more field on contact - Goodness.  Santa Force can look at this field and determine if you’ve been good or bad - there’s also some useful help text that will guide a contact’s behaviour if they view this though the community.

 

Screen Shot 2017 12 15 at 10 19 03

 

Why?

The key question is what is all this information being gathered for? Checking the calendar, we can see that it’s for an event scheduled for 24th December:

 

Screen Shot 2017 12 15 at 10 22 16

 

As you can see, Santa Force is Coming to Town!

Happy Christmas everyone and thanks for reading the Bob Buzzard Blog.

 

Tuesday, 12 December 2017

SFDX and the Metadata API

SFDX and the Metadata API

Introduction

SFDX became Generally Available in the Winter 18 Release of Salesforce and I was ready for it. However, my use case was our BrightMedia appcelerator which is mostly targeted at sandboxes and production orgs, where scratch orgs wouldn’t really help that much. The good news is that the SFDX CLI has support for metadata deploy/retrieve operations via the mdapi commands in the force topic.

What you need

In order to deploy metadata you need the directory structure and package.xml manifest - if you’ve used the Force.com migration tool (ant) or the Force CLI, this should be familiar. For the purposes of this blog I’m using the GITHUB repository from my Take a Moment blog post, which has the following structure:

src/
src/package.xml
src/aura/
src/aura/TakeAMoment
src/aura/TakeAMoment/TakeAMoment.cmp
src/aura/TakeAMoment/TakeAMoment.cmp-meta.xml
src/aura/TakeAMoment/TakeAMoment.css
src/aura/TakeAMoment/TakeAMomentController.js
src/aura/TakeAMoment/TakeAMomentHelper.js
src/aura/TakeAMoment/TakeAMomentRenderer.js

What you do

The first thing I do is clone the repo to my local filesystem and navigate to the directory created:

 > git clone https://github.com/keirbowden/TakeAMoment.git
Cloning into 'TakeAMoment'...
remote: Counting objects: 20, done.
remote: Total 20 (delta 0), reused 0 (delta 0), pack-reused 20
Unpacking objects: 100% (20/20), done.
> cd TakeAMoment

I then set this up as an SFDX project:

> sfdx force:project:create -n .
create sfdx-project.json
conflict README.md
force README.md
create config/project-scratch-def.json

Next I login to one of my dev orgs:

> sfdx force:auth:web:login
Successfully authorized keirbowden@googlemail.com with org ID …..
You may now close the browser

(For the purposes of this blog my login is ‘keirbowden@googlemail.com’ - substitute your username in the commands below)

Everything is now set up and I can deploy to my dev org:

> sfdx force:mdapi:deploy -d src -u keirbowden@googlemail.com
2884 bytes written to /var/folders/tn/q5mzq6n53blbszymdmtqkflc0000gs/T/src.zip using 36.913msDeploying /var/folders/tn/q5mzq6n53blbszymdmtqkflc0000gs/T/src.zip...
=== StatusStatus:  Queuedjobid:  0Af80000003ynf6CAA
The deploy request did not complete within the specified wait time [0 minutes].To check the status of this deployment, run "sfdx force:mdapi:deploy:report"

Sometimes the deployment completes immediately, but most of the time it takes a bit longer and I have to query the status via the command that the SFDX CLI helpfully gives me in the output:

> sfdx force:mdapi:deploy:report
=== Result
Status: Succeeded
jobid: 0Af80000003ynf6CAA
Completed: 2017-12-12T16:28:39.000Z
Component errors: 0
Components checked: 1
Components total: 1
Tests errors: 0
Tests completed: 0
Tests total: 0
Check only: true

And that’s it - my deployment is done!

Why would you do this?

That’s a really good question. For me, the following reasons are good enough:

  1. The SFDX CLI, unlike the Force Migration Tool, uses oauth to authorise operations, so I don’t need to specify the password in plaintext. It also means that the rest of my team don’t need to learn ANT.
  2. The SFDX CLI, unlike the Force CLI, allows me to fire the deployment off and query the status later, plus it gives me a lot of information in the report.

It’s also clear to me that SFDX is the future, so aligning myself with the SFDX CLI seems a sensible move.

It also allows me to get the status of the deployment as JSON:

> sfdx force:mdapi:deploy:report --json

which gives me a ton of information:

{
  "status": 0,
  "result": {
    "checkOnly": false,
    "completedDate": "2017-12-12T16:28:39.000Z",
    "createdBy": "00580000001ju2C",
    "createdByName": "Keir Bowden",
    "createdDate": "2017-12-12T16:28:09.000Z",
    "details": {
      "componentSuccesses": [
        {
          "changed": "true",
          "componentType": "AuraDefinitionBundle",
          "created": "true",
          "createdDate": "2017-12-12T16:28:36.000Z",
          "deleted": "false",
          "fileName": "src\/aura\/TakeAMoment",
          "fullName": "TakeAMoment",
          "id": "0Ab80000000PEGWCA4",
          "success": "true"
        },
        {
          "changed": "true",
          "componentType": "",
          "created": "false",
          "createdDate": "2017-12-12T16:28:38.000Z",
          "deleted": "false",
          "fileName": "src\/package.xml",
          "fullName": "package.xml",
          "success": "true"
        }
      ],
      "runTestResult": {
        "numFailures": "0",
        "numTestsRun": "0",
        "totalTime": "0.0"
      }
    },
    "done": true,
    "id": "0Af80000003ynf6CAA",
    "ignoreWarnings": false,
    "lastModifiedDate": "2017-12-12T16:28:39.000Z",
    "numberComponentErrors": 0,
    "numberComponentsDeployed": 1,
    "numberComponentsTotal": 1,
    "numberTestErrors": 0,
    "numberTestsCompleted": 0,
    "numberTestsTotal": 0,
    "rollbackOnError": true,
    "runTestsEnabled": "false",
    "startDate": "2017-12-12T16:28:29.000Z",
    "status": "Succeeded",
    "success": true
  }
}

Having the results in JSON also means that I can easily process it in JavaScript, which I’ll cover in my next post.

Related Posts

 

Monday, 20 November 2017

Animated Lightning Progress Bar

Animated Lightning Progress Bar

Introduction

The Salesforce Lightning Design System has a progress bar component, which can be used to communicate how far through a process the user is, or how close to achieving their target audience they are in BrightMedia. Typically this will be wired up to an attribute so that it updates automatically when the attribute value changes, for example:

<aura:application extends="force:slds" >
    <aura:attribute name="value" type="Integer" default="25" />
    <div class="slds-m-around_small">
        <div class="slds-text-heading_large slds-m-bottom_small">Progress Bar Demo</div>
        <div class="slds-m-bottom_large">
            <label>Enter value : <ui:inputNumber value="{!v.value}"/></label>
        </div>
        <div>
            <div style="width:25%" class="slds-progress-bar slds-progress-bar_circular slds-progress-bar_large"
                    aria-valuemin="0" aria-valuemax="100" aria-valuenow="{!v.value}" role="progressbar">
                <span class="slds-progress-bar__value" style="{! 'width:  ' + v.value + '%;'}">
                    <span class="slds-assistive-text">{!'Progress: ' + v.value + '%'}</span>
                </span>
                <div class="slds-text-align--center"><ui:outputNumber value="{!v.value}"/>
                   /
                <ui:outputNumber value="100"/></div>
            </div>
        </div>
    </div>
</aura:application>

which jumps the progress bar to the specified value:

while this works fine , it's not the greatest user experience. When a progress bar updates I prefer to see an animated version where it gradually makes it's way to the final value. There's no difference functionality-wise, but it just looks better to me.

The Animator

Animating a progress bar in JavaScript is simply a matter of making small changes to move between the current and desired value, typically via a timer that fires a function every ’n’ milliseconds to advance the value by a small amount. When using Lightning Components this is a little more tricky as the function executed by the timer is modifying the component outside of the framework lifecycle. In the revised app, when the user changes the desired value this is stored in a separate attribute and a controller function is executed:

<aura:application extends="force:slds" >
    <aura:attribute name="value" type="Integer" default="25" />
    <aura:attribute name="inputVal" type="Integer" default="25" />
    <aura:attribute name="timeoutRef" type="object" />
    <div class="slds-m-around_small">
        <div class="slds-text-heading_large slds-m-bottom_small">Progress Bar Demo</div>
        <div class="slds-m-bottom_large">
            <label>Enter value : <ui:inputNumber value="{!v.inputVal}" change="{!c.valueChanged}" /></label>
        </div>
        <div>
            <div style="width:25%" class="slds-progress-bar slds-progress-bar_circular slds-progress-bar_large"
                 aria-valuemin="0" aria-valuemax="100" aria-valuenow="{!v.value}" role="progressbar">
                <span class="slds-progress-bar__value" style="{! 'width:  ' + v.value + '%;'}">
                    <span class="slds-assistive-text">{!'Progress: ' + v.value + '%'}</span>
                </span>
                <div class="slds-text-align--center"><ui:outputNumber value="{!v.value}"
                    /
                >/<ui:outputNumber value="100"/></div>
            </div>
        </div>
    </div>
</aura:application>

following best practice, the controller method simply delegates to the associated helper

({
	valueChanged : function(component, event, helper) {
        helper.valueChanged(component, event);
	}
})

which does the actual work :

({
    valueChanged : function(cmp, ev) {
        var times=0;
        var current=cmp.get('v.value');
        var final=cmp.get('v.inputVal');
        var increment=1;
        if (final<current) {
            increment=-1;
        }
        var self=this;
        var timeoutRef = window.setInterval($A.getCallback(function() {
            if (cmp.isValid()) {
                var value=cmp.get('v.value');
                value+=increment;
                if (value==final) {
                    window.clearInterval(cmp.get('v.timeoutRef'));
                    cmp.set('v.timeoutRef', null);
                }
                cmp.set('v.value', value);
            }
        }), 100);
        cmp.set('v.timeoutRef', timeoutRef);
    }
})

the first part of the helper function simply captures the start and end values and figures out if we need to increment or decrement from the current value.  Next the timer is set up to repeat every 100 milliseconds. As the function executed by the timer changes the app component attributes, I have to wrap it in a $A.getCallback function call, which ensures that the lightning components framework rerenders the markup. Once the current value equals the desired final value, the timer is cleared otherwise it will fire forever more.

Change values with care

Refreshing the app now animates the progress bar to apply the changed value. Incrementing by 1 is probably overkill, especially if you are dealing with values of hundreds of thousands for example. In this situation I’d simply decide how many “jumps” I wanted to apply to the progress bar, divide the difference between the current and desired value by the number of jumps and then add the result to the value each time the timer fired.

Related posts

 

Saturday, 21 October 2017

Programming against Apex Interfaces

Programming against Apex Interfaces

Interfaces

Introduction

In the dim and distant past (getting on for 10 years ago now), when I was a Java programmer working in the systems integration and financial services space, the majority of the programming that I did was against interfaces rather than concrete class instances. This isn’t something that I see very often in the Salesforce world (at least on the SI side - I’d imagine ISVs go in for it a lot more) for a variety of reasons including:

  • Lots of Salesforce work is a point solution on an existing implementation, so it doesn’t always merit abstraction via interfaces.
  • Customers don’t want to the extra cost and accept that additional development effort may be required in future.
  • Many people have ended up as Salesforce developers through non-traditional routes and haven’t been exposed to interfaces.

Simply put, programming against interfaces means that rather than identifying the specific class that carries out an operation in your code, you identify an interface and rely on there being a class that implements that interface at run time. This allows your code to focus on what the operation needs to do rather than how it is accomplished.

Why?

Programming against interfaces introduces flexibility. You can change the implementation of the interface without affecting any of the code that uses the interface. Thus you could start out with a faker implementation that simply returns canned data to allow you to develop the real world interface implementation and the consuming code in parallel. It also means you can have multiple implementations of an interface inside a single system and swap them around via configuration.

Code me. Now.

The scenario for the example is calculating the discount due to an account. The customer has told us that at the moment it is a flat 10%, but this is an area that they will want to change in the future to take into account the account’s industry. This is a classic use case for an interface as we know that we will need to swap out the implementation in the future, and if it changes once it is like to change again in the future.

Concrete classes

My initial implementation of the class to calculate the discount is as follows:

public class SimpleAccountDiscount {
	public double getDiscount(Id accountId) {
        return 10;
    }
}

and I can use this directly in code:

double discount=new SimpleAccountDiscount().getDiscount('00124000004N1TfAAK');
System.debug('Discount = ' + discount);

producing the following output:

07:02:45:045 USER_DEBUG [2]|DEBUG|Discount = 10.0

all well and good. Next I create the more complex version which takes the industry into account - yes, complex is probably over-egging it a bit, but all things are relative.

public class ComplexAccountDiscount {
    public double getDiscount(Id accountId) {
        // default value
        Double discount=10;
        Account acc=[select id, Industry
                     from Account
                     where id=:accountId];

        if (acc.Industry=='Apparel’) {
            discount=15;
        }
        else if (acc.Industry=='Consulting’) {
            discount=5;
        }
        
        return discount;
    }
}

And I can use this in code just as easily :

System.debug('Discount for Burlington (apparel) = '
             + new ComplexAccountDiscount().getDiscount('00124000004RIGFAA4'));

System.debug('Discount for Dickenson (consulting) = '
             + new ComplexAccountDiscount().getDiscount('00124000004RIGHAA4'));

produces the output:

17:14:25:078 USER_DEBUG [1]|DEBUG|Discount for Burlington (apparel) = 15.0
17:14:25:081 USER_DEBUG [4]|DEBUG|Discount for Dickenson (consulting) = 5.0

However, when the customer is ready to move to the more complex version, I need to carry out a deployment in order to start using my new class. Plus if it turned out that a downstream system wasn’t ready for the change, I’d have to carry out another deployment to revert to the simple version. 

Implementing an interface

The following Apex interface reflects the method that must be exposed by any discount implementation:

public interface AccountDiscountInterface
{
    double GetDiscount(Id accountId);
}

Note that I don’t specify an access modifier for the method - as an interface reflects the public interface, the methods in the interface are implicitly public. I then modify may classes (only the complex version shown):

public class ComplexAccountDiscount implements AccountDiscountInterface {

I can then use the interface in place of a concrete class:

AccountDiscountInterface adi=new ComplexAccountDiscount();
System.debug('Discount for Burlington (apparel) = '
             + adi.GetDiscount('00124000004RIGFAA4'));

So my code that gets the discount doesn’t care about the implementation, but the previous line that instantiates the concrete class does. A partial success at most.

Dynamically instantiating a class

The final piece of the puzzle is dynamically instantiating a class based on configuration. If I can do this, my customer can switch implementations simply by changing a custom setting. Dynamical instantiation consists of two parts. First Type.forName() is used to get the type of the Apex class. Then the newInstance() method of the resulting Type is executed to create an instance of the named class. I’ve placed the name of the implementing class into an instance of the Account_Discount_Setting__c custom setting named ‘Default’. This has a field called Implementing_Class__c that I’ve set to ‘ComplexAccountDiscount’:

Account_Discount_Setting__c setting=
    Account_Discount_Setting__c.getInstance('Default');

Type impl = Type.forName(setting.Implementing_Class__c);
AccountDiscountInterface adi=
    (AccountDiscountInterface) impl.newInstance();

System.debug('Discount for Burlington (apparel) = '
             + adi.GetDiscount('00124000004RIGFAA4'));

Now my code has no knowledge of the class that is implementing the discount calculation, it simply creates whatever class has been configured and uses that. If my customer wants to switch the implementation, it’s as simple as changing a field in a custom setting. It also allows me to set up a different version for testing - unit tests should be as simple as possible so if I have a genuinely complex discount implementation I probably don’t want to use that when testing the consuming code in case it has a side effect that my test isn’t expecting - I’d still test that implementation, but in it’s own unit tests.

So these should be used always?

As I mentioned earlier, you don’t always need an interface. They do add a little overhead, as I now have an additional custom setting and interface to create and deploy. If the implementation is never likely to change then there’s no point in abstracting it away like this. We use them a lot in BrightMedia as it allows us to have a selection of implementations for services that customers can chose between. 

 

 

 

 

Saturday, 23 September 2017

Taking a Moment with a Lightning Component

Taking a Moment with a Lightning Component

Metime

Looking after yourself

A huge amount of effort nowadays goes in to making people more productive. New AI features on the Salesforce platform figure out the key leads and deals so that reps focus on the right tasks, more information is pushed to employees to help their decision making, and barely 10 minutes goes by without a new listicle being published on Medium promising to teach you life hacks so that you can spend more of your time carrying out that sweet, sweet work. It’s easy to forget that you are more productive if you look after yourself while working. Sitting down staring at a screen for hours on end can cause you real damage, but as it’s not an instant, acute pain more often than not you don’t realise it. This is where the systems that you work with should be helping you, rather than trying to squeeze more out of you.

TakeAMoment (Lighting Component)

When you create a Lightning application in Salesforce, you can add a utility bar. Lightning components in this utility bar have special powers - they persist across the various tabs and pages of the application, unaffected by your navigation. Using the “Load in background when the app opens” setting means that the component can know the point at which you started the application and can make some helpful suggestions after specific periods of time have elapsed.

The TakeAMoment lightning component starts a JavaScript interval timer upon initialisation that fires every minute. When the timer fires, the component then figures out how long it’s been since the user started the application and, if it is a specific amount of time, suggests that the user take a moment to do something to benefit themselves. For example, after 20 minutes the user is advised to blink 10 times to ensure they don’t dry their eyes out. Here’s a video of the component doing it’s thing - note that in time honoured fashion for anything tech related, the sequence has been shortened:

(The ‘Come with me if you want to live’ initial message is because I had The Terminator on in the background while I wrote the component - I thnk it grabs the user’s attention!).

Note that I’m using the variant of the toast message that allows me to include a clickable URL - each suggested activity contains a link to a page on the web explaining why it’s a good idea - at the end of the video I click through to the story in the New York Times that explains why getting up and walking around for 5 minutes every hour is a good thing.

The you can view the component at the github repo shown below - it’s pretty basic at the moment, the timings and messages are all hardcoded, but this has the advantage that there’s no interaction with the server - everything takes place on the client. If you want to use it, create or edit a lightning app (I just added the utility bar component to the standard Sales app) and add the TakeAMoment component to the utility bar. Make sure to check the ‘Load in the background when the app opens’, otherwise the user has to click the component in the utility bar to initialise it.

Obviously as well as looking after my users, this component can also be used for nefarious purposes, typically by my evil co-worker - regular messages telling users they aren’t working hard enough, that winners work through lunch, family dinners are overrated etc. Sadly this is the nature of most if not all technology.

Related Posts

 

Sunday, 20 August 2017

Lightning Testing Service Part 2 - Custom Jasmine Reporter

Lightning Testing Service Part 2 - Custom Jasmine Reporter

Customjasmine

(Note: This blog applies to the Jasmine variant of the Lightning Testing Service)

Introduction

One of the cool things about Jasmine is how easy it is to add your own reporter. Compared to some of the other JavaScript testing frameworks I’ve used in the past, it’s entirely straightforward. Essentially you are implementing an interface, although as JavaScript doesn’t have interfaces it’s very much based on what you should rather than must implement. A Jasmine Reporter is a JavaScript object with the appropriate functions for the framework to call when something interesting happens. Even cooler is the fact that the framework first checks that you have provided the function before it is invoked, so if you don’t care about specific events, you just leave out the functions to handle those events and you are all good.

Functions

Some or all of the following functions are required to handle the various events that occur as test are executed - basically things commencing and completing:

  • jasmineStarted/jasmineDone - called before any specs (tests) execute/once all tests have completed
  • suiteStarted/suiteDone - called before a specific suite (group of tests) execute/once they have completed
  • specStarted/specDone - called before a specific test executes/once it has completed

Once you have your object with the desired functions, it must be registered before any tests are queued:

jasmine.getEnv().addReporter(your_reporter);

and that’s all there is to it.

Example

Below is an example lightning component that creates a simple reporter to capture the success/failure of each suite/spec and log information to the console. Note that this relies on the Jasmine artefacts installed by the latest version of the Lightning Testing Service unmanaged package. The component is named KABJasmineReporter:

Component

<aura:component extensible="true">
    <ltng:require scripts="{!join(',',
				$Resource.lts_jasmine + '/lib/jasmine-2.6.1/jasmine.js',
				$Resource.lts_jasmine + '/lib/jasmine-2.6.1/jasmine-html.js',
				$Resource.lts_jasmineboot
				)}"
                  afterScriptsLoaded="{!c.doInit}" />
</aura:component>

Controller

({
	doInit : function(component, event, helper) {
		helper.initialiseJasmineReporter(component, event);
	}
})

Helper

({
    myReporter : {
        content : '',
        suites : [],
        totalSuccesses:0,
        totalFailures:0,
        totalTests:0,
        output : function(message) {
            console.log(message);
            this.content+=message;
        },
        clear: function() {
            this.content='';
            this.suites=[];
            this.totalSuccesses=0;
            this.totalFailures=0;
            this.totalTests=0;
        },
        getCurrentSuite: function() {
            return this.suites[this.suites.length-1];
        },
        getCurrentSpec : function() {
            return this.getCurrentSuite().specs[this.getCurrentSuite().specs.length - 1];
        },
        jasmineStarted: function(suiteInfo) {
            this.output('Running suite with ' + suiteInfo.totalSpecsDefined + ' specs');
        },
        suiteStarted: function(result) {
            this.output('Suite started: ' + result.description + ' whose full description is: ' + result.fullName);
            this.suites.push({name : result.fullName,
                              specs : []});
        },
        specStarted: function(result) {
            this.output('Spec started: ' + result.description + ' whose full description is: ' + result.fullName);
            this.getCurrentSuite().specs.push({name: result.description,
                                               failures: [],
                                               failureCount: 0,
                                               successes: 0});
        },
        specDone: function(result) {
            this.output('Spec: ' + result.description + ' complete status was ' + result.status);
            this.output(result.failedExpectations.length + ' failures');
            for(var i = 0; i < result.failedExpectations.length; i++) {
                var failure=result.failedExpectations[i];
                this.output('Failure: ' + failure.message);
                this.output(failure.stack);
                this.getCurrentSpec().failures.push({message: failure.message,
                                                     stack : failure.stack});
                this.getCurrentSpec().failureCount++;
                this.totalFailures++;
            }
            this.output(result.passedExpectations.length + ' successes');
            this.getCurrentSpec().successes+=result.passedExpectations.length;
            this.totalSuccesses+=result.passedExpectations.length;
        },
        suiteDone: function(result) {
            this.output('Suite: ' + result.description + ' was ' + result.status);
            for(var i = 0; i < result.failedExpectations.length; i++) {
                this.output('AfterAll ' + result.failedExpectations[i].message);
                this.output(result.failedExpectations[i].stack);
            }
        },
        jasmineDone: function() {
            this.totalTests=this.totalSuccesses+this.totalFailures;
	        this.output('Finished tests');
    	    this.output('Successes : ' + this.totalSuccesses);
	        this.output('Failures : ' + this.totalFailures);
	        this.output('Details : ' + JSON.stringify(this.suites, null, 4));
        }
    },
    initialiseJasmineReporter : function(component, event) {
        console.log('Initialising jasmine reporter');
        var self=this;
        this.myReporter.clear();
        var env = jasmine.getEnv();
        jasmine.getEnv().addReporter(this.myReporter);
    }
})

A couple of tweaks to the jasmineTests app to include my reporter (and to limit to a couple of tests, otherwise there’s a lot of information in the console log):

App

<aura:application >
    <c:KAB_JasmineReporter />
    <c:lts_jasmineRunner testFiles="{!join(',',
    	$Resource.jasmineHelloWorldTests
    )}" />
</aura:application>

Executing the app produces the following console output:

Initialising jasmine reporter
Running suite with 2 specs
Suite started: A simple passing test whose full description is: A simple passing test
Spec started: verifies that true is always true whose full description is: A simple passing test verifies that true is always true
Spec: verifies that true is always true complete status was passed
0 failures
1 successes
Suite: A simple passing test was finished
Suite started: A simple failing test whose full description is: A simple failing test
Spec started: fails when false does not equal true whose full description is: A simple failing test fails when false does not equal true
Spec: fails when false does not equal true complete status was pending
0 failures
0 successes
Suite: A simple failing test was finished
Finished tests
Successes : 1
Failures : 0
Details : [
    {
        "name": "A simple passing test",
        "specs": [
            {
                "name": "verifies that true is always true",
                "failures": [],
                "failureCount": 0,
                "successes": 1
            }
        ]
    },
    {
        "name": "A simple failing test",
        "specs": [
            {
                "name": "fails when false does not equal true",
                "failures": [],
                "failureCount": 0,
                "successes": 0
            }
        ]
    }
]

Conclusion

While this has been a simple example, there’s a lot more that can be done with custom reporters, such as posting notifications with the tests results, which I plan to explore in later posts. 

Related Posts

 

Saturday, 5 August 2017

Lightning experience utility bar - add an app for that

Lightning experience utility bar - add an app for that

Introduction

This week I’ve been working on adding utility bar functionality to our BrightMedia appcelerator. Typically when I build functionality of this nature I’ll start off with with the component markup using hardcoded values to get the basic styling right, then make it dynamic with the data coming from the JavaScript controller/helper, before finally wiring it up to an Apex controller that extract data from the Salesforce database, either through sobjects or custom settings.

For the purposes of this blog I’m going to say that it was presenting a list of Trailmixes, the new feature in Trailhead (it wasn’t, but this is a much simpler example and taps into the zeitgeist).

First incarnation

The version of the component that simply displayed a Trailmix with a button to open it:

<aura:component implements="flexipage:availableForAllPageTypes"
                access="global">
    
    <div class="slds-p-around--x-small slds-border--bottom slds-theme--shade">
        <div class="slds-grid slds-grid--align-spread slds-grid--vertical-align-center">
            <div>
                Blog Trailmix
            </div>
            <div>
            </div>
            <div>
                <lightning:buttonIcon iconName="utility:open"
                                      title="Open"
                                      alternativeText="Open" variant="border-filled"/>
            </div>
        </div>
    </div>
</aura:component>

and, not surprisingly, this worked fine:

Screen Shot 2017 08 05 at 16 54 45

Second incarnation

The second version initialised a list of Trailmixes, still containing a single element, in the JavaScript controller, which the component then iterated. First the component:

<aura:component implements="flexipage:availableForAllPageTypes"
                access="global">
    
    <aura:attribute name="mixes" type="Object[]" />
    
    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
    <aura:iteration items="{!v.mixes}" var="mix">
        <div class="slds-p-around--x-small slds-border--bottom slds-theme--shade">
            <div class="slds-grid slds-grid--align-spread slds-grid--vertical-align-center">
                <div>
                    {!mix.name}
                </div>
                <div>
                </div>
                <div data-record="{!mix.key}">
                    <lightning:buttonIcon onclick="{!c.OpenMix}"
                                          iconName="utility:open"
                                          title="Open"
                                          alternativeText="Open" variant="border-filled"/>
                </div>
            </div>
        </div>
    </aura:iteration>
</aura:component>

Next, the controller

({
	doInit : function(component, event, helper) {
        var mixes[];
        mixes.push({key:"BLOG",
                    name:"Blog Trailmix",
                    link:"https://trailhead.salesforce.com/users/0055000000617DXAAY/trailmixes/basics"});
		component.set('v.mixes', mixes);
    }
})

Here things started to go awry - clicking the utility bar item to open it did nothing and a few seconds later a toast message would appear, with a message along the lines of “we’re still working on your request”, but nothing further. Changing the component in the utility bar configuration to initialise in the background got a little further, but still no content, instead a perpetual spinner:

Screen Shot 2017 08 05 at 17 03 23

 

Viewing the JavaScript console showed nothing out of the ordinary. I’d been having a few problems with my internet connection soI assumed it was either that or Salesforce having an issue, and as it was fairly late at night I decided to leave it until the morning to see if things were resolved. No such luck.

Wrap it and app it

I then did what I usually do when I’m having issues with a lightning component - create an app with just the component in and see what happens then. The app couldn’t be simpler:

<aura:application >
    <c:Trailmixes />
</aura:application>

Previewing this showed that there was an error that was somehow being swallowed:

Screen Shot 2017 08 05 at 17 08 10

which I’m sure the eagle-eyed reader has spotted - the declaration of my mixes variable that eventually gets stored as an attribute was missing an ‘=‘ character:

var mixes[];

After correcting this and refreshing the page a couple of times, and I was back on track with my Trailmix component. 

In conclusion

Always try any troublesome component in an app of it’s own - while in most cases you won’t have the utility bar swallowing errors, it’s way easier to debug a component when there aren’t 50 others on the same page firing events and changing attributes. Also, sometimes a syntax type error was shown in the JavaScript console and sometimes not, so look there first.

Related posts