Commit 7280a587 by Andy Armstrong

Merge pull request #9885 from edx/andya/teams-router-testing

Improve team testing of routers
parents 5e2290f9 d09034e0
define([ // jshint ignore:line define(["backbone"],
], function(Backbone) {
function() { 'use strict';
'use strict'; var getLocationHash, preventBackboneChangingUrl;
var getLocationHash;
/**
* Helper method that returns url hash.
* @return {String} Returns anchor part of current url.
*/
getLocationHash = function() {
return window.location.hash;
};
return { /**
'getLocationHash': getLocationHash * Helper method that returns url hash.
}; * @return {String} Returns anchor part of current url.
*/
getLocationHash = function() {
return window.location.hash;
};
}); /**
* Prevent Backbone tests from changing the browser's URL.
*
* This function modifies Backbone so that tests can navigate
* without modifying the browser's URL. It works be adding
* stub versions of Backbone's hash functions so that updating
* the hash doesn't change the URL but instead updates a
* local object. The router's callbacks are still invoked
* so that to the test it appears that navigation is behaving
* as expected.
*
* Note: it is important that tests don't update the browser's
* URL because subsequent tests could find themselves in an
* unexpected navigation state.
*/
preventBackboneChangingUrl = function() {
var history = {
currentFragment: ''
};
// Stub out the Backbone router so that the browser doesn't actually navigate
spyOn(Backbone.history, '_updateHash').andCallFake(function (location, fragment, replace) {
history.currentFragment = fragment;
});
// Stub out getHash so that Backbone thinks that the browser has navigated
spyOn(Backbone.history, 'getHash').andCallFake(function () {
return history.currentFragment;
});
};
return {
'getLocationHash': getLocationHash,
'preventBackboneChangingUrl': preventBackboneChangingUrl
};
});
define(['jquery', 'backbone', 'teams/js/teams_tab_factory', define(['jquery', 'backbone', 'teams/js/teams_tab_factory',
'teams/js/spec_helpers/team_spec_helpers'], 'common/js/spec_helpers/page_helpers', 'teams/js/spec_helpers/team_spec_helpers'],
function($, Backbone, TeamsTabFactory, TeamSpecHelpers) { function($, Backbone, TeamsTabFactory, PageHelpers, TeamSpecHelpers) {
'use strict'; 'use strict';
describe("Teams Tab Factory", function() { describe("Teams Tab Factory", function() {
...@@ -10,6 +10,7 @@ define(['jquery', 'backbone', 'teams/js/teams_tab_factory', ...@@ -10,6 +10,7 @@ define(['jquery', 'backbone', 'teams/js/teams_tab_factory',
beforeEach(function() { beforeEach(function() {
setFixtures('<section class="teams-content"></section>'); setFixtures('<section class="teams-content"></section>');
PageHelpers.preventBackboneChangingUrl();
}); });
afterEach(function() { afterEach(function() {
...@@ -17,11 +18,6 @@ define(['jquery', 'backbone', 'teams/js/teams_tab_factory', ...@@ -17,11 +18,6 @@ define(['jquery', 'backbone', 'teams/js/teams_tab_factory',
}); });
it('can render the "Teams" tab', function() { it('can render the "Teams" tab', function() {
// Hack to make sure the URL fragments from earlier
// tests don't interfere with Backbone routing by the
// teams tab view
document.location.hash = '';
initializeTeamsTabFactory(); initializeTeamsTabFactory();
expect($('.teams-content').text()).toContain('See all teams in your course, organized by topic'); expect($('.teams-content').text()).toContain('See all teams in your course, organized by topic');
}); });
......
...@@ -3,10 +3,11 @@ define([ ...@@ -3,10 +3,11 @@ define([
'underscore', 'underscore',
'backbone', 'backbone',
'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/ajax_helpers',
'common/js/spec_helpers/page_helpers',
'teams/js/views/edit_team', 'teams/js/views/edit_team',
'teams/js/models/team', 'teams/js/models/team',
'teams/js/spec_helpers/team_spec_helpers' 'teams/js/spec_helpers/team_spec_helpers'
], function ($, _, Backbone, AjaxHelpers, TeamEditView, TeamModel, TeamSpecHelpers) { ], function ($, _, Backbone, AjaxHelpers, PageHelpers, TeamEditView, TeamModel, TeamSpecHelpers) {
'use strict'; 'use strict';
describe('CreateEditTeam', function() { describe('CreateEditTeam', function() {
...@@ -90,6 +91,7 @@ define([ ...@@ -90,6 +91,7 @@ define([
beforeEach(function () { beforeEach(function () {
setFixtures('<div class="teams-content"></div>'); setFixtures('<div class="teams-content"></div>');
PageHelpers.preventBackboneChangingUrl();
spyOn(Backbone.history, 'navigate'); spyOn(Backbone.history, 'navigate');
}); });
......
...@@ -6,8 +6,9 @@ define([ ...@@ -6,8 +6,9 @@ define([
'teams/js/views/instructor_tools', 'teams/js/views/instructor_tools',
'teams/js/views/team_utils', 'teams/js/views/team_utils',
'teams/js/spec_helpers/team_spec_helpers', 'teams/js/spec_helpers/team_spec_helpers',
'common/js/spec_helpers/ajax_helpers' 'common/js/spec_helpers/ajax_helpers',
], function ($, Backbone, _, Team, InstructorToolsView, TeamUtils, TeamSpecHelpers, AjaxHelpers) { 'common/js/spec_helpers/page_helpers'
], function ($, Backbone, _, Team, InstructorToolsView, TeamUtils, TeamSpecHelpers, AjaxHelpers, PageHelpers) {
'use strict'; 'use strict';
describe('Instructor Tools', function () { describe('Instructor Tools', function () {
...@@ -37,6 +38,7 @@ define([ ...@@ -37,6 +38,7 @@ define([
beforeEach(function () { beforeEach(function () {
setFixtures('<div id="page-prompt"></div>'); setFixtures('<div id="page-prompt"></div>');
PageHelpers.preventBackboneChangingUrl();
spyOn(Backbone.history, 'navigate'); spyOn(Backbone.history, 'navigate');
spyOn(TeamUtils, 'showMessage'); spyOn(TeamUtils, 'showMessage');
view = createInstructorTools().render(); view = createInstructorTools().render();
......
...@@ -3,13 +3,16 @@ define([ ...@@ -3,13 +3,16 @@ define([
'backbone', 'backbone',
'logger', 'logger',
'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/ajax_helpers',
'common/js/spec_helpers/page_helpers',
'common/js/spec_helpers/spec_helpers', 'common/js/spec_helpers/spec_helpers',
'teams/js/views/teams_tab', 'teams/js/views/teams_tab',
'teams/js/spec_helpers/team_spec_helpers' 'teams/js/spec_helpers/team_spec_helpers'
], function ($, Backbone, Logger, AjaxHelpers, SpecHelpers, TeamsTabView, TeamSpecHelpers) { ], function ($, Backbone, Logger, AjaxHelpers, PageHelpers, SpecHelpers, TeamsTabView, TeamSpecHelpers) {
'use strict'; 'use strict';
describe('TeamsTab', function() { describe('TeamsTab', function() {
var requests;
var expectError = function (teamsTabView, text) { var expectError = function (teamsTabView, text) {
expect(teamsTabView.$('.warning').text()).toContain(text); expect(teamsTabView.$('.warning').text()).toContain(text);
}; };
...@@ -18,13 +21,31 @@ define([ ...@@ -18,13 +21,31 @@ define([
expect(element.focus).toHaveBeenCalled(); expect(element.focus).toHaveBeenCalled();
}; };
var createTeamsTabView = function(options) { var verifyTeamsRequest = function(options) {
AjaxHelpers.expectRequestURL(requests, TeamSpecHelpers.testContext.teamsUrl,
_.extend(
{
topic_id: TeamSpecHelpers.testTopicID,
expand: 'user',
course_id: TeamSpecHelpers.testCourseID,
order_by: '',
page: '1',
page_size: '10',
text_search: ''
},
options
));
};
var createTeamsTabView = function(test, options) {
var teamsTabView = new TeamsTabView( var teamsTabView = new TeamsTabView(
{ {
el: $('.teams-content'), el: $('.teams-content'),
context: TeamSpecHelpers.createMockContext(options) context: TeamSpecHelpers.createMockContext(options)
} }
); );
requests = AjaxHelpers.requests(test);
PageHelpers.preventBackboneChangingUrl();
teamsTabView.start(); teamsTabView.start();
return teamsTabView; return teamsTabView;
}; };
...@@ -39,7 +60,7 @@ define([ ...@@ -39,7 +60,7 @@ define([
describe('Navigation', function () { describe('Navigation', function () {
it('does not render breadcrumbs for the top level tabs', function() { it('does not render breadcrumbs for the top level tabs', function() {
var teamsTabView = createTeamsTabView(); var teamsTabView = createTeamsTabView(this);
teamsTabView.router.navigate('#my-teams', {trigger: true}); teamsTabView.router.navigate('#my-teams', {trigger: true});
expect(teamsTabView.$('.breadcrumbs').length).toBe(0); expect(teamsTabView.$('.breadcrumbs').length).toBe(0);
teamsTabView.router.navigate('#browse', {trigger: true}); teamsTabView.router.navigate('#browse', {trigger: true});
...@@ -47,21 +68,20 @@ define([ ...@@ -47,21 +68,20 @@ define([
}); });
it('does not interfere with anchor links to #content', function () { it('does not interfere with anchor links to #content', function () {
var teamsTabView = createTeamsTabView(); var teamsTabView = createTeamsTabView(this);
teamsTabView.router.navigate('#content', {trigger: true}); teamsTabView.router.navigate('#content', {trigger: true});
expect(teamsTabView.$('.wrapper-msg')).toHaveClass('is-hidden'); expect(teamsTabView.$('.wrapper-msg')).toHaveClass('is-hidden');
}); });
it('displays and focuses an error message when trying to navigate to a nonexistent page', function () { it('displays and focuses an error message when trying to navigate to a nonexistent page', function () {
var teamsTabView = createTeamsTabView(); var teamsTabView = createTeamsTabView(this);
teamsTabView.router.navigate('no_such_page', {trigger: true}); teamsTabView.router.navigate('no_such_page', {trigger: true});
expectError(teamsTabView, 'The page "no_such_page" could not be found.'); expectError(teamsTabView, 'The page "no_such_page" could not be found.');
expectFocus(teamsTabView.$('.warning')); expectFocus(teamsTabView.$('.warning'));
}); });
it('displays and focuses an error message when trying to navigate to a nonexistent topic', function () { it('displays and focuses an error message when trying to navigate to a nonexistent topic', function () {
var requests = AjaxHelpers.requests(this), var teamsTabView = createTeamsTabView(this);
teamsTabView = createTeamsTabView();
teamsTabView.router.navigate('topics/no_such_topic', {trigger: true}); teamsTabView.router.navigate('topics/no_such_topic', {trigger: true});
AjaxHelpers.expectRequest(requests, 'GET', '/api/team/v0/topics/no_such_topic,course/1', null); AjaxHelpers.expectRequest(requests, 'GET', '/api/team/v0/topics/no_such_topic,course/1', null);
AjaxHelpers.respondWithError(requests, 404); AjaxHelpers.respondWithError(requests, 404);
...@@ -70,8 +90,7 @@ define([ ...@@ -70,8 +90,7 @@ define([
}); });
it('displays and focuses an error message when trying to navigate to a nonexistent team', function () { it('displays and focuses an error message when trying to navigate to a nonexistent team', function () {
var requests = AjaxHelpers.requests(this), var teamsTabView = createTeamsTabView(this);
teamsTabView = createTeamsTabView();
teamsTabView.router.navigate('teams/' + TeamSpecHelpers.testTopicID + '/no_such_team', {trigger: true}); teamsTabView.router.navigate('teams/' + TeamSpecHelpers.testTopicID + '/no_such_team', {trigger: true});
AjaxHelpers.expectRequest(requests, 'GET', '/api/team/v0/teams/no_such_team?expand=user', null); AjaxHelpers.expectRequest(requests, 'GET', '/api/team/v0/teams/no_such_team?expand=user', null);
AjaxHelpers.respondWithError(requests, 404); AjaxHelpers.respondWithError(requests, 404);
...@@ -80,8 +99,7 @@ define([ ...@@ -80,8 +99,7 @@ define([
}); });
it('displays and focuses an error message when it receives a 401 AJAX response', function () { it('displays and focuses an error message when it receives a 401 AJAX response', function () {
var requests = AjaxHelpers.requests(this), var teamsTabView = createTeamsTabView(this).render();
teamsTabView = createTeamsTabView().render();
teamsTabView.router.navigate('topics/' + TeamSpecHelpers.testTopicID, {trigger: true}); teamsTabView.router.navigate('topics/' + TeamSpecHelpers.testTopicID, {trigger: true});
AjaxHelpers.respondWithError(requests, 401); AjaxHelpers.respondWithError(requests, 401);
expectError(teamsTabView, "Your request could not be completed. Reload the page and try again."); expectError(teamsTabView, "Your request could not be completed. Reload the page and try again.");
...@@ -89,8 +107,7 @@ define([ ...@@ -89,8 +107,7 @@ define([
}); });
it('displays and focuses an error message when it receives a 500 AJAX response', function () { it('displays and focuses an error message when it receives a 500 AJAX response', function () {
var requests = AjaxHelpers.requests(this), var teamsTabView = createTeamsTabView(this).render();
teamsTabView = createTeamsTabView().render();
teamsTabView.router.navigate('topics/' + TeamSpecHelpers.testTopicID, {trigger: true}); teamsTabView.router.navigate('topics/' + TeamSpecHelpers.testTopicID, {trigger: true});
AjaxHelpers.respondWithError(requests, 500); AjaxHelpers.respondWithError(requests, 500);
expectError(teamsTabView, "Your request could not be completed due to a server problem. Reload the page and try again. If the issue persists, click the Help tab to report the problem."); expectError(teamsTabView, "Your request could not be completed due to a server problem. Reload the page and try again. If the issue persists, click the Help tab to report the problem.");
...@@ -98,7 +115,7 @@ define([ ...@@ -98,7 +115,7 @@ define([
}); });
it('does not navigate to the topics page when syncing its collection if not on the search page', function () { it('does not navigate to the topics page when syncing its collection if not on the search page', function () {
var teamsTabView = createTeamsTabView(), var teamsTabView = createTeamsTabView(this),
collection = TeamSpecHelpers.createMockTeams(); collection = TeamSpecHelpers.createMockTeams();
teamsTabView.createTeamsListView({ teamsTabView.createTeamsListView({
collection: collection, collection: collection,
...@@ -153,8 +170,7 @@ define([ ...@@ -153,8 +170,7 @@ define([
} }
] ]
}, function (url, expectedEvent) { }, function (url, expectedEvent) {
var requests = AjaxHelpers.requests(this), var teamsTabView = createTeamsTabView(this, {
teamsTabView = createTeamsTabView({
userInfo: TeamSpecHelpers.createMockUserInfo({staff: true}) userInfo: TeamSpecHelpers.createMockUserInfo({staff: true})
}); });
teamsTabView.router.navigate(url, {trigger: true}); teamsTabView.router.navigate(url, {trigger: true});
...@@ -167,9 +183,9 @@ define([ ...@@ -167,9 +183,9 @@ define([
describe('Discussion privileges', function () { describe('Discussion privileges', function () {
it('allows privileged access to any team', function () { it('allows privileged access to any team', function () {
var teamsTabView = createTeamsTabView({ var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({privileged: true}) userInfo: TeamSpecHelpers.createMockUserInfo({privileged: true})
}); });
// Note: using `undefined` here to ensure that we // Note: using `undefined` here to ensure that we
// don't even look at the team when the user is // don't even look at the team when the user is
// privileged // privileged
...@@ -177,12 +193,12 @@ define([ ...@@ -177,12 +193,12 @@ define([
}); });
it('allows access to a team which an unprivileged user is a member of', function () { it('allows access to a team which an unprivileged user is a member of', function () {
var teamsTabView = createTeamsTabView({ var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({ userInfo: TeamSpecHelpers.createMockUserInfo({
username: TeamSpecHelpers.testUser, username: TeamSpecHelpers.testUser,
privileged: false privileged: false
}) })
}); });
expect(teamsTabView.readOnlyDiscussion({ expect(teamsTabView.readOnlyDiscussion({
attributes: { attributes: {
membership: [{ membership: [{
...@@ -195,9 +211,9 @@ define([ ...@@ -195,9 +211,9 @@ define([
}); });
it('does not allow access if the user is neither privileged nor a team member', function () { it('does not allow access if the user is neither privileged nor a team member', function () {
var teamsTabView = createTeamsTabView({ var teamsTabView = createTeamsTabView(this, {
userInfo: TeamSpecHelpers.createMockUserInfo({privileged: false, staff: true}) userInfo: TeamSpecHelpers.createMockUserInfo({privileged: false, staff: true})
}); });
expect(teamsTabView.readOnlyDiscussion({ expect(teamsTabView.readOnlyDiscussion({
attributes: {membership: []} attributes: {membership: []}
})).toBe(true); })).toBe(true);
...@@ -205,25 +221,10 @@ define([ ...@@ -205,25 +221,10 @@ define([
}); });
describe('Search', function () { describe('Search', function () {
var verifyTeamsRequest = function(requests, options) {
AjaxHelpers.expectRequestURL(requests, TeamSpecHelpers.testContext.teamsUrl,
_.extend(
{
topic_id: TeamSpecHelpers.testTopicID,
expand: 'user',
course_id: TeamSpecHelpers.testCourseID,
order_by: '',
page: '1',
page_size: '10',
text_search: ''
},
options
));
};
var performSearch = function(requests, teamsTabView) { var performSearch = function(requests, teamsTabView) {
teamsTabView.$('.search-field').val('foo'); teamsTabView.$('.search-field').val('foo');
teamsTabView.$('.action-search').click(); teamsTabView.$('.action-search').click();
verifyTeamsRequest(requests, { verifyTeamsRequest({
order_by: '', order_by: '',
text_search: 'foo' text_search: 'foo'
}); });
...@@ -231,11 +232,10 @@ define([ ...@@ -231,11 +232,10 @@ define([
}; };
it('can search teams', function () { it('can search teams', function () {
var requests = AjaxHelpers.requests(this), var teamsTabView = createTeamsTabView(this),
teamsTabView = createTeamsTabView(),
requestCountBeforeSearch; requestCountBeforeSearch;
teamsTabView.browseTopic(TeamSpecHelpers.testTopicID); teamsTabView.browseTopic(TeamSpecHelpers.testTopicID);
verifyTeamsRequest(requests, { verifyTeamsRequest({
order_by: 'last_activity_at', order_by: 'last_activity_at',
text_search: '' text_search: ''
}); });
...@@ -250,11 +250,9 @@ define([ ...@@ -250,11 +250,9 @@ define([
}); });
it('can clear a search', function () { it('can clear a search', function () {
var requests = AjaxHelpers.requests(this), var teamsTabView = createTeamsTabView(this);
teamsTabView = createTeamsTabView();
teamsTabView.browseTopic(TeamSpecHelpers.testTopicID); teamsTabView.browseTopic(TeamSpecHelpers.testTopicID);
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
performSearch(requests, teamsTabView);
// Perform a search // Perform a search
performSearch(requests, teamsTabView); performSearch(requests, teamsTabView);
...@@ -262,7 +260,7 @@ define([ ...@@ -262,7 +260,7 @@ define([
// Clear the search and submit it again // Clear the search and submit it again
teamsTabView.$('.search-field').val(''); teamsTabView.$('.search-field').val('');
teamsTabView.$('.action-search').click(); teamsTabView.$('.action-search').click();
verifyTeamsRequest(requests, { verifyTeamsRequest({
order_by: 'last_activity_at', order_by: 'last_activity_at',
text_search: '' text_search: ''
}); });
...@@ -272,8 +270,7 @@ define([ ...@@ -272,8 +270,7 @@ define([
}); });
it('can navigate back to all teams from a search', function () { it('can navigate back to all teams from a search', function () {
var requests = AjaxHelpers.requests(this), var teamsTabView = createTeamsTabView(this);
teamsTabView = createTeamsTabView();
teamsTabView.browseTopic(TeamSpecHelpers.testTopicID); teamsTabView.browseTopic(TeamSpecHelpers.testTopicID);
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
...@@ -283,7 +280,7 @@ define([ ...@@ -283,7 +280,7 @@ define([
// Verify the breadcrumbs have a link back to the teams list, and click on it // Verify the breadcrumbs have a link back to the teams list, and click on it
expect(teamsTabView.$('.breadcrumbs a').length).toBe(2); expect(teamsTabView.$('.breadcrumbs a').length).toBe(2);
teamsTabView.$('.breadcrumbs a').last().click(); teamsTabView.$('.breadcrumbs a').last().click();
verifyTeamsRequest(requests, { verifyTeamsRequest({
order_by: 'last_activity_at', order_by: 'last_activity_at',
text_search: '' text_search: ''
}); });
...@@ -293,8 +290,7 @@ define([ ...@@ -293,8 +290,7 @@ define([
}); });
it('does not switch to showing results when the search returns an error', function () { it('does not switch to showing results when the search returns an error', function () {
var requests = AjaxHelpers.requests(this), var teamsTabView = createTeamsTabView(this);
teamsTabView = createTeamsTabView();
teamsTabView.browseTopic(TeamSpecHelpers.testTopicID); teamsTabView.browseTopic(TeamSpecHelpers.testTopicID);
AjaxHelpers.respondWithJson(requests, {}); AjaxHelpers.respondWithJson(requests, {});
......
...@@ -4,8 +4,10 @@ define([ ...@@ -4,8 +4,10 @@ define([
'teams/js/collections/team_membership', 'teams/js/collections/team_membership',
'teams/js/views/topic_teams', 'teams/js/views/topic_teams',
'teams/js/spec_helpers/team_spec_helpers', 'teams/js/spec_helpers/team_spec_helpers',
'common/js/spec_helpers/ajax_helpers' 'common/js/spec_helpers/ajax_helpers',
], function (Backbone, TeamCollection, TeamMembershipCollection, TopicTeamsView, TeamSpecHelpers, AjaxHelpers) { 'common/js/spec_helpers/page_helpers'
], function (Backbone, TeamCollection, TeamMembershipCollection, TopicTeamsView, TeamSpecHelpers,
AjaxHelpers, PageHelpers) {
'use strict'; 'use strict';
describe('Topic Teams View', function () { describe('Topic Teams View', function () {
var createTopicTeamsView = function(options) { var createTopicTeamsView = function(options) {
...@@ -39,6 +41,7 @@ define([ ...@@ -39,6 +41,7 @@ define([
beforeEach(function () { beforeEach(function () {
setFixtures('<div class="teams-container"></div>'); setFixtures('<div class="teams-container"></div>');
PageHelpers.preventBackboneChangingUrl();
}); });
it('can render itself', function () { it('can render itself', function () {
......
...@@ -3,6 +3,7 @@ define([ ...@@ -3,6 +3,7 @@ define([
'backbone', 'backbone',
'logger', 'logger',
'common/js/spec_helpers/ajax_helpers', 'common/js/spec_helpers/ajax_helpers',
'common/js/spec_helpers/page_helpers',
'common/js/spec_helpers/template_helpers', 'common/js/spec_helpers/template_helpers',
'js/search/base/models/search_result', 'js/search/base/models/search_result',
'js/search/base/collections/search_collection', 'js/search/base/collections/search_collection',
...@@ -20,6 +21,7 @@ define([ ...@@ -20,6 +21,7 @@ define([
Backbone, Backbone,
Logger, Logger,
AjaxHelpers, AjaxHelpers,
PageHelpers,
TemplateHelpers, TemplateHelpers,
SearchResult, SearchResult,
SearchCollection, SearchCollection,
...@@ -35,699 +37,578 @@ define([ ...@@ -35,699 +37,578 @@ define([
) { ) {
'use strict'; 'use strict';
describe('Search', function() {
describe('SearchResult', function () {
beforeEach(function () { beforeEach(function () {
this.result = new SearchResult(); PageHelpers.preventBackboneChangingUrl();
});
it('has properties', function () {
expect(this.result.get('location')).toBeDefined();
expect(this.result.get('content_type')).toBeDefined();
expect(this.result.get('excerpt')).toBeDefined();
expect(this.result.get('url')).toBeDefined();
}); });
}); describe('SearchResult', function () {
describe('SearchCollection', function () { beforeEach(function () {
this.result = new SearchResult();
beforeEach(function () { });
this.collection = new SearchCollection();
this.onSearch = jasmine.createSpy('onSearch');
this.collection.on('search', this.onSearch);
this.onNext = jasmine.createSpy('onNext'); it('has properties', function () {
this.collection.on('next', this.onNext); expect(this.result.get('location')).toBeDefined();
expect(this.result.get('content_type')).toBeDefined();
expect(this.result.get('excerpt')).toBeDefined();
expect(this.result.get('url')).toBeDefined();
});
this.onError = jasmine.createSpy('onError');
this.collection.on('error', this.onError);
}); });
it('sends a request without a course ID', function () {
var collection = new SearchCollection([]);
spyOn($, 'ajax');
collection.performSearch('search string');
expect($.ajax.mostRecentCall.args[0].url).toEqual('/search/');
});
it('sends a request with course ID', function () { describe('SearchCollection', function () {
var collection = new SearchCollection([], { courseId: 'edx101' });
spyOn($, 'ajax');
collection.performSearch('search string');
expect($.ajax.mostRecentCall.args[0].url).toEqual('/search/edx101');
});
it('sends a request and parses the json result', function () { beforeEach(function () {
var requests = AjaxHelpers.requests(this); this.collection = new SearchCollection();
this.collection.performSearch('search string');
var response = {
total: 2,
access_denied_count: 1,
results: [{
data: {
location: ['section', 'subsection', 'unit'],
url: '/some/url/to/content',
content_type: 'text',
excerpt: 'this is a short excerpt'
}
}]
};
AjaxHelpers.respondWithJson(requests, response);
expect(this.onSearch).toHaveBeenCalled();
expect(this.collection.totalCount).toEqual(1);
expect(this.collection.latestModelsCount).toEqual(1);
expect(this.collection.accessDeniedCount).toEqual(1);
expect(this.collection.page).toEqual(0);
expect(this.collection.first().attributes).toEqual(response.results[0].data);
});
it('handles errors', function () { this.onSearch = jasmine.createSpy('onSearch');
var requests = AjaxHelpers.requests(this); this.collection.on('search', this.onSearch);
this.collection.performSearch('search string');
AjaxHelpers.respondWithError(requests, 500);
expect(this.onSearch).not.toHaveBeenCalled();
expect(this.onError).toHaveBeenCalled();
});
it('loads next page', function () { this.onNext = jasmine.createSpy('onNext');
var requests = AjaxHelpers.requests(this); this.collection.on('next', this.onNext);
var response = { total: 35, results: [] };
this.collection.loadNextPage();
AjaxHelpers.respondWithJson(requests, response);
expect(this.onNext).toHaveBeenCalled();
expect(this.onError).not.toHaveBeenCalled();
});
it('sends correct paging parameters', function () { this.onError = jasmine.createSpy('onError');
var requests = AjaxHelpers.requests(this); this.collection.on('error', this.onError);
var searchString = 'search string'; });
var response = { total: 52, results: [] };
this.collection.performSearch(searchString);
AjaxHelpers.respondWithJson(requests, response);
this.collection.loadNextPage();
AjaxHelpers.respondWithJson(requests, response);
spyOn($, 'ajax');
this.collection.loadNextPage();
expect($.ajax.mostRecentCall.args[0].url).toEqual(this.collection.url);
expect($.ajax.mostRecentCall.args[0].data.search_string).toEqual(searchString);
expect($.ajax.mostRecentCall.args[0].data.page_size).toEqual(this.collection.pageSize);
expect($.ajax.mostRecentCall.args[0].data.page_index).toEqual(2);
});
it('has next page', function () { it('sends a request without a course ID', function () {
var requests = AjaxHelpers.requests(this); var collection = new SearchCollection([]);
var response = { total: 35, access_denied_count: 5, results: [] }; spyOn($, 'ajax');
this.collection.performSearch('search string'); collection.performSearch('search string');
AjaxHelpers.respondWithJson(requests, response); expect($.ajax.mostRecentCall.args[0].url).toEqual('/search/');
expect(this.collection.hasNextPage()).toEqual(true); });
this.collection.loadNextPage();
AjaxHelpers.respondWithJson(requests, response);
expect(this.collection.hasNextPage()).toEqual(false);
});
it('aborts any previous request', function () { it('sends a request with course ID', function () {
var requests = AjaxHelpers.requests(this); var collection = new SearchCollection([], { courseId: 'edx101' });
var response = { total: 35, results: [] }; spyOn($, 'ajax');
collection.performSearch('search string');
expect($.ajax.mostRecentCall.args[0].url).toEqual('/search/edx101');
});
this.collection.performSearch('old search'); it('sends a request and parses the json result', function () {
this.collection.performSearch('new search'); var requests = AjaxHelpers.requests(this);
AjaxHelpers.respondWithJson(requests, response); this.collection.performSearch('search string');
expect(this.onSearch.calls.length).toEqual(1); var response = {
total: 2,
access_denied_count: 1,
results: [{
data: {
location: ['section', 'subsection', 'unit'],
url: '/some/url/to/content',
content_type: 'text',
excerpt: 'this is a short excerpt'
}
}]
};
AjaxHelpers.respondWithJson(requests, response);
this.collection.performSearch('old search'); expect(this.onSearch).toHaveBeenCalled();
this.collection.cancelSearch(); expect(this.collection.totalCount).toEqual(1);
AjaxHelpers.respondWithJson(requests, response); expect(this.collection.latestModelsCount).toEqual(1);
expect(this.onSearch.calls.length).toEqual(1); expect(this.collection.accessDeniedCount).toEqual(1);
expect(this.collection.page).toEqual(0);
expect(this.collection.first().attributes).toEqual(response.results[0].data);
});
this.collection.loadNextPage(); it('handles errors', function () {
this.collection.loadNextPage(); var requests = AjaxHelpers.requests(this);
AjaxHelpers.respondWithJson(requests, response); this.collection.performSearch('search string');
expect(this.onNext.calls.length).toEqual(1); AjaxHelpers.respondWithError(requests, 500);
}); expect(this.onSearch).not.toHaveBeenCalled();
expect(this.onError).toHaveBeenCalled();
});
describe('reset state', function () { it('loads next page', function () {
var requests = AjaxHelpers.requests(this);
var response = { total: 35, results: [] };
this.collection.loadNextPage();
AjaxHelpers.respondWithJson(requests, response);
expect(this.onNext).toHaveBeenCalled();
expect(this.onError).not.toHaveBeenCalled();
});
beforeEach(function () { it('sends correct paging parameters', function () {
this.collection.page = 2; var requests = AjaxHelpers.requests(this);
this.collection.totalCount = 35; var searchString = 'search string';
this.collection.latestModelsCount = 5; var response = { total: 52, results: [] };
this.collection.performSearch(searchString);
AjaxHelpers.respondWithJson(requests, response);
this.collection.loadNextPage();
AjaxHelpers.respondWithJson(requests, response);
spyOn($, 'ajax');
this.collection.loadNextPage();
expect($.ajax.mostRecentCall.args[0].url).toEqual(this.collection.url);
expect($.ajax.mostRecentCall.args[0].data.search_string).toEqual(searchString);
expect($.ajax.mostRecentCall.args[0].data.page_size).toEqual(this.collection.pageSize);
expect($.ajax.mostRecentCall.args[0].data.page_index).toEqual(2);
}); });
it('resets state when performing new search', function () { it('has next page', function () {
var requests = AjaxHelpers.requests(this);
var response = { total: 35, access_denied_count: 5, results: [] };
this.collection.performSearch('search string'); this.collection.performSearch('search string');
expect(this.collection.models.length).toEqual(0); AjaxHelpers.respondWithJson(requests, response);
expect(this.collection.page).toEqual(0); expect(this.collection.hasNextPage()).toEqual(true);
expect(this.collection.totalCount).toEqual(0); this.collection.loadNextPage();
expect(this.collection.latestModelsCount).toEqual(0); AjaxHelpers.respondWithJson(requests, response);
expect(this.collection.hasNextPage()).toEqual(false);
}); });
it('resets state when canceling a search', function () { it('aborts any previous request', function () {
this.collection.cancelSearch(); var requests = AjaxHelpers.requests(this);
expect(this.collection.models.length).toEqual(0); var response = { total: 35, results: [] };
expect(this.collection.page).toEqual(0);
expect(this.collection.totalCount).toEqual(0);
expect(this.collection.latestModelsCount).toEqual(0);
});
}); this.collection.performSearch('old search');
this.collection.performSearch('new search');
AjaxHelpers.respondWithJson(requests, response);
expect(this.onSearch.calls.length).toEqual(1);
}); this.collection.performSearch('old search');
this.collection.cancelSearch();
AjaxHelpers.respondWithJson(requests, response);
expect(this.onSearch.calls.length).toEqual(1);
this.collection.loadNextPage();
this.collection.loadNextPage();
AjaxHelpers.respondWithJson(requests, response);
expect(this.onNext.calls.length).toEqual(1);
});
describe('SearchRouter', function () { describe('reset state', function () {
beforeEach(function () { beforeEach(function () {
this.router = new SearchRouter(); this.collection.page = 2;
this.onSearch = jasmine.createSpy('onSearch'); this.collection.totalCount = 35;
this.router.on('search', this.onSearch); this.collection.latestModelsCount = 5;
}); });
it ('has a search route', function () { it('resets state when performing new search', function () {
expect(this.router.routes['search/:query']).toEqual('search'); this.collection.performSearch('search string');
}); expect(this.collection.models.length).toEqual(0);
expect(this.collection.page).toEqual(0);
expect(this.collection.totalCount).toEqual(0);
expect(this.collection.latestModelsCount).toEqual(0);
});
it ('triggers a search event', function () { it('resets state when canceling a search', function () {
var query = 'mercury'; this.collection.cancelSearch();
this.router.search(query); expect(this.collection.models.length).toEqual(0);
expect(this.onSearch).toHaveBeenCalledWith(query); expect(this.collection.page).toEqual(0);
}); expect(this.collection.totalCount).toEqual(0);
expect(this.collection.latestModelsCount).toEqual(0);
});
}); });
});
describe('SearchItemView', function () {
function beforeEachHelper(SearchItemView) { describe('SearchRouter', function () {
TemplateHelpers.installTemplates([
'templates/search/course_search_item',
'templates/search/dashboard_search_item'
]);
this.model = new SearchResult({ beforeEach(function () {
location: ['section', 'subsection', 'unit'], this.router = new SearchRouter();
content_type: 'Video', this.onSearch = jasmine.createSpy('onSearch');
course_name: 'Course Name', this.router.on('search', this.onSearch);
excerpt: 'A short excerpt.',
url: 'path/to/content'
}); });
this.seqModel = new SearchResult({ it ('has a search route', function () {
location: ['section', 'subsection'], expect(this.router.routes['search/:query']).toEqual('search');
content_type: 'Sequence',
course_name: 'Course Name',
excerpt: 'A short excerpt.',
url: 'path/to/content'
}); });
this.item = new SearchItemView({ model: this.model }); it ('triggers a search event', function () {
this.item.render(); var query = 'mercury';
this.seqItem = new SearchItemView({ model: this.seqModel }); this.router.search(query);
this.seqItem.render(); expect(this.onSearch).toHaveBeenCalledWith(query);
}
function rendersItem() {
expect(this.item.$el).toHaveAttr('role', 'region');
expect(this.item.$el).toHaveAttr('aria-label', 'search result');
expect(this.item.$el).toContain('a[href="' + this.model.get('url') + '"]');
expect(this.item.$el.find('.result-type')).toContainHtml(this.model.get('content_type'));
expect(this.item.$el.find('.result-excerpt')).toContainHtml(this.model.get('excerpt'));
expect(this.item.$el.find('.result-location')).toContainHtml('section ▸ subsection ▸ unit');
}
function rendersSequentialItem() {
expect(this.seqItem.$el).toHaveAttr('role', 'region');
expect(this.seqItem.$el).toHaveAttr('aria-label', 'search result');
expect(this.seqItem.$el).toContain('a[href="' + this.seqModel.get('url') + '"]');
expect(this.seqItem.$el.find('.result-type')).toBeEmpty();
expect(this.seqItem.$el.find('.result-excerpt')).toBeEmpty();
expect(this.seqItem.$el.find('.result-location')).toContainHtml('section ▸ subsection');
}
function logsSearchItemViewEvent() {
this.model.collection = new SearchCollection([this.model], { course_id: 'edx101' });
this.item.render();
// Mock the redirect call
spyOn(this.item, 'redirect').andCallFake( function() {} );
spyOn(Logger, 'log').andReturn($.Deferred().resolve());
this.item.$el.find('a').trigger('click');
expect(this.item.redirect).toHaveBeenCalled();
this.item.$el.trigger('click');
expect(this.item.redirect).toHaveBeenCalled();
}
describe('CourseSearchItemView', function () {
beforeEach(function () {
beforeEachHelper.call(this, CourseSearchItemView);
}); });
it('renders items correctly', rendersItem);
it('renders Sequence items correctly', rendersSequentialItem);
it('logs view event', logsSearchItemViewEvent);
});
describe('DashSearchItemView', function () {
beforeEach(function () {
beforeEachHelper.call(this, DashSearchItemView);
});
it('renders items correctly', rendersItem);
it('renders Sequence items correctly', rendersSequentialItem);
it('displays course name in breadcrumbs', function () {
expect(this.seqItem.$el.find('.result-course-name')).toContainHtml(this.model.get('course_name'));
});
it('logs view event', logsSearchItemViewEvent);
}); });
});
describe('SearchItemView', function () {
describe('SearchForm', function () { function beforeEachHelper(SearchItemView) {
TemplateHelpers.installTemplates([
function trimsInputString() { 'templates/search/course_search_item',
var term = ' search string '; 'templates/search/dashboard_search_item'
$('.search-field').val(term); ]);
$('form').trigger('submit');
expect(this.onSearch).toHaveBeenCalledWith($.trim(term)); this.model = new SearchResult({
} location: ['section', 'subsection', 'unit'],
content_type: 'Video',
function doesSearch() { course_name: 'Course Name',
var term = ' search string '; excerpt: 'A short excerpt.',
$('.search-field').val(term); url: 'path/to/content'
this.form.doSearch(term); });
expect(this.onSearch).toHaveBeenCalledWith($.trim(term));
expect($('.search-field').val()).toEqual(term); this.seqModel = new SearchResult({
expect($('.search-field')).toHaveClass('is-active'); location: ['section', 'subsection'],
expect($('.search-button')).toBeHidden(); content_type: 'Sequence',
expect($('.cancel-button')).toBeVisible(); course_name: 'Course Name',
} excerpt: 'A short excerpt.',
url: 'path/to/content'
function triggersSearchEvent() { });
var term = 'search string';
$('.search-field').val(term); this.item = new SearchItemView({ model: this.model });
$('form').trigger('submit'); this.item.render();
expect(this.onSearch).toHaveBeenCalledWith(term); this.seqItem = new SearchItemView({ model: this.seqModel });
expect($('.search-field')).toHaveClass('is-active'); this.seqItem.render();
expect($('.search-button')).toBeHidden(); }
expect($('.cancel-button')).toBeVisible();
} function rendersItem() {
expect(this.item.$el).toHaveAttr('role', 'region');
function clearsSearchOnCancel() { expect(this.item.$el).toHaveAttr('aria-label', 'search result');
$('.search-field').val('search string'); expect(this.item.$el).toContain('a[href="' + this.model.get('url') + '"]');
$('.search-button').trigger('click'); expect(this.item.$el.find('.result-type')).toContainHtml(this.model.get('content_type'));
$('.cancel-button').trigger('click'); expect(this.item.$el.find('.result-excerpt')).toContainHtml(this.model.get('excerpt'));
expect($('.search-field')).not.toHaveClass('is-active'); expect(this.item.$el.find('.result-location')).toContainHtml('section ▸ subsection ▸ unit');
expect($('.search-button')).toBeVisible(); }
expect($('.cancel-button')).toBeHidden();
expect($('.search-field')).toHaveValue(''); function rendersSequentialItem() {
} expect(this.seqItem.$el).toHaveAttr('role', 'region');
expect(this.seqItem.$el).toHaveAttr('aria-label', 'search result');
function clearsSearchOnEmpty() { expect(this.seqItem.$el).toContain('a[href="' + this.seqModel.get('url') + '"]');
$('.search-field').val(''); expect(this.seqItem.$el.find('.result-type')).toBeEmpty();
$('form').trigger('submit'); expect(this.seqItem.$el.find('.result-excerpt')).toBeEmpty();
expect(this.onClear).toHaveBeenCalled(); expect(this.seqItem.$el.find('.result-location')).toContainHtml('section ▸ subsection');
expect($('.search-field')).not.toHaveClass('is-active'); }
expect($('.cancel-button')).toBeHidden();
expect($('.search-button')).toBeVisible(); function logsSearchItemViewEvent() {
} this.model.collection = new SearchCollection([this.model], { course_id: 'edx101' });
this.item.render();
describe('CourseSearchForm', function () { // Mock the redirect call
beforeEach(function () { spyOn(this.item, 'redirect').andCallFake( function() {} );
loadFixtures('js/fixtures/search/course_search_form.html'); spyOn(Logger, 'log').andReturn($.Deferred().resolve());
this.form = new CourseSearchForm(); this.item.$el.find('a').trigger('click');
this.onClear = jasmine.createSpy('onClear'); expect(this.item.redirect).toHaveBeenCalled();
this.onSearch = jasmine.createSpy('onSearch'); this.item.$el.trigger('click');
this.form.on('clear', this.onClear); expect(this.item.redirect).toHaveBeenCalled();
this.form.on('search', this.onSearch); }
describe('CourseSearchItemView', function () {
beforeEach(function () {
beforeEachHelper.call(this, CourseSearchItemView);
});
it('renders items correctly', rendersItem);
it('renders Sequence items correctly', rendersSequentialItem);
it('logs view event', logsSearchItemViewEvent);
}); });
it('trims input string', trimsInputString);
it('handles calls to doSearch', doesSearch);
it('triggers a search event and changes to active state', triggersSearchEvent);
it('clears search when clicking on cancel button', clearsSearchOnCancel);
it('clears search when search box is empty', clearsSearchOnEmpty);
});
describe('DashSearchForm', function () { describe('DashSearchItemView', function () {
beforeEach(function () { beforeEach(function () {
loadFixtures('js/fixtures/search/dashboard_search_form.html'); beforeEachHelper.call(this, DashSearchItemView);
this.form = new DashSearchForm(); });
this.onClear = jasmine.createSpy('onClear'); it('renders items correctly', rendersItem);
this.onSearch = jasmine.createSpy('onSearch'); it('renders Sequence items correctly', rendersSequentialItem);
this.form.on('clear', this.onClear); it('displays course name in breadcrumbs', function () {
this.form.on('search', this.onSearch); expect(this.seqItem.$el.find('.result-course-name')).toContainHtml(this.model.get('course_name'));
});
it('logs view event', logsSearchItemViewEvent);
}); });
it('trims input string', trimsInputString);
it('handles calls to doSearch', doesSearch);
it('triggers a search event and changes to active state', triggersSearchEvent);
it('clears search when clicking on cancel button', clearsSearchOnCancel);
it('clears search when search box is empty', clearsSearchOnEmpty);
});
}); });
describe('SearchResultsView', function () { describe('SearchForm', function () {
function showsLoadingMessage () { function trimsInputString() {
this.resultsView.showLoadingMessage(); var term = ' search string ';
expect(this.resultsView.$contentElement).toBeHidden(); $('.search-field').val(term);
expect(this.resultsView.$el).toBeVisible(); $('form').trigger('submit');
expect(this.resultsView.$el).not.toBeEmpty(); expect(this.onSearch).toHaveBeenCalledWith($.trim(term));
} }
function showsErrorMessage () { function doesSearch() {
this.resultsView.showErrorMessage(); var term = ' search string ';
expect(this.resultsView.$contentElement).toBeHidden(); $('.search-field').val(term);
expect(this.resultsView.$el).toBeVisible(); this.form.doSearch(term);
expect(this.resultsView.$el).not.toBeEmpty(); expect(this.onSearch).toHaveBeenCalledWith($.trim(term));
} expect($('.search-field').val()).toEqual(term);
expect($('.search-field')).toHaveClass('is-active');
function returnsToContent () { expect($('.search-button')).toBeHidden();
this.resultsView.clear(); expect($('.cancel-button')).toBeVisible();
expect(this.resultsView.$contentElement).toBeVisible(); }
expect(this.resultsView.$el).toBeHidden();
expect(this.resultsView.$el).toBeEmpty(); function triggersSearchEvent() {
} var term = 'search string';
$('.search-field').val(term);
function showsNoResultsMessage() { $('form').trigger('submit');
this.collection.reset(); expect(this.onSearch).toHaveBeenCalledWith(term);
this.resultsView.render(); expect($('.search-field')).toHaveClass('is-active');
expect(this.resultsView.$el).toContainHtml('no results'); expect($('.search-button')).toBeHidden();
expect(this.resultsView.$el.find('ol')).not.toExist(); expect($('.cancel-button')).toBeVisible();
} }
function rendersSearchResults () { function clearsSearchOnCancel() {
var searchResults = [{ $('.search-field').val('search string');
location: ['section', 'subsection', 'unit'], $('.search-button').trigger('click');
url: '/some/url/to/content', $('.cancel-button').trigger('click');
content_type: 'text', expect($('.search-field')).not.toHaveClass('is-active');
course_name: '', expect($('.search-button')).toBeVisible();
excerpt: 'this is a short excerpt' expect($('.cancel-button')).toBeHidden();
}]; expect($('.search-field')).toHaveValue('');
this.collection.set(searchResults); }
this.collection.latestModelsCount = 1;
this.collection.totalCount = 1; function clearsSearchOnEmpty() {
$('.search-field').val('');
this.resultsView.render(); $('form').trigger('submit');
expect(this.resultsView.$el.find('ol')[0]).toExist(); expect(this.onClear).toHaveBeenCalled();
expect(this.resultsView.$el.find('li').length).toEqual(1); expect($('.search-field')).not.toHaveClass('is-active');
expect(this.resultsView.$el).toContainHtml('Search Results'); expect($('.cancel-button')).toBeHidden();
expect(this.resultsView.$el).toContainHtml('this is a short excerpt'); expect($('.search-button')).toBeVisible();
}
this.collection.set(searchResults);
this.collection.totalCount = 2; describe('CourseSearchForm', function () {
this.resultsView.renderNext(); beforeEach(function () {
expect(this.resultsView.$el.find('.search-count')).toContainHtml('2'); loadFixtures('js/fixtures/search/course_search_form.html');
expect(this.resultsView.$el.find('li').length).toEqual(2); this.form = new CourseSearchForm();
} this.onClear = jasmine.createSpy('onClear');
this.onSearch = jasmine.createSpy('onSearch');
function showsMoreResultsLink () { this.form.on('clear', this.onClear);
this.collection.totalCount = 123; this.form.on('search', this.onSearch);
this.collection.hasNextPage = function () { return true; }; });
this.resultsView.render(); it('trims input string', trimsInputString);
expect(this.resultsView.$el.find('a.search-load-next')[0]).toExist(); it('handles calls to doSearch', doesSearch);
it('triggers a search event and changes to active state', triggersSearchEvent);
this.collection.totalCount = 123; it('clears search when clicking on cancel button', clearsSearchOnCancel);
this.collection.hasNextPage = function () { return false; }; it('clears search when search box is empty', clearsSearchOnEmpty);
this.resultsView.render();
expect(this.resultsView.$el.find('a.search-load-next')[0]).not.toExist();
}
function triggersNextPageEvent () {
var onNext = jasmine.createSpy('onNext');
this.resultsView.on('next', onNext);
this.collection.totalCount = 123;
this.collection.hasNextPage = function () { return true; };
this.resultsView.render();
this.resultsView.$el.find('a.search-load-next').click();
expect(onNext).toHaveBeenCalled();
}
function showsLoadMoreSpinner () {
this.collection.totalCount = 123;
this.collection.hasNextPage = function () { return true; };
this.resultsView.render();
expect(this.resultsView.$el.find('a.search-load-next .icon')).toBeHidden();
this.resultsView.loadNext();
// toBeVisible does not work with inline
expect(this.resultsView.$el.find('a.search-load-next .icon')).toHaveCss({ 'display': 'inline' });
this.resultsView.renderNext();
expect(this.resultsView.$el.find('a.search-load-next .icon')).toBeHidden();
}
function beforeEachHelper(SearchResultsView) {
appendSetFixtures(
'<section id="courseware-search-results"></section>' +
'<section id="course-content"></section>' +
'<section id="dashboard-search-results"></section>' +
'<section id="my-courses"></section>'
);
TemplateHelpers.installTemplates([
'templates/search/course_search_item',
'templates/search/dashboard_search_item',
'templates/search/course_search_results',
'templates/search/dashboard_search_results',
'templates/search/search_list',
'templates/search/search_loading',
'templates/search/search_error'
]);
var MockCollection = Backbone.Collection.extend({
hasNextPage: function () {},
latestModelsCount: 0,
pageSize: 20,
latestModels: function () {
return SearchCollection.prototype.latestModels.apply(this, arguments);
}
}); });
this.collection = new MockCollection();
this.resultsView = new SearchResultsView({ collection: this.collection });
}
describe('CourseSearchResultsView', function () { describe('DashSearchForm', function () {
beforeEach(function() { beforeEach(function () {
beforeEachHelper.call(this, CourseSearchResultsView); loadFixtures('js/fixtures/search/dashboard_search_form.html');
this.form = new DashSearchForm();
this.onClear = jasmine.createSpy('onClear');
this.onSearch = jasmine.createSpy('onSearch');
this.form.on('clear', this.onClear);
this.form.on('search', this.onSearch);
});
it('trims input string', trimsInputString);
it('handles calls to doSearch', doesSearch);
it('triggers a search event and changes to active state', triggersSearchEvent);
it('clears search when clicking on cancel button', clearsSearchOnCancel);
it('clears search when search box is empty', clearsSearchOnEmpty);
}); });
it('shows loading message', showsLoadingMessage);
it('shows error message', showsErrorMessage);
it('returns to content', returnsToContent);
it('shows a message when there are no results', showsNoResultsMessage);
it('renders search results', rendersSearchResults);
it('shows a link to load more results', showsMoreResultsLink);
it('triggers an event for next page', triggersNextPageEvent);
it('shows a spinner when loading more results', showsLoadMoreSpinner);
}); });
describe('DashSearchResultsView', function () {
beforeEach(function() { describe('SearchResultsView', function () {
beforeEachHelper.call(this, DashSearchResultsView);
}); function showsLoadingMessage () {
it('shows loading message', showsLoadingMessage); this.resultsView.showLoadingMessage();
it('shows error message', showsErrorMessage); expect(this.resultsView.$contentElement).toBeHidden();
it('returns to content', returnsToContent); expect(this.resultsView.$el).toBeVisible();
it('shows a message when there are no results', showsNoResultsMessage); expect(this.resultsView.$el).not.toBeEmpty();
it('renders search results', rendersSearchResults); }
it('shows a link to load more results', showsMoreResultsLink);
it('triggers an event for next page', triggersNextPageEvent); function showsErrorMessage () {
it('shows a spinner when loading more results', showsLoadMoreSpinner); this.resultsView.showErrorMessage();
it('returns back to courses', function () { expect(this.resultsView.$contentElement).toBeHidden();
var onReset = jasmine.createSpy('onReset'); expect(this.resultsView.$el).toBeVisible();
this.resultsView.on('reset', onReset); expect(this.resultsView.$el).not.toBeEmpty();
this.resultsView.render(); }
expect(this.resultsView.$el.find('a.search-back-to-courses')).toExist();
this.resultsView.$el.find('.search-back-to-courses').click(); function returnsToContent () {
expect(onReset).toHaveBeenCalled(); this.resultsView.clear();
expect(this.resultsView.$contentElement).toBeVisible(); expect(this.resultsView.$contentElement).toBeVisible();
expect(this.resultsView.$el).toBeHidden(); expect(this.resultsView.$el).toBeHidden();
}); expect(this.resultsView.$el).toBeEmpty();
}); }
}); function showsNoResultsMessage() {
this.collection.reset();
this.resultsView.render();
expect(this.resultsView.$el).toContainHtml('no results');
expect(this.resultsView.$el.find('ol')).not.toExist();
}
function rendersSearchResults () {
var searchResults = [{
location: ['section', 'subsection', 'unit'],
url: '/some/url/to/content',
content_type: 'text',
course_name: '',
excerpt: 'this is a short excerpt'
}];
this.collection.set(searchResults);
this.collection.latestModelsCount = 1;
this.collection.totalCount = 1;
this.resultsView.render();
expect(this.resultsView.$el.find('ol')[0]).toExist();
expect(this.resultsView.$el.find('li').length).toEqual(1);
expect(this.resultsView.$el).toContainHtml('Search Results');
expect(this.resultsView.$el).toContainHtml('this is a short excerpt');
this.collection.set(searchResults);
this.collection.totalCount = 2;
this.resultsView.renderNext();
expect(this.resultsView.$el.find('.search-count')).toContainHtml('2');
expect(this.resultsView.$el.find('li').length).toEqual(2);
}
function showsMoreResultsLink () {
this.collection.totalCount = 123;
this.collection.hasNextPage = function () { return true; };
this.resultsView.render();
expect(this.resultsView.$el.find('a.search-load-next')[0]).toExist();
describe('SearchApp', function () { this.collection.totalCount = 123;
this.collection.hasNextPage = function () { return false; };
function showsLoadingMessage () { this.resultsView.render();
$('.search-field').val('search string'); expect(this.resultsView.$el.find('a.search-load-next')[0]).not.toExist();
$('.search-button').trigger('click'); }
expect(this.$contentElement).toBeHidden();
expect(this.$searchResults).toBeVisible(); function triggersNextPageEvent () {
expect(this.$searchResults).not.toBeEmpty(); var onNext = jasmine.createSpy('onNext');
} this.resultsView.on('next', onNext);
this.collection.totalCount = 123;
function performsSearch () { this.collection.hasNextPage = function () { return true; };
var requests = AjaxHelpers.requests(this); this.resultsView.render();
$('.search-field').val('search string'); this.resultsView.$el.find('a.search-load-next').click();
$('.search-button').trigger('click'); expect(onNext).toHaveBeenCalled();
AjaxHelpers.respondWithJson(requests, { }
total: 1337,
access_denied_count: 12,
results: [{
data: {
location: ['section', 'subsection', 'unit'],
url: '/some/url/to/content',
content_type: 'text',
excerpt: 'this is a short excerpt',
course_name: ''
}
}]
});
expect($('.search-info')).toExist();
expect($('.search-result-list')).toBeVisible();
expect(this.$searchResults.find('li').length).toEqual(1);
}
function showsErrorMessage () {
var requests = AjaxHelpers.requests(this);
$('.search-field').val('search string');
$('.search-button').trigger('click');
AjaxHelpers.respondWithError(requests, 500, {});
expect(this.$searchResults).toContainHtml('There was an error');
}
function updatesNavigationHistory () {
$('.search-field').val('edx');
$('.search-button').trigger('click');
expect(Backbone.history.navigate.calls[0].args).toContain('search/edx');
$('.cancel-button').trigger('click');
expect(Backbone.history.navigate.calls[1].args).toContain('');
}
function cancelsSearchRequest () {
var requests = AjaxHelpers.requests(this);
// send search request to server
$('.search-field').val('search string');
$('.search-button').trigger('click');
// cancel search
$('.cancel-button').trigger('click');
AjaxHelpers.respondWithJson(requests, {
total: 1337,
access_denied_count: 12,
results: [{
data: {
location: ['section', 'subsection', 'unit'],
url: '/some/url/to/content',
content_type: 'text',
excerpt: 'this is a short excerpt',
course_name: ''
}
}]
});
// there should be no results
expect(this.$contentElement).toBeVisible();
expect(this.$searchResults).toBeHidden();
}
function clearsResults () {
$('.cancel-button').trigger('click');
expect(this.$contentElement).toBeVisible();
expect(this.$searchResults).toBeHidden();
}
function loadsNextPage () {
var requests = AjaxHelpers.requests(this);
var response = {
total: 1337,
access_denied_count: 12,
results: [{
data: {
location: ['section', 'subsection', 'unit'],
url: '/some/url/to/content',
content_type: 'text',
excerpt: 'this is a short excerpt',
course_name: ''
}
}]
};
$('.search-field').val('query');
$('.search-button').trigger('click');
AjaxHelpers.respondWithJson(requests, response);
expect(this.$searchResults.find('li').length).toEqual(1);
expect($('.search-load-next')).toBeVisible();
$('.search-load-next').trigger('click');
var body = requests[1].requestBody;
expect(body).toContain('search_string=query');
expect(body).toContain('page_index=1');
AjaxHelpers.respondWithJson(requests, response);
expect(this.$searchResults.find('li').length).toEqual(2);
}
function navigatesToSearch () {
var requests = AjaxHelpers.requests(this);
Backbone.history.loadUrl('search/query');
expect(requests[0].requestBody).toContain('search_string=query');
}
function loadTemplates () {
TemplateHelpers.installTemplates([
'templates/search/course_search_item',
'templates/search/dashboard_search_item',
'templates/search/search_loading',
'templates/search/search_error',
'templates/search/course_search_results',
'templates/search/dashboard_search_results'
]);
}
describe('CourseSearchApp', function () {
beforeEach(function () { function showsLoadMoreSpinner () {
loadFixtures('js/fixtures/search/course_search_form.html'); this.collection.totalCount = 123;
this.collection.hasNextPage = function () { return true; };
this.resultsView.render();
expect(this.resultsView.$el.find('a.search-load-next .icon')).toBeHidden();
this.resultsView.loadNext();
// toBeVisible does not work with inline
expect(this.resultsView.$el.find('a.search-load-next .icon')).toHaveCss({ 'display': 'inline' });
this.resultsView.renderNext();
expect(this.resultsView.$el.find('a.search-load-next .icon')).toBeHidden();
}
function beforeEachHelper(SearchResultsView) {
appendSetFixtures( appendSetFixtures(
'<section id="courseware-search-results"></section>' + '<section id="courseware-search-results"></section>' +
'<section id="course-content"></section>' '<section id="course-content"></section>' +
'<section id="dashboard-search-results"></section>' +
'<section id="my-courses"></section>'
); );
loadTemplates.call(this);
var courseId = 'a/b/c'; TemplateHelpers.installTemplates([
CourseSearchFactory(courseId); 'templates/search/course_search_item',
spyOn(Backbone.history, 'navigate'); 'templates/search/dashboard_search_item',
this.$contentElement = $('#course-content'); 'templates/search/course_search_results',
this.$searchResults = $('#courseware-search-results'); 'templates/search/dashboard_search_results',
'templates/search/search_list',
'templates/search/search_loading',
'templates/search/search_error'
]);
var MockCollection = Backbone.Collection.extend({
hasNextPage: function () {},
latestModelsCount: 0,
pageSize: 20,
latestModels: function () {
return SearchCollection.prototype.latestModels.apply(this, arguments);
}
});
this.collection = new MockCollection();
this.resultsView = new SearchResultsView({ collection: this.collection });
}
describe('CourseSearchResultsView', function () {
beforeEach(function() {
beforeEachHelper.call(this, CourseSearchResultsView);
});
it('shows loading message', showsLoadingMessage);
it('shows error message', showsErrorMessage);
it('returns to content', returnsToContent);
it('shows a message when there are no results', showsNoResultsMessage);
it('renders search results', rendersSearchResults);
it('shows a link to load more results', showsMoreResultsLink);
it('triggers an event for next page', triggersNextPageEvent);
it('shows a spinner when loading more results', showsLoadMoreSpinner);
}); });
it('shows loading message on search', showsLoadingMessage); describe('DashSearchResultsView', function () {
it('performs search', performsSearch); beforeEach(function() {
it('shows an error message', showsErrorMessage); beforeEachHelper.call(this, DashSearchResultsView);
it('updates navigation history', updatesNavigationHistory); });
it('cancels search request', cancelsSearchRequest); it('shows loading message', showsLoadingMessage);
it('clears results', clearsResults); it('shows error message', showsErrorMessage);
it('loads next page', loadsNextPage); it('returns to content', returnsToContent);
it('navigates to search', navigatesToSearch); it('shows a message when there are no results', showsNoResultsMessage);
it('renders search results', rendersSearchResults);
it('shows a link to load more results', showsMoreResultsLink);
it('triggers an event for next page', triggersNextPageEvent);
it('shows a spinner when loading more results', showsLoadMoreSpinner);
it('returns back to courses', function () {
var onReset = jasmine.createSpy('onReset');
this.resultsView.on('reset', onReset);
this.resultsView.render();
expect(this.resultsView.$el.find('a.search-back-to-courses')).toExist();
this.resultsView.$el.find('.search-back-to-courses').click();
expect(onReset).toHaveBeenCalled();
expect(this.resultsView.$contentElement).toBeVisible();
expect(this.resultsView.$el).toBeHidden();
});
});
}); });
describe('DashSearchApp', function () {
beforeEach(function () { describe('SearchApp', function () {
loadFixtures('js/fixtures/search/dashboard_search_form.html');
appendSetFixtures(
'<section id="dashboard-search-results"></section>' +
'<section id="my-courses"></section>'
);
loadTemplates.call(this);
DashboardSearchFactory();
spyOn(Backbone.history, 'navigate'); function showsLoadingMessage () {
this.$contentElement = $('#my-courses'); $('.search-field').val('search string');
this.$searchResults = $('#dashboard-search-results'); $('.search-button').trigger('click');
}); expect(this.$contentElement).toBeHidden();
expect(this.$searchResults).toBeVisible();
expect(this.$searchResults).not.toBeEmpty();
}
function performsSearch () {
var requests = AjaxHelpers.requests(this);
$('.search-field').val('search string');
$('.search-button').trigger('click');
AjaxHelpers.respondWithJson(requests, {
total: 1337,
access_denied_count: 12,
results: [{
data: {
location: ['section', 'subsection', 'unit'],
url: '/some/url/to/content',
content_type: 'text',
excerpt: 'this is a short excerpt',
course_name: ''
}
}]
});
expect($('.search-info')).toExist();
expect($('.search-result-list')).toBeVisible();
expect(this.$searchResults.find('li').length).toEqual(1);
}
it('shows loading message on search', showsLoadingMessage); function showsErrorMessage () {
it('performs search', performsSearch);
it('shows an error message', showsErrorMessage);
it('updates navigation history', updatesNavigationHistory);
it('cancels search request', cancelsSearchRequest);
it('clears results', clearsResults);
it('loads next page', loadsNextPage);
it('navigates to search', navigatesToSearch);
it('returns to course list', function () {
var requests = AjaxHelpers.requests(this); var requests = AjaxHelpers.requests(this);
$('.search-field').val('search string'); $('.search-field').val('search string');
$('.search-button').trigger('click'); $('.search-button').trigger('click');
AjaxHelpers.respondWithError(requests, 500, {});
expect(this.$searchResults).toContainHtml('There was an error');
}
function updatesNavigationHistory () {
$('.search-field').val('edx');
$('.search-button').trigger('click');
expect(Backbone.history.navigate.calls[0].args).toContain('search/edx');
$('.cancel-button').trigger('click');
expect(Backbone.history.navigate.calls[1].args).toContain('');
}
function cancelsSearchRequest () {
var requests = AjaxHelpers.requests(this);
// send search request to server
$('.search-field').val('search string');
$('.search-button').trigger('click');
// cancel search
$('.cancel-button').trigger('click');
AjaxHelpers.respondWithJson(requests, { AjaxHelpers.respondWithJson(requests, {
total: 1337, total: 1337,
access_denied_count: 12, access_denied_count: 12,
...@@ -741,15 +622,141 @@ define([ ...@@ -741,15 +622,141 @@ define([
} }
}] }]
}); });
expect($('.search-back-to-courses')).toExist(); // there should be no results
$('.search-back-to-courses').trigger('click');
expect(this.$contentElement).toBeVisible(); expect(this.$contentElement).toBeVisible();
expect(this.$searchResults).toBeHidden(); expect(this.$searchResults).toBeHidden();
expect(this.$searchResults).toBeEmpty(); }
function clearsResults () {
$('.cancel-button').trigger('click');
expect(this.$contentElement).toBeVisible();
expect(this.$searchResults).toBeHidden();
}
function loadsNextPage () {
var requests = AjaxHelpers.requests(this);
var response = {
total: 1337,
access_denied_count: 12,
results: [{
data: {
location: ['section', 'subsection', 'unit'],
url: '/some/url/to/content',
content_type: 'text',
excerpt: 'this is a short excerpt',
course_name: ''
}
}]
};
$('.search-field').val('query');
$('.search-button').trigger('click');
AjaxHelpers.respondWithJson(requests, response);
expect(this.$searchResults.find('li').length).toEqual(1);
expect($('.search-load-next')).toBeVisible();
$('.search-load-next').trigger('click');
var body = requests[1].requestBody;
expect(body).toContain('search_string=query');
expect(body).toContain('page_index=1');
AjaxHelpers.respondWithJson(requests, response);
expect(this.$searchResults.find('li').length).toEqual(2);
}
function navigatesToSearch () {
var requests = AjaxHelpers.requests(this);
Backbone.history.loadUrl('search/query');
expect(requests[0].requestBody).toContain('search_string=query');
}
function loadTemplates () {
TemplateHelpers.installTemplates([
'templates/search/course_search_item',
'templates/search/dashboard_search_item',
'templates/search/search_loading',
'templates/search/search_error',
'templates/search/course_search_results',
'templates/search/dashboard_search_results'
]);
}
describe('CourseSearchApp', function () {
beforeEach(function () {
loadFixtures('js/fixtures/search/course_search_form.html');
appendSetFixtures(
'<section id="courseware-search-results"></section>' +
'<section id="course-content"></section>'
);
loadTemplates.call(this);
var courseId = 'a/b/c';
CourseSearchFactory(courseId);
spyOn(Backbone.history, 'navigate');
this.$contentElement = $('#course-content');
this.$searchResults = $('#courseware-search-results');
});
it('shows loading message on search', showsLoadingMessage);
it('performs search', performsSearch);
it('shows an error message', showsErrorMessage);
it('updates navigation history', updatesNavigationHistory);
it('cancels search request', cancelsSearchRequest);
it('clears results', clearsResults);
it('loads next page', loadsNextPage);
it('navigates to search', navigatesToSearch);
});
describe('DashSearchApp', function () {
beforeEach(function () {
loadFixtures('js/fixtures/search/dashboard_search_form.html');
appendSetFixtures(
'<section id="dashboard-search-results"></section>' +
'<section id="my-courses"></section>'
);
loadTemplates.call(this);
DashboardSearchFactory();
spyOn(Backbone.history, 'navigate');
this.$contentElement = $('#my-courses');
this.$searchResults = $('#dashboard-search-results');
});
it('shows loading message on search', showsLoadingMessage);
it('performs search', performsSearch);
it('shows an error message', showsErrorMessage);
it('updates navigation history', updatesNavigationHistory);
it('cancels search request', cancelsSearchRequest);
it('clears results', clearsResults);
it('loads next page', loadsNextPage);
it('navigates to search', navigatesToSearch);
it('returns to course list', function () {
var requests = AjaxHelpers.requests(this);
$('.search-field').val('search string');
$('.search-button').trigger('click');
AjaxHelpers.respondWithJson(requests, {
total: 1337,
access_denied_count: 12,
results: [{
data: {
location: ['section', 'subsection', 'unit'],
url: '/some/url/to/content',
content_type: 'text',
excerpt: 'this is a short excerpt',
course_name: ''
}
}]
});
expect($('.search-back-to-courses')).toExist();
$('.search-back-to-courses').trigger('click');
expect(this.$contentElement).toBeVisible();
expect(this.$searchResults).toBeHidden();
expect(this.$searchResults).toBeEmpty();
});
}); });
}); });
}); });
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment