Commit ef4b5cb8 by Brian Jacobel

Clean up spec and feature; both pass lint and run in Karma

parent c5df4aa6
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
"IE >= 11" "IE >= 11"
] ]
}, },
"modules": "commonjs" "modules": false
} }
] ]
] ]
......
...@@ -170,7 +170,8 @@ function junitSettings(config) { ...@@ -170,7 +170,8 @@ function junitSettings(config) {
* @param {String} pattern * @param {String} pattern
* @return {String} * @return {String}
*/ */
var defaultNormalizeFunc = function(appRoot, pattern) { // I'd like to change fix the no-shadow violation on the next line, but it would break this shared conf's API.
function defaultNormalizeFunc(appRoot, pattern) { // eslint-disable-line no-shadow
if (pattern.match(/^common\/js/)) { if (pattern.match(/^common\/js/)) {
pattern = path.join(appRoot, '/common/static/' + pattern); pattern = path.join(appRoot, '/common/static/' + pattern);
} else if (pattern.match(/^xmodule_js\/common_static/)) { } else if (pattern.match(/^xmodule_js\/common_static/)) {
...@@ -178,9 +179,9 @@ var defaultNormalizeFunc = function(appRoot, pattern) { ...@@ -178,9 +179,9 @@ var defaultNormalizeFunc = function(appRoot, pattern) {
pattern.replace(/^xmodule_js\/common_static\//, '')); pattern.replace(/^xmodule_js\/common_static\//, ''));
} }
return pattern; return pattern;
}; }
var normalizePathsForCoverage = function(files, normalizeFunc, preprocessors) { function normalizePathsForCoverage(files, normalizeFunc, preprocessors) {
var normalizeFn = normalizeFunc || defaultNormalizeFunc, var normalizeFn = normalizeFunc || defaultNormalizeFunc,
normalizedFile, normalizedFile,
filesForCoverage = {}; filesForCoverage = {};
...@@ -193,7 +194,7 @@ var normalizePathsForCoverage = function(files, normalizeFunc, preprocessors) { ...@@ -193,7 +194,7 @@ var normalizePathsForCoverage = function(files, normalizeFunc, preprocessors) {
}); });
return filesForCoverage; return filesForCoverage;
}; }
/** /**
* Sets defaults for each file pattern. * Sets defaults for each file pattern.
...@@ -202,7 +203,7 @@ var normalizePathsForCoverage = function(files, normalizeFunc, preprocessors) { ...@@ -202,7 +203,7 @@ var normalizePathsForCoverage = function(files, normalizeFunc, preprocessors) {
* @param {Object} files * @param {Object} files
* @return {Object} * @return {Object}
*/ */
var setDefaults = function(files) { function setDefaults(files) {
return files.map(function(f) { return files.map(function(f) {
var file = _.isObject(f) ? f : {pattern: f}; var file = _.isObject(f) ? f : {pattern: f};
if (!file.included && !file.webpack) { if (!file.included && !file.webpack) {
...@@ -210,9 +211,9 @@ var setDefaults = function(files) { ...@@ -210,9 +211,9 @@ var setDefaults = function(files) {
} }
return file; return file;
}); });
}; }
var getBaseConfig = function(config, useRequireJs) { function getBaseConfig(config, useRequireJs) {
var getFrameworkFiles = function() { var getFrameworkFiles = function() {
var files = [ var files = [
'node_modules/jquery/dist/jquery.js', 'node_modules/jquery/dist/jquery.js',
...@@ -347,9 +348,9 @@ var getBaseConfig = function(config, useRequireJs) { ...@@ -347,9 +348,9 @@ var getBaseConfig = function(config, useRequireJs) {
webpack: webpackConfig webpack: webpackConfig
}; };
}; }
var configure = function(config, options) { function configure(config, options) {
var useRequireJs = options.useRequireJs === undefined ? true : useRequireJs, var useRequireJs = options.useRequireJs === undefined ? true : useRequireJs,
baseConfig = getBaseConfig(config, useRequireJs); baseConfig = getBaseConfig(config, useRequireJs);
...@@ -397,7 +398,7 @@ var configure = function(config, options) { ...@@ -397,7 +398,7 @@ var configure = function(config, options) {
files: files, files: files,
preprocessors: preprocessors preprocessors: preprocessors
})); }));
}; }
module.exports = { module.exports = {
configure: configure, configure: configure,
......
import * as constants from 'edx-ui-toolkit/src/js/utils/constants'; /* globals Logger */
import log from 'logger';
export class CourseOutline { // import constants from 'edx-ui-toolkit/src/js/utils/constants';
constructor(root) {
document.querySelector(root).addEventListener('keydown', (event) => {
const focusable = [...document.querySelectorAll('.outline-item.focusable')];
const currentFocusIndex = focusable.indexOf(event.target);
switch (event.keyCode) { // eslint-disable-line default-case // @TODO: Figure out how to make webpack handle default exports when libraryTarget: 'window'
case constants.keyCodes.down: export class CourseOutline { // eslint-disable-line import/prefer-default-export
constructor() {
const focusable = [...document.querySelectorAll('.outline-item.focusable')];
focusable.forEach(el => el.addEventListener('keydown', (event) => {
const index = focusable.indexOf(event.target);
switch (event.key) { // eslint-disable-line default-case
case 'ArrowDown': // @TODO: Get these from the UI Toolkit
event.preventDefault(); event.preventDefault();
focusable[Math.min(currentFocusIndex + 1, focusable.length - 1)].focus(); focusable[Math.min(index + 1, focusable.length - 1)].focus();
break; break;
case constants.keyCodes.up: case 'ArrowUp': // @TODO: Get these from the UI Toolkit
event.preventDefault(); event.preventDefault();
focusable[Math.max(currentFocusIndex - 1, 0)].focus(); focusable[Math.max(index - 1, 0)].focus();
break; break;
} }
}); }));
document.querySelectorAll('a:not([href^="#"])').addEventListener('click', (event) => { document.querySelectorAll('a:not([href^="#"])')
log( .forEach(link => link.addEventListener('click', (event) => {
'edx.ui.lms.link_clicked', Logger.log(
{ 'edx.ui.lms.link_clicked',
current_url: window.location.href, {
target_url: event.currentTarget.href current_url: window.location.href,
} target_url: event.currentTarget.href,
},
); );
}); }),
);
} }
} }
import * as constants from "edx-ui-toolkit/js/utils/constants"; /* globals Logger, loadFixtures */
import log from 'logger';
import { CourseOutline } from "../CourseOutline"; // import constants from 'edx-ui-toolkit/src/js/utils/constants';
import { CourseOutline } from '../CourseOutline';
describe('Course outline factory', () => { describe('Course outline factory', () => {
let outline; // eslint-disable-line no-unused-vars
// Our block IDs are invalid DOM selectors unless we first escape `:`, `+` and `@`
const escapeIds = idObj => Object.assign({}, ...Object.keys(idObj).map(key => ({
[key]: idObj[key]
.replace(/@/g, '\\@')
.replace(/:/, '\\:')
.replace(/\+/g, '\\+'),
})));
const outlineIds = escapeIds({
homeworkLabsAndDemos: 'a#block-v1:edX+DemoX+Demo_Course+type@sequential+block@graded_simulations',
homeworkEssays: 'a#block-v1:edX+DemoX+Demo_Course+type@sequential+block@175e76c4951144a29d46211361266e0e',
lesson3BeSocial: 'a#block-v1:edX+DemoX+Demo_Course+type@sequential+block@48ecb924d7fe4b66a230137626bfa93e',
exampleWeek3BeSocial: 'li#block-v1:edX+DemoX+Demo_Course+type@chapter+block@social_integration',
});
describe('keyboard listener', () => { describe('keyboard listener', () => {
const triggerKeyListener = (current, destination, keyCode) => { const triggerKeyListener = (current, destination, key) => {
current.focus(); current.focus();
spyOn(destination, 'focus'); spyOn(destination, 'focus');
$('.block-tree').trigger( current.dispatchEvent(new KeyboardEvent('keydown', { key }));
$.Event('keydown', {
keyCode,
target: current,
}),
);
}; };
beforeEach(() => { beforeEach(() => {
loadFixtures('course_experience/fixtures/course-outline-fragment.html'); loadFixtures('course_experience/fixtures/course-outline-fragment.html');
new CourseOutline('.block-tree'); outline = new CourseOutline();
}); });
describe('when the down arrow is pressed', () => { describe('when the down arrow is pressed', () => {
it('moves focus from a subsection to the next subsection in the outline', () => { it('moves focus from a subsection to the next subsection in the outline', () => {
const current = $('a.focusable:contains("Homework - Labs and Demos")')[0]; const current = document.querySelector(outlineIds.homeworkLabsAndDemos);
const destination = $('a.focusable:contains("Homework - Essays")')[0]; const destination = document.querySelector(outlineIds.homeworkEssays);
triggerKeyListener(current, destination, constants.keyCodes.down); triggerKeyListener(current, destination, 'ArrowDown'); // @TODO: Get these from the UI Toolkit
expect(destination.focus).toHaveBeenCalled(); expect(destination.focus).toHaveBeenCalled();
}); });
it('moves focus to the section list if at a section boundary', () => { it('moves focus to the subsection list if at the top of a section', () => {
const current = $('li.focusable:contains("Example Week 3: Be Social")')[0]; const current = document.querySelector(outlineIds.exampleWeek3BeSocial);
const destination = $('ol.focusable:contains("Lesson 3 - Be Social")')[0]; const destination = document.querySelector(`${outlineIds.exampleWeek3BeSocial} > ol`);
triggerKeyListener(current, destination, constants.keyCodes.down); triggerKeyListener(current, destination, 'ArrowDown'); // @TODO: Get these from the UI Toolkit
expect(destination.focus).toHaveBeenCalled(); expect(destination.focus).toHaveBeenCalled();
}); });
it('moves focus to the next section if on the last subsection', () => { it('moves focus to the next section if on the last subsection', () => {
const current = $('a.focusable:contains("Homework - Essays")')[0]; const current = document.querySelector(outlineIds.homeworkEssays);
const destination = $('li.focusable:contains("Example Week 3: Be Social")')[0]; const destination = document.querySelector(outlineIds.exampleWeek3BeSocial);
triggerKeyListener(current, destination, constants.keyCodes.down); triggerKeyListener(current, destination, 'ArrowDown'); // @TODO: Get these from the UI Toolkit
expect(destination.focus).toHaveBeenCalled(); expect(destination.focus).toHaveBeenCalled();
}); });
...@@ -52,53 +66,48 @@ describe('Course outline factory', () => { ...@@ -52,53 +66,48 @@ describe('Course outline factory', () => {
describe('when the up arrow is pressed', () => { describe('when the up arrow is pressed', () => {
it('moves focus from a subsection to the previous subsection in the outline', () => { it('moves focus from a subsection to the previous subsection in the outline', () => {
const current = $('a.focusable:contains("Homework - Essays")')[0]; const current = document.querySelector(outlineIds.homeworkEssays);
const destination = $('a.focusable:contains("Homework - Labs and Demos")')[0]; const destination = document.querySelector(outlineIds.homeworkLabsAndDemos);
triggerKeyListener(current, destination, constants.keyCodes.up); triggerKeyListener(current, destination, 'ArrowUp'); // @TODO: Get these from the UI Toolkit
expect(destination.focus).toHaveBeenCalled(); expect(destination.focus).toHaveBeenCalled();
}); });
it('moves focus to the section group if at the first subsection', () => { it('moves focus to the section list if at the first subsection', () => {
const current = $('a.focusable:contains("Lesson 3 - Be Social")')[0]; const current = document.querySelector(outlineIds.lesson3BeSocial);
const destination = $('ol.focusable:contains("Lesson 3 - Be Social")')[0]; const destination = document.querySelector(`${outlineIds.exampleWeek3BeSocial} > ol`);
triggerKeyListener(current, destination, constants.keyCodes.up); triggerKeyListener(current, destination, 'ArrowUp'); // @TODO: Get these from the UI Toolkit
expect(destination.focus).toHaveBeenCalled(); expect(destination.focus).toHaveBeenCalled();
}); });
it('moves focus last subsection of the previous section if at a section boundary', () => { it('moves focus last subsection of the previous section if at a section boundary', () => {
const current = $('li.focusable:contains("Example Week 3: Be Social")')[0]; const current = document.querySelector(outlineIds.exampleWeek3BeSocial);
const destination = $('a.focusable:contains("Homework - Essays")')[0]; const destination = document.querySelector(outlineIds.homeworkEssays);
triggerKeyListener(current, destination, constants.keyCodes.up); triggerKeyListener(current, destination, 'ArrowUp'); // @TODO: Get these from the UI Toolkit
expect(destination.focus).toHaveBeenCalled(); expect(destination.focus).toHaveBeenCalled();
}); });
}); });
}); });
describe("eventing", function() { describe('eventing', () => {
beforeEach(function() { beforeEach(() => {
loadFixtures("course_experience/fixtures/course-outline-fragment.html"); loadFixtures('course_experience/fixtures/course-outline-fragment.html');
CourseOutlineFactory(".block-tree"); outline = new CourseOutline();
spyOn(Logger, "log"); spyOn(Logger, 'log');
}); });
it("sends an event when an outline section is clicked", function() { it('sends an event when an outline section is clicked', () => {
$('a.focusable:contains("Homework - Labs and Demos")').click(); document.querySelector(outlineIds.homeworkLabsAndDemos).dispatchEvent(new Event('click'));
expect(Logger.log).toHaveBeenCalledWith("edx.ui.lms.link_clicked", { expect(Logger.log).toHaveBeenCalledWith('edx.ui.lms.link_clicked', {
target_url: ( target_url: `${window.location.origin}/courses/course-v1:edX+DemoX+Demo_Course/jump_to/block-v1:edX+DemoX+Demo_Course+type@sequential+block@graded_simulations`,
window.location.origin + current_url: window.location.toString(),
"/courses/course-v1:edX+DemoX+Demo_Course/jump_to/block-v1:edX+DemoX+Demo_Course+type" +
"@sequential+block@graded_simulations"
),
current_url: window.location.toString()
}); });
}); });
}); });
}); });
...@@ -161,5 +161,5 @@ from openedx.core.djangolib.markup import HTML, Text ...@@ -161,5 +161,5 @@ from openedx.core.djangolib.markup import HTML, Text
</%static:require_module_async> </%static:require_module_async>
<%static:webpack entry="CourseOutline"> <%static:webpack entry="CourseOutline">
new CourseOutline('.block-tree'); new CourseOutline();
</%static:webpack> </%static:webpack>
const path = require('path'); var path = require('path');
const webpack = require('webpack'); var webpack = require('webpack');
const BundleTracker = require('webpack-bundle-tracker'); var BundleTracker = require('webpack-bundle-tracker');
const isProd = process.env.NODE_ENV === 'production'; var isProd = process.env.NODE_ENV === 'production';
const wpconfig = { var wpconfig = {
context: __dirname, context: __dirname,
entry: { entry: {
CourseOutline: './openedx/features/course_experience/static/course_experience/js/CourseOutline.js', CourseOutline: './openedx/features/course_experience/static/course_experience/js/CourseOutline.js'
}, },
output: { output: {
path: path.resolve(__dirname, 'common/static/bundles'), path: path.resolve(__dirname, 'common/static/bundles'),
filename: '[name]-[hash].js', filename: '[name]-[hash].js',
libraryTarget: 'window', libraryTarget: 'window'
}, },
devtool: isProd ? false : 'cheap-eval-source-map', devtool: isProd ? false : 'eval-source-map',
plugins: [ plugins: [
new webpack.NoEmitOnErrorsPlugin(), new webpack.NoEmitOnErrorsPlugin(),
new webpack.NamedModulesPlugin(), new webpack.NamedModulesPlugin(),
new webpack.DefinePlugin({ new webpack.DefinePlugin({
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'), 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
}), }),
new webpack.LoaderOptionsPlugin({ new webpack.LoaderOptionsPlugin({
debug: !isProd, debug: !isProd
}), }),
new BundleTracker({ new BundleTracker({
filename: './webpack-stats.json' filename: './webpack-stats.json'
}), })
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader',
},
], ],
},
resolve: { module: {
extensions: ['.js', '.json'], rules: [
} {
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
},
resolve: {
extensions: ['.js', '.json']
}
}; };
if (isProd) { if (isProd) {
wpconfig.plugins = [ wpconfig.plugins = wpconfig.plugins.concat([
new webpack.LoaderOptionsPlugin({ new webpack.LoaderOptionsPlugin({
minimize: true, minimize: true
}), }),
new webpack.optimize.UglifyJsPlugin(), new webpack.optimize.UglifyJsPlugin()
...wpconfig.plugins, ]);
];
} }
module.exports = wpconfig; module.exports = wpconfig;
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