зеркало из https://github.com/mozilla/kitsune.git
fix tests to work with webpack
run tests through webpack first, then mocha reorganise webpack config files
This commit is contained in:
Родитель
8e66941f01
Коммит
97a7514315
|
@ -39,7 +39,7 @@ jobs:
|
||||||
command: ./bin/dc_ci.sh run test ./bin/run-unit-tests.sh
|
command: ./bin/dc_ci.sh run test ./bin/run-unit-tests.sh
|
||||||
- run:
|
- run:
|
||||||
name: Run js tests
|
name: Run js tests
|
||||||
command: ./bin/dc_ci.sh run test ./bin/run-mocha-tests.sh
|
command: ./bin/dc_ci.sh run test npm run webpack:test
|
||||||
- when:
|
- when:
|
||||||
condition:
|
condition:
|
||||||
or:
|
or:
|
||||||
|
|
|
@ -28,7 +28,7 @@ repos:
|
||||||
hooks:
|
hooks:
|
||||||
- id: eslint
|
- id: eslint
|
||||||
args: [--no-eslintrc, --config=webpack/eslintrc.js]
|
args: [--no-eslintrc, --config=webpack/eslintrc.js]
|
||||||
exclude: "webpack/.*"
|
exclude: "webpack/.*|webpack\\..*\\.js"
|
||||||
additional_dependencies:
|
additional_dependencies:
|
||||||
- eslint@8.1.0
|
- eslint@8.1.0
|
||||||
- eslint-import-resolver-webpack@0.13.2
|
- eslint-import-resolver-webpack@0.13.2
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -81,7 +81,7 @@ test: .docker-build
|
||||||
${DC} run web ./bin/run-unit-tests.sh
|
${DC} run web ./bin/run-unit-tests.sh
|
||||||
|
|
||||||
test-js: .docker-build
|
test-js: .docker-build
|
||||||
${DC} run web ./bin/run-mocha-tests.sh
|
${DC} run web npm run webpack:test
|
||||||
|
|
||||||
docs: .docker-build
|
docs: .docker-build
|
||||||
${DC} run web $(MAKE) -C docs/ clean
|
${DC} run web $(MAKE) -C docs/ clean
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
./node_modules/.bin/mocha --require ./webpack/mocha-require --recursive kitsune/*/static/*/js/tests/* $@
|
|
|
@ -144,7 +144,7 @@ Running JavaScript Tests
|
||||||
To run tests, make sure you have have the NPM dependencies installed, and
|
To run tests, make sure you have have the NPM dependencies installed, and
|
||||||
then run::
|
then run::
|
||||||
|
|
||||||
$ bin/run-mocha-tests.sh
|
$ npm run webpack:test
|
||||||
|
|
||||||
Writing JavaScript Tests
|
Writing JavaScript Tests
|
||||||
------------------------
|
------------------------
|
||||||
|
@ -164,7 +164,6 @@ Here are a few tips for writing tests:
|
||||||
* You can use `sinon` to mock out parts of libraries or functions under
|
* You can use `sinon` to mock out parts of libraries or functions under
|
||||||
test. This is useful for testing AJAX.
|
test. This is useful for testing AJAX.
|
||||||
* The tests run in a Node.js environment. A browser environment can be
|
* The tests run in a Node.js environment. A browser environment can be
|
||||||
simulated using ``jsdom``. Specifically, ``mocha-jsdom`` is useful to
|
simulated using ``jsdom``.
|
||||||
set up and tear down the simulated environment.
|
|
||||||
|
|
||||||
.. _Mocha: https://mochajs.org/
|
.. _Mocha: https://mochajs.org/
|
||||||
|
|
|
@ -5,7 +5,7 @@ export default env;
|
||||||
|
|
||||||
(function($) {
|
(function($) {
|
||||||
|
|
||||||
env.addGlobal('_', gettext);
|
env.addGlobal('_', window.gettext);
|
||||||
env.addGlobal('ngettext', window.ngettext);
|
env.addGlobal('ngettext', window.ngettext);
|
||||||
|
|
||||||
env.addFilter('f', function(fmt, obj, named) {
|
env.addFilter('f', function(fmt, obj, named) {
|
||||||
|
@ -16,7 +16,7 @@ export default env;
|
||||||
obj[keys[i]] = escape(obj[keys[i]]);
|
obj[keys[i]] = escape(obj[keys[i]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return interpolate(fmt, obj, named);
|
return window.interpolate(fmt, obj, named);
|
||||||
});
|
});
|
||||||
|
|
||||||
env.addFilter('urlparams', function(url, params) {
|
env.addFilter('urlparams', function(url, params) {
|
||||||
|
|
|
@ -17,313 +17,309 @@ import AAQSystemInfo from "sumo/js/aaq";
|
||||||
// TODO: Figure out how to break out the functionality here into
|
// TODO: Figure out how to break out the functionality here into
|
||||||
// testable parts.
|
// testable parts.
|
||||||
|
|
||||||
(function($) {
|
function init() {
|
||||||
|
var $body = $('body');
|
||||||
|
|
||||||
function init() {
|
// if there's an error on page load, focus the field.
|
||||||
var $body = $('body');
|
$('.has-error input, .has-error textarea').first().focus();
|
||||||
|
|
||||||
// if there's an error on page load, focus the field.
|
if ($body.is('.new-question')) {
|
||||||
$('.has-error input, .has-error textarea').first().focus();
|
initQuestion();
|
||||||
|
|
||||||
if ($body.is('.new-question')) {
|
if (window.location.search.indexOf('step=aaq-register') > -1) {
|
||||||
initQuestion();
|
trackEvent('Ask A Question Flow', 'step 1 page');
|
||||||
|
} else if (window.location.search.indexOf('step=aaq-question') > -1) {
|
||||||
if (window.location.search.indexOf('step=aaq-register') > -1) {
|
trackEvent('Ask A Question Flow', 'step 2 page');
|
||||||
trackEvent('Ask A Question Flow', 'step 1 page');
|
|
||||||
} else if (window.location.search.indexOf('step=aaq-question') > -1) {
|
|
||||||
trackEvent('Ask A Question Flow', 'step 2 page');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($body.is('.edit-question')) {
|
|
||||||
initQuestion("editing");
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($body.is('.questions')) {
|
|
||||||
initTagFilterToggle();
|
|
||||||
|
|
||||||
$('#flag-filter input[type="checkbox"]').on('click', function() {
|
|
||||||
window.location = $(this).data('url');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (window.location.pathname.indexOf('questions/new/confirm') > -1) {
|
|
||||||
trackEvent('Ask A Question Flow', 'step 3 confirm page');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($body.is('.answers')) {
|
|
||||||
// Put last search query into search box
|
|
||||||
$('#support-search input[name=q]')
|
|
||||||
.val(unquote($.cookie('last_search')));
|
|
||||||
|
|
||||||
function takeQuestion() {
|
|
||||||
if ($(this).val().length > 0) {
|
|
||||||
var $form = $(this).closest('form');
|
|
||||||
var url = $form.data('take-question-url');
|
|
||||||
var csrftoken = $('input[name=csrfmiddlewaretoken]').val();
|
|
||||||
$.ajax({
|
|
||||||
url: url,
|
|
||||||
method: 'POST',
|
|
||||||
beforeSend: function(xhr, settings) {
|
|
||||||
xhr.setRequestHeader('X-CSRFToken', csrftoken);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$('#id_content').on('keyup', _throttle(takeQuestion, 60000));
|
|
||||||
|
|
||||||
$(document).on('click', '#details-edit', function(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
$('#question-details').addClass('editing');
|
|
||||||
});
|
|
||||||
|
|
||||||
initHaveThisProblemTooAjax();
|
|
||||||
initHelpfulVote();
|
|
||||||
initCrashIdLinking();
|
|
||||||
initEditDetails();
|
|
||||||
addReferrerAndQueryToVoteForm();
|
|
||||||
initReplyToAnswer();
|
|
||||||
new AjaxPreview($('#preview'));
|
|
||||||
}
|
|
||||||
|
|
||||||
Marky.createSimpleToolbar('.editor-tools', '#reply-content, #id_content', {cannedResponses: !$body.is('.new-question')});
|
|
||||||
|
|
||||||
// product selector page reloading
|
|
||||||
$('#product-selector select').on('change', function() {
|
|
||||||
var val = $(this).val();
|
|
||||||
var queryParams = getQueryParamsAsDict(document.location.toString());
|
|
||||||
|
|
||||||
if (val === '') {
|
|
||||||
delete queryParams.product;
|
|
||||||
} else {
|
|
||||||
queryParams.product = val;
|
|
||||||
}
|
|
||||||
document.location = document.location.pathname + '?' + $.param(queryParams);
|
|
||||||
});
|
|
||||||
|
|
||||||
// sort questions page reloading
|
|
||||||
$('[data-sort-questions]').on('change', function() {
|
|
||||||
document.location = $(this).val()
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if ($body.is('.edit-question')) {
|
||||||
* Initialize the new/edit question page/form
|
initQuestion("editing");
|
||||||
*/
|
}
|
||||||
function initQuestion(action) {
|
|
||||||
var $questionForm = $('#question-form');
|
if ($body.is('.questions')) {
|
||||||
var aaq = new AAQSystemInfo($questionForm);
|
initTagFilterToggle();
|
||||||
if (action === "editing") {
|
|
||||||
$("#troubleshooting-field").show();
|
$('#flag-filter input[type="checkbox"]').on('click', function() {
|
||||||
|
window.location = $(this).data('url');
|
||||||
|
});
|
||||||
|
|
||||||
|
if (window.location.pathname.indexOf('questions/new/confirm') > -1) {
|
||||||
|
trackEvent('Ask A Question Flow', 'step 3 confirm page');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($body.is('.answers')) {
|
||||||
|
// Put last search query into search box
|
||||||
|
$('#support-search input[name=q]')
|
||||||
|
.val(unquote($.cookie('last_search')));
|
||||||
|
|
||||||
|
function takeQuestion() {
|
||||||
|
if ($(this).val().length > 0) {
|
||||||
|
var $form = $(this).closest('form');
|
||||||
|
var url = $form.data('take-question-url');
|
||||||
|
var csrftoken = $('input[name=csrfmiddlewaretoken]').val();
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
method: 'POST',
|
||||||
|
beforeSend: function(xhr, settings) {
|
||||||
|
xhr.setRequestHeader('X-CSRFToken', csrftoken);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$('#id_content').on('keyup', _throttle(takeQuestion, 60000));
|
||||||
|
|
||||||
|
$(document).on('click', '#details-edit', function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
$('#question-details').addClass('editing');
|
||||||
|
});
|
||||||
|
|
||||||
|
initHaveThisProblemTooAjax();
|
||||||
|
initHelpfulVote();
|
||||||
|
initCrashIdLinking();
|
||||||
|
initEditDetails();
|
||||||
|
addReferrerAndQueryToVoteForm();
|
||||||
|
initReplyToAnswer();
|
||||||
|
new AjaxPreview($('#preview'));
|
||||||
|
}
|
||||||
|
|
||||||
|
Marky.createSimpleToolbar('.editor-tools', '#reply-content, #id_content', {cannedResponses: !$body.is('.new-question')});
|
||||||
|
|
||||||
|
// product selector page reloading
|
||||||
|
$('#product-selector select').on('change', function() {
|
||||||
|
var val = $(this).val();
|
||||||
|
var queryParams = getQueryParamsAsDict(document.location.toString());
|
||||||
|
|
||||||
|
if (val === '') {
|
||||||
|
delete queryParams.product;
|
||||||
} else {
|
} else {
|
||||||
hideDetails($questionForm, aaq);
|
queryParams.product = val;
|
||||||
}
|
}
|
||||||
|
document.location = document.location.pathname + '?' + $.param(queryParams);
|
||||||
|
});
|
||||||
|
|
||||||
|
// sort questions page reloading
|
||||||
|
$('[data-sort-questions]').on('change', function() {
|
||||||
|
document.location = $(this).val()
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the new/edit question page/form
|
||||||
|
*/
|
||||||
|
function initQuestion(action) {
|
||||||
|
var $questionForm = $('#question-form');
|
||||||
|
var aaq = new AAQSystemInfo($questionForm);
|
||||||
|
if (action === "editing") {
|
||||||
|
$("#troubleshooting-field").show();
|
||||||
|
} else {
|
||||||
|
hideDetails($questionForm, aaq);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function isLoggedIn() {
|
function isLoggedIn() {
|
||||||
return $('#greeting span.user').length > 0;
|
return $('#greeting span.user').length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle changes to the details for a question
|
// Handle changes to the details for a question
|
||||||
function initEditDetails() {
|
function initEditDetails() {
|
||||||
$('#details-product').on('change', function() {
|
$('#details-product').on('change', function() {
|
||||||
var $selected;
|
var $selected;
|
||||||
|
|
||||||
$(this).children().each(function() {
|
$(this).children().each(function() {
|
||||||
if (this.selected) {
|
if (this.selected) {
|
||||||
$selected = $(this);
|
$selected = $(this);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#details-topic').children().remove();
|
||||||
|
$('#details-submit').prop('disabled', true);
|
||||||
|
|
||||||
|
$.ajax($selected.data('url'), {
|
||||||
|
'dataType': 'json',
|
||||||
|
'success': function(data) {
|
||||||
|
for (var i = 0; i < data.topics.length; i++) {
|
||||||
|
var topic = data.topics[i];
|
||||||
|
var $opt = $('<option />');
|
||||||
|
|
||||||
|
$opt.attr('value', topic.id);
|
||||||
|
$opt.text(topic.title);
|
||||||
|
|
||||||
|
$('#details-topic').append($opt);
|
||||||
}
|
}
|
||||||
});
|
$('#details-submit').prop('disabled', false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$('#details-topic').children().remove();
|
// Hide the browser/system details for users on FF with js enabled
|
||||||
$('#details-submit').prop('disabled', true);
|
// and are submitting a question for FF on desktop.
|
||||||
|
function hideDetails($form, aaq) {
|
||||||
|
$form.find('ul').addClass('hide-details');
|
||||||
|
$form.find('a.show, a.hide').click(function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
$(this).closest('li')
|
||||||
|
.toggleClass('show')
|
||||||
|
.toggleClass('hide')
|
||||||
|
.closest('ul')
|
||||||
|
.toggleClass('show-details');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
$.ajax($selected.data('url'), {
|
/*
|
||||||
'dataType': 'json',
|
* Ajaxify any "I have this problem too" forms (may be multiple per page)
|
||||||
'success': function(data) {
|
*/
|
||||||
for (var i = 0; i < data.topics.length; i++) {
|
function initHaveThisProblemTooAjax() {
|
||||||
var topic = data.topics[i];
|
var $container = $('#question div.me-too, .question-tools div.me-too');
|
||||||
var $opt = $('<option />');
|
|
||||||
|
|
||||||
$opt.attr('value', topic.id);
|
// ajaxify each form individually so the resulting kbox attaches to
|
||||||
$opt.text(topic.title);
|
// the correct DOM element
|
||||||
|
$container.each(function() {
|
||||||
|
initAjaxForm($(this), 'form', '#vote-thanks');
|
||||||
|
});
|
||||||
|
|
||||||
$('#details-topic').append($opt);
|
$container.find('input').click(function() {
|
||||||
|
$(this).attr('disabled', 'disabled');
|
||||||
|
});
|
||||||
|
|
||||||
|
// closing or cancelling the kbox on any of the forms should remove
|
||||||
|
// all of them
|
||||||
|
$container.delegate('.kbox-close, .kbox-cancel', 'click', function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
$container.unbind().remove();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function addReferrerAndQueryToVoteForm() {
|
||||||
|
// Add the source/referrer and query terms to the helpful vote form
|
||||||
|
var urlParams = getQueryParamsAsDict(),
|
||||||
|
referrer = getReferrer(urlParams),
|
||||||
|
query = getSearchQuery(urlParams, referrer);
|
||||||
|
$('form.helpful, .me-too form')
|
||||||
|
.append($('<input type="hidden" name="referrer"/>')
|
||||||
|
.attr('value', referrer))
|
||||||
|
.append($('<input type="hidden" name="query"/>')
|
||||||
|
.attr('value', query));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ajaxify the Helpful/Not Helpful form
|
||||||
|
*/
|
||||||
|
function initHelpfulVote() {
|
||||||
|
$('.sumo-l-two-col--sidebar, #document-list, .answer-tools').each(function() {
|
||||||
|
new AjaxVote($(this).find('form.helpful'), { // eslint-disable-line
|
||||||
|
replaceFormWithMessage: true,
|
||||||
|
removeForm: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper
|
||||||
|
function initAjaxForm($container, formSelector, boxSelector, onKboxClose) {
|
||||||
|
$container.delegate(formSelector, 'submit', function(ev) {
|
||||||
|
ev.preventDefault();
|
||||||
|
var $form = $(this);
|
||||||
|
var url = $form.attr('action');
|
||||||
|
var data = $form.serialize();
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: url,
|
||||||
|
type: 'POST',
|
||||||
|
data: data,
|
||||||
|
dataType: 'json',
|
||||||
|
success: function(response) {
|
||||||
|
if (response.html) {
|
||||||
|
if ($(boxSelector).length === 0) {
|
||||||
|
// We don't have a modal set up yet.
|
||||||
|
var kbox = new KBox(response.html, {
|
||||||
|
container: $container,
|
||||||
|
preClose: onKboxClose
|
||||||
|
});
|
||||||
|
kbox.open();
|
||||||
|
} else {
|
||||||
|
$(boxSelector).html($(response.html).children());
|
||||||
}
|
}
|
||||||
$('#details-submit').prop('disabled', false);
|
} else if (response.message) {
|
||||||
|
var html = '<div class="msg"></div>';
|
||||||
|
$(boxSelector)
|
||||||
|
.html(html)
|
||||||
|
.find('.msg').text(response.message);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hide the browser/system details for users on FF with js enabled
|
if (!response.ignored) {
|
||||||
// and are submitting a question for FF on desktop.
|
// Trigger a document event for others to listen for.
|
||||||
function hideDetails($form, aaq) {
|
$(document).trigger('vote', $.extend(data, {url: url}));
|
||||||
$form.find('ul').addClass('hide-details');
|
|
||||||
$form.find('a.show, a.hide').click(function(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
$(this).closest('li')
|
|
||||||
.toggleClass('show')
|
|
||||||
.toggleClass('hide')
|
|
||||||
.closest('ul')
|
|
||||||
.toggleClass('show-details');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ajaxify any "I have this problem too" forms (may be multiple per page)
|
|
||||||
*/
|
|
||||||
function initHaveThisProblemTooAjax() {
|
|
||||||
var $container = $('#question div.me-too, .question-tools div.me-too');
|
|
||||||
|
|
||||||
// ajaxify each form individually so the resulting kbox attaches to
|
|
||||||
// the correct DOM element
|
|
||||||
$container.each(function() {
|
|
||||||
initAjaxForm($(this), 'form', '#vote-thanks');
|
|
||||||
});
|
|
||||||
|
|
||||||
$container.find('input').click(function() {
|
|
||||||
$(this).attr('disabled', 'disabled');
|
|
||||||
});
|
|
||||||
|
|
||||||
// closing or cancelling the kbox on any of the forms should remove
|
|
||||||
// all of them
|
|
||||||
$container.delegate('.kbox-close, .kbox-cancel', 'click', function(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
$container.unbind().remove();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function addReferrerAndQueryToVoteForm() {
|
|
||||||
// Add the source/referrer and query terms to the helpful vote form
|
|
||||||
var urlParams = getQueryParamsAsDict(),
|
|
||||||
referrer = getReferrer(urlParams),
|
|
||||||
query = getSearchQuery(urlParams, referrer);
|
|
||||||
$('form.helpful, .me-too form')
|
|
||||||
.append($('<input type="hidden" name="referrer"/>')
|
|
||||||
.attr('value', referrer))
|
|
||||||
.append($('<input type="hidden" name="query"/>')
|
|
||||||
.attr('value', query));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ajaxify the Helpful/Not Helpful form
|
|
||||||
*/
|
|
||||||
function initHelpfulVote() {
|
|
||||||
$('.sumo-l-two-col--sidebar, #document-list, .answer-tools').each(function() {
|
|
||||||
new AjaxVote($(this).find('form.helpful'), { // eslint-disable-line
|
|
||||||
replaceFormWithMessage: true,
|
|
||||||
removeForm: true
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper
|
|
||||||
function initAjaxForm($container, formSelector, boxSelector, onKboxClose) {
|
|
||||||
$container.delegate(formSelector, 'submit', function(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
var $form = $(this);
|
|
||||||
var url = $form.attr('action');
|
|
||||||
var data = $form.serialize();
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
url: url,
|
|
||||||
type: 'POST',
|
|
||||||
data: data,
|
|
||||||
dataType: 'json',
|
|
||||||
success: function(response) {
|
|
||||||
if (response.html) {
|
|
||||||
if ($(boxSelector).length === 0) {
|
|
||||||
// We don't have a modal set up yet.
|
|
||||||
var kbox = new KBox(response.html, {
|
|
||||||
container: $container,
|
|
||||||
preClose: onKboxClose
|
|
||||||
});
|
|
||||||
kbox.open();
|
|
||||||
} else {
|
|
||||||
$(boxSelector).html($(response.html).children());
|
|
||||||
}
|
|
||||||
} else if (response.message) {
|
|
||||||
var html = '<div class="msg"></div>';
|
|
||||||
$(boxSelector)
|
|
||||||
.html(html)
|
|
||||||
.find('.msg').text(response.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!response.ignored) {
|
|
||||||
// Trigger a document event for others to listen for.
|
|
||||||
$(document).trigger('vote', $.extend(data, {url: url}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function() {
|
|
||||||
var message = gettext('There was an error.');
|
|
||||||
alert(message);
|
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
error: function() {
|
||||||
return false;
|
var message = gettext('There was an error.');
|
||||||
|
alert(message);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initTagFilterToggle() {
|
||||||
|
$('#toggle-tag-filter').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$('#tag-filter').slideToggle('fast'); // CSS3: Y U NO TRANSITION TO `height: auto;`?
|
||||||
|
$(this).toggleClass('off');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Links all crash IDs found in the passed HTML container elements
|
||||||
|
*/
|
||||||
|
export function linkCrashIds(container) {
|
||||||
|
if (!container) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
var crashIDRegex = new RegExp('(bp-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})', 'g');
|
||||||
|
var crashStatsBase = 'https://crash-stats.mozilla.com/report/index/';
|
||||||
|
var helpingWithCrashesArticle = '/kb/helping-crashes';
|
||||||
|
var crashReportContainer =
|
||||||
|
"<span class='crash-report'>" +
|
||||||
|
"<a href='" + crashStatsBase + "$1' target='_blank'>$1</a>" +
|
||||||
|
"<a href='" + helpingWithCrashesArticle + "' target='_blank'>" +
|
||||||
|
"<img src='" + questionmarkIcon + "'></img></a></span>";
|
||||||
|
|
||||||
function initTagFilterToggle() {
|
container.html(container.html().replace(crashIDRegex, crashReportContainer));
|
||||||
$('#toggle-tag-filter').click(function(e) {
|
}
|
||||||
e.preventDefault();
|
|
||||||
$('#tag-filter').slideToggle('fast'); // CSS3: Y U NO TRANSITION TO `height: auto;`?
|
|
||||||
$(this).toggleClass('off');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Links all crash IDs found in the passed HTML container elements
|
* Initialize the automatic linking of crash IDs
|
||||||
*/
|
*/
|
||||||
function linkCrashIds(container) {
|
function initCrashIdLinking() {
|
||||||
if (!container) {
|
var postContents = $('.question .main-content, .answer .main-content, #more-system-details');
|
||||||
return;
|
postContents.each(function() {
|
||||||
}
|
linkCrashIds($(this));
|
||||||
var crashIDRegex = new RegExp('(bp-[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})', 'g');
|
});
|
||||||
var crashStatsBase = 'https://crash-stats.mozilla.com/report/index/';
|
}
|
||||||
var helpingWithCrashesArticle = '/kb/helping-crashes';
|
|
||||||
var crashReportContainer =
|
|
||||||
"<span class='crash-report'>" +
|
|
||||||
"<a href='" + crashStatsBase + "$1' target='_blank'>$1</a>" +
|
|
||||||
"<a href='" + helpingWithCrashesArticle + "' target='_blank'>" +
|
|
||||||
"<img src='" + questionmarkIcon + "'></img></a></span>";
|
|
||||||
|
|
||||||
container.html(container.html().replace(crashIDRegex, crashReportContainer));
|
function initReplyToAnswer() {
|
||||||
}
|
$('a.quoted-reply').click(function() {
|
||||||
|
var contentId = $(this).data('content-id'),
|
||||||
|
$content = $('#' + contentId),
|
||||||
|
text = $content.find('.content-raw').text(),
|
||||||
|
user = $content.find('.display-name').text(),
|
||||||
|
reply_text = `''<p>${user} [[#${contentId}|${gettext('said')}]]</p>''\n<blockquote>${text}\n</blockquote>\n\n`,
|
||||||
|
$textarea = $('#id_content'),
|
||||||
|
oldtext = $textarea.val();
|
||||||
|
|
||||||
/*
|
$textarea.val(oldtext + reply_text);
|
||||||
* Initialize the automatic linking of crash IDs
|
|
||||||
*/
|
|
||||||
function initCrashIdLinking() {
|
|
||||||
var postContents = $('.question .main-content, .answer .main-content, #more-system-details');
|
|
||||||
postContents.each(function() {
|
|
||||||
linkCrashIds($(this));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function initReplyToAnswer() {
|
setTimeout(function() {
|
||||||
$('a.quoted-reply').click(function() {
|
$textarea.focus();
|
||||||
var contentId = $(this).data('content-id'),
|
}, 10);
|
||||||
$content = $('#' + contentId),
|
|
||||||
text = $content.find('.content-raw').text(),
|
|
||||||
user = $content.find('.display-name').text(),
|
|
||||||
reply_text = `''<p>${user} [[#${contentId}|${gettext('said')}]]</p>''\n<blockquote>${text}\n</blockquote>\n\n`,
|
|
||||||
$textarea = $('#id_content'),
|
|
||||||
oldtext = $textarea.val();
|
|
||||||
|
|
||||||
$textarea.val(oldtext + reply_text);
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
setTimeout(function() {
|
$(document).ready(init);
|
||||||
$textarea.focus();
|
|
||||||
}, 10);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(init);
|
|
||||||
|
|
||||||
})(jQuery);
|
|
||||||
|
|
|
@ -345,9 +345,9 @@ ShowFor.prototype.initShowFuncs = function() {
|
||||||
ShowFor.prototype.showAndHide = function() {
|
ShowFor.prototype.showAndHide = function() {
|
||||||
this.$container.find('.for').each(function(i, elem) {
|
this.$container.find('.for').each(function(i, elem) {
|
||||||
var $elem = $(elem);
|
var $elem = $(elem);
|
||||||
var showFunc = $elem.data('show-func');
|
var showFuncVal = $elem.data('show-func')();
|
||||||
if (showFunc) {
|
if (showFuncVal !== undefined) {
|
||||||
$elem.toggle(showFunc());
|
$elem.toggle(showFuncVal);
|
||||||
} else {
|
} else {
|
||||||
$elem.show();
|
$elem.show();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,93 +6,90 @@ import _keys from "underscore/modules/keys";
|
||||||
* A tag filtering form.
|
* A tag filtering form.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function($) {
|
function init($container) {
|
||||||
|
var $form = $container ? $container.find('form') : $('#tag-filter form'),
|
||||||
|
$tags = $form.find('input[type="text"]'), $btn = $form.find('input[type="submit"], button'),
|
||||||
|
$hidden = $('<input type="hidden"/>'),
|
||||||
|
vocab = $tags.data('vocabulary'),
|
||||||
|
lowerVocab = {};
|
||||||
|
|
||||||
function init($container) {
|
if (!$form.length) {
|
||||||
var $form = $container ? $container.find('form') : $('#tag-filter form'),
|
return;
|
||||||
$tags = $form.find('input[type="text"]'), $btn = $form.find('input[type="submit"], button'),
|
}
|
||||||
$hidden = $('<input type="hidden"/>'),
|
|
||||||
vocab = $tags.data('vocabulary'),
|
|
||||||
lowerVocab = {};
|
|
||||||
|
|
||||||
if (!$form.length) {
|
// Create a lower case vocab for case insensitive match.
|
||||||
return;
|
_each(_keys(vocab), function(name) {
|
||||||
|
lowerVocab[name.toLowerCase()] = vocab[name];
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add a hidden field for comma-separated slugs.
|
||||||
|
$hidden.attr('name', $tags.attr('name'))
|
||||||
|
.appendTo($form);
|
||||||
|
$tags.removeAttr('name');
|
||||||
|
|
||||||
|
// Disable button while text input is empty.
|
||||||
|
$btn.attr('disabled', 'disabled');
|
||||||
|
$tags.keyup(function() {
|
||||||
|
if ($tags.val()) {
|
||||||
|
$btn.removeAttr('disabled');
|
||||||
|
} else {
|
||||||
|
$btn.attr('disabled', 'disabled');
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Create a lower case vocab for case insensitive match.
|
// Set up autocomplete
|
||||||
_each(_keys(vocab), function(name) {
|
// Skip if the autocomplete plugin isn't available (unit tests).
|
||||||
lowerVocab[name.toLowerCase()] = vocab[name];
|
if ($tags.autocomplete) {
|
||||||
});
|
$tags.autocomplete({
|
||||||
|
source: _keys(vocab),
|
||||||
// Add a hidden field for comma-separated slugs.
|
delay: 0,
|
||||||
$hidden.attr('name', $tags.attr('name'))
|
minLength: 1
|
||||||
.appendTo($form);
|
|
||||||
$tags.removeAttr('name');
|
|
||||||
|
|
||||||
// Disable button while text input is empty.
|
|
||||||
$btn.attr('disabled', 'disabled');
|
|
||||||
$tags.keyup(function() {
|
|
||||||
if ($tags.val()) {
|
|
||||||
$btn.removeAttr('disabled');
|
|
||||||
} else {
|
|
||||||
$btn.attr('disabled', 'disabled');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Set up autocomplete
|
|
||||||
// Skip if the autocomplete plugin isn't available (unit tests).
|
|
||||||
if ($tags.autocomplete) {
|
|
||||||
$tags.autocomplete({
|
|
||||||
source: _keys(vocab),
|
|
||||||
delay: 0,
|
|
||||||
minLength: 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// When form is submitted, get the slugs to send over in request.
|
|
||||||
$form.submit(function() {
|
|
||||||
var tagNames = $tags.val(),
|
|
||||||
slugNames = [],
|
|
||||||
currentSlugs = $form.find('input.current-tagged').val(),
|
|
||||||
slugs,
|
|
||||||
invalid = false;
|
|
||||||
|
|
||||||
// For each tag name, find the slug.
|
|
||||||
_each(tagNames.split(','), function(tag) {
|
|
||||||
var trimmed = $.trim(tag),
|
|
||||||
slug = lowerVocab[trimmed.toLowerCase()];
|
|
||||||
if (slug) {
|
|
||||||
slugNames.push(slug);
|
|
||||||
} else if (trimmed) {
|
|
||||||
invalid = true;
|
|
||||||
alert(interpolate(gettext('Invalid tag entered: %s'), [tag]));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Invalid or no tags? No requests!
|
|
||||||
if (invalid || slugNames.length === 0) {
|
|
||||||
$form.trigger('ajaxComplete');
|
|
||||||
if (!invalid) {
|
|
||||||
alert(gettext('No tags entered.'));
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
slugs = slugNames.join(',');
|
|
||||||
|
|
||||||
// Prepend any existing filters applied.
|
|
||||||
if (currentSlugs) {
|
|
||||||
slugs = currentSlugs + ',' + slugs;
|
|
||||||
}
|
|
||||||
$hidden.val(slugs);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const TagsFilter = {
|
// When form is submitted, get the slugs to send over in request.
|
||||||
init: init
|
$form.submit(function() {
|
||||||
};
|
var tagNames = $tags.val(),
|
||||||
|
slugNames = [],
|
||||||
|
currentSlugs = $form.find('input.current-tagged').val(),
|
||||||
|
slugs,
|
||||||
|
invalid = false;
|
||||||
|
|
||||||
$(document).ready(function() {
|
// For each tag name, find the slug.
|
||||||
TagsFilter.init();
|
_each(tagNames.split(','), function(tag) {
|
||||||
|
var trimmed = $.trim(tag),
|
||||||
|
slug = lowerVocab[trimmed.toLowerCase()];
|
||||||
|
if (slug) {
|
||||||
|
slugNames.push(slug);
|
||||||
|
} else if (trimmed) {
|
||||||
|
invalid = true;
|
||||||
|
alert(interpolate(gettext('Invalid tag entered: %s'), [tag]));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Invalid or no tags? No requests!
|
||||||
|
if (invalid || slugNames.length === 0) {
|
||||||
|
$form.trigger('ajaxComplete');
|
||||||
|
if (!invalid) {
|
||||||
|
alert(gettext('No tags entered.'));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
slugs = slugNames.join(',');
|
||||||
|
|
||||||
|
// Prepend any existing filters applied.
|
||||||
|
if (currentSlugs) {
|
||||||
|
slugs = currentSlugs + ',' + slugs;
|
||||||
|
}
|
||||||
|
$hidden.val(slugs);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
})(jQuery);
|
const TagsFilter = {
|
||||||
|
init: init
|
||||||
|
};
|
||||||
|
export default TagsFilter;
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
TagsFilter.init();
|
||||||
|
});
|
||||||
|
|
|
@ -1,28 +1,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
import mochaGettext from './fixtures/mochaGettext.js';
|
|
||||||
import mochaK from './fixtures/mochaK.js';
|
|
||||||
import mochaJquery from './fixtures/mochaJquery.js';
|
|
||||||
|
|
||||||
import AjaxPreview from "sumo/js/ajaxpreview";
|
import AjaxPreview from "sumo/js/ajaxpreview";
|
||||||
|
|
||||||
describe('ajax preview', () => {
|
describe('ajax preview', () => {
|
||||||
mochaJsdom({useEach: true, url: 'http://localhost'});
|
|
||||||
mochaJquery();
|
|
||||||
mochaK();
|
|
||||||
mochaGettext();
|
|
||||||
/* globals window, $, k */
|
|
||||||
|
|
||||||
var fakeServer;
|
|
||||||
|
|
||||||
describe('events', () => {
|
describe('events', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
rerequire('../ajaxpreview.js');
|
|
||||||
rerequire('../libs/jquery.lazyload.js');
|
|
||||||
|
|
||||||
sinon.stub($, 'ajax').yieldsTo('success', '<p>The content to preview.</p>');
|
sinon.stub($, 'ajax').yieldsTo('success', '<p>The content to preview.</p>');
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
import mochaK from './fixtures/mochaK.js';
|
|
||||||
import mochaJquery from './fixtures/mochaJquery.js';
|
|
||||||
|
|
||||||
import AjaxVote from "sumo/js/ajaxvote";
|
import AjaxVote from "sumo/js/ajaxvote";
|
||||||
|
|
||||||
describe('ajaxvote', () => {
|
describe('ajaxvote', () => {
|
||||||
mochaJsdom({useEach: true, url: 'http://localhost'});
|
|
||||||
mochaJquery();
|
|
||||||
mochaK();
|
|
||||||
/* globals window, document, $, k */
|
|
||||||
|
|
||||||
describe('helpful vote', () => {
|
describe('helpful vote', () => {
|
||||||
let fakeServer;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
rerequire('../ajaxvote.js');
|
|
||||||
|
|
||||||
sinon.stub($, 'ajax').yieldsTo('success', {message: 'Thanks for the vote!'});
|
sinon.stub($, 'ajax').yieldsTo('success', {message: 'Thanks for the vote!'});
|
||||||
|
|
||||||
let sandbox = (
|
let sandbox = (
|
||||||
|
@ -34,6 +21,7 @@ describe('ajaxvote', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
$.ajax.restore();
|
$.ajax.restore();
|
||||||
React.unmountComponentAtNode(document.body);
|
React.unmountComponentAtNode(document.body);
|
||||||
|
$(document).off('vote');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fire an event on a helpful vote', done => {
|
it('should fire an event on a helpful vote', done => {
|
||||||
|
|
|
@ -1,21 +1,8 @@
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
|
|
||||||
import mochaUnderscore from './fixtures/mochaUnderscore.js';
|
import BrowserDetect from "sumo/js/browserdetect";
|
||||||
|
|
||||||
describe('BrowserDetect', () => {
|
describe('BrowserDetect', () => {
|
||||||
mochaJsdom({useEach: true, url: 'http://localhost'});
|
|
||||||
mochaUnderscore();
|
|
||||||
/* globals window */
|
|
||||||
|
|
||||||
let BrowserDetect;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rerequire('../browserdetect.js');
|
|
||||||
|
|
||||||
BrowserDetect = window.BrowserDetect;
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('Fennec versions', () => {
|
describe('Fennec versions', () => {
|
||||||
it('should detect Fennec 7', () => {
|
it('should detect Fennec 7', () => {
|
||||||
let ua = 'Mozilla/5.0 (Android; Linux armv7l; rv:7.0.1) Gecko/ Firefox/7.0.1 Fennec/7.0.1';
|
let ua = 'Mozilla/5.0 (Android; Linux armv7l; rv:7.0.1) Gecko/ Firefox/7.0.1 Fennec/7.0.1';
|
||||||
|
|
|
@ -1,51 +1,37 @@
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
import {default as chai, expect} from 'chai';
|
import {default as chai, expect} from 'chai';
|
||||||
import chaiLint from 'chai-lint';
|
import chaiLint from 'chai-lint';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import sinonChai from 'sinon-chai';
|
import sinonChai from 'sinon-chai';
|
||||||
|
|
||||||
import mochaK from './fixtures/mochaK.js';
|
import {
|
||||||
import mochaJquery from './fixtures/mochaJquery.js';
|
getQueryParamsAsDict,
|
||||||
import mochaUnderscore from './fixtures/mochaUnderscore.js';
|
getReferrer,
|
||||||
|
getSearchQuery,
|
||||||
|
unquote,
|
||||||
|
safeString,
|
||||||
|
safeInterpolate,
|
||||||
|
} from "sumo/js/main";
|
||||||
|
|
||||||
chai.use(chaiLint);
|
chai.use(chaiLint);
|
||||||
chai.use(sinonChai);
|
chai.use(sinonChai);
|
||||||
|
|
||||||
describe('k', () => {
|
describe('k', () => {
|
||||||
mochaJsdom({
|
|
||||||
useEach: true,
|
|
||||||
url: 'http://localhost',
|
|
||||||
document: {
|
|
||||||
referrer: 'http://google.com/?q=cookies',
|
|
||||||
referer: 'http://google.com/?q=cookies',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
mochaJquery();
|
|
||||||
mochaK();
|
|
||||||
mochaUnderscore();
|
|
||||||
/* globals document:false, $:false, k:false */
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rerequire('../libs/jquery.placeholder.js');
|
|
||||||
rerequire('../main.js');
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getQueryParamsAsDict', () => {
|
describe('getQueryParamsAsDict', () => {
|
||||||
it('should return an empty object for no params', () => {
|
it('should return an empty object for no params', () => {
|
||||||
let url = 'http://example.com';
|
let url = 'http://example.com';
|
||||||
let params = k.getQueryParamsAsDict(url);
|
let params = getQueryParamsAsDict(url);
|
||||||
expect(params).to.deep.equal({});
|
expect(params).to.deep.equal({});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse a query string with one parameter', () => {
|
it('should parse a query string with one parameter', () => {
|
||||||
let url = 'http://example.com/?test=woot';
|
let url = 'http://example.com/?test=woot';
|
||||||
let params = k.getQueryParamsAsDict(url);
|
let params = getQueryParamsAsDict(url);
|
||||||
expect(params).to.deep.equal({test: 'woot'});
|
expect(params).to.deep.equal({test: 'woot'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse a query string with two paramaters', () => {
|
it('should parse a query string with two paramaters', () => {
|
||||||
let url = 'http://example.com/?x=foo&y=bar';
|
let url = 'http://example.com/?x=foo&y=bar';
|
||||||
let params = k.getQueryParamsAsDict(url);
|
let params = getQueryParamsAsDict(url);
|
||||||
expect(params).to.deep.equal({x: 'foo', y: 'bar'});
|
expect(params).to.deep.equal({x: 'foo', y: 'bar'});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -54,7 +40,7 @@ describe('k', () => {
|
||||||
'ved=0CDEQFjAA&url=http%3A%2F%2Fsupport.mozilla.com%2F&' +
|
'ved=0CDEQFjAA&url=http%3A%2F%2Fsupport.mozilla.com%2F&' +
|
||||||
'rct=j&q=firefox%20help&ei=OsBSTpbZBIGtgQfgzv3yBg&' +
|
'rct=j&q=firefox%20help&ei=OsBSTpbZBIGtgQfgzv3yBg&' +
|
||||||
'usg=AFQjCNFIV7wgd9Pnr0m3Ofc7r1zVTNK8dw');
|
'usg=AFQjCNFIV7wgd9Pnr0m3Ofc7r1zVTNK8dw');
|
||||||
let params = k.getQueryParamsAsDict(url);
|
let params = getQueryParamsAsDict(url);
|
||||||
expect(params).to.deep.equal({
|
expect(params).to.deep.equal({
|
||||||
sa: 't',
|
sa: 't',
|
||||||
source: 'web',
|
source: 'web',
|
||||||
|
@ -70,50 +56,23 @@ describe('k', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('queryParamStringFromDict', () => {
|
|
||||||
it('should serialize an empty dict into a ?', () => {
|
|
||||||
let actual = k.queryParamStringFromDict({});
|
|
||||||
expect(actual).to.equal('?');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('it should serialize an object with a single key', () => {
|
|
||||||
let actual = k.queryParamStringFromDict({foo: 1});
|
|
||||||
expect(actual).to.equal('?foo=1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should serialize an object with two keys', () => {
|
|
||||||
let actual = k.queryParamStringFromDict({foo: 1, bar: 2});
|
|
||||||
expect(actual).to.equal('?foo=1&bar=2');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not include null or undefined in the output', () => {
|
|
||||||
let actual = k.queryParamStringFromDict({foo: undefined, bar: 2, baz: null});
|
|
||||||
expect(actual).to.equal('?bar=2');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should serialize an object with three keys', () => {
|
|
||||||
let actual = k.queryParamStringFromDict({foo: 1, bar: 2, baz: 3});
|
|
||||||
expect(actual).to.deep.equal('?foo=1&bar=2&baz=3');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('getReferrer', () => {
|
describe('getReferrer', () => {
|
||||||
it('should recognize search referrers', () => {
|
it('should recognize search referrers', () => {
|
||||||
let params = {as: 's', s: 'cookies'};
|
let params = {as: 's', s: 'cookies'};
|
||||||
let actual = k.getReferrer(params);
|
let actual = getReferrer(params);
|
||||||
expect(actual).to.equal('search');
|
expect(actual).to.equal('search');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should recognize inproduct referrers', () => {
|
it('should recognize inproduct referrers', () => {
|
||||||
let params = {as: 'u'};
|
let params = {as: 'u'};
|
||||||
let actual = k.getReferrer(params);
|
let actual = getReferrer(params);
|
||||||
expect(actual).to.equal('inproduct');
|
expect(actual).to.equal('inproduct');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fall back to `document.referrer`', () => {
|
it('should fall back to `document.referrer`', () => {
|
||||||
let referrer = 'http://google.com/?q=cookies';
|
let referrer = 'http://google.com/?q=cookies';
|
||||||
expect(document.referrer).to.equal(referrer);
|
expect(document.referrer).to.equal(referrer);
|
||||||
expect(k.getReferrer({})).to.equal(referrer);
|
expect(getReferrer({})).to.equal(referrer);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -121,54 +80,54 @@ describe('k', () => {
|
||||||
it('should return the s query string for local search referrers', () => {
|
it('should return the s query string for local search referrers', () => {
|
||||||
let params = {as: 's', s: 'cookies'};
|
let params = {as: 's', s: 'cookies'};
|
||||||
let referrer = 'search';
|
let referrer = 'search';
|
||||||
expect(k.getSearchQuery(params, referrer)).to.equal('cookies');
|
expect(getSearchQuery(params, referrer)).to.equal('cookies');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return an empty string fro inproduct referrers', () => {
|
it('should return an empty string fro inproduct referrers', () => {
|
||||||
let params = {as: 'u', s: 'wrong'};
|
let params = {as: 'u', s: 'wrong'};
|
||||||
let referrer = 'inproduct';
|
let referrer = 'inproduct';
|
||||||
expect(k.getSearchQuery(params, referrer)).to.equal('');
|
expect(getSearchQuery(params, referrer)).to.equal('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should detect external search parameters from google', () => {
|
it('should detect external search parameters from google', () => {
|
||||||
let referrer = 'http://google.com/?q=cookies';
|
let referrer = 'http://google.com/?q=cookies';
|
||||||
expect(k.getSearchQuery({}, referrer)).to.equal('cookies');
|
expect(getSearchQuery({}, referrer)).to.equal('cookies');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('unquote', () => {
|
describe('unquote', () => {
|
||||||
it('should return undefined for undefined input', () => {
|
it('should return undefined for undefined input', () => {
|
||||||
expect(k.unquote(undefined)).to.beUndefined();
|
expect(unquote(undefined)).to.beUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should unquote simply quoted strings', () => {
|
it('should unquote simply quoted strings', () => {
|
||||||
expect(k.unquote('"delete cookies"')).to.equal('delete cookies');
|
expect(unquote('"delete cookies"')).to.equal('delete cookies');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle escaped quotes', () => {
|
it('should handle escaped quotes', () => {
|
||||||
expect(k.unquote('"\\"delete\\" cookies"')).to.equal('"delete" cookies');
|
expect(unquote('"\\"delete\\" cookies"')).to.equal('"delete" cookies');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle escaped quotes with no other quotes', () => {
|
it('should handle escaped quotes with no other quotes', () => {
|
||||||
expect(k.unquote('\\"delete\\" cookies')).to.equal('"delete" cookies');
|
expect(unquote('\\"delete\\" cookies')).to.equal('"delete" cookies');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should pass strings without quotes through unmodified', () => {
|
it('should pass strings without quotes through unmodified', () => {
|
||||||
let s = 'cookies';
|
let s = 'cookies';
|
||||||
expect(k.unquote(s)).to.equal(s);
|
expect(unquote(s)).to.equal(s);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('safeString', () => {
|
describe('safeString', () => {
|
||||||
it('should escape html', function() {
|
it('should escape html', function() {
|
||||||
let unsafeString = '<a href="foo&\'">';
|
let unsafeString = '<a href="foo&\'">';
|
||||||
let safeString = '<a href="foo&'">';
|
let expectedString = '<a href="foo&'">';
|
||||||
expect(k.safeString(unsafeString)).to.equal(safeString);
|
expect(safeString(unsafeString)).to.equal(expectedString);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('safeInterpolate', () => {
|
describe('safeInterpolate', () => {
|
||||||
/* k.safeInterpolate works by delegating to `interpolate`, a Django
|
/* safeInterpolate works by delegating to `interpolate`, a Django
|
||||||
* gettext function. These tests mock out interpolate and make sure
|
* gettext function. These tests mock out interpolate and make sure
|
||||||
* it was called appropriately.
|
* it was called appropriately.
|
||||||
*/
|
*/
|
||||||
|
@ -183,7 +142,7 @@ describe('k', () => {
|
||||||
let unsafe = ['<a>', '<script>'];
|
let unsafe = ['<a>', '<script>'];
|
||||||
let safe = ['<a>', '<script>'];
|
let safe = ['<a>', '<script>'];
|
||||||
|
|
||||||
k.safeInterpolate(html, unsafe, false);
|
safeInterpolate(html, unsafe, false);
|
||||||
|
|
||||||
expect(interpolateSpy).to.have.callCount(1);
|
expect(interpolateSpy).to.have.callCount(1);
|
||||||
expect(interpolateSpy).to.have.been.calledWithExactly(html, safe, false);
|
expect(interpolateSpy).to.have.been.calledWithExactly(html, safe, false);
|
||||||
|
@ -199,7 +158,7 @@ describe('k', () => {
|
||||||
display: '<script>alert('xss');</script>',
|
display: '<script>alert('xss');</script>',
|
||||||
name: 'Jo&mdash;hn',
|
name: 'Jo&mdash;hn',
|
||||||
};
|
};
|
||||||
k.safeInterpolate(html, unsafe, true);
|
safeInterpolate(html, unsafe, true);
|
||||||
|
|
||||||
expect(interpolateSpy).to.have.callCount(1);
|
expect(interpolateSpy).to.have.callCount(1);
|
||||||
expect(interpolateSpy).to.have.been.calledWithExactly(html, safe, true);
|
expect(interpolateSpy).to.have.been.calledWithExactly(html, safe, true);
|
||||||
|
|
|
@ -1,26 +1,10 @@
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import mochaK from './fixtures/mochaK.js';
|
import { linkCrashIds } from "sumo/js/questions";
|
||||||
import mochaJquery from './fixtures/mochaJquery.js';
|
|
||||||
import mochaGettext from './fixtures/mochaGettext.js';
|
|
||||||
import mochaMarky from './fixtures/mochaMarky.js';
|
|
||||||
|
|
||||||
describe('k', () => {
|
describe('k', () => {
|
||||||
mochaJsdom({useEach: true, url: 'http://localhost'});
|
|
||||||
mochaJquery();
|
|
||||||
mochaK();
|
|
||||||
mochaGettext();
|
|
||||||
mochaMarky();
|
|
||||||
/* globals window, document, $, k */
|
|
||||||
|
|
||||||
describe('linkCrashIds', () => {
|
describe('linkCrashIds', () => {
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rerequire('../questions.js');
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
React.unmountComponentAtNode(document.body);
|
React.unmountComponentAtNode(document.body);
|
||||||
});
|
});
|
||||||
|
@ -39,7 +23,7 @@ describe('k', () => {
|
||||||
);
|
);
|
||||||
React.render(sandbox, document.body);
|
React.render(sandbox, document.body);
|
||||||
|
|
||||||
k.linkCrashIds($('body'));
|
linkCrashIds($('body'));
|
||||||
expect($('.crash-report').length).to.equal(1);
|
expect($('.crash-report').length).to.equal(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -61,7 +45,7 @@ describe('k', () => {
|
||||||
);
|
);
|
||||||
React.render(sandbox, document.body);
|
React.render(sandbox, document.body);
|
||||||
|
|
||||||
k.linkCrashIds($('body'));
|
linkCrashIds($('body'));
|
||||||
expect($('.crash-report').length).to.equal(5);
|
expect($('.crash-report').length).to.equal(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -73,7 +57,7 @@ describe('k', () => {
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
React.render(sandbox, document.body);
|
React.render(sandbox, document.body);
|
||||||
k.linkCrashIds($('body'));
|
linkCrashIds($('body'));
|
||||||
|
|
||||||
expect($('.crash-report').length).to.equal(0);
|
expect($('.crash-report').length).to.equal(0);
|
||||||
});
|
});
|
||||||
|
@ -87,7 +71,7 @@ describe('k', () => {
|
||||||
);
|
);
|
||||||
React.render(sandbox, document.body);
|
React.render(sandbox, document.body);
|
||||||
|
|
||||||
k.linkCrashIds($('body'));
|
linkCrashIds($('body'));
|
||||||
expect($('.crash-report').length).to.equal(0);
|
expect($('.crash-report').length).to.equal(0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import mochaFixtureHelper from './mochaFixtureHelper.js';
|
|
||||||
|
|
||||||
export default mochaFixtureHelper(({browser='firefox', version=25.0, OS='winxp'}={}) => {
|
|
||||||
let BrowserDetect = {browser, version, OS};
|
|
||||||
return {
|
|
||||||
BrowserDetect: BrowserDetect,
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,31 +0,0 @@
|
||||||
/**
|
|
||||||
* Install globals into the jsdom namespace.
|
|
||||||
* @param {function} mapFunc This function will be called to get the list of
|
|
||||||
* things to install into the namespace. Should return an object of keys
|
|
||||||
* to values to install.
|
|
||||||
*/
|
|
||||||
export default function(mapFunc) {
|
|
||||||
return function(options) {
|
|
||||||
let map;
|
|
||||||
|
|
||||||
global.beforeEach(() => {
|
|
||||||
map = mapFunc(options);
|
|
||||||
for (let key in map) {
|
|
||||||
let val = map[key];
|
|
||||||
global[key] = val;
|
|
||||||
if (global.window) {
|
|
||||||
global.window[key] = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
global.afterEach(() => {
|
|
||||||
for (let key in map) {
|
|
||||||
delete global[key];
|
|
||||||
if (global.window) {
|
|
||||||
delete global.window[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
import mochaFixtureHelper from './mochaFixtureHelper.js';
|
|
||||||
|
|
||||||
function fakeGettext(msgid) {
|
|
||||||
return msgid;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default mochaFixtureHelper(() => {
|
|
||||||
return {
|
|
||||||
gettext: fakeGettext,
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,8 +0,0 @@
|
||||||
import mochaFixtureHelper from './mochaFixtureHelper.js';
|
|
||||||
|
|
||||||
export default mochaFixtureHelper(() => {
|
|
||||||
return {
|
|
||||||
_gaq: [],
|
|
||||||
trackEvent: function() {}
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,10 +0,0 @@
|
||||||
import mochaFixtureHelper from './mochaFixtureHelper.js';
|
|
||||||
import jQuery from 'jquery';
|
|
||||||
|
|
||||||
export default mochaFixtureHelper(() => {
|
|
||||||
let jq = jQuery(global.window);
|
|
||||||
return {
|
|
||||||
$: jq,
|
|
||||||
jQuery: jq,
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,8 +0,0 @@
|
||||||
import mochaFixtureHelper from './mochaFixtureHelper.js';
|
|
||||||
|
|
||||||
export default mochaFixtureHelper(() => {
|
|
||||||
let k = global.k || (global.window ? global.window.k : null) || {};
|
|
||||||
return {
|
|
||||||
k: k,
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,9 +0,0 @@
|
||||||
import {rerequire} from 'mocha-jsdom';
|
|
||||||
import mochaFixtureHelper from './mochaFixtureHelper.js';
|
|
||||||
|
|
||||||
export default mochaFixtureHelper(() => {
|
|
||||||
rerequire('../../markup.js');
|
|
||||||
return {
|
|
||||||
Marky: global.window.Marky,
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,37 +0,0 @@
|
||||||
import path from 'path';
|
|
||||||
import {rerequire} from 'mocha-jsdom';
|
|
||||||
import {FileSystemLoader} from 'nunjucks';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load and set up nunjucks and the Sumo nunjucks environment for Mocha tests.
|
|
||||||
*/
|
|
||||||
export default function() {
|
|
||||||
global.beforeEach(() => {
|
|
||||||
let nunjucks = rerequire('nunjucks');
|
|
||||||
|
|
||||||
global.nunjucks = nunjucks;
|
|
||||||
if (global.window) {
|
|
||||||
global.window.nunjucks = nunjucks;
|
|
||||||
}
|
|
||||||
|
|
||||||
const originalConfigure = nunjucks.configure;
|
|
||||||
nunjucks.configure = opts => {
|
|
||||||
opts.watch = false;
|
|
||||||
return originalConfigure(opts);
|
|
||||||
};
|
|
||||||
|
|
||||||
rerequire('../../nunjucks.js');
|
|
||||||
global.window.k.nunjucksEnv.loaders = [
|
|
||||||
new FileSystemLoader('kitsune/sumo/static/sumo/tpl', true, true)
|
|
||||||
];
|
|
||||||
global.window.k.nunjucksEnv.initCache();
|
|
||||||
});
|
|
||||||
|
|
||||||
global.afterEach(() => {
|
|
||||||
delete global.nunjucks;
|
|
||||||
delete global.nunjucksEnv;
|
|
||||||
if (global.window) {
|
|
||||||
delete global.window.nunjucks;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
import mochaFixtureHelper from './mochaFixtureHelper.js';
|
|
||||||
import _ from 'underscore';
|
|
||||||
|
|
||||||
export default mochaFixtureHelper(() => {
|
|
||||||
return {
|
|
||||||
_: _,
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,48 +1,23 @@
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
import {default as chai, expect} from 'chai';
|
import {default as chai, expect} from 'chai';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import chaiLint from 'chai-lint';
|
import chaiLint from 'chai-lint';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
import mochaK from './fixtures/mochaK.js';
|
import "sumo/js/templates/search-results";
|
||||||
import mochaJquery from './fixtures/mochaJquery.js';
|
import "sumo/js/instant_search";
|
||||||
import mochaGoogleAnalytics from './fixtures/mochaGoogleAnalytics.js';
|
import CachedXHR from "sumo/js/cached_xhr";
|
||||||
import mochaNunjucks from './fixtures/mochaNunjucks.js';
|
|
||||||
import mochaGettext from './fixtures/mochaGettext.js';
|
|
||||||
|
|
||||||
chai.use(chaiLint);
|
chai.use(chaiLint);
|
||||||
|
|
||||||
describe('instant search', () => {
|
describe('instant search', () => {
|
||||||
mochaJsdom({useEach: true, url: 'http://localhost'});
|
|
||||||
mochaJquery();
|
|
||||||
mochaK();
|
|
||||||
mochaGoogleAnalytics();
|
|
||||||
mochaGettext();
|
|
||||||
mochaNunjucks();
|
|
||||||
/* globals window, document, $ */
|
|
||||||
|
|
||||||
describe('', () => {
|
describe('', () => {
|
||||||
let $sandbox;
|
|
||||||
let clock;
|
let clock;
|
||||||
|
let cxhrMock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
clock = sinon.useFakeTimers();
|
clock = sinon.useFakeTimers();
|
||||||
window.matchMedia = () => {
|
cxhrMock = sinon.fake();
|
||||||
return {
|
sinon.replace(CachedXHR.prototype, "request", cxhrMock);
|
||||||
matches: false,
|
|
||||||
addListener: () => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
global.matchMedia = window.matchMedia;
|
|
||||||
window.Mzp = {};
|
|
||||||
window._localStorage = { getItem: () => undefined };
|
|
||||||
|
|
||||||
rerequire('../i18n.js');
|
|
||||||
global.interpolate = global.window.interpolate;
|
|
||||||
rerequire('../search_utils.js');
|
|
||||||
rerequire('../instant_search.js');
|
|
||||||
|
|
||||||
let content = (
|
let content = (
|
||||||
<div>
|
<div>
|
||||||
<div id="main-content"/>
|
<div id="main-content"/>
|
||||||
|
@ -58,6 +33,7 @@ describe('instant search', () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
React.unmountComponentAtNode(document.body);
|
React.unmountComponentAtNode(document.body);
|
||||||
clock.restore();
|
clock.restore();
|
||||||
|
sinon.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows and hides the main content correctly', () => {
|
it('shows and hides the main content correctly', () => {
|
||||||
|
@ -65,27 +41,24 @@ describe('instant search', () => {
|
||||||
expect($('#main-content').css('display')).to.not.equal('none');
|
expect($('#main-content').css('display')).to.not.equal('none');
|
||||||
|
|
||||||
$searchInput.val('test');
|
$searchInput.val('test');
|
||||||
$searchInput.keyup();
|
$searchInput.trigger('keyup');
|
||||||
expect($('#main-content').css('display')).to.equal('none');
|
expect($('#main-content').css('display')).to.equal('none');
|
||||||
|
|
||||||
$searchInput.val('');
|
$searchInput.val('');
|
||||||
$searchInput.keyup();
|
$searchInput.trigger('keyup');
|
||||||
expect($('#main-content').css('display')).to.not.equal('none');
|
expect($('#main-content').css('display')).to.not.equal('none');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows the search query at the top of the page', () => {
|
it('shows the search query at the top of the page', () => {
|
||||||
const query = 'search query';
|
const query = 'search query';
|
||||||
const requestExpectation = cxhrMock.expects('request')
|
|
||||||
.once()
|
|
||||||
.withArgs(sinon.match.string, sinon.match(opts => opts.data.q === query));
|
|
||||||
|
|
||||||
const $searchInput = $('#search-q');
|
const $searchInput = $('#search-q');
|
||||||
$searchInput.val(query);
|
$searchInput.val(query);
|
||||||
$searchInput.keyup();
|
$searchInput.trigger('keyup');
|
||||||
|
|
||||||
clock.tick(200);
|
clock.tick(200);
|
||||||
// call the callback to actually render things
|
// call the callback to actually render things
|
||||||
requestExpectation.firstCall.args[1].success({
|
cxhrMock.firstCall.args[1].success({
|
||||||
num_results: 0,
|
num_results: 0,
|
||||||
q: query,
|
q: query,
|
||||||
});
|
});
|
||||||
|
@ -96,15 +69,14 @@ describe('instant search', () => {
|
||||||
|
|
||||||
it('escapes the search query at the top of the page', () => {
|
it('escapes the search query at the top of the page', () => {
|
||||||
const query = '<';
|
const query = '<';
|
||||||
const requestExpectation = cxhrMock.expects('request');
|
|
||||||
|
|
||||||
const $searchInput = $('#search-q');
|
const $searchInput = $('#search-q');
|
||||||
$searchInput.val(query);
|
$searchInput.val(query);
|
||||||
$searchInput.keyup();
|
$searchInput.trigger('keyup');
|
||||||
|
|
||||||
clock.tick(200);
|
clock.tick(200);
|
||||||
// call the callback to actually render things
|
// call the callback to actually render things
|
||||||
requestExpectation.firstCall.args[1].success({
|
cxhrMock.firstCall.args[1].success({
|
||||||
num_results: 0,
|
num_results: 0,
|
||||||
q: query,
|
q: query,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,31 +1,16 @@
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
import {default as chai, expect} from 'chai';
|
import {default as chai, expect} from 'chai';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import chaiLint from 'chai-lint';
|
import chaiLint from 'chai-lint';
|
||||||
|
|
||||||
import mochaK from './fixtures/mochaK.js';
|
|
||||||
import mochaJquery from './fixtures/mochaJquery.js';
|
|
||||||
import mochaGettext from './fixtures/mochaGettext.js';
|
|
||||||
import mochaMarky from './fixtures/mochaMarky.js';
|
|
||||||
|
|
||||||
import KBox from "sumo/js/kbox.js";
|
import KBox from "sumo/js/kbox.js";
|
||||||
|
|
||||||
chai.use(chaiLint);
|
chai.use(chaiLint);
|
||||||
|
|
||||||
describe('kbox', () => {
|
describe('kbox', () => {
|
||||||
mochaJsdom({useEach: true, url: 'http://localhost'});
|
|
||||||
mochaJquery();
|
|
||||||
mochaK();
|
|
||||||
mochaGettext();
|
|
||||||
mochaMarky();
|
|
||||||
/* globals window, document, $ */
|
|
||||||
|
|
||||||
describe('declarative', () => {
|
describe('declarative', () => {
|
||||||
let $kbox, kbox;
|
let $kbox, kbox;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
rerequire('../kbox.js');
|
|
||||||
|
|
||||||
let sandbox = (
|
let sandbox = (
|
||||||
<div id="sandbox">
|
<div id="sandbox">
|
||||||
<div className="kbox"
|
<div className="kbox"
|
||||||
|
|
|
@ -1,21 +1,10 @@
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
import {default as chai, expect} from 'chai';
|
import {default as chai, expect} from 'chai';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import chaiLint from 'chai-lint';
|
import chaiLint from 'chai-lint';
|
||||||
|
|
||||||
import mochaJquery from './fixtures/mochaJquery.js';
|
|
||||||
|
|
||||||
chai.use(chaiLint);
|
chai.use(chaiLint);
|
||||||
|
|
||||||
describe('lazyload', () => {
|
describe('lazyload', () => {
|
||||||
mochaJsdom({useEach: true, url: 'http://localhost'});
|
|
||||||
mochaJquery();
|
|
||||||
/* globals document, $ */
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
rerequire('../libs/jquery.lazyload.js');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should load original image', () => {
|
it('should load original image', () => {
|
||||||
let img = <img className="lazy" data-original-src="http://example.com/test.jpg"/>;
|
let img = <img className="lazy" data-original-src="http://example.com/test.jpg"/>;
|
||||||
React.render(img, document.body);
|
React.render(img, document.body);
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
import {default as chai, expect} from 'chai';
|
import {default as chai, expect} from 'chai';
|
||||||
import chaiLint from 'chai-lint';
|
import chaiLint from 'chai-lint';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
|
|
||||||
import mochaJquery from './fixtures/mochaJquery.js';
|
import BrowserDetect from 'sumo/js/browserdetect';
|
||||||
import mochaBrowserDetect from './fixtures/mochaBrowserDetect.js';
|
import ShowFor from "sumo/js/showfor";
|
||||||
|
|
||||||
chai.use(chaiLint);
|
chai.use(chaiLint);
|
||||||
|
|
||||||
|
@ -15,7 +14,7 @@ chai.use(chaiLint);
|
||||||
* be bound to the passed $sandbox.
|
* be bound to the passed $sandbox.
|
||||||
*/
|
*/
|
||||||
function showForNoInit($sandbox) {
|
function showForNoInit($sandbox) {
|
||||||
let sf = Object.create(window.ShowFor.prototype);
|
let sf = Object.create(ShowFor.prototype);
|
||||||
sf.$container = $sandbox;
|
sf.$container = $sandbox;
|
||||||
sf.state = {};
|
sf.state = {};
|
||||||
return sf;
|
return sf;
|
||||||
|
@ -29,14 +28,9 @@ function unorderedEquals(arr1, arr2) {
|
||||||
|
|
||||||
|
|
||||||
describe('ShowFor', () => {
|
describe('ShowFor', () => {
|
||||||
mochaJsdom({useEach: true, url: 'http://localhost'});
|
|
||||||
mochaJquery();
|
|
||||||
/* globals window, document, $ */
|
|
||||||
let showFor;
|
let showFor;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
rerequire('../showfor.js');
|
|
||||||
|
|
||||||
// Wow. That's a lot of data. Can we make this smaller?
|
// Wow. That's a lot of data. Can we make this smaller?
|
||||||
let sandbox = (
|
let sandbox = (
|
||||||
<div>
|
<div>
|
||||||
|
@ -124,6 +118,14 @@ describe('ShowFor', () => {
|
||||||
|
|
||||||
React.render(sandbox, document.body);
|
React.render(sandbox, document.body);
|
||||||
showFor = showForNoInit($('body'));
|
showFor = showForNoInit($('body'));
|
||||||
|
|
||||||
|
BrowserDetect.browser = "firefox";
|
||||||
|
BrowserDetect.version = 25.0;
|
||||||
|
BrowserDetect.OS = "winxp";
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
BrowserDetect.init();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('loadData', () => {
|
describe('loadData', () => {
|
||||||
|
@ -145,13 +147,10 @@ describe('ShowFor', () => {
|
||||||
|
|
||||||
describe('updateUI', () => {
|
describe('updateUI', () => {
|
||||||
describe('Firefox 26 on Windows XP', () => {
|
describe('Firefox 26 on Windows XP', () => {
|
||||||
mochaBrowserDetect({
|
|
||||||
browser: 'fx',
|
|
||||||
version: 26.0,
|
|
||||||
OS: 'winxp',
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
BrowserDetect.browser = "fx"
|
||||||
|
BrowserDetect.version = 26.0
|
||||||
|
BrowserDetect.OS = "winxp"
|
||||||
showFor.loadData();
|
showFor.loadData();
|
||||||
showFor.updateUI();
|
showFor.updateUI();
|
||||||
});
|
});
|
||||||
|
@ -163,13 +162,10 @@ describe('ShowFor', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Firefox for Android 23', () => {
|
describe('Firefox for Android 23', () => {
|
||||||
mochaBrowserDetect({
|
|
||||||
browser: 'm',
|
|
||||||
version: 23.0,
|
|
||||||
OS: 'android',
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
BrowserDetect.browser = "m"
|
||||||
|
BrowserDetect.version = 23.0
|
||||||
|
BrowserDetect.OS = "android"
|
||||||
showFor.loadData();
|
showFor.loadData();
|
||||||
showFor.updateUI();
|
showFor.updateUI();
|
||||||
});
|
});
|
||||||
|
@ -182,7 +178,6 @@ describe('ShowFor', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('updateState', () => {
|
describe('updateState', () => {
|
||||||
mochaBrowserDetect();
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
showFor.loadData();
|
showFor.loadData();
|
||||||
|
@ -242,7 +237,6 @@ describe('ShowFor', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('initShowFuncs', () => {
|
describe('initShowFuncs', () => {
|
||||||
mochaBrowserDetect();
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
sinon.stub(showFor, 'matchesCriteria');
|
sinon.stub(showFor, 'matchesCriteria');
|
||||||
|
@ -274,7 +268,6 @@ describe('ShowFor', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('showAndHide', () => {
|
describe('showAndHide', () => {
|
||||||
mochaBrowserDetect();
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
showFor.loadData();
|
showFor.loadData();
|
||||||
|
@ -302,7 +295,6 @@ describe('ShowFor', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('matchesCriteria', () => {
|
describe('matchesCriteria', () => {
|
||||||
mochaBrowserDetect();
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
showFor.loadData();
|
showFor.loadData();
|
||||||
|
|
|
@ -1,26 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {default as mochaJsdom, rerequire} from 'mocha-jsdom';
|
|
||||||
import {expect} from 'chai';
|
import {expect} from 'chai';
|
||||||
import sinon from 'sinon';
|
|
||||||
|
|
||||||
import mochaGettext from './fixtures/mochaGettext.js';
|
import TagsFilter from "sumo/js/tags.filter";
|
||||||
import mochaK from './fixtures/mochaK.js';
|
|
||||||
import mochaJquery from './fixtures/mochaJquery.js';
|
|
||||||
import mochaUnderscore from './fixtures/mochaUnderscore.js';
|
|
||||||
|
|
||||||
describe('k', () => {
|
describe('k', () => {
|
||||||
let form;
|
|
||||||
|
|
||||||
mochaJsdom({useEach: true, url: 'http://localhost'});
|
|
||||||
mochaJquery();
|
|
||||||
mochaK();
|
|
||||||
mochaUnderscore();
|
|
||||||
/* globals window, $, k */
|
|
||||||
|
|
||||||
describe('TagsFilter', () => {
|
describe('TagsFilter', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
rerequire('../tags.filter.js');
|
|
||||||
|
|
||||||
let sandbox = (
|
let sandbox = (
|
||||||
<div>
|
<div>
|
||||||
<section className="tag-filter">
|
<section className="tag-filter">
|
||||||
|
@ -41,7 +26,7 @@ describe('k', () => {
|
||||||
);
|
);
|
||||||
React.render(sandbox, window.document.body);
|
React.render(sandbox, window.document.body);
|
||||||
|
|
||||||
k.TagsFilter.init($('body'));
|
TagsFilter.init($('body'));
|
||||||
// Don't let forms submit
|
// Don't let forms submit
|
||||||
$('form').submit((e) => e.preventDefault());
|
$('form').submit((e) => e.preventDefault());
|
||||||
});
|
});
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
19
package.json
19
package.json
|
@ -32,9 +32,10 @@
|
||||||
"browser-sync:docs": "browser-sync start --no-open --serveStatic \"styleguide/build\" --files \"styleguide/build/**/*\" --port 4000 --reload-delay=300",
|
"browser-sync:docs": "browser-sync start --no-open --serveStatic \"styleguide/build\" --files \"styleguide/build/**/*\" --port 4000 --reload-delay=300",
|
||||||
"start": "concurrently --raw --kill-others \"npm run webpack:watch\" \"npm run browser-sync\"",
|
"start": "concurrently --raw --kill-others \"npm run webpack:watch\" \"npm run browser-sync\"",
|
||||||
"lint:webpack": "npx eslint --no-eslintrc -c webpack/eslintrc.js kitsune",
|
"lint:webpack": "npx eslint --no-eslintrc -c webpack/eslintrc.js kitsune",
|
||||||
"webpack:build": "npx webpack build --mode development",
|
"webpack:build": "npx webpack build --config webpack.dev.js",
|
||||||
"webpack:build:prod": "npx webpack build --mode production",
|
"webpack:build:prod": "npx webpack build --config webpack.prod.js",
|
||||||
"webpack:watch": "npx webpack watch --mode development"
|
"webpack:watch": "npx webpack watch --config webpack.dev.js",
|
||||||
|
"webpack:test": "npx webpack build --config webpack.test.js && npx mocha --require ./webpack/mocha-require dist/tests.js"
|
||||||
},
|
},
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -46,7 +47,7 @@
|
||||||
"fontawesome": "^4.3.0",
|
"fontawesome": "^4.3.0",
|
||||||
"jquery": "1.11.3",
|
"jquery": "1.11.3",
|
||||||
"jquery-ui": "1.12.1",
|
"jquery-ui": "1.12.1",
|
||||||
"nunjucks": "^1.3.4",
|
"nunjucks": "^3.2.3",
|
||||||
"react": "0.13.3",
|
"react": "0.13.3",
|
||||||
"underscore": "^1.13.1"
|
"underscore": "^1.13.1"
|
||||||
},
|
},
|
||||||
|
@ -74,17 +75,17 @@
|
||||||
"eslint-plugin-import": "^2.25.2",
|
"eslint-plugin-import": "^2.25.2",
|
||||||
"exports-loader": "^3.0.0",
|
"exports-loader": "^3.0.0",
|
||||||
"expose-loader": "^3.0.0",
|
"expose-loader": "^3.0.0",
|
||||||
|
"glob": "^7.2.0",
|
||||||
"html-webpack-plugin": "^5.3.2",
|
"html-webpack-plugin": "^5.3.2",
|
||||||
"image-minimizer-webpack-plugin": "^2.2.0",
|
"image-minimizer-webpack-plugin": "^2.2.0",
|
||||||
"imagemin-optipng": "^8.0.0",
|
"imagemin-optipng": "^8.0.0",
|
||||||
"imagemin-svgo": "^9.0.0",
|
"imagemin-svgo": "^9.0.0",
|
||||||
"imports-loader": "^3.0.0",
|
"imports-loader": "^3.0.0",
|
||||||
|
"jsdom": "^19.0.0",
|
||||||
"kss": "^3.0.0-beta.25",
|
"kss": "^3.0.0-beta.25",
|
||||||
"locutus": "^2.0.15",
|
"locutus": "^2.0.15",
|
||||||
"mini-css-extract-plugin": "^1.6.0",
|
"mini-css-extract-plugin": "^1.6.0",
|
||||||
"mocha": "2.3.2",
|
"mocha": "2.3.2",
|
||||||
"mocha-jsdom": "^2.0.0",
|
|
||||||
"nunjucks": "^1.3.4",
|
|
||||||
"onchange": "^6.1.0",
|
"onchange": "^6.1.0",
|
||||||
"path-parse": "^1.0.7",
|
"path-parse": "^1.0.7",
|
||||||
"postcss": "^8.3.5",
|
"postcss": "^8.3.5",
|
||||||
|
@ -92,8 +93,9 @@
|
||||||
"postcss-loader": "^6.1.1",
|
"postcss-loader": "^6.1.1",
|
||||||
"sass": "^1.23.2",
|
"sass": "^1.23.2",
|
||||||
"sass-loader": "^12.0.0",
|
"sass-loader": "^12.0.0",
|
||||||
"sinon": "1.16.1",
|
"sinon": "12.0.1",
|
||||||
"sinon-chai": "2.8.0",
|
"sinon-chai": "2.8.0",
|
||||||
|
"source-map-support": "^0.5.21",
|
||||||
"style-loader": "^2.0.0",
|
"style-loader": "^2.0.0",
|
||||||
"stylelint": "^11.1.1",
|
"stylelint": "^11.1.1",
|
||||||
"stylelint-config-recommended-scss": "^3.3.0",
|
"stylelint-config-recommended-scss": "^3.3.0",
|
||||||
|
@ -102,6 +104,7 @@
|
||||||
"svgo": "^1.3.2",
|
"svgo": "^1.3.2",
|
||||||
"webpack": "^5.38.1",
|
"webpack": "^5.38.1",
|
||||||
"webpack-bundle-analyzer": "^4.4.2",
|
"webpack-bundle-analyzer": "^4.4.2",
|
||||||
"webpack-cli": "^4.7.2"
|
"webpack-cli": "^4.7.2",
|
||||||
|
"webpack-merge": "^5.8.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
const webpack = require("webpack");
|
||||||
|
const path = require("path");
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
mode: "development",
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
protocol: "@mozilla-protocol/core/protocol",
|
||||||
|
sumo: path.resolve(__dirname, "kitsune/sumo/static/sumo"),
|
||||||
|
community: path.resolve(__dirname, "kitsune/community/static/community"),
|
||||||
|
kpi: path.resolve(__dirname, "kitsune/kpi/static/kpi"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: "babel-loader",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.scss$/,
|
||||||
|
use: [
|
||||||
|
MiniCssExtractPlugin.loader,
|
||||||
|
"css-loader",
|
||||||
|
"postcss-loader",
|
||||||
|
"sass-loader",
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(svg|png|gif|woff2?)$/,
|
||||||
|
type: "asset/resource",
|
||||||
|
},
|
||||||
|
// we copy these libraries from external sources, so define their exports here,
|
||||||
|
// rather than having to modify them, making updating them more difficult:
|
||||||
|
exports(
|
||||||
|
"./kitsune/sumo/static/sumo/js/libs/dnt-helper.js",
|
||||||
|
"default Mozilla.dntEnabled"
|
||||||
|
),
|
||||||
|
exports(
|
||||||
|
"./kitsune/sumo/static/sumo/js/libs/uitour.js",
|
||||||
|
"default Mozilla.UITour"
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
plugins: [
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
$: "jquery",
|
||||||
|
jQuery: "jquery",
|
||||||
|
"window.jQuery": "jquery",
|
||||||
|
}),
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: "[name].css",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
cache: {
|
||||||
|
type: "filesystem",
|
||||||
|
},
|
||||||
|
devtool: "cheap-module-source-map",
|
||||||
|
output: {
|
||||||
|
filename: "[name].js",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
function exports(path, exports) {
|
||||||
|
// export the named variable
|
||||||
|
return {
|
||||||
|
test: require.resolve(path),
|
||||||
|
loader: "exports-loader",
|
||||||
|
options: {
|
||||||
|
type: "module",
|
||||||
|
exports,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
|
@ -1,90 +0,0 @@
|
||||||
const webpack = require("webpack");
|
|
||||||
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
|
||||||
const CopyPlugin = require("copy-webpack-plugin");
|
|
||||||
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
|
|
||||||
const AssetJsonPlugin = require("./webpack/asset-json-plugin");
|
|
||||||
|
|
||||||
const aliases = require("./webpack/aliases");
|
|
||||||
const entrypoints = require("./webpack/entrypoints");
|
|
||||||
const entrypointsHtml = require("./webpack/entrypoints-html");
|
|
||||||
const exportRules = require("./webpack/export-rules");
|
|
||||||
|
|
||||||
const assetModuleFilename = "[name].[contenthash][ext]";
|
|
||||||
|
|
||||||
module.exports = (env, argv) => {
|
|
||||||
const dev = argv.mode === "development";
|
|
||||||
const config = {
|
|
||||||
resolve: {
|
|
||||||
alias: aliases,
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.js$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: "babel-loader",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.scss$/,
|
|
||||||
use: [
|
|
||||||
MiniCssExtractPlugin.loader,
|
|
||||||
"css-loader",
|
|
||||||
"postcss-loader",
|
|
||||||
"sass-loader",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
test: /\.(svg|png|gif|woff2?)$/,
|
|
||||||
type: "asset/resource",
|
|
||||||
},
|
|
||||||
...exportRules,
|
|
||||||
],
|
|
||||||
},
|
|
||||||
entry: entrypoints,
|
|
||||||
plugins: [
|
|
||||||
new webpack.ProvidePlugin({
|
|
||||||
$: "jquery",
|
|
||||||
jQuery: "jquery",
|
|
||||||
"window.jQuery": "jquery",
|
|
||||||
}),
|
|
||||||
new MiniCssExtractPlugin({
|
|
||||||
filename: dev ? "[name].css" : "[name].[contenthash].css",
|
|
||||||
}),
|
|
||||||
...entrypointsHtml,
|
|
||||||
new CopyPlugin({
|
|
||||||
patterns: [
|
|
||||||
{ from: "node_modules/@mozilla-protocol/core/protocol/img/icons/**", to: assetModuleFilename },
|
|
||||||
{ from: "kitsune/*/static/**/img/**", to: assetModuleFilename },
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
new ImageMinimizerPlugin({
|
|
||||||
minimizerOptions: {
|
|
||||||
plugins: [
|
|
||||||
"optipng",
|
|
||||||
"svgo",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
new AssetJsonPlugin(),
|
|
||||||
],
|
|
||||||
output: {
|
|
||||||
filename: dev ? "[name].js" : "[name].[contenthash].js",
|
|
||||||
assetModuleFilename: assetModuleFilename,
|
|
||||||
},
|
|
||||||
cache: dev ? { type: "filesystem" } : false,
|
|
||||||
optimization: {
|
|
||||||
splitChunks: {
|
|
||||||
chunks: 'all',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (dev) {
|
|
||||||
// eval source maps don't work with our css loaders
|
|
||||||
config.devtool = "cheap-module-source-map";
|
|
||||||
}
|
|
||||||
|
|
||||||
return config;
|
|
||||||
};
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
const { merge } = require("webpack-merge");
|
||||||
|
const CopyPlugin = require("copy-webpack-plugin");
|
||||||
|
const ImageMinimizerPlugin = require("image-minimizer-webpack-plugin");
|
||||||
|
const AssetJsonPlugin = require("./webpack/asset-json-plugin");
|
||||||
|
|
||||||
|
const common = require("./webpack.common.js");
|
||||||
|
const entrypoints = require("./webpack/entrypoints");
|
||||||
|
const entrypointsHtml = require("./webpack/entrypoints-html");
|
||||||
|
|
||||||
|
const assetModuleFilename = "[name].[contenthash][ext]";
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
entry: entrypoints,
|
||||||
|
plugins: [
|
||||||
|
...entrypointsHtml,
|
||||||
|
new CopyPlugin({
|
||||||
|
patterns: [
|
||||||
|
{
|
||||||
|
from: "node_modules/@mozilla-protocol/core/protocol/img/icons/**",
|
||||||
|
to: assetModuleFilename,
|
||||||
|
},
|
||||||
|
{ from: "kitsune/*/static/**/img/**", to: assetModuleFilename },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
new ImageMinimizerPlugin({
|
||||||
|
minimizerOptions: {
|
||||||
|
plugins: ["optipng", "svgo"],
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
new AssetJsonPlugin(),
|
||||||
|
],
|
||||||
|
optimization: {
|
||||||
|
splitChunks: {
|
||||||
|
chunks: "all",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
output: {
|
||||||
|
assetModuleFilename: assetModuleFilename,
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
const { mergeWithCustomize, unique } = require("webpack-merge");
|
||||||
|
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
|
||||||
|
|
||||||
|
const dev = require("./webpack.dev.js");
|
||||||
|
|
||||||
|
module.exports = mergeWithCustomize({
|
||||||
|
customizeArray: unique(
|
||||||
|
"plugins",
|
||||||
|
["MiniCssExtractPlugin"],
|
||||||
|
(plugin) => plugin.constructor && plugin.constructor.name
|
||||||
|
),
|
||||||
|
})(dev, {
|
||||||
|
mode: "production",
|
||||||
|
plugins: [
|
||||||
|
new MiniCssExtractPlugin({
|
||||||
|
filename: "[name].[contenthash].css",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
cache: false,
|
||||||
|
devtool: false,
|
||||||
|
output: {
|
||||||
|
filename: "[name].[contenthash].js",
|
||||||
|
},
|
||||||
|
});
|
|
@ -0,0 +1,11 @@
|
||||||
|
const { merge } = require("webpack-merge");
|
||||||
|
const glob = require("glob");
|
||||||
|
|
||||||
|
const common = require("./webpack.common.js");
|
||||||
|
|
||||||
|
module.exports = merge(common, {
|
||||||
|
target: "node",
|
||||||
|
entry: {
|
||||||
|
tests: [...glob.sync("./kitsune/*/static/*/js/tests/*.js")],
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,8 +0,0 @@
|
||||||
const path = require("path");
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
protocol: "@mozilla-protocol/core/protocol",
|
|
||||||
sumo: path.resolve(__dirname, "../kitsune/sumo/static/sumo"),
|
|
||||||
community: path.resolve(__dirname, "../kitsune/community/static/community"),
|
|
||||||
kpi: path.resolve(__dirname, "../kitsune/kpi/static/kpi"),
|
|
||||||
};
|
|
|
@ -3,7 +3,11 @@ module.exports = {
|
||||||
"plugin:import/recommended",
|
"plugin:import/recommended",
|
||||||
],
|
],
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": "webpack",
|
"import/resolver": {
|
||||||
|
"webpack": {
|
||||||
|
"config": "./webpack.common.js",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"es6": true,
|
"es6": true,
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
module.exports = [
|
|
||||||
// we copy these libraries from external sources, so define their exports here,
|
|
||||||
// rather than having to modify them, making updating them more difficult:
|
|
||||||
exports(
|
|
||||||
"../kitsune/sumo/static/sumo/js/libs/dnt-helper.js",
|
|
||||||
"default Mozilla.dntEnabled"
|
|
||||||
),
|
|
||||||
exports(
|
|
||||||
"../kitsune/sumo/static/sumo/js/libs/uitour.js",
|
|
||||||
"default Mozilla.UITour"
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
function exports(path, exports) {
|
|
||||||
// export the named variable
|
|
||||||
return {
|
|
||||||
test: require.resolve(path),
|
|
||||||
loader: "exports-loader",
|
|
||||||
options: {
|
|
||||||
type: "module",
|
|
||||||
exports,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,15 +1,21 @@
|
||||||
require("@babel/register")({
|
require('source-map-support').install();
|
||||||
plugins: [
|
|
||||||
[
|
|
||||||
"module-resolver",
|
|
||||||
{
|
|
||||||
// make babel resolve our webpack aliases in tests
|
|
||||||
alias: require("./aliases"),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
});
|
|
||||||
|
|
||||||
// make images imports return null, we don't need them in tests
|
const jsdom = require("jsdom");
|
||||||
require.extensions[".svg"] = () => null;
|
const { JSDOM } = jsdom;
|
||||||
require.extensions[".png"] = () => null;
|
const dom = new JSDOM("<html></html>", {
|
||||||
|
url: "https://example.com",
|
||||||
|
referrer: "http://google.com/?q=cookies",
|
||||||
|
});
|
||||||
|
global.window = dom.window;
|
||||||
|
global.document = dom.window.document;
|
||||||
|
global.navigator = dom.window.navigator;
|
||||||
|
global.sessionStorage = dom.window.sessionStorage;
|
||||||
|
global.history = dom.window.history;
|
||||||
|
global.Element = dom.window.Element;
|
||||||
|
global.matchMedia = () => ({
|
||||||
|
matches : false,
|
||||||
|
addListener : () =>{},
|
||||||
|
removeListener: () =>{},
|
||||||
|
});
|
||||||
|
global.jQuery = global.$ = require("jquery");
|
||||||
|
require("../kitsune/sumo/static/sumo/js/i18n");
|
||||||
|
|
Загрузка…
Ссылка в новой задаче