UNPKG

openweathermap-apis

Version:

An abstraction layer for the openweathermap.org APIs

551 lines (453 loc) 19.8 kB
/* eslint no-underscore-dangle: ["error", { "allow": ["__get__"] }] */ 'use strict'; const Mocha = require('mocha'); const Chai = require('chai'); const jsdocx = require('jsdoc-x'); const http = require('http'); const rewire = require('rewire'); const qs = require('querystring'); const EventEmitter = require('events'); const moment = require('moment'); const { Test, Suite } = Mocha; const { expect } = Chai; const serverEmitter = new EventEmitter(); const serverPort = 3334; const serverAddress = '127.0.0.1'; const serverHost = `http://${serverAddress}`; const serverValidKey = 'validKey'; let server; const OpenWeatherMap = require('../../index'); function generateArgsFromParams(params) { const parameters = ((Array.isArray(params)) ? params : []) .slice(0) .map((param) => { return param.type; }) .map((param) => { if (param.includes('Array.<')) { switch (param.substr(7, (param.length - 8))) { case 'String': return ['test1', 'test2', 'test3']; case 'Number': return [5, 7, 9]; case 'Object': return [{ test: true }, { test: false }]; default: return []; } } else { switch (param) { case 'options={}': return {}; case 'String': return 'test1'; case 'Number': return 5; case 'Object': return { test: true }; case 'CityIDReqParams': return { id: 'London' }; case 'Coordinate': return { latitude: 51.509865, longitude: -0.118092 }; case 'PollutionParams': return { coordinates: { latitude: 51.509865, longitude: -0.118092 }, datetime: moment() }; default: return null; } } }); return parameters; } /** * @method getFunctionArgumentNames * @description parses a function to determine its arguments and returns them as a string * @param {Function} func the function to parse * @returns {String[]} an array of argument names for the suppl;ied function */ function getFunctionArgumentNames(func) { const argMatches = func.toString().match(/\(([^)]*)\)/); const argString = (argMatches && Array.isArray(argMatches) && argMatches.length >= 2) ? argMatches[1] : ''; return argString.split(',').map((arg) => { return arg.trim().replace('={}', ''); }); } /** * @method getAPITests * @description gets all of the js files within the lib directory and parses there jsdoc tags in * order to proceedurly generate tests based on the jsdocs. This requires certain tags to be * present such as the type (used to determine what type of request) aswell as the item under test * needing to be an inner method of a class. * @summary parse api jsdocs to generate test list * @returns {Promise} resolves with an object containing the jsdoc info or rejects with an error */ function getAPITests() { return jsdocx.parse('./lib/*.js') .then((docs) => { const innerMethods = docs .filter((doc) => { // only get methods return doc.scope === 'inner' && doc.access && doc.access === 'public'; }) .map((doc) => { // map the docs to remove useless data return { params: (doc.params) ? doc.params .map((param) => { return { type: param.type.names[0], name: param.name.replace('={}', ''), optional: (param.optional && param.optional === true) }; }) .filter((param) => { return !param.name.includes('.'); }) : [], memberof: doc.memberof, description: doc.description, see: doc.see, name: doc.name }; }) .reduce((methods, doc) => { // sort all the functions into categories (mixins) if (!methods[doc.memberof]) { methods[doc.memberof] = []; } methods[doc.memberof].push(doc); return methods; }, {}); return innerMethods; }); } const apiSuite = new Suite('civocloud-nodejs api tests'); module.exports = () => { return getAPITests() .then((methods) => { apiSuite.beforeAll('Test Endpoint setup', (done) => { server = http.createServer((req, res) => { let data = ''; req.on('data', (chunk) => { data += chunk; }); req.on('end', () => { serverEmitter.emit('receivedRequest', { req, body: qs.parse(data) || {}, params: (req.url.includes('?')) ? qs.parse(req.url.split('?')[1]) || {} : {} }); res.writeHead(200); res.write('{}'); res.end(); }); }); server.on('listening', () => { done(); }); server.listen(serverPort, serverAddress); }); apiSuite.afterAll('Test Endpoint destroy', (done) => { server.close((err) => { done(err); }); }); const innerMethods = methods; const outerMethods = Object.keys(methods); for (let o = 0, oLength = outerMethods.length; o < oLength; o += 1) { const testSuite = new Suite(`${outerMethods[o]}`); for (let i = 0, iLength = innerMethods[outerMethods[o]].length; i < iLength; i += 1) { const method = innerMethods[outerMethods[o]][i]; const methodSuite = new Suite(`${method.name}()`); methodSuite.timeout(5000); methodSuite.addTest(new Test('Function exposed', () => { const owm = new OpenWeatherMap.OpenWeatherMap({ apiKey: 'test' }); expect(owm[method.name]).to.be.a('function', 'method is not exposed as a function'); })); methodSuite.addTest(new Test('Function has description', () => { expect(method.description).to.not.be.equal(undefined); expect(method.description).to.not.be.equal(null); expect(method.description).to.not.be.equal(''); })); methodSuite.addTest(new Test('Function has see link to openweathermap.org', () => { expect(method.see).to.not.be.equal(undefined); expect(method.see).to.be.an('array'); expect(method.see).to.have.lengthOf(1); expect(method.see).to.include.to.match(/{@link https:\/\/openweathermap\.org.+/); })); if (/\[GET|POST|PUT|HEAD|DELETE|OPTIONS\]/.test(method.description)) { // request stuff here methodSuite.addTest(new Test('Function calls API endpoint', (done) => { const methodType = method.description .match(/\[(GET|POST|PUT|HEAD|DELETE|OPTIONS)\]/)[1]; const MUTT = new OpenWeatherMap.OpenWeatherMap({ apiKey: serverValidKey, host: serverAddress, port: serverPort }); serverEmitter.once('receivedRequest', (payload) => { expect(payload.req.headers).to.deep.include({ accept: 'application/json' }); expect(payload.params).to.include.keys(['APPID']); expect(payload.req.method).to.be.equal(methodType); done(); }); MUTT[method.name](...generateArgsFromParams(method.params)) .catch((err) => { done(err); }); })); } methodSuite.addTest(new Test('Correct Parameters', () => { const owm = new OpenWeatherMap.OpenWeatherMap({ apiKey: 'test' }); const nonOptionalParams = method.params .filter((param) => { return ((!param.optional || (param.optional && param.optional === false)) && param.name !== ''); }) .map((param) => { return param.name; }); const hasOptionals = method.params .map((param) => { return param.optional || false; }) .reduce((hasOptional, arg) => { return (hasOptional || arg); }, false); if (hasOptionals === true) { nonOptionalParams.push('options'); } if (nonOptionalParams.length === 0) { nonOptionalParams.push(''); } expect(getFunctionArgumentNames(owm[method.name])).to.have.members(nonOptionalParams); expect(owm[method.name]).to.be.an('function'); })); testSuite.addSuite(methodSuite); } apiSuite.addSuite(testSuite); } const sendRequestSuite = new Suite('sendRequest functional tests'); sendRequestSuite.addTest(new Test('sendRequest exists', () => { const owm = new OpenWeatherMap.OpenWeatherMap({ apiToken: 'test' }); expect(owm.sendRequest).to.not.be.equal(undefined); expect(owm.sendRequest).to.be.an('function'); })); apiSuite.addSuite(sendRequestSuite); const parsePartialDateTimeSuite = new Suite('parsePartialDateTime functional tests'); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime exists', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); expect(parsePartialDateTime).to.not.be.equal(undefined); expect(parsePartialDateTime).to.be.an('function'); done(); })); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime no datetime data', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); const result = parsePartialDateTime({}); expect(result).to.not.be.equal(undefined); expect(result).to.be.equal(null); done(); })); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime only year data', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); const result = parsePartialDateTime({ year: 2017 }); expect(result).to.not.be.equal(undefined); expect(result).to.not.be.equal(null); expect(result).to.be.an('string'); expect(result).to.be.equal('2017Z'); done(); })); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime year-month data', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); const result = parsePartialDateTime({ year: 2017, month: 12 }); expect(result).to.not.be.equal(undefined); expect(result).to.not.be.equal(null); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12Z'); done(); })); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime year-month-day data', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); const result = parsePartialDateTime({ year: 2017, month: 12, day: 24 }); expect(result).to.not.be.equal(undefined); expect(result).to.not.be.equal(null); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12-24Z'); done(); })); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime year-month-dayThour data', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); const result = parsePartialDateTime({ year: 2017, month: 12, day: 24, hour: 16 }); expect(result).to.not.be.equal(undefined); expect(result).to.not.be.equal(null); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12-24T16Z'); done(); })); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime year-month-dayThour:minute data', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); const result = parsePartialDateTime({ year: 2017, month: 12, day: 24, hour: 16, minute: 38 }); expect(result).to.not.be.equal(undefined); expect(result).to.not.be.equal(null); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12-24T16:38Z'); done(); })); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime year-month-dayThour:minute:second data', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); const result = parsePartialDateTime({ year: 2017, month: 12, day: 24, hour: 16, minute: 38, second: 32 }); expect(result).to.not.be.equal(undefined); expect(result).to.not.be.equal(null); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12-24T16:38:32Z'); done(); })); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime all terms missing month data', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); const result = parsePartialDateTime({ year: 2017, day: 24, hour: 16, minute: 38, second: 32 }); expect(result).to.not.be.equal(undefined); expect(result).to.not.be.equal(null); expect(result).to.be.an('string'); expect(result).to.be.equal('2017Z'); done(); })); parsePartialDateTimeSuite.addTest(new Test('parsePartialDateTime all terms missing minute data', (done) => { const airPollution = rewire('../../lib/airPollution'); const parsePartialDateTime = airPollution.__get__('parsePartialDateTime'); const result = parsePartialDateTime({ year: 2017, month: 12, day: 24, hour: 16, second: 32 }); expect(result).to.not.be.equal(undefined); expect(result).to.not.be.equal(null); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12-24T16Z'); done(); })); apiSuite.addSuite(parsePartialDateTimeSuite); const formatDateTimeSuite = new Suite('formatDateTime functional tests'); formatDateTimeSuite.addTest(new Test('formatDateTime exists', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatDateTime = airPollution.__get__('formatDateTime'); expect(formatDateTime).to.not.be.equal(undefined); expect(formatDateTime).to.be.an('function'); done(); })); formatDateTimeSuite.addTest(new Test('formatDateTime returns null', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatDateTime = airPollution.__get__('formatDateTime'); const result = formatDateTime({}); expect(result).to.not.be.equal(undefined); expect(result).to.be.equal(null); done(); })); formatDateTimeSuite.addTest(new Test('formatDateTime using moment', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatDateTime = airPollution.__get__('formatDateTime'); const result = formatDateTime(moment('2017-12-24T16:47:03Z')); expect(result).to.not.be.equal(undefined); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12-24T16:47:03.000Z'); done(); })); formatDateTimeSuite.addTest(new Test('formatDateTime using native Date', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatDateTime = airPollution.__get__('formatDateTime'); const result = formatDateTime(new Date(1514134023000)); // is '2017-12-24T16:47:03.000Z' expect(result).to.not.be.equal(undefined); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12-24T16:47:03.000Z'); done(); })); formatDateTimeSuite.addTest(new Test('formatDateTime using partial date object', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatDateTime = airPollution.__get__('formatDateTime'); const result = formatDateTime({ year: 2017, month: 12, day: 24 }); expect(result).to.not.be.equal(undefined); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12-24Z'); done(); })); formatDateTimeSuite.addTest(new Test('formatDateTime using manual string', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatDateTime = airPollution.__get__('formatDateTime'); const result = formatDateTime('2017-12-24T16:47:03.000Z'); expect(result).to.not.be.equal(undefined); expect(result).to.be.an('string'); expect(result).to.be.equal('2017-12-24T16:47:03.000Z'); done(); })); apiSuite.addSuite(formatDateTimeSuite); const formatCoordinatesSuite = new Suite('formatCoordinates functional tests'); formatCoordinatesSuite.addTest(new Test('formatCoordinates exists', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatCoordinates = airPollution.__get__('formatCoordinates'); expect(formatCoordinates).to.not.be.equal(undefined); expect(formatCoordinates).to.be.an('function'); done(); })); formatCoordinatesSuite.addTest(new Test('formatCoordinates returns null on empty input', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatCoordinates = airPollution.__get__('formatCoordinates'); const result = formatCoordinates(); expect(result).to.not.be.equal(undefined); expect(result).to.be.equal(null); done(); })); formatCoordinatesSuite.addTest(new Test('formatCoordinates returns null on empty object', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatCoordinates = airPollution.__get__('formatCoordinates'); const result = formatCoordinates({}); expect(result).to.not.be.equal(undefined); expect(result).to.be.equal(null); done(); })); formatCoordinatesSuite.addTest(new Test('formatCoordinates returns string with coordinates', (done) => { const airPollution = rewire('../../lib/airPollution'); const formatCoordinates = airPollution.__get__('formatCoordinates'); const result = formatCoordinates({ latitude: 12.3456, longitude: 7.89 }); expect(result).to.not.be.equal(undefined); expect(result).to.be.an('string'); expect(result).to.be.equal('12.3456,7.89'); done(); })); apiSuite.addSuite(formatCoordinatesSuite); return apiSuite; }); };