diff --git a/.travis.yml b/.travis.yml index 87c1ae9..5c15b9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,4 +2,5 @@ language: node_js node_js: - stable before_install: -- openssl aes-256-cbc -K $encrypted_8216d40451d0_key -iv $encrypted_8216d40451d0_iv -in config.json.enc -out config.json -d +- openssl aes-256-cbc -K $encrypted_8216d40451d0_key -iv $encrypted_8216d40451d0_iv + -in config.json.enc -out config.json -d diff --git a/config.json.enc b/config.json.enc index b2fee75..4dcfb47 100644 Binary files a/config.json.enc and b/config.json.enc differ diff --git a/src/commands/blame.js b/src/commands/blame.js index 96ed4e3..3f3bc14 100644 --- a/src/commands/blame.js +++ b/src/commands/blame.js @@ -6,7 +6,6 @@ import colors from 'irc-colors'; import moment from 'moment'; const log = debug('Blame'); -const SHOWTIMES_URL = `${config.showtimes.server}`; export class Blame extends Command { message(from, to, text) { @@ -14,89 +13,75 @@ export class Blame extends Command { const blame = text.match(blameRegex); if (blame) { - return new Promise((resolve, reject) => { - this.blameRequest(from, to, blame[1]).then(response => { - this.send(to, response); - return resolve(); - }, error => { - this.send(to, 'Error connecting to Deschtimes'); - log(`Error: ${error.message}`); - return reject(error); - }); + return this.blameRequest(from, to, blame[1]).then(response => { + this.send(to, response); + }, error => { + this.send(to, error.message); + log(`Error: ${error.message}`); + return error; }); } else if (text.trim().toLowerCase() === '.blame') { this.send(to, 'Please use: ".blame " (ie, `.blame bokumachi`)'); } + + // Needed for tests + return new Promise(resolve => resolve()); } - blameRequest(from, to, show, names = false) { + blameRequest(from, to, show, useNames = false) { log(`Blame request by ${from} in ${to} for ${show}`); - return new Promise((resolve, reject) => { - let uri = `${SHOWTIMES_URL}/blame.json?`; - uri += `irc=${encodeURIComponent(to)}`; - uri += `&show=${encodeURIComponent(show.trim())}`; - - fetch(uri).then(response => { - if (response.ok) { - response.json().then(data => { - let message = ''; - - if (data.message) { - message = data.message; - } else { - const updatedDate = moment(new Date(data.updated_at)); - const airDate = moment(new Date(data.air_date)); - const status = new Map(); - let job; - - data.status.forEach(staff => { - if (staff.finished) { - // Pending takes precedence - if (!status.get(staff.acronym)) { - status.set(staff.acronym, colors.bold.green(staff.acronym)); - } - } else { - status.set(staff.acronym, colors.bold.red(staff.acronym)); - - if (!job) { - if (names) { - job = staff.staff; - } else { - job = staff.position; - } - } - } - }); - - if (job === undefined) { - job = 'release'; - } - - message = `Ep ${data.episode} of ${data.name}`; - - if (updatedDate > airDate) { - message += ` is at ${job} (last update ${updatedDate.fromNow()}). `; - } else { - if (airDate > Date.now()) { - message += ' airs'; - } else { - message += ' aired'; - } - - message += ` ${airDate.fromNow()}. `; - } - - message += `[${[...status.values()].join(' ')}]`; - } - - resolve(message); - }); - } else { - response.json().then(data => reject(Error(data.message))); + let uri = `${config.showtimes.server}/blame.json?`; + uri += `irc=${encodeURIComponent(to)}`; + uri += `&show=${encodeURIComponent(show.trim())}`; + + return fetch(uri).then(response => { + if (response.ok) { + return response.json().then(data => this.createMessage(data, useNames)); + } + + return response.json().then(data => { + log(`Blame Request Error: ${data}`); + Error(data.message); + }); + }).catch(error => Error(error)); + } + + createMessage(json, useNames) { + if (json.message) { + return json.message; + } + + const updatedDate = moment(new Date(json.updated_at)); + const airDate = moment(new Date(json.air_date)); + const status = new Map(); + let job = 'release'; + + let message = `Ep ${json.episode} of ${json.name}`; + + json.status.forEach(staff => { + // Pending takes precedence + if (staff.finished && !status.has(staff.acronym)) { + status.set(staff.acronym, colors.bold.green(staff.acronym)); + } else { + status.set(staff.acronym, colors.bold.red(staff.acronym)); + + if (job === 'release') { + job = useNames ? staff.staff : staff.position; } - }).catch(error => reject(Error(error))); + } }); + + if (updatedDate > airDate) { + message += ` is at ${job} (last update ${updatedDate.fromNow()}). `; + } else { + message += airDate > Date.now() ? ' airs' : ' aired'; + message += ` ${airDate.fromNow()}. `; + } + + message += `[${[...status.values()].join(' ')}]`; + + return message; } help(from) { diff --git a/src/commands/youtube.js b/src/commands/youtube.js index 2393fa3..b766dd6 100644 --- a/src/commands/youtube.js +++ b/src/commands/youtube.js @@ -113,9 +113,7 @@ export class Youtube extends Command { log(`YouTube Search Request Error: ${data}`); return Error(`YouTube Search Request Error: ${data}`); }); - }).catch(error => { - return Error(error); - }); + }).catch(error => Error(error)); } help(from) { diff --git a/test/commands/test_blame.js b/test/commands/test_blame.js new file mode 100644 index 0000000..e7028a1 --- /dev/null +++ b/test/commands/test_blame.js @@ -0,0 +1,105 @@ +import 'babel-polyfill'; +import { describe, before, afterEach, it } from 'mocha'; +import assert from 'assert'; +import { Client } from '../helpers.js'; +import { Blame } from '../../src/commands/blame'; + +const client = new Client(); +const blame = new Blame(client); + +describe('Blame', () => { + describe('Triggers', () => { + afterEach(() => client.resetLog()); + + it('should respond to .blame trigger', () => { + return blame.message('Mocha', '#arx-7', '.blame').then(() => { + assert.notEqual(client.lastMessage, null); + }); + }); + + it('should respond to .blame trigger', () => { + return blame.message('Mocha', '#arx-7', '.blame aoty').then(() => { + assert.notEqual(client.lastMessage, null); + }); + }); + + it('should respond to .show trigger', () => { + return blame.message('Mocha', '#arx-7', '.show aoty').then(() => { + assert.notEqual(client.lastMessage, null); + }); + }); + + it('should not activate in middle of phrase', () => { + return blame.message('Mocha', '#arx-7', 'test .blame aoty').then(() => { + assert.equal(client.lastMessage, null); + }); + }); + + it('should be case insensitive', () => { + return blame.message('Mocha', '#arx-7', '.BLAME AOTY').then(() => { + assert.notEqual(client.lastMessage, null); + }); + }); + }); + + describe('General Usage', () => { + it('should respond in correct channel', () => { + return blame.message('Mocha', '#arx-7', '.blame AOTY').then(() => { + assert.equal(client.lastTarget, '#arx-7'); + }); + }); + + it('should respond with correct usage', () => { + return blame.message('Mocha', '#arx-7', '.blame').then(() => { + assert(client.lastMessage.startsWith('Please use:'), 'Incorrect usage information'); + }); + }); + }); + + // To avoid repeatedly spamming endpoint, we only hit the API once. The + // lastMessage contains the results; these tests don't need fresh results. + describe('Message Formatting', () => { + before(() => blame.message('Mocha', '#arx-7', '.blame aoty')); + + it('should should include show name', () => { + assert(client.lastMessage.includes("Desch's Slice of Life"), 'Show name not present'); + }); + + it('should include episode number', () => { + assert(client.lastMessage.includes('Ep 7'), 'Episode number not present'); + }); + + it('should use position acronym', () => { + assert(client.lastMessage.includes('TL'), 'TL acronym not present'); + assert(!client.lastMessage.includes('Translator'), 'Translator position present'); + }); + + it('should color completed positions green', () => { + assert(client.lastMessage.includes('\u0002QC'), 'QC not marked as complete'); + }); + + it('should color incompleted positions red', () => { + assert(client.lastMessage.includes('\u0002TL'), 'TL not marked as incomplete'); + }); + + it('should reduce multiple positions'); + + it('should indicate incomplete on mix jobs'); + + it('should not include staff names', () => { + assert(!client.lastMessage.includes('at Desch'), 'Staff name included'); + assert(!client.lastMessage.includes('Fyurie'), 'Staff name included'); + }); + + it('should include air date', () => { + let present = false; + present = client.lastMessage.includes('airs') || client.lastMessage.includes('aired'); + assert(present, 'Air date not present'); + }); + + // These tests require custom commands for test shows at different stages + it('should include last update'); + + it('should indicate release status'); + }); +}); diff --git a/test/test_config.json b/test/test_config.json index 351cca7..51d7338 100644 --- a/test/test_config.json +++ b/test/test_config.json @@ -6,10 +6,6 @@ "ssl": false, "userName": "chidori", "admins": ["Desch", "Jukey", "Aoi-chan"], - "showtimes": { - "server": "http://localhost:3000", - "key": "secretpassword" - }, "channels": [ { "name": "#arbalest",