Sunday, October 13, 2019

Code Coverage Export Using Lightning Web Components(lwc)

This post explains how to export code coverage using the lightning web component

This component explores
1.  It will show the total Apex lines org.
2.  Shows how many line covered and uncovered lines 
3.  how much percentage covered in org
4.  shows the different color based on the percentage covered of apex class in the data table



CodeCoverageExportLWC.html
<template>
    <lightning-card title="Code Coverage Analysis Using Lightning Web Component" icon-name="standard:apex"><br/>
        <div class="slds-grid slds-gutters" style="margin-left: 3%;">
            <div class="slds-col">
                <span class="slds-badge"><b>Total Apex Lines In Org: <b style="color: steelblue">{orgTotalApexLines}</b></b></span>
            </div>
            <div class="slds-col">
                <span class="slds-badge"><b>Total Apex Lines Covered In Org: <b style="color: steelblue">{OrgTotalCoveredLines}</b></b></span>
            </div>
            <div class="slds-col">
                <span class="slds-badge"><b>Total Code Coverage In Org: <b style="color: steelblue">{OrgTotalCodeCoverage}%</b></b></span>
            </div>
        </div><br/>

        <template if:true={data}>
            <lightning-datatable columns={columns} 
                                 data={data} 
                                 key-field="id"
                                 hide-checkbox-column="true"
                                 show-row-number-column="true"></lightning-datatable>
        </template>

    </lightning-card>
</template>

CodeCoverageExportLWC.js
import { LightningElement, track } from 'lwc';

// Importing Aapex class
import codeCoverageAnalysis from '@salesforce/apex/CodeCoverageExportController.exportClassCoverage';

const columns = [
    {
        label: 'Name',
        fieldName: 'ApexClassOrTrigger',
        cellAttributes: { class: { fieldName: 'covrageLowCSSClass' }}
    },{
        label: 'Total Lines',
        fieldName: 'TotalLines'
    }, {
        label: 'NumLinesCovered',
        fieldName: 'CoveredLinesCount'
    }, {
        label: 'NumLinesUncovered',
        fieldName: 'UncoveredLinesCount'
    }, {
        label: 'Percentage Covered',
        fieldName: 'PercentageCovered',
        cellAttributes: { class: { fieldName: 'covrageLowCSSClass' }}
    }
];

export default class CodeCoverageExportLWC extends LightningElement {
    // reactive variables
    @track columns = columns;
    @track data = [];
    @track orgTotalApexLines;
    @track OrgTotalCodeCoverage;
    @track OrgTotalCoveredLines;

    connectedCallback() {
        this.codeAnalysis();
    }

    codeAnalysis() {
        codeCoverageAnalysis()
        .then(result => {
            let rows = result.records;
            let orgTotalLines = 0;
            let totalCoveredLines = 0;
            let totalCountLines = 0;

            rows.forEach((key) => {
                let row = key;
                
                // Total apex lines in org
                orgTotalLines = orgTotalLines + totalCountLines;
                
                // Total lines of current apex class
                totalCountLines = Number(row.NumLinesCovered) + Number(row.NumLinesUncovered);

                // Covered lines of Apex class
                totalCoveredLines = totalCoveredLines + Number(row.NumLinesCovered);

                // Percentage covered for current apex class
                row.PercentageCovered = Math.round(Number(row.NumLinesCovered) / totalCountLines * 100).toString() + '%';

                // Showing different color in column based on percentage covered
                if(Math.round(Number(row.NumLinesCovered) / totalCountLines * 100) < 75) {
                    row.covrageLowCSSClass = 'slds-text-color_error';
                }
                else {
                    row.covrageLowCSSClass = 'slds-text-color_success';
                }


                row.TotalLines = totalCountLines.toString();

                if(row.ApexClassOrTrigger) {
                    row.ApexClassOrTrigger = row.ApexClassOrTrigger.Name;
                }

                if(row) {
                    row.CoveredLinesCount = row.NumLinesCovered.toString();
                    row.UncoveredLinesCount = row.NumLinesUncovered.toString();
                }
            }); 

            this.OrgTotalCodeCoverage = Math.round(totalCoveredLines/orgTotalLines * 100);
            this.orgTotalApexLines = orgTotalLines;
            this.OrgTotalCoveredLines = totalCoveredLines;
            
            // assging data to datatable
            this.data = rows;
        })
        .catch(error => {
            this.error = error;
        });
    }


}

Apex Class
public inherited sharing class CodeCoverageExportController {

    @AuraEnabled
    public static CoverageWrap exportClassCoverage() {
        String strResourceQuery = null;
        String strResponse = null;
        CoverageWrap cw;

        strResourceQuery = 'tooling/query/?q=Select+id,ApexClassorTrigger.Name,NumLinesCovered,NumLinesUncovered+from+ApexCodeCoverageAggregate';

        if(strResourceQuery != null) {
            // OrgREST Custom Class returns the response as string
            strResponse = OrgREST.retriveResult(strResourceQuery, 'GET', null);
        }

        if(strResponse != null) {
            cw = new CoverageWrap();
            cw  = (CoverageWrap)JSON.deserialize(strResponse, CoverageWrap.class);
        }

        return cw;
 
    }


    public class Attributes {
       @AuraEnabled public String type;
       @AuraEnabled public String url;
    }

    public class Records {
       @AuraEnabled public Attributes attributes;
       @AuraEnabled public String Id;
       @AuraEnabled public ApexClassOrTrigger ApexClassOrTrigger;
       @AuraEnabled public Integer NumLinesCovered;
       @AuraEnabled public Integer NumLinesUncovered;
    }

    public class ApexClassOrTrigger {
       @AuraEnabled public Attributes attributes;
       @AuraEnabled public String Name;
    }
    
    public class CoverageWrap{
      @AuraEnabled public Integer size;
      @AuraEnabled public Integer totalSize;
      @AuraEnabled public Boolean done;
      @AuraEnabled public Object queryLocator;
      @AuraEnabled public String entityTypeName;
      @AuraEnabled  public List<Records> records;
    }
}

OrgRest Apex Class
/*
purpose: This Class gives response based on resource name you entered.
Author: Shaik Nagajani
Date: 15/June/2019
*/

public inherited sharing class OrgREST {
    
    public static String retriveResult(String strResourceName, String strMethod, String strBody) {
        String response;
         String strEndPonitURL = URL.getSalesforceBaseUrl().toExternalForm() + '/services/data/v46.0/' + strResourceName;
        if(String.isNotBlank(strResourceName)) {
            HttpRequest httpRequest = new HttpRequest();  
            httpRequest.setEndpoint(strEndPonitURL);

            httpRequest.setMethod(strMethod);   
            httpRequest.setHeader('Authorization', 'OAuth ' + getSessionId());        
            httpRequest.setHeader('Authorization', 'Bearer ' + getSessionId()); 
            
            if(String.isNotBlank(strBody)) {
                httpRequest.setBody(strBody);
                httpRequest.setHeader('Content-Type', 'application/json');
             httpRequest.setHeader('Accept', 'application/json');
            }
            
            try {  
                Http http = new Http();   
                HttpResponse httpResponse = http.send(httpRequest);  

                while(httpResponse.getStatusCode() == 302) {
                    httpRequest.setEndpoint(httpResponse.getHeader('Location'));
                    httpResponse = new Http().send(httpRequest);
                }

                if (httpResponse.getStatusCode() == 200 ) {  
                    response = httpResponse.getBody();  
                } 
                else {  
                    throw new CalloutException(httpResponse.getBody());  
                }   
            } 
            catch(Exception ex) {  
                throw ex;  
            }  
        } 
        return response;
    }

    public static String getSessionId() {
        return Page.GenerateSessionIdForLWC.getContent().toString();
    }
}

You can also find the code on Github
GitHub Link


Output

1 comment:

  1. can you please share how we can create VF for this

    ReplyDelete