зеркало из https://github.com/mozilla/bedrock.git
Add JS tests for /careers job filtering (Fixes #11080)
This commit is contained in:
Родитель
9b29dd8d1e
Коммит
4deaca396d
13
.eslintrc.js
13
.eslintrc.js
|
@ -72,13 +72,22 @@ module.exports = {
|
|||
files: [
|
||||
'media/js/firefox/welcome/**/*.js',
|
||||
'media/js/firefox/whatsnew/**/*.js',
|
||||
'media/js/firefox/firstrun/**/*.js',
|
||||
'tests/unit/**/*.js'
|
||||
'media/js/firefox/firstrun/**/*.js'
|
||||
],
|
||||
env: {
|
||||
es2017: true
|
||||
}
|
||||
},
|
||||
{
|
||||
// JS Karma test files.
|
||||
files: ['tests/unit/**/*.js'],
|
||||
env: {
|
||||
es2017: true
|
||||
},
|
||||
parserOptions: {
|
||||
sourceType: 'module'
|
||||
}
|
||||
},
|
||||
{
|
||||
// JS build files for local dev.
|
||||
files: [
|
||||
|
|
|
@ -1,184 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Monitors inputs and filters a list of positions when they change.
|
||||
*/
|
||||
function PositionFilters(
|
||||
typeInput,
|
||||
teamInput,
|
||||
locationInput,
|
||||
positionTable
|
||||
) {
|
||||
this.typeInput = typeInput;
|
||||
this.teamInput = teamInput;
|
||||
this.locationInput = locationInput;
|
||||
this.positionTable = positionTable;
|
||||
this.emptyFilterMessage = document.getElementById(
|
||||
'empty-filter-message'
|
||||
);
|
||||
}
|
||||
|
||||
PositionFilters.prototype = {
|
||||
/**
|
||||
* Bind onFilterChange to the change events for each input.
|
||||
*/
|
||||
bindEvents: function () {
|
||||
var self = this;
|
||||
var callback = function () {
|
||||
self.onFilterChange();
|
||||
};
|
||||
|
||||
this.typeInput.addEventListener('change', callback);
|
||||
this.teamInput.addEventListener('change', callback);
|
||||
this.locationInput.addEventListener('change', callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* When a filter changes, refresh the position list.
|
||||
*/
|
||||
onFilterChange: function () {
|
||||
var i;
|
||||
// collection of tr.position elements
|
||||
var positions =
|
||||
this.positionTable.getElementsByClassName('position');
|
||||
var positionsVisible = false;
|
||||
var querystring = '';
|
||||
|
||||
var filters = {
|
||||
position_type: this.typeInput.value,
|
||||
team: this.teamInput.value,
|
||||
location: this.locationInput.value
|
||||
};
|
||||
|
||||
// Hide table and show all positions.
|
||||
this.positionTable.classList.add('hidden');
|
||||
this.emptyFilterMessage.classList.add('hidden');
|
||||
|
||||
for (i = 0; i < positions.length; i++) {
|
||||
positions.item(i).classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Hide positions that don't match the current filters.
|
||||
this.filterPositions('type', filters['position_type']);
|
||||
this.filterPositions('team', filters['team']);
|
||||
this.filterLocations(filters['location']);
|
||||
|
||||
// If there aren't any positions being shown, show the no-results message.
|
||||
for (i = 0; i < positions.length; i++) {
|
||||
if (!positions.item(i).classList.contains('hidden')) {
|
||||
positionsVisible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!positionsVisible) {
|
||||
this.emptyFilterMessage.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Get rid of unset filters.
|
||||
for (var k in filters) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(filters, k) &&
|
||||
!filters[k]
|
||||
) {
|
||||
delete filters[k];
|
||||
}
|
||||
}
|
||||
|
||||
// Build a querystring from populated filters.
|
||||
for (var prop in filters) {
|
||||
querystring += prop + '=' + filters[prop];
|
||||
}
|
||||
|
||||
// Preserve Google Analytics parameters.
|
||||
var ga_parameters = window.location.search
|
||||
.substr(1)
|
||||
.split('&')
|
||||
.filter(function (parameter) {
|
||||
return parameter.indexOf('utm_') === 0;
|
||||
});
|
||||
|
||||
if (querystring.length && ga_parameters.length) {
|
||||
querystring += '&';
|
||||
}
|
||||
|
||||
querystring += ga_parameters.join('&');
|
||||
|
||||
if (querystring.length) {
|
||||
querystring = '?' + querystring;
|
||||
}
|
||||
|
||||
// Replace history state with this filtered state.
|
||||
window.history.replaceState(
|
||||
filters,
|
||||
'Filtered',
|
||||
location.pathname + querystring
|
||||
);
|
||||
|
||||
this.positionTable.classList.remove('hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide any positions that do have the correct value for the given field.
|
||||
*/
|
||||
filterPositions: function (field, value) {
|
||||
if (!value) return;
|
||||
|
||||
var positions =
|
||||
this.positionTable.getElementsByClassName('position');
|
||||
|
||||
for (var i = 0; i < positions.length; i++) {
|
||||
var data = positions.item(i).dataset[field];
|
||||
|
||||
if (data.indexOf(value + ',') === -1) {
|
||||
positions.item(i).classList.add('hidden');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
filterLocations: function (value) {
|
||||
// Note that filtering is based on a data attr, but the position's
|
||||
// location shown in the HTML may be different to (or contain _more_
|
||||
// items than) the data attribute's value.
|
||||
if (!value) return;
|
||||
|
||||
var positions =
|
||||
this.positionTable.getElementsByClassName('position');
|
||||
|
||||
for (var i = 0; i < positions.length; i++) {
|
||||
var data = positions.item(i).dataset.location;
|
||||
|
||||
// When user selects 'Remote' only list jobs explicitly marked
|
||||
// Remote otherwise list jobs matching value (which is a mozilla
|
||||
// office) and those marked as 'All Offices'
|
||||
if (value === 'Remote') {
|
||||
if (data.indexOf(value + ',') === -1) {
|
||||
positions.item(i).classList.add('hidden');
|
||||
}
|
||||
} else if (
|
||||
data.indexOf(value + ',') === -1 &&
|
||||
data.indexOf('All Offices,') === -1
|
||||
) {
|
||||
positions.item(i).classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var inputs = document.getElementById('listings-filters').elements;
|
||||
var filters = new PositionFilters(
|
||||
inputs.position_type,
|
||||
inputs.team,
|
||||
inputs.location,
|
||||
document.getElementById('listings-positions')
|
||||
);
|
||||
filters.bindEvents();
|
||||
filters.onFilterChange(); // Trigger sorting on initial load for querystring arguments.
|
||||
})();
|
|
@ -1,66 +0,0 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
/*
|
||||
* Listings
|
||||
*/
|
||||
|
||||
/**
|
||||
* Take filter values in querystring and propogate to select inputs
|
||||
*/
|
||||
function propogateQueryParamsToSelects() {
|
||||
var i;
|
||||
var j;
|
||||
var keyVal;
|
||||
var keyVals;
|
||||
var match;
|
||||
var qs = window.location.search;
|
||||
var select;
|
||||
var val;
|
||||
|
||||
if (qs) {
|
||||
// drop the '?'
|
||||
qs = qs.slice(1);
|
||||
|
||||
// split the querystring into key=val strings
|
||||
keyVals = qs.split('&');
|
||||
|
||||
// for each key/value pair, update the associated select box
|
||||
for (i = 0; i < keyVals.length; i++) {
|
||||
keyVal = keyVals[i].split('=');
|
||||
|
||||
// first index is the key, which, with an 'id_' prefix, matches the field id
|
||||
select = document.getElementById('id_' + keyVal[0]);
|
||||
|
||||
// make sure the key is valid, then update the associated select box
|
||||
if (select) {
|
||||
// (decodeURIComponent does not change '+' to ' ', hence the replace call)
|
||||
val = decodeURIComponent(keyVal[1]).replace(/\+/gi, ' ');
|
||||
|
||||
// make sure select has an option matching the proposed value
|
||||
// this ensures the select box doesn't get set to an empty value if
|
||||
// e.g. there are no Intern positions available
|
||||
for (j = 0; j < select.options.length; j++) {
|
||||
if (select.options[j].value === val) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
select.value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// init listings
|
||||
propogateQueryParamsToSelects();
|
||||
})();
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Monitors inputs and filters a list of positions when they change.
|
||||
*/
|
||||
function PositionFilters(typeInput, teamInput, locationInput, positionTable) {
|
||||
this.typeInput = typeInput;
|
||||
this.teamInput = teamInput;
|
||||
this.locationInput = locationInput;
|
||||
this.positionTable = positionTable;
|
||||
this.emptyFilterMessage = document.getElementById('empty-filter-message');
|
||||
}
|
||||
|
||||
PositionFilters.prototype = {
|
||||
/**
|
||||
* Bind onFilterChange to the change events for each input.
|
||||
*/
|
||||
bindEvents: function () {
|
||||
const self = this;
|
||||
const callback = function () {
|
||||
self.onFilterChange();
|
||||
};
|
||||
|
||||
this.typeInput.addEventListener('change', callback);
|
||||
this.teamInput.addEventListener('change', callback);
|
||||
this.locationInput.addEventListener('change', callback);
|
||||
},
|
||||
|
||||
/**
|
||||
* When a filter changes, refresh the position list.
|
||||
*/
|
||||
onFilterChange: function () {
|
||||
// collection of tr.position elements
|
||||
const positions = this.positionTable.getElementsByClassName('position');
|
||||
let positionsVisible = false;
|
||||
let querystring = '';
|
||||
|
||||
const filters = {
|
||||
position_type: this.typeInput.value,
|
||||
team: this.teamInput.value,
|
||||
location: this.locationInput.value
|
||||
};
|
||||
|
||||
// Hide table and show all positions.
|
||||
this.positionTable.classList.add('hidden');
|
||||
this.emptyFilterMessage.classList.add('hidden');
|
||||
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
positions.item(i).classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Hide positions that don't match the current filters.
|
||||
this.filterPositions('type', filters['position_type']);
|
||||
this.filterPositions('team', filters['team']);
|
||||
this.filterLocations(filters['location']);
|
||||
|
||||
// If there aren't any positions being shown, show the no-results message.
|
||||
for (let j = 0; j < positions.length; j++) {
|
||||
if (!positions.item(j).classList.contains('hidden')) {
|
||||
positionsVisible = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!positionsVisible) {
|
||||
this.emptyFilterMessage.classList.remove('hidden');
|
||||
}
|
||||
|
||||
// Get rid of unset filters.
|
||||
for (const k in filters) {
|
||||
if (
|
||||
Object.prototype.hasOwnProperty.call(filters, k) &&
|
||||
!filters[k]
|
||||
) {
|
||||
delete filters[k];
|
||||
}
|
||||
}
|
||||
|
||||
// Build a querystring from populated filters.
|
||||
for (const prop in filters) {
|
||||
querystring += prop + '=' + filters[prop];
|
||||
}
|
||||
|
||||
// Preserve Google Analytics parameters.
|
||||
const ga_parameters = window.location.search
|
||||
.substr(1)
|
||||
.split('&')
|
||||
.filter(function (parameter) {
|
||||
return parameter.indexOf('utm_') === 0;
|
||||
});
|
||||
|
||||
if (querystring.length && ga_parameters.length) {
|
||||
querystring += '&';
|
||||
}
|
||||
|
||||
querystring += ga_parameters.join('&');
|
||||
|
||||
if (querystring.length) {
|
||||
querystring = '?' + querystring;
|
||||
}
|
||||
|
||||
// Replace history state with this filtered state.
|
||||
window.history.replaceState(
|
||||
filters,
|
||||
'Filtered',
|
||||
location.pathname + querystring
|
||||
);
|
||||
|
||||
this.positionTable.classList.remove('hidden');
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide any positions that do have the correct value for the given field.
|
||||
*/
|
||||
filterPositions: function (field, value) {
|
||||
if (!value) return;
|
||||
|
||||
const positions = this.positionTable.getElementsByClassName('position');
|
||||
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
const data = positions.item(i).dataset[field];
|
||||
|
||||
if (data.indexOf(value + ',') === -1) {
|
||||
positions.item(i).classList.add('hidden');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
filterLocations: function (value) {
|
||||
// Note that filtering is based on a data attr, but the position's
|
||||
// location shown in the HTML may be different to (or contain _more_
|
||||
// items than) the data attribute's value.
|
||||
if (!value) return;
|
||||
|
||||
const positions = this.positionTable.getElementsByClassName('position');
|
||||
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
const data = positions.item(i).dataset.location;
|
||||
|
||||
// When user selects 'Remote' only list jobs explicitly marked
|
||||
// Remote otherwise list jobs matching value (which is a mozilla
|
||||
// office) and those marked as 'All Offices'
|
||||
if (value === 'Remote') {
|
||||
if (data.indexOf(value + ',') === -1) {
|
||||
positions.item(i).classList.add('hidden');
|
||||
}
|
||||
} else if (
|
||||
data.indexOf(value + ',') === -1 &&
|
||||
data.indexOf('All Offices,') === -1
|
||||
) {
|
||||
positions.item(i).classList.add('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default PositionFilters;
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import propogateQueryParamsToSelects from './params.es6.js';
|
||||
import PositionFilters from './filters.es6.js';
|
||||
|
||||
// Take filter values in querystring and propogate to select inputs.
|
||||
propogateQueryParamsToSelects();
|
||||
|
||||
const inputs = document.getElementById('listings-filters').elements;
|
||||
const filters = new PositionFilters(
|
||||
inputs.position_type,
|
||||
inputs.team,
|
||||
inputs.location,
|
||||
document.getElementById('listings-positions')
|
||||
);
|
||||
filters.bindEvents();
|
||||
|
||||
// Trigger sorting on initial load for querystring arguments.
|
||||
filters.onFilterChange();
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Take filter values in querystring and propogate to select inputs
|
||||
*/
|
||||
function propogateQueryParamsToSelects(query) {
|
||||
let keyVals;
|
||||
let match;
|
||||
let qs = typeof query !== 'undefined' ? query : window.location.search;
|
||||
let select;
|
||||
|
||||
if (qs) {
|
||||
// drop the '?'
|
||||
qs = qs.slice(1);
|
||||
|
||||
// split the querystring into key=val strings
|
||||
keyVals = qs.split('&');
|
||||
|
||||
// for each key/value pair, update the associated select box
|
||||
for (let i = 0; i < keyVals.length; i++) {
|
||||
const keyVal = keyVals[i].split('=');
|
||||
|
||||
// first index is the key, which, with an 'id_' prefix, matches the field id
|
||||
select = document.getElementById('id_' + keyVal[0]);
|
||||
|
||||
// make sure the key is valid, then update the associated select box
|
||||
if (select && select.nodeName === 'SELECT') {
|
||||
// (decodeURIComponent does not change '+' to ' ', hence the replace call)
|
||||
const val = decodeURIComponent(keyVal[1]).replace(/\+/gi, ' ');
|
||||
|
||||
// make sure select has an option matching the proposed value
|
||||
// this ensures the select box doesn't get set to an empty value if
|
||||
// e.g. there are no Intern positions available
|
||||
for (let j = 0; j < select.options.length; j++) {
|
||||
if (select.options[j].value === val) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
select.value = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default propogateQueryParamsToSelects;
|
|
@ -1653,8 +1653,7 @@
|
|||
},
|
||||
{
|
||||
"files": [
|
||||
"js/careers/listings.js",
|
||||
"js/careers/filters.js"
|
||||
"js/careers/listings/listings.es6.js"
|
||||
],
|
||||
"name": "careers-listings"
|
||||
}
|
||||
|
|
|
@ -4,8 +4,6 @@
|
|||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
// Karma configuration
|
||||
|
@ -61,6 +59,8 @@ module.exports = function (config) {
|
|||
'tests/unit/spec/base/send-to-device.js',
|
||||
'tests/unit/spec/base/site.js',
|
||||
'tests/unit/spec/base/stub-attribution.js',
|
||||
'tests/unit/spec/careers/filters.js',
|
||||
'tests/unit/spec/careers/params.js',
|
||||
'tests/unit/spec/firefox/all/all-downloads-unified.js',
|
||||
'tests/unit/spec/firefox/new/common/thanks.js',
|
||||
{
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
/* global sinon */
|
||||
|
||||
describe('core-datalayer-page-id.js', function () {
|
||||
'use strict';
|
||||
|
||||
beforeEach(function () {
|
||||
// stub out Mozilla.Cookie lib
|
||||
window.Mozilla.Cookies = sinon.stub();
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
/* global sinon */
|
||||
|
||||
describe('core-datalayer.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('pageHasDownload', function () {
|
||||
it('will return "true" when download button is present on page.', function () {
|
||||
const downloadMarkup =
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('dnt-helper.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('Mozilla.dntEnabled', function () {
|
||||
it('should return true for Fx41 on Mac with DNT set to true', function () {
|
||||
const dnt = 1;
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('experiment-utils.es6.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('isApprovedToRun', function () {
|
||||
it('should return true if experimental params are not found in the page URL', function () {
|
||||
const isApprovedToRun =
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
/* global sinon */
|
||||
|
||||
describe('fxa-utm-referral.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('getHostName', function () {
|
||||
it('should return a hostname as expected', function () {
|
||||
const url1 =
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
/* global sinon, */
|
||||
|
||||
describe('mozilla-banner.js', function () {
|
||||
'use strict';
|
||||
|
||||
beforeEach(function () {
|
||||
// stub out Mozilla.Cookie lib
|
||||
window.Mozilla.Cookies = sinon.stub();
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('mozilla-client.js', function () {
|
||||
'use strict';
|
||||
|
||||
// User-agent strings for the most of the following tests
|
||||
const uas = {
|
||||
// Firefox family
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('mozilla-convert.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('getCurrentExperiment', function () {
|
||||
it('should return the current experiment name and variation', function () {
|
||||
const data = {
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
/* global sinon */
|
||||
|
||||
describe('mozilla-fxa-form.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('init', function () {
|
||||
beforeEach(function () {
|
||||
const form = `<form action="https://accounts.firefox.com/" data-mozillaonline-action="https://accounts.firefox.com.cn/" id="fxa-email-form" class="fxa-email-form">
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
/* global sinon */
|
||||
|
||||
describe('mozilla-fxa-link.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('init', function () {
|
||||
beforeEach(function () {
|
||||
const link =
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('mozilla-fxa-product-button.js', function () {
|
||||
'use strict';
|
||||
|
||||
beforeEach(function () {
|
||||
const button = `<a class="js-fxa-product-button" href="https://accounts.firefox.com/signup?form_type=button&service=test&entrypoint=mozilla.org-whatsnew60&utm_source=mozilla.org-whatsnew60&utm_medium=referral&utm_campaign=whatsnew60&context=fx_desktop_v3" data-action="https://accounts.firefox.com/" data-mozillaonline-link="https://accounts.firefox.com.cn/signup?form_type=button&service=test&entrypoint=mozilla.org-whatsnew60&utm_source=mozilla.org-whatsnew60&utm_medium=referral&utm_campaign=whatsnew60&context=fx_desktop_v3" data-mozillaonline-action="https://accounts.firefox.com.cn/">Sign Up to Monitor</a>
|
||||
<a class="js-fxa-product-button" href="https://getpocket.com/ff_signup?s=ffwelcome2&form_type=button&entrypoint=mozilla.org-firefox-welcome-2&utm_source=mozilla.org-firefox-welcome-2&utm_campaign=welcome-2-pocket&utm_medium=referral" data-action="https://accounts.firefox.com/">Activate Pocket</a>
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('mozilla-fxa.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('FxaState.applyStateToBody', function () {
|
||||
beforeEach(function () {
|
||||
document.body.classList.add('js', 'state-fxa-default');
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
/* global sinon */
|
||||
|
||||
describe('mozilla-pixel.js', function () {
|
||||
'use strict';
|
||||
|
||||
afterEach(function () {
|
||||
document.querySelectorAll('.moz-px').forEach((e) => {
|
||||
e.parentNode.removeChild(e);
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('mozilla-run.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('run', function () {
|
||||
afterEach(function () {
|
||||
window.site.isModernBrowser = window.site.cutsTheMustard();
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('mozilla-smoothscroll.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('Mozilla.smoothScroll scrolling', function () {
|
||||
it('should use window.scrollTo when native smooth scrolling is supported', function () {
|
||||
spyOn(window, 'scrollTo');
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('mozilla-utils.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('trans', function () {
|
||||
let stringDiv;
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
/* global _SearchParams */
|
||||
|
||||
describe('search-params.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('_SearchParams', function () {
|
||||
it('should return a proper value', function () {
|
||||
const params = new _SearchParams('scene=2&source=getfirefox');
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
/* eslint new-cap: [2, {"capIsNewExceptions": ["Deferred"]}] */
|
||||
|
||||
describe('send-to-device.js', function () {
|
||||
'use strict';
|
||||
|
||||
let form;
|
||||
|
||||
beforeEach(function () {
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
/* global sinon */
|
||||
|
||||
describe('site.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('getPlatform', function () {
|
||||
it('should identify Windows', function () {
|
||||
expect(
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
/* eslint new-cap: [2, {"capIsNewExceptions": ["Deferred"]}] */
|
||||
|
||||
describe('stub-attribution.js', function () {
|
||||
'use strict';
|
||||
|
||||
const GA_VISIT_ID = '1456954538.1610960957';
|
||||
|
||||
beforeEach(function () {
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/* For reference read the Jasmine and Sinon docs
|
||||
* Jasmine docs: https://jasmine.github.io/2.0/introduction.html
|
||||
* Sinon docs: http://sinonjs.org/docs/
|
||||
*/
|
||||
|
||||
import PositionFilters from '../../../../media/js/careers/listings/filters.es6.js';
|
||||
|
||||
describe('filters.js', function () {
|
||||
beforeEach(function () {
|
||||
const form = `<form id="listings-filters" class="hide-from-legacy-ie">
|
||||
<div class="listings-filter listings-filter-location">
|
||||
<select name="location" required="" id="id_location">
|
||||
<option value="" selected="">All Locations</option>
|
||||
<option value="Berlin Office">Berlin Office</option>
|
||||
<option value="Portland Office">Portland Office</option>
|
||||
<option value="Remote">Remote</option>
|
||||
<option value="Remote Canada">Remote Canada</option>
|
||||
<option value="Remote France">Remote France</option>
|
||||
<option value="Remote Germany">Remote Germany</option>
|
||||
<option value="Remote San Francisco Bay Area">Remote San Francisco Bay Area</option>
|
||||
<option value="Remote UK">Remote UK</option>
|
||||
<option value="Remote US">Remote US</option>
|
||||
<option value="San Francisco Office">San Francisco Office</option>
|
||||
<option value="Toronto Office">Toronto Office</option>
|
||||
</select>
|
||||
<label for="id_location" class="visually-hidden">Location</label>
|
||||
</div>
|
||||
<input type="hidden" id="id_position_type" name="position_type" value="">
|
||||
<div class="listings-filter listings-filter-name">
|
||||
<select name="team" autocomplete="off" required="" id="id_team">
|
||||
<option value="" selected="">All Teams</option>
|
||||
<option value="Business Development">Business Development</option>
|
||||
<option value="Core Product-Firefox">Core Product-Firefox</option>
|
||||
<option value="Core Product-Security">Core Product-Security</option>
|
||||
<option value="Data Organization">Data Organization</option>
|
||||
<option value="IT">IT</option>
|
||||
<option value="Marketing">Marketing</option>
|
||||
<option value="Mozilla Foundation">Mozilla Foundation</option>
|
||||
<option value="People & Organizational Development">People & Organizational Development</option>
|
||||
</select>
|
||||
<label for="id_team" class="visually-hidden">Team</label>
|
||||
</div>
|
||||
</form>`;
|
||||
|
||||
const table = `<table class="listings-positions" id="listings-positions">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title" scope="col"><h4>Job Title</h4></th>
|
||||
<th class="location" scope="col"><h4>Location</h4></th>
|
||||
<th class="name" scope="col"><h4>Team</h4></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="position" data-team="Business Development," data-type="," data-location="San Francisco Office,">
|
||||
<td class="title"><a href="#">Operations Specialist - Business Development</a></td>
|
||||
<td class="location">San Francisco Office</td>
|
||||
<td class="name">Business Development</td>
|
||||
</tr>
|
||||
<tr class="position" data-team="Core Product-Firefox," data-type="," data-location="Remote San Francisco Bay Area,">
|
||||
<td class="title"><a href="#"> Localization Program Manager </a></td>
|
||||
<td class="location">Remote San Francisco Bay Area</td>
|
||||
<td class="name">Core Product-Firefox</td>
|
||||
</tr>
|
||||
<tr class="position" data-team="Core Product-Firefox," data-type="," data-location="Remote Canada,">
|
||||
<td class="title"><a href="#">Senior Program Manager</a></td>
|
||||
<td class="location">Remote Canada</td>
|
||||
<td class="name">Core Product-Firefox</td>
|
||||
</tr>
|
||||
<tr class="position" data-team="Core Product-Security," data-type="," data-location="Remote US,">
|
||||
<td class="title"><a href="#">Senior Software Engineer (C++)</a></td>
|
||||
<td class="location">Remote US</td>
|
||||
<td class="name">Core Product-Security</td>
|
||||
</tr>
|
||||
<tr class="position" data-team="Core Product-Security," data-type="," data-location="San Francisco Office,">
|
||||
<td class="title"><a href="#">Senior UI Engineer</a></td>
|
||||
<td class="location">San Francisco Office</td>
|
||||
<td class="name">Core Product-Security</td>
|
||||
</tr>
|
||||
<tr class="position" data-team="Data Organization," data-type="," data-location="Remote Canada,Remote Germany,Remote US,">
|
||||
<td class="title"><a href="#">Data Engineer</a></td>
|
||||
<td class="location">Remote Canada, Remote Germany, Remote US</td>
|
||||
<td class="name">Data Organization</td>
|
||||
</tr>
|
||||
<tr class="position" data-team="Data Organization," data-type="," data-location="Remote Canada,Remote US,">
|
||||
<td class="title"><a href="#">Inference Data Scientist (Staff Level)</a></td>
|
||||
<td class="location">Remote Canada, Remote US</td>
|
||||
<td class="name">Data Organization</td>
|
||||
</tr>
|
||||
<tr class="position" data-team="Mozilla Foundation," data-type="," data-location="Remote,">
|
||||
<td class="title"><a href="#">Senior Software Engineer</a></td>
|
||||
<td class="location">Remote</td>
|
||||
<td class="name">Mozilla Foundation</td>
|
||||
</tr>
|
||||
<tr class="empty-filter-message hidden" id="empty-filter-message">
|
||||
<td colspan="4">
|
||||
<p>No jobs found that match the selected filters.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', form);
|
||||
document.body.insertAdjacentHTML('beforeend', table);
|
||||
|
||||
const inputs = document.getElementById('listings-filters').elements;
|
||||
const filters = new PositionFilters(
|
||||
inputs.position_type,
|
||||
inputs.team,
|
||||
inputs.location,
|
||||
document.getElementById('listings-positions')
|
||||
);
|
||||
filters.bindEvents();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
const form = document.getElementById('listings-filters');
|
||||
form.parentNode.removeChild(form);
|
||||
|
||||
const table = document.getElementById('listings-positions');
|
||||
table.parentNode.removeChild(table);
|
||||
});
|
||||
|
||||
it('should filter the table based on selected location', function () {
|
||||
const location = document.getElementById('id_location');
|
||||
const evt = new CustomEvent('change');
|
||||
location.value = 'Remote US';
|
||||
location.dispatchEvent(evt);
|
||||
|
||||
expect(
|
||||
document.querySelectorAll(
|
||||
'.listings-positions tbody tr.position:not(.hidden)'
|
||||
).length
|
||||
).toEqual(3);
|
||||
});
|
||||
|
||||
it('should filter the table based on selected team', function () {
|
||||
const team = document.getElementById('id_team');
|
||||
const evt = new CustomEvent('change');
|
||||
team.value = 'Core Product-Firefox';
|
||||
team.dispatchEvent(evt);
|
||||
|
||||
expect(
|
||||
document.querySelectorAll(
|
||||
'.listings-positions tbody tr.position:not(.hidden)'
|
||||
).length
|
||||
).toEqual(2);
|
||||
});
|
||||
|
||||
it('should filter the table based on both location and team', function () {
|
||||
const evt = new CustomEvent('change');
|
||||
const location = document.getElementById('id_location');
|
||||
const team = document.getElementById('id_team');
|
||||
|
||||
location.value = 'Remote US';
|
||||
location.dispatchEvent(evt);
|
||||
|
||||
team.value = 'Core Product-Security';
|
||||
team.dispatchEvent(evt);
|
||||
|
||||
expect(
|
||||
document.querySelectorAll(
|
||||
'.listings-positions tbody tr.position:not(.hidden)'
|
||||
).length
|
||||
).toEqual(1);
|
||||
});
|
||||
|
||||
it('should display a message if no matching jobs are found', function () {
|
||||
const location = document.getElementById('id_location');
|
||||
const evt = new CustomEvent('change');
|
||||
location.value = 'Toronto Office';
|
||||
location.dispatchEvent(evt);
|
||||
|
||||
const message = document.querySelector(
|
||||
'.listings-positions tbody tr.empty-filter-message'
|
||||
);
|
||||
|
||||
expect(message.classList.contains('hidden')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should display all jobs and locations when filters are unset', function () {
|
||||
const evt = new CustomEvent('change');
|
||||
const location = document.getElementById('id_location');
|
||||
const team = document.getElementById('id_team');
|
||||
|
||||
location.value = 'Remote US';
|
||||
location.dispatchEvent(evt);
|
||||
|
||||
team.value = 'Core Product-Security';
|
||||
team.dispatchEvent(evt);
|
||||
|
||||
expect(
|
||||
document.querySelectorAll(
|
||||
'.listings-positions tbody tr.position:not(.hidden)'
|
||||
).length
|
||||
).toEqual(1);
|
||||
|
||||
location.value = 'All Locations';
|
||||
location.dispatchEvent(evt);
|
||||
|
||||
team.value = 'All Teams';
|
||||
team.dispatchEvent(evt);
|
||||
|
||||
expect(
|
||||
document.querySelectorAll(
|
||||
'.listings-positions tbody tr.position:not(.hidden)'
|
||||
).length
|
||||
).toEqual(8);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/* For reference read the Jasmine and Sinon docs
|
||||
* Jasmine docs: https://jasmine.github.io/2.0/introduction.html
|
||||
* Sinon docs: http://sinonjs.org/docs/
|
||||
*/
|
||||
|
||||
import propogateQueryParamsToSelects from '../../../../media/js/careers/listings/params.es6.js';
|
||||
|
||||
describe('params.js', function () {
|
||||
beforeEach(function () {
|
||||
const location = `<select name="location" required="" id="id_location">
|
||||
<option value="" selected="">All Locations</option>
|
||||
<option value="Berlin Office">Berlin Office</option>
|
||||
<option value="Portland Office">Portland Office</option>
|
||||
<option value="Remote">Remote</option>
|
||||
<option value="Remote Canada">Remote Canada</option>
|
||||
<option value="Remote France">Remote France</option>
|
||||
<option value="Remote Germany">Remote Germany</option>
|
||||
<option value="Remote San Francisco Bay Area">Remote San Francisco Bay Area</option>
|
||||
<option value="Remote UK">Remote UK</option>
|
||||
<option value="Remote US">Remote US</option>
|
||||
<option value="San Francisco Office">San Francisco Office</option>
|
||||
<option value="Toronto Office">Toronto Office</option>
|
||||
</select>`;
|
||||
|
||||
const team = `<select name="team" required="" id="id_team">
|
||||
<option value="" selected="">All Teams</option>
|
||||
<option value="Business Development">Business Development</option>
|
||||
<option value="Core Product-Firefox">Core Product-Firefox</option>
|
||||
<option value="Core Product-Security">Core Product-Security</option>
|
||||
<option value="Data Organization">Data Organization</option>
|
||||
<option value="IT">IT</option>
|
||||
<option value="Marketing">Marketing</option>
|
||||
<option value="Mozilla Foundation">Mozilla Foundation</option>
|
||||
<option value="People & Organizational Development">People & Organizational Development</option>
|
||||
</select>`;
|
||||
|
||||
document.body.insertAdjacentHTML('beforeend', location);
|
||||
document.body.insertAdjacentHTML('beforeend', team);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
const location = document.getElementById('id_location');
|
||||
location.parentNode.removeChild(location);
|
||||
|
||||
const team = document.getElementById('id_team');
|
||||
team.parentNode.removeChild(team);
|
||||
});
|
||||
|
||||
it('should select location based on query param value', function () {
|
||||
const select = document.getElementById('id_location');
|
||||
|
||||
propogateQueryParamsToSelects('?location=Berlin%20Office');
|
||||
expect(select.options[select.selectedIndex].value).toEqual(
|
||||
'Berlin Office'
|
||||
);
|
||||
|
||||
propogateQueryParamsToSelects('?location=Remote%20UK');
|
||||
expect(select.options[select.selectedIndex].value).toEqual('Remote UK');
|
||||
});
|
||||
|
||||
it('should select team based on query param value', function () {
|
||||
const select = document.getElementById('id_team');
|
||||
|
||||
propogateQueryParamsToSelects('?team=Core%20Product-Firefox');
|
||||
expect(select.options[select.selectedIndex].value).toEqual(
|
||||
'Core Product-Firefox'
|
||||
);
|
||||
|
||||
propogateQueryParamsToSelects('?team=Marketing');
|
||||
expect(select.options[select.selectedIndex].value).toEqual('Marketing');
|
||||
});
|
||||
|
||||
it('should handle multiple parameters', function () {
|
||||
const location = document.getElementById('id_location');
|
||||
const team = document.getElementById('id_team');
|
||||
|
||||
propogateQueryParamsToSelects(
|
||||
'?team=Core%20Product-Firefox&location=Berlin%20Office'
|
||||
);
|
||||
|
||||
expect(location.options[location.selectedIndex].value).toEqual(
|
||||
'Berlin Office'
|
||||
);
|
||||
|
||||
expect(team.options[team.selectedIndex].value).toEqual(
|
||||
'Core Product-Firefox'
|
||||
);
|
||||
});
|
||||
|
||||
it('should no nothing if a corresponding input is not found', function () {
|
||||
const location = document.getElementById('id_location');
|
||||
const team = document.getElementById('id_team');
|
||||
|
||||
propogateQueryParamsToSelects('?position_type=Intern');
|
||||
|
||||
expect(location.options[location.selectedIndex].value).toEqual('');
|
||||
expect(team.options[team.selectedIndex].value).toEqual('');
|
||||
});
|
||||
|
||||
it('should do nothing is a corresponding value is not found', function () {
|
||||
const location = document.getElementById('id_location');
|
||||
const team = document.getElementById('id_team');
|
||||
|
||||
propogateQueryParamsToSelects('?location=nowhere');
|
||||
propogateQueryParamsToSelects('?team=thedude');
|
||||
|
||||
expect(location.options[location.selectedIndex].value).toEqual('');
|
||||
expect(team.options[team.selectedIndex].value).toEqual('');
|
||||
});
|
||||
});
|
|
@ -14,8 +14,6 @@
|
|||
/* global sinon */
|
||||
|
||||
describe('all-downloads-unified.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('getSelectOption', function () {
|
||||
const select = `<select id="select-product" class="c-selection-input">
|
||||
<option value="desktop_developer">Firefox Developer Edition</option>
|
||||
|
|
|
@ -10,8 +10,6 @@
|
|||
*/
|
||||
|
||||
describe('thanks.js', function () {
|
||||
'use strict';
|
||||
|
||||
describe('shouldAutoDownload', function () {
|
||||
it('should return true for supported platforms', function () {
|
||||
expect(
|
||||
|
|
Загрузка…
Ссылка в новой задаче