Commit fc70b1bd by chrisndodge

Merge pull request #52 from edx/muhhshoaib/jasmine-insfrastucture

(WIP) Jasmine Setup and tests
parents 6c6236b3 3d4f6cdb
......@@ -53,3 +53,4 @@ coverage/
htmlcov/
acceptance_tests/*.png
node_modules/
\ No newline at end of file
......@@ -12,6 +12,7 @@ before_install:
sudo: false
install:
- npm install
- "pip install -r requirements.txt"
- "pip install -r test_requirements.txt"
- "pip install coveralls"
......@@ -19,6 +20,7 @@ install:
script:
- coverage run ./manage.py test edx_proctoring
- coverage report -m
- gulp test
- pep8 edx_proctoring
- pylint edx_proctoring --report=no
......
window.gettext = function(s){return s;};
window.ngettext = function(singular, plural, num){ return num == 1 ? singular : plural }
function interpolate(fmt, obj, named) {
if (named) {
return fmt.replace(/%\(\w+\)s/g, function(match){return String(obj[match.slice(2,-2)])});
} else {
return fmt.replace(/%s/g, function(match){return String(obj.shift())});
}
}
......@@ -114,7 +114,6 @@ var edx = edx || {};
},
render: function () {
if (this.template !== null) {
var self = this;
var data = {
proctored_exam_attempts: this.collection.toJSON()[0].proctored_exam_attempts,
pagination_info: this.collection.toJSON()[0].pagination_info,
......
......@@ -95,6 +95,9 @@ var edx = edx || {};
}
return this;
},
reloadPage: function () {
location.reload();
},
unloadMessage: function () {
return gettext("Are you sure you want to leave this page? \n" +
"To pass your proctored exam you must submit your \n" +
......@@ -122,7 +125,7 @@ var edx = edx || {};
clearInterval(self.timerId); // stop the timer once the time finishes.
$(window).unbind('beforeunload', this.unloadMessage);
// refresh the page when the timer expired
location.reload();
this.reloadPage()
}
}
});
......
describe('ProctoredExamView', function () {
var html = '';
var expectedProctoredExamAttemptJson = [{
attempt_url: '/api/edx_proctoring/v1/proctored_exam/attempt/course_id/edX/DemoX/Demo_Course',
pagination_info: {
current_page: 1,
has_next: false,
has_previous: false,
total_pages: 1
},
proctored_exam_attempts: [{
allowed_time_limit_mins: 1,
attempt_code: "20C32387-372E-48BD-BCAC-A2BE9DC91E09",
completed_at: null,
created: "2015-08-10T09:15:45Z",
external_id: "40eceb15-bcc3-4791-b43f-4e843afb7ae8",
id: 43,
is_sample_attempt: false,
last_poll_ipaddr: null,
last_poll_timestamp: null,
modified: "2015-08-10T09:15:45Z",
started_at: null,
status: "created",
taking_as_proctored: true,
proctored_exam: {
content_id: "i4x://edX/DemoX/sequential/9f5e9b018a244ea38e5d157e0019e60c",
course_id: "edX/DemoX/Demo_Course",
exam_name: "Normal Exam",
external_id: null,
id: 17,
is_active: true,
is_practice_exam: false,
is_proctored: true,
time_limit_mins: 1
},
user: {
username: 'testuser1',
email: 'testuser1@test.com'
}
}]
}];
beforeEach(function () {
html = '<div class="wrapper-content wrapper">' +
'<% var is_proctored_attempts = proctored_exam_attempts.length !== 0 %>' +
'<section class="content">' +
'<div class="top-header">' +
'<div class="search-attempts">' +
'<input type="text" id="search_attempt_id" placeholder="e.g johndoe or john.doe@gmail.com"' +
'<% if (inSearchMode) { %> value="<%= searchText %>" <%} %>' +
'/> <span class="search"><i class="fa fa-search"></i></span> <span class="clear-search"><i class="fa fa-remove"></i></i></span>' +
'</div>' +
'<ul class="pagination">' +
'<% if (!pagination_info.has_previous){ %>' +
'<li class="disabled"> <a aria-label="Previous"> <span aria-hidden="true">&laquo;</span> </a> </li>' +
'<% } else { %>' +
'<li>' +
'<a class="target-link " data-target-url="' +
'<%- interpolate("%(attempt_url)s?page=%(count)s ", {attempt_url: attempt_url, count: pagination_info.current_page - 1}, true) %>' +
'"' +
'href="#" aria-label="Previous">' +
'<span aria-hidden="true">&laquo;</span> </a> </li> <% }%>' +
'<% for(var n = 1; n <= pagination_info.total_pages; n++) { %>' +
'<li> <a class="target-link <% if (pagination_info.current_page == n){ %> active <% } %>" data-target-url=" ' +
'<%- interpolate("%(attempt_url)s?page=%(count)s ", {attempt_url: attempt_url, count: n}, true) %>' +
'"href="#"><%= n %> </a></li> <% } %>' +
'<% if (!pagination_info.has_next){ %> <li class="disabled"> <a aria-label="Next"> <span aria-hidden="true">&raquo;</span> </a></li>' +
'<% } else { %> <li> <a class="target-link" href="#" aria-label="Next" data-target-url="' +
'<%- interpolate("%(attempt_url)s?page=%(count)s ",{attempt_url: attempt_url, count: pagination_info.current_page + 1}, true) %>' +
'" > <span aria-hidden="true">&raquo;</span></a> </li> <% }%> </ul><div class="clearfix"></div></div>' +
'<table class="exam-attempts-table"> <thead><tr class="exam-attempt-headings">' +
'<th class="username">Username</th>' +
'<th class="exam-name">Exam Name</th>' +
'<th class="attempt-allowed-time">Allowed Time (Minutes)</th>' +
'<th class="attempt-started-at">Started At</th>' +
'<th class="attempt-completed-at">Completed At</th>' +
'<th class="attempt-status">Status</th>' +
'<th class="c_action">Actions</th>' +
'</tr></thead>' +
'<% if (is_proctored_attempts) { %>' +
'<tbody>' +
'<% _.each(proctored_exam_attempts, function(proctored_exam_attempt){ %><tr class="allowance-items">' +
'<td>' +
' <%= proctored_exam_attempt.user.username %> ' +
' </td>' +
'<td>' +
' <%- interpolate(gettext(" %(exam_display_name)s "), { exam_display_name: proctored_exam_attempt.proctored_exam.exam_name }, true) %>' +
'</td>' +
'<td>' +
' <%= proctored_exam_attempt.allowed_time_limit_mins %> ' +
'</td>' +
'<td>' +
' <%= getDateFormat(proctored_exam_attempt.started_at) %>' +
'</td>' +
'<td>' +
' <%= getDateFormat(proctored_exam_attempt.completed_at) %>' +
'</td>' +
'<td>' +
' <% if (proctored_exam_attempt.status){ %> <%= proctored_exam_attempt.status %> <% } else { %> N/A <% } %> ' +
'</td>' +
'<td>' +
' <% if (proctored_exam_attempt.status){ %> ' +
'<a href="#" class="remove-attempt" data-attempt-id="<%= proctored_exam_attempt.id %>" >[x]</a> </td>' +
' <% } else { %>N/A <% } %>' +
'</tr>' +
' <% }); %> ' +
'</tbody>' +
' <% } %>' +
' </table>' +
'<% if (!is_proctored_attempts) { %> ' +
'<p> No exam results found. </p>' +
'<% } %> ' +
'</section> </div>';
this.server = sinon.fakeServer.create();
this.server.autoRespond = true;
setFixtures('<div class="student-proctored-exam-container" data-course-id="test_course_id"></div>');
});
afterEach(function() {
this.server.restore();
});
it("should render the proctored exam attempt view properly", function () {
this.server.respondWith("GET", "/static/proctoring/templates/student-proctored-exam-attempts.underscore",
[
200,
{"Content-Type": "text/html"},
html
]
);
this.server.respondWith('GET', '/api/edx_proctoring/v1/proctored_exam/attempt/course_id/test_course_id',
[
200,
{
"Content-Type": "application/json"
},
JSON.stringify(expectedProctoredExamAttemptJson)
]
);
var callbacks = [sinon.spy(), sinon.spy()];
this.proctored_exam_attempt_view = new edx.instructor_dashboard.proctoring.ProctoredExamAttemptView(
{
el: $('.student-proctored-exam-container'),
template_url: '/static/proctoring/templates/student-proctored-exam-attempts.underscore'
}
);
console.log(this.server.requests); // Logs all requests so far
this.server.respond(); // Process all requests so far
});
});
define(['jquery', 'backbone', 'common/js/spec_helpers/template_helpers', 'js/courseware/base/models/proctored_exam_model', 'js/courseware/base/views/proctored_exam_view'
], function($, Backbone, TemplateHelpers, ProctoredExamModel, ProctoredExamView) {
'use strict';
describe('Proctored Exam', function () {
beforeEach(function () {
this.model = new ProctoredExamModel();
});
it('model has properties', function () {
expect(this.model.get('in_timed_exam')).toBeDefined();
expect(this.model.get('is_proctored')).toBeDefined();
expect(this.model.get('exam_display_name')).toBeDefined();
expect(this.model.get('exam_url_path')).toBeDefined();
expect(this.model.get('time_remaining_seconds')).toBeDefined();
expect(this.model.get('low_threshold')).toBeDefined();
expect(this.model.get('critically_low_threshold')).toBeDefined();
expect(this.model.get('lastFetched')).toBeDefined();
describe('ProctoredExamView', function () {
beforeEach(function () {
this.server = sinon.fakeServer.create();
jasmine.clock().install();
setFixtures(
'<div class="proctored_exam_status">' +
'<script type="text/template" id="proctored-exam-status-tpl">' +
'<div class="exam-timer">' +
'<%- gettext("You are taking") %>' +
'<a href="<%= exam_url_path %>"> <%= exam_display_name %> </a>' +
'<%- gettext(" exam as a proctored exam. Good Luck!") %>' +
'<span id="time_remaining_id" class="pull-right"> <b> </b> </span> </div>' +
'</script>'+
'</div>'
);
this.model = new ProctoredExamModel({
in_timed_exam: true,
is_proctored: true,
exam_display_name: 'Midterm',
taking_as_proctored: true,
exam_url_path: '/test_url',
time_remaining_seconds: 45, //2 * 60 + 15,
low_threshold_sec: 30,
attempt_id: 2,
critically_low_threshold_sec: 15,
lastFetched: new Date()
});
this.proctored_exam_view = new edx.coursware.proctored_exam.ProctoredExamView(
{
model: this.model,
el: $(".proctored_exam_status"),
proctored_template: '#proctored-exam-status-tpl'
}
);
this.proctored_exam_view.render();
});
describe('ProctoredExamView', function () {
beforeEach(function () {
TemplateHelpers.installTemplate('templates/courseware/proctored-exam-status', true, 'proctored-exam-status-tpl');
appendSetFixtures('<div class="proctored_exam_status"></div>');
this.model = new ProctoredExamModel({
in_timed_exam: true,
is_proctored: true,
exam_display_name: 'Midterm',
exam_url_path: '/test_url',
time_remaining_seconds: 45, //2 * 60 + 15,
low_threshold: 30,
critically_low_threshold: 15,
lastFetched: new Date()
});
afterEach(function() {
this.server.restore();
jasmine.clock().uninstall();
});
this.proctored_exam_view = new edx.coursware.proctored_exam.ProctoredExamView(
{
model: this.model,
el: $(".proctored_exam_status"),
proctored_template: '#proctored-exam-status-tpl'
}
);
this.proctored_exam_view.render();
it('renders items correctly', function () {
expect(this.proctored_exam_view.$el.find('a')).toHaveAttr('href', this.model.get("exam_url_path"));
expect(this.proctored_exam_view.$el.find('a')).toContainHtml(this.model.get('exam_display_name'));
});
it('changes behavior when clock time decreases low threshold', function () {
spyOn(this.model, 'getRemainingSeconds').and.callFake(function() {
return 25;
});
expect(this.model.getRemainingSeconds()).toEqual(25);
expect(this.proctored_exam_view.$el.find('div.exam-timer')).not.toHaveClass('low-time warning');
this.proctored_exam_view.render();
it('renders items correctly', function () {
expect(this.proctored_exam_view.$el.find('a')).toHaveAttr('href', this.model.get("exam_url_path"));
expect(this.proctored_exam_view.$el.find('a')).toContainHtml(this.model.get('exam_display_name'));
});
it('changes behavior when clock time decreases low threshold', function () {
spyOn(this.model, 'getRemainingSeconds').andCallFake(function () {
return 25;
});
expect(this.model.getRemainingSeconds()).toEqual(25);
expect(this.proctored_exam_view.$el.find('div.exam-timer')).not.toHaveClass('low-time warning');
this.proctored_exam_view.render();
expect(this.proctored_exam_view.$el.find('div.exam-timer')).toHaveClass('low-time warning');
expect(this.proctored_exam_view.$el.find('div.exam-timer')).toHaveClass('low-time warning');
});
it('changes behavior when clock time decreases critically low threshold', function () {
spyOn(this.model, 'getRemainingSeconds').and.callFake(function () {
return 5;
});
it('changes behavior when clock time decreases critically low threshold', function () {
spyOn(this.model, 'getRemainingSeconds').andCallFake(function () {
return 5;
});
expect(this.model.getRemainingSeconds()).toEqual(5);
expect(this.proctored_exam_view.$el.find('div.exam-timer')).not.toHaveClass('low-time critical');
this.proctored_exam_view.render();
expect(this.proctored_exam_view.$el.find('div.exam-timer')).toHaveClass('low-time critical');
expect(this.model.getRemainingSeconds()).toEqual(5);
expect(this.proctored_exam_view.$el.find('div.exam-timer')).not.toHaveClass('low-time critical');
this.proctored_exam_view.render();
expect(this.proctored_exam_view.$el.find('div.exam-timer')).toHaveClass('low-time critical');
});
it("reload the page when the exam time finishes", function(){
spyOn(this.model, 'getRemainingSeconds').and.callFake(function() {
return -10;
});
expect(this.model.getRemainingSeconds()).toEqual(-10);
var reloadPage = spyOn(this.proctored_exam_view, 'reloadPage');
this.proctored_exam_view.render();
expect(reloadPage).toHaveBeenCalled();
});
});
......@@ -121,7 +121,7 @@
<% } %>
</table>
<% if (!is_proctored_attempts) { %>
<p> No exam results found.
<p> No exam results found. </p>
<% } %>
</section>
......
var gulp = require('gulp');
var karma = require('karma').server;
var coverageOnOff = 'coverage';
/**
* Run test once and exit
*/
gulp.task('test', function (done) {
karma.start({
configFile: __dirname + '/karma.conf.js',
singleRun: true
}, done);
});
/**
* Watch for file changes and re-run tests on each change
*/
gulp.task('tdd', function (done) {
karma.start({
configFile: __dirname + '/karma.conf.js'
}, done);
});
gulp.task('default', ['tdd']);
/**
* Run test in debug mode
*/
gulp.task('debug', function (done) {
karma.start({
configFile: __dirname + '/karma.conf.js',
singleRun: false
}, done);
});
\ No newline at end of file
//Add ability to turn coverage off when the tests are run in debug mode
var sourcePreprocessors = 'coverage';
function isDebug(argument) {
return argument === 'debug';
}
if (process.argv.some(isDebug)) {
sourcePreprocessors = [];
}
module.exports = function(config) {
config.set({
basePath: '',
//plugins required for running the karma tests
plugins:[
'karma-jasmine',
'karma-jasmine-jquery',
'karma-firefox-launcher',
'karma-jasmine-jquery',
'karma-chrome-launcher',
'karma-coverage',
'karma-sinon'
],
// start the browser
browsers: ['Firefox'],
//frameworks to use
frameworks: ['jasmine-jquery', 'jasmine', 'sinon'],
//patterns to load all files in child folders
files: [
'edx_proctoring/static/proctoring/js/vendor/i18n.js',
'edx_proctoring/static/proctoring/js/vendor/jquery.js',
'edx_proctoring/static/proctoring/js/vendor/underscore.js',
'edx_proctoring/static/proctoring/js/vendor/backbone.js',
'edx_proctoring/static/proctoring/js/vendor/date.js',
'edx_proctoring/static/proctoring/js/models/*.js',
'edx_proctoring/static/proctoring/js/collections/*.js',
'edx_proctoring/static/proctoring/js/views/*.js',
'edx_proctoring/static/proctoring/spec/*.js'
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'edx_proctoring/static/proctoring/js/models/*.js': sourcePreprocessors,
'edx_proctoring/static/proctoring/js/collections/*.js': sourcePreprocessors,
'edx_proctoring/static/proctoring/js/views/*.js': sourcePreprocessors
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'coverage'],
coverageReporter: {
dir:'build', subdir: 'coverage-js',
reporters:[
{type: 'html', subdir: 'coverage-js/html'},
{type: 'cobertura', file: 'coverage.xml'},
{type: 'text-summary'}
]
},
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
captureTimeout: 60000,
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false
});
};
{
"name": "edx-proctoring",
"repository": {
"type": "git",
"url": "git://github.com/edx/edx-proctoring"
},
"devDependencies": {
"gulp": "^3.9.0",
"gulp-karma": "0.0.1",
"karma": "^0.13.0",
"karma-chrome-launcher": "^0.2.0",
"karma-coverage": "latest",
"karma-firefox-launcher": "latest",
"karma-jasmine": "^0.3.6",
"karma-jasmine-jquery": "0.1.1",
"karma-sinon": "latest"
}
}
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