Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit f6be59c

Browse files
committed
chore(ngdocs): provide test code for lunr search in docs
1 parent 46dfb92 commit f6be59c

File tree

4 files changed

+199
-87
lines changed

4 files changed

+199
-87
lines changed
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
describe("DocsNavigationCtrl", function() {
2+
3+
beforeEach(module('docsApp'));
4+
5+
var ctrl, $scope;
6+
7+
beforeEach(function() {
8+
module(function($provide) {
9+
$provide.value('docsPages', []);
10+
$provide.factory('docsSearch', function() {
11+
return function(q) {
12+
return ['one','two','three'];
13+
};
14+
});
15+
});
16+
inject(function($controller, $rootScope, $location, docsSearch) {
17+
$scope = $rootScope.$new();
18+
ctrl = $controller('DocsNavigationCtrl', {
19+
$scope : $scope,
20+
$location : $location,
21+
docsSearch : docsSearch
22+
});
23+
});
24+
});
25+
26+
it("should search and return data from docsSearch", function() {
27+
$scope.search('1234')
28+
expect($scope.results.join(',')).toBe('one,two,three');
29+
expect($scope.hasResults).toBe(true);
30+
});
31+
32+
it("should avoid searching if the search term is too short", function() {
33+
$scope.search('1')
34+
expect($scope.results.length).toBe(0);
35+
expect($scope.hasResults).toBe(false);
36+
});
37+
38+
it("should set the columns classname based on the total grouped results", function() {
39+
$scope.search('1234');
40+
expect($scope.colClassName).toBe('cols-3');
41+
42+
$scope.search('1');
43+
expect($scope.colClassName).toBe(null);
44+
});
45+
46+
it("should hide and clear the results when called", function() {
47+
$scope.hasResults = true;
48+
$scope.results = ['one'];
49+
$scope.colClassName = '...';
50+
$scope.hideResults();
51+
expect($scope.hasResults).toBe(false);
52+
expect($scope.results.length).toBe(0);
53+
expect($scope.colClassName).toBe(null);
54+
});
55+
56+
it("should hide, clear and change the path of the page when submitted", inject(function($location) {
57+
$scope.hasResults = true;
58+
$scope.results = {
59+
api : [
60+
{url : '/home'}
61+
],
62+
tutorial : [
63+
{url : '/tutorial'}
64+
]
65+
};
66+
$scope.submit();
67+
expect($location.path()).toBe('/home');
68+
expect($scope.results.length).toBe(0);
69+
expect($scope.hasResults).toBe(false);
70+
}));
71+
72+
});

docs/component-spec/docsSearchSpec.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
describe("docsSearch", function() {
2+
3+
beforeEach(module('docsApp'));
4+
5+
var interceptedLunrResults;
6+
beforeEach(function() {
7+
interceptedLunrResults = [];
8+
});
9+
10+
beforeEach(function() {
11+
module(function($provide) {
12+
var results = [];
13+
results[0] = { section: 'tutorial', shortName: 'item one', keywords: 'item, one, 1' };
14+
results[1] = { section: 'tutorial', shortName: 'item man', keywords: 'item, man' };
15+
results[2] = { section: 'api', shortName: 'item other', keywords: 'item, other' };
16+
results[3] = { section: 'cookbook', shortName: 'item cookbook', keywords: 'item, other' };
17+
results[4] = { section: 'api', shortName: 'ngRepeat', keywords: 'item, other' };
18+
19+
$provide.value('NG_PAGES', results);
20+
$provide.factory('lunrSearch', function() {
21+
return function() {
22+
return {
23+
store : function(value) {
24+
interceptedLunrResults.push(value);
25+
},
26+
search : function(q) {
27+
var data = [];
28+
angular.forEach(results, function(res, i) {
29+
data.push({ ref : i });
30+
});
31+
return data;
32+
}
33+
}
34+
};
35+
});
36+
});
37+
});
38+
39+
it("should lookup and organize values properly", inject(function(docsSearch) {
40+
var items = docsSearch('item');
41+
expect(items['api'].length).toBe(2);
42+
}));
43+
44+
it("should place cookbook items in the tutorial", inject(function(docsSearch) {
45+
var items = docsSearch('item');
46+
expect(items['tutorial'].length).toBe(3);
47+
}));
48+
49+
it("should return all results without a search", inject(function(docsSearch) {
50+
var items = docsSearch();
51+
expect(items['tutorial'].length).toBe(3);
52+
expect(items['api'].length).toBe(2);
53+
}));
54+
55+
it("should store values with and without a ng prefix", inject(function(docsSearch) {
56+
expect(interceptedLunrResults[4].title).toBe('ngRepeat repeat');
57+
}));
58+
59+
});

docs/src/templates/css/docs.css

+1
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,7 @@ pre ol li {
461461
box-shadow:-6px 0 5px #555;
462462
display:block;
463463
border-radius:10px;
464+
}
464465

465466
.docs-version-jump {
466467
width:180px;

docs/src/templates/js/docs.js

+67-87
Original file line numberDiff line numberDiff line change
@@ -62,29 +62,31 @@ docsApp.controller.DocsVersionsCtrl = ['$scope', '$window', 'NG_VERSIONS', funct
6262
};
6363
}];
6464

65-
docsApp.controller.DocsNavigationCtrl = ['$scope', 'fullTextSearch', '$location', function($scope, fullTextSearch, $location) {
66-
fullTextSearch.init();
65+
docsApp.controller.DocsNavigationCtrl = ['$scope', '$location', 'docsSearch', function($scope, $location, docsSearch) {
66+
function clearResults() {
67+
$scope.results = [];
68+
$scope.colClassName = null;
69+
$scope.hasResults = false;
70+
}
71+
6772
$scope.search = function(q) {
68-
fullTextSearch.search(q, function(results) {
69-
if(q && q.length >= 4) {
70-
$scope.results = results;
71-
var totalSections = 0;
72-
for(var i in results) {
73-
++totalSections;
74-
}
75-
if(totalSections > 0) {
76-
$scope.colClassName = 'cols-' + totalSections;
77-
$scope.hasResults = true;
78-
}
79-
else {
80-
$scope.hasResults = false;
81-
}
73+
var MIN_SEARCH_LENGTH = 4;
74+
if(q.length >= MIN_SEARCH_LENGTH) {
75+
var results = docsSearch(q);
76+
var totalSections = 0;
77+
for(var i in results) {
78+
++totalSections;
8279
}
83-
else {
84-
$scope.hasResults = false;
80+
if(totalSections > 0) {
81+
$scope.colClassName = 'cols-' + totalSections;
82+
$scope.hasResults = true;
8583
}
86-
if(!$scope.$$phase) $scope.$apply();
87-
});
84+
$scope.results = results;
85+
}
86+
else {
87+
clearResults();
88+
}
89+
if(!$scope.$$phase) $scope.$apply();
8890
};
8991
$scope.submit = function() {
9092
var result;
@@ -100,82 +102,60 @@ docsApp.controller.DocsNavigationCtrl = ['$scope', 'fullTextSearch', '$location'
100102
}
101103
};
102104
$scope.hideResults = function() {
103-
$scope.hasResults = false;
105+
clearResults();
104106
$scope.q = '';
105107
};
106108
}];
107109

108-
docsApp.serviceFactory.fullTextSearch = ['$q', '$rootScope', 'NG_PAGES', function($q, $rootScope, NG_PAGES) {
109-
return {
110-
dbName : 'docs',
111-
indexName : 'docsindex',
110+
docsApp.serviceFactory.lunrSearch = function() {
111+
return function(properties) {
112+
var engine = lunr(properties);
113+
return {
114+
store : function(values) {
115+
engine.add(values);
116+
},
117+
search : function(q) {
118+
return engine.search(q);
119+
}
120+
};
121+
};
122+
};
112123

113-
init : function(onReady) {
114-
this.init = function() {};
124+
docsApp.serviceFactory.docsSearch = ['$rootScope','lunrSearch', 'NG_PAGES',
125+
function($rootScope, lunrSearch, NG_PAGES) {
115126

116-
var self = this;
117-
this.deferReady = $q.defer();
118-
this.readyPromise = this.deferReady.promise;
127+
var index = lunrSearch(function() {
128+
this.ref('id');
129+
this.field('title', {boost: 50});
130+
this.field('description', { boost : 20 });
131+
});
119132

120-
this.engine = lunr(function () {
121-
this.ref('id');
122-
this.field('title', {boost: 50});
123-
this.field('description', { boost : 20 });
124-
});
125-
this.prepare();
126-
this.onReady();
127-
},
128-
onReady : function() {
129-
this.ready = true;
130-
var self = this;
131-
self.deferReady.resolve();
132-
if(!$rootScope.$$phase) {
133-
$rootScope.$apply();
134-
}
135-
},
136-
whenReady : function(fn) {
137-
if(this.ready) {
138-
fn();
139-
}
140-
else {
141-
this.init();
142-
this.readyPromise.then(fn);
133+
angular.forEach(NG_PAGES, function(page, i) {
134+
var title = page.shortName;
135+
if(title.charAt(0) == 'n' && title.charAt(1) == 'g') {
136+
title = title + ' ' + title.charAt(2).toLowerCase() + title.substr(3);
137+
}
138+
index.store({
139+
id: i,
140+
title: title,
141+
description: page.keywords
142+
});
143+
});
144+
145+
return function(q) {
146+
var results = {};
147+
angular.forEach(index.search(q), function(result) {
148+
var item = NG_PAGES[result.ref];
149+
var section = item.section;
150+
if(section == 'cookbook') {
151+
section = 'tutorial';
143152
}
144-
},
145-
prepare : function(injector, callback) {
146-
for(var i=0;i<NG_PAGES.length;i++) {
147-
var page = NG_PAGES[i];
148-
var title = page.shortName;
149-
if(title.charAt(0) == 'n' && title.charAt(1) == 'g') {
150-
title = title + ' ' + title.substr(2);
151-
}
152-
this.engine.add({
153-
id: i,
154-
title: title,
155-
description: page.keywords
156-
});
153+
results[section] = results[section] || [];
154+
if(results[section].length < 15) {
155+
results[section].push(item);
157156
}
158-
},
159-
search : function(q, onReady) {
160-
var self = this;
161-
this.whenReady(function() {
162-
var data = [];
163-
var results = self.engine.search(q);
164-
var groups = {};
165-
angular.forEach(results, function(result) {
166-
var item = NG_PAGES[result.ref];
167-
var section = item.section;
168-
if(section == 'cookbook') {
169-
section = 'tutorial';
170-
}
171-
groups[section] = groups[section] || [];
172-
if(groups[section].length < 15) {
173-
groups[section].push(item);
174-
}
175-
});
176-
onReady(groups);
177-
});
178-
}
157+
});
158+
return results;
179159
};
180160
}];
181161

0 commit comments

Comments
 (0)