You are using an older browser that might negatively affect how this site is displayed. Please update to a modern browser to have a better experience. Sorry for the inconvenience!

Cinnamon Automation Framework for Salesforce


By: Santhosh

Cinnamon is an automation framework designed for Salesforce. It provides a set of functionalities that allow you to build and run Selenium tests for GUI-based integration, end-to-end, and browser-compatibility testing. It can be installed as a managed package inside our Salesforce org. Cinnamon is available for two environments in Salesforce: Developer Edition Org and Sandbox Org.

Cinnamon installation, setup and Sauce Lab configurations steps can be read from the URL below.

https://github.com/forcedotcom/cinnamon/blob/master/README.md

Cinnamon is used to test custom VF UI pages in salesforce. We can write test scripts and execute them inside our Salesforce org. Also, we can run our tests in Sauce lab/ Browser stack/ AWS Servers to test our Salesforce custom pages against different OS/browser combinations without having any hardware setup. When running our test in Sauce Labs, it provides video as well as screenshots inside our Salesforce Org.

After the installation of Cinnamon, it will be added as a new app into our Salesforce Org. It has some default Objects called Home, Test Console, Test Suites, Tests, and Settings. Below are the screenshots of the Cinnamon App and its default Objects respectively.

Image

Cinnamon App in Salesforce 

Image

Default Objects in Cinnamon  

Components of Cinnamon Tests: 

There are three main components in Cinnamon test:

  1. Page Object Class
  2. Base Test Class
  3. Visualforce page

Page Object Class: 

Page Object is a class which includes a set of methods that can be used to access and manipulate UI components in our VF page. It is used to specify the location of UI elements. Also, it separates test code from page specific code to improve the maintainability and readability of the test code. Every UI page should have one associated Page Object Class, and it can be reused for multiple tests in our Salesforce Org. The cinnamon framework provides some built-in support for Page Object pattern that helps to write robust test code.

Base Test Class: 

Base Test is a class is used to drive the Page Object Class. All the method calls are handled in the Base Test Class. Without Base Test Class, we can’t run our tests in Cinnamon. While we are running our test in the Cinnamon framework, it manages the entire test life cycle and executes the setup, test, and teardown methods during test execution.

All the Base Test Classes will be displayed in Test Console Object

Image

Base Test Classes in Cinnamon 

Visualforce Pages:

Cinnamon Visual Force page is a custom UI page used to interact with Objects in Salesforce. The Page object will interact with the web elements only through Visualforce pages. Visualforce Locators are used to locate an HTML element generated by a Visualforce tag. It should be passed to the Page Object by getElement() method as an argument to obtain a WebElement object. Like any other automation tool, Cinnamon could not interact with the web page directly, but it will interact with the web elements only through Visualforce page.

Sample Program: 

Visual Force Page: 

This sample Visual force page is used to create a single record in Account Object with fields like

<apex:page controller="testcustomController"> 

    <apex:form > 

    <apex:pageBlock title="Enter Account Detail"> 

        <apex:pageMessages /> 

        <apex:pageBlockButtons >             

             <apex:commandButton id="saveAct" value="Save" action="{!save}"/>                       

        </apex:pageBlockButtons> 

        <apex:pageBlockSection > 

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="Inputtext" for="name"/> 

                <apex:inputText id="Inputtext" value="{!newvalue}"/> 

            </apex:pageBlockSectionItem> 

      

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="Name" for="name"/> 

                <apex:inputField id="name" value="{!act.name}"/> 

            </apex:pageBlockSectionItem> 

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="Phone" /> 

                <apex:inputField id="phone" value="{!act.Phone}"/> 

            </apex:pageBlockSectionItem>  

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="Rating" /> 

                <apex:selectList id="rating" value="{!rating}" multiselect="false"> 

                <apex:selectOptions value="{!ratings}"/> 

                </apex:selectList> 

            </apex:pageBlockSectionItem> 

             

            <!--Newly Added ---> 

             

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="Number" /> 

                <apex:inputField id="NOL" value="{!act.NumberofLocations__c}"/> 

            </apex:pageBlockSectionItem> 

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="Date" /> 

                <apex:inputField id="date" value="{!act.SLAExpirationDate__c}"/> 

            </apex:pageBlockSectionItem>  

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="Currency" /> 

                <apex:inputField id="currency" value="{!act.currency__c}"/> 

            </apex:pageBlockSectionItem> 

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="checkbox" /> 

                <apex:inputField id="checkbox" value="{!act.checkbox__c}"/> 

            </apex:pageBlockSectionItem> 

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="lokup" /> 

                <apex:inputField id="lokup" value="{!act.Lookup__c}"/> 

            </apex:pageBlockSectionItem> 

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="percent" /> 

                <apex:inputField id="percent" value="{!act.percent__c}"/> 

            </apex:pageBlockSectionItem>             

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="picklist" /> 

                <apex:inputField id="picklist" value="{!act.Active__c}"/> 

            </apex:pageBlockSectionItem> 

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="textarea" /> 

                <apex:inputField id="textarea" value="{!act.Text_Area__c}"/> 

            </apex:pageBlockSectionItem> 

            <apex:pageBlockSectionItem > 

                <apex:outputLabel value="DateTime" /> 

                <apex:inputField id="datetime" value="{!act.DateTime__c}"/> 

            </apex:pageBlockSectionItem> 

        </apex:pageBlockSection> 

    </apex:pageBlock> 

    </apex:form> 

