MetaPhase-Consulting/State-TalentMAP

View on GitHub
src/utilities.test.ts

Summary

Maintainability
A
0 mins
Test Coverage
import sinon from 'sinon';
import { isEqual } from 'lodash';
import {
  anyToTitleCase,
  cleanQueryParams,
  difference,
  downloadFromResponse,
  existsInNestedObject,
  fetchUserToken,
  focusByFirstOfHeader,
  focusById,
  formExploreRegionDropdown,
  formQueryString,
  formatBidTitle,
  formatDate,
  formatIdSpacing,
  formatWaiverTitle,
  getAccessiblePositionNumber,
  getApplicationPath,
  getAriaValue,
  getAssetPath,
  getBidCycleName,
  getBidListStats,
  getDifferentialPercentage,
  getItemLabel,
  getPostName,
  getScrollDistanceFromBottom,
  getTimeDistanceInWords,
  hasValidToken,
  ifEnter,
  isUrl,
  loadImg,
  localStorageFetchValue,
  localStorageToggleValue,
  mapSavedSearchToDescriptions,
  mapSavedSearchesToSingleQuery,
  move,
  numbersToPercentString,
  propOrDefault,
  propSort,
  redirectToLoginRedirect,
  returnObjectsWherePropMatches,
  scrollToGlossaryTerm,
  scrollToTop,
  shortenString,
  sortGrades,
  spliceStringForCSV,
  splitByLineBreak,
  stopProp,
  userHasPermissions,
  validStateEmail,
  wrapForMultiSelect,
} from './utilities';
import { searchObjectParent } from './__mocks__/searchObject';

describe('local storage', () => {
  it('should be able to fetch the existence of a value when there is one values in the array', () => {
    localStorage.setItem('keyName', JSON.stringify(['1']));
    const retrieved = localStorageFetchValue('keyName', '1');
    expect(retrieved.exists).toBe(true);
    localStorage.clear();
  });

  it('should be able to fetch the existence of a value when there are multiple values in the array', () => {
    localStorage.setItem('keyName', JSON.stringify(['1', '2']));
    const retrieved = localStorageFetchValue('keyName', '1');
    expect(retrieved.exists).toBe(true);
    localStorage.clear();
  });

  it('should be able to fetch the existence of a value when that value is not in the array', () => {
    localStorage.setItem('keyName', JSON.stringify(['2', '3']));
    const retrieved = localStorageFetchValue('keyName', '1');
    expect(retrieved.exists).toBe(false);
    localStorage.clear();
  });

  it('should be able to fetch the count of an array', () => {
    localStorage.setItem('keyName', JSON.stringify(['1', '2']));
    const retrieved = localStorageFetchValue('keyName', '1');
    expect(retrieved.count).toBe(2);
    localStorage.clear();
  });

  it('should be able to toggle a value in the array', () => {
    localStorage.setItem('keyName', JSON.stringify(['1', '2']));
    let retrieved = localStorageFetchValue('keyName', '1');
    expect(retrieved.exists).toBe(true);
    localStorageToggleValue('keyName', '1');
    retrieved = localStorageFetchValue('keyName', '1');
    expect(retrieved.exists).toBe(false);
    localStorage.clear();
  });
});

describe('validStateEmail', () => {
  it('should return true for a valid State email', () => {
    const email = 'joe123@state.gov';
    const output = validStateEmail(email);
    expect(output).toBe(true);
  });

  it('should return false for an invalid State email', () => {
    const email = 'joe123@email.com';
    const output = validStateEmail(email);
    expect(output).toBe(false);
  });
});

describe('fetchUserToken', () => {
  it('should be able to fetch the auth token', () => {
    localStorage.clear();
    localStorage.setItem('token', '1234');
    const output = fetchUserToken();
    expect(output).toBe('Token 1234');
    localStorage.clear();
  });
});

describe('sort functions', () => {
  const items = [{ title: 'a', description: 'a' }, { title: 'b', description: 'b' }];
  const grades = [{ code: '01' }, { code: '02' }, { code: 'fake' }, { code: 'MC' }];

  it('can sort by description', () => {
    expect(propSort('description')(items[0], items[1])).toBe(-1);
    expect(propSort('description')(items[1], items[0])).toBe(1);
    expect(propSort('description')(items[0], items[0])).toBe(0);
  });

  it('can sort by title', () => {
    expect(propSort('title')(items[0], items[1])).toBe(-1);
    expect(propSort('title')(items[1], items[0])).toBe(1);
    expect(propSort('title')(items[0], items[0])).toBe(0);
  });

  it('can apply custom sorting to grades', () => {
    expect(sortGrades(grades[0], grades[1])).toBe(-1);
    expect(sortGrades(grades[1], grades[0])).toBe(1);
    expect(sortGrades(grades[0], grades[2])).toBe(-1);
    expect(sortGrades(grades[3], grades[2])).toBe(-1);
    expect(sortGrades(grades[2], grades[3])).toBe(1);
    expect(sortGrades(grades[2], grades[2])).toBe(0);
  });
});

const filters = [{ item: { title: 'Bureau' }, data: [{ long_description: 'regionA', code: 'code' }] }, { item: { title: 'Language' } }];

describe('formExploreRegionDropdown function', () => {
  const regions = formExploreRegionDropdown(filters);

  it('can filter for region', () => {
    if (filters[0].data)
      expect(regions[1].long_description).toBe(filters[0].data[0].long_description);
  });

  it('can add new properties', () => {
    if (filters[0].data)
      expect(regions[1].text).toBe(filters[0].data[0].long_description);
  });

  it('adds the placeholder object to the beginning of the array', () => {
    expect(regions[0].disabled).toBe(true);
  });
});

describe('scrollToTop function', () => {
  it('can call the scrollToTop function', () => {
    scrollToTop();
  });
});

describe('getItemLabel function', () => {
  it('can can get an item label', () => {
    if (filters[0].data)
      expect(getItemLabel(filters[0].data[0])).toBe(filters[0].data[0].long_description);
    expect(getItemLabel({ description: 'test' })).toBe('test');
    expect(getItemLabel({ code: '0' })).toBe('0');
  });
});

describe('shortenString function', () => {
  const string = '012345';
  it('can shorten a string with the default suffix', () => {
    expect(shortenString(string, 0)).toBe('...');
    expect(shortenString(string, 2)).toBe('...');
    expect(shortenString(string, 3)).toBe('...');
    expect(shortenString(string, 4)).toBe('0...');
    expect(shortenString(string, 5)).toBe('01...');
    expect(shortenString(string, 6)).toBe('012345');
    expect(shortenString(string, 7)).toBe('012345');
  });

  it('can shorten a string with a null suffix', () => {
    expect(shortenString(string, 0, null)).toBe('');
    expect(shortenString(string, 2, null)).toBe('01');
    expect(shortenString(string, 3, null)).toBe('012');
    expect(shortenString(string, 4, null)).toBe('0123');
    expect(shortenString(string, 5, null)).toBe('01234');
    expect(shortenString(string, 6, null)).toBe('012345');
    expect(shortenString(string, 7, null)).toBe('012345');
  });
});

describe('cleanQueryParams', () => {
  const query = { q: 'test', fake: 'test' };
  it('retain only real query params', () => {
    expect(cleanQueryParams(query).fake).toBe(undefined);
    expect(cleanQueryParams(query).q).toBe(query.q);
    expect(Object.keys(cleanQueryParams(query)).length).toBe(1);
  });
});

describe('ifEnter', () => {
  it('only returns true for keyCode of 13', () => {
    expect(ifEnter({ keyCode: 13 } as React.KeyboardEvent)).toBe(true);
    expect(ifEnter({ keyCode: 14 } as React.KeyboardEvent)).toBe(false);
  });
});

describe('formQueryString', () => {
  it('can return a string', () => {
    expect(formQueryString({ q: 'test' })).toBe('q=test');
  });
});

describe('existsInNestedObject', () => {
  it('can return true when something exists in a nested object', () => {
    const arr = [{ position_info: { id: 1 } }];
    expect(existsInNestedObject(1, arr)).toEqual(arr[0]);
  });

  it('can return false when something does not exist in a nested object', () => {
    expect(existsInNestedObject(1, [{ position: { otherId: 2 } }])).toBe(false);
  });
});

describe('distanceInWords', () => {
  it('returns a defined value containg "ago" in the string', () => {
    const timeDistanceInWords = getTimeDistanceInWords(new Date());
    // we won't explicitly test for values since we can expect date-fns to work
    expect(timeDistanceInWords).toBeDefined();
    // but we will test that it contains ' ago' since that is custom text we added in
    expect(timeDistanceInWords).toContain(' ago');
  });
});

