From eafc14c20f82f799616257d8ebdcd0ca9f955c3f Mon Sep 17 00:00:00 2001 From: Shaun Burdick <github@shaunburdick.com> Date: Tue, 16 Jun 2015 17:20:09 -0400 Subject: [PATCH] Added some bug fixes and resolves #2 --- package.json | 4 +- release/js/lib/ooo_user.js | 178 ++++++++++++++++++++--- release/js/spec/ooo_user.spec.js | 123 ++++++++++++++-- src/lib/ooo_user.ts | 238 ++++++++++++++++++++++++++----- src/spec/ooo_user.spec.ts | 144 +++++++++++++++++-- 5 files changed, 606 insertions(+), 81 deletions(-) diff --git a/package.json b/package.json index 090befd..c5c9856 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "author": "Shaun Burdick <github@shaunburdick.com>", "license": "MIT", "dependencies": { + "chrono-node": "^1.0.6", + "moment": "^2.10.3", "slack-client": "^1.4.1", "winston": "^1.0.0" }, @@ -19,6 +21,6 @@ "gulp-batch": "^1.0.5", "gulp-typescript": "^2.7.6", "gulp-watch": "^4.2.4", - "jasmine-node": "^1.14.5" + "jasmine-node": "^2.0.0" } } diff --git a/release/js/lib/ooo_user.js b/release/js/lib/ooo_user.js index 431e7b6..ac76b6b 100644 --- a/release/js/lib/ooo_user.js +++ b/release/js/lib/ooo_user.js @@ -1,5 +1,7 @@ /// <reference path="../typings/tsd.d.ts" /> var logger = require('./logger'); +var moment = require('moment'); +var chrono = require('chrono-node'); var OOO_User = (function () { /** * Constructor @@ -13,12 +15,15 @@ var OOO_User = (function () { this.STATUS_AWAITING_MESSAGE = 'awaiting_message'; this.STATUS_REGISTERED = 'registered'; this.STATUS_AWAITING_DEREGISTER = 'awaiting_deregister'; + this.COMMAND_MESSAGE = 'message'; + this.COMMAND_START = 'start'; + this.COMMAND_END = 'end'; this.status = this.STATUS_UNCONFIRMED; this.MESSAGE_TIMEOUT = 60000; // Five minutes this.DEREGISTER_TIMEOUT = 60000; // Five minutes this.POSITIVE_REGEXP = /(yes|ok|sure|yeah)/i; this.NEGATIVE_REGEXP = /(no|negative)/i; - this.last_communication = new Date(); + this.last_communication = moment(); } /** * Check if the user is out of the office @@ -27,13 +32,138 @@ var OOO_User = (function () { */ OOO_User.prototype.isOOO = function () { var retVal = false; - var now = new Date(); - retVal = (this.ooo_start && this.ooo_start < now); + var now = moment(); + retVal = (this.ooo_start && this.ooo_start.isBefore(now)); if (this.ooo_end) { - retVal = retVal && (this.ooo_end > now); + retVal = retVal && (this.ooo_end.isAfter(now)); } return retVal; }; + /** + * Gets the ms since last communication + * + * @return integer + */ + OOO_User.prototype.lastCommunication = function () { + return this.last_communication ? moment().diff(this.last_communication) : 0; + }; + /** + * Set the user's OOO message and return a response + * + * @param string message The message to set + * @return string A response for the user + */ + OOO_User.prototype.setMessage = function (message) { + this.message = message; + return "Setting your OOO Message to:\n" + message; + }; + /** + * Set the start of the user's OOO + * + * @param string start A parsable date/time string + * @return string A response for the user + */ + OOO_User.prototype.setStart = function (start) { + var retVal = "Unable to parse " + start + " into a valid date/time"; + var time; + if (start) { + time = this.parseDate(start); + } + else { + time = moment(); + } + if (time.isValid()) { + this.ooo_start = time; + retVal = "You " + (time.isBefore() ? 'are' : 'will be') + " marked Out of Office at " + time.calendar(); + } + return retVal; + }; + /** + * Set the end of the user's OOO + * + * @param string end A parsable date/time string + * @return string A response for the user + */ + OOO_User.prototype.setEnd = function (end) { + var retVal = "Unable to parse " + end + " into a valid date/time"; + var time; + if (end) { + time = this.parseDate(end); + } + else { + time = moment(); + } + if (time.isValid()) { + this.ooo_end = time; + if (time.isBefore()) { + retVal = 'You are no longer marked Out of Office'; + } + else { + if (!this.ooo_start) { + // Set the start time to now + this.ooo_start = moment(); + } + retVal = "You are marked Out of Office returning on " + time.calendar(); + } + } + return retVal; + }; + /** + * Parse a string into a moment date. + * + * @param string strDate The date string + * @return Moment + */ + OOO_User.prototype.parseDate = function (strDate) { + var pDate = chrono.parseDate(strDate); + return pDate ? moment(pDate) : moment.invalid(); + }; + /** + * Parse any commands and their values from a message. + * + * @param string message The raw message + * @return string[] + */ + OOO_User.prototype.parseCommands = function (message) { + var retVal = {}; + var splits = message.split(/(start:|end:|message:)/); + var curCommand; + for (var x in splits) { + switch (splits[x].toLowerCase()) { + case 'message:': + case 'start:': + case 'end:': + curCommand = splits[x].toLowerCase().replace(':', ''); + break; + default: + if (curCommand) { + retVal[curCommand] = splits[x].trim(); + } + } + } + return retVal; + }; + /** + * Return some help flavor text. + * + * @return string + */ + OOO_User.prototype.getHelp = function () { + var retVal = ''; + retVal = '*Out of Office Bot*\n\n'; + retVal += 'I can keep track of when you are out of the office and tell people that mention you.\n\n'; + retVal += '*Usage*:\n'; + retVal += 'To set yourself out of office, say hello and follow my prompts!\n'; + retVal += 'To return to the office once you are back, say hello again!\n\n'; + retVal += '*Direct Commands:*\n'; + retVal += '- message: _string_, To set your Out of Office message\n'; + retVal += ' Example: `message: I am out of the office`\n'; + retVal += '- start: _string_, A parsable date/time string when your Out of Office begins\n'; + retVal += ' Example: `start: 2015-06-06 8:00`\n'; + retVal += '- end: _string_, A parsable date/time string when your Out of Office ends\n'; + retVal += ' Example: `end: 2015-06-06 16:00`\n'; + return retVal; + }; /** * Handle a direct message to the bot * @@ -42,12 +172,27 @@ var OOO_User = (function () { */ OOO_User.prototype.handleMessage = function (message) { var retVal = ''; - if (message.match(/help/i)) { - retVal = '*Out of Office Bot*\n\n'; - retVal += 'I can keep track of when you are out of the office and tell people that mention you.\n\n'; - retVal += '*Usage*:\n'; - retVal += 'To set yourself out of office, say hello and follow my prompts!\n'; - retVal += 'To return to the office once you are back, say hello again!'; + var commands = this.parseCommands(message); + if (message.match(/^help/i)) { + retVal = this.getHelp(); + } + else if (Object.keys(commands).length) { + for (var command in commands) { + switch (command) { + case this.COMMAND_MESSAGE: + retVal += "-" + this.setMessage(commands[command]) + "\n"; + break; + case this.COMMAND_START: + retVal += "-" + this.setStart(commands[command]) + "\n"; + break; + case this.COMMAND_END: + retVal += "-" + this.setEnd(commands[command]) + "\n"; + break; + default: + retVal += "-Error: Unknown comand: " + command + "\n"; + logger.error("Unknown command: " + command); + } + } } else { switch (this.status) { @@ -60,8 +205,8 @@ var OOO_User = (function () { case this.STATUS_AWAITING_CONFIRMATION: if (message.match(this.POSITIVE_REGEXP)) { this.status = this.STATUS_AWAITING_MESSAGE; - this.ooo_start = new Date(); - retVal = "Sweet. You are now marked Out of Office with no message.\n"; + this.setStart(); + retVal = "Sweet. You are now marked Out of Office starting now with no message.\n"; retVal += "If you would like to set your Out of Office message, send it to me now"; } else if (message.match(this.NEGATIVE_REGEXP)) { @@ -70,10 +215,9 @@ var OOO_User = (function () { } break; case this.STATUS_AWAITING_MESSAGE: - if ((new Date().getTime() - this.last_communication.getTime()) < this.MESSAGE_TIMEOUT) { - this.message = message; + if (this.lastCommunication() < this.MESSAGE_TIMEOUT) { this.status = this.STATUS_REGISTERED; - retVal = "Setting your OOO Message to:\n" + message; + retVal = this.setMessage(message); } else { // set status to registered and handle it again @@ -86,7 +230,7 @@ var OOO_User = (function () { this.status = this.STATUS_AWAITING_DEREGISTER; break; case this.STATUS_AWAITING_DEREGISTER: - if ((new Date().getTime() - this.last_communication.getTime()) < this.MESSAGE_TIMEOUT) { + if (this.lastCommunication() < this.MESSAGE_TIMEOUT) { if (message.match(this.POSITIVE_REGEXP)) { this.status = this.STATUS_UNCONFIRMED; this.ooo_start = null; @@ -107,7 +251,7 @@ var OOO_User = (function () { this.status = this.STATUS_UNCONFIRMED; } } - this.last_communication = new Date(); + this.last_communication = moment(); return retVal; }; return OOO_User; diff --git a/release/js/spec/ooo_user.spec.js b/release/js/spec/ooo_user.spec.js index 1b3ea24..e911702 100644 --- a/release/js/spec/ooo_user.spec.js +++ b/release/js/spec/ooo_user.spec.js @@ -1,14 +1,87 @@ /// <reference path="../typings/tsd.d.ts" /> var OOO_User = require('../lib/ooo_user'); +var moment = require('moment'); describe('OOO_User', function () { it('should instantiate and set username', function () { var user = new OOO_User('foo'); expect(user.username).toEqual('foo'); expect(user.status).toEqual(user.STATUS_UNCONFIRMED); }); + describe('Date Handling', function () { + var user; + var now = moment(); + beforeEach(function () { + user = new OOO_User('foo'); + }); + it('should parse a date', function () { + expect(user.parseDate('2015-03-03').isValid()).toBeTruthy(); + expect(user.parseDate('Next Tuesday').isValid()).toBeTruthy(); + expect(user.parseDate('Yesterday').isValid()).toBeTruthy(); + expect(user.parseDate('3pm').isValid()).toBeTruthy(); + }); + it('should show ms since last communication', function () { + user.last_communication = moment().subtract(10, 'minutes'); + expect(user.lastCommunication()).toBeGreaterThan(0); + }); + describe('Out of Office Start', function () { + it('should set the time to now if no string passed', function () { + user.setStart(); + expect(moment.isMoment(user.ooo_start)).toBeTruthy(); + }); + it('should set the time to the time specified', function () { + var strTime = '1982-05-20'; + var momentTime = user.parseDate(strTime); + user.setStart(strTime); + expect(user.ooo_start.isSame(momentTime)).toBeTruthy(); + }); + }); + describe('Out of Office End', function () { + it('should set the time to now if no string passed', function () { + user.setEnd(); + expect(moment.isMoment(user.ooo_end)).toBeTruthy(); + }); + it('should set the time to the time specified', function () { + var strTime = '1982-05-20'; + var momentTime = user.parseDate(strTime); + user.setEnd(strTime); + expect(user.ooo_end.isSame(momentTime)).toBeTruthy(); + }); + }); + }); + describe('Command Parsing', function () { + var user; + beforeEach(function () { + user = new OOO_User('foo'); + }); + it('should return help info', function () { + expect(user.handleMessage('help')).toEqual(user.getHelp()); + }); + it('should parse the start command', function () { + expect(user.parseCommands('start: foo')).toEqual({ + start: 'foo' + }); + }); + it('should parse the end command', function () { + expect(user.parseCommands('end: foo')).toEqual({ + end: 'foo' + }); + }); + it('should parse the end command', function () { + expect(user.parseCommands('message: foo')).toEqual({ + message: 'foo' + }); + }); + it('should parse multiple commands', function () { + expect(user.parseCommands('message: foo bar fizz buzz start: s end: e')).toEqual({ + message: 'foo bar fizz buzz', + start: 's', + end: 'e' + }); + }); + }); describe('Out of Office Flagging', function () { var user; - var now = new Date(); + var now = moment(); beforeEach(function () { user = new OOO_User('foo'); }); @@ -16,15 +89,15 @@ describe('OOO_User', function () { expect(user.isOOO()).toBeFalsy(); }); it('should be out of office if start date is less than now', function () { - user.ooo_start = new Date(); - user.ooo_start.setMinutes(now.getMinutes() - 10); + user.setStart(); + user.ooo_start.subtract(10, 'minutes'); expect(user.isOOO()).toBeTruthy(); }); it('should not be out of office if end date is less than now', function () { - user.ooo_start = new Date(); - user.ooo_start.setMinutes(now.getMinutes() - 10); - user.ooo_end = new Date(); - user.ooo_end.setMinutes(now.getMinutes() - 5); + user.setStart(); + user.ooo_start.subtract(10, 'minutes'); + user.setEnd(); + user.ooo_end.subtract(5, 'minutes'); expect(user.isOOO()).toBeFalsy(); }); }); @@ -33,12 +106,39 @@ describe('OOO_User', function () { beforeEach(function () { user = new OOO_User('foo'); }); + it('should react to the message command', function () { + spyOn(user, 'setMessage').and.callThrough(); + user.handleMessage('message: foo'); + expect(user.setMessage).toHaveBeenCalled(); + expect(user.message).toEqual('foo'); + }); + it('should react to the start command', function () { + spyOn(user, 'setStart').and.callThrough(); + user.handleMessage('start: 2015-05-20'); + expect(user.setStart).toHaveBeenCalled(); + expect(moment.isMoment(user.ooo_start)).toBeTruthy(); + }); + it('should react to the end command', function () { + spyOn(user, 'setEnd').and.callThrough(); + user.handleMessage('end: 2015-05-20'); + expect(user.setEnd).toHaveBeenCalled(); + expect(moment.isMoment(user.ooo_end)).toBeTruthy(); + }); + it('should react to multiple commands', function () { + spyOn(user, 'setMessage').and.callThrough(); + spyOn(user, 'setStart').and.callThrough(); + spyOn(user, 'setEnd').and.callThrough(); + user.handleMessage('message: foo bar fizz buzz start: s end: e'); + expect(user.setMessage).toHaveBeenCalled(); + expect(user.setStart).toHaveBeenCalled(); + expect(user.setEnd).toHaveBeenCalled(); + }); it('should set the last communication time after each message', function () { - user.last_communication.setMinutes(user.last_communication.getMinutes() - 10); + user.last_communication.subtract(10, 'minutes'); var last_comm = user.last_communication; user.handleMessage('foo'); expect(typeof user.last_communication).toEqual('object'); - expect(user.last_communication.getTime()).toBeGreaterThan(last_comm.getTime()); + expect(user.last_communication.isAfter(last_comm)).toBeTruthy(); }); it('should transition from unconfirmed to awaiting confirmation', function () { expect(user.status).toEqual(user.STATUS_UNCONFIRMED); @@ -58,7 +158,7 @@ describe('OOO_User', function () { }); it('should transition from awaiting message to registered after receiveing a message', function () { var message = 'This is my message'; - user.last_communication = new Date(); + user.last_communication = moment(); user.status = user.STATUS_AWAITING_MESSAGE; user.handleMessage(message); expect(user.status).toEqual(user.STATUS_REGISTERED); @@ -66,8 +166,7 @@ describe('OOO_User', function () { }); it('should not accept a message after the timeout', function () { var message = 'This is my message'; - var aWhileAgo = new Date(); - aWhileAgo.setMinutes(aWhileAgo.getMinutes() - 10); + var aWhileAgo = moment().subtract(10, 'minutes'); user.status = user.STATUS_AWAITING_MESSAGE; user.last_communication = aWhileAgo; user.handleMessage(message); diff --git a/src/lib/ooo_user.ts b/src/lib/ooo_user.ts index 54ecdcc..bdf490c 100644 --- a/src/lib/ooo_user.ts +++ b/src/lib/ooo_user.ts @@ -1,6 +1,8 @@ /// <reference path="../typings/tsd.d.ts" /> import logger = require('./logger'); +import moment = require('moment'); +var chrono = require('chrono-node'); class OOO_User { STATUS_UNCONFIRMED = 'unconfirmed'; @@ -9,17 +11,21 @@ class OOO_User { STATUS_REGISTERED = 'registered'; STATUS_AWAITING_DEREGISTER = 'awaiting_deregister'; + COMMAND_MESSAGE = 'message'; + COMMAND_START = 'start'; + COMMAND_END = 'end'; + status: string = this.STATUS_UNCONFIRMED; message: string; - ooo_start: Date; + ooo_start: moment.Moment; - ooo_end: Date; + ooo_end: moment.Moment; - last_communication: Date; + last_communication: moment.Moment; - last_response: Date; + last_response: { [channel: string]: moment.Moment }; MESSAGE_TIMEOUT = 60000; // Five minutes DEREGISTER_TIMEOUT = 60000; // Five minutes @@ -33,7 +39,7 @@ class OOO_User { * @param string username the name of the user */ constructor (public username: string) { - this.last_communication = new Date(); + this.last_communication = moment(); } /** @@ -44,15 +50,156 @@ class OOO_User { isOOO (): boolean { var retVal = false; - var now = new Date(); - retVal = (this.ooo_start && this.ooo_start < now); + var now = moment(); + retVal = (this.ooo_start && this.ooo_start.isBefore(now)); if (this.ooo_end) { - retVal = retVal && (this.ooo_end > now); + retVal = retVal && (this.ooo_end.isAfter(now)); + } + + return retVal; + } + + /** + * Gets the ms since last communication + * + * @return integer + */ + lastCommunication (): number { + return this.last_communication ? moment().diff(this.last_communication) : 0; + } + + /** + * Set the user's OOO message and return a response + * + * @param string message The message to set + * @return string A response for the user + */ + setMessage (message: string): string { + this.message = message; + + return `Setting your OOO Message to:\n${message}`; + } + + /** + * Set the start of the user's OOO + * + * @param string start A parsable date/time string + * @return string A response for the user + */ + setStart (start?: string): string { + var retVal = `Unable to parse ${start} into a valid date/time`; + var time: moment.Moment; + + if (start) { + time = this.parseDate(start); + } else { + time = moment(); + } + + if (time.isValid()) { + this.ooo_start = time; + retVal = `You ${time.isBefore() ? 'are' : 'will be'} marked Out of Office at ${time.calendar()}`; } return retVal; } + /** + * Set the end of the user's OOO + * + * @param string end A parsable date/time string + * @return string A response for the user + */ + setEnd(end?: string): string { + var retVal = `Unable to parse ${end} into a valid date/time`; + var time: moment.Moment; + + if (end) { + time = this.parseDate(end); + } else { + time = moment(); + } + + if (time.isValid()) { + this.ooo_end = time; + if (time.isBefore()) { + retVal = 'You are no longer marked Out of Office'; + } else { + if (!this.ooo_start) { + // Set the start time to now + this.ooo_start = moment(); + } + retVal = `You are marked Out of Office returning on ${time.calendar()}`; + } + } + + return retVal; + } + + /** + * Parse a string into a moment date. + * + * @param string strDate The date string + * @return Moment + */ + parseDate (strDate: string): moment.Moment { + var pDate = chrono.parseDate(strDate); + return pDate ? moment(pDate) : moment.invalid(); + } + + /** + * Parse any commands and their values from a message. + * + * @param string message The raw message + * @return string[] + */ + parseCommands (message: string): { [command: string]: string } { + var retVal: { [command: string]: string } = {}; + + var splits = message.split(/(start:|end:|message:)/); + var curCommand: string; + + for (var x in splits) { + switch (splits[x].toLowerCase()) { + case 'message:': + case 'start:': + case 'end:': + curCommand = splits[x].toLowerCase().replace(':', ''); + break; + default: + if (curCommand) { + retVal[curCommand] = splits[x].trim(); + } + } + } + + return retVal; + } + + /** + * Return some help flavor text. + * + * @return string + */ + getHelp (): string { + var retVal = ''; + + retVal = '*Out of Office Bot*\n\n'; + retVal += 'I can keep track of when you are out of the office and tell people that mention you.\n\n'; + retVal += '*Usage*:\n'; + retVal += 'To set yourself out of office, say hello and follow my prompts!\n'; + retVal += 'To return to the office once you are back, say hello again!\n\n'; + retVal += '*Direct Commands:*\n'; + retVal += '- message: _string_, To set your Out of Office message\n'; + retVal += ' Example: `message: I am out of the office`\n'; + retVal += '- start: _string_, A parsable date/time string when your Out of Office begins\n'; + retVal += ' Example: `start: 2015-06-06 8:00`\n'; + retVal += '- end: _string_, A parsable date/time string when your Out of Office ends\n'; + retVal += ' Example: `end: 2015-06-06 16:00`\n'; + + return retVal; + } + /** * Handle a direct message to the bot * @@ -61,13 +208,27 @@ class OOO_User { */ handleMessage (message: string): string { var retVal = ''; + var commands = this.parseCommands(message); - if (message.match(/help/i)) { - retVal = '*Out of Office Bot*\n\n'; - retVal += 'I can keep track of when you are out of the office and tell people that mention you.\n\n'; - retVal += '*Usage*:\n'; - retVal += 'To set yourself out of office, say hello and follow my prompts!\n'; - retVal += 'To return to the office once you are back, say hello again!'; + if (message.match(/^help/i)) { + retVal = this.getHelp(); + } else if (Object.keys(commands).length) { + for (var command in commands) { + switch (command) { + case this.COMMAND_MESSAGE: + retVal += `-${this.setMessage(commands[command])}\n`; + break; + case this.COMMAND_START: + retVal += `-${this.setStart(commands[command])}\n`; + break; + case this.COMMAND_END: + retVal += `-${this.setEnd(commands[command])}\n`; + break; + default: + retVal += `-Error: Unknown comand: ${command}\n`; + logger.error(`Unknown command: ${command}`); + } + } } else { switch (this.status) { case this.STATUS_UNCONFIRMED: @@ -78,24 +239,23 @@ class OOO_User { break; case this.STATUS_AWAITING_CONFIRMATION: if (message.match(this.POSITIVE_REGEXP)) { - this.status = this.STATUS_AWAITING_MESSAGE; - this.ooo_start = new Date(); - retVal = "Sweet. You are now marked Out of Office with no message.\n"; - retVal += "If you would like to set your Out of Office message, send it to me now"; + this.status = this.STATUS_AWAITING_MESSAGE; + this.setStart(); + retVal = "Sweet. You are now marked Out of Office starting now with no message.\n"; + retVal += "If you would like to set your Out of Office message, send it to me now"; } else if (message.match(this.NEGATIVE_REGEXP)) { - this.status = this.STATUS_UNCONFIRMED; - retVal = `Fine. Be that way`; + this.status = this.STATUS_UNCONFIRMED; + retVal = `Fine. Be that way`; } break; case this.STATUS_AWAITING_MESSAGE: - if ((new Date().getTime() - this.last_communication.getTime()) < this.MESSAGE_TIMEOUT) { - this.message = message; - this.status = this.STATUS_REGISTERED; - retVal = `Setting your OOO Message to:\n${message}`; + if (this.lastCommunication() < this.MESSAGE_TIMEOUT) { + this.status = this.STATUS_REGISTERED; + retVal = this.setMessage(message); } else { - // set status to registered and handle it again - this.status = this.STATUS_REGISTERED; - retVal = this.handleMessage(message); + // set status to registered and handle it again + this.status = this.STATUS_REGISTERED; + retVal = this.handleMessage(message); } break; case this.STATUS_REGISTERED: @@ -103,18 +263,18 @@ class OOO_User { this.status = this.STATUS_AWAITING_DEREGISTER; break; case this.STATUS_AWAITING_DEREGISTER: - if ((new Date().getTime() - this.last_communication.getTime()) < this.MESSAGE_TIMEOUT) { - if (message.match(this.POSITIVE_REGEXP)) { - this.status = this.STATUS_UNCONFIRMED; - this.ooo_start = null; - this.ooo_end = null; - retVal = `Welcome back! You are no longer marked as out of the office.`; - } else if (message.match(this.NEGATIVE_REGEXP)) { - this.status = this.STATUS_REGISTERED; - retVal = `Ok, then get out of here!`; - } + if (this.lastCommunication() < this.MESSAGE_TIMEOUT) { + if (message.match(this.POSITIVE_REGEXP)) { + this.status = this.STATUS_UNCONFIRMED; + this.ooo_start = null; + this.ooo_end = null; + retVal = `Welcome back! You are no longer marked as out of the office.`; + } else if (message.match(this.NEGATIVE_REGEXP)) { + this.status = this.STATUS_REGISTERED; + retVal = `Ok, then get out of here!`; + } } else { - retVal = "I haven't heard from you in a while? Are you trying to return to the office? [Yes/No]"; + retVal = "I haven't heard from you in a while? Are you trying to return to the office? [Yes/No]"; } break; @@ -124,7 +284,7 @@ class OOO_User { } } - this.last_communication = new Date(); + this.last_communication = moment(); return retVal; } diff --git a/src/spec/ooo_user.spec.ts b/src/spec/ooo_user.spec.ts index 4b68975..5eb095f 100644 --- a/src/spec/ooo_user.spec.ts +++ b/src/spec/ooo_user.spec.ts @@ -1,6 +1,7 @@ /// <reference path="../typings/tsd.d.ts" /> import OOO_User = require('../lib/ooo_user'); +import moment = require('moment'); describe ('OOO_User', () => { it('should instantiate and set username', () => { @@ -9,9 +10,97 @@ describe ('OOO_User', () => { expect(user.status).toEqual(user.STATUS_UNCONFIRMED); }) + describe('Date Handling', () => { + var user: OOO_User; + var now = moment(); + + beforeEach(() => { + user = new OOO_User('foo'); + }) + + it('should parse a date', () => { + expect(user.parseDate('2015-03-03').isValid()).toBeTruthy(); + expect(user.parseDate('Next Tuesday').isValid()).toBeTruthy(); + expect(user.parseDate('Yesterday').isValid()).toBeTruthy(); + expect(user.parseDate('3pm').isValid()).toBeTruthy(); + expect(user.parseDate('derp').isValid()).toBeFalsy(); + }) + + it('should show ms since last communication', () => { + user.last_communication = moment().subtract(10, 'minutes'); + expect(user.lastCommunication()).toBeGreaterThan(0); + }) + + describe('Out of Office Start', () => { + it('should set the time to now if no string passed', () => { + user.setStart(); + expect(moment.isMoment(user.ooo_start)).toBeTruthy(); + }) + + it('should set the time to the time specified', () => { + var strTime = '1982-05-20'; + var momentTime = user.parseDate(strTime); + user.setStart(strTime); + expect(user.ooo_start.isSame(momentTime)).toBeTruthy(); + }) + }) + + describe('Out of Office End', () => { + it('should set the time to now if no string passed', () => { + user.setEnd(); + expect(moment.isMoment(user.ooo_end)).toBeTruthy(); + }) + + it('should set the time to the time specified', () => { + var strTime = '1982-05-20'; + var momentTime = user.parseDate(strTime); + user.setEnd(strTime); + expect(user.ooo_end.isSame(momentTime)).toBeTruthy(); + }) + }) + }); + + describe('Command Parsing', () => { + var user: OOO_User; + + beforeEach(() => { + user = new OOO_User('foo'); + }) + + it('should return help info', () => { + expect(user.handleMessage('help')).toEqual(user.getHelp()); + }) + + it('should parse the start command', () => { + expect(user.parseCommands('start: foo')).toEqual({ + start: 'foo' + }) + }) + + it('should parse the end command', () => { + expect(user.parseCommands('end: foo')).toEqual({ + end: 'foo' + }) + }) + + it('should parse the end command', () => { + expect(user.parseCommands('message: foo')).toEqual({ + message: 'foo' + }) + }) + + it('should parse multiple commands', () => { + expect(user.parseCommands('message: foo bar fizz buzz start: s end: e')).toEqual({ + message: 'foo bar fizz buzz', + start: 's', + end: 'e' + }) + }) + }) + describe('Out of Office Flagging', () => { var user: OOO_User; - var now = new Date(); + var now = moment(); beforeEach(() => { user = new OOO_User('foo'); @@ -22,18 +111,18 @@ describe ('OOO_User', () => { }) it('should be out of office if start date is less than now', () => { - user.ooo_start = new Date(); - user.ooo_start.setMinutes(now.getMinutes() - 10); + user.setStart(); + user.ooo_start.subtract(10, 'minutes'); expect(user.isOOO()).toBeTruthy(); }) it('should not be out of office if end date is less than now', () => { - user.ooo_start = new Date(); - user.ooo_start.setMinutes(now.getMinutes() - 10); + user.setStart(); + user.ooo_start.subtract(10, 'minutes'); - user.ooo_end = new Date(); - user.ooo_end.setMinutes(now.getMinutes() - 5); + user.setEnd(); + user.ooo_end.subtract(5, 'minutes'); expect(user.isOOO()).toBeFalsy(); }) @@ -46,13 +135,45 @@ describe ('OOO_User', () => { user = new OOO_User('foo'); }) + it('should react to the message command', () => { + spyOn(user, 'setMessage').and.callThrough(); + user.handleMessage('message: foo'); + expect(user.setMessage).toHaveBeenCalled(); + expect(user.message).toEqual('foo'); + }) + + it('should react to the start command', () => { + spyOn(user, 'setStart').and.callThrough(); + user.handleMessage('start: 2015-05-20'); + expect(user.setStart).toHaveBeenCalled(); + expect(moment.isMoment(user.ooo_start)).toBeTruthy(); + }) + + it('should react to the end command', () => { + spyOn(user, 'setEnd').and.callThrough(); + user.handleMessage('end: 2015-05-20'); + expect(user.setEnd).toHaveBeenCalled(); + expect(moment.isMoment(user.ooo_end)).toBeTruthy(); + }) + + it('should react to multiple commands', () => { + spyOn(user, 'setMessage').and.callThrough(); + spyOn(user, 'setStart').and.callThrough(); + spyOn(user, 'setEnd').and.callThrough(); + + user.handleMessage('message: foo bar fizz buzz start: s end: e'); + expect(user.setMessage).toHaveBeenCalled(); + expect(user.setStart).toHaveBeenCalled(); + expect(user.setEnd).toHaveBeenCalled(); + }) + it('should set the last communication time after each message', () => { - user.last_communication.setMinutes(user.last_communication.getMinutes() - 10); + user.last_communication.subtract(10, 'minutes'); var last_comm = user.last_communication user.handleMessage('foo'); expect(typeof user.last_communication).toEqual('object'); - expect(user.last_communication.getTime()).toBeGreaterThan(last_comm.getTime()); + expect(user.last_communication.isAfter(last_comm)).toBeTruthy(); }) it('should transition from unconfirmed to awaiting confirmation', () => { @@ -77,7 +198,7 @@ describe ('OOO_User', () => { it('should transition from awaiting message to registered after receiveing a message', () => { var message = 'This is my message'; - user.last_communication = new Date(); + user.last_communication = moment(); user.status = user.STATUS_AWAITING_MESSAGE; user.handleMessage(message); @@ -87,8 +208,7 @@ describe ('OOO_User', () => { it('should not accept a message after the timeout', () => { var message = 'This is my message'; - var aWhileAgo = new Date(); - aWhileAgo.setMinutes(aWhileAgo.getMinutes() - 10); + var aWhileAgo = moment().subtract(10, 'minutes'); user.status = user.STATUS_AWAITING_MESSAGE; user.last_communication = aWhileAgo; -- GitLab