Merge pull request #27 from algolia/test/formatHits
test(formatHits): Add tests for formatHits
This commit is contained in:
commit
40ef992c5b
7 changed files with 432 additions and 26 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
.DS_Store
|
||||
node_modules/
|
||||
dist/
|
||||
npm-debug.log*
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@
|
|||
"mocha-jsdom": "^1.0.0",
|
||||
"mversion": "^1.10.1",
|
||||
"nd": "^1.2.0",
|
||||
"node-fixtures": "0.0.1",
|
||||
"node-sass": "^3.4.2",
|
||||
"npm-shrinkwrap": "^200.4.0",
|
||||
"onchange": "^2.0.0",
|
||||
|
|
|
|||
|
|
@ -102,16 +102,19 @@ class DocSearch {
|
|||
query: query,
|
||||
params: this.algoliaOptions
|
||||
}]).then((data) => {
|
||||
callback(this.formatHits(data.results[0].hits));
|
||||
callback(DocSearch.formatHits(data.results[0].hits));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Given a list of hits returned by the API, will reformat them to be used in
|
||||
// a Hogan template
|
||||
formatHits(receivedHits) {
|
||||
let hits = receivedHits.map((hit) => {
|
||||
hit._highlightResult = utils.mergeKeyWithParent(hit._highlightResult, 'hierarchy');
|
||||
static formatHits(receivedHits) {
|
||||
let clonedHits = utils.deepClone(receivedHits);
|
||||
let hits = clonedHits.map((hit) => {
|
||||
if (hit._highlightResult) {
|
||||
hit._highlightResult = utils.mergeKeyWithParent(hit._highlightResult, 'hierarchy');
|
||||
}
|
||||
return utils.mergeKeyWithParent(hit, 'hierarchy');
|
||||
});
|
||||
|
||||
|
|
@ -140,7 +143,7 @@ class DocSearch {
|
|||
|
||||
return {
|
||||
isCategoryHeader: hit.isCategoryHeader,
|
||||
isSubcategoryHeader: hit.isSubcategoryHeader,
|
||||
isSubCategoryHeader: hit.isSubCategoryHeader,
|
||||
category: category,
|
||||
subcategory: subcategory,
|
||||
title: displayTitle,
|
||||
|
|
|
|||
|
|
@ -24,10 +24,10 @@ let utils = {
|
|||
*/
|
||||
mergeKeyWithParent(object, property) {
|
||||
if (object[property] === undefined) {
|
||||
throw new Error(`[mergeKeyWithParent]: Object has no key ${property}`);
|
||||
return object;
|
||||
}
|
||||
if (typeof object[property] !== 'object') {
|
||||
throw new Error(`[mergeKeyWithParent]: Key ${property} is not an object`);
|
||||
return object;
|
||||
}
|
||||
let newObject = $.extend({}, object, object[property]);
|
||||
delete newObject[property];
|
||||
|
|
@ -144,9 +144,11 @@ let utils = {
|
|||
* @return {array}
|
||||
*/
|
||||
flattenAndFlagFirst(object, flag) {
|
||||
let values = this.values(object).map(value => {
|
||||
value[0][flag] = true;
|
||||
return value;
|
||||
let values = this.values(object).map(collection => {
|
||||
return collection.map((item, index) => {
|
||||
item[flag] = (index === 0);
|
||||
return item;
|
||||
});
|
||||
});
|
||||
return this.flatten(values);
|
||||
},
|
||||
|
|
@ -230,9 +232,16 @@ let utils = {
|
|||
snippet = `${snippet}…`;
|
||||
}
|
||||
return snippet;
|
||||
},
|
||||
/*
|
||||
* Deep clone an object.
|
||||
* Note: This will not clone functions and dates
|
||||
* @param {object} object Object to clone
|
||||
* @return {object}
|
||||
*/
|
||||
deepClone(object) {
|
||||
return JSON.parse(JSON.stringify(object));
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
export default utils;
|
||||
|
|
|
|||
|
|
@ -4,11 +4,11 @@
|
|||
import jsdom from 'mocha-jsdom';
|
||||
import expect from 'expect';
|
||||
import sinon from 'sinon';
|
||||
// import fixtures from 'node-fixtures';
|
||||
|
||||
describe('DocSearch', () => {
|
||||
let DocSearch;
|
||||
let $;
|
||||
|
||||
jsdom({useEach: true});
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
@ -309,4 +309,362 @@ describe('DocSearch', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatHits', () => {
|
||||
it('should not mutate the input', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(input).toNotBe(actual);
|
||||
});
|
||||
it('should set category headers to the first of each category', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}, {
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'Geo-search',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}, {
|
||||
hierarchy: {
|
||||
lvl0: 'Python',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].isCategoryHeader).toEqual(true);
|
||||
expect(actual[2].isCategoryHeader).toEqual(true);
|
||||
});
|
||||
it('should group items of same category together', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}, {
|
||||
hierarchy: {
|
||||
lvl0: 'Python',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}, {
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'Geo-search',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].category).toEqual('Ruby');
|
||||
expect(actual[1].category).toEqual('Ruby');
|
||||
expect(actual[2].category).toEqual('Python');
|
||||
});
|
||||
it('should mark all first elements as subcategories', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}, {
|
||||
hierarchy: {
|
||||
lvl0: 'Python',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}, {
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'Geo-search',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].isSubCategoryHeader).toEqual(true);
|
||||
expect(actual[2].isSubCategoryHeader).toEqual(true);
|
||||
});
|
||||
it('should mark new subcategories as such', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: 'Foo',
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}, {
|
||||
hierarchy: {
|
||||
lvl0: 'Python',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}, {
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: 'Bar',
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}, {
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'Geo-search',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].isSubCategoryHeader).toEqual(true);
|
||||
expect(actual[1].isSubCategoryHeader).toEqual(false);
|
||||
expect(actual[2].isSubCategoryHeader).toEqual(true);
|
||||
expect(actual[3].isSubCategoryHeader).toEqual(true);
|
||||
});
|
||||
it('should use highlighted category and subcategory if exists', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: 'Foo',
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
},
|
||||
_highlightResult: {
|
||||
hierarchy: {
|
||||
lvl0: {
|
||||
value: '<mark>Ruby</mark>'
|
||||
},
|
||||
lvl1: {
|
||||
value: '<mark>API</mark>'
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].category).toEqual('<mark>Ruby</mark>');
|
||||
expect(actual[0].subcategory).toEqual('<mark>API</mark>');
|
||||
});
|
||||
it('should use lvl2 as title', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: 'Foo',
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].title).toEqual('Foo');
|
||||
});
|
||||
it('should use lvl1 as title if no lvl2', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].title).toEqual('API');
|
||||
});
|
||||
it('should use lvl0 as title if no lvl2 nor lvl2', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: null,
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].title).toEqual('Ruby');
|
||||
});
|
||||
it('should concatenate lvl2+ for title if more', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: 'Geo-search',
|
||||
lvl3: 'Foo',
|
||||
lvl4: 'Bar',
|
||||
lvl5: 'Baz'
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].title).toEqual('Geo-search › Foo › Bar › Baz');
|
||||
});
|
||||
it('should concatenate highlighted elements', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: 'Geo-search',
|
||||
lvl3: 'Foo',
|
||||
lvl4: 'Bar',
|
||||
lvl5: 'Baz'
|
||||
},
|
||||
_highlightResult: {
|
||||
hierarchy: {
|
||||
lvl0: {
|
||||
value: '<mark>Ruby</mark>'
|
||||
},
|
||||
lvl1: {
|
||||
value: '<mark>API</mark>'
|
||||
},
|
||||
lvl2: {
|
||||
value: '<mark>Geo-search</mark>'
|
||||
},
|
||||
lvl3: {
|
||||
value: '<mark>Foo</mark>'
|
||||
},
|
||||
lvl4: {
|
||||
value: '<mark>Bar</mark>'
|
||||
},
|
||||
lvl5: {
|
||||
value: '<mark>Baz</mark>'
|
||||
}
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].title).toEqual('<mark>Geo-search</mark> › <mark>Foo</mark> › <mark>Bar</mark> › <mark>Baz</mark>');
|
||||
});
|
||||
it('should add ellipsis to content', () => {
|
||||
// Given
|
||||
let input = [{
|
||||
hierarchy: {
|
||||
lvl0: 'Ruby',
|
||||
lvl1: 'API',
|
||||
lvl2: null,
|
||||
lvl3: null,
|
||||
lvl4: null,
|
||||
lvl5: null
|
||||
},
|
||||
content: 'foo bar',
|
||||
_snippetResult: {
|
||||
content: {
|
||||
value: 'lorem <mark>foo</mark> bar ipsum.'
|
||||
}
|
||||
}
|
||||
}];
|
||||
|
||||
// When
|
||||
let actual = DocSearch.formatHits(input);
|
||||
|
||||
// Then
|
||||
expect(actual[0].text).toEqual('…lorem <mark>foo</mark> bar ipsum.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
2
test/fixtures/formatHits.json
vendored
2
test/fixtures/formatHits.json
vendored
|
|
@ -23,7 +23,7 @@
|
|||
"value": "API"
|
||||
},
|
||||
"lvl2": {
|
||||
"value": "<mark>Search</mark>"
|
||||
"value": "Search"
|
||||
},
|
||||
"lvl3": null,
|
||||
"lvl4": null,
|
||||
|
|
|
|||
|
|
@ -65,16 +65,17 @@ describe('utils', () => {
|
|||
expect(actual.lvl0).toNotEqual(42);
|
||||
expect(actual.lvl0).toEqual('bar');
|
||||
});
|
||||
it('should throw an error if no such key', () => {
|
||||
it('should do nothing if no such key', () => {
|
||||
// Given
|
||||
let input = {
|
||||
name: 'foo'
|
||||
};
|
||||
|
||||
// When
|
||||
expect(() => {
|
||||
utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
}).toThrow(Error);
|
||||
let actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
|
||||
// Then
|
||||
expect(actual).toBe(input);
|
||||
});
|
||||
it('should throw an error if key is no an object', () => {
|
||||
// Given
|
||||
|
|
@ -84,9 +85,10 @@ describe('utils', () => {
|
|||
};
|
||||
|
||||
// When
|
||||
expect(() => {
|
||||
utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
}).toThrow(Error);
|
||||
let actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
|
||||
// Then
|
||||
expect(actual).toBe(input);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -193,11 +195,11 @@ describe('utils', () => {
|
|||
// Then
|
||||
expect(actual).toEqual([
|
||||
{name: 'Tim', category: 'dev', isTop: true},
|
||||
{name: 'Vincent', category: 'dev'},
|
||||
{name: 'AlexS', category: 'dev'},
|
||||
{name: 'Vincent', category: 'dev', isTop: false},
|
||||
{name: 'AlexS', category: 'dev', isTop: false},
|
||||
{name: 'Ben', category: 'sales', isTop: true},
|
||||
{name: 'Jeremy', category: 'sales'},
|
||||
{name: 'AlexK', category: 'sales'}
|
||||
{name: 'Jeremy', category: 'sales', isTop: false},
|
||||
{name: 'AlexK', category: 'sales', isTop: false}
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -328,4 +330,38 @@ describe('utils', () => {
|
|||
expect(actual).toEqual('This is an <mark>finished</mark> sentence…');
|
||||
});
|
||||
});
|
||||
|
||||
describe('deepClone', () => {
|
||||
it('should create an object with the exact same value', () => {
|
||||
// Given
|
||||
let input = {
|
||||
foo: {
|
||||
bar: 'baz'
|
||||
}
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.deepClone(input);
|
||||
|
||||
// Then
|
||||
expect(actual.foo.bar).toEqual('baz');
|
||||
});
|
||||
it('should not change the initial object', () => {
|
||||
// Given
|
||||
let input = {
|
||||
foo: {
|
||||
bar: 'baz'
|
||||
}
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.deepClone(input);
|
||||
input.foo.bar = 42;
|
||||
|
||||
// Then
|
||||
expect(input.foo.bar).toEqual(42);
|
||||
expect(actual.foo.bar).toNotEqual(42);
|
||||
expect(actual.foo.bar).toEqual('baz');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue