From f4a1ece7e1e4d275617d619fdf303f8129e18b47 Mon Sep 17 00:00:00 2001 From: "Kees C. Bakker" Date: Tue, 5 Sep 2023 08:26:53 +0200 Subject: [PATCH] Multiline support (#131) * fix: unit tests for mention * 8.0.1 * chore: upgrade packages * chore: fix typo * chore: fix log level while testing * feat: support multiline matching for parameters --- package-lock.json | 126 +++++++++--------- package.json | 16 +-- src/entities/parameters/ValueExtractor.ts | 2 +- src/mappers/map_tool.ts | 2 +- ...hitespaceCharactersFromIncomingMessages.ts | 2 +- src/utils/regex.ts | 4 +- test/_parameter-testing.ts | 4 +- test/commands/replaced_by.spec.ts | 4 +- test/common/test-bot.ts | 3 +- test/parameters/StringParameters.spec.ts | 6 + test/scenarios/multiline.spec.ts | 32 +++++ 11 files changed, 119 insertions(+), 82 deletions(-) create mode 100644 test/scenarios/multiline.spec.ts diff --git a/package-lock.json b/package-lock.json index 335fda5..f2baf03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,28 +1,28 @@ { "name": "hubot-command-mapper", - "version": "8.0.0", + "version": "8.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "hubot-command-mapper", - "version": "8.0.0", + "version": "8.0.1", "license": "MIT", "dependencies": { "named-regexp-groups": "^1.0.6", "remove-markdown": "^0.5.0" }, "devDependencies": { - "@types/chai": "^4.3.5", - "@types/hubot": "^3.3.2", + "@types/chai": "^4.3.6", + "@types/hubot": "^3.3.3", "@types/mocha": "^10.0.1", - "@types/node": "^20.4.6", - "chai": "^4.3.7", - "del-cli": "^5.0.0", + "@types/node": "^20.5.9", + "chai": "^4.3.8", + "del-cli": "^5.1.0", "hubot": "^7.0.0", - "hubot-mock-adapter": "^1.1.1", + "hubot-mock-adapter": "^2.0.0", "mocha": "^10.2.0", - "prettier": "^3.0.1", + "prettier": "^3.0.3", "ts-node": "^10.9.1" } }, @@ -350,9 +350,9 @@ } }, "node_modules/@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==", "dev": true }, "node_modules/@types/connect": { @@ -388,9 +388,9 @@ } }, "node_modules/@types/hubot": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@types/hubot/-/hubot-3.3.2.tgz", - "integrity": "sha512-cxwfH8NLHQK6IGqiDnyQVkZE0hY2kuv5W8J2AcqSazBNXqLS6IFFt+J3AHnpFQ8p+rkpArL9gLiUbSceMwDbVQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@types/hubot/-/hubot-3.3.3.tgz", + "integrity": "sha512-WBVhSlA+ylDp5sRRXd7EUR8qik+zWkVlWOtzc3eITAs8nd5R+swc6m1eU1aQO4Aq5sOoY6jo9epNrkgZqMriuA==", "dev": true, "dependencies": { "@types/express": "*", @@ -422,9 +422,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.4.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.6.tgz", - "integrity": "sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==", + "version": "20.5.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", + "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==", "dev": true }, "node_modules/@types/normalize-package-data": { @@ -886,9 +886,9 @@ } }, "node_modules/chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", + "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", @@ -1180,9 +1180,9 @@ } }, "node_modules/del": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-7.0.0.tgz", - "integrity": "sha512-tQbV/4u5WVB8HMJr08pgw0b6nG4RGt/tj+7Numvq+zqcvUFeMaIWWOUFltiU+6go8BSO2/ogsB4EasDaj0y68Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-7.1.0.tgz", + "integrity": "sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==", "dev": true, "dependencies": { "globby": "^13.1.2", @@ -1202,12 +1202,12 @@ } }, "node_modules/del-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/del-cli/-/del-cli-5.0.0.tgz", - "integrity": "sha512-rENFhUaYcjoMODwFhhlON+ogN7DoG+4+GFN+bsA1XeDt4w2OKQnQadFP1thHSAlK9FAtl88qgP66wOV+eFZZiQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/del-cli/-/del-cli-5.1.0.tgz", + "integrity": "sha512-xwMeh2acluWeccsfzE7VLsG3yTr7nWikbfw+xhMnpRrF15pGSkw+3/vJZWlGoE4I86UiLRNHicmKt4tkIX9Jtg==", "dev": true, "dependencies": { - "del": "^7.0.0", + "del": "^7.1.0", "meow": "^10.1.3" }, "bin": { @@ -2562,12 +2562,12 @@ } }, "node_modules/hubot-mock-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/hubot-mock-adapter/-/hubot-mock-adapter-1.1.1.tgz", - "integrity": "sha512-ScxUKd0XCUx2zavw2JF+128Ekmffi9N8dRcPWvPMvdPtqN5mvhdlLpxH26YYOlh8Og1MpKWL/UFQeVrEPfTawA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hubot-mock-adapter/-/hubot-mock-adapter-2.0.0.tgz", + "integrity": "sha512-TNaFGAHvEQVdpgka+xZpo/LXxHc+9W3Ri6jpiAeWJEI73ylKK1w2RCpXGF+6AhOp8NegEew4cbjAftUslFDveQ==", "dev": true, - "dependencies": { - "hubot": ">=2.5.4" + "peerDependencies": { + "hubot": "^7.x.x" } }, "node_modules/hubot/node_modules/accepts": { @@ -4768,9 +4768,9 @@ } }, "node_modules/prettier": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz", - "integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -6297,9 +6297,9 @@ } }, "@types/chai": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz", - "integrity": "sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.6.tgz", + "integrity": "sha512-VOVRLM1mBxIRxydiViqPcKn6MIxZytrbMpd6RJLIWKxUNr3zux8no0Oc7kJx0WAPIitgZ0gkrDS+btlqQpubpw==", "dev": true }, "@types/connect": { @@ -6335,9 +6335,9 @@ } }, "@types/hubot": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/@types/hubot/-/hubot-3.3.2.tgz", - "integrity": "sha512-cxwfH8NLHQK6IGqiDnyQVkZE0hY2kuv5W8J2AcqSazBNXqLS6IFFt+J3AHnpFQ8p+rkpArL9gLiUbSceMwDbVQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@types/hubot/-/hubot-3.3.3.tgz", + "integrity": "sha512-WBVhSlA+ylDp5sRRXd7EUR8qik+zWkVlWOtzc3eITAs8nd5R+swc6m1eU1aQO4Aq5sOoY6jo9epNrkgZqMriuA==", "dev": true, "requires": { "@types/express": "*", @@ -6369,9 +6369,9 @@ "dev": true }, "@types/node": { - "version": "20.4.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.6.tgz", - "integrity": "sha512-q0RkvNgMweWWIvSMDiXhflGUKMdIxBo2M2tYM/0kEGDueQByFzK4KZAgu5YHGFNxziTlppNpTIBcqHQAxlfHdA==", + "version": "20.5.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.9.tgz", + "integrity": "sha512-PcGNd//40kHAS3sTlzKB9C9XL4K0sTup8nbG5lC14kzEteTNuAFh9u5nA0o5TWnSG2r/JNPRXFVcHJIIeRlmqQ==", "dev": true }, "@types/normalize-package-data": { @@ -6703,9 +6703,9 @@ } }, "chai": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", - "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", + "version": "4.3.8", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.8.tgz", + "integrity": "sha512-vX4YvVVtxlfSZ2VecZgFUTU5qPCYsobVI2O9FmwEXBhDigYGQA6jRXCycIs1yJnnWbZ6/+a2zNIF5DfVCcJBFQ==", "dev": true, "requires": { "assertion-error": "^1.1.0", @@ -6919,9 +6919,9 @@ } }, "del": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/del/-/del-7.0.0.tgz", - "integrity": "sha512-tQbV/4u5WVB8HMJr08pgw0b6nG4RGt/tj+7Numvq+zqcvUFeMaIWWOUFltiU+6go8BSO2/ogsB4EasDaj0y68Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/del/-/del-7.1.0.tgz", + "integrity": "sha512-v2KyNk7efxhlyHpjEvfyxaAihKKK0nWCuf6ZtqZcFFpQRG0bJ12Qsr0RpvsICMjAAZ8DOVCxrlqpxISlMHC4Kg==", "dev": true, "requires": { "globby": "^13.1.2", @@ -6935,12 +6935,12 @@ } }, "del-cli": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/del-cli/-/del-cli-5.0.0.tgz", - "integrity": "sha512-rENFhUaYcjoMODwFhhlON+ogN7DoG+4+GFN+bsA1XeDt4w2OKQnQadFP1thHSAlK9FAtl88qgP66wOV+eFZZiQ==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/del-cli/-/del-cli-5.1.0.tgz", + "integrity": "sha512-xwMeh2acluWeccsfzE7VLsG3yTr7nWikbfw+xhMnpRrF15pGSkw+3/vJZWlGoE4I86UiLRNHicmKt4tkIX9Jtg==", "dev": true, "requires": { - "del": "^7.0.0", + "del": "^7.1.0", "meow": "^10.1.3" } }, @@ -8490,13 +8490,11 @@ } }, "hubot-mock-adapter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/hubot-mock-adapter/-/hubot-mock-adapter-1.1.1.tgz", - "integrity": "sha512-ScxUKd0XCUx2zavw2JF+128Ekmffi9N8dRcPWvPMvdPtqN5mvhdlLpxH26YYOlh8Og1MpKWL/UFQeVrEPfTawA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hubot-mock-adapter/-/hubot-mock-adapter-2.0.0.tgz", + "integrity": "sha512-TNaFGAHvEQVdpgka+xZpo/LXxHc+9W3Ri6jpiAeWJEI73ylKK1w2RCpXGF+6AhOp8NegEew4cbjAftUslFDveQ==", "dev": true, - "requires": { - "hubot": ">=2.5.4" - } + "requires": {} }, "ieee754": { "version": "1.2.1", @@ -9555,9 +9553,9 @@ "dev": true }, "prettier": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.1.tgz", - "integrity": "sha512-fcOWSnnpCrovBsmFZIGIy9UqK2FaI7Hqax+DIO0A9UxeVoY4iweyaFjS5TavZN97Hfehph0nhsZnjlVKzEQSrQ==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", "dev": true }, "process": { diff --git a/package.json b/package.json index 1533f08..9a2b55a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hubot-command-mapper", - "version": "8.0.0", + "version": "8.0.1", "description": "Helps with mapping tools and commands to hubot", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -35,16 +35,16 @@ "remove-markdown": "^0.5.0" }, "devDependencies": { - "@types/chai": "^4.3.5", - "@types/hubot": "^3.3.2", + "@types/chai": "^4.3.6", + "@types/hubot": "^3.3.3", "@types/mocha": "^10.0.1", - "@types/node": "^20.4.6", - "chai": "^4.3.7", - "del-cli": "^5.0.0", + "@types/node": "^20.5.9", + "chai": "^4.3.8", + "del-cli": "^5.1.0", "hubot": "^7.0.0", - "hubot-mock-adapter": "^1.1.1", + "hubot-mock-adapter": "^2.0.0", "mocha": "^10.2.0", - "prettier": "^3.0.1", + "prettier": "^3.0.3", "ts-node": "^10.9.1" }, "files": [ diff --git a/src/entities/parameters/ValueExtractor.ts b/src/entities/parameters/ValueExtractor.ts index 30f39e0..20537fe 100644 --- a/src/entities/parameters/ValueExtractor.ts +++ b/src/entities/parameters/ValueExtractor.ts @@ -14,7 +14,7 @@ export function getValues( if (command.parameters) { let r = convertCommandIntoRegexString(robotName, robotAlias, tool, command, true) - let nr = new NamedRegExp(r, "i") + let nr = new NamedRegExp(r, "si") let answer = nr.exec(message).groups diff --git a/src/mappers/map_tool.ts b/src/mappers/map_tool.ts index 6068e18..5e905d6 100644 --- a/src/mappers/map_tool.ts +++ b/src/mappers/map_tool.ts @@ -47,7 +47,7 @@ export function map_tool(robot: Hubot.Robot, tool: InternalTool, options: IOptio //match edge cases in which certain phrases end with a command name const strValidationRegex = convertCommandIntoRegexString(robot.name, robot.alias, tool, cmd) - cmd.validationRegex = new RegExp(strValidationRegex, "i") + cmd.validationRegex = new RegExp(strValidationRegex, "si") if (robot.logger) { robot.logger.info(`Mapping '${tool.name}.${cmd.name}' as '${strValidationRegex}'.`) diff --git a/src/middleware/removeTrailingBotWhitespaceCharactersFromIncomingMessages.ts b/src/middleware/removeTrailingBotWhitespaceCharactersFromIncomingMessages.ts index 1baea46..bbb8c5e 100644 --- a/src/middleware/removeTrailingBotWhitespaceCharactersFromIncomingMessages.ts +++ b/src/middleware/removeTrailingBotWhitespaceCharactersFromIncomingMessages.ts @@ -20,7 +20,7 @@ export function removeTrailingBotWhitespaceCharactersFromIncomingMessages(robot: robot.receiveMiddleware((context, next, done) => { const text = context.response.message.text if (text) { - const newText = text.replace(new RegExp(`(${robotNameRegexString})\\s+`, "i"), "$1 ") + const newText = text.replace(new RegExp(`(${robotNameRegexString})\\s+`, "si"), "$1 ") if (text != newText) { context.response.message.text = newText } diff --git a/src/utils/regex.ts b/src/utils/regex.ts index 5b38dcc..13d053b 100644 --- a/src/utils/regex.ts +++ b/src/utils/regex.ts @@ -117,13 +117,13 @@ export function convertCommandIntoRegexString( regexString += ")" if (cmd.capture) { - //command / tool sperator! + //command / tool separator! if (extraSpace) { regexString += " " } //capture the capture in a group so it will - //be accessable through the matches + //be accessible through the matches regexString += "(" regexString += cmd.capture regexString += ")" diff --git a/test/_parameter-testing.ts b/test/_parameter-testing.ts index 3aa700b..8a3f901 100644 --- a/test/_parameter-testing.ts +++ b/test/_parameter-testing.ts @@ -2,12 +2,12 @@ import { IParameter, ITool } from "../src" import { convertCommandIntoRegexString } from "../src/utils/regex" export function test(regex: string, dataToTest: string) { - var r = new RegExp(regex, "i") + var r = new RegExp(regex, "si") return r.test(dataToTest) } export function exec(regex: string, dataToTest: string) { - var r = new RegExp(regex, "i") + var r = new RegExp(regex, "si") return r.exec(dataToTest) } diff --git a/test/commands/replaced_by.spec.ts b/test/commands/replaced_by.spec.ts index 23127df..68dea47 100644 --- a/test/commands/replaced_by.spec.ts +++ b/test/commands/replaced_by.spec.ts @@ -22,7 +22,7 @@ describe("replaced_by.spec.ts / Replaced by another bot", () => { }) let response = await context.sendAndWaitForResponse("@hubot clear screen") - expect(response).to.eql("Sorry, this feature has been replaced by @kz. Please use:\n```\n@kz clear screen\n```\n") + expect(response).to.eql("Sorry, this feature has been replaced by <@kz>. Please use:\n```\n@kz clear screen\n```\n") }) it("Tool replacement", async () => { @@ -41,6 +41,6 @@ describe("replaced_by.spec.ts / Replaced by another bot", () => { ) let response = await context.sendAndWaitForResponse("@hubot c d") - expect(response).to.eql("Sorry, this feature has been replaced by @kz. Please use:\n```\n@kz c d\n```\n") + expect(response).to.eql("Sorry, this feature has been replaced by <@kz>. Please use:\n```\n@kz c d\n```\n") }) }) diff --git a/test/common/test-bot.ts b/test/common/test-bot.ts index 135df64..4d7a539 100644 --- a/test/common/test-bot.ts +++ b/test/common/test-bot.ts @@ -45,6 +45,7 @@ export class TestBotContext { shutdown(): void { this.robot.shutdown() + delete process.env.HUBOT_LOG_LEVEL } } @@ -56,7 +57,7 @@ export type TestBotSettings = { } export async function createTestBot(settings: TestBotSettings | null = null): Promise { - process.env["HUBOT_LOG_LEVEL"] = settings?.logLevel || "error" + process.env.HUBOT_LOG_LEVEL = settings?.logLevel || "silent" return new Promise(async done => { // create new robot, without http, using the mock adapter diff --git a/test/parameters/StringParameters.spec.ts b/test/parameters/StringParameters.spec.ts index 28903b4..d475d2b 100644 --- a/test/parameters/StringParameters.spec.ts +++ b/test/parameters/StringParameters.spec.ts @@ -12,6 +12,12 @@ describe("StringParameters.spec.ts", () => { var r = createRegex([p]) expect(test(r, "hubot test cmd Capture all")).to.eq(true) }) + + it("Multi line parameter", () => { + var p = new RestParameter("a") + var r = createRegex([p]) + expect(test(r, "hubot test cmd Capture all\nnew lines\n")).to.eq(true) + }) }) describe("StringParameter", () => { diff --git a/test/scenarios/multiline.spec.ts b/test/scenarios/multiline.spec.ts new file mode 100644 index 0000000..a95a7f6 --- /dev/null +++ b/test/scenarios/multiline.spec.ts @@ -0,0 +1,32 @@ +import { createTestBot, TestBotContext } from "../common/test-bot" +import { expect } from "chai" +import { map_command, RestParameter } from "../../src" + +describe("multiline.spec.ts > todo example", () => { + let context: TestBotContext + + beforeEach(async () => { + context = await createTestBot() + map_command(context.robot, "act", new RestParameter("value"), context => context.res.reply(context.values.value)) + }) + + afterEach(() => context.shutdown()) + + it("one line", async () => { + let line = "And now the end is here" + let result = await context.sendAndWaitForResponse("hubot act " + line) + expect(result).to.eql(line) + }) + + it("two lines", async () => { + let line = "And now the end is here\nAnd so I face that final curtain" + let result = await context.sendAndWaitForResponse("hubot act " + line) + expect(result).to.eql(line) + }) + + it("three lines", async () => { + let line = "And now the end is here\nAnd so I face that final curtain\nMy friend I'll make it clear" + let result = await context.sendAndWaitForResponse("hubot act " + line) + expect(result).to.eql(line) + }) +})