</apex:page>

 

Preview of the Visualforce Page: 

Image

Visual Force Page 

Custom Controller (Apex Class) for passing inputs into Drop Down Fields:

public class testcustomController{ 
    public Account act{get;set;} 
    public Contact ctct{get;set;} 
    public string rating{get;set;} 
    public string newvalue{get;set;} 
     
    public List<SelectOption> getratings() { 
        List<SelectOption> options = new List<SelectOption>(); 
        options.add(new SelectOption('Hot','Hot')); 
        options.add(new SelectOption('Cold','Cold')); 
        options.add(new SelectOption('Warm','Warm')); 
        return options; 
    }             
     
     
    public testcustomController(){ 
        act = new Account(); 
    } 
     
    public PageReference save() { 
        act.SLASerialNumber__c = newvalue; 
        act.rating = rating; 
        insert act;      

        PageReference orderPage = new PageReference('/'+act.id); 
        orderPage.setRedirect(true); 
        return orderPage; 
    }     
    } 

Page Object Class (Apex Class):

public class checkCustomController extends cinnamon.PageObject { 
 
    cinnamon.WebElement inputtext,name,phone,rating,NOL,date1,currency1,checkbox,lokup,percent1,datetime1,saveAct; 
     
 
    public override void initializePageObject() { 
         
        inputtext = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'Inputtext')); 
        name = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'name')); 
        phone = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'phone'));     
        rating = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_SELECTLIST, 'rating'));  
        NOL = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'NOL'));  
        date1 = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'date'));  
        currency1 = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'currency'));  
        checkbox  = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'checkbox')); 
        lokup = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'lokup')); 
        percent1 =  getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'percent'));   
        datetime1 = getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_INPUTFIELD, 'datetime'));    
         
        saveAct =  getElement(new cinnamon.VisualforceLocator(cinnamon.VisualforceTag.APEX_COMMANDBUTTON, 'saveAct'));               
    } 
 
    public checkCustomController saveAccount() { 
        saveAct.click(); 
        return this; 
    } 
 
    public checkCustomController typeAccountInfo(String data1,String data2,String data3,String data4,String nol1,String date11,String currecy11,String checkbox1,String lokup1,String percent11,String datetime11) { 
        inputtext.sendkeys(data1); 
        name.sendKeys(data2); 
        phone.sendKeys(data3); 
        Cinnamon.SelectOptionAdapter sel = new Cinnamon.SelectOptionAdapter(rating,data4); 
        sel.click();   
        NOL.sendKeys(nol1); 
        date1.sendKeys(date11); 
        currency1.sendKeys(currecy11); 
        checkbox.sendKeys(checkbox1); 
        lokup.sendkeys(lokup1); 
        percent1.sendKeys(percent11); 
        datetime1.sendKeys(datetime11);   
        saveAccount();             
        return this; 
         
    }      
     
} 



Base Test Class (Apex Class): 

public class TestCustomControllerPage extends cinnamon.BaseTest { 
 
    public override void setup(cinnamon.Context context) { 

    
    } 
    public override String getStartingPath(cinnamon.Context context) { 
        return '/apex/CustomAccountPage'; 
    } 
 
    public override void test(cinnamon.Context context) {         

        checkCustomController page = (checkCustomController) context.getPageObject(checkCustomController.class); 
        Datetime myDateTime = DateTime.parse('10/14/2011 11:46 AM'); 
        String dateInStringString = myDateTime.format('MM/dd/yyyy'); 
         
        page.typeAccountInfo('Ac001','Test Account63016','9876543210','Cold','101',dateInStringString,'35','True','dgd','11','5/25/2016 3:35 AM');  
        } 
    public override void tearDown(cinnamon.Context context) { 
        Ids.add((String) context.get('cstid')); 
        Database.delete(Ids); 
    } 
} 

By using Cinnamon, we can achieve the following:

  1. Creating a record in Standard Object.
  2. Editing a record in Standard Object.
  3. Testing Field mappings not in the UI level can validate only through DB.
  4. Testing the Task Creation.

Below are some of the challenging scenarios in Cinnamon:

  1. UI Validation like field Labels.
  2. Profile/User and Permission Set based testing scenarios.
  3. Workflow validation and Time based Actions testing scenarios.
  4. Email Validation testing scenarios.
  5. Page Responsiveness like browser re-size/ mobile view.
  6. Navigation of different VF pages.
  7. Scheduled Jobs.
  8. Web Service testing (Rest client, Postman Client).

Data types in Cinnamon:

Supported Unsupported
Email Field Picklist Field
Phone Field Checkbox Field
Date Field Text Area Field
Date Time Field Long Text Area Field
Number Field Multi-Select Picklist Field
Currency Field Lookup / Master-Detail (if More than one matching record) Field
Percent Field Radio Button Field

 

Limitations of Cinnamon:

  • It may require client’s permission for installing Cinnamon inside Salesforce Org.
  • Sauce Lab, BrowserStack, and AWS (For Hosting the test in the cloud) are paid versions.
  • There is no updated documentation/ support available for Cinnamon.
  • QA need Developers’ assistance for this Automation.

Conclusion: 

Cinnamon will be more useful for Unit Testing like data manipulation in UI level (VF Pages) and Field Mappings. It cannot be used for regression testing and functional testing.