Skip to main content

Test Scripts in Code Blocks

Encompass developer guide to writing Mocha/Chai test scripts for JavaScript Code Blocks on custom Dashboards; scripts need 80% statement coverage before release. Requires v24.01+.

B
Written by Bailey Naas-Thornton

Minimum Version Required: 24.01

Pre-Conditions

Test Scripts are required to release any Dashboard with a Code Block that contains JavaScript.

Test Scripts must hit a minimum of 80% Statement coverage.

Steps

Test Scripts are required to release any Dashboard with a Code Block that contains JavaScript.
​
Test Scripts must hit a minimum of 80% Statement coverage.

Things to Know

  • Put the corresponding code in the correct code tab
    ​
    ​

    • i.e. Don't put JavaScript in a <script> tag in the HTML tab
      ​
      ​
      ​

  • The HTML tab contents do NOT have access to the JavaScript file directly
    ​
    ​

  • The test scripts use the Chai Assert APIs and the Mocha testing framework
    ​
    ​

  • Tests run sequentially, in the order they are written
    ​
    ​

  • You cannot debug test scripts
    ​
    ​

    • If a test script is failing for an unknown reason try using console.log to see data during the test (make sure to remove once done)
      ​
      ​

    • console.log can only be used in the Test Script tab, it cannot be in the JavaScript tab otherwise the test won't run due to an error
      ​
      ​
      ​

  • Test scripts will fail if the page is unloaded for some reason (redirect on click for example)
    ​
    ​

    • Make sure the actual redirect is not run during the test
      ​
      ​
      ​

  • Use EC_Fmt.TriggerEvent to trigger specific events on elements during testing
    ​
    ​

    • Most commonly used for UI based tests
      ​
      ​
      ​

Best Practices

  • Write test cases for as much code as possible
    ​
    ​

    • 80% is the minimum allowed but should not be the goal
      ​
      ​
      ​

  • Don't write test scripts without at least 1 assert call
    ​
    ​

    • This may require some refactoring of JavaScript tab code to return data to check against
      ​
      ​

    • Check DOM changes when testing UI
      ​
      ​

    • Async tests with done are an exception
      ​
      ​
      ​

  • Avoid calling APIs, downloading files, prompting to print, etc. in test scripts
    ​
    ​

    • APIs take more time to run and typically the result is all that is needed
      ​
      ​

    • Create test results and put those in the test scripts to feed into tests
      ​
      ​

    • Use a "is testing" variable to know when tests are running; if running don't call the APIs (example below)
      ​
      ​
      ​

  • Keep test cases simple and focused
    ​
    ​

    • Don't test multiple things in a single test
      ​
      ​
      ​

  • If using manual delays in test scripts, modify the test's expected slow time to avoid timeouts and false flags of slowness
    ​
    ​

  • If testing UI changes (dialog opening, text changing, etc.) query the DOM for the effected elements and assert the changes you expect those elements to have
    ​
    ​
    ​
    ​

Definitions

  • describe - Function called to describe a set of test scripts
    ​
    ​

    • Could be a specific function or a workflow
      ​
      ​

    • Dictates how the final test results are grouped
      ​
      ​
      ​

  • it - Function called to perform a particular test
    ​
    ​

    • Typically nested inside describe calls but can be by themselves
      ​
      ​
      ​

  • assert - Used to confirm test script values are expected
    ​
    ​

    • Will fail a test script if the assert fails

Templates/Examples

Basic

const assert = chai.assert;describe("Some Function", () => { it("will do this", () => { const TestData = { Test: "abc" }; const result = SomeFunction(TestData); assert.equal(result, "cba", "result should be reversed"); });});;

Debug Variable

JavaScript tab

// At the toplet TestsAreRunning = false;function Enable_Testing() { TestsAreRunning = true;}[...]// inside a function that redirects or calls an APIconst myRequest = new EC_Request("SomeAPICommand")if (!TestsAreRunning) { myRequest.submit()}

Test Script tab

const assert = chai.assert;before(() => { Enable_Testing();});describe("Some Function", () => { it("will do this", () => { const TestData = { Test: "abc" }; const result = SomeFunction(TestData); assert.equal(result, "cba", "result should be reversed"); });});

Manual Delay

This is typically used when waiting for a UI action to occur and checking results of that action

