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
This means you cannot create
<input>tags with built-in ECP-ECP-ECP-onclick/onchange/etc. events
To set event listeners use
document.querySelectorordocument.getElementByIdto get the element then callelement.addEventListener
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.logto see data during the test (make sure to remove once done)
console.logcan 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.TriggerEventto 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
assertcall
This may require some refactoring of JavaScript tab code to return data to check against
Check DOM changes when testing UI
Async tests with
doneare 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
See Mocha's test duration info for more information
Example below
If testing UI changes (dialog opening, text changing, etc.) query the DOM for the effected elements and
assertthe 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
describecalls 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: numberNumber 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:
booleantrueif a dialog is on screen,falseif 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:
ElementThe first showing dialog element, containing header, contents, and buttons;nullotherwise
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:
stringThe 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:
stringThe 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");