Compare commits

..

32 Commits

Author SHA1 Message Date
Andrew Mee
0a14a321d2 Version 1.12.0 2016-05-26 15:45:03 +01:00
Rowan Manning
ae6208d87e Support Node.js 0.10–6 (#132)
* Update dependencies

* Switch from jsdom to cheerio

* Update the supported Node.js versions

* Recompile the client-side CSS and JS
2016-05-26 15:33:58 +01:00
Rowan Manning
6bfb4f72d9 Version 1.11.0 2016-05-23 19:13:46 +01:00
Rowan Manning
b3a28bb1e8 Update the README based on review comments 2016-05-23 19:07:23 +01:00
Rowan Manning
4bd5613427 Update references to the new Pa11y organisation 2016-05-23 19:00:22 +01:00
Rowan Manning
17b04daae0 Document how to use environment variables 2016-05-22 13:10:52 +01:00
Rowan Manning
c15cc32542 Fix some lint errors 2016-05-22 13:05:08 +01:00
Josh Chisholm
08a1a9060f allow configuration by environment variables 2016-05-22 13:01:05 +01:00
Rowan Manning
1358d0f38d Add a changelog 2016-05-22 12:58:13 +01:00
Rowan Manning
4eb2fba6aa Bring the README into line with newer projects 2016-05-22 11:45:57 +01:00
Rowan Manning
6a1e06a435 Update to the latest version of the webservice 2016-05-22 11:39:00 +01:00
Rayraegah
5bfc958270 Revert "if wait value is null or undefined, set it to 0"
This reverts commit 7ce4127908.
2016-05-21 16:14:53 +01:00
Rayraegah
7ce4127908 if wait value is null or undefined, set it to 0 2016-05-21 15:43:58 +01:00
Rayraegah
2a15773e04 Added "wait" field in new task and edit task pages
Added UI control to set `wait` option in pa11y script. Updated test
cases.

[pa11y-dashboard issue
#127](https://github.com/springernature/pa11y-dashboard/issues/127)
2016-05-21 14:13:38 +01:00
Hollie Kay
aa3f0eb727 Merge pull request #128 from springernature/sidekick-update
Sidekick update
2016-05-20 16:21:35 +01:00
hollsk
aad26d8cd1 add horizontal rules 2016-05-20 16:20:47 +01:00
hollsk
fbe6875cf0 moar space 2016-05-20 15:45:37 +01:00
hollsk
c0ee197138 add sidekick update 2016-05-20 15:44:19 +01:00
hollsk
1723472b93 Version 1.10.0 2016-05-18 21:30:11 +01:00
hollsk
f8f1e8c15d merge pull request #124 from springernature/make-task-clickable 2016-05-18 21:23:13 +01:00
hollsk
95869aae56 merge pull request #125 from springernature/focus-filter 2016-05-18 21:22:34 +01:00
hollsk
83d8503ef6 automatically focus on the filter input box when you select the filter 2016-05-18 14:16:38 +01:00
Hollie Kay
1e5e310b4e Merge pull request #122 from springernature/tweak-install-steps
hopefully make the steps a littler clearer/more obvious
2016-05-18 14:07:54 +01:00
hollsk
7e8d6b122b make task URL clickable 2016-05-18 13:47:42 +01:00
Hollie Kay
a2e7e1ffc4 Merge pull request #123 from springernature/update-copyright-date
ITS BEEN THREE YEARS
2016-05-13 13:41:28 +01:00
hollsk
feacb2601d Removed line about webservice 2016-05-13 10:54:05 +01:00
hollsk
358278ad00 ITS BEEN THREE YEARS 2016-05-11 14:33:24 +01:00
hollsk
a94a9ed465 hopefully make the steps a littler clearer/more obvious 2016-05-11 14:25:02 +01:00
Hollie Kay
0918403876 Merge pull request #120 from springernature/add-unas-post
create a resources section and add a link to it
2016-05-11 14:21:36 +01:00
hollsk
e7b7792f89 create a resources section and add a link to it 2016-05-11 11:12:36 +01:00
Hollie Kay
46b2fcd3af Merge pull request #117 from una/master
docs updates
2016-05-04 10:40:42 +01:00
Una Kravets
c298668b76 docs updates 2016-04-29 14:57:27 -05:00
28 changed files with 499 additions and 209 deletions

View File

@@ -3,16 +3,11 @@
language: node_js language: node_js
matrix: matrix:
include: include:
# Run tests in Node.js 0.10 (unsupported)
- node_js: '0.10' - node_js: '0.10'
# Run tests in Node.js 0.12
- node_js: '0.12' - node_js: '0.12'
- node_js: '4'
# Allow Node.js 0.10 to fail it's unsupported - node_js: '5'
allow_failures: - node_js: '6'
- node_js: '0.10'
# Build only master (and pull-requests) # Build only master (and pull-requests)
branches: branches:

185
CHANGELOG.md Normal file
View File

@@ -0,0 +1,185 @@
# Changelog
## 1.12.0 (2016-05-26)
* Update Node.js version support to 0.106.0
* Update dependencies
* body-parser: added at ~1.15
* chalk: ~0.2 to ~1.1
* compression: added at ~1.6
* express: ~3.4 to ~4.13
* express-hbs: ~0.2 to ~1.0
* moment: ~2.2 to ~2.13
* pa11y-webservice: ~1.10 to ~1.11
* pa11y-webservice-client-node: ~1.1 to ~1.2
* bower: ~1.2 to ~1.7
* cheerio: added at ~0.20
* jsdom: removed
* request: ~2.27 to ^2
* uglify-js: ~2.4 to ~2.6
## 1.11.0 (2016-05-23)
* Add the ability to configure task wait times
* Allow configuration by environment variables
* Update repository references to the new Pa11y organisation
* Add a changelog
## 1.10.0 (2016-05-18)
* Automatically focus on the filter input box when you select the filter
* Make a task URL clickable
* Tweak the documentation to make it more usable
* Add a resources section to the README
## 1.9.0 (2016-04-25)
* Show errors' context and selector on the results page
* Add context and selector to CSV output
* Fix lint errors
* Switch from Grunt to Make
* Add a `SIGINT` handler
* Update dependencies
* pa11y-webservice: ~1.6 to ~1.8
## 1.8.2 (2016-02-10)
* Update the license in the footer
## 1.8.1 (2016-02-10)
* Update repository references to springernature
## 1.8.0 (2016-02-04)
* Make the graph more accessible to color-blind users
* Fix lint errors
## 1.7.0 (2016-01-29)
* Hide the date list from individual result pages
* Make the date selector properly keyboard accessible
* Change the options button into a more accessible list
* Make the errors/warnings/notices lists keyboard accessible
## 1.6.1 (2016-01-26)
* Add keyboard access for filters
* Fix lint errors
## 1.6.0 (2015-08-20)
* Hide all graph data except for errors by default
## 1.5.0 (2015-07-06)
* Add the ability to use HTTP basic auth with task URLs
* Update dependencies
* pa11y-webservice: ~1.5 to ~1.6
## 1.4.0 (2015-07-02)
* Add the ability to set a per-task timeout
## 1.3.2 (2015-01-17)
* Update dependencies
* pa11y-webservice: ~1.3 to ~1.4
## 1.3.1 (2014-03-05)
* Fix the URL filter position when in demo mode
## 1.3.0 (2014-03-04)
* Add filtering of tasks on the home page
* Add the ability to ignore certain rules
* Add the ability to ignore a rule from the result page
* Tweak the display of task cards
## 1.2.3 (2014-01-13)
* Fix CSV export for the OS X version of Excel
## 1.2.2 (2014-01-09)
* Fix spacing issues when the graph is not visible
* Add notes on publishing a release
## 1.2.1 (2014-01-08)
* Fix dropdown positioning in Internet Explorer 7 and 8
## 1.2.0 (2013-12-12)
* Add HTML Codesniffer links on the results page
* Display the ignore rules for results on the results page
* Link the breadcrumbs on task sub-pages
* Fix an issue with saving empty ignore rules
* Cache-bust the CSS and JavaScript
* Add the ability to edit tasks
* Fix lint errors
* Tweaks to the display of the graphs
* Update dependencies
* pa11y-webservice: ~1.1 to ~1.2
* pa11y-webservice-client-node: ~1.0 to ~1.1
## 1.1.0 (2013-11-22)
* Add a functional test suite
* Allow the webservice to run automatically
* Documentation improvements
* Add a Travis config
* Fix lint errors
## 1.0.0 (2013-11-19)
* Initial stable release
* Add the ability to set a site-wide message
* Add a demo mode for demo/public-facing sites
* Disable search engine indexing by default
* Tweak the task header at smaller screen sizes
* Make checkboxes on the graph WCAG2AA compliant
* Make checkbox inputs and labels WCAG2AA compliant on new URL page
* Colour changes to ensure there are no contrast issues
* Make the copy more consistent
* Update screenshots
* Update dependencies
* pa11y-webservice-client-node: 1.0.0-beta.7 to ~1.0
## 1.0.0-beta.3 pre-release (2013-11-12)
* Fix lint errors
* Add descriptive labels to tasks
* Add a name field to "New URL" form
* Add a WCAG 2.0 link to the footer
* Tweak the layout at smaller screen sizes
* Notify users when there are no ignored rules
* Fix the expires headers for front end assets
* Move from Make to Grunt
* Compress static files
* Minify the site JavaScript
* Compile LESS files with grunt
* Add a watch task to recompile assets on change
* Commit compiled front-end code to the repo
* Add development instructions
* Update screenshots
* Update dependencies
* pa11y-webservice-client-node: 1.0.0-beta.4 to 1.0.0-beta.7
## 1.0.0-beta.2 pre-release (2013-10-04)
* Add screenshots to the README
* Fix margins
* Add bower package management
* Stop the graph from appearing if there's only one result
* Add the ability to run tasks ad-hoc
* Add more useful information to the footer
* General copy edits
* Update dependencies
* pa11y-webservice-client-node: 1.0.0-beta.3 to 1.0.0-beta.4
## 1.0.0-beta.1 pre-release (2013-09-27)
* Initial release

View File

@@ -3,9 +3,16 @@ pa11y-dashboard
pa11y-dashboard is a web interface to the [pa11y][pa11y] accessibility reporter; allowing you to focus on *fixing* issues rather than hunting them down. pa11y-dashboard is a web interface to the [pa11y][pa11y] accessibility reporter; allowing you to focus on *fixing* issues rather than hunting them down.
**Current Version:** *1.9.0* ![Version][shield-version]
**Build Status:** [![Build Status][travis-img]][travis] [![Node.js version support][shield-node]][info-node]
**Node Version Support:** *0.10* [![Build status][shield-build]][info-build]
[![GPL-3.0 licensed][shield-license]][info-license]
---
✨ 🔜 ✨ The Pa11y team is very excited to announce plans for the successor to pa11y-dashboard and pa11y-webservice, codename "Sidekick". Help us define the features that you want to see by visiting the [proposal][sidekick-proposal]. ✨
---
![dashboard](https://f.cloud.github.com/assets/1225142/1549567/f0361e72-4de8-11e3-8d14-3fe6900cc15d.jpg) ![dashboard](https://f.cloud.github.com/assets/1225142/1549567/f0361e72-4de8-11e3-8d14-3fe6900cc15d.jpg)
@@ -15,11 +22,25 @@ pa11y-dashboard is a web interface to the [pa11y][pa11y] accessibility reporter;
Setup Setup
----- -----
pa11y-dashboard requires [Node.js][node] 0.10+, [PhantomJS][phantom], and [MongoDB][mongo] to be installed. See the [pa11y][pa11y] and [pa11y-webservice][pa11y-webservice] documentation for more information on these dependencies. pa11y-dashboard requires [Node.js][node] 0.10+ and [PhantomJS][phantom]. See the [pa11y][pa11y] documentation for detailed instructions on how to install these dependencies on your operating system.
You'll then need to clone this repo locally and install dependencies with `npm install`. You'll also need to have [MongoDB][mongo] installed and running. See the [MongoDB install guide][mongo-install] for more information on this.
Once you have a local clone, you'll need to copy some sample configuration files in order to run the application. From within the repo, run the following commands: You'll then need to clone this repo locally and install dependencies with `npm install`. Now you need to add some configuration before you can run the application. We can do this in two ways:
### Option 1: Using Environment Variables
Each configuration can be set with an environment variable rather than a config file. For example to run the application on port `8080` you can use the following:
```sh
PORT=8080 node index.js
```
The [available configurations are documented here](#configurations).
### Option 2: Using Config Files
You'll need to copy and modify different config files depending on your environment (set with `NODE_ENV`):
```sh ```sh
cp config/development.sample.json config/development.json cp config/development.sample.json config/development.json
@@ -27,17 +48,15 @@ cp config/production.sample.json config/production.json
cp config/test.sample.json config/test.json cp config/test.sample.json config/test.json
``` ```
Each of these files defines configurations for a different environment. If you're just running the application locally, then you should be OK with just development configurations. The [available configurations are documented here](#configurations). Each of these files defines configurations for a different environment. If you're just running the application locally, then you should be OK with just development and test configurations. The [available configurations are documented here](#configurations).
Now that you've got your application configured, you can run in each mode with the following commands: Now that you've got your application configured, make sure you have a MongoDB server running with the `mongod` command in another terminal window. You can run in each mode by changing the `NODE_ENV` environment variable:
```sh ```sh
NODE_ENV=production node index.js # Run in production NODE_ENV=development node index.js
NODE_ENV=development node index.js # Run in development
NODE_ENV=test node index.js # Run in test
``` ```
Check the [development instructions](#development) for more information about running locally (and restarting automatically when files change). See [development instructions](#development) for more information about running locally (and restarting automatically when files change).
Configurations Configurations
@@ -46,19 +65,19 @@ Configurations
The boot configurations for pa11y-dashboard are as follows. Look at the sample JSON files in the repo for example usage. The boot configurations for pa11y-dashboard are as follows. Look at the sample JSON files in the repo for example usage.
### port ### port
*(number)* The port to run the application on. *(number)* The port to run the application on. Set via a config file or the `PORT` environment variable.
### noindex ### noindex
*(boolean)* If set to `true` (default), the dashboard will not be indexed by search engines. Set to `false` to allow indexing. *(boolean)* If set to `true` (default), the dashboard will not be indexed by search engines. Set to `false` to allow indexing. Set via a config file or the `NOINDEX` environment variable.
### readonly ### readonly
*(boolean)* If set to `true`, users will not be able to add, delete or run URLs (defaults to `false`). *(boolean)* If set to `true`, users will not be able to add, delete or run URLs (defaults to `false`). Set via a config file or the `READONLY` environment variable.
### siteMessage ### siteMessage
*(string)* A message to display prominently on the site home page. Defaults to `null`. *(string)* A message to display prominently on the site home page. Defaults to `null`.
### webservice ### webservice
This can either be an object containing [pa11y-webservice configurations][pa11y-webservice-config], or a string which is the base URL of a [pa11y-webservice][pa11y-webservice] instance you are running separately. This can either be an object containing [pa11y-webservice configurations][pa11y-webservice-config], or a string which is the base URL of a [pa11y-webservice][pa11y-webservice] instance you are running separately. If using environment variables, prefix the webservice vars with `WEBSERVICE_`.
Development Development
@@ -85,25 +104,40 @@ Code with lint errors or failing tests will not be accepted, please use the buil
To compile the client-side JavaScript and CSS, you'll need the following commands. Compiled code is committed to the repository. To compile the client-side JavaScript and CSS, you'll need the following commands. Compiled code is committed to the repository.
```sh ```sh
make css # Compile the site CSS from LESS files make less # Compile the site CSS from LESS files
make uglify # Compile and uglify the client-side JavaScript make uglify # Compile and uglify the client-side JavaScript
``` ```
Useful Resources
-------
* [Setting up An Accessibility Dashboard from Scratch with Pa11y on DigitialOcean][resource-una-k]
License License
------- -------
[Copyright 2013 Springer Nature](LICENSE.txt). pa11y-dashboard is licensed under the [GNU General Public License 3.0][info-license].
pa11y-dashboard is licensed under the [GNU General Public License 3.0][gpl]. Copyright © 20132016, Springer Nature
[gpl]: http://www.gnu.org/licenses/gpl-3.0.html [gpl]: http://www.gnu.org/licenses/gpl-3.0.html
[mongo]: http://www.mongodb.org/ [mongo]: http://www.mongodb.org/
[mongo-install]: https://docs.mongodb.org/manual/installation/
[node]: http://nodejs.org/ [node]: http://nodejs.org/
[pa11y]: https://github.com/springernature/pa11y [pa11y]: https://github.com/pa11y/pa11y
[pa11y-webservice]: https://github.com/springernature/pa11y-webservice [pa11y-webservice-config]: https://github.com/pa11y/pa11y-webservice#configurations
[pa11y-webservice-config]: https://github.com/springernature/pa11y-webservice#configurations
[phantom]: http://phantomjs.org/ [phantom]: http://phantomjs.org/
[travis]: https://travis-ci.org/springernature/pa11y-dashboard [resource-una-k]: https://una.im/pa11y-dash/
[travis-img]: https://travis-ci.org/springernature/pa11y-dashboard.png?branch=master [sidekick-proposal]: https://github.com/pa11y/sidekick/blob/master/PROPOSAL.md
[travis]: https://travis-ci.org/pa11y/pa11y-dashboard
[travis-img]: https://travis-ci.org/pa11y/pa11y-dashboard.png?branch=master
[info-license]: LICENSE
[info-node]: package.json
[info-build]: https://travis-ci.org/pa11y/pa11y-dashboard
[shield-license]: https://img.shields.io/badge/license-GPL%203.0-blue.svg
[shield-node]: https://img.shields.io/badge/node.js%20support-0.106-brightgreen.svg
[shield-version]: https://img.shields.io/badge/version-1.12.0-blue.svg
[shield-build]: https://img.shields.io/travis/pa11y/pa11y-dashboard/master.svg

27
app.js
View File

@@ -15,6 +15,8 @@
'use strict'; 'use strict';
var bodyParser = require('body-parser');
var compression = require('compression');
var createClient = require('pa11y-webservice-client-node'); var createClient = require('pa11y-webservice-client-node');
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter;
var express = require('express'); var express = require('express');
@@ -40,7 +42,7 @@ function initApp(config, callback) {
app.webservice = createClient(webserviceUrl); app.webservice = createClient(webserviceUrl);
// Compression // Compression
app.express.use(express.compress()); app.express.use(compression());
// Public files // Public files
app.express.use(express.static(__dirname + '/public', { app.express.use(express.static(__dirname + '/public', {
@@ -49,26 +51,28 @@ function initApp(config, callback) {
// General express config // General express config
app.express.disable('x-powered-by'); app.express.disable('x-powered-by');
app.express.use(express.bodyParser()); app.express.use(bodyParser.urlencoded({
extended: true
}));
// View engine // View engine
app.express.set('views', __dirname + '/view'); app.express.engine('html', hbs.express4({
app.express.engine('html', hbs.express3({
extname: '.html', extname: '.html',
contentHelperName: 'content', contentHelperName: 'content',
layoutsDir: __dirname + '/view/layout', layoutsDir: __dirname + '/view/layout',
partialsDir: __dirname + '/view/partial', partialsDir: __dirname + '/view/partial',
defaultLayout: __dirname + '/view/layout/default' defaultLayout: __dirname + '/view/layout/default'
})); }));
app.express.set('views', __dirname + '/view');
app.express.set('view engine', 'html'); app.express.set('view engine', 'html');
// View helpers // View helpers
require('./view/helper/date')(hbs.registerHelper); require('./view/helper/date')(hbs);
require('./view/helper/string')(hbs.registerHelper); require('./view/helper/string')(hbs);
require('./view/helper/url')(hbs.registerHelper); require('./view/helper/url')(hbs);
// Populate view locals // Populate view locals
app.express.locals({ app.express.locals = {
lang: 'en', lang: 'en',
year: (new Date()).getFullYear(), year: (new Date()).getFullYear(),
version: pkg.version, version: pkg.version,
@@ -76,12 +80,13 @@ function initApp(config, callback) {
bugtracker: pkg.bugs, bugtracker: pkg.bugs,
noindex: config.noindex, noindex: config.noindex,
readonly: config.readonly, readonly: config.readonly,
siteMessage: config.siteMessage siteMessage: config.siteMessage,
}); settings: {}
};
app.express.use(function(req, res, next) { app.express.use(function(req, res, next) {
res.locals.isHomePage = (req.path === '/'); res.locals.isHomePage = (req.path === '/');
res.locals.host = req.host; res.locals.host = req.hostname;
next(); next();
}); });

41
config.js Normal file
View File

@@ -0,0 +1,41 @@
// This file is part of pa11y-dashboard.
//
// pa11y-dashboard is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// pa11y-dashboard is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with pa11y-dashboard. If not, see <http://www.gnu.org/licenses/>.
'use strict';
var fs = require('fs');
var jsonPath = './config/' + (process.env.NODE_ENV || 'development') + '.json';
if (fs.existsSync(jsonPath)) {
module.exports = require(jsonPath);
} else {
module.exports = {
port: Number(env('PORT', '4000')),
noindex: env('NOINDEX', 'true') === 'true',
readonly: env('READONLY', 'false') === 'true',
webservice: env('WEBSERVICE_URL', {
database: env('WEBSERVICE_DATABASE', 'mongodb://localhost/pa11y-webservice'),
host: env('WEBSERVICE_HOST', '0.0.0.0'),
port: Number(env('WEBSERVICE_PORT', '3000')),
cron: env('WEBSERVICE_CRON', false)
})
};
}
function env(name, defaultValue) {
var value = process.env[name];
return typeof value === 'string' ? value : defaultValue;
}

View File

@@ -16,7 +16,7 @@
'use strict'; 'use strict';
var chalk = require('chalk'); var chalk = require('chalk');
var config = require('./config/' + (process.env.NODE_ENV || 'development') + '.json'); var config = require('./config');
process.on('SIGINT', function() { process.on('SIGINT', function() {
console.log('\nGracefully shutting down from SIGINT (Ctrl-C)'); console.log('\nGracefully shutting down from SIGINT (Ctrl-C)');

View File

@@ -1,6 +1,6 @@
{ {
"name": "pa11y-dashboard", "name": "pa11y-dashboard",
"version": "1.9.0", "version": "1.12.0",
"private": true, "private": true,
"description": "pa11y-dashboard is a visual web interface to the pa11y accessibility reporter", "description": "pa11y-dashboard is a visual web interface to the pa11y accessibility reporter",
@@ -12,33 +12,35 @@
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/springernature/pa11y-dashboard.git" "url": "https://github.com/pa11y/pa11y-dashboard.git"
}, },
"homepage": "https://github.com/springernature/pa11y-dashboard", "homepage": "https://github.com/pa11y/pa11y-dashboard",
"bugs": "https://github.com/springernature/pa11y-dashboard/issues", "bugs": "https://github.com/pa11y/pa11y-dashboard/issues",
"engines": { "engines": {
"node": ">=0.10" "node": ">=0.10"
}, },
"dependencies": { "dependencies": {
"chalk": "~0.2", "body-parser": "~1.15",
"express": "~3.4", "chalk": "~1.1",
"express-hbs": "~0.2", "compression": "~1.6",
"moment": "~2.2", "express": "~4.13",
"pa11y-webservice": "~1.8", "express-hbs": "~1.0",
"pa11y-webservice-client-node": "~1.1", "moment": "~2.13",
"underscore": "~1.5" "pa11y-webservice": "~1.11",
"pa11y-webservice-client-node": "~1.2",
"underscore": "~1.8"
}, },
"devDependencies": { "devDependencies": {
"bower": "~1.2", "bower": "~1.7",
"cheerio": "~0.20",
"jscs": "^2", "jscs": "^2",
"jsdom": "^3",
"jshint": "^2", "jshint": "^2",
"less": "~1.5", "less": "~2.7",
"mocha": "^2", "mocha": "^2",
"proclaim": "^3", "proclaim": "^3",
"request": "~2.27", "request": "^2",
"uglify-js": "~2.4" "uglify-js": "~2.6"
}, },
"scripts": { "scripts": {

File diff suppressed because one or more lines are too long

12
public/js/site.min.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -42,6 +42,7 @@ function route(app) {
standard: req.body.standard, standard: req.body.standard,
ignore: req.body.ignore || [], ignore: req.body.ignore || [],
timeout: req.body.timeout, timeout: req.body.timeout,
wait: req.body.wait,
username: req.body.username, username: req.body.username,
password: req.body.password password: req.body.password
}; };

View File

@@ -60,6 +60,7 @@ function route(app) {
task.name = req.body.name; task.name = req.body.name;
task.ignore = req.body.ignore; task.ignore = req.body.ignore;
task.timeout = req.body.timeout; task.timeout = req.body.timeout;
task.wait = req.body.wait;
task.username = req.body.username; task.username = req.body.username;
task.password = req.body.password; task.password = req.body.password;
var standards = getStandards().map(function(standard) { var standards = getStandards().map(function(standard) {

View File

@@ -15,7 +15,7 @@
'use strict'; 'use strict';
var jsdom = require('jsdom'); var cheerio = require('cheerio');
var request = require('request'); var request = require('request');
module.exports = createNavigator; module.exports = createNavigator;
@@ -29,13 +29,12 @@ function createNavigator(baseUrl, store) {
store.request = null; store.request = null;
store.response = null; store.response = null;
store.status = null; store.status = null;
store.window = null;
request({ request({
url: baseUrl + opts.endpoint, url: baseUrl + opts.endpoint,
method: opts.method || 'GET', method: opts.method || 'GET',
body: opts.body, form: opts.form,
json: true, json: opts.json || false,
qs: opts.query, qs: opts.query,
followAllRedirects: true followAllRedirects: true
}, function(err, res, body) { }, function(err, res, body) {
@@ -46,19 +45,11 @@ function createNavigator(baseUrl, store) {
store.status = res.statusCode; store.status = res.statusCode;
if (opts.nonDom) { if (opts.nonDom) {
store.window = null;
store.dom = null; store.dom = null;
callback();
} else { } else {
jsdom.env( store.dom = cheerio.load(store.body);
store.body,
function(err, window) {
store.window = window;
store.dom = window.document;
callback();
}
);
} }
callback();
}); });

View File

@@ -17,7 +17,7 @@
var assert = require('proclaim'); var assert = require('proclaim');
describe('GET /', function() { describe.only('GET /', function() {
beforeEach(function(done) { beforeEach(function(done) {
var req = { var req = {
@@ -32,61 +32,61 @@ describe('GET /', function() {
}); });
it('should display an "Add new URL" button', function() { it('should display an "Add new URL" button', function() {
var elem = this.last.dom.querySelectorAll('[data-test=add-task]'); var elem = this.last.dom('[data-test=add-task]');
assert.strictEqual(elem.length, 1); assert.strictEqual(elem.length, 1);
assert.strictEqual(elem[0].getAttribute('href'), '/new'); assert.strictEqual(elem.eq(0).attr('href'), '/new');
}); });
it('should display all of the expected tasks', function() { it('should display all of the expected tasks', function() {
var tasks = this.last.dom.querySelectorAll('[data-test=task]'); var tasks = this.last.dom('[data-test=task]');
assert.strictEqual(tasks.length, 3); assert.strictEqual(tasks.length, 3);
assert.match(tasks[0].textContent, /npg home\s+\(wcag2aa\)/i); assert.match(tasks.eq(0).text(), /npg home\s+\(wcag2aa\)/i);
assert.match(tasks[1].textContent, /npg home\s+\(wcag2aaa\)/i); assert.match(tasks.eq(1).text(), /npg home\s+\(wcag2aaa\)/i);
assert.match(tasks[2].textContent, /nature news\s+\(section508\)/i); assert.match(tasks.eq(2).text(), /nature news\s+\(section508\)/i);
}); });
it('should have links to each task', function() { it('should have links to each task', function() {
var tasks = this.last.dom.querySelectorAll('[data-test=task]'); var tasks = this.last.dom('[data-test=task]');
assert.strictEqual(tasks[0].querySelectorAll('[href="/abc000000000000000000001"]').length, 1); assert.strictEqual(tasks.eq(0).find('[href="/abc000000000000000000001"]').length, 1);
assert.strictEqual(tasks[1].querySelectorAll('[href="/abc000000000000000000002"]').length, 1); assert.strictEqual(tasks.eq(1).find('[href="/abc000000000000000000002"]').length, 1);
assert.strictEqual(tasks[2].querySelectorAll('[href="/abc000000000000000000003"]').length, 1); assert.strictEqual(tasks.eq(2).find('[href="/abc000000000000000000003"]').length, 1);
}); });
it('should display an "Edit" button for each task', function() { it('should display an "Edit" button for each task', function() {
var tasks = this.last.dom.querySelectorAll('[data-test=task]'); var tasks = this.last.dom('[data-test=task]');
assert.strictEqual(tasks[0].querySelectorAll('[href="/abc000000000000000000001/edit"]').length, 1); assert.strictEqual(tasks.eq(0).find('[href="/abc000000000000000000001/edit"]').length, 1);
assert.strictEqual(tasks[1].querySelectorAll('[href="/abc000000000000000000002/edit"]').length, 1); assert.strictEqual(tasks.eq(1).find('[href="/abc000000000000000000002/edit"]').length, 1);
assert.strictEqual(tasks[2].querySelectorAll('[href="/abc000000000000000000003/edit"]').length, 1); assert.strictEqual(tasks.eq(2).find('[href="/abc000000000000000000003/edit"]').length, 1);
}); });
it('should display a "Delete" button for each task', function() { it('should display a "Delete" button for each task', function() {
var tasks = this.last.dom.querySelectorAll('[data-test=task]'); var tasks = this.last.dom('[data-test=task]');
assert.strictEqual(tasks[0].querySelectorAll('[href="/abc000000000000000000001/delete"]').length, 1); assert.strictEqual(tasks.eq(0).find('[href="/abc000000000000000000001/delete"]').length, 1);
assert.strictEqual(tasks[1].querySelectorAll('[href="/abc000000000000000000002/delete"]').length, 1); assert.strictEqual(tasks.eq(1).find('[href="/abc000000000000000000002/delete"]').length, 1);
assert.strictEqual(tasks[2].querySelectorAll('[href="/abc000000000000000000003/delete"]').length, 1); assert.strictEqual(tasks.eq(2).find('[href="/abc000000000000000000003/delete"]').length, 1);
}); });
it('should display a "Run" button for each task', function() { it('should display a "Run" button for each task', function() {
var tasks = this.last.dom.querySelectorAll('[data-test=task]'); var tasks = this.last.dom('[data-test=task]');
assert.strictEqual(tasks[0].querySelectorAll('[href="/abc000000000000000000001/run"]').length, 1); assert.strictEqual(tasks.eq(0).find('[href="/abc000000000000000000001/run"]').length, 1);
assert.strictEqual(tasks[1].querySelectorAll('[href="/abc000000000000000000002/run"]').length, 1); assert.strictEqual(tasks.eq(1).find('[href="/abc000000000000000000002/run"]').length, 1);
assert.strictEqual(tasks[2].querySelectorAll('[href="/abc000000000000000000003/run"]').length, 1); assert.strictEqual(tasks.eq(2).find('[href="/abc000000000000000000003/run"]').length, 1);
}); });
it('should display the task result counts if the task has been run', function() { it('should display the task result counts if the task has been run', function() {
var tasks = this.last.dom.querySelectorAll('[data-test=task]'); var tasks = this.last.dom('[data-test=task]');
assert.match(tasks[0].textContent, /1\s*errors/i); assert.match(tasks.eq(0).text(), /1\s*errors/i);
assert.match(tasks[0].textContent, /2\s*warnings/i); assert.match(tasks.eq(0).text(), /2\s*warnings/i);
assert.match(tasks[0].textContent, /3\s*notices/i); assert.match(tasks.eq(0).text(), /3\s*notices/i);
}); });
it('should display a message indicating that there are no results if the task has not been run', function() { it('should display a message indicating that there are no results if the task has not been run', function() {
var tasks = this.last.dom.querySelectorAll('[data-test=task]'); var tasks = this.last.dom('[data-test=task]');
assert.match(tasks[2].textContent, /no results/i); assert.match(tasks.eq(2).text(), /no results/i);
}); });
it('should not display an alert message', function() { it('should not display an alert message', function() {
assert.strictEqual(this.last.dom.querySelectorAll('[data-test=alert]').length, 0); assert.strictEqual(this.last.dom('[data-test=alert]').length, 0);
}); });
}); });

View File

@@ -32,58 +32,65 @@ describe('GET /new', function() {
}); });
it('should not display an error message', function() { it('should not display an error message', function() {
assert.strictEqual(this.last.dom.querySelectorAll('[data-test=error]').length, 0); assert.strictEqual(this.last.dom('[data-test=error]').length, 0);
}); });
it('should have an "Add new URL" form', function() { it('should have an "Add new URL" form', function() {
var form = this.last.dom.querySelectorAll('[data-test=new-url-form]')[0]; var form = this.last.dom('[data-test=new-url-form]').eq(0);
assert.isDefined(form); assert.isDefined(form);
assert.strictEqual(form.getAttribute('action'), '/new'); assert.strictEqual(form.attr('action'), '/new');
assert.strictEqual(form.getAttribute('method'), 'post'); assert.strictEqual(form.attr('method'), 'post');
}); });
describe('"Add New URL" form', function() { describe('"Add New URL" form', function() {
beforeEach(function() { beforeEach(function() {
this.form = this.last.dom.querySelectorAll('[data-test=new-url-form]')[0]; this.form = this.last.dom('[data-test=new-url-form]').eq(0);
}); });
it('should have a "name" field', function() { it('should have a "name" field', function() {
var field = this.form.querySelectorAll('input[name=name]')[0]; var field = this.form.find('input[name=name]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'text'); assert.strictEqual(field.attr('type'), 'text');
assert.strictEqual(field.getAttribute('value'), ''); assert.strictEqual(field.attr('value'), '');
}); });
it('should have a "url" field', function() { it('should have a "url" field', function() {
var field = this.form.querySelectorAll('input[name=url]')[0]; var field = this.form.find('input[name=url]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'url'); assert.strictEqual(field.attr('type'), 'url');
assert.strictEqual(field.getAttribute('value'), ''); assert.strictEqual(field.attr('value'), '');
});
it('should have a "wait" field', function() {
var field = this.form.find('input[name=wait]').eq(0);
assert.isDefined(field);
assert.strictEqual(field.attr('type'), 'text');
assert.strictEqual(field.attr('value'), '');
}); });
it('should have a "username" field', function() { it('should have a "username" field', function() {
var field = this.form.querySelectorAll('input[name=username]')[0]; var field = this.form.find('input[name=username]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'text'); assert.strictEqual(field.attr('type'), 'text');
assert.strictEqual(field.getAttribute('value'), ''); assert.strictEqual(field.attr('value'), '');
}); });
it('should have a "password" field', function() { it('should have a "password" field', function() {
var field = this.form.querySelectorAll('input[name=password]')[0]; var field = this.form.find('input[name=password]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'text'); assert.strictEqual(field.attr('type'), 'text');
assert.strictEqual(field.getAttribute('value'), ''); assert.strictEqual(field.attr('value'), '');
}); });
it('should have a "standard" field', function() { it('should have a "standard" field', function() {
var field = this.form.querySelectorAll('select[name=standard]')[0]; var field = this.form.find('select[name=standard]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.strictEqual(field.querySelectorAll('option').length, 4); assert.strictEqual(field.find('option').length, 4);
}); });
it('should have "ignore" fields', function() { it('should have "ignore" fields', function() {
var fields = this.form.querySelectorAll('input[name="ignore[]"]'); var fields = this.form.find('input[name="ignore[]"]');
assert.isDefined(fields); assert.isDefined(fields);
assert.notStrictEqual(fields.length, 0); assert.notStrictEqual(fields.length, 0);
}); });
@@ -100,7 +107,7 @@ describe('POST /new', function() {
var req = { var req = {
method: 'POST', method: 'POST',
endpoint: '/new', endpoint: '/new',
body: { form: {
name: '', name: '',
url: '' url: ''
} }
@@ -113,7 +120,7 @@ describe('POST /new', function() {
}); });
it('should display an error message', function() { it('should display an error message', function() {
assert.strictEqual(this.last.dom.querySelectorAll('[data-test=error]').length, 1); assert.strictEqual(this.last.dom('[data-test=error]').length, 1);
}); });
}); });
@@ -124,7 +131,7 @@ describe('POST /new', function() {
var req = { var req = {
method: 'POST', method: 'POST',
endpoint: '/new', endpoint: '/new',
body: { form: {
name: 'Example', name: 'Example',
url: 'http://example.com/', url: 'http://example.com/',
standard: 'WCAG2AA' standard: 'WCAG2AA'
@@ -149,11 +156,11 @@ describe('POST /new', function() {
}); });
it('should not display an error message', function() { it('should not display an error message', function() {
assert.strictEqual(this.last.dom.querySelectorAll('[data-test=error]').length, 0); assert.strictEqual(this.last.dom('[data-test=error]').length, 0);
}); });
it('should display a success message', function() { it('should display a success message', function() {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0]; var alert = this.last.dom('[data-test=alert]').eq(0);
assert.isDefined(alert); assert.isDefined(alert);
assert.match(alert.textContent, /url has been added/i); assert.match(alert.textContent, /url has been added/i);
}); });

View File

@@ -44,7 +44,8 @@ describe('GET /<task-id>/<result-id>.json', function() {
var req = { var req = {
method: 'GET', method: 'GET',
endpoint: '/abc000000000000000000001/def000000000000000000001.json', endpoint: '/abc000000000000000000001/def000000000000000000001.json',
nonDom: true nonDom: true,
json: true
}; };
this.navigate(req, done); this.navigate(req, done);
}); });

View File

@@ -32,37 +32,37 @@ describe('GET /<task-id>/<result-id>', function() {
}); });
it('should display a "Download CSV" button', function() { it('should display a "Download CSV" button', function() {
var elem = this.last.dom.querySelectorAll('[data-test=download-csv]'); var elem = this.last.dom('[data-test=download-csv]');
assert.strictEqual(elem.length, 1); assert.strictEqual(elem.length, 1);
assert.strictEqual(elem[0].getAttribute('href'), '/abc000000000000000000001/def000000000000000000001.csv'); assert.strictEqual(elem.eq(0).attr('href'), '/abc000000000000000000001/def000000000000000000001.csv');
}); });
it('should display a "Download JSON" button', function() { it('should display a "Download JSON" button', function() {
var elem = this.last.dom.querySelectorAll('[data-test=download-json]'); var elem = this.last.dom('[data-test=download-json]');
assert.strictEqual(elem.length, 1); assert.strictEqual(elem.length, 1);
assert.strictEqual(elem[0].getAttribute('href'), '/abc000000000000000000001/def000000000000000000001.json'); assert.strictEqual(elem.eq(0).attr('href'), '/abc000000000000000000001/def000000000000000000001.json');
}); });
it('should display a link back to the task', function() { it('should display a link back to the task', function() {
assert.isDefined(this.last.dom.querySelectorAll('[href="/abc000000000000000000001"]')[0]); assert.isDefined(this.last.dom('[href="/abc000000000000000000001"]').eq(0));
}); });
it('should display errors', function() { it('should display errors', function() {
var elem = this.last.dom.querySelectorAll('[data-test=task-errors]')[0]; var elem = this.last.dom('[data-test=task-errors]').eq(0);
assert.isDefined(elem); assert.isDefined(elem);
assert.match(elem.textContent, /errors \( 1 \)/i); assert.match(elem.text(), /errors \( 1 \)/i);
}); });
it('should display warnings', function() { it('should display warnings', function() {
var elem = this.last.dom.querySelectorAll('[data-test=task-warnings]')[0]; var elem = this.last.dom('[data-test=task-warnings]').eq(0);
assert.isDefined(elem); assert.isDefined(elem);
assert.match(elem.textContent, /warnings \( 2 \)/i); assert.match(elem.text(), /warnings \( 2 \)/i);
}); });
it('should display notices', function() { it('should display notices', function() {
var elem = this.last.dom.querySelectorAll('[data-test=task-notices]')[0]; var elem = this.last.dom('[data-test=task-notices]').eq(0);
assert.isDefined(elem); assert.isDefined(elem);
assert.match(elem.textContent, /notices \( 3 \)/i); assert.match(elem.text(), /notices \( 3 \)/i);
}); });
}); });

View File

@@ -32,14 +32,14 @@ describe('GET /<task-id>/delete', function() {
}); });
it('should have a "Delete URL" form', function() { it('should have a "Delete URL" form', function() {
var form = this.last.dom.querySelectorAll('[data-test=delete-url-form]')[0]; var form = this.last.dom('[data-test=delete-url-form]').eq(0);
assert.isDefined(form); assert.isDefined(form);
assert.strictEqual(form.getAttribute('action'), '/abc000000000000000000001/delete'); assert.strictEqual(form.attr('action'), '/abc000000000000000000001/delete');
assert.strictEqual(form.getAttribute('method'), 'post'); assert.strictEqual(form.attr('method'), 'post');
}); });
it('should display a link back to the task page', function() { it('should display a link back to the task page', function() {
assert.greaterThan(this.last.dom.querySelectorAll('[href="/abc000000000000000000001"]').length, 0); assert.greaterThan(this.last.dom('[href="/abc000000000000000000001"]').length, 0);
}); });
}); });
@@ -70,9 +70,9 @@ describe('POST /<task-id>/delete', function() {
}); });
it('should display a success message', function() { it('should display a success message', function() {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0]; var alert = this.last.dom('[data-test=alert]').eq(0);
assert.isDefined(alert); assert.isDefined(alert);
assert.match(alert.textContent, /been deleted/i); assert.match(alert.text(), /been deleted/i);
}); });
}); });

View File

@@ -32,59 +32,66 @@ describe('GET /<task-id>/edit', function() {
}); });
it('should have an "Edit URL" form', function() { it('should have an "Edit URL" form', function() {
var form = this.last.dom.querySelectorAll('[data-test=edit-url-form]')[0]; var form = this.last.dom('[data-test=edit-url-form]').eq(0);
assert.isDefined(form); assert.isDefined(form);
assert.strictEqual(form.getAttribute('action'), '/abc000000000000000000001/edit'); assert.strictEqual(form.attr('action'), '/abc000000000000000000001/edit');
assert.strictEqual(form.getAttribute('method'), 'post'); assert.strictEqual(form.attr('method'), 'post');
}); });
it('should display a link back to the task page', function() { it('should display a link back to the task page', function() {
assert.greaterThan(this.last.dom.querySelectorAll('[href="/abc000000000000000000001"]').length, 0); assert.greaterThan(this.last.dom('[href="/abc000000000000000000001"]').length, 0);
}); });
describe('"Edit URL" form', function() { describe('"Edit URL" form', function() {
beforeEach(function() { beforeEach(function() {
this.form = this.last.dom.querySelectorAll('[data-test=edit-url-form]')[0]; this.form = this.last.dom('[data-test=edit-url-form]').eq(0);
}); });
it('should have a "name" field', function() { it('should have a "name" field', function() {
var field = this.form.querySelectorAll('input[name=name]')[0]; var field = this.form.find('input[name=name]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'text'); assert.strictEqual(field.attr('type'), 'text');
assert.strictEqual(field.getAttribute('value'), 'NPG Home'); assert.strictEqual(field.attr('value'), 'NPG Home');
}); });
it('should have a disabled "url" field', function() { it('should have a disabled "url" field', function() {
var field = this.form.querySelectorAll('input[name=url]')[0]; var field = this.form.find('input[name=url]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'url'); assert.strictEqual(field.attr('type'), 'url');
assert.strictEqual(field.getAttribute('value'), 'nature.com'); assert.strictEqual(field.attr('value'), 'nature.com');
assert.isDefined(field.getAttribute('disabled')); assert.isDefined(field.attr('disabled'));
});
it('should have a "wait" field', function() {
var field = this.form.find('input[name=wait]').eq(0);
assert.isDefined(field);
assert.strictEqual(field.attr('type'), 'text');
assert.strictEqual(field.attr('value'), '0');
}); });
it('should have a disabled "standard" field', function() { it('should have a disabled "standard" field', function() {
var field = this.form.querySelectorAll('select[name=standard]')[0]; var field = this.form.find('select[name=standard]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.isDefined(field.getAttribute('disabled')); assert.isDefined(field.attr('disabled'));
}); });
it('should have a "username" field', function() { it('should have a "username" field', function() {
var field = this.form.querySelectorAll('input[name=username]')[0]; var field = this.form.find('input[name=username]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'text'); assert.strictEqual(field.attr('type'), 'text');
assert.strictEqual(field.getAttribute('value'), 'user'); assert.strictEqual(field.attr('value'), 'user');
}); });
it('should have a "password" field', function() { it('should have a "password" field', function() {
var field = this.form.querySelectorAll('input[name=password]')[0]; var field = this.form.find('input[name=password]').eq(0);
assert.isDefined(field); assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'text'); assert.strictEqual(field.attr('type'), 'text');
assert.strictEqual(field.getAttribute('value'), 'access'); assert.strictEqual(field.attr('value'), 'access');
}); });
it('should have "ignore" fields', function() { it('should have "ignore" fields', function() {
var fields = this.form.querySelectorAll('input[name="ignore[]"]'); var fields = this.form.find('input[name="ignore[]"]');
assert.isDefined(fields); assert.isDefined(fields);
assert.notStrictEqual(fields.length, 0); assert.notStrictEqual(fields.length, 0);
}); });
@@ -99,7 +106,7 @@ describe('POST /<task-id>/edit', function() {
var req = { var req = {
method: 'POST', method: 'POST',
endpoint: '/abc000000000000000000001/edit', endpoint: '/abc000000000000000000001/edit',
body: { form: {
name: 'foo', name: 'foo',
username: 'newuser', username: 'newuser',
password: 'secure', password: 'secure',
@@ -124,9 +131,9 @@ describe('POST /<task-id>/edit', function() {
}); });
it('should display a success message', function() { it('should display a success message', function() {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0]; var alert = this.last.dom('[data-test=alert]').eq(0);
assert.isDefined(alert); assert.isDefined(alert);
assert.match(alert.textContent, /been saved/i); assert.match(alert.text(), /been saved/i);
}); });
}); });

View File

@@ -34,46 +34,46 @@ describe('GET /<task-id>', function() {
}); });
it('should display an "Edit" button', function() { it('should display an "Edit" button', function() {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/edit"]').length, 1); assert.strictEqual(this.last.dom('[href="/abc000000000000000000001/edit"]').length, 1);
}); });
it('should display a "Delete" button', function() { it('should display a "Delete" button', function() {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/delete"]').length, 1); assert.strictEqual(this.last.dom('[href="/abc000000000000000000001/delete"]').length, 1);
}); });
it('should display a "Run" button', function() { it('should display a "Run" button', function() {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/run"]').length, 1); assert.strictEqual(this.last.dom('[href="/abc000000000000000000001/run"]').length, 1);
}); });
it('should display a "Download CSV" button for the latest result', function() { it('should display a "Download CSV" button for the latest result', function() {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/def000000000000000000001.csv"]').length, 1); assert.strictEqual(this.last.dom('[href="/abc000000000000000000001/def000000000000000000001.csv"]').length, 1);
}); });
it('should display a "Download JSON" button for the latest result', function() { it('should display a "Download JSON" button for the latest result', function() {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/def000000000000000000001.json"]').length, 1); assert.strictEqual(this.last.dom('[href="/abc000000000000000000001/def000000000000000000001.json"]').length, 1);
}); });
it('should display links to all results', function() { it('should display links to all results', function() {
assert.isDefined(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/def000000000000000000001"]')[0]); assert.isDefined(this.last.dom('[href="/abc000000000000000000001/def000000000000000000001"]').eq(0));
assert.isDefined(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/def000000000000000000003"]')[0]); assert.isDefined(this.last.dom('[href="/abc000000000000000000001/def000000000000000000003"]').eq(0));
}); });
it('should display errors', function() { it('should display errors', function() {
var elem = this.last.dom.querySelectorAll('[data-test=task-errors]')[0]; var elem = this.last.dom('[data-test=task-errors]').eq(0);
assert.isDefined(elem); assert.isDefined(elem);
assert.match(elem.textContent, /errors \( 1 \)/i); assert.match(elem.text(), /errors \( 1 \)/i);
}); });
it('should display warnings', function() { it('should display warnings', function() {
var elem = this.last.dom.querySelectorAll('[data-test=task-warnings]')[0]; var elem = this.last.dom('[data-test=task-warnings]').eq(0);
assert.isDefined(elem); assert.isDefined(elem);
assert.match(elem.textContent, /warnings \( 2 \)/i); assert.match(elem.text(), /warnings \( 2 \)/i);
}); });
it('should display notices', function() { it('should display notices', function() {
var elem = this.last.dom.querySelectorAll('[data-test=task-notices]')[0]; var elem = this.last.dom('[data-test=task-notices]').eq(0);
assert.isDefined(elem); assert.isDefined(elem);
assert.match(elem.textContent, /notices \( 3 \)/i); assert.match(elem.text(), /notices \( 3 \)/i);
}); });
}); });
@@ -93,15 +93,15 @@ describe('GET /<task-id>', function() {
}); });
it('should display a "Run" button', function() { it('should display a "Run" button', function() {
var elem = this.last.dom.querySelectorAll('[data-test=run-task]'); var elem = this.last.dom('[data-test=run-task]');
assert.strictEqual(elem.length, 1); assert.strictEqual(elem.length, 1);
assert.strictEqual(elem[0].getAttribute('href'), '/abc000000000000000000003/run'); assert.strictEqual(elem.eq(0).attr('href'), '/abc000000000000000000003/run');
}); });
it('should display a message indicating that there are no results', function() { it('should display a message indicating that there are no results', function() {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0]; var alert = this.last.dom('[data-test=alert]').eq(0);
assert.isDefined(alert); assert.isDefined(alert);
assert.match(alert.textContent, /there are no results to show/i); assert.match(alert.text(), /there are no results to show/i);
}); });
}); });

View File

@@ -36,9 +36,9 @@ describe('GET /<task-id>/run', function() {
}); });
it('should display a success message', function() { it('should display a success message', function() {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0]; var alert = this.last.dom('[data-test=alert]').eq(0);
assert.isDefined(alert); assert.isDefined(alert);
assert.match(alert.textContent, /new results are being generated/i); assert.match(alert.text(), /new results are being generated/i);
}); });
}); });

View File

@@ -19,20 +19,20 @@ var moment = require('moment');
module.exports = helper; module.exports = helper;
function helper(register) { function helper(hbs) {
// Format a date with Moment // Format a date with Moment
register('date-format', function(context, block) { hbs.registerHelper('date-format', function(context, block) {
var format = block.hash.format || 'YYYY-MM-DD HH:mm:ss'; var format = block.hash.format || 'YYYY-MM-DD HH:mm:ss';
return moment(context).format(format); return moment(context).format(format);
}); });
// Get a relative date // Get a relative date
register('date-relative', function(context) { hbs.registerHelper('date-relative', function(context) {
return moment(context).fromNow(); return moment(context).fromNow();
}); });
register('date-timestamp', function(context) { hbs.registerHelper('date-timestamp', function(context) {
return moment(context).valueOf(); return moment(context).valueOf();
}); });

View File

@@ -2,10 +2,10 @@
module.exports = helper; module.exports = helper;
function helper(register) { function helper(hbs) {
// Convert a string to lower-case // Convert a string to lower-case
register('lowercase', function(context) { hbs.registerHelper('lowercase', function(context) {
return context.toLowerCase(); return context.toLowerCase();
}); });

View File

@@ -17,10 +17,10 @@
module.exports = helper; module.exports = helper;
function helper(register) { function helper(hbs) {
// Simplify url by removing (eg http://, https://, trailing slashes) from url // Simplify url by removing (eg http://, https://, trailing slashes) from url
register('simplify-url', function(context) { hbs.registerHelper('simplify-url', function(context) {
return context.replace(/^https?:\/\//i, '').replace(/\/$/, '').toLowerCase(); return context.replace(/^https?:\/\//i, '').replace(/\/$/, '').toLowerCase();
}); });

View File

@@ -20,7 +20,7 @@ along with pa11y-dashboard. If not, see <http://www.gnu.org/licenses/>.
<div class="clearfix"> <div class="clearfix">
<div class="col-md-6 col-md-offset-3 filter-toggle no-js-hide text-center"> <div class="col-md-6 col-md-offset-3 filter-toggle no-js-hide text-center">
<label for="filter-input" class="filter-trigger" data-toggle="collapse" data-target="#filter-input">Filter<span class="glyphicon glyphicon-filter"></span> <label for="task-filter" class="filter-trigger" data-toggle="collapse" data-target="#filter-input">Filter<span class="glyphicon glyphicon-filter"></span>
</label> </label>
<div id="filter-input" class="collapse"> <div id="filter-input" class="collapse">
<input class="form-control" id="task-filter" type="text" data-role="input" placeholder="Type filter term (name or standard)"/> <input class="form-control" id="task-filter" type="text" data-role="input" placeholder="Type filter term (name or standard)"/>

View File

@@ -74,6 +74,16 @@ along with pa11y-dashboard. If not, see <http://www.gnu.org/licenses/>.
</div> </div>
</div> </div>
<div class="form-group clearfix">
<div class="row">
<div class="col-md-4 col-sm-4 col-xs-6">
<label class="control-label" for="new-task-wait">Wait (milliseconds)</label>
<input class="form-control" id="new-task-wait" type="text" placeholder="E.g. 3000" name="wait" value="{{task.wait}}"/>
<em>(Note: default wait time is 0ms)</em>
</div>
</div>
</div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<div class="row"> <div class="row">
<div class="col-md-4 col-sm-4 col-xs-6"> <div class="col-md-4 col-sm-4 col-xs-6">
@@ -92,7 +102,7 @@ along with pa11y-dashboard. If not, see <http://www.gnu.org/licenses/>.
</div> </div>
</div> </div>
<p class="control-label"><b>Ignore these rules</b> <a target="_blank" href="https://github.com/springernature/pa11y/wiki/HTML-CodeSniffer-Rules">(full list of rules here)</a></p> <p class="control-label"><b>Ignore these rules</b> <a target="_blank" href="https://github.com/pa11y/pa11y/wiki/HTML-CodeSniffer-Rules">(full list of rules here)</a></p>
<div class="standards-lists"> <div class="standards-lists">
{{#standards}} {{#standards}}

View File

@@ -19,7 +19,7 @@ along with pa11y-dashboard. If not, see <http://www.gnu.org/licenses/>.
<div class="row clearfix"> <div class="row clearfix">
<div class="col-md-12"> <div class="col-md-12">
<h1 class="h2 crunch-top">{{task.name}}</h1> <h1 class="h2 crunch-top">{{task.name}}</h1>
<p class="h4">{{simplify-url task.url}}<span class="h5"> ({{task.standard}})</span></p> <p class="h4"><a href="{{task.url}}">{{simplify-url task.url}}</a><span class="h5"> ({{task.standard}})</span></p>
</div> </div>
<div class="run-details task-header clearfix"> <div class="run-details task-header clearfix">
<div class="col-md-12 clearfix"> <div class="col-md-12 clearfix">

View File

@@ -86,6 +86,16 @@ along with pa11y-dashboard. If not, see <http://www.gnu.org/licenses/>.
</div> </div>
</div> </div>
<div class="form-group clearfix">
<div class="row">
<div class="col-md-4 col-sm-4 col-xs-6">
<label class="control-label" for="new-task-wait">Wait (milliseconds)</label>
<input class="form-control" id="new-task-wait" type="text" placeholder="E.g. 3000" name="wait" value="{{task.wait}}"/>
<em>(Note: default wait time is 0ms)</em>
</div>
</div>
</div>
<div class="form-group clearfix"> <div class="form-group clearfix">
<div class="row"> <div class="row">
<div class="col-md-4 col-sm-4 col-xs-6"> <div class="col-md-4 col-sm-4 col-xs-6">
@@ -104,7 +114,7 @@ along with pa11y-dashboard. If not, see <http://www.gnu.org/licenses/>.
</div> </div>
</div> </div>
<p class="control-label"><b>Ignore these rules</b> <a target="_blank" href="https://github.com/springernature/pa11y/wiki/HTML-CodeSniffer-Rules">(full list of rules here)</a></p> <p class="control-label"><b>Ignore these rules</b> <a target="_blank" href="https://github.com/pa11y/pa11y/wiki/HTML-CodeSniffer-Rules">(full list of rules here)</a></p>
<div class="standards-lists"> <div class="standards-lists">
{{#standards}} {{#standards}}