const assert = chai.assert;describe("Some Function", () => { it("will open a dialog", async () => { // FAST = slow/2 // We are waiting 1 seconds always, default is 75ms this.slow(2075); const TestData = { Test: "abc" }; SomeFunction(TestData); // Wait 1 second to make sure dialog has time to popup await delay(1000); assert.isTrue(Dialog_IsShowing(), "dialog should be showing"); assert.equal(Get_OnScreen_Dialog_Title(), TestData.Test, "dialog title should match passed in data"); }); after(() => { // Cleanup any open dialogs after the test Click_CloseButton_On_ShowingDialogs(); })});

UI Test

const assert = chai.assert;describe("Clicking the open dialog button", () => { it("will open a dialog when valid data set", async () => { // FAST = slow/2 // We are waiting 1 seconds always, default is 75ms this.slow(2075); const ValidData = { Test: "abc" }; UpdateFormData(ValidData); EC_Fmt.TriggerEvent(document.getElementById("my-button"), "click", { bubbles: true }); // Wait 1 second to make sure dialog has time to popup await delay(1000); assert.isTrue(Dialog_IsShowing(), "dialog should be showing"); assert.equal(Get_OnScreen_Dialog_Title(), ValidData.Test, "dialog title should match passed in data"); }); it("will not open a dialog with inputs when valid data set", async () => { // FAST = slow/2 // We are waiting 1 seconds always, default is 75ms this.slow(2075); const InvalidData; UpdateFormData(InvalidData); EC_Fmt.TriggerEvent(document.getElementById("my-button"), "click", { bubbles: true }); // Wait 1 second to make sure dialog has time to popup await delay(1000); assert.isFalse(Dialog_IsShowing(), "dialog should not be showing"); assert.isTrue(document.querySelector("input[name=my-input]").hasAttribute("error"), "input should be in error"); }); after(() => { // Cleanup any open dialogs after the test Click_CloseButton_On_ShowingDialogs(); })});;

Asynchronous Tests

See Mocha's Asynchronous Code for more information about done

const assert = chai.assert;describe("Async Functions", () => { // passing in a callback // testing will wait until done() is called it("will work without failing", (done) => { SomeFunction((error) => { if (error) { done(error); } else { done(); } }); }); // Asserting with a returned Promise it("will return a promise with the correct results", (done) => { return SomeFunction().then((data) => { assert.equal(data.value, "1234", "function returns 1234"); }); }); // Awaiting a promise it("will return correct results", async (done) => { const Data = await SomeFunction(); assert.equal(Data.value, "1234", "function returns 1234"); });});

Helpful Functions

delay(milliseconds)

Halts execution for the specified milliseconds

  • milliseconds: number Number of milliseconds to wait for

function delay(milliseconds) { return new Promise(res => setTimeout(res, milliseconds));};

Example

// wait for 1 secondawait delay(1000);

Click_ConfirmButton_On_ShowingDialogs()

Click the confirm button on all showing dialogs

function Click_ConfirmButton_On_ShowingDialogs() { const dialogs = document.querySelectorAll(".ecp-dialog"); for (let i = 0; i < dialogs.length; ++i) { // Find dialogs that are still showing if (dialogs[i].style.display !== "none") { EC_Fmt.TriggerEvent(dialogs[i].querySelector(".ecp-dialog-confirm-button"), "click", { bubbles: true }); } }}

Example

SomeFunctionThatOpenDialog();// wait for the dialog to show upawait delay(1000);// Click confirm to test the confirm action on a dialogClick_ConfirmButton_On_ShowingDialogs();

Click_CloseButton_On_ShowingDialogs()

Click the close button (top-right X button) on all showing dialogs

function Click_CloseButton_On_ShowingDialogs() { const Dialogs = document.querySelectorAll(".ecp-dialog"); for (let i = 0; i < Dialogs.length; ++i) { // Find dialogs that are still showing if (Dialogs[i].style.display !== "none") { EC_Fmt.TriggerEvent(Dialogs[i].querySelector(".ecp-dialog-close"), "click", { bubbles: true }); } }}

Example

// close all opened dialogs after test cases run before the next setafter(() => { // Click confirm to test the confirm action on a dialog Click_CloseButton_On_ShowingDialogs();});

Dialog_IsShowing()

Determines if there is a dialog currently displayed during tests

  • Returns: booleantrue if a dialog is on screen, false if no dialog is showing

function Dialog_IsShowing() { const dialogs = document.querySelectorAll(".ecp-dialog"); for (let i = 0; i < dialogs.length; ++i) { // Find dialogs that are still showing if (dialogs[i].style.display !== "none") { return true; } } return false;}

Example

SomeFunctionThatOpenDialog();// wait for the dialog to show upawait delay(1000);assert.isTrue(Dialog_IsShowing(), "dialog should be showing");

Get_OnScreen_Dialog()

Gets the first showing dialog and returns the whole Element

  • Returns: Element The first showing dialog element, containing header, contents, and buttons; null otherwise

function Get_OnScreen_Dialog() { const dialogs = document.querySelectorAll(".ecp-dialog"); for (let i = 0; i < dialogs.length; ++i) { // Find dialogs that are still showing if (dialogs[i].style.display !== "none") { return dialogs[i]; } } return null;}

Example

SomeFunctionThatOpenDialog();// wait for the dialog to show upawait delay(1000);const OpenDialog = Get_OnScreen_Dialog();assert.exists(OpenDialog.querySelector("[name=SomeName]"), "dialog should be open");

Get_OnScreen_Dialog_Title()

Returns the title string of the first showing dialog

  • Returns: string The title of first found open dialog; "" (empty string) returned if no Dialog found

function Get_OnScreen_Dialog_Title() { const dialogs = document.querySelectorAll(".ecp-dialog"); for (let i = 0; i < dialogs.length; ++i) { // Find dialogs that are still showing if (dialogs[i].style.display !== "none") { return dialogs[i].querySelector(".ecp-dialog-title").innerText; } } return "";}

Example

SomeFunctionThatOpenDialog();// wait for the dialog to show upawait delay(1000);assert.equal(Get_OnScreen_Dialog_Title(), "My Dialog Title", "title should match");

Get_OnScreen_Dialog_Content()

Returns the innerHTML string of the content of the first open dialog
Used for simple dialog content checking

  • Returns: string The innerHTML of the found dialog

function Get_OnScreen_Dialog_Content() { const dialogs = document.querySelectorAll(".ecp-dialog"); for (let i = 0; i < dialogs.length; ++i) { // Find dialogs that are still showing if (dialogs[i].style.display !== "none") { return dialogs[i].querySelector(".ecp-dialog-content div").innerHTML; } } return "";}

Example

SomeFunctionThatOpenDialog();// wait for the dialog to show upawait delay(1000);assert.equal(Get_OnScreen_Dialog_Content(), "Are you sure you want to do this action?", "contents should match");
Did this answer your question?