diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..f381343 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,282 @@ +module.exports = { + "env": { + "node": true + }, + "extends": "eslint:recommended", + "rules": { + "accessor-pairs": "error", + "array-bracket-spacing": "error", + "array-callback-return": "error", + "arrow-body-style": "error", + "arrow-parens": "error", + "arrow-spacing": "error", + "block-scoped-var": "error", + "block-spacing": "error", + "brace-style": [ + "error", + "1tbs" + ], + "callback-return": "off", + "camelcase": "off", + "capitalized-comments": [ + "error", + "never" + ], + "class-methods-use-this": "error", + "comma-dangle": "error", + "comma-spacing": [ + "error", + { + "after": true, + "before": false + } + ], + "comma-style": [ + "error", + "last" + ], + "complexity": "error", + "computed-property-spacing": [ + "error", + "never" + ], + "consistent-return": "error", + "consistent-this": "off", + "curly": "error", + "default-case": "error", + "dot-location": "error", + "dot-notation": "error", + "eol-last": "error", + "eqeqeq": "off", + "func-call-spacing": "error", + "func-name-matching": "error", + "func-names": [ + "error", + "never" + ], + "func-style": [ + "error", + "declaration" + ], + "generator-star-spacing": "error", + "global-require": "off", + "guard-for-in": "error", + "handle-callback-err": "error", + "id-blacklist": "error", + "id-length": "off", + "id-match": "error", + "indent": "off", + "init-declarations": "error", + "jsx-quotes": "error", + "key-spacing": "error", + "keyword-spacing": [ + "error", + { + "after": true, + "before": true + } + ], + "line-comment-position": "error", + "linebreak-style": [ + "error", + "unix" + ], + "lines-around-comment": "error", + "lines-around-directive": "error", + "max-depth": "error", + "max-len": "off", + "max-lines": "error", + "max-nested-callbacks": "error", + "max-params": "error", + "max-statements": "off", + "max-statements-per-line": "error", + "multiline-ternary": [ + "error", + "never" + ], + "new-cap": "error", + "new-parens": "error", + "newline-after-var": "off", + "newline-before-return": "error", + "newline-per-chained-call": "off", + "no-alert": "error", + "no-array-constructor": "error", + "no-await-in-loop": "error", + "no-bitwise": "error", + "no-caller": "error", + "no-catch-shadow": "error", + "no-confusing-arrow": "error", + "no-continue": "error", + "no-div-regex": "error", + "no-duplicate-imports": "error", + "no-else-return": "error", + "no-empty": [ + "error", + { + "allowEmptyCatch": true + } + ], + "no-empty-function": "error", + "no-eq-null": "error", + "no-eval": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-label": "error", + "no-extra-parens": "off", + "no-floating-decimal": "error", + "no-implicit-globals": "error", + "no-implied-eval": "error", + "no-inline-comments": "error", + "no-inner-declarations": [ + "error", + "functions" + ], + "no-invalid-this": "error", + "no-iterator": "error", + "no-label-var": "error", + "no-labels": "error", + "no-lone-blocks": "error", + "no-lonely-if": "error", + "no-loop-func": "error", + "no-magic-numbers": "off", + "no-mixed-operators": "error", + "no-mixed-requires": "error", + "no-multi-assign": "error", + "no-multi-spaces": "error", + "no-multi-str": "error", + "no-multiple-empty-lines": "error", + "no-native-reassign": "error", + "no-negated-condition": "error", + "no-negated-in-lhs": "error", + "no-nested-ternary": "error", + "no-new": "error", + "no-new-func": "error", + "no-new-object": "error", + "no-new-require": "error", + "no-new-wrappers": "error", + "no-octal-escape": "error", + "no-param-reassign": "off", + "no-path-concat": "error", + "no-plusplus": "error", + "no-process-env": "off", + "no-process-exit": "error", + "no-proto": "error", + "no-prototype-builtins": "error", + "no-restricted-globals": "error", + "no-restricted-imports": "error", + "no-restricted-modules": "error", + "no-restricted-properties": "error", + "no-restricted-syntax": "error", + "no-return-assign": "error", + "no-return-await": "error", + "no-script-url": "error", + "no-self-compare": "error", + "no-sequences": "error", + "no-shadow": "error", + "no-shadow-restricted-names": "error", + "no-spaced-func": "error", + "no-sync": "off", + "no-tabs": "error", + "no-template-curly-in-string": "error", + "no-ternary": "off", + "no-throw-literal": "error", + "no-trailing-spaces": "error", + "no-undef-init": "error", + "no-undefined": "error", + "no-underscore-dangle": "error", + "no-unmodified-loop-condition": "error", + "no-unneeded-ternary": "error", + "no-unused-expressions": "error", + "no-use-before-define": "off", + "no-useless-call": "error", + "no-useless-computed-key": "error", + "no-useless-concat": "error", + "no-useless-constructor": "error", + "no-useless-escape": "error", + "no-useless-rename": "error", + "no-useless-return": "error", + "no-var": "off", + "no-void": "error", + "no-warning-comments": "error", + "no-whitespace-before-property": "error", + "no-with": "error", + "object-curly-newline": "error", + "object-curly-spacing": [ + "error", + "always" + ], + "object-property-newline": [ + "error", + { + "allowMultiplePropertiesPerLine": true + } + ], + "object-shorthand": "off", + "one-var": "off", + "one-var-declaration-per-line": "error", + "operator-assignment": [ + "error", + "always" + ], + "operator-linebreak": "error", + "padded-blocks": "off", + "prefer-arrow-callback": "off", + "prefer-const": "error", + "prefer-destructuring": [ + "error", + { + "array": false, + "object": false + } + ], + "prefer-numeric-literals": "error", + "prefer-promise-reject-errors": "error", + "prefer-reflect": "error", + "prefer-rest-params": "error", + "prefer-spread": "error", + "prefer-template": "off", + "quote-props": "off", + "quotes": [ + "error", + "single" + ], + "radix": "error", + "require-await": "error", + "require-jsdoc": "off", + "rest-spread-spacing": "error", + "semi": "error", + "semi-spacing": "error", + "sort-imports": "error", + "sort-keys": "off", + "sort-vars": "error", + "space-before-blocks": "error", + "space-before-function-paren": "off", + "space-in-parens": [ + "error", + "never" + ], + "space-infix-ops": "error", + "space-unary-ops": "off", + "spaced-comment": [ + "error", + "always" + ], + "strict": "error", + "symbol-description": "error", + "template-curly-spacing": "error", + "template-tag-spacing": "error", + "unicode-bom": [ + "error", + "never" + ], + "valid-jsdoc": "error", + "vars-on-top": "off", + "wrap-iife": "error", + "wrap-regex": "error", + "yield-star-spacing": "error", + "yoda": [ + "error", + "never" + ] + } +}; \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a83f838 --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ +SHELL = /bin/bash +export PATH := ./node_modules/.bin/:${PATH} + +MAKEFLAGS += --no-print-directory --silent + +clean: + rm -rf build/ \ + npm-debug.log +lint: + eslint lib/ +test: clean lint + mocha --reporter spec test/ $(MOCHAFLAGS) \ No newline at end of file diff --git a/lib/api.js b/lib/api.js index aac4c40..0fae9d4 100644 --- a/lib/api.js +++ b/lib/api.js @@ -1,18 +1,16 @@ 'use strict'; -var https = require('https'); var querystring = require('qs'); +var fs = require('fs'); +var path = require('path'); +var request = require('request'); -module.exports = TestingBot; function TestingBot(options) { this.options = options || {}; - this.options.api_key = process.env.TESTINGBOT_KEY || this.options.api_key || null; - this.options.api_secret = process.env.TESTINGBOT_SECRET || this.options.api_secret || null; + this.options.api_key = this.options.api_key || process.env.TESTINGBOT_KEY || null; + this.options.api_secret = this.options.api_secret || process.env.TESTINGBOT_SECRET || null; if (!this.options.api_key || !this.options.api_secret) { - var fs = require('fs'); - var path = require('path'); - try { var tbFile = path.join(process.env[(process.platform == 'win32') ? 'USERPROFILE' : 'HOME'], '.testingbot'); if (fs.statSync(tbFile).isFile() === true) { @@ -30,7 +28,7 @@ function TestingBot(options) { } TestingBot.prototype.getTestDetails = function(testID, callback) { - this.api({ + this.request({ method: 'GET', url: '/tests/' + testID }, callback); @@ -41,7 +39,7 @@ TestingBot.prototype.getBrowsers = function(callback, type) { if (type) { data.type = type; } - this.api({ + this.request({ method: 'GET', url: '/browsers', data: data @@ -49,21 +47,21 @@ TestingBot.prototype.getBrowsers = function(callback, type) { }; TestingBot.prototype.getLabTestDetails = function(testID, callback) { - this.api({ + this.request({ method: 'GET', url: '/lab/' + testID }, callback); }; TestingBot.prototype.getTunnel = function(callback) { - this.api({ + this.request({ method: 'GET', url: '/tunnel' }, callback); }; TestingBot.prototype.getUserInfo = function(callback) { - this.api({ + this.request({ method: 'GET', url: '/user' }, callback); @@ -76,7 +74,7 @@ TestingBot.prototype.getTests = function(callback, offset, limit) { if (!limit) { limit = 10; } - this.api({ + this.request({ method: 'GET', url: '/tests/', data: { offset: offset, limit: limit } @@ -90,7 +88,7 @@ TestingBot.prototype.getLabTests = function(callback, offset, limit) { if (!limit) { limit = 10; } - this.api({ + this.request({ method: 'GET', url: '/lab', data: { offset: offset, limit: limit } @@ -98,7 +96,7 @@ TestingBot.prototype.getLabTests = function(callback, offset, limit) { }; TestingBot.prototype.updateUserInfo = function(data, callback) { - this.api({ + this.request({ method: 'PUT', url: '/user', data: data @@ -106,7 +104,7 @@ TestingBot.prototype.updateUserInfo = function(data, callback) { }; TestingBot.prototype.updateTest = function(data, testID, callback) { - this.api({ + this.request({ method: 'PUT', url: '/tests/' + testID, data: data @@ -114,7 +112,7 @@ TestingBot.prototype.updateTest = function(data, testID, callback) { }; TestingBot.prototype.updateLabTest = function(data, testID, callback) { - this.api({ + this.request({ method: 'PUT', url: '/lab/' + testID, data: data @@ -122,72 +120,42 @@ TestingBot.prototype.updateLabTest = function(data, testID, callback) { }; TestingBot.prototype.deleteTest = function(testID, callback) { - this.api({ + this.request({ method: 'DELETE', url: '/tests/' + testID }, callback); }; TestingBot.prototype.deleteLabTest = function(testID, callback) { - this.api({ + this.request({ method: 'DELETE', url: '/lab/' + testID }, callback); }; -TestingBot.prototype.api = function(req_data, callback) { - - var self = this; - - var url = req_data.url; +TestingBot.prototype.request = function(req_data, callback) { + var requestPath = '/v1/' + req_data.url; if (req_data.method === 'GET' && req_data.data) { - var path = + '?' + querystring.stringify(req_data.data); + requestPath = requestPath + '?' + querystring.stringify(req_data.data); } - var req_options = { - host: 'api.testingbot.com', - port: 443, - path: '/v1/' + url, - method: req_data.method - }; - - req_options.headers = req_data.headers || {}; - req_options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; - req_options.headers['Content-length'] = - req_data.data ? querystring.stringify(req_data.data).length : 0; - - req_options.auth = this.options.api_key + ':' + this.options.api_secret; - - var req = https.request(req_options, function(res) { - res.setEncoding('utf8'); - var response = ''; - res.on('data', function(chunk) { - response += chunk; - }).on('end', function() { - if (typeof(callback) === 'function') { - try { - if (res.statusCode === 200) { - callback(null, JSON.parse(response)); - } else { - callback(JSON.parse(response), null); - } - } catch (err) { - callback(new Error('Couldnt parse ' + response), null); - } - } - }); - }); - - req.on('error', function(e) { - var response = 'error'; - if (typeof(callback) === 'function') { - callback(new Error(response), null); + request({ + method: req_data.method, + uri: 'https://api.testingbot.com' + requestPath, + auth: { + user: this.options.api_key, + pass: this.options.api_secret, + sendImmediately: true + } + }, function(error, response) { + var responseBody = null; + if (response.statusCode.toString().substring(0, 1) === '2') { + responseBody = JSON.parse(response.body); + } else { + error = JSON.parse(response.body); } + callback(error, responseBody); }); - - if (self.options.method !== 'GET' && req_data.data) { - // write data to request body - req.write(querystring.stringify(req_data.data)); - } - req.end(); }; + +module.exports = TestingBot; diff --git a/package.json b/package.json index 95cfd9f..63c4934 100644 --- a/package.json +++ b/package.json @@ -2,19 +2,30 @@ "author": "TestingBot (testingbot.com)", "name": "testingbot-api", "description": "A wrapper around TestingBot's REST API", - "version": "1.0.1", + "version": "1.0.2", + "scripts": { + "lint": "eslint .", + "test": "make test" + }, "homepage": "https://github.com/testingbot/testingbot-api", "repository": { "type": "git", "url": "git://github.com/testingbot/testingbot-api.git" }, "devDependencies": { - "mocha": "" + "eslint": "^3.15.0", + "eslint-config-airbnb-base": "^11.1.0", + "eslint-config-standard": "^6.2.1", + "eslint-plugin-import": "^2.2.0", + "eslint-plugin-promise": "^3.4.1", + "eslint-plugin-standard": "^2.0.1", + "mocha": "^3.1.2" }, "engines": { "node": "*" }, "dependencies": { - "qs": "" + "qs": "^6.3.1", + "request": "^2.79.0" } -} \ No newline at end of file +} diff --git a/test/api_test.js b/test/api_test.js index 2862030..b6efd30 100644 --- a/test/api_test.js +++ b/test/api_test.js @@ -1,115 +1,54 @@ 'use strict'; var TbApi = require('../lib/api.js'); +var assert = require('assert'); -module.exports = { - setUp: function(callback) { - var fs = require('fs'); - try { - var data = fs.readFileSync(process.env.HOME + '/.testingbot'); - if (data !== null) { - var arr = data.toString().replace('\n', '').split(':'); - var api_key = arr[0]; - var api_secret = arr[1]; - this.api = new TbApi({ api_key: api_key, api_secret: api_secret }); - callback(); - } - } catch (e) { - console.log('couldn\'t read $HOME/.testingbot'); - console.log(e); - } - }, - tearDown: function(callback) { - // clean up - callback(); - }, - testInfoUser: function(test) { - var userInfo = this.api.getUserInfo(function(err, response) { - test.ok(true, (typeof(response.first_name) === 'String')); - test.ok(true, (response.plan !== undefined)); - test.done(); - }); - }, - testWrongCredentials: function(test) { +describe('Api Tests', function() { + beforeEach(function(done) { + this.api = new TbApi(); + done(); + }); + + it('should get the userinfo for the current user', function(done) { + this.api.getUserInfo(function(err, response) { + assert.equal(true, (typeof(response.first_name) === 'string')); + assert.equal(true, (response.plan !== undefined)); + done(); + }); + }); + + it('should detect wrong credentials', function(done) { var api = new TbApi({api_key: 'bogus', api_secret: 'bogus'}); - var userInfo = api.getUserInfo(function(err, response) { - test.equal(null, response); - test.done(); - }); - }, - testUpdateInfoUser: function(test) { - var firstName = 'testing' + Math.round(Math.random() * 100); + api.getUserInfo(function(err, response) { + assert.equal(response, null); + assert.notEqual(err, null); + done(); + }); + }); - var data = { - 'user[first_name]' : firstName - }; - var that = this; - var userInfo = this.api.updateUserInfo(data, function(r) { - var userInfo = that.api.getUserInfo(function(err, response) { - test.equal(firstName, - response.first_name); - test.done(); - }); - }); - }, - testListTests: function(test) { - var list = this.api.getTests(function(err, response) { - test.ok(response.data && response.data.length > 0, true); - test.done(); - }); - }, - testInfoSpecificTest: function(test) { - var that = this; - var testInfo = this.api.getTests(function(err, response) { - test.ok(response.data && response.data.length > 0, true); - var singleTest = response.data[0]; + it('should list tests', function(done) { + this.api.getTests(function(err, response) { + assert.equal(response.data && response.data.length > 0, true); + done(); + }); + }); - that.api.getTestDetails(singleTest.id, - function(err, response) { - test.equal( - response.session_id, - singleTest.session_id); - test.done(); - }); - }); - }, - testInfoNotFoundTest: function(test) { - var notFound = this.api.getTestDetails(324234234324, function(err, response) { - test.equal(null, response); - test.done(); - }); - }, - testUpdateTest: function(test) { - var that = this; - var testInfo = this.api.getTests(function(err, response) { - test.ok(response.data && response.data.length > 0, true); - var singleTest = response.data[0]; + it('should error when not test is found', function(done) { + this.api.getTestDetails(324234234324, function(err, response) { + assert.equal(null, response); + assert.notEqual(err, null); + done(); + }); + }); - var newTestName = 'test' + Math.round(Math.random() * 100); - var newTestData = { - 'test[name]' : newTestName - }; - that.api.updateTest(newTestData, singleTest.id, - function(err, response) { - that.api.getTestDetails( - singleTest.id, - function(err, response) { - test.equal(response.name, - newTestName); - test.done(); - }); - }); - }); - }, - testUpdateTestNotFound: function(test) { - var newTestName = 'test' + Math.round(Math.random() * 100); - var newTestData = { - 'test[name]' : newTestName - }; - var update = this.api.updateTest(newTestData, - 324324234, - function(err, response) { - test.equal(null, response); - test.done(); - }); - } -}; \ No newline at end of file + it('should find a specific test', function(done) { + var that = this; + this.api.getTests(function(err, response) { + assert.equal(response.data && response.data.length > 0, true); + var singleTest = response.data[0]; + that.api.getTestDetails(singleTest.id, function(err, response) { + assert.notEqual(response, null); + done(); + }); + }); + }); +}); \ No newline at end of file