From 90f4b012830444b810c1275b150b9e2f3d7773bb Mon Sep 17 00:00:00 2001 From: Mosab Samara Date: Sun, 6 Sep 2020 16:52:33 +0300 Subject: [PATCH 1/5] Add the ability to use regex in test --- test.js | 71 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/test.js b/test.js index a3a8493..b06286f 100644 --- a/test.js +++ b/test.js @@ -49,7 +49,7 @@ async function test(context, testData) { } } else { - reason = getTestTitle(testData) + ": " + err.message; + reason = getTestTitle(testData) + ": " + err.message; } return new Result(false, reason, 500); } @@ -79,7 +79,7 @@ function createConversationSteps(testData) { } function isUserMessage(testData, message) { - return (testData && testData.userId) ? (message.from.id == testData.userId) : (message.recipient ? (message.recipient.role == "bot") : (message.from.role != "bot")); + return (testData && testData.userId) ? (message.from.id == testData.userId) : (message.recipient ? (message.recipient.role == "bot") : (message.from.role != "bot")); } function conversationStep(message) { @@ -93,7 +93,7 @@ function testConversation(context, testUserId, conversationSteps, conversationId context.log("conversationSteps: " + utils.stringify(conversationSteps)); context.log("conversationId: " + conversationId); context.log("defaultTimeout: " + defaultTimeout); - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { var index = 0; function nextStep() { if (index < conversationSteps.length) { @@ -105,7 +105,7 @@ function testConversation(context, testUserId, conversationSteps, conversationId } else { context.log("testConversation end"); - resolve({count: index}); + resolve({ count: index }); } } return nextStep(); @@ -128,16 +128,16 @@ function testStep(context, conversationId, userMessage, expectedReplies, timeout context.log("expectedReplies: " + utils.stringify(expectedReplies)); context.log("timeoutMilliseconds: " + timeoutMilliseconds); return directline.sendMessage(conversationId, userMessage) - .then(function(response) { + .then(function (response) { var nMessages = expectedReplies.hasOwnProperty("length") ? expectedReplies.length : 1; var bUserMessageIncluded = response != null; return directline.pollMessages(conversationId, nMessages, bUserMessageIncluded, timeoutMilliseconds); }) - .then(function(messages) { + .then(function (messages) { return compareMessages(context, userMessage, expectedReplies, messages); }) - .catch(function(err) { - var message = `User message '${userMessage.text}' response failed - ${err.message}`; + .catch(function (err) { + var message = `User message '${userMessage.text}' response failed - ${err.message}`; if (err.hasOwnProperty("details")) { err.details.message = message; } @@ -148,14 +148,45 @@ function testStep(context, conversationId, userMessage, expectedReplies, timeout }); } +// Replacement method for chai's deep.equal +function deepEqual(expected, actual) { + // Test using regex for attributes starting with regex keyword + if ((typeof expected === "string" && expected.startsWith("regex:"))) { + const regex = new RegExp(expected.replace("regex:", "").trim()); + if (!regex.test(actual)) + throw "Actual value doesn't match provided regex"; + else return true; + } + // Regular test for other values + else if (expected === actual) + return true; + else if ((typeof expected === "object" && expected !== null) && (typeof actual === "object" && actual !== null)) { + for (var prop in expected) { + if (actual.hasOwnProperty(prop)) { + try { + deepEqual(expected[prop], actual[prop]) + } catch (error) { + throw error; + } + } else { + throw "Actual card is missing '" + prop + "' property"; + } + } + return true; + } + else { + throw "Cards are not equal"; + } +} + function compareMessages(context, userMessage, expectedReplies, actualMessages) { context.log("compareMessages started"); context.log("actualMessages: " + utils.stringify(actualMessages)); // Filter out messages from the (test) user, leaving only bot replies - var botReplies = _.reject(actualMessages, - function(message) { - return message.from.id == userMessage.from.id; - }); + var botReplies = _.reject(actualMessages, + function (message) { + return message.from.id == userMessage.from.id; + }); expect(botReplies, `reply to user message '${userMessage.text}'`).to.have.lengthOf(expectedReplies.length); @@ -165,16 +196,22 @@ function compareMessages(context, userMessage, expectedReplies, actualMessages) var botReply = botReplies[i]; if (botReply.hasOwnProperty("text")) { - var expr = 'expect(botReply.text, "user message number ' + (i+1) + ' ").' + assert + '(expectedReply.text)'; - eval(expr); + // Can + if (expectedReply.text.startsWith("regex:")) { + const regex = new RegExp(expectedReply.text.replace("regex:", "").trim()); + expect(regex.test(botReply.text)).to.equals(true); + } else { + var expr = 'expect(botReply.text, "user message number ' + (i + 1) + ' ").' + assert + '(expectedReply.text)'; + eval(expr); + } } if (botReply.hasOwnProperty("attachments")) { try { - expect(botReply.attachments,`attachments of reply number ${i+1} to user message '${userMessage.text}'`).to.deep.equal(expectedReply.attachments); + expect(deepEqual(expectedReply.attachments, botReply.attachments)).to.equals(true, "Attachments are not equal"); } catch (err) { var exception = new Error(err.message); - exception.details = {message: err.message, expected: err.expected, actual: err.actual, diff: diff(err.expected, err.actual)}; + exception.details = { message: err.message, expected: expectedReply.attachments, actual: botReply.attachments, diff: diff(expectedReply.attachments, botReply.attachments) }; throw exception; } } @@ -183,7 +220,7 @@ function compareMessages(context, userMessage, expectedReplies, actualMessages) } function getTestTitle(testData) { - return `Test ${testData.name? `'${testData.name}'` : `#${testData.index || 0}`}`; + return `Test ${testData.name ? `'${testData.name}'` : `#${testData.index || 0}`}`; } module.exports = Test; From fa75fb4581815bdd70991773f40ba8101055287f Mon Sep 17 00:00:00 2001 From: Mosab Samara Date: Sun, 6 Sep 2020 16:52:59 +0300 Subject: [PATCH 2/5] Edit the script to remove some minor properties for attachments --- transcriptTransformationScript.js | 66 ++++++++++++------------------- 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/transcriptTransformationScript.js b/transcriptTransformationScript.js index b321b08..4b3e95f 100644 --- a/transcriptTransformationScript.js +++ b/transcriptTransformationScript.js @@ -7,16 +7,18 @@ Bot Framework Emulator, and creates a new transformed transcript file named "'"transfromed.transcript" in the script directory. Transformation is done by applying all defined handlers on each entry of the transcript object. -**/ + **/ const fs = require('fs'); /** Here we can add all the handlers we need **/ // Handler 1 -function removeSchemaAttribute(currEntry) { - if (currEntry['type'] === 'message') { - if (currEntry.hasOwnProperty("attachments") && currEntry["attachments"][0].hasOwnProperty("content") && currEntry["attachments"][0]["content"].hasOwnProperty("$schema")) { - delete currEntry["attachments"][0]["content"]["$schema"]; +function removeUnnecessaryAttributes(attachment, attributesToRemove) { + for (attr in attachment) { + if (typeof attachment[attr] === 'object') { + removeUnnecessaryAttributes(attachment[attr], attributesToRemove); + } else if (attributesToRemove.includes(attr)) { + delete attachment[attr]; } } } @@ -42,6 +44,7 @@ function convertColumnsWidthToString(items) { } } } + // Handler 3 function convertNumbersToString(currEntry) { if (currEntry['type'] === 'message') { @@ -65,53 +68,37 @@ function convertNumbersToString(currEntry) { } } +function convertAttachmentAttributesToCamelCase(attachment, unchangedAttributes) { + for (attr in attachment) { + if (typeof attachment[attr] === 'object') { + convertAttachmentAttributesToCamelCase(attachment[attr], unchangedAttributes); + } else if (typeof attachment[attr] === 'string' && !unchangedAttributes.includes(attr)) { + attachment[attr] = attachment[attr].charAt(0).toLowerCase() + attachment[attr].slice(1, attachment[attr].length); + } + } +} + // Handler 4 -function convertColumnAttributesToCamelCase(currEntry) { +function editAttachmentsAttributes(currEntry) { + const attributesToRemove = ['horizontalAlignment', 'style', 'version', '$schema']; + const unchangedAttributes = ['placeholder', 'text', 'type', 'title', 'value', 'id', 'label', 'FoodChoice']; if (currEntry['type'] === 'message') { if (currEntry.hasOwnProperty("attachments")) { const attachments = currEntry["attachments"]; attachments.forEach(attachment => { - if (attachment.hasOwnProperty("content")) { - const content = attachment["content"]; - if (content.hasOwnProperty("body")) { - const contentBody = content["body"][0]; - if (contentBody.hasOwnProperty("items")) { - const bodyItems = contentBody["items"]; - bodyItems.forEach(bodyItem => { - if (bodyItem.hasOwnProperty("columns")) { - const columns = bodyItem["columns"]; - columns.forEach(column => { - if (column.hasOwnProperty("items")) { - const colItems = column["items"]; - const attributesToEdit = ["size", "weight", "color", "horizontalAlignment", "spacing"]; - colItems.forEach(colItem => { - attributesToEdit.forEach(attr => { - if (colItem.hasOwnProperty(attr)) { - colItem[attr] = colItem[attr].charAt(0).toLowerCase() + colItem[attr].slice(1, colItem[attr].length); - } - }); - }); - } - }); - } - }); - } - } - } + removeUnnecessaryAttributes(attachment, attributesToRemove); + convertAttachmentAttributesToCamelCase(attachment, unchangedAttributes); }); } } } - /** This is the main function - It iterates over all entries of the transcript, and applies all handlers on each entry **/ function main(path) { - console.log("Started"); let contentBuffer; try { contentBuffer = fs.readFileSync(path); - } - catch (e) { + } catch (e) { console.log("Cannot open file", e.path); return; } @@ -119,16 +106,13 @@ function main(path) { for (let i = 0; i < jsonTranscript.length; i++) { let currEntry = jsonTranscript[i]; // Here we call to all the handlers we defined - removeSchemaAttribute(currEntry); addSeparationAttribute(currEntry); convertNumbersToString(currEntry); - convertColumnAttributesToCamelCase(currEntry); - + editAttachmentsAttributes(currEntry); } try { const filename = path.replace(/^.*[\\\/]/, '').replace(/\.[^/.]+$/, ''); // Extracts filename without extension from full path. fs.writeFileSync(filename + '_transformed.transcript', JSON.stringify(jsonTranscript)); - console.log("Done"); } catch (e) { console.log("Cannot write file ", e); } From 029eb5e1975efd6520da756d883467b6ff2cf67b Mon Sep 17 00:00:00 2001 From: Mosab Samara Date: Mon, 7 Sep 2020 14:31:17 +0300 Subject: [PATCH 3/5] Some enhancements to the code --- test.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/test.js b/test.js index b06286f..82929a9 100644 --- a/test.js +++ b/test.js @@ -154,7 +154,7 @@ function deepEqual(expected, actual) { if ((typeof expected === "string" && expected.startsWith("regex:"))) { const regex = new RegExp(expected.replace("regex:", "").trim()); if (!regex.test(actual)) - throw "Actual value doesn't match provided regex"; + throw "Actual value doesn't match provided regex, Regex: " + regex.toString() + ", Actual: " + JSON.stringify(actual); else return true; } // Regular test for other values @@ -164,8 +164,10 @@ function deepEqual(expected, actual) { for (var prop in expected) { if (actual.hasOwnProperty(prop)) { try { - deepEqual(expected[prop], actual[prop]) + deepEqual(expected[prop], actual[prop]); } catch (error) { + if (error === "1") + throw "Cards are not equal, Error in attribute '" + prop + "', expectedValue: " + JSON.stringify(expected[prop]) + ", actualValue: " + actual[prop]; throw error; } } else { @@ -175,7 +177,7 @@ function deepEqual(expected, actual) { return true; } else { - throw "Cards are not equal"; + throw "1"; } } @@ -207,11 +209,11 @@ function compareMessages(context, userMessage, expectedReplies, actualMessages) } if (botReply.hasOwnProperty("attachments")) { try { - expect(deepEqual(expectedReply.attachments, botReply.attachments)).to.equals(true, "Attachments are not equal"); + expect(deepEqual(expectedReply.attachments, botReply.attachments)).to.be.true; } catch (err) { - var exception = new Error(err.message); - exception.details = { message: err.message, expected: expectedReply.attachments, actual: botReply.attachments, diff: diff(expectedReply.attachments, botReply.attachments) }; + var exception = new Error(err); + exception.details = { message: err, expected: expectedReply.attachments, actual: botReply.attachments, diff: diff(expectedReply.attachments, botReply.attachments) }; throw exception; } } From b6d3c7e89f442b708d194884b018affaa5a82299 Mon Sep 17 00:00:00 2001 From: Mosab Samara Date: Mon, 7 Sep 2020 15:13:50 +0300 Subject: [PATCH 4/5] Add custom error message to text assertion and use to.be.true assertion method --- test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test.js b/test.js index 82929a9..368887d 100644 --- a/test.js +++ b/test.js @@ -198,10 +198,10 @@ function compareMessages(context, userMessage, expectedReplies, actualMessages) var botReply = botReplies[i]; if (botReply.hasOwnProperty("text")) { - // Can + // Test using regex for text starting with regex keyword if (expectedReply.text.startsWith("regex:")) { const regex = new RegExp(expectedReply.text.replace("regex:", "").trim()); - expect(regex.test(botReply.text)).to.equals(true); + expect(regex.test(botReply.text), "Regex: "+regex.toString()+" doesn't match "+botReply.text).to.be.true; } else { var expr = 'expect(botReply.text, "user message number ' + (i + 1) + ' ").' + assert + '(expectedReply.text)'; eval(expr); From 1727213335947dd2a6cf87865e0d2f76014da95b Mon Sep 17 00:00:00 2001 From: Mosab Samara Date: Mon, 7 Sep 2020 16:36:24 +0300 Subject: [PATCH 5/5] Change thrown exception message --- app.js | 1 - test.js | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/app.js b/app.js index 28267f2..d5edbea 100644 --- a/app.js +++ b/app.js @@ -5,7 +5,6 @@ var SuiteData = require("./suiteData.js"); var Suite = require("./suite"); var ResultsManager = require("./resultsManager"); var config = require("./config.json"); - var restify = require("restify"); const applicationinsights = require("applicationinsights"); diff --git a/test.js b/test.js index 368887d..c9db520 100644 --- a/test.js +++ b/test.js @@ -166,7 +166,7 @@ function deepEqual(expected, actual) { try { deepEqual(expected[prop], actual[prop]); } catch (error) { - if (error === "1") + if (error === "Attributes mismatch") throw "Cards are not equal, Error in attribute '" + prop + "', expectedValue: " + JSON.stringify(expected[prop]) + ", actualValue: " + actual[prop]; throw error; } @@ -177,7 +177,7 @@ function deepEqual(expected, actual) { return true; } else { - throw "1"; + throw "Attributes mismatch"; } } @@ -201,7 +201,7 @@ function compareMessages(context, userMessage, expectedReplies, actualMessages) // Test using regex for text starting with regex keyword if (expectedReply.text.startsWith("regex:")) { const regex = new RegExp(expectedReply.text.replace("regex:", "").trim()); - expect(regex.test(botReply.text), "Regex: "+regex.toString()+" doesn't match "+botReply.text).to.be.true; + expect(regex.test(botReply.text), "Regex: " + regex.toString() + " doesn't match: " + botReply.text).to.be.true; } else { var expr = 'expect(botReply.text, "user message number ' + (i + 1) + ' ").' + assert + '(expectedReply.text)'; eval(expr);