From 528ab1fd6fca0672d372d76ac33bd0eeff9c68a8 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Wed, 30 Oct 2013 10:54:04 +0800 Subject: [PATCH 1/3] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=88=86=E7=BB=84API?= =?UTF-8?q?=E5=92=8C=E4=BA=8C=E7=BB=B4=E7=A0=81API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common.js | 119 ++++++++++++++++++++++++++++++++++++++++---- test/common.test.js | 58 +++++++++++++++++++++ 2 files changed, 168 insertions(+), 9 deletions(-) diff --git a/lib/common.js b/lib/common.js index 683bac1d..cb50e870 100644 --- a/lib/common.js +++ b/lib/common.js @@ -16,6 +16,17 @@ var wrapper = function (callback) { }; }; +var postJSON = function (data) { + return { + dataType: 'json', + type: 'POST', + data: data, + headers: { + 'Content-Type': 'application/json' + } + }; +}; + var API = function (appid, appsecret) { this.appid = appid; this.appsecret = appsecret; @@ -48,15 +59,7 @@ API.prototype.getAccessToken = function (callback) { */ API.prototype.createMenu = function (menu, callback) { var url = this.prefix + 'menu/create?access_token=' + this.token; - var args = { - dataType: 'json', - type: 'POST', - data: menu, - headers: { - 'Content-Type': 'application/json' - } - }; - urllib.request(url, args, wrapper(callback)); + urllib.request(url, postJSON(menu), wrapper(callback)); }; /** @@ -84,6 +87,104 @@ API.prototype.removeMenu = function (callback) { */ API.prototype.getQRCode = function (data, callback) { var url = this.prefix + 'qrcode/get?access_token=' + this.token; + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 创建临时二维码 + * @param {String} sceneId 场景ID + * @param {Number} expire 过期时间,单位秒。最大不超过1800 + * @param {Function} callback 回调函数 + */ +API.prototype.createTmpQRCode = function (sceneId, expire, callback) { + var url = this.prefix + 'qrcode/create?access_token=' + this.token; + var data = { + "expire_seconds": expire, + "action_name": "QR_SCENE", + "action_info": {"scene": {"scene_id": sceneId}} + }; + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 创建永久二维码 + * @param {String} sceneId 场景ID。ID不能大于1000 + * @param {Function} callback 回调函数 + */ +API.prototype.createLimitQRCode = function (sceneId, callback) { + var url = this.prefix + 'qrcode/create?access_token=' + this.token; + var data = { + "action_name": "QR_LIMIT_SCENE", + "action_info": {"scene": {"scene_id": sceneId}} + }; + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 生成显示二维码的链接 + * @param {String} ticket 二维码Ticket + */ +API.prototype.showQRCodeURL = function (ticket) { + return this.prefix + 'showqrcode?ticket=' + ticket; +}; + +// ## 分组API http://mp.weixin.qq.com/wiki/index.php?title=%E5%88%86%E7%BB%84%E7%AE%A1%E7%90%86%E6%8E%A5%E5%8F%A3 +/** + * 获取分组列表 + */ +API.prototype.getGroups = function (callback) { + // https://api.weixin.qq.com/cgi-bin/groups/get?access_token=ACCESS_TOKEN + var url = this.prefix + 'groups/get?access_token=' + this.token; + urllib.request(url, {dataType: 'json'}, wrapper(callback)); +}; + +/** + * 创建分组 + * @param {String} name 分组名字 + */ +API.prototype.createGroup = function (name, callback) { + // https://api.weixin.qq.com/cgi-bin/groups/create?access_token=ACCESS_TOKEN + // POST数据格式:json + // POST数据例子:{"group":{"name":"test"}} + var url = this.prefix + 'groups/create?access_token=' + this.token; + var data = { + "group": {"name": name} + }; + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 更新分组名字 + * @param {Number} id 分组ID + * @param {String} name 新的分组名字 + */ +API.prototype.updateGroup = function (id, name, callback) { + // http请求方式: POST(请使用https协议) + // https://api.weixin.qq.com/cgi-bin/groups/update?access_token=ACCESS_TOKEN + // POST数据格式:json + // POST数据例子:{"group":{"id":108,"name":"test2_modify2"}} + var url = this.prefix + 'groups/update?access_token=' + this.token; + var data = { + "group": {"id": id, "name": name} + }; + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 移动用户进分组 + * @param {String} openid 用户的openid + * @param {Number} groupId 分组ID + */ +API.prototype.moveUserToGroup = function (openid, groupId, callback) { + // http请求方式: POST(请使用https协议) + // https://api.weixin.qq.com/cgi-bin/groups/members/update?access_token=ACCESS_TOKEN + // POST数据格式:json + // POST数据例子:{"openid":"oDF3iYx0ro3_7jD4HFRDfrjdCM58","to_groupid":108} + var url = this.prefix + 'groups/update?access_token=' + this.token; + var data = { + "openid": openid, + "to_groupid": groupId + }; var args = { dataType: 'json', type: 'POST', diff --git a/test/common.test.js b/test/common.test.js index b6bb59c4..5ec451ae 100644 --- a/test/common.test.js +++ b/test/common.test.js @@ -106,6 +106,64 @@ describe('common.js', function () { done(); }); }); + + it('createTmpQRCode should not ok', function (done) { + api.createTmpQRCode(123, 1800, function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('createLimitQRCode should not ok', function (done) { + api.createLimitQRCode(123, function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('showQRCodeURL should not ok', function () { + api.showQRCodeURL('ticket').should.be.equal('https://api.weixin.qq.com/cgi-bin/showqrcode?ticket=ticket'); + }); + + it('getGroups should not ok', function (done) { + api.getGroups(function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('createGroup should not ok', function (done) { + api.createGroup('new group', function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('updateGroup should not ok', function (done) { + api.updateGroup(123, 'new group', function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('moveUserToGroup should not ok', function (done) { + api.moveUserToGroup('openid', 123, function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); }); describe('mock', function () { From 57e2c5650fb7abe7cfc6ba37948389398e594798 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Wed, 30 Oct 2013 11:46:30 +0800 Subject: [PATCH 2/3] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=AE=A2=E6=9C=8D?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=8E=A5=E5=8F=A3=E5=92=8C=E5=9F=BA=E6=9C=AC?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common.js | 175 ++++++++++++++++++++++++++++++++++++++++++-- test/common.test.js | 89 ++++++++++++++++++++++ 2 files changed, 257 insertions(+), 7 deletions(-) diff --git a/lib/common.js b/lib/common.js index cb50e870..1db732b7 100644 --- a/lib/common.js +++ b/lib/common.js @@ -185,15 +185,176 @@ API.prototype.moveUserToGroup = function (openid, groupId, callback) { "openid": openid, "to_groupid": groupId }; - var args = { - dataType: 'json', - type: 'POST', - data: data, - headers: { - 'Content-Type': 'application/json' + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 获取用户信息 + * @param {String} openid 用户的openid + */ +API.prototype.getUser = function (openid, callback) { + // https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID + var url = this.prefix + 'user/info?openid=' + openid + '&access_token=' + this.token; + urllib.request(url, {dataType: 'json'}, wrapper(callback)); +}; + +/** + * 获取关注者列表 + * 详细细节 http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96%E5%85%B3%E6%B3%A8%E8%80%85%E5%88%97%E8%A1%A8 + * @param {String} nextOpenid 调用一次之后,传递回来的nextOpenid。第一次获取时可不填 + */ +API.prototype.getFollowers = function (nextOpenid, callback) { + // https://api.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&next_openid=NEXT_OPENID + if (typeof nextOpenid === 'function') { + callback = nextOpenid; + nextOpenid = ''; + } + var url = this.prefix + 'user/info?next_openid=' + nextOpenid + '&access_token=' + this.token; + urllib.request(url, {dataType: 'json'}, wrapper(callback)); +}; + +// 客服消息 +// http请求方式: POST https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=ACCESS_TOKEN +/** + * 发送文字消息 + * @param {String} openid 用户的openid + * @param {String} text 发送的消息内容 + */ +API.prototype.sendText = function (openid, text, callback) { + // { + // "touser":"OPENID", + // "msgtype":"text", + // "text": + // { + // "content":"Hello World" + // } + // } + var url = this.prefix + 'message/custom/send?access_token=' + this.token; + var data = { + "touser": openid, + "msgtype": "text", + "text": { + "content": text } }; - urllib.request(url, args, wrapper(callback)); + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 发送图片消息 + * @param {String} openid 用户的openid + * @param {String} mediaId 媒体文件的ID + */ +API.prototype.sendImage = function (openid, mediaId, callback) { + // { + // "touser":"OPENID", + // "msgtype":"image", + // "image": + // { + // "media_id":"MEDIA_ID" + // } + // } + var url = this.prefix + 'message/custom/send?access_token=' + this.token; + var data = { + "touser": openid, + "msgtype":"image", + "image": { + "media_id": mediaId + } + }; + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 发送视频消息 + * @param {String} openid 用户的openid + * @param {String} mediaId 媒体文件的ID + * @param {String} thumbMediaId 缩略图文件的ID + */ +API.prototype.sendVideo = function (openid, mediaId, thumbMediaId, callback) { + // { + // "touser":"OPENID", + // "msgtype":"video", + // "image": + // { + // "media_id":"MEDIA_ID" + // "thumb_media_id":"THUMB_MEDIA_ID" + // } + // } + var url = this.prefix + 'message/custom/send?access_token=' + this.token; + var data = { + "touser": openid, + "msgtype":"video", + "image": { + "media_id": mediaId, + "thumb_media_id": thumbMediaId + } + }; + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 发送音乐消息 + * @param {String} openid 用户的openid + * @param {Object} music 音乐文件 + */ +API.prototype.sendMusic = function (openid, music, callback) { + // { + // "touser":"OPENID", + // "msgtype":"music", + // "music": + // { + // "title":"MUSIC_TITLE", // 可选 + // "description":"MUSIC_DESCRIPTION", // 可选 + // "musicurl":"MUSIC_URL", + // "hqmusicurl":"HQ_MUSIC_URL", + // "thumb_media_id":"THUMB_MEDIA_ID" + // } + // } + var url = this.prefix + 'message/custom/send?access_token=' + this.token; + var data = { + "touser": openid, + "msgtype":"music", + "music": music + }; + urllib.request(url, postJSON(data), wrapper(callback)); +}; + +/** + * 发送图文消息 + * @param {String} openid 用户的openid + * @param {Array} articles 图文列表 + */ +API.prototype.sendNews = function (openid, articles, callback) { + // { + // "touser":"OPENID", + // "msgtype":"news", + // "news":{ + // "articles": [ + // { + // "title":"Happy Day", + // "description":"Is Really A Happy Day", + // "url":"URL", + // "picurl":"PIC_URL" + // }, + // { + // "title":"Happy Day", + // "description":"Is Really A Happy Day", + // "url":"URL", + // "picurl":"PIC_URL" + // } + // ] + // } + // } + var url = this.prefix + 'message/custom/send?access_token=' + this.token; + var data = { + "touser": openid, + "msgtype":"news", + "news": { + "articles": articles + } + }; + urllib.request(url, postJSON(data), wrapper(callback)); }; module.exports = API; diff --git a/test/common.test.js b/test/common.test.js index 5ec451ae..2ab222c8 100644 --- a/test/common.test.js +++ b/test/common.test.js @@ -164,6 +164,95 @@ describe('common.js', function () { done(); }); }); + + it('getUser should not ok', function (done) { + api.getUser('openid', function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('getFollowers should not ok', function (done) { + api.getFollowers(function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('getFollowers with nextOpenId should not ok', function (done) { + api.getFollowers('openid', function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('sendText should not ok', function (done) { + api.sendText('openid', 'Hellow World', function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('sendImage should not ok', function (done) { + api.sendImage('openid', 'imageId', function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('sendVideo should not ok', function (done) { + api.sendVideo('openid', 'mediaId', 'thumbMediaId', function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('sendMusic should not ok', function (done) { + var music = { + "title":"MUSIC_TITLE", // 可选 + "description":"MUSIC_DESCRIPTION", // 可选 + "musicurl":"MUSIC_URL", + "hqmusicurl":"HQ_MUSIC_URL", + "thumb_media_id":"THUMB_MEDIA_ID" + }; + + api.sendMusic('openid', music, function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + + it('sendNews should not ok', function (done) { + var articles = [ + { + "title":"Happy Day", + "description":"Is Really A Happy Day", + "url":"URL", + "picurl":"PIC_URL" + } + ]; + + api.sendNews('openid', articles, function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); }); describe('mock', function () { From ae56f2f08644efe9ce1cd138ba93d099b3069f48 Mon Sep 17 00:00:00 2001 From: Jackson Tian Date: Wed, 30 Oct 2013 14:24:15 +0800 Subject: [PATCH 3/3] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=AA=92=E4=BD=93?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=B8=8B=E8=BD=BD=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common.js | 75 +++++++++++++++++++++++ lib/wechat.js | 78 +++++++++++++++--------- package.json | 3 +- test/common.test.js | 9 +++ test/reply.test.js | 137 +++++++++++++++++++++++++++++++++++++++++++ test/wechat.test.js | 4 -- test/wechat2.test.js | 8 --- test/wechat3.test.js | 8 --- 8 files changed, 273 insertions(+), 49 deletions(-) create mode 100644 test/reply.test.js diff --git a/lib/common.js b/lib/common.js index 1db732b7..3b99c0e5 100644 --- a/lib/common.js +++ b/lib/common.js @@ -1,4 +1,5 @@ var urllib = require('urllib'); +var formstream = require('formstream'); // http://mp.weixin.qq.com/wiki/index.php?title=%E8%BF%94%E5%9B%9E%E7%A0%81%E8%AF%B4%E6%98%8E var wrapper = function (callback) { @@ -31,6 +32,7 @@ var API = function (appid, appsecret) { this.appid = appid; this.appsecret = appsecret; this.prefix = 'https://api.weixin.qq.com/cgi-bin/'; + this.fileServerPrefix = 'http://file.api.weixin.qq.com/cgi-bin/'; }; /** @@ -265,6 +267,31 @@ API.prototype.sendImage = function (openid, mediaId, callback) { urllib.request(url, postJSON(data), wrapper(callback)); }; +/** + * 发送语音消息 + * @param {String} openid 用户的openid + * @param {String} mediaId 媒体文件的ID + */ +API.prototype.sendVoice = function (openid, mediaId, callback) { + // { + // "touser":"OPENID", + // "msgtype":"image", + // "image": + // { + // "media_id":"MEDIA_ID" + // } + // } + var url = this.prefix + 'message/custom/send?access_token=' + this.token; + var data = { + "touser": openid, + "msgtype":"voice", + "image": { + "media_id": mediaId + } + }; + urllib.request(url, postJSON(data), wrapper(callback)); +}; + /** * 发送视频消息 * @param {String} openid 用户的openid @@ -357,4 +384,52 @@ API.prototype.sendNews = function (openid, articles, callback) { urllib.request(url, postJSON(data), wrapper(callback)); }; +// 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb) +['image', 'voice', 'video', 'thumb'].forEach(function (type) { + var method = 'upload' + type[0].toUpperCase() + type.substring(1); + API.prototype[method] = function (filepath, callback) { + var form = formstream(); + form.file('media', filepath); + form.headers({ + 'Content-Type': 'application/json' + }); + var url = this.fileServerPrefix + 'media/upload?access_token=' + this.token + '&type=' + type; + urllib.request(url, { + dataType: 'json', + type: 'POST', + headers: form.headers(), + stream: form + }, wrapper(callback)); + }; +}); + +/** + * 根据媒体ID获取媒体内容 + * @param {String} mediaId 媒体文件的ID + */ +API.prototype.getMedia = function (mediaId, callback) { + var url = this.fileServerPrefix + 'media/get?access_token=' + this.token + '&media_id=' + mediaId; + urllib.request(url, {}, wrapper(function (err, data, res) { + // 不用处理err,因为wrapper函数处理过 + var contentType = res.headers['content-type']; + if (contentType === 'application/json') { + var ret; + try { + ret = JSON.parse(data); + if (ret.errcode) { + err = new Error(ret.errmsg); + err.name = 'WeChatAPIError'; + } + } catch (ex) { + callback(ex, data, res); + return; + } + callback(err, ret, res); + } else { + // 输出Buffer对象 + callback(null, data, res); + } + })); +}; + module.exports = API; diff --git a/lib/wechat.js b/lib/wechat.js index 054b4555..0739b1fd 100644 --- a/lib/wechat.js +++ b/lib/wechat.js @@ -41,10 +41,22 @@ var tpl = ['', ']]>', ']]>', '', + '<% } else if (msgType === "voice") { %>', + '', + ']]>', + '', + '<% } else if (msgType === "image") { %>', + '', + ']]>', + '', + '<% } else if (msgType === "video") { %>', + '', '<% } else { %>', ']]>', '<% } %>', - '<%=funcFlag%>', ''].join(''); var compiled = ejs.compile(tpl); @@ -73,6 +85,29 @@ var formatMessage = function (result) { return message; }; +var reply = function (content, fromUsername, toUsername) { + var info = {}; + var type = 'text'; + info.content = content || ''; + if (Array.isArray(content)) { + type = 'news'; + } else if (typeof content === 'object') { + if (content.hasOwnProperty('type')) { + type = content.type; + info.content = content.content; + } else { + type = 'music'; + } + } else { + type = 'text'; + } + info.msgType = type; + info.createTime = new Date().getTime(); + info.toUsername = toUsername; + info.fromUsername = fromUsername; + return compiled(info); +}; + var respond = function (handler) { return function (req, res, next) { getMessage(req, function (err, result) { @@ -83,29 +118,9 @@ var respond = function (handler) { var message = formatMessage(result); var callback = handler.getHandler(message.MsgType); req.weixin = message; - res.reply = function (content, funcFlag) { - var info = {}; - var type = 'text'; - info.content = content || ''; - if (Array.isArray(content)) { - type = 'news'; - } else if (typeof content === 'object') { - if (content.type === 'text') { - type = 'text'; - info.content = content.content; - } else { - type = 'music'; - } - } else { - type = 'text'; - } - info.msgType = type; - info.createTime = new Date().getTime(); - info.funcFlag = funcFlag ? 1 : 0; - info.toUsername = message.FromUserName; - info.fromUsername = message.ToUserName; + res.reply = function (content) { res.writeHead(200); - res.end(compiled(info)); + res.end(reply(content, message.ToUserName, message.FromUserName)); }; var done = function () { @@ -198,7 +213,8 @@ Handler.prototype.getHandler = function (type) { next(); }; }; -['text', 'image', 'location', 'voice', 'link', 'event'].forEach(function (method) { + +['text', 'image', 'voice', 'video', 'location', 'link', 'event'].forEach(function (method) { Handler.prototype[method] = function (fn) { return this.setHandler(method, fn); }; @@ -268,15 +284,20 @@ var middleware = function (token, handle) { */ /** - * 位置推送处理 - * @param {Function} fn 处理位置推送的回调函数,接受参数为(location, req, res, next)。 + * 声音推送处理 + * @param {Function} fn 处理图片推送的回调函数,接受参数为(image, req, res, next)。 */ /** - * 声音推送处理 + * 视频推送处理 * @param {Function} fn 处理声音推送的回调函数,接受参数为(voice, req, res, next)。 */ +/** + * 位置推送处理 + * @param {Function} fn 处理位置推送的回调函数,接受参数为(location, req, res, next)。 + */ + /** * 链接推送处理 * @param {Function} fn 处理链接推送的回调函数,接受参数为(link, req, res, next)。 @@ -286,7 +307,7 @@ var middleware = function (token, handle) { * 事件推送处理 * @param {Function} fn 处理事件推送的回调函数,接受参数为(event, req, res, next)。 */ -['text', 'image', 'location', 'voice', 'link', 'event'].forEach(function (method) { +['text', 'image', 'voice', 'video', 'location', 'link', 'event'].forEach(function (method) { middleware[method] = function (fn) { return (new Handler())[method](fn); }; @@ -294,3 +315,4 @@ var middleware = function (token, handle) { module.exports = middleware; module.exports.toXML = compiled; +module.exports.reply = reply; diff --git a/package.json b/package.json index 39f9fb71..9aece58c 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,8 @@ "xml2js": "0.2.6", "ejs": ">=0.8.3", "bufferhelper": ">=0.2.0", - "urllib": ">=0.3.4" + "formstream": ">=0.0.7", + "urllib": ">=0.5.3" }, "devDependencies": { "supertest": "*", diff --git a/test/common.test.js b/test/common.test.js index 2ab222c8..db9e348c 100644 --- a/test/common.test.js +++ b/test/common.test.js @@ -210,6 +210,15 @@ describe('common.js', function () { }); }); + it('sendVoice should not ok', function (done) { + api.sendVoice('openid', 'imageId', function (err, data, res) { + should.exist(err); + err.name.should.be.equal('WeChatAPIError'); + err.message.should.be.equal('invalid credential'); + done(); + }); + }); + it('sendVideo should not ok', function (done) { api.sendVideo('openid', 'mediaId', 'thumbMediaId', function (err, data, res) { should.exist(err); diff --git a/test/reply.test.js b/test/reply.test.js new file mode 100644 index 00000000..ea6ff06e --- /dev/null +++ b/test/reply.test.js @@ -0,0 +1,137 @@ +require('should'); +var reply = require('../').reply; + +describe('wechat.js', function () { + describe('reply text', function () { + it('reply("text") should ok', function () { + var result = reply('hello world', 'from', 'to'); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + }); + + it('reply({type: "text", content: content}) should ok', function () { + var result = reply({type: 'text', content: 'hello world'}, 'from', 'to'); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + }); + }); + + describe('reply music', function () { + it('reply(object) should ok', function () { + var result = reply({ + title: "来段音乐吧", + description: "一无所有", + musicUrl: "http://mp3.com/xx.mp3", + hqMusicUrl: "http://mp3.com/xx.mp3" + }, 'from', 'to'); + result.should.be.include('<![CDATA[来段音乐吧]]>'); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + }); + + it('reply(object) with type should ok', function () { + var result = reply({ + type: "music", + content: { + title: "来段音乐吧", + description: "一无所有", + musicUrl: "http://mp3.com/xx.mp3", + hqMusicUrl: "http://mp3.com/xx.mp3" + } + }, 'from', 'to'); + result.should.be.include('<![CDATA[来段音乐吧]]>'); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + }); + }); + + describe('reply news', function () { + var news = [ + { + title: '你来我家接我吧', + description: '这是女神与高富帅之间的对话', + picurl: 'http://nodeapi.cloudfoundry.com/qrcode.jpg', + url: 'http://nodeapi.cloudfoundry.com/' + } + ]; + + it('reply(Array) should ok', function () { + var result = reply(news, 'from', 'to'); + result.should.be.include('1'); + result.should.be.include('<![CDATA[你来我家接我吧]]>'); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + }); + + it('reply({type: "news", content: news}) should ok', function () { + var result = reply({type: 'news', content: news}, 'from', 'to'); + result.should.be.include('1'); + result.should.be.include('<![CDATA[你来我家接我吧]]>'); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + }); + }); + + describe('reply image', function () { + var image = { + mediaId: 'mediaId' + }; + + it('reply({type: "image", content: image}) should ok', function () { + var result = reply({type: 'image', content: image}, 'from', 'to'); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + }); + }); + + describe('reply voice', function () { + var voice = { + mediaId: 'mediaId' + }; + + it('reply({type: "voice", content: voice}) should ok', function () { + var result = reply({type: 'voice', content: voice}, 'from', 'to'); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + }); + }); + + describe('reply video', function () { + var video = { + mediaId: 'mediaId', + thumbMediaId: 'thumbMediaId' + }; + + it('reply({type: "video", content: video}) should ok', function () { + var result = reply({type: 'video', content: video}, 'from', 'to'); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + result.should.be.include(''); + }); + }); +}); diff --git a/test/wechat.test.js b/test/wechat.test.js index 4b5a0df9..4fbed43b 100644 --- a/test/wechat.test.js +++ b/test/wechat.test.js @@ -137,7 +137,6 @@ describe('wechat.js', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -162,7 +161,6 @@ describe('wechat.js', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -191,7 +189,6 @@ describe('wechat.js', function () { body.should.include(''); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -221,7 +218,6 @@ describe('wechat.js', function () { body.should.include(']]>'); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); diff --git a/test/wechat2.test.js b/test/wechat2.test.js index b0ec5c6d..a3ca14a2 100644 --- a/test/wechat2.test.js +++ b/test/wechat2.test.js @@ -126,7 +126,6 @@ describe('wechat.js 0.2.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -155,7 +154,6 @@ describe('wechat.js 0.2.0', function () { body.should.include(''); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -180,7 +178,6 @@ describe('wechat.js 0.2.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -208,7 +205,6 @@ describe('wechat.js 0.2.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -234,7 +230,6 @@ describe('wechat.js 0.2.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -261,7 +256,6 @@ describe('wechat.js 0.2.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -289,7 +283,6 @@ describe('wechat.js 0.2.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -314,7 +307,6 @@ describe('wechat.js 0.2.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); diff --git a/test/wechat3.test.js b/test/wechat3.test.js index 844d7801..0dd0a923 100644 --- a/test/wechat3.test.js +++ b/test/wechat3.test.js @@ -124,7 +124,6 @@ describe('wechat.js 0.3.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -153,7 +152,6 @@ describe('wechat.js 0.3.0', function () { body.should.include(''); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -178,7 +176,6 @@ describe('wechat.js 0.3.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -206,7 +203,6 @@ describe('wechat.js 0.3.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -232,7 +228,6 @@ describe('wechat.js 0.3.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -259,7 +254,6 @@ describe('wechat.js 0.3.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -287,7 +281,6 @@ describe('wechat.js 0.3.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); }); @@ -312,7 +305,6 @@ describe('wechat.js 0.3.0', function () { body.should.match(/\d{13}<\/CreateTime>/); body.should.include(''); body.should.include(''); - body.should.include('0'); done(); }); });