pat310/google-trends-api

View on GitHub
test/utilities.spec.js

Summary

Maintainability
F
6 days
Test Coverage
'use strict';
import chai from 'chai';
import {
  constructInterestObj,
  constructTrendingObj,
  convertDateToString,
  formatResolution,
  formatTime,
  formatComparisonItems,
  getInterestResults,
  getTrendingResults,
  isLessThan7Days,
  parseResults,
} from '../src/utilities';
import request from '../src/request';

const expect = chai.expect;

describe('utilities', () => {

  describe('isLessThan7Days', () => {
    it('should return true if difference is less than 7 days', () => {
      const d1 = new Date('2017-02-04');
      const d2 = new Date('2017-02-10');
      const d3 = new Date('2017-02-01');

      expect(isLessThan7Days(d1, d2)).to.be.true;
      expect(isLessThan7Days(d2, d3)).to.be.false;
    });
  });

  describe('convertDateToString', () => {
    it('should be able to return a date formatted as YYYY-MM-DD', () => {
      const d = new Date('2017-02-04');

      expect(convertDateToString(d)).to.equal('2017-02-4');
    });

    it('should be able to return a date formatted as YYYY-MM-DDTHH\\:MM\\:SS',
        () => {
          const d = new Date('2017', '01', '04', '12', '43');
          const utcHour = d.getUTCHours();

          expect(convertDateToString(d, true)).to.equal(
            `2017-02-4T${utcHour}\\:43\\:00`);
        });

    it('should be able to return a date formatted as YYYYMMDDTHH\\:MM\\:SS',
        () => {
          const d = new Date('2017', '01', '04', '12', '43');
          const utcHour = d.getUTCHours();

          expect(convertDateToString(d, true, true)).to.equal(
            `20170204T${utcHour}\\:43\\:00`);
        });
  });

  describe('formatTime', () => {
    it('should make endTime the current date if not provided', () => {
      const d = new Date();
      const endTime = convertDateToString(d);
      const time = formatTime({}).time.split(' ');

      expect(formatTime({}).time).to.exist;
      expect(time[1]).to.equal(endTime);
    });

    it('should make the startTime 2004-01-01 if not provided', () => {
      const time = formatTime({}).time.split(' ');

      expect(formatTime({}).time).to.exist;
      expect(time[0]).to.equal('2004-01-1');
    });

    it('should include time if dates are less than a week and ' +
      'granularTimeResolution set to true', () => {
      const endTime = new Date();
      const startTime = new Date(Date.now() - (5 * 24 * 60 * 60 * 1000));
      const startTime2 = new Date(Date.now() - (8 * 24 * 60 * 60 * 1000));

      expect(/T/.test(formatTime({
        endTime,
        startTime,
        granularTimeResolution: true,
      }).time)).to.be.true;

      expect(/T/.test(formatTime({
        endTime,
        startTime,
        granularTimeResolution: false,
      }).time)).to.be.false;

      expect(/T/.test(formatTime({
        endTime,
        startTime: startTime2,
        granularTimeResolution: true,
      }).time)).to.be.false;
    });

    it('should switch startTime and endTime if startTime is after endTime',
        () => {
          const endTime = new Date();
          const startTime = new Date(Date.now() + (5 * 24 * 60 * 60 * 1000));
          const result = formatTime({startTime, endTime});

          expect(result.startTime).to.equal(endTime);
          expect(result.endTime).to.equal(startTime);
        });
  });

  describe('formatComparisonItems', () => {
    it('should return single comparisonItem', () => {
      let keywords = formatComparisonItems({ keyword: 'test' });

      expect(keywords).to.deep.equal([{ keyword: 'test' }]);
    });

    it('should return an array of comparisonItems', () => {
      let keywords = formatComparisonItems({
        keyword: ['test', 'test2'],
        startDate: '2017-01-01',
      });

      expect(keywords).to.deep.equal([
        {keyword: 'test', startDate: '2017-01-01'},
        {keyword: 'test2', startDate: '2017-01-01'},
      ]);
    });

    it('should return comparisonItems with same region', () => {
      let keywords = formatComparisonItems({
        keyword: ['test', 'test2'],
        startDate: '2017-01-01',
        geo: 'GB',
      });

      expect(keywords).to.deep.equal([
        {keyword: 'test', startDate: '2017-01-01', geo: 'GB'},
        {keyword: 'test2', startDate: '2017-01-01', geo: 'GB'},
      ]);
    });

    it('should return comparisonItems with different regions', () => {
      let keywords = formatComparisonItems({
        keyword: ['test', 'test2'],
        startDate: '2017-01-01',
        geo: ['GB', 'ES'],
      });

      expect(keywords).to.deep.equal([
        {keyword: 'test', startDate: '2017-01-01', geo: 'GB'},
        {keyword: 'test2', startDate: '2017-01-01', geo: 'ES'},
      ]);
    });

    it('should duplicate single keyword searches for multiple regions', () => {
      let keywords = formatComparisonItems({
        keyword: 'test',
        startDate: '2018-01-01',
        geo: ['US-MA', 'US-VA'],
      });

      expect(keywords).to.deep.equal([
        {keyword: 'test', startDate: '2018-01-01', geo: 'US-MA'},
        {keyword: 'test', startDate: '2018-01-01', geo: 'US-VA'},
      ]);
    });
  });

  describe('constructInterestObj', () => {
    it('should return an error if first argument is not an object', () => {
      expect(constructInterestObj('not an obj').obj).to.be.an('error');
    });

    it('should return an error if keyword is not provided', () => {
      expect(constructInterestObj({endTime: new Date()}).obj)
        .to.be.an('error');
      expect(constructInterestObj({keywords: 'Brooklyn'}).obj)
        .to.be.an('error');
    });

    it('should return an error if keyword and geo length are not equal', () => {
      expect(constructInterestObj({
        keyword: ['foo', 'bar'],
        geo: ['Brooklyn', 'DC', 'Boston'],
      }).obj).to.be.an('error');
    });

    it('should return an error if startTime is not a date', () => {
      expect(constructInterestObj({
        keyword: 'test',
        startTime: '2018-01-01',
      }).obj).to.be.an('error');
    });

    it('should return an error if endTime is not a date', () => {
      expect(constructInterestObj({
        keyword: 'test',
        endTime: '2018-01-01',
      }).obj).to.be.an('error');
    });

    it('should return an error if keyword and geo length are not equal', () => {
      expect(constructInterestObj({
        keyword: ['foo', 'bar'],
        geo: ['Brooklyn', 'DC', 'Boston'],
      }).obj).to.be.an('error');
    });

    it('should return an error if startTime is not a date', () => {
      expect(constructInterestObj({
        keyword: 'test',
        startTime: '2018-01-01',
      }).obj).to.be.an('error');
    });

    it('should return an error if endTime is not a date', () => {
      expect(constructInterestObj({
        keyword: 'test',
        endTime: '2018-01-01',
      }).obj).to.be.an('error');
    });

    it('should return an error if cbFunc is not a function', () => {
      expect(constructInterestObj({keyword: 'Brooklyn'}, 'str').obj)
        .to.be.an('error');
    });

    it('should not require a callback function', () => {
      expect(constructInterestObj({keyword: 'Brooklyn'}).obj)
        .to.not.be.an('error');
    });

    it('should create a callback if one is not provided', () => {
      expect(constructInterestObj({keyword: 'Brooklyn'}).cbFunc)
        .to.be.a('function');
    });

    it('should add default hl to english if not provided', () => {
      expect(constructInterestObj({keyword: 'Brooklyn'}).obj.hl)
        .to.equal('en-US');
    });

    it('should add default category to 0 if not provided', () => {
      expect(constructInterestObj({keyword: 'Brooklyn'}).obj.category)
        .to.equal(0);
    });

    it('@test should have a property if provided', () => {
      expect(constructInterestObj({
        keyword: 'Brooklyn',
        property: 'youtube',
      }).obj.property).to.equal('youtube');
    });

    it('@test has only allowed properties', () => {
      expect(constructInterestObj({
        keyword: 'Brooklyn',
        property: [],
      }).obj.property).to.equal('');
      expect(constructInterestObj({
        keyword: 'Brooklyn',
        property: 'netflix',
      }).obj.property).to.equal('');
      expect(constructInterestObj({
        keyword: 'Brooklyn',
        property: undefined,
      }).obj.property).to.equal('');
    });
  });

  describe('formatResolution', () => {
    const resolutions = ['COUNTRY', 'REGION', 'CITY', 'DMA'];

    it('should accept all words on the enumerated list', () => {
      const isItTrue = resolutions.every(formatResolution);

      expect(isItTrue).to.be.true;
    });

    it('should return an empty string if word is not on list', () => {
      expect(formatResolution('random string')).to.equal('');
    });

    it('should capitalize a word on the list', () => {
      expect(formatResolution('country')).to.equal('COUNTRY');
    });
  });

  describe('getInterestResults', () => {
    it('should return a function', () => {
      const resultsFunc = getInterestResults();

      expect(resultsFunc).to.be.a('function');
    });

    it('should eventually return', (done) => {
      const resultsFunc = getInterestResults(request);
      const { obj } = constructInterestObj({keyword: 'Brooklyn'});

      resultsFunc('Interest over time', obj)
      .then((res) => {
        expect(res).to.exist;
        expect(JSON.parse(res)).to.not.be.an('error');
        done();
      })
      .catch((e) => {
        expect(e).to.not.exist();
        done();
      });
    });

    it('should error if widgets do not contain selected api type', (done) => {
      function promiseFunc() {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            const fakeReqObj = {
              widgets: [{
                request: {
                  requestOptions: {},
                },
                token: 'dogman',
                title: 'Related topics',
              }],
            };

            resolve(`1234${JSON.stringify(fakeReqObj)}`);
          }, 500);
        });
      }

      const resultsFunc = getInterestResults(promiseFunc);
      const { obj } = constructInterestObj({ keyword: 'Brooklyn' });

      resultsFunc('Interest over time', obj)
      .catch((e) => {
        const message = 'Available widgets does not contain selected api type';

        expect(e.message).to.equal(message);
        done();
      });
    });

    it('should error if JSON is not valid', (done) => {
      let count = 0;
      const expectedFailureMsg = 'not valid json';

      function promiseFunc() {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            if (count === 0) {
              count += 1;

              const fakeReqObj = {
                widgets: [{
                  request: {
                    requestOptions: {},
                  },
                  token: 'dogman',
                  title: 'Interest over time',
                }],
              };

              resolve(`1234${JSON.stringify(fakeReqObj)}`);
            } else {
              resolve(expectedFailureMsg);
            }
          }, 500);
        });
      }

      const resultsFunc = getInterestResults(promiseFunc);
      const { obj } = constructInterestObj({keyword: 'Brooklyn'});

      resultsFunc('Interest over time', obj)
      .then((res) => {
        expect(res).to.exist;
        expect(res).to.equal(expectedFailureMsg);
        done();
      })
      .catch((e) => {
        done();
      });
    });
  });

  describe('parseResults', () => {
    it('should return a javascript object', () => {
      // The 'abcd' is the 4 characters that are returned by the request
      let validJSON = parseResults('abcd{' +
          '"some": "valid json",' +
          '"widgets": ["a", "b", "c"]' +
      '}');

      expect(validJSON).to.deep.equal(['a', 'b', 'c']);
    });

    it('should throw an error on invalid JSON', () => {
      expect(() => parseResults('<!DOCTYPE html><html</html>')).to.throw(Error);
    });
  });

  describe('constructTrendingObj', () => {
    it('should return an error if first argument is not an object', () => {
      expect(constructTrendingObj('not an obj').obj).to.be.an('error');
    });

    it('should return an error if cbFunc is not a function', () => {
      expect(constructTrendingObj({geo: 'US'}, 'str').obj).to.be.an('error');
    });

    it('should not require a callback function', () => {
      expect(constructTrendingObj({geo: 'US'}).obj).to.not.be.an('error');
    });

    it('should create a functioning callback if one is not provided', () => {
      expect(constructTrendingObj({geo: 'US'}).cbFunc).to.be.a('function');
      expect(constructTrendingObj({geo: 'US'})
        .cbFunc(new Error())).to.be.an('error');
      expect(constructTrendingObj({geo: 'US'}).cbFunc(0, '')).to.equal('');
    });

    it('should add default hl to english if not provided', () => {
      expect(constructTrendingObj({geo: 'US'}).obj.hl).to.equal('en-US');
    });

    it('should add default category to \'all\' if not provided', () => {
      expect(constructTrendingObj({geo: 'US'}).obj.category).to.equal('all');
    });

    it('should return an error if the geo is not provided', () => {
      expect(constructTrendingObj({trendDate: '2018-12-25'}).obj)
        .to.be.an('error');
    });

    it('should default trendDate to today if not provided', () => {
      expect(
        convertDateToString(constructTrendingObj(
          {geo: 'US', trendDate: '2018-12-25'}).obj.trendDate)).to.equal(
          convertDateToString(new Date()));
    });

    it('should default trendDate to today provided in wrong format', () => {
      expect(
        convertDateToString(
          constructTrendingObj({geo: 'US', trendDate: '2018-12-25'})
          .obj.trendDate)).to.equal(convertDateToString(new Date()));
    });

    it('should default TimeZone to the current TZ if not provided', () => {
      expect(constructTrendingObj({geo: 'US'}).obj.timezone).to.equal(
          new Date().getTimezoneOffset());
    });

    it('should default ns to 15 if not not provided', () => {
      expect(constructTrendingObj({geo: 'US'}).obj.ns).to.equal(15);
    });
  });

  describe('getTrendingResults', () => {
    it('should return a function', () => {
      const resultsFunc = getTrendingResults();

      expect(resultsFunc).to.be.a('function');
    });

    it('should eventually return', (done) => {
      const resultsFunc = getTrendingResults(request);
      const { obj } = constructTrendingObj({geo: 'US'});

      resultsFunc('Daily trends', obj)
      .then((res) => {
        expect(res).to.exist;
        expect(JSON.parse(res)).to.not.be.an('error');
        done();
      })
      .catch((e) => {
        expect(e).to.not.exist();
        done();
      });
    });

    it('should error if JSON is not valid', (done) => {
      const expectedFailureMsg = 'not valid json';

      function promiseFunc() {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(expectedFailureMsg);
          }, 500);
        });
      }

      const resultsFunc = getTrendingResults(promiseFunc);
      const { obj } = constructTrendingObj({geo: 'US'});

      resultsFunc('Daily trends', obj)
      .then((res) => {
        expect(res).to.exist;
        expect(res).to.equal(expectedFailureMsg);
        done();
      })
      .catch((e) => {
        done();
      });
    });
  });
});