[fix bug 1158842] Add front-end functional tests to bedrock using CasperJS

This commit is contained in:
Alex Gibson 2015-04-27 12:19:02 +01:00
Родитель 78e7f03e7c
Коммит 41be1a766e
12 изменённых файлов: 693 добавлений и 1 удалений

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

@ -28,6 +28,7 @@ Contents
php
l10n
coding
testing
javascript-libs
contribute
grunt

82
docs/testing.rst Normal file
Просмотреть файл

@ -0,0 +1,82 @@
.. 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 http://mozilla.org/MPL/2.0/.
.. _testing:
==================
Functional testing
==================
Bedrock runs a suite of front-end functional tests using `CasperJS`_, which is powered by
a headless web browser called `PhantomJS`_.
Installation
------------
The specific version of CasperJS and PhantomJS needed to run the tests can be installed by running the
following command from the root directory of the project::
npm install
.. Note::
You may have already run ``npm install`` when initially setting up bedrock to run locally,
in which case you can skip running this command again.
To confirm that you have PhantomJS installed correctly, you can run the following command::
./node_modules/.bin/phantomjs --version
This should output the following::
1.9.8
To confirm that you have CasperJS installed and running together with the correct version
of PhantomJS, you can run the following command::
PHANTOMJS_EXECUTABLE=./node_modules/.bin/phantomjs ./node_modules/.bin/casperjs
You should now see output similar to::
CasperJS version 1.1.0-beta3 at /bedrock/node_modules/casperjs, using phantomjs version 1.9.8
Running tests
-------------
To run the functional tests against your local bedrock instance, type::
npm test
This is a shortcut for the following command::
PHANTOMJS_EXECUTABLE=./node_modules/.bin/phantomjs ./node_modules/.bin/casperjs test tests/functional --config=tests/config.json
This will run all test files found in the ``tests/functional`` directory and assumes you
have bedrock running at ``localhost`` on port ```8000``
To run a single test suite you can tell CasperJS to excecute a specific file e.g.::
.../casperjs test tests/functional/home.js
You can also easily run the tests against any bedrock environment by specifying the domain.
For example, to run all functional tests against dev::
.../casperjs test tests/functional --domain=https://www-dev.allizom.org ...
.. Note::
Make sure to include the configuration file ``--config=tests/config.json`` when running tests.
This is needed for PhantomJS to open certain URLs served over https.
Debugging
---------
You can enable logging on the command line by passing the following additional flags::
--verbose --log-level=debug
.. _CasperJS: http://casperjs.org/
.. _PhantomJS: http://phantomjs.org/
.. _PhantomJS 1.9.8: https://bitbucket.org/ariya/phantomjs/downloads

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

@ -4,7 +4,7 @@
"description": "Making mozilla.org awesome, one pebble at a time",
"private": true,
"scripts": {
"test": "grunt test"
"test": "PHANTOMJS_EXECUTABLE=./node_modules/.bin/phantomjs ./node_modules/.bin/casperjs test tests/functional --config=tests/config.json"
},
"dependencies": {
"cssmin": "0.4.3",
@ -21,6 +21,7 @@
"url": "https://bugzilla.mozilla.org/"
},
"devDependencies": {
"casperjs": "^1.1.0-beta3",
"grunt": "~0.4.2",
"grunt-cli": "~0.1.11",
"grunt-contrib-clean": "^0.6.0",
@ -40,6 +41,7 @@
"karma-requirejs": "~0.2.1",
"karma-script-launcher": "~0.1.0",
"load-grunt-tasks": "^1.0.0",
"phantomjs": "^1.9.15",
"requirejs": "~2.1.11",
"sinon": "~1.8.2"
}

4
tests/config.json Normal file
Просмотреть файл

@ -0,0 +1,4 @@
{
"sslProtocol": "any",
"ignoreSslErrors": true
}

115
tests/functional/android.js Normal file
Просмотреть файл

@ -0,0 +1,115 @@
/* 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 http://mozilla.org/MPL/2.0/. */
/* global casper */
'use strict';
var config = require('../lib/config');
var helpers = require('../lib/helpers');
var path = '/firefox/android/';
var url = config.base() + path;
casper.test.begin('Android, Elements: ' + url, 6, function suite(test) {
casper.start(url, function() {
test.assertHttpStatus(200);
test.assertElementCount('.send-to', 3, '3 modal buttons exist');
test.assertElementCount('.dl-button', 3, '3 Google Play Store buttons exist');
test.assertExists('#customize-accordion', 'Customize accordion exists');
test.assertElementCount('#privacy ul li > a', 4, 'Privacy links exist');
test.assertExists('#newsletter-form', 'Newsletter form exists');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Android, Expand accordion: ' + url, 2, function suite(test) {
casper.start(url, function() {
test.assertNotVisible('#addons-panel', 'Addons accordion panel is closed initially');
this.click('#addons-header');
});
casper.waitUntilVisible('#addons-panel', function() {
test.assert(true, 'Addons accordion panel opened successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Android, Accordion navigation: ' + url, 4, function suite(test) {
casper.start(url, function() {
test.assertNotVisible('#addons-panel', 'Addons accordion panel is closed initially');
this.click('#customize-next');
});
casper.waitUntilVisible('#addons-panel', function() {
test.assert(true, 'Addons accordion panel opened successfully');
this.wait(1000, function() {
this.click('#customize-prev');
});
});
casper.waitUntilVisible('#broadcast-panel', function() {
test.assert(true, 'Broadcast accordion panel opened successfully');
test.assertNotVisible('#addons-panel', 'Addons accordion panel closed again');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Android, Send to device modal: ' + url, 3, function suite(test) {
casper.start(url, function() {
this.click('#intro .send-to');
});
casper.waitUntilVisible('#modal', function() {
test.assert(true, 'Modal opened successfully');
test.assertExists('#modal #send-to-device-form', 'Send to device form is displayed in modal');
this.click('#modal-close');
});
casper.waitWhileVisible('#modal', function() {
test.assert(true, 'Modal closed successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Android, Send to device form submission: ' + url, 2, function suite(test) {
casper.start(url, function() {
this.click('#intro .send-to');
});
casper.waitUntilVisible('#modal #send-to-device-form', function() {
test.assertNotVisible('#modal #send-to-device-form .thank-you', 'Thank you message is not visible initially');
this.fillSelectors('#modal #send-to-device-form', {
'#id-input': 'noreply@mozilla.com'
}, true);
});
casper.waitUntilVisible('#modal #send-to-device-form .thank-you', function() {
test.assert(true, 'Send to device form submitted successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});

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

@ -0,0 +1,130 @@
/* 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 http://mozilla.org/MPL/2.0/. */
/* global casper */
'use strict';
var config = require('../lib/config');
var helpers = require('../lib/helpers');
var path = '/firefox/os/2.0/';
var url = config.base() + path;
casper.test.begin('Family Navigation, Elements: ' + url, 6, function suite(test) {
casper.start(url, function() {
test.assertVisible('#fxfamilynav-primary > li > a', 'Primary navigation links are visible');
test.assertVisible('.primary-link.selected[data-id="os"]', 'Firefox OS primary link is set as default');
test.assertVisible('#os-subnav a.selected[data-id="os-index"]', 'Firefox OS secondary links are visible');
test.assertNotVisible('#fxfamilynav-tertiarynav', 'Tertiary navigation is not visible');
test.assertVisible('#fxfamilynav-tertiarynav-trigger', 'Tertiary trigger is not visible');
test.assertExists('#fxfamilynav-cta-wrapper .primary-cta-phone', 'CTA button is inserted into navigation');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Family Navigation, Open tertiary link menu: ' + url, 1, function suite(test) {
casper.start(url, function() {
this.mouse.click('#fxfamilynav-tertiarynav-trigger');
});
casper.waitUntilVisible('#fxfamilynav-tertiarynav', function() {
test.assert(true, 'Tertiary links opened successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Family Navigation, Link hover: ' + url, 2, function suite(test) {
casper.start(url, function() {
this.mouse.move('#fxfamilynav-primary > li > a[data-id="android"]');
});
casper.waitUntilVisible('#android-subnav', function() {
test.assert(true, 'Secondary nav shows on hover');
this.mouse.move(0,0);
});
casper.waitWhileVisible('#android-subnav', function() {
test.assert(true, 'Secondary nav hides on leave');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Family Navigation, Scroll down: ' + url, 2, function suite(test) {
casper.start(url, function() {
this.scrollToBottom();
});
casper.waitForSelector('#fxfamilynav-header.stuck', function() {
test.assert(true, 'Navigation is sticky when scrolled');
var buttonVisibility = this.evaluate(function() {
return $('#fxfamilynav-cta-wrapper').css('visibility') === 'visible';
});
test.assertTrue(buttonVisibility, 'CTA button is visible when scrolled');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Family Navigation, Scroll up: ' + url, 2, function suite(test) {
casper.start(url, function() {
this.scrollToBottom();
});
casper.waitForSelector('#fxfamilynav-header.stuck', function() {
this.scrollTo(0,0);
});
casper.waitWhileSelector('#fxfamilynav-header.stuck', function() {
test.assert(true, 'Navigation is no longer sticky when scrolled back to top');
var buttonVisibility = this.evaluate(function() {
return $('#fxfamilynav-cta-wrapper').css('visibility') === 'visible';
});
test.assertFalsy(buttonVisibility, 'CTA button is no longer visible when scrolled up');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Family Navigation, Mobile interaction: ' + url, 4, function suite(test) {
casper.options.viewportSize = config.viewport.mobile;
casper.start(url, function() {
this.scrollToBottom();
});
casper.then(function() {
test.assertDoesntExist('#fxfamilynav-header.stuck', 'Navigation is not sticky when scrolled');
test.assertNotVisible('#fxfamilynav-primary > li > .subnav', 'Sub navigation is not visible');
test.assertNotVisible('#fxfamilynav-tertiarynav', 'Tertiary navigation is not visible');
test.assertNotVisible('#fxfamilynav-tertiarynav-trigger', 'Tertiary trigger is not visible');
});
casper.run(function() {
test.done();
helpers.done();
});
});

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

@ -0,0 +1,59 @@
/* 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 http://mozilla.org/MPL/2.0/. */
/* global casper */
'use strict';
var config = require('../lib/config');
var helpers = require('../lib/helpers');
var path = '/firefox/new/';
var url = config.base() + path;
casper.test.begin('Firefox New, Elements: ' + url, 4, function suite(test) {
casper.start(url, function() {
test.assertHttpStatus(200);
test.assertVisible('#features-download .download-button', 'Download button exists');
test.assertExists('#direct-download-link', 'Direct download link exists');
test.assertVisible('#firefox-screenshot > img', 'Browser screenshot exists');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Firefox New, Direct download link: ' + url, 1, function suite(test) {
casper.start(url, function() {
var href = this.getElementAttribute('#direct-download-link', 'href');
test.assert(href.indexOf('https://download.mozilla.org/') !== -1, 'Download link has a valid URL');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Firefox New, Click download button: ' + url, 2, function suite(test) {
casper.start(url, function() {
this.click('#features-download .download-list .os_win .download-link');
});
casper.waitForUrl(/#download-fx/, function() {
test.assert(true, 'Scene 2 triggered');
});
casper.waitForResource(/https:\/\/download\.mozilla\.org\//, function() {
test.assert(true, 'Download started successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});

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

@ -0,0 +1,95 @@
/* 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 http://mozilla.org/MPL/2.0/. */
/* global casper */
'use strict';
var config = require('../lib/config');
var helpers = require('../lib/helpers');
var path = '/firefox/os/devices/';
var url = config.base() + path;
casper.test.begin('Firefox OS Devices, Elements: ' + url, 7, function suite(test) {
casper.start(url, function() {
test.assertHttpStatus(200);
test.assertExists('#location', 'Location picker exists');
test.assertExists('#device-nav', 'Device navigation exists');
test.assertExists('#smartphones', 'Smartphone section exists');
test.assertExists('#tvs', 'Television section exists');
test.assertExists('#provider-links', 'Provider links exist');
test.assertElementCount('.purchase-button', 2, 'Purchase buttons exist');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Firefox OS Devices, Click phone: ' + url, 2, function suite(test) {
casper.start(url, function() {
this.click('a[href="#alcatel_onetouchfire"]');
});
casper.waitUntilVisible('#alcatel_onetouchfire', function() {
test.assert(true, 'Phone detail expanded successfully');
this.click('#alcatel_onetouchfire .device-detail-close');
});
casper.waitWhileVisible('#alcatel_onetouchfire', function() {
test.assert(true, 'Phone detail collapsed successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Firefox OS Devices, Click tab navigation: ' + url, 1, function suite(test) {
casper.start(url, function() {
this.click('a[href="#alcatel_onetouchfire"]');
});
casper.waitUntilVisible('#alcatel_onetouchfire-specifications-tab', function() {
this.click('#alcatel_onetouchfire-specifications-tab');
});
casper.waitUntilVisible('#page-alcatel_onetouchfire-specifications', function() {
test.assert(true, 'Phone specs shown successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Firefox OS Devices, Purchase modal: ' + url, 3, function suite(test) {
casper.start(url, function() {
this.fillSelectors('#form-locations', {
'#location': 'ar'
});
this.click('.purchase-button');
});
casper.waitUntilVisible('#modal', function() {
test.assert(true, 'Purchase modal opened successfully');
test.assertExists('#modal #get-device');
this.click('#modal-close');
});
casper.waitWhileVisible('#modal', function() {
test.assert(true, 'Purchase modal closed successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});

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

@ -0,0 +1,101 @@
/* 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 http://mozilla.org/MPL/2.0/. */
/* global casper */
'use strict';
var config = require('../lib/config');
var helpers = require('../lib/helpers');
var path = '/firefox/os/2.0/';
var url = config.base() + path;
casper.test.begin('Firefox OS, Elements: ' + url, 11, function suite(test) {
casper.start(url, function() {
test.assertHttpStatus(200);
test.assertExists('.fxos-cta > h1 > img', 'Firefox OS wordmark exists');
test.assertExists('.phone-container > img', 'Firefox OS phone image exists');
test.assertElementCount('.primary-cta-phone', 2, 'Get phone CTA buttons exist');
test.assertElementCount('.primary-cta-signup', 2, 'Newsletter CTA buttons exist');
test.assertExists('.cta-button.marketplace', 'Marketplace CTA button exists');
test.assertElementCount('.fxos-news a', 4, 'Firefox OS news links exist');
test.assertExists('.fxos-community .more', 'Community learn more links exist');
test.assertElementCount('.fxos-help a', 7, 'Firefox OS help links exist');
test.assertExists('#provider-links', 'Provider links exist');
test.assertExists('#newsletter-form', 'Newsletter form exists');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Firefox OS, Apps for all you do: ' + url, 3, function suite(test) {
casper.start(url, function() {
this.click('#connected');
});
casper.waitForSelector('#connected.active-state', function() {
test.assert(true, 'Navigation state updated');
});
casper.waitForSelector('.fxos-apps .apps .organized.fade', function() {
test.assert(true, 'Organized icons faded out');
});
casper.waitForSelector('.fxos-apps .apps .entertained.fade', function() {
test.assert(true, 'Entertained icons faded out');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Firefox OS, Newsletter modal: ' + url, 3, function suite(test) {
casper.start(url, function() {
this.click('.primary-cta-signup');
});
casper.waitUntilVisible('#modal', function() {
test.assert(true, 'Modal opened successfully');
test.assertExists('#modal #newsletter-form');
this.click('#modal-close');
});
casper.waitWhileVisible('#modal', function() {
test.assert(true, 'Modal closed successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Firefox OS, Get phone modal: ' + url, 3, function suite(test) {
casper.start(url, function() {
this.click('.primary-cta-phone');
});
casper.waitUntilVisible('#modal', function() {
test.assert(true, 'Modal opened successfully');
test.assertExists('#modal #get-device');
this.click('#modal-close');
});
casper.waitWhileVisible('#modal', function() {
test.assert(true, 'Modal closed successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});

50
tests/functional/home.js Normal file
Просмотреть файл

@ -0,0 +1,50 @@
/* 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 http://mozilla.org/MPL/2.0/. */
/* global casper */
'use strict';
var config = require('../lib/config');
var helpers = require('../lib/helpers');
var path = '/';
var url = config.base() + path;
casper.test.begin('Home page, Elements: ' + url, 9, function suite(test) {
casper.start(url, function() {
test.assertHttpStatus(200);
test.assertExists('.main-header h1', 'Mozilla wordmark exists');
test.assertElementCount('.promo-grid > li', 16, '16 home page promos exist');
test.assertExists('#community .contribute-btn', 'Contribute CTA button exists');
test.assertExists('#upcoming-events .featured-event', 'Featured event exists');
test.assertElementCount('#upcoming-events .events-list > li > a', 3, 'Upcoming events exist');
test.assertExists('#upcoming-events .more-large', 'Events CTA button exists');
test.assertElementCount('#secondary-links ul > li > a', 3, 'Secondary links exist');
test.assertExists('.footer-newsletter-form', 'Newsletter form exists');
});
casper.run(function() {
test.done();
helpers.done();
});
});
casper.test.begin('Home page, Newsletter submission: ' + url, 1, function suite(test) {
casper.start(url, function() {
this.fillSelectors('#mozorg-newsletter-form', {
'#id_email': 'noreply@mozilla.com',
'#id_privacy': true
}, true);
});
casper.waitForUrl(/sign-up-for-mozilla/, function() {
test.assertUrlMatch(/sign-up-for-mozilla/, 'Newsletter submitted successfully');
});
casper.run(function() {
test.done();
helpers.done();
});
});

37
tests/lib/config.js Normal file
Просмотреть файл

@ -0,0 +1,37 @@
/* 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 http://mozilla.org/MPL/2.0/. */
/* global casper */
'use strict';
var domain = casper.cli.get('domain') || 'http://localhost:8000';
var locale = casper.cli.get('locale') || 'en-US';
// common sandstone breakpoints for bedrock
var viewport = {
desktopWide: { width: 1400, height: 840},
desktop: { width: 1000, height: 600 },
tablet: { width: 760, height: 456 },
mobileLandscape: { width: 480, height: 300 },
mobile: { width: 320, height: 460 }
};
// set default viewport size to desktop
casper.options.viewportSize = viewport.desktop;
/*
* Returns the environment being tested
* plus the specified locale.
* e.g. 'http://localhost:8000/en-US'
*/
function base() {
return domain + '/' + locale;
}
module.exports = {
base: base,
locale: locale,
domain: domain,
viewport: viewport
};

16
tests/lib/helpers.js Normal file
Просмотреть файл

@ -0,0 +1,16 @@
/* 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 http://mozilla.org/MPL/2.0/. */
/* global casper */
'use strict';
var config = require('../lib/config');
function done() {
casper.options.viewportSize = config.viewport.desktop;
}
module.exports = {
done: done
};