describe('formatDate', () => {
  it('returns a properly formatted date', () => {
    // how we expect the date from the API
    const unformattedDate = '2017-01-15';
    // converted date
    const formattedDate = formatDate(unformattedDate);
    // should be formatted using the default format
    expect(formattedDate).toBe('01/15/2017');
  });

  it('returns a properly formatted date with a custom format', () => {
    // how we expect the date from the API
    const unformattedDate = '2017-01-01';
    // converted date
    const formattedDate = formatDate(unformattedDate, 'M-D-YYYY');
    // should be formatted using the custom format
    expect(formattedDate).toBe('1-1-2017');
  });

  // Function is type checked in new TS utilities so this unit test may not be needed
  // In that case, remove null from param types in formatDate
  it('returns null if no date is provided', () => {
    expect(formatDate(null)).toBe(null);
  });
});

describe('focusById', () => {
  let focusSpy: sinon.SinonSpy;
  it('can focus by ID', () => {
    focusSpy = sinon.spy();
    const elements: any = {
      test: {
        focus: focusSpy,
      },
    };
    global.document.getElementById = id => elements[id];
    focusById('test');
    sinon.assert.calledOnce(focusSpy);
  });

  it('can focus by ID when there is a timeout', (done) => {
    focusSpy = sinon.spy();
    const elements: any = {
      test: {
        focus: focusSpy,
      },
    };
    global.document.getElementById = id => elements[id];
    const timeout = 10;
    focusById('test', timeout);
    setTimeout(() => {
      sinon.assert.calledOnce(focusSpy);
      done();
    }, timeout);
  });
});

describe('focusByFirstOfHeader', () => {
  let valuesSetBySetAttribute: string[] = [];
  const setAttribute = (attribute: string, value: string) => { valuesSetBySetAttribute = [attribute, value]; };

  let focusSpy = sinon.spy();
  let elements: any = {
    h1: [
      {},
      { focus: focusSpy, setAttribute },
    ],
    h2: {},
  };
  global.document.getElementsByTagName = (tag: string) => elements[tag];
  it('can focus and set attributes to the first header found', (done) => {
    focusByFirstOfHeader();
    const f = () => {
      setTimeout(() => {
        sinon.assert.calledOnce(focusSpy);
        expect(valuesSetBySetAttribute[0]).toBe('tabindex');
        expect(valuesSetBySetAttribute[1]).toBe('-1');
        done();
      }, 10);
    };
    f();
  });

  it('can focus the first h2 if h1 does not exist', (done) => {
    // reset spy and attribute array
    valuesSetBySetAttribute = [];
    focusSpy = sinon.spy();
    elements = {
      h2: [
        { focus: focusSpy, setAttribute },
      ],
    };
    focusByFirstOfHeader();
    const f = () => {
      setTimeout(() => {
        sinon.assert.calledOnce(focusSpy);
        expect(valuesSetBySetAttribute[0]).toBe('tabindex');
        expect(valuesSetBySetAttribute[1]).toBe('-1');
        done();
      }, 10);
    };
    f();
  });
});

describe('wrapForMultiSelect', () => {
  it('can convert properties', () => {
    const options = [{ code: '100', description: 'one hundred' }, { code: '200', description: 'two hundred' }];
    const wrappedOptions = wrapForMultiSelect(options, 'code', 'description');
    expect(wrappedOptions).toBeDefined();
    expect(wrappedOptions[0].value).toBe(options[0].code);
    expect(wrappedOptions[0].label).toBe(options[0].description);
    expect(wrappedOptions[1].value).toBe(options[1].code);
    expect(wrappedOptions[1].label).toBe(options[1].description);
  });
});

describe('returnObjectsWherePropMatches', () => {
  it('can return objects in two arrays where a property matches in both arrays', () => {
    const sourceArray = [
      { code: '1', description: 'one' },
      { code: '2', description: 'two' },
    ];
    const compareArray = [
      { code: '2', description: 't-w-o', label: 'two' },
      { code: '4', description: 'four' },
      { code: '5', description: 'five' },
    ];
    const propToCheck = 'code';
    const newArray = returnObjectsWherePropMatches(sourceArray, compareArray, propToCheck);
    // only one code matches between the two arrays, the one where code === '2'
    expect(newArray.length).toBe(1);
    // the description should be the one from the first array
    expect(newArray[0].description).toBe(sourceArray[1].description);
    // no props from the second array should be used
    expect(newArray[0].label).toBeUndefined();
  });
});

describe('numbersToPercentString', () => {
  let numerator = 2;
  let denominator = 10;

  it('can return a percent', () => {
    const percent = numbersToPercentString(numerator, denominator);
    expect(percent).toBe('20.0%');
  });

  it('can return a percent with proper format', () => {
    numerator = 3;
    denominator = 7;
    const format = '0.00%';
    const percent = numbersToPercentString(numerator, denominator, format);
    expect(percent).toBe('42.86%');
  });
});

describe('formatBidTitle', () => {
  it('can format a bid title', () => {
    const bid = {
      position: {
        position_number: '0AA',
        title: 'Title',
      },
    };
    const expected = 'Title (0AA)';
    expect(formatBidTitle(bid)).toBe(expected);
  });
});

describe('formatWaiverTitle', () => {
  it('can format a bid title', () => {
    const waiver = {
      position: 'Position',
      category: 'category',
    };
    const expected = 'Position - CATEGORY';
    expect(formatWaiverTitle(waiver)).toBe(expected);
  });
});

describe('propOrDefault', () => {
  const nestedObject = {
    a: {
      b: true,
      c: {
        d: {},
        e: 1,
        f: 0,
      },
    },
  };

  it('traverses nested objects', () => {
    expect(propOrDefault(nestedObject, 'a.b')).toBe(true);
    expect(propOrDefault(nestedObject, 'a.c.d')).toBeDefined();
    expect(propOrDefault(nestedObject, 'a.c.e')).toBe(1);
  });

  it('returns the default value when the nested property does not exist', () => {
    expect(propOrDefault(nestedObject, 'a.b.e.e.e')).toBe(null);
    expect(propOrDefault(nestedObject, 'a.g')).toBe(null);
    expect(propOrDefault(nestedObject, 'a.b.c.d.d', 'value')).toBe('value');
  });

  it('returns the property value when the property value exists, but is 0', () => {
    expect(propOrDefault(nestedObject, 'a.c.f')).toBe(0);
  });
});

describe('formatIdSpacing', () => {
  it('can format strings', () => {
    expect(formatIdSpacing('two words')).toBe('two-words');
    expect(formatIdSpacing('has Three words')).toBe('has-Three-words');
  });

  it('can format numbers', () => {
    expect(formatIdSpacing(3)).toBe('3');
  });

  it('can format undefined values', () => {
    // these will be randomly generated shortids, so we just check that they have length
    // greater than 3
    expect(formatIdSpacing(undefined)).toBeDefined();
    expect(formatIdSpacing(null)).toBeDefined();
    // expect(formatIdSpacing(false)).toBeDefined(); // type checked with new TS utility functions
  });
});

describe('userHasPermissions', () => {
  let userPermissions: string[];
  let permissionsToCheck: string[];
  beforeEach(() => {
    userPermissions = ['a', 'b', 'c'];
    permissionsToCheck = ['a', 'c'];
  });

  it('returns true if the user has all the needed permissions', () => {
    expect(userHasPermissions(permissionsToCheck, userPermissions)).toBe(true);
  });

  it('returns false if the user has some of the needed permissions', () => {
    userPermissions = ['a'];
    expect(userHasPermissions(permissionsToCheck, userPermissions)).toBe(false);
  });

  it('returns true if the user has more than the needed permissions', () => {
    userPermissions.push('d', 'e');
    expect(userHasPermissions(permissionsToCheck, userPermissions)).toBe(true);
  });

  it('returns false if userPermissions are not provided', () => {
    userPermissions = [];
    expect(userHasPermissions(permissionsToCheck, userPermissions)).toBe(false);
  });

  it('returns true if permissionsToCheck are not provided', () => {
    permissionsToCheck = [];
    expect(userHasPermissions(permissionsToCheck, userPermissions)).toBe(true);
  });

  it('returns true if permissionsToCheck and userPermissions are not provided', () => {
    permissionsToCheck = [];
    userPermissions = [];
    expect(userHasPermissions(permissionsToCheck, userPermissions)).toBe(true);
  });
});

describe('getAssetPath', () => {
  it('returns the correct path with no PUBLIC_URL', () => {
    const assetPath = '/image.png';
    const result = getAssetPath(assetPath);
    expect(result).toBe(assetPath);
  });

  it('returns the correct path with a PUBLIC_URL', () => {
    process.env.PUBLIC_URL = '/public';
    const assetPath = '/image.png';
    const result = getAssetPath(assetPath);
    expect(result).toBe(`/public${assetPath}`);
  });

  it('returns the correct path with a PUBLIC_URL with a trailing slash', () => {
    process.env.PUBLIC_URL = '/public/';
    const assetPath = '/image.png';
    const result = getAssetPath(assetPath);
    expect(result).toBe(`/public${assetPath}`);
  });
});

describe('getApplicationPath', () => {
  it('returns a valid path', () => {
    // set env
    process.env.PUBLIC_URL = '/application/';

    const result = getApplicationPath();

    expect(result).toBe('http://localhost/application/');
  });
});

describe('getAccessiblePositionNumber', () => {
  it('adds spaces to a position number', () => {
    const positionNumber = 'S7001';

    expect(getAccessiblePositionNumber(positionNumber)).toBe('S 7 0 0 1');
  });

  it('returns null if positionNumber is undefined', () => {
    const positionNumber = undefined;

    expect(getAccessiblePositionNumber(positionNumber)).toBe(null);
  });
});

describe('getPostName', () => {
  it('returns a domestic post name', () => {
    const post = { location: { city: 'Arlington', state: 'VA', country: 'United States' } };
    expect(getPostName(post)).toBe('Arlington, VA');
  });

  it('returns a domestic post name when country === USA', () => {
    const post = { location: { city: 'Arlington', state: 'VA', country: 'USA' } };
    expect(getPostName(post)).toBe('Arlington, VA');
  });

  it('returns an overseas post name', () => {
    const post = { location: { city: 'London', state: null, country: 'United Kingdom' } };
    expect(getPostName(post)).toBe('London, United Kingdom');
  });

  it('returns the code when location data is not available', () => {
    const post = { location: null, code: '0AA' };
    expect(getPostName(post)).toBe('0AA');
  });

  it('returns the default defaultValue when the code and location data are not available', () => {
    const post = { location: null };
    expect(getPostName(post)).toBe(null);
  });

  it('returns a custom defaultValue when the code and location data are not available', () => {
    const post = { location: null };
    expect(getPostName(post, 'default')).toBe('default');
  });
});

describe('getDifferentialPercentage', () => {
  it('returns a percentage for a differential', () => {
    expect(getDifferentialPercentage(30)).toBe('30%');
  });

  it('returns a percentage for a differential of 0', () => {
    expect(getDifferentialPercentage(0)).toBe('0%');
  });

  it('returns the default value for a differential of null', () => {
    expect(getDifferentialPercentage(null)).toBe('');
  });

  it('returns a custom default value for a differential of null', () => {
    expect(getDifferentialPercentage(null, 'custom')).toBe('custom');
  });
});

describe('mapSavedSearchesToSingleQuery', () => {
  const searches = searchObjectParent;
  it('maps multiple saved searches to a single query', () => {
    const mappedSearch = mapSavedSearchesToSingleQuery(searches);
    const expected = {
      position__grade__code__in: '02',
      position__post__tour_of_duty__code__in: 'O',
      q: 'german',
      position__skill__code__in: '6080',
    };
    expect(isEqual(mappedSearch, expected)).toBe(true);
  });
});

describe('mapSavedSearchToDescriptions', () => {
  const searches = searchObjectParent;
  const mappedFilters = [{
    codeRef: '6080',
    descCode: 'CONSULAR AFFAIRS',
    description: 'CONSULAR AFFAIRS',
    hasDuplicateDescription: true,
    isCommon: undefined,
    isTandem: true,
    isToggle: undefined,
    selectionRef: 'position__skill__code__in'
  }];

  it('maps saved searches to descriptions', () => {
    const mappedDescriptions = mapSavedSearchToDescriptions(
      searches.results[0].filters, mappedFilters);
    const expected = [
      { description: 'german', isTandem: undefined, isCommon: true, isToggle: undefined },
      { description: 'CONSULAR AFFAIRS', isTandem: true, isCommon: undefined, isToggle: undefined },
    ];
    expect(isEqual(mappedDescriptions, expected)).toBe(true);
  });
});

describe('difference', () => {
  it('verify diff between 2 - 1 level objects when there ARE differences', () => {
    const itemA = {
      boolean: true,
      array: null,
    };

    const itemB = {
      boolean: false,
      array: [],
      new: 'I am new!',
    };

    const diff = difference(itemA, itemB);
    const expected = {
      boolean: false,
      array: [],
      new: 'I am new!',
    };

    expect(diff).toEqual(expected);
  });

  it('verify diff between 2 - 1 level objects when there NO differences', () => {
    const itemA = {
      boolean: true,
      array: [],
      string: 'string',
    };

    const itemB = {
      boolean: true,
      array: [],
    };

    const diff = difference(itemA, itemB);
    const expected = {};

    expect(diff).toEqual(expected);
  });

  it('verify diff between 2 - deep level objects', () => {
    const itemA = {
      data: {
        boolean: false,
        number: -1,
        key: 'key',
      },

      params: {
        config: '/var/usr/app',
        count: 0,
        data: {
          boolean: false,
          page: 1,
        },
      },
    };

    const itemB = {
      data: {
        boolean: false,
        number: -1,
        key: 'key',
      },

      params: {
        config: '/var/usr/app',
        count: 320,
        data: {
          boolean: false,
          page: 10,
        },
      },
    };

    const diff = difference(itemA, itemB);
    const expected = {
      params: {
        count: 320,
        data: {
          page: 10,
        },
      },
    };

    expect(diff).toEqual(expected);
  });
});

describe('redirectToLoginRedirect', () => {
  it('assigns a new window location', () => {
    let newLocation;
    process.env.PUBLIC_URL = '/test';
    // eslint-disable-next-line no-return-assign
    window.location.assign = loc => newLocation = loc;
    redirectToLoginRedirect();
    expect(newLocation).toBe('/test/loginRedirect');
  });
});

describe('isUrl', () => {
  it('returns the expected output for different strings', () => {
    [
      ['http', false], ['https', false], ['ftp', false], ['https://www', false], ['http.goo', false], ['goog.com', false],
      ['http://google.com', true], ['https://www.goo.co', true],
    ].map((u) => {
      // assume first item in array is a string from declaration above
      const output = isUrl(u[0] as string);
      return u[1] ? expect(output).toBeTruthy() : expect(output).toBeFalsy();
    });
  });
});

describe('hasValidToken', () => {
  it('removes the localStorage token if it is invalid', () => {
    localStorage.setItem('token', '{');
    expect(hasValidToken()).toBe(false);
    expect(localStorage.getItem('token')).toBe(null);
  });
});

describe('getScrollDistanceFromBottom', () => {
  it('returns the scroll distance from the bottom of the page', () => {
    window.pageYOffset = 100;
    window.innerHeight = 800;
    const z = document.createElement('body');
    Object.setPrototypeOf(z, { offsetHeight: 3000 });
    document.body = z;
    expect(getScrollDistanceFromBottom()).toBe(2100);
  });
});

describe('getAriaValue', () => {
  [[true, 'true'], [false, 'false'], ['true', 'true'], ['false', 'false'], [null, 'false'], [1, 'true']]
    .map(m => (
      it(`returns ${m[1]} for ${m[0]}`, () => {
        expect(getAriaValue(m[0])).toBe(m[1]);
      })
    ));
});

describe('spliceStringForCSV', () => {
  it('splices the string correctly when index 1 === "="', () => {
    expect(spliceStringForCSV('"=jjj"')).toBe('="jjj"');
  });

  it('returns the value unchanged when index 1 !== "="', () => {
    expect(spliceStringForCSV('"jjj"')).toBe('"jjj"');
  });
});

describe('scrollToGlossaryTerm', () => {
  it("calls element's functions", (done) => {
    const scrollSpy = sinon.spy();
    const clickSpy = sinon.spy();

    window.document.getElementById = () => ({
      scrollIntoView: scrollSpy, 
      getAttribute: () => '', 
      click: clickSpy, 
      focus: () => { },
    } as unknown as HTMLElement);

    scrollToGlossaryTerm('term');

    setTimeout(() => {
      sinon.assert.calledOnce(scrollSpy);
      sinon.assert.calledOnce(clickSpy);
      done();
    }, 500);
  });

  describe('getBidCycleName', () => {
    const cyclename = 'Summer 2020';

    it('returns the correct value for strings', () => {
      expect(getBidCycleName(cyclename)).toBe(cyclename);
    });

    it('returns the correct value for objects', () => {
      expect(getBidCycleName({ name: cyclename })).toBe(cyclename);
    });

    it('returns the correct value when it cannot find a name', () => {
      expect(getBidCycleName({ a: cyclename })).not.toBe(cyclename);
      expect(getBidCycleName([])).not.toBe(cyclename);
      expect(getBidCycleName({ cyclename: 1 })).not.toBe(cyclename);
    });
  });

  describe('loadImg', () => {
    it('does not throw an error', () => {
      expect(loadImg).not.toThrowError();
    });
  });

  describe('downloadFromResponse', () => {
    let blobSpy: sinon.SinonSpy;
    // let blobSpy: sinon.SinonSpy | ((blob: any, defaultName?: string | undefined) => boolean) | undefined;
    // let blobSpy: sinon.SinonSpy<any[], any> | sinon.SinonSpy<any, any>;
    // let blobSpy: sinon.SinonSpy<any[], any> | ((blob: any, defaultName?: string | undefined) => boolean) | sinon.SinonSpy<any, any> | undefined;
    let response: { headers: { 'content-disposition': string; }; data: string; };

    beforeEach(() => {
      blobSpy = sinon.spy();
      response = {
        headers: { 'content-disposition': 'attachment; filename=test.csv' },
        data: 'some data',
      };
      global.window.navigator.msSaveOrOpenBlob = blobSpy;
    });

    it('calls msSaveOrOpenBlob if msSaveBlob exists', () => {
      downloadFromResponse(response);
      sinon.assert.calledOnce(blobSpy);
      blobSpy.reset();
    });

    it('does not call msSaveOrOpenBlob if msSaveBlob does not exist', () => {
      global.window.navigator.msSaveBlob = undefined;
      downloadFromResponse(response);
      sinon.assert.notCalled(blobSpy);
    });
  });

  describe('anyToTitleCase', () => {
    it('converts a string to title case', () => {
      const result = 'The Quick Dog';
      ['tHE qUick Dog', 'THE QUICK DOG', 'the quick dog', 'The Quick Dog', 'tHe Quick dOg']
        .map(m => expect(anyToTitleCase(m)).toBe(result));
    });
  });

  describe('stopProp', () => {
    it('calls stopPropagation on stopProp', () => {
      const spy = sinon.spy();
      const e = { stopPropagation: spy };
      stopProp(e);
      sinon.assert.calledOnce(spy);
    });
  });

  describe('getBidListStats', () => {
    it('properly counts statuses', () => {
      const bidList = [{ status: 'a' }, { status: 'b' }, { status: 'c' }, { status: 'd' }, { status: 'e' }];
      const statuses = ['a', 'b', 'c'];
      expect(getBidListStats(bidList, statuses, false)).toBe(3);
      expect(getBidListStats(bidList, ['x', 'y', 'z'])).toBe(0);
      expect(getBidListStats(bidList, ['d'])).toBe(1);
    });

    it('properly pads numbers less than 10 when padWithZero', () => {
      const bidList = [
        { status: 'a' }, { status: 'a' }, { status: 'a' }, { status: 'a' }, { status: 'a' },
        { status: 'a' }, { status: 'a' }, { status: 'a' }, { status: 'a' },
        { status: 'b' }, { status: 'b' }, { status: 'b' }, { status: 'b' }, { status: 'b' },
        { status: 'b' }, { status: 'b' }, { status: 'b' }, { status: 'b' }, { status: 'b' },
      ];
      const statuses = ['a', 'b', 'c'];
      expect(getBidListStats(bidList, ['x', 'y', 'z'], true)).toBe('00');
      expect(getBidListStats(bidList, ['a'], true)).toBe('09');
      expect(getBidListStats(bidList, ['b'], true)).toBe('10');
      expect(getBidListStats(bidList, statuses, true)).toBe('19');
    });
  });
});

describe('move', () => {
  it('moves an item in an array', () => {
    const arr = [1, 2, 3];
    const exp = move(arr, 0, 1);
    expect(exp).toEqual([2, 1, 3]);
  });
});

describe('splitByLineBreak', () => {
  // We use 3 line breaks to signal a split
  it('splits a single message', () => {
    expect(splitByLineBreak('test')).toEqual(['test']);
  });

  it('splits no messages', () => {
    expect(splitByLineBreak(null)).toEqual(['']);
  });

  it('splits 2 messages', () => {
    expect(splitByLineBreak('test\n\n\ntest2')).toEqual(['test', 'test2']);
  });

  it('splits 5 messages', () => {
    expect(splitByLineBreak('test \n a  \n\n\ntest2 a\n\n\n test 3 \n\n\n test4 4 4 \n\n\ntest5 \n\n').length).toEqual(5);
  });
});