diff --git a/src/engines/searchIndexingEngine.test.js b/src/engines/searchIndexingEngine.test.js
new file mode 100644
index 000000000..46efda3cf
--- /dev/null
+++ b/src/engines/searchIndexingEngine.test.js
@@ -0,0 +1,9 @@
+import searchIndexingEngine from './searchIndexingEngine';
+
+describe('searchIndexingEngine', () => {
+ it('parses the tokens from the given string', () => {
+ const str = 'Creates an object with the same values as the provided object and keys generated by running the provided function for each key. Use Object.keys(obj) to iterate over the object\'s keys. Use Array.prototype.reduce() to create a new object with the same values and mapped keys using fn. Additionally, yeet caress seed recreational practicality';
+ const result = ['creat', 'object', 'valu', 'provid', 'kei', 'gener', 'run', 'function', 'us', 'obj', 'iter', "object'", 'arrai', 'prototyp', 'reduc', 'new', 'map', 'fn', 'addition', 'yeet', 'caress', 'seed', 'recreat', 'practic'];
+ expect(searchIndexingEngine(str)).toEqual(result);
+ });
+});
diff --git a/src/functions/hooks/useFetchSearchIndex.js b/src/functions/hooks/useFetchSearchIndex.js
index 05180fca1..029ae3feb 100644
--- a/src/functions/hooks/useFetchSearchIndex.js
+++ b/src/functions/hooks/useFetchSearchIndex.js
@@ -1,6 +1,7 @@
import { useEffect } from 'react';
import { startIndexFetch, finishIndexFetch } from 'state/search';
+/* istanbul ignore next */
const useFetchSearchIndex = dispatch => useEffect(() => {
if(typeof window !== 'undefined' && typeof fetch !== 'undefined') {
dispatch(startIndexFetch());
diff --git a/src/functions/hooks/useMedia.js b/src/functions/hooks/useMedia.js
index 6b08c48ea..c09962213 100644
--- a/src/functions/hooks/useMedia.js
+++ b/src/functions/hooks/useMedia.js
@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react';
+/* istanbul ignore next */
/**
* Given an array of queries and an array of values to use on each of them
* matching, this hook runs the media queries and returns the result.
diff --git a/src/functions/utils/array.test.js b/src/functions/utils/array.test.js
new file mode 100644
index 000000000..a98bc383e
--- /dev/null
+++ b/src/functions/utils/array.test.js
@@ -0,0 +1,88 @@
+import {
+ determineExpertiseFromTags,
+ stripExpertiseFromTags,
+ transformSnippetIndex,
+ uniqueElements,
+ similarity,
+ weightedSample,
+ chunk
+} from './array';
+
+describe('determineExpertiseFromTags', () => {
+ it('determines expertise from tags', () => {
+ expect(determineExpertiseFromTags(['array', 'advanced'])).toBe('advanced');
+ });
+ it('returns default expertise if none is found', () => {
+ expect(determineExpertiseFromTags(['array'])).toBe('intermediate');
+ });
+});
+
+describe('stripExpertiseFromTags', () => {
+ it('strips expertise from tags', () => {
+ expect(stripExpertiseFromTags(['array', 'advanced'])).toEqual(['array']);
+ });
+});
+
+describe('transformSnippetIndex', () => {
+ it('transforms the snippet index', () => {
+ const edges = [
+ {
+ node: {
+ title: 'a',
+ expertise: 'intermediate',
+ tags: {
+ primary: 'array',
+ },
+ language: {
+ long: 'lang',
+ short: 'l',
+ },
+ html: {
+ description: 'desc ',
+ },
+ slug: '/a',
+ searchTokens: '',
+ irrelevantStuff: 'data',
+ },
+ },
+ ];
+ const result = transformSnippetIndex(edges);
+ expect(result[0].title).toBe(edges[0].node.title);
+ expect(result[0].expertise).toBe(edges[0].node.expertise);
+ expect(result[0].primaryTag).toBe(edges[0].node.tags.primary);
+ expect(result[0].language).toBe(edges[0].node.language.long);
+ expect(result[0].description).toBe(edges[0].node.html.description.trim());
+ expect(result[0].url).toBe(edges[0].node.slug);
+ expect(result[0].searchTokens).toBe(edges[0].node.searchTokens);
+ expect(result[0].irrelevantStuff).toBe(undefined);
+ });
+});
+
+describe('uniqueElements', () => {
+ it('returns the unique elements in an array', () => {
+ expect(uniqueElements([1, 2, 2, 3, 4, 4, 5])).toEqual([1, 2, 3, 4, 5]);
+ });
+});
+
+describe('similarity', () => {
+ it('returns an array of elements that appear in both arrays.', () => {
+ expect(similarity([1, 2, 3], [1, 2, 4])).toEqual([1, 2]);
+ });
+});
+
+describe('weightedSample', () => {
+ it('returns a random element from the array', () => {
+ const arr = [3, 7, 9, 11];
+ const weights = [0.1, 0.2, 0.6, 0.1];
+ expect(arr.includes(weightedSample(arr, weights))).toBeTruthy();
+ });
+});
+
+describe('chunk', () => {
+ it('chunks an array with a remainder', () => {
+ expect(chunk([1, 2, 3, 4, 5], 2)).toEqual([[1, 2], [3, 4], [5]]);
+ });
+ it('chunks an empty array', () => {
+ expect(chunk([])).toEqual([]);
+ });
+});
diff --git a/src/functions/utils/function.js b/src/functions/utils/function.js
index c4bb7ed69..c0c292609 100644
--- a/src/functions/utils/function.js
+++ b/src/functions/utils/function.js
@@ -1,5 +1,7 @@
+/* istanbul ignore next */
/** Creates a throttled function that only invokes the provided function at
- * most once per every wait milliseconds */
+ * most once per every wait milliseconds
+ */
export const throttle = (fn, wait) => {
let inThrottle, lastFn, lastTime;
return function() {
diff --git a/src/functions/utils/math.test.js b/src/functions/utils/math.test.js
new file mode 100644
index 000000000..1712dde66
--- /dev/null
+++ b/src/functions/utils/math.test.js
@@ -0,0 +1,9 @@
+import {
+ mapNumRange
+} from './math';
+
+describe('mapNumRange', () => {
+ it('maps 5 to the range 0-100 from 0-10', () => {
+ expect(mapNumRange(5, 0, 10, 0, 100)).toEqual(50);
+ });
+});
diff --git a/src/functions/utils/string.test.js b/src/functions/utils/string.test.js
index 7abdb27b5..939fa416a 100644
--- a/src/functions/utils/string.test.js
+++ b/src/functions/utils/string.test.js
@@ -1,4 +1,14 @@
-import { trimWhiteSpace } from './string';
+import {
+ trimWhiteSpace,
+ capitalize,
+ optimizeNodes,
+ optimizeAllNodes,
+ getURLParameters,
+ getBaseURL,
+ stripMarkdownFormat,
+ toKebabCase,
+ convertToSeoSlug
+} from './string';
const stringComparator = (str1, str2) => {
return expect(str1.split(' ').sort()).toEqual(str2.split(' ').sort());
@@ -99,3 +109,82 @@ describe('trimWhiteSpace', () => {
});
});
});
+
+describe('capitalize', () => {
+ it('capitalizes the first letter of a string', () => {
+ expect(capitalize('fooBar')).toBe('FooBar');
+ });
+
+ it('capitalizes the first letter of a string, lowercases the rest', () => {
+ expect(capitalize('fooBar', true)).toBe('Foobar');
+ });
+});
+
+describe('optimizeNodes', () => {
+ it('optimizes nodes', () => {
+ const data = 'foobar';
+ const regexp = /([^\0<]*?)<\/span>([\n\r\s]*)([^\0]*?)<\/span>/gm;
+ const replacer = (match, p1, p2, p3) =>
+ `${p1}${p2}${p3}`;
+ const result = 'foobar';
+ expect(optimizeNodes(data, regexp, replacer)).toBe(result);
+ });
+});
+
+describe('optimizeAllNodes', () => {
+ it('optimizes all nodes', () => {
+ const data = 'foobar foobar foobar';
+ const result = 'foobar foobar foobar';
+ expect(optimizeAllNodes(data)).toBe(result);
+ });
+});
+
+describe('getURLParameters', () => {
+ it('returns an object containing the parameters of the current URL', () => {
+ expect(getURLParameters('http://url.com/page?name=Adam&surname=Smith')).toEqual({
+ name: 'Adam',
+ surname: 'Smith',
+ });
+ });
+});
+
+describe('getBaseURL', () => {
+ it('returns the current URL without parameters', () => {
+ expect(getBaseURL('http://url.com/page?name=Adam&surname=Smith')).toBe('http://url.com/page');
+ });
+});
+
+describe('stripMarkdownFormat', () => {
+ it('strips down markdown format', () => {
+ const md = 'I have `code` and [links](lala). \nI am also multiline.';
+ const output = 'I have code and links. I am also multiline.';
+ expect(stripMarkdownFormat(md)).toBe(output);
+ });
+});
+
+describe('toKebabCase', () => {
+ it('works with camel case', () => {
+ expect(toKebabCase('camelCase')).toBe('camel-case');
+ });
+ it('works with spaces', () => {
+ expect(toKebabCase('some text')).toBe('some-text');
+ });
+ it('works with mixed strings', () => {
+ expect(toKebabCase('some-mixed-string With spaces-underscores-and-hyphens')).toBe(
+ 'some-mixed-string-with-spaces-underscores-and-hyphens'
+ );
+ });
+ it('works with capital words in camel case', () => {
+ expect(
+ toKebabCase('IAmListeningToFMWhileLoadingDifferentURLOnMyBrowserAndAlsoEditingSomeXMLAndHTML')
+ ).toBe(
+ 'i-am-listening-to-fm-while-loading-different-url-on-my-browser-and-also-editing-some-xml-and-html'
+ );
+ });
+});
+
+describe('convertToSeoSlug', () => {
+ it('returns the SEO-friendly slug', () => {
+ expect(convertToSeoSlug('mySnippet')).toBe('/my-snippet');
+ });
+});