Add JS tests for /careers job filtering (Fixes #11080)

This commit is contained in:
Alex Gibson 2022-01-11 10:27:46 +00:00
Родитель 9b29dd8d1e
Коммит 4deaca396d
32 изменённых файлов: 582 добавлений и 300 удалений

Просмотреть файл

@ -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&amp;form_type=button&amp;entrypoint=mozilla.org-firefox-welcome-2&amp;utm_source=mozilla.org-firefox-welcome-2&amp;utm_campaign=welcome-2-pocket&amp;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 &amp; Organizational Development">People &amp; 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 &amp; Organizational Development">People &amp; 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(