Compare commits

...

78 Commits
1.0.0 ... 1.3.0

Author SHA1 Message Date
Rowan Manning
ff239edf5a Version 1.3.0 2014-03-04 14:28:03 +00:00
Rowan Manning
c2013e42d4 Commit style changes 2014-03-04 14:15:31 +00:00
Rowan Manning
a79a5a2fe6 Merge branch 'filter-urls' of github.com:nature/pa11y-dashboard into develop 2014-03-04 14:14:49 +00:00
perryharlock
85fe2c4a37 Move toggle functionality from filter input to containing div 2014-03-04 14:08:57 +00:00
Rowan Manning
c04396e80e Merge branch 'gpl-preamble' of github.com:nature/pa11y-dashboard into develop 2014-02-10 14:56:06 +00:00
Jude Robinson
7b10f2de91 adding the gpl preamble 2014-02-10 14:08:38 +00:00
perryharlock
890ec38216 Add filter styling 2014-01-21 10:50:57 +00:00
Rowan Manning
d8cb1b6c71 Write the JavaScript for URL filtering 2014-01-14 11:40:23 +00:00
Rowan Manning
a605835cc9 Attempt to fix timeout errors in tests 2014-01-13 09:59:21 +00:00
perryharlock
01897d8a17 Amend styles for ignore button 2014-01-13 09:59:21 +00:00
Rowan Manning
02e22eb094 Add "unigore" buttons to ignored rules 2014-01-13 09:59:13 +00:00
perryharlock
dfac541294 Amends to task-card min heights 2014-01-13 09:59:13 +00:00
Rowan Manning
f3b295982f Fix typo 2014-01-13 09:58:35 +00:00
Rowan Manning
ddf2c705c9 Fix the ignore buttons for readonly/result pages 2014-01-13 09:58:35 +00:00
perryharlock
20de93bf2d Change ignore rules in results to come from mainResult 2014-01-13 09:58:35 +00:00
Rowan Manning
ccc7ddfc18 Write the back-end for rule ignoring 2014-01-13 09:58:35 +00:00
perryharlock
4347acf654 Issue 65 - Ability to select a rule to ignore from result - css / html 2014-01-13 09:58:35 +00:00
Rowan Manning
da9b383909 Fix mistakes in versioning instructions 2014-01-13 09:58:34 +00:00
Rowan Manning
d0feee8dd5 Version 1.2.3 2014-01-13 09:38:10 +00:00
Rowan Manning
f97e224679 Update the tests to allow for the new CSV format 2014-01-13 09:37:16 +00:00
Rowan Manning
ddbb0db33d Remove spaces in the CSV header row 2014-01-13 09:33:51 +00:00
Quannon Au
e0290b4fc5 Issue 74 - Fix CSV format for Mac Excel 2014-01-10 11:25:16 -08:00
perryharlock
1f6ea332ac Version 1.2.2 2014-01-09 11:44:25 +00:00
perryharlock
5b109fb23d Amend versioning instructions 2014-01-09 11:43:44 +00:00
perryharlock
3b03af7e8f Merge branch 'develop' of github.com:nature/pa11y-dashboard into develop 2014-01-09 11:05:22 +00:00
perryharlock
1c96369bb0 Issue 68 - Bad spacing when graph not visible 2014-01-09 11:04:53 +00:00
Rowan Manning
e421444487 Versioning clarifications 2014-01-09 10:36:08 +00:00
Rowan Manning
5c7d9bfc43 Add notes on publishing a release 2014-01-09 10:31:50 +00:00
Rowan Manning
75497ff95f Version 1.2.1 2014-01-08 11:51:57 +00:00
perryharlock
c481f8dbb5 IE7 and IE8 amends for Options dropdown positioning
Conflicts:
	public/css/site.min.css
2014-01-08 11:43:47 +00:00
perryharlock
716151e696 Include IE8and.less file and make IE amends for Options dropdowns
Conflicts:
	public/css/site.min.css
2014-01-08 11:42:34 +00:00
Rowan Manning
b1de71f59c Version 1.2.0 2013-12-12 14:39:19 +00:00
Rowan Manning
4e19b125e0 Add a count for ignored rules 2013-12-12 14:36:14 +00:00
perryharlock
d3148d4194 Add HTML Codesniffer links 2013-12-12 14:17:54 +00:00
Rowan Manning
1ad95627ee Don't trim trailing spaces in markdown 2013-12-12 09:38:21 +00:00
Rowan Manning
4296effbc1 Refactor to reduce repetition 2013-12-11 14:12:25 +00:00
Rowan Manning
917057a109 Display the ignore rules for results, not tasks 2013-12-11 14:06:51 +00:00
Rowan Manning
c8b97df4ee Merge branch 'edit-page' of github.com:nature/pa11y-dashboard into develop 2013-12-02 15:32:40 +00:00
perryharlock
724175ef66 Add number of ignore rules to result page 2013-12-02 11:58:01 +00:00
perryharlock
b2273234ce Merge branch 'edit-page' of github.com:nature/pa11y-dashboard into edit-page 2013-11-29 15:14:31 +00:00
perryharlock
0546175809 Change link decriptions for editing and delting a task 2013-11-29 15:14:22 +00:00
Rowan Manning
52a923ace3 Write basic tests for task editing 2013-11-28 13:59:27 +00:00
Rowan Manning
786922e592 Add an .editorconfig file 2013-11-28 13:28:28 +00:00
Rowan Manning
4ff53093f4 Link breadcrumb on task sub-pages 2013-11-28 08:59:05 +00:00
Rowan Manning
ce77e57659 Fix an issue with saving empty ignore rules 2013-11-28 08:54:40 +00:00
Rowan Manning
4a7800e288 Wire up task editing 2013-11-27 16:47:14 +00:00
Rowan Manning
5b1963599b Update pa11y-webservice to include edit endpoint 2013-11-27 16:38:19 +00:00
perryharlock
1b36dc12ea make dropdowns appear on hover for non js users 2013-11-27 16:37:52 +00:00
perryharlock
963ab33cce Make edit route work and add options dropdown 2013-11-27 16:21:18 +00:00
perryharlock
b95238645a Merge branch 'develop' of github.com:nature/pa11y-dashboard into edit-page 2013-11-27 14:11:45 +00:00
perryharlock
8657138e13 Fix linting erros 2013-11-27 13:58:55 +00:00
perryharlock
f580f00d4c Reduce font size for tooltips in graph 2013-11-27 13:40:27 +00:00
perryharlock
fdc631e3ca Merge branch 'develop' of github.com:nature/pa11y-dashboard into develop 2013-11-27 13:38:06 +00:00
perryharlock
98dafba137 Small tweak to tooltip appearance on graph 2013-11-27 13:37:42 +00:00
Rowan Manning
fd32a5d894 Cache-bust the CSS and JS 2013-11-27 13:29:19 +00:00
perryharlock
68a3e26472 Disable fields that are not editable 2013-11-27 11:40:22 +00:00
Rowan Manning
bb197744fd Add dates to graph tooltips 2013-11-27 11:31:42 +00:00
perryharlock
ae6b551f54 Merge branch 'develop' of github.com:nature/pa11y-dashboard into edit-page 2013-11-27 11:30:59 +00:00
perryharlock
16231a3609 Amend x-axis label width 2013-11-27 11:27:31 +00:00
perryharlock
74dd5b18c9 Add route and view for edit page 2013-11-27 11:13:19 +00:00
perryharlock
5c10261c56 Fix x-axis - stop multiple appearances of dates 2013-11-26 11:36:25 +00:00
perryharlock
e13de45e4a Fix lint error 2013-11-25 16:37:50 +00:00
perryharlock
9383de3410 Amend xaxis mode to time from category to try to reduce x axis clutter 2013-11-25 16:19:01 +00:00
perryharlock
7c2647653d issue 57 - make y axis display ticks as integers 2013-11-25 11:27:36 +00:00
Rowan Manning
2ebb54f545 Version 1.1.0 2013-11-22 14:01:53 +00:00
Rowan Manning
d57ae45b4f Write tests for result pages (and related actions) 2013-11-22 11:44:54 +00:00
Rowan Manning
ea330548b1 Write tests for task pages (and related actions) 2013-11-22 11:10:57 +00:00
Rowan Manning
ae5b214834 Write tests for the new URL form 2013-11-22 09:17:44 +00:00
Rowan Manning
b5735b7f05 Reload fixtures for tests and finish testing home 2013-11-21 13:54:52 +00:00
Rowan Manning
a47fb38d7f Add test running instructions to the readme 2013-11-21 12:04:42 +00:00
Rowan Manning
da0e98eab1 Update the Travis config to run tests 2013-11-21 11:52:40 +00:00
Rowan Manning
623a52e112 Build a basic framework for functional testing 2013-11-21 09:58:44 +00:00
Rowan Manning
9d72e50b4e Update docs to include webservice dependencies 2013-11-21 09:25:28 +00:00
Rowan Manning
3535b9b11c Document new configuration options 2013-11-21 09:21:15 +00:00
Rowan Manning
8f636173a0 Allow the webservice to run automatically 2013-11-21 09:12:54 +00:00
Rowan Manning
ec9f82aa6f Add build status to readme 2013-11-20 13:47:47 +00:00
Rowan Manning
34a7c351c9 Add Travis config 2013-11-20 13:46:47 +00:00
Rowan Manning
c13af05422 Fix lint errors 2013-11-20 13:46:41 +00:00
68 changed files with 2468 additions and 281 deletions

13
.editorconfig Normal file
View File

@@ -0,0 +1,13 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
indent_style = tab
trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
# Config files # Config files
config/development.json config/development.json
config/production.json config/production.json
config/test.json
# Generated npm files # Generated npm files
node_modules node_modules

24
.travis.yml Normal file
View File

@@ -0,0 +1,24 @@
# Language/versions
language: node_js
node_js:
- "0.10"
# Services setup
services:
- mongodb
# Build script
before_script:
- npm install -g grunt-cli
- cp config/test.sample.json config/test.json
- grunt start-test &
- sleep 5 # give server time to start
script: 'grunt ci'
# Notifications
notifications:
email:
- j.robinson@nature.com
- perry.harlock@nature.com
- rowan.manning@nature.com

View File

@@ -31,8 +31,23 @@ Coding Guidelines
* Don't commit code without passing tests (run `grunt test`). * Don't commit code without passing tests (run `grunt test`).
Versioning
----------
We use [Semantic Versioning][semver] in this project. The process for releasing a new version is as follows; this should only be done by core contributors you don't need to include a tagged version in your pull-requests.
* Switch to `master` and merge the `develop` branch into it
* Update the version number in `package.json` and `README.md`
* Commit the changes with the message: "Version x.x.x" (x.x.x being the new version number)
* Tag the commit with the version number (just the numbers, no "version" or "v"): `git tag x.x.x`
* Push with tags: `git push && git push --tags`
* Check out the `develop` branch, merge `master` into it, and push
* On GitHub, add [release notes][release-notes] for the new version. The title should be "Version x.x.x", and the description should be a list of new features/fixes
[bugs]: https://github.com/nature/pa11y-dashboard/issues?labels=bug&state=open [bugs]: https://github.com/nature/pa11y-dashboard/issues?labels=bug&state=open
[ready]: https://github.com/nature/pa11y-dashboard/issues?labels=ready&state=open [ready]: https://github.com/nature/pa11y-dashboard/issues?labels=ready&state=open
[issues]: https://github.com/nature/pa11y-dashboard/issues [issues]: https://github.com/nature/pa11y-dashboard/issues
[milestones]: https://github.com/nature/pa11y-dashboard/issues/milestones [milestones]: https://github.com/nature/pa11y-dashboard/issues/milestones
[release-notes]: https://github.com/nature/pa11y-dashboard/releases
[semver]: http://semver.org/

View File

@@ -1,3 +1,18 @@
// 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/>.
module.exports = function (grunt) { module.exports = function (grunt) {
grunt.initConfig({ grunt.initConfig({
@@ -34,6 +49,16 @@ module.exports = function (grunt) {
} }
}, },
mochaTest: {
functional: {
src: ['test/functional/**/*.js'],
options: {
reporter: 'spec',
timeout: 4000
}
}
},
nodemon: { nodemon: {
development: { development: {
options: { options: {
@@ -43,6 +68,15 @@ module.exports = function (grunt) {
NODE_ENV: 'development' NODE_ENV: 'development'
} }
} }
},
test: {
options: {
cwd: __dirname,
file: 'index.js',
env: {
NODE_ENV: 'test'
}
}
} }
}, },
@@ -57,8 +91,10 @@ module.exports = function (grunt) {
'public/js/vendor/bootstrap/js/alert.js', 'public/js/vendor/bootstrap/js/alert.js',
'public/js/vendor/bootstrap/js/dropdown.js', 'public/js/vendor/bootstrap/js/dropdown.js',
'public/js/vendor/bootstrap/js/tooltip.js', 'public/js/vendor/bootstrap/js/tooltip.js',
'public/js/vendor/bootstrap/js/transition.js',
'public/js/vendor/bootstrap/js/collapse.js',
'public/js/vendor/flot/jquery.flot.js', 'public/js/vendor/flot/jquery.flot.js',
'public/js/vendor/flot/jquery.flot.categories.js', 'public/js/vendor/flot/jquery.flot.time.js',
'public/js/vendor/flot/jquery.flot.selection.js', 'public/js/vendor/flot/jquery.flot.selection.js',
'public/js/vendor/flot/jquery.flot.resize.js', 'public/js/vendor/flot/jquery.flot.resize.js',
'public/js/site.js' 'public/js/site.js'
@@ -84,12 +120,15 @@ module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-contrib-less'); grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-mocha-test');
grunt.loadNpmTasks('grunt-nodemon'); grunt.loadNpmTasks('grunt-nodemon');
grunt.registerTask('lint', ['jshint']); grunt.registerTask('lint', ['jshint']);
grunt.registerTask('test', ['mochaTest']);
grunt.registerTask('compile', ['less', 'uglify']); grunt.registerTask('compile', ['less', 'uglify']);
grunt.registerTask('start', ['nodemon:development']); grunt.registerTask('start', ['nodemon:development']);
grunt.registerTask('default', ['compile', 'lint']); grunt.registerTask('start-test', ['nodemon:test']);
grunt.registerTask('ci', ['compile', 'lint']); grunt.registerTask('default', ['compile', 'lint', 'test']);
grunt.registerTask('ci', ['lint', 'test']);
}; };

View File

@@ -3,7 +3,8 @@ 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.0.0* **Current Version:** *1.3.0*
**Build Status:** [![Build Status][travis-img]][travis]
**Node Version Support:** *0.10* **Node Version Support:** *0.10*
@@ -14,7 +15,7 @@ pa11y-dashboard is a web interface to the [pa11y][pa11y] accessibility reporter;
Setup Setup
----- -----
pa11y-dashboard requires [Node.js][node] 0.10+ and [pa11y-webservice][pa11y-webservice] to be installed and running. You'll need to follow the setup guide for pa11y-webservice before setting up pa11y-dashboard. 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.
You'll then need to clone this repo locally and install dependencies with `npm install`. You'll then need to clone this repo locally and install dependencies with `npm install`.
@@ -23,6 +24,7 @@ Once you have a local clone, you'll need to copy some sample configuration files
```sh ```sh
$ cp config/development.sample.json config/development.json $ cp config/development.sample.json config/development.json
$ cp config/production.sample.json config/production.json $ cp config/production.sample.json config/production.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 configurations. The [available configurations are documented here](#configurations).
@@ -32,6 +34,7 @@ Now that you've got your application configured, you can run in each mode with t
```sh ```sh
$ NODE_ENV=production node . # Run in production $ NODE_ENV=production node . # Run in production
$ NODE_ENV=development node . # Run in development $ NODE_ENV=development node . # Run in development
$ NODE_ENV=test node . # Run in test
``` ```
Check the [development instructions](#development) for more information about running locally (and restarting automatically when files change). Check the [development instructions](#development) for more information about running locally (and restarting automatically when files change).
@@ -42,9 +45,6 @@ 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.
### webservice
*(string)* The base URL of the [pa11y-webservice][pa11y-webservice] instance you intend on using.
### port ### port
*(number)* The port to run the application on. *(number)* The port to run the application on.
@@ -57,20 +57,31 @@ The boot configurations for pa11y-dashboard are as follows. Look at the sample J
### 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
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.
Development Development
----------- -----------
To develop pa11y-dashboard, you'll need to clone the repo and get set up as outlined in the [setup guide](#setup). You'll also need [Grunt][grunt] to be installed globally in order to run tests, you can do this with `npm install -g grunt-cli`. To develop pa11y-dashboard, you'll need to clone the repo and get set up as outlined in the [setup guide](#setup). You'll also need [Grunt][grunt] to be installed globally in order to run tests, you can do this with `npm install -g grunt-cli`.
Once you've done this, you'll need to start the application in test mode with:
```sh
$ grunt start-test
```
Now you'll be able to run the following commands: Now you'll be able to run the following commands:
```sh ```sh
$ grunt # Run the lint and test tasks together $ grunt # Run the lint and test tasks together
$ grunt lint # Run JSHint with the correct config $ grunt lint # Run JSHint with the correct config
$ grunt compile # Compile front-end assets $ grunt compile # Compile front-end assets
$ grunt start # Run app in development mode, restarting if files change $ grunt start # Run app in development mode, restarting if files change
$ grunt watch # Watch for file changes and compile assets $ grunt start-test # Run app in test mode, restarting if files change
$ grunt test # Run functional tests
$ grunt watch # Watch for file changes and compile assets
``` ```
Code with lint errors or failing tests will not be accepted, please use the build tools outlined above. Code with lint errors or failing tests will not be accepted, please use the build tools outlined above.
@@ -88,7 +99,11 @@ pa11y-dashboard is licensed under the [GNU General Public License 3.0][gpl].
[gpl]: http://www.gnu.org/licenses/gpl-3.0.html [gpl]: http://www.gnu.org/licenses/gpl-3.0.html
[grunt]: http://gruntjs.com/ [grunt]: http://gruntjs.com/
[mongo]: http://www.mongodb.org/
[node]: http://nodejs.org/ [node]: http://nodejs.org/
[pa11y]: https://github.com/nature/pa11y [pa11y]: https://github.com/nature/pa11y
[pa11y-webservice]: https://github.com/nature/pa11y-webservice [pa11y-webservice]: https://github.com/nature/pa11y-webservice
[supervisor]: https://github.com/isaacs/node-supervisor [pa11y-webservice-config]: https://github.com/nature/pa11y-webservice#configurations
[phantom]: http://phantomjs.org/
[travis]: https://travis-ci.org/nature/pa11y-dashboard
[travis-img]: https://travis-ci.org/nature/pa11y-dashboard.png?branch=master

26
app.js
View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var createClient = require('pa11y-webservice-client-node'); var createClient = require('pa11y-webservice-client-node');
@@ -13,11 +28,16 @@ module.exports = initApp;
function initApp (config, callback) { function initApp (config, callback) {
config = defaultConfig(config); config = defaultConfig(config);
var webserviceUrl = config.webservice;
if (typeof webserviceUrl == 'object') {
webserviceUrl = 'http://' + webserviceUrl.host + ':' + webserviceUrl.port + '/';
}
var app = new EventEmitter(); var app = new EventEmitter();
app.address = null; app.address = null;
app.express = express(); app.express = express();
app.server = http.createServer(app.express); app.server = http.createServer(app.express);
app.webservice = createClient(config.webservice); app.webservice = createClient(webserviceUrl);
// Compression // Compression
app.express.use(express.compress()); app.express.use(express.compress());
@@ -44,6 +64,7 @@ function initApp (config, callback) {
// View helpers // View helpers
require('./view/helper/date')(hbs.registerHelper); require('./view/helper/date')(hbs.registerHelper);
require('./view/helper/string')(hbs.registerHelper);
require('./view/helper/url')(hbs.registerHelper); require('./view/helper/url')(hbs.registerHelper);
// Populate view locals // Populate view locals
@@ -73,6 +94,9 @@ function initApp (config, callback) {
require('./route/new')(app); require('./route/new')(app);
require('./route/task/delete')(app); require('./route/task/delete')(app);
require('./route/task/run')(app); require('./route/task/run')(app);
require('./route/task/edit')(app);
require('./route/task/ignore')(app);
require('./route/task/unignore')(app);
} }
// Error handling // Error handling

View File

@@ -1,6 +1,12 @@
{ {
"webservice": "http://localhost:3000/",
"port": 4000, "port": 4000,
"noindex": true, "noindex": true,
"readonly": false "readonly": false,
"webservice": {
"database": "mongodb://localhost/pa11y-webservice-dev",
"host": "0.0.0.0",
"port": 3000,
"cron": "0 30 0 * * *"
}
} }

View File

@@ -1,6 +1,12 @@
{ {
"webservice": "http://localhost:3000/",
"port": 4000, "port": 4000,
"noindex": true, "noindex": true,
"readonly": false "readonly": false,
"webservice": {
"database": "mongodb://localhost/pa11y-webservice",
"host": "0.0.0.0",
"port": 3000,
"cron": "0 30 0 * * *"
}
} }

12
config/test.sample.json Normal file
View File

@@ -0,0 +1,12 @@
{
"port": 4000,
"noindex": true,
"readonly": false,
"webservice": {
"database": "mongodb://localhost/pa11y-webservice-test",
"host": "0.0.0.0",
"port": 3000,
"cron": "0 30 0 * * *"
}
}

View File

@@ -1,3 +1,18 @@
// 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/>.
/* jshint maxlen: false */ /* jshint maxlen: false */
'use strict'; 'use strict';

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var chalk = require('chalk'); var chalk = require('chalk');
@@ -18,4 +33,14 @@ require('./app')(config, function (err, app) {
console.error(chalk.grey(stack.join('\n'))); console.error(chalk.grey(stack.join('\n')));
}); });
// Start the webservice if required
if (typeof config.webservice === 'object') {
require('pa11y-webservice')(config.webservice, function (err, webservice) {
console.log('');
console.log(chalk.underline.cyan('pa11y-webservice started'));
console.log(chalk.grey('mode: %s'), process.env.NODE_ENV);
console.log(chalk.grey('uri: %s'), webservice.server.info.uri);
});
}
}); });

View File

@@ -1,6 +1,6 @@
{ {
"name": "pa11y-dashboard", "name": "pa11y-dashboard",
"version": "1.0.0", "version": "1.3.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",
@@ -25,7 +25,8 @@
"express": "~3.4", "express": "~3.4",
"express-hbs": "~0.2", "express-hbs": "~0.2",
"moment": "~2.2", "moment": "~2.2",
"pa11y-webservice-client-node": "~1.0", "pa11y-webservice": "~1.3",
"pa11y-webservice-client-node": "~1.1",
"underscore": "~1.5" "underscore": "~1.5"
}, },
"devDependencies": { "devDependencies": {
@@ -35,10 +36,14 @@
"grunt-contrib-less": "~0.8", "grunt-contrib-less": "~0.8",
"grunt-contrib-uglify": "~0.2", "grunt-contrib-uglify": "~0.2",
"grunt-contrib-watch": "~0.5", "grunt-contrib-watch": "~0.5",
"grunt-nodemon": "~0.1" "grunt-mocha-test": "~0.7",
"grunt-nodemon": "~0.1",
"jsdom": "~0.8",
"proclaim": "~2.0",
"request": "~2.27"
}, },
"scripts": { "scripts": {
"start": "node ." "start": "node ."
} }
} }

File diff suppressed because one or more lines are too long

View File

@@ -1,3 +1,18 @@
// 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/>.
$(document).ready(function(){ $(document).ready(function(){
var data = {}; var data = {};
@@ -17,8 +32,13 @@ $(document).ready(function(){
hoverable: true hoverable: true
}, },
xaxis: { xaxis: {
mode: 'categories', mode: 'time',
tickLength: 0 tickLength: 0,
minTickSize: [1, 'day'],
timeformat: '%d %b'
},
yaxis: {
tickDecimals: 0
}, },
lines: { lines: {
lineWidth: 3 lineWidth: 3
@@ -112,7 +132,7 @@ $(document).ready(function(){
} }
function getXAxisLabel (el) { function getXAxisLabel (el) {
return el.find('[data-role="category"]').html(); return el.find('[data-role="date"]').attr('data-value');
} }
function storeDatum (el, label) { function storeDatum (el, label) {
@@ -167,16 +187,24 @@ $(document).ready(function(){
$.each(datasets, function(key, val) { $.each(datasets, function(key, val) {
var lowerCaseValue = (val.label.substring(0, val.label.length - 1)).toLowerCase(); var lowerCaseValue = (val.label.substring(0, val.label.length - 1)).toLowerCase();
choiceContainer.append('<li class="text-center '+ choiceContainer.append(
lowerCaseValue +'"><div class="series-checkbox-container"><input type="checkbox" name="' + key + '<li class="text-center ' + lowerCaseValue + '">' +
'" checked="checked" id="id' + key + '<div class="series-checkbox-container">' +
'"/><label for="id' + key + '<input type="checkbox"' +
'"><span class="stat-type">' + val.label + 'name="' + key + '" ' +
'</span></label></div></li>'); 'checked="checked" ' +
'id="id' + key + '"' +
'/>' +
'<label for="id' + key + '">' +
'<span class="stat-type">' + val.label + '</span>' +
'</label>' +
'</div>' +
'</li>'
);
}); });
choiceContainer.find('input').click(plotAccordingToChoices); choiceContainer.find('input').click(plotAccordingToChoices);
function plotAccordingToChoices() { function plotAccordingToChoices() {
var data = []; var data = [];
choiceContainer.find('input:checked').each(function () { choiceContainer.find('input:checked').each(function () {
@@ -185,14 +213,14 @@ $(document).ready(function(){
data.push(datasets[key]); data.push(datasets[key]);
} }
}); });
if (data.length > -1) { if (data.length > -1) {
$.plot(graphContainer, data, graphOptions); $.plot(graphContainer, data, graphOptions);
} }
} }
function showTooltip(x, y, contents) { function showTooltip(x, y, contents) {
$('<div data-role="tooltip" class="tooltip in"><div class="tooltip-inner">' + $('<div data-role="tooltip" class="tooltip tooltip-graph in"><div class="tooltip-inner">' +
contents + contents +
'</div></div>').css({top: y + 5,left: x + 5}).appendTo('body').fadeIn(200); '</div></div>').css({top: y + 5,left: x + 5}).appendTo('body').fadeIn(200);
} }
@@ -203,8 +231,13 @@ $(document).ready(function(){
if (previousPoint != item.dataIndex) { if (previousPoint != item.dataIndex) {
previousPoint = item.dataIndex; previousPoint = item.dataIndex;
$('[data-role="tooltip"]').remove(); $('[data-role="tooltip"]').remove();
var y = item.datapoint[1].toFixed(0); var count = item.datapoint[1].toFixed(0);
var contents = '<h6 class="crunch">' + y + ' ' + item.series.label + '</h6>'; var date = $.plot.formatDate(new Date(item.datapoint[0]), '%d %b' +
'<small> (%H:%M)</small>');
var contents = '<p class="crunch">' +
date + '<br/>' +
count + ' ' + item.series.label +
'</[h6]>';
showTooltip(item.pageX, item.pageY, contents); showTooltip(item.pageX, item.pageY, contents);
} }
} else { } else {
@@ -212,4 +245,45 @@ $(document).ready(function(){
previousPoint = null; previousPoint = null;
} }
}); });
});
// Task filter
function initTaskFilter (container) {
var tasks = initTaskFilterTasks(container);
var input = initTaskFilterInput(container, tasks);
}
function initTaskFilterTasks (container) {
var tasks = container.find('[data-role=task]');
return tasks;
}
function initTaskFilterInput (container, tasks) {
var input = container.find('[data-role=input]');
input.on('keyup', function () {
filterTasks(tasks, input.val());
});
return input;
}
function filterTasks (tasks, query) {
query = $.trim(query.replace(/[^a-z0-9\s]+/gi, ''));
tasks.removeClass('hidden');
if (/^\s*$/.test(query)) {
return;
}
var queryRegExp = new RegExp('(' + query.replace(/\s+/gi, '|') + ')', 'i');
tasks.filter(function () {
return !queryRegExp.test($(this).data('keywords'));
}).addClass('hidden');
}
var taskLists = $('[data-control=task-list]');
if (taskLists.length > 0) {
$('[data-control=task-list]').each(function () {
initTaskFilter($(this));
});
}
});

File diff suppressed because one or more lines are too long

93
public/less/ie8and.less Normal file
View File

@@ -0,0 +1,93 @@
// Making up for the non support of IE8 and IE7 in Bootstrap 3
// Amend the width of container if you want to here
@container-md-ie8: @container-md;
@grid-adjustment: percentage(@grid-gutter-width / @container-md-ie8);
.ie7, .ie8 {
* {
box-sizing: content-box;
}
.clearfix {
*zoom: 1;
}
ul, ol {
margin-left:0;
}
.container {
width: @container-md-ie8;
}
.col-md-1,
.col-md-2,
.col-md-3,
.col-md-4,
.col-md-5,
.col-md-6,
.col-md-7,
.col-md-8,
.col-md-9,
.col-md-10,
.col-md-11 {
float: left;
}
.col-md-1 { width: percentage((1 / @grid-columns)) - @grid-adjustment; }
.col-md-2 { width: percentage((2 / @grid-columns)) - @grid-adjustment; }
.col-md-3 { width: percentage((3 / @grid-columns)) - @grid-adjustment; }
.col-md-4 { width: percentage((4 / @grid-columns)) - @grid-adjustment; }
.col-md-5 { width: percentage((5 / @grid-columns)) - @grid-adjustment; }
.col-md-6 { width: percentage((6 / @grid-columns)) - @grid-adjustment; }
.col-md-7 { width: percentage((7 / @grid-columns)) - @grid-adjustment; }
.col-md-8 { width: percentage((8 / @grid-columns)) - @grid-adjustment; }
.col-md-9 { width: percentage((9 / @grid-columns)) - @grid-adjustment; }
.col-md-10 { width: percentage((10/ @grid-columns)) - @grid-adjustment; }
.col-md-11 { width: percentage((11/ @grid-columns)) - @grid-adjustment; }
.col-md-12 { width: 100% - @grid-adjustment; }
// Push and pull columns for source order changes
.col-md-push-0 { left: auto; }
.col-md-push-1 { left: percentage((1 / @grid-columns)); }
.col-md-push-2 { left: percentage((2 / @grid-columns)); }
.col-md-push-3 { left: percentage((3 / @grid-columns)); }
.col-md-push-4 { left: percentage((4 / @grid-columns)); }
.col-md-push-5 { left: percentage((5 / @grid-columns)); }
.col-md-push-6 { left: percentage((6 / @grid-columns)); }
.col-md-push-7 { left: percentage((7 / @grid-columns)); }
.col-md-push-8 { left: percentage((8 / @grid-columns)); }
.col-md-push-9 { left: percentage((9 / @grid-columns)); }
.col-md-push-10 { left: percentage((10/ @grid-columns)); }
.col-md-push-11 { left: percentage((11/ @grid-columns)); }
.col-md-pull-0 { right: auto; }
.col-md-pull-1 { right: percentage((1 / @grid-columns)); }
.col-md-pull-2 { right: percentage((2 / @grid-columns)); }
.col-md-pull-3 { right: percentage((3 / @grid-columns)); }
.col-md-pull-4 { right: percentage((4 / @grid-columns)); }
.col-md-pull-5 { right: percentage((5 / @grid-columns)); }
.col-md-pull-6 { right: percentage((6 / @grid-columns)); }
.col-md-pull-7 { right: percentage((7 / @grid-columns)); }
.col-md-pull-8 { right: percentage((8 / @grid-columns)); }
.col-md-pull-9 { right: percentage((9 / @grid-columns)); }
.col-md-pull-10 { right: percentage((10/ @grid-columns)); }
.col-md-pull-11 { right: percentage((11/ @grid-columns)); }
// Offsets
.col-md-offset-0 { margin-left: 0; }
.col-md-offset-1 { margin-left: percentage((1 / @grid-columns)); }
.col-md-offset-2 { margin-left: percentage((2 / @grid-columns)); }
.col-md-offset-3 { margin-left: percentage((3 / @grid-columns)); }
.col-md-offset-4 { margin-left: percentage((4 / @grid-columns)); }
.col-md-offset-5 { margin-left: percentage((5 / @grid-columns)); }
.col-md-offset-6 { margin-left: percentage((6 / @grid-columns)); }
.col-md-offset-7 { margin-left: percentage((7 / @grid-columns)); }
.col-md-offset-8 { margin-left: percentage((8 / @grid-columns)); }
.col-md-offset-9 { margin-left: percentage((9 / @grid-columns)); }
.col-md-offset-10 { margin-left: percentage((10/ @grid-columns)); }
.col-md-offset-11 { margin-left: percentage((11/ @grid-columns)); }
.clearfix {
*zoom: 1;
}
}

View File

@@ -58,4 +58,5 @@
@import "site-responsive.less"; @import "site-responsive.less";
// Stupid ie // Stupid ie
@import "site-ie8and.less"; @import "ie8and.less";
@import "site-ie8and.less";

View File

@@ -1,75 +1,20 @@
// 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/>.
// Making up for the non support of IE8 and IE7 in Bootstrap 3 // Making up for the non support of IE8 and IE7 in Bootstrap 3
.ie7, .ie8 { .ie7, .ie8 {
.container {
width: @container-md;
}
.col-md-1,
.col-md-2,
.col-md-3,
.col-md-4,
.col-md-5,
.col-md-6,
.col-md-7,
.col-md-8,
.col-md-9,
.col-md-10,
.col-md-11 {
float: left;
}
.col-md-1 { width: percentage((1 / @grid-columns)); }
.col-md-2 { width: percentage((2 / @grid-columns)); }
.col-md-3 { width: percentage((3 / @grid-columns)); }
.col-md-4 { width: percentage((4 / @grid-columns)); }
.col-md-5 { width: percentage((5 / @grid-columns)); }
.col-md-6 { width: percentage((6 / @grid-columns)); }
.col-md-7 { width: percentage((7 / @grid-columns)); }
.col-md-8 { width: percentage((8 / @grid-columns)); }
.col-md-9 { width: percentage((9 / @grid-columns)); }
.col-md-10 { width: percentage((10/ @grid-columns)); }
.col-md-11 { width: percentage((11/ @grid-columns)); }
.col-md-12 { width: 100%; }
// Push and pull columns for source order changes
.col-md-push-0 { left: auto; }
.col-md-push-1 { left: percentage((1 / @grid-columns)); }
.col-md-push-2 { left: percentage((2 / @grid-columns)); }
.col-md-push-3 { left: percentage((3 / @grid-columns)); }
.col-md-push-4 { left: percentage((4 / @grid-columns)); }
.col-md-push-5 { left: percentage((5 / @grid-columns)); }
.col-md-push-6 { left: percentage((6 / @grid-columns)); }
.col-md-push-7 { left: percentage((7 / @grid-columns)); }
.col-md-push-8 { left: percentage((8 / @grid-columns)); }
.col-md-push-9 { left: percentage((9 / @grid-columns)); }
.col-md-push-10 { left: percentage((10/ @grid-columns)); }
.col-md-push-11 { left: percentage((11/ @grid-columns)); }
.col-md-pull-0 { right: auto; }
.col-md-pull-1 { right: percentage((1 / @grid-columns)); }
.col-md-pull-2 { right: percentage((2 / @grid-columns)); }
.col-md-pull-3 { right: percentage((3 / @grid-columns)); }
.col-md-pull-4 { right: percentage((4 / @grid-columns)); }
.col-md-pull-5 { right: percentage((5 / @grid-columns)); }
.col-md-pull-6 { right: percentage((6 / @grid-columns)); }
.col-md-pull-7 { right: percentage((7 / @grid-columns)); }
.col-md-pull-8 { right: percentage((8 / @grid-columns)); }
.col-md-pull-9 { right: percentage((9 / @grid-columns)); }
.col-md-pull-10 { right: percentage((10/ @grid-columns)); }
.col-md-pull-11 { right: percentage((11/ @grid-columns)); }
// Offsets
.col-md-offset-0 { margin-left: 0; }
.col-md-offset-1 { margin-left: percentage((1 / @grid-columns)); }
.col-md-offset-2 { margin-left: percentage((2 / @grid-columns)); }
.col-md-offset-3 { margin-left: percentage((3 / @grid-columns)); }
.col-md-offset-4 { margin-left: percentage((4 / @grid-columns)); }
.col-md-offset-5 { margin-left: percentage((5 / @grid-columns)); }
.col-md-offset-6 { margin-left: percentage((6 / @grid-columns)); }
.col-md-offset-7 { margin-left: percentage((7 / @grid-columns)); }
.col-md-offset-8 { margin-left: percentage((8 / @grid-columns)); }
.col-md-offset-9 { margin-left: percentage((9 / @grid-columns)); }
.col-md-offset-10 { margin-left: percentage((10/ @grid-columns)); }
.col-md-offset-11 { margin-left: percentage((11/ @grid-columns)); }
.legend { .legend {
display: block; display: block;
width: 100%; width: 100%;
@@ -83,14 +28,11 @@
.tooltip-inner { .tooltip-inner {
background-color: #000000; background-color: #000000;
} }
.clearfix {
*zoom: 1;
}
.date { .date {
font-size:85%; font-size:85%;
} }
.task-card-link { .task-card-link {
min-height:190px; min-height:160px;
} }
.series-checkboxes li { .series-checkboxes li {
margin-right:1%; margin-right:1%;
@@ -98,34 +40,41 @@
.stat-type { .stat-type {
font-size:floor(@font-size-base * 0.65); // ~10px; font-size:floor(@font-size-base * 0.65); // ~10px;
} }
}
.ie7 {
ul {
margin-left:0;
}
.col-md-2 { width:14%; }
.col-md-3 { width:22%; }
.col-md-4 {
width: 32%;
padding:0 5px;
}
.col-md-9 { width:72%; }
.col-md-10 { width:80%; }
.col-md-12 { width: 97%; }
.aside .task-stats li {
width:31.5%;
}
.aside .action-buttons .btn { .aside .action-buttons .btn {
width:79%; width:79%;
} }
.date-selector { .pull-right.dropdown-menu {
zoom:1; right:134px;
}
.run-details .pull-right.dropdown-menu {
right:64px;
}
.date-selector .btn-full-width {
width:90%;
}
.filter-toggle {
&:before {
height:110%;
width:100%;
left:0;
top:0;
}
input {
width:92%;
}
.filter-trigger {
padding-bottom:0;
}
}
}
.ie7 {
.aside .task-stats li {
width:31.5%;
} }
.zfix { .zfix {
position:relative; position:relative;
z-index:100; z-index:1001;
} }
.list-group li .list-group-item { .list-group li .list-group-item {
margin:0; margin:0;
@@ -136,6 +85,44 @@
padding-bottom:90px; padding-bottom:90px;
} }
.date-selector { .date-selector {
margin-top:-155px; zoom:1;
.btn-full-width {
width:100%;
}
} }
} .sr-only {
position:relative;
}
.breadcrumb li {
vertical-align: top;
zoom: 1;
display: inline;
margin-right:10px;
}
.pull-right.dropdown-menu {
right:0px;
}
.run-details .pull-right.dropdown-menu {
right:82px;
}
.tasks-list li {
padding-right:105px;
}
.filter-toggle {
width:30%;
margin:0 35%;
margin-top:-10px;
background-color:lighten(@gray-lighter, 4%);
padding-bottom:10px;
.glyphicon {
display:none;
}
input {
width:80%;
margin-left:-25%;
}
}
}

View File

@@ -1,3 +1,18 @@
// 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/>.
/* No javascript */ /* No javascript */
.no-javascript { .no-javascript {
.graph-container, .expander { .graph-container, .expander {
@@ -10,7 +25,7 @@
.date-links.hidden { .date-links.hidden {
display:none !important; display:none !important;
} }
.date-selector .btn-group:hover ul.date-links { .btn-group:hover ul {
display:block !important; display:block !important;
} }
table.hidden { table.hidden {
@@ -26,4 +41,7 @@
.show-class { .show-class {
display:block; display:block;
} }
} .no-js-hide {
display: none;
}
}

View File

@@ -1,9 +1,19 @@
// 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/>.
/* Media queries */ /* Media queries */
@media (max-width:1199px) {
.task-card .task-card-link {
min-height:215px;
}
}
@media (max-width:991px) { @media (max-width:991px) {
.h1, h1 { .h1, h1 {
font-size:floor(@font-size-base * 2.15); // ~32px; font-size:floor(@font-size-base * 2.15); // ~32px;
@@ -11,9 +21,6 @@
.h2, h2 { .h2, h2 {
font-size:floor(@font-size-base * 1.9); // ~28px; font-size:floor(@font-size-base * 1.9); // ~28px;
} }
.task-card .task-card-link {
min-height:200px;
}
.task-header .h4 { .task-header .h4 {
font-size:floor(@font-size-base * 1.15); // ~17px; font-size:floor(@font-size-base * 1.15); // ~17px;
} }
@@ -36,11 +43,16 @@
.other-tasks { .other-tasks {
.sr-only(); .sr-only();
} }
.action-buttons,
.task-stats, .task-stats,
.btn-full-width { .btn-full-width {
margin-bottom:0; margin-bottom:0;
} }
.task-card .task-stats {
margin-bottom:10px;
}
.action-buttons {
margin-bottom:20px;
}
.aside { .aside {
margin-bottom:15px; margin-bottom:15px;
} }
@@ -54,7 +66,7 @@
float:none; float:none;
li { li {
width:33%; width:25%;
} }
} }
} }
@@ -75,6 +87,7 @@
} }
.action-buttons { .action-buttons {
margin-top:10px; margin-top:10px;
margin-bottom:0;
.btn-full-width { .btn-full-width {
margin-bottom:10px; margin-bottom:10px;
@@ -85,6 +98,9 @@
text-align:left; text-align:left;
} }
} }
.task-card .task-card-link {
min-height:0;
}
} }
@media (max-width:640px) { @media (max-width:640px) {
body { body {
@@ -126,6 +142,11 @@
margin-bottom:20px; margin-bottom:20px;
} }
} }
.footer .nav {
li {
width:50%;
}
}
} }
@media (max-width:479px) { @media (max-width:479px) {
.graph { .graph {
@@ -170,4 +191,4 @@
} }
} }
} }
} }

View File

@@ -1,3 +1,18 @@
// 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/>.
/* Site specific styling */ /* Site specific styling */
/* Helper Classes */ /* Helper Classes */
@@ -70,7 +85,7 @@
} }
.dropdown-toggle { .dropdown-toggle {
margin-top:0; margin-top:0;
margin-bottom:10px; margin-bottom:5px;
} }
.list-group { .list-group {
li .list-group-item { li .list-group-item {
@@ -86,15 +101,11 @@
border-bottom-right-radius:3px; border-bottom-right-radius:3px;
margin-bottom:0; margin-bottom:0;
} }
} }
.delete-button { .options-button {
position:absolute; position:absolute;
top:5px; top:5px;
right:20px; right:20px;
color:@brand-danger;
}
.delete-button:hover {
color:lighten(@brand-danger, 8%);
} }
.footer a, .breadcrumb a { .footer a, .breadcrumb a {
text-decoration:underline; text-decoration:underline;
@@ -183,15 +194,15 @@
} }
.task-card-link { .task-card-link {
color:@gray-dark; color:@gray-dark;
min-height:200px; min-height:190px;
display:block; display:block;
transition: background 0.5s; transition: background 0.5s;
-webkit-transition: background 0.5s; -webkit-transition: background 0.5s;
} }
.delete-button { .options-button {
display:none; display:none;
} }
&:hover .delete-button { &:hover .options-button {
display:block; display:block;
} }
.task-card-link:hover, .task-card-link:hover,
@@ -211,14 +222,15 @@
text-align:center; text-align:center;
color:@badge-color; color:@badge-color;
} }
.dropdown-menu {
top:25px;
}
} }
/* Badges */ /* Badges */
.badge { .badge {
border-radius:0.25em; border-radius:0.25em;
}
.badge {
display:inline-block; display:inline-block;
padding:10px; padding:10px;
font-size:ceil(@font-size-base * 0.85); // ~13px; font-size:ceil(@font-size-base * 0.85); // ~13px;
@@ -239,6 +251,9 @@
h2 { h2 {
word-wrap:break-word; word-wrap:break-word;
} }
.h4 {
margin-bottom:22px;
}
} }
.date { .date {
margin-top:5px; margin-top:5px;
@@ -254,10 +269,27 @@
li { li {
margin-bottom:20px; margin-bottom:20px;
padding-right:90px;
position:relative;
&:last-child { &:last-child {
margin-bottom:0; margin-bottom:0;
} }
form {
display:none;
position:absolute;
right:0;
top:0;
&:hover .btn {
color:@brand-primary;
}
}
&:hover form {
display:block;
}
} }
.rule-name { .rule-name {
@@ -265,6 +297,7 @@
font-style:italic; font-style:italic;
word-wrap:break-word; word-wrap:break-word;
} }
} }
.task-danger { .task-danger {
border-color:@brand-danger; border-color:@brand-danger;
@@ -321,7 +354,7 @@ ul.date-links {
} }
&.single-result { &.single-result {
margin-top:-55px; margin-top:-52px;
.show-stats { .show-stats {
display:none; display:none;
@@ -347,7 +380,7 @@ ul.date-links {
} }
.series-checkboxes { .series-checkboxes {
margin-top:15px; margin-top:15px;
li { li {
width:32%; width:32%;
margin-right:2%; margin-right:2%;
@@ -383,6 +416,14 @@ ul.date-links {
margin-top:-24px; margin-top:-24px;
margin-right:35px margin-right:35px
} }
.flot-x-axis {
.flot-tick-label {
max-width:45px !important;
}
}
.tooltip-graph {
font-size:12px;
}
/* New task page */ /* New task page */
.standards-lists { .standards-lists {
@@ -407,7 +448,33 @@ ul.date-links {
} }
} }
/* Sidebar */ .filter-toggle {
.action-buttons { top:-20px;
margin-bottom:30px; margin-top:-10px;
font-size:18px;
font-weight:bold;
.filter-trigger {
padding-bottom:20px;
cursor: pointer;
.glyphicon {
display:block;
margin:0 auto;
}
}
&:before {
position:absolute;
content:"";
height:90px;
width:90px;
left:50%;
top:-45px;
background-color:lighten(@gray-lighter, 4%);
transform: translateX(-50%) rotate(45deg);
-ms-transform: translateX(-50%) rotate(45deg);
-webkit-transform: translateX(-50%) rotate(45deg);
z-index:-1;
}
} }

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var presentTask = require('../view/presenter/task'); var presentTask = require('../view/presenter/task');

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var getStandards = require('../data/standards'); var getStandards = require('../data/standards');

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var moment = require('moment'); var moment = require('moment');
@@ -46,13 +61,13 @@ function route (app) {
app.express.get('/:id/:rid.csv', getTaskAndResult, function (req, res) { app.express.get('/:id/:rid.csv', getTaskAndResult, function (req, res) {
var task = res.locals.task; var task = res.locals.task;
var result = res.locals.result; var result = res.locals.result;
var rows = ['"code", "message", "type"']; var rows = ['"code","message","type"'];
result.results.forEach(function (msg) { result.results.forEach(function (msg) {
rows.push([ rows.push([
JSON.stringify(msg.code), JSON.stringify(msg.code),
JSON.stringify(msg.message), JSON.stringify(msg.message),
JSON.stringify(msg.type) JSON.stringify(msg.type)
].join(', ')); ].join(','));
}); });
res.attachment(getDownloadFileName(task, result, 'csv')); res.attachment(getDownloadFileName(task, result, 'csv'));
res.send(rows.join('\n')); res.send(rows.join('\n'));

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var presentTask = require('../../view/presenter/task'); var presentTask = require('../../view/presenter/task');

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var _ = require('underscore'); var _ = require('underscore');
@@ -15,7 +30,7 @@ function route (app) {
} }
res.render('task/delete', { res.render('task/delete', {
task: presentTask(task), task: presentTask(task),
isTaskPage: true isTaskSubPage: true
}); });
}); });
}); });

87
route/task/edit.js Normal file
View File

@@ -0,0 +1,87 @@
// 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 _ = require('underscore');
var presentTask = require('../../view/presenter/task');
var getStandards = require('../../data/standards');
module.exports = route;
// Route definition
function route (app) {
app.express.get('/:id/edit', function (req, res, next) {
app.webservice.task(req.params.id).get({}, function (err, task) {
if (err) {
return next();
}
var standards = getStandards().map(function (standard) {
if (standard.title === task.standard) {
standard.selected = true;
}
standard.rules = standard.rules.map(function (rule) {
if (task.ignore.indexOf(rule.name) !== -1) {
rule.ignored = true;
}
return rule;
});
return standard;
});
res.render('task/edit', {
edited: (typeof req.query.edited !== 'undefined'),
standards: standards,
task: presentTask(task),
isTaskSubPage: true
});
});
});
app.express.post('/:id/edit', function (req, res, next) {
app.webservice.task(req.params.id).get({}, function (err, task) {
if (err) {
return next();
}
req.body.ignore = req.body.ignore || [];
app.webservice.task(req.params.id).edit(req.body, function (err) {
if (err) {
task.name = req.body.name;
task.ignore = req.body.ignore;
var standards = getStandards().map(function (standard) {
if (standard.title === task.standard) {
standard.selected = true;
}
standard.rules = standard.rules.map(function (rule) {
if (task.ignore.indexOf(rule.name) !== -1) {
rule.ignored = true;
}
return rule;
});
return standard;
});
return res.render('task/edit', {
error: err,
standards: standards,
task: task,
isTaskSubPage: true
});
}
res.redirect('/' + req.params.id + '/edit?edited');
});
});
});
}

30
route/task/ignore.js Normal file
View File

@@ -0,0 +1,30 @@
'use strict';
var _ = require('underscore');
var presentTask = require('../../view/presenter/task');
var getStandards = require('../../data/standards');
module.exports = route;
// Route definition
function route (app) {
app.express.post('/:id/ignore', function (req, res, next) {
app.webservice.task(req.params.id).get({}, function (err, task) {
if (err) {
return next();
}
var edit = {
name: task.name,
ignore: task.ignore
};
if (typeof req.body.rule === 'string') {
edit.ignore.push(req.body.rule);
}
app.webservice.task(req.params.id).edit(edit, function () {
res.redirect('/' + req.params.id + '?rule-ignored');
});
});
});
}

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var presentTask = require('../../view/presenter/task'); var presentTask = require('../../view/presenter/task');
@@ -25,6 +40,8 @@ function route (app) {
mainResult: task.lastResult || null, mainResult: task.lastResult || null,
added: (typeof req.query.added !== 'undefined'), added: (typeof req.query.added !== 'undefined'),
running: (typeof req.query.running !== 'undefined'), running: (typeof req.query.running !== 'undefined'),
ruleIgnored: (typeof req.query['rule-ignored'] !== 'undefined'),
ruleUnignored: (typeof req.query['rule-unignored'] !== 'undefined'),
hasOneResult: (presentedResults.length < 2), hasOneResult: (presentedResults.length < 2),
isTaskPage: true isTaskPage: true
}); });

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
module.exports = route; module.exports = route;

31
route/task/unignore.js Normal file
View File

@@ -0,0 +1,31 @@
'use strict';
var _ = require('underscore');
var presentTask = require('../../view/presenter/task');
var getStandards = require('../../data/standards');
module.exports = route;
// Route definition
function route (app) {
app.express.post('/:id/unignore', function (req, res, next) {
app.webservice.task(req.params.id).get({}, function (err, task) {
if (err) {
return next();
}
var edit = {
name: task.name,
ignore: task.ignore
};
var indexOfRule = edit.ignore.indexOf(req.body.rule);
if (typeof req.body.rule === 'string' && indexOfRule !== -1) {
edit.ignore.splice(indexOfRule, 1);
}
app.webservice.task(req.params.id).edit(edit, function () {
res.redirect('/' + req.params.id + '?rule-unignored');
});
});
});
}

View File

@@ -0,0 +1,66 @@
// 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 jsdom = require('jsdom');
var request = require('request');
module.exports = createNavigator;
// Create a navigate function
function createNavigator (baseUrl, store) {
return function (opts, callback) {
store.body = null;
store.dom = null;
store.request = null;
store.response = null;
store.status = null;
store.window = null;
request({
url: baseUrl + opts.endpoint,
method: opts.method || 'GET',
body: opts.body,
json: true,
qs: opts.query,
followAllRedirects: true
}, function (err, res, body) {
store.body = body;
store.request = res.request;
store.response = res;
store.status = res.statusCode;
if (opts.nonDom) {
store.window = null;
store.dom = null;
callback();
} else {
jsdom.env(
store.body,
function (err, window) {
store.window = window;
store.dom = window.document;
callback();
}
);
}
});
};
}

View File

@@ -0,0 +1,29 @@
// 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 createClient = require('pa11y-webservice-client-node');
module.exports = createWebserviceClient;
// Create a webservice client
function createWebserviceClient (config) {
var webserviceUrl = config.webservice;
if (typeof webserviceUrl == 'object') {
webserviceUrl = 'http://' + webserviceUrl.host + ':' + webserviceUrl.port + '/';
}
return createClient(webserviceUrl);
}

View File

@@ -0,0 +1,94 @@
// 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/>.
/* global beforeEach, describe, it */
/* jshint maxlen: false, maxstatements: false */
'use strict';
var assert = require('proclaim');
describe('GET /', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/'
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should display an "Add new URL" button', function () {
var elem = this.last.dom.querySelectorAll('[data-test=add-task]');
assert.strictEqual(elem.length, 1);
assert.strictEqual(elem[0].getAttribute('href'), '/new');
});
it('should display all of the expected tasks', function () {
var tasks = this.last.dom.querySelectorAll('[data-test=task]');
assert.strictEqual(tasks.length, 3);
assert.match(tasks[0].textContent, /npg home\s+\(wcag2aa\)/i);
assert.match(tasks[1].textContent, /npg home\s+\(wcag2aaa\)/i);
assert.match(tasks[2].textContent, /nature news\s+\(section508\)/i);
});
it('should have links to each task', function () {
var tasks = this.last.dom.querySelectorAll('[data-test=task]');
assert.strictEqual(tasks[0].querySelectorAll('[href="/abc000000000000000000001"]').length, 1);
assert.strictEqual(tasks[1].querySelectorAll('[href="/abc000000000000000000002"]').length, 1);
assert.strictEqual(tasks[2].querySelectorAll('[href="/abc000000000000000000003"]').length, 1);
});
it('should display an "Edit" button for each task', function () {
var tasks = this.last.dom.querySelectorAll('[data-test=task]');
assert.strictEqual(tasks[0].querySelectorAll('[href="/abc000000000000000000001/edit"]').length, 1);
assert.strictEqual(tasks[1].querySelectorAll('[href="/abc000000000000000000002/edit"]').length, 1);
assert.strictEqual(tasks[2].querySelectorAll('[href="/abc000000000000000000003/edit"]').length, 1);
});
it('should display a "Delete" button for each task', function () {
var tasks = this.last.dom.querySelectorAll('[data-test=task]');
assert.strictEqual(tasks[0].querySelectorAll('[href="/abc000000000000000000001/delete"]').length, 1);
assert.strictEqual(tasks[1].querySelectorAll('[href="/abc000000000000000000002/delete"]').length, 1);
assert.strictEqual(tasks[2].querySelectorAll('[href="/abc000000000000000000003/delete"]').length, 1);
});
it('should display a "Run" button for each task', function () {
var tasks = this.last.dom.querySelectorAll('[data-test=task]');
assert.strictEqual(tasks[0].querySelectorAll('[href="/abc000000000000000000001/run"]').length, 1);
assert.strictEqual(tasks[1].querySelectorAll('[href="/abc000000000000000000002/run"]').length, 1);
assert.strictEqual(tasks[2].querySelectorAll('[href="/abc000000000000000000003/run"]').length, 1);
});
it('should display the task result counts if the task has been run', function () {
var tasks = this.last.dom.querySelectorAll('[data-test=task]');
assert.match(tasks[0].textContent, /1\s*errors/i);
assert.match(tasks[0].textContent, /2\s*warnings/i);
assert.match(tasks[0].textContent, /3\s*notices/i);
});
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]');
assert.match(tasks[2].textContent, /no results/i);
});
it('should not display an alert message', function () {
assert.strictEqual(this.last.dom.querySelectorAll('[data-test=alert]').length, 0);
});
});

View File

@@ -0,0 +1,151 @@
// 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/>.
/* global beforeEach, describe, it */
/* jshint maxlen: false, maxstatements: false */
'use strict';
var assert = require('proclaim');
describe('GET /new', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/new'
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should not display an error message', function () {
assert.strictEqual(this.last.dom.querySelectorAll('[data-test=error]').length, 0);
});
it('should have an "Add new URL" form', function () {
var form = this.last.dom.querySelectorAll('[data-test=new-url-form]')[0];
assert.isDefined(form);
assert.strictEqual(form.getAttribute('action'), '/new');
assert.strictEqual(form.getAttribute('method'), 'post');
});
describe('"Add New URL" form', function () {
beforeEach(function () {
this.form = this.last.dom.querySelectorAll('[data-test=new-url-form]')[0];
});
it('should have a "name" field', function () {
var field = this.form.querySelectorAll('input[name=name]')[0];
assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'text');
assert.strictEqual(field.getAttribute('value'), '');
});
it('should have a "url" field', function () {
var field = this.form.querySelectorAll('input[name=url]')[0];
assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'url');
assert.strictEqual(field.getAttribute('value'), '');
});
it('should have a "standard" field', function () {
var field = this.form.querySelectorAll('select[name=standard]')[0];
assert.isDefined(field);
assert.strictEqual(field.querySelectorAll('option').length, 4);
});
it('should have "ignore" fields', function () {
var fields = this.form.querySelectorAll('input[name="ignore[]"]');
assert.isDefined(fields);
assert.notStrictEqual(fields.length, 0);
});
});
});
describe('POST /new', function () {
describe('with invalid query', function () {
beforeEach(function (done) {
var req = {
method: 'POST',
endpoint: '/new',
body: {
name: '',
url: ''
}
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should display an error message', function () {
assert.strictEqual(this.last.dom.querySelectorAll('[data-test=error]').length, 1);
});
});
describe('with valid query', function () {
beforeEach(function (done) {
var req = {
method: 'POST',
endpoint: '/new',
body: {
name: 'Example',
url: 'http://example.com/',
standard: 'WCAG2AA'
}
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should create the task', function (done) {
this.webservice.tasks.get({}, function (err, tasks) {
assert.strictEqual(tasks.length, 4);
done();
});
});
it('should redirect me to the new URL page', function () {
assert.match(this.last.request.uri.pathname, /^\/[a-z0-9]{24}$/i);
});
it('should not display an error message', function () {
assert.strictEqual(this.last.dom.querySelectorAll('[data-test=error]').length, 0);
});
it('should display a success message', function () {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0];
assert.isDefined(alert);
assert.match(alert.textContent, /url has been added/i);
});
});
});

View File

@@ -0,0 +1,68 @@
// 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/>.
/* global beforeEach, describe, it */
/* jshint maxlen: false, maxstatements: false */
'use strict';
var assert = require('proclaim');
describe('GET /<task-id>/<result-id>.csv', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/abc000000000000000000001/def000000000000000000001.csv',
nonDom: true
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should output CSV results', function () {
assert.match(this.last.body, /^"code","message","type"/);
});
});
describe('GET /<task-id>/<result-id>.json', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/abc000000000000000000001/def000000000000000000001.json',
nonDom: true
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should output JSON results', function () {
var json = this.last.body;
assert.strictEqual(json.task.name, 'NPG Home');
assert.strictEqual(json.task.url, 'nature.com');
assert.strictEqual(json.count.error, 1);
assert.strictEqual(json.count.warning, 2);
assert.strictEqual(json.count.notice, 3);
assert.isArray(json.results);
});
});

View File

@@ -0,0 +1,70 @@
// 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/>.
/* global beforeEach, describe, it */
/* jshint maxlen: false, maxstatements: false */
'use strict';
var assert = require('proclaim');
describe('GET /<task-id>/<result-id>', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/abc000000000000000000001/def000000000000000000001'
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should display a "Download CSV" button', function () {
var elem = this.last.dom.querySelectorAll('[data-test=download-csv]');
assert.strictEqual(elem.length, 1);
assert.strictEqual(elem[0].getAttribute('href'), '/abc000000000000000000001/def000000000000000000001.csv');
});
it('should display a "Download JSON" button', function () {
var elem = this.last.dom.querySelectorAll('[data-test=download-json]');
assert.strictEqual(elem.length, 1);
assert.strictEqual(elem[0].getAttribute('href'), '/abc000000000000000000001/def000000000000000000001.json');
});
it('should display a link back to the task', function () {
assert.isDefined(this.last.dom.querySelectorAll('[href="/abc000000000000000000001"]')[0]);
});
it('should display errors', function () {
var elem = this.last.dom.querySelectorAll('[data-test=task-errors]')[0];
assert.isDefined(elem);
assert.match(elem.textContent, /errors \( 1 \)/i);
});
it('should display warnings', function () {
var elem = this.last.dom.querySelectorAll('[data-test=task-warnings]')[0];
assert.isDefined(elem);
assert.match(elem.textContent, /warnings \( 2 \)/i);
});
it('should display notices', function () {
var elem = this.last.dom.querySelectorAll('[data-test=task-notices]')[0];
assert.isDefined(elem);
assert.match(elem.textContent, /notices \( 3 \)/i);
});
});

View File

@@ -0,0 +1,80 @@
// 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/>.
/* global beforeEach, describe, it */
/* jshint maxlen: false, maxstatements: false */
'use strict';
var assert = require('proclaim');
describe('GET /<task-id>/delete', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/abc000000000000000000001/delete'
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should have a "Delete URL" form', function () {
var form = this.last.dom.querySelectorAll('[data-test=delete-url-form]')[0];
assert.isDefined(form);
assert.strictEqual(form.getAttribute('action'), '/abc000000000000000000001/delete');
assert.strictEqual(form.getAttribute('method'), 'post');
});
it('should display a link back to the task page', function () {
assert.greaterThan(this.last.dom.querySelectorAll('[href="/abc000000000000000000001"]').length, 0);
});
});
describe('POST /<task-id>/delete', function () {
beforeEach(function (done) {
var req = {
method: 'POST',
endpoint: '/abc000000000000000000001/delete'
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should delete the task', function (done) {
this.webservice.task('abc000000000000000000001').get({}, function (err) {
assert.strictEqual(err.message, 'Error 404');
done();
});
});
it('should redirect me to the home page', function () {
assert.strictEqual(this.last.request.uri.pathname, '/');
});
it('should display a success message', function () {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0];
assert.isDefined(alert);
assert.match(alert.textContent, /been deleted/i);
});
});

View File

@@ -0,0 +1,117 @@
// 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/>.
/* global beforeEach, describe, it */
/* jshint maxlen: false, maxstatements: false */
'use strict';
var assert = require('proclaim');
describe('GET /<task-id>/edit', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/abc000000000000000000001/edit'
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should have an "Edit URL" form', function () {
var form = this.last.dom.querySelectorAll('[data-test=edit-url-form]')[0];
assert.isDefined(form);
assert.strictEqual(form.getAttribute('action'), '/abc000000000000000000001/edit');
assert.strictEqual(form.getAttribute('method'), 'post');
});
it('should display a link back to the task page', function () {
assert.greaterThan(this.last.dom.querySelectorAll('[href="/abc000000000000000000001"]').length, 0);
});
describe('"Edit URL" form', function () {
beforeEach(function () {
this.form = this.last.dom.querySelectorAll('[data-test=edit-url-form]')[0];
});
it('should have a "name" field', function () {
var field = this.form.querySelectorAll('input[name=name]')[0];
assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'text');
assert.strictEqual(field.getAttribute('value'), 'NPG Home');
});
it('should have a disabled "url" field', function () {
var field = this.form.querySelectorAll('input[name=url]')[0];
assert.isDefined(field);
assert.strictEqual(field.getAttribute('type'), 'url');
assert.strictEqual(field.getAttribute('value'), 'nature.com');
assert.isDefined(field.getAttribute('disabled'));
});
it('should have a disabled "standard" field', function () {
var field = this.form.querySelectorAll('select[name=standard]')[0];
assert.isDefined(field);
assert.isDefined(field.getAttribute('disabled'));
});
it('should have "ignore" fields', function () {
var fields = this.form.querySelectorAll('input[name="ignore[]"]');
assert.isDefined(fields);
assert.notStrictEqual(fields.length, 0);
});
});
});
describe('POST /<task-id>/edit', function () {
beforeEach(function (done) {
var req = {
method: 'POST',
endpoint: '/abc000000000000000000001/edit',
body: {
name: 'foo',
ignore: ['bar', 'baz']
}
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should edit the task', function (done) {
this.webservice.task('abc000000000000000000001').get({}, function (err, task) {
assert.strictEqual(task.name, 'foo');
assert.deepEqual(task.ignore, ['bar', 'baz']);
done();
});
});
it('should display a success message', function () {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0];
assert.isDefined(alert);
assert.match(alert.textContent, /been saved/i);
});
});

View File

@@ -0,0 +1,111 @@
// 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/>.
/* global beforeEach, describe, it */
/* jshint maxlen: false, maxstatements: false */
'use strict';
var assert = require('proclaim');
describe('GET /<task-id>', function () {
describe('when task has results', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/abc000000000000000000001'
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should display an "Edit" button', function () {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/edit"]').length, 1);
});
it('should display a "Delete" button', function () {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/delete"]').length, 1);
});
it('should display a "Run" button', function () {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/run"]').length, 1);
});
it('should display a "Download CSV" button for the latest result', function () {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/def000000000000000000001.csv"]').length, 1);
});
it('should display a "Download JSON" button for the latest result', function () {
assert.strictEqual(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/def000000000000000000001.json"]').length, 1);
});
it('should display links to all results', function () {
assert.isDefined(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/def000000000000000000001"]')[0]);
assert.isDefined(this.last.dom.querySelectorAll('[href="/abc000000000000000000001/def000000000000000000003"]')[0]);
});
it('should display errors', function () {
var elem = this.last.dom.querySelectorAll('[data-test=task-errors]')[0];
assert.isDefined(elem);
assert.match(elem.textContent, /errors \( 1 \)/i);
});
it('should display warnings', function () {
var elem = this.last.dom.querySelectorAll('[data-test=task-warnings]')[0];
assert.isDefined(elem);
assert.match(elem.textContent, /warnings \( 2 \)/i);
});
it('should display notices', function () {
var elem = this.last.dom.querySelectorAll('[data-test=task-notices]')[0];
assert.isDefined(elem);
assert.match(elem.textContent, /notices \( 3 \)/i);
});
});
describe('when task has no results', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/abc000000000000000000003'
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should display a "Run" button', function () {
var elem = this.last.dom.querySelectorAll('[data-test=run-task]');
assert.strictEqual(elem.length, 1);
assert.strictEqual(elem[0].getAttribute('href'), '/abc000000000000000000003/run');
});
it('should display a message indicating that there are no results', function () {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0];
assert.isDefined(alert);
assert.match(alert.textContent, /there are no results to show/i);
});
});
});

View File

@@ -0,0 +1,46 @@
// 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/>.
/* global beforeEach, describe, it */
/* jshint maxlen: false, maxstatements: false */
'use strict';
var assert = require('proclaim');
describe('GET /<task-id>/run', function () {
beforeEach(function (done) {
var req = {
method: 'GET',
endpoint: '/abc000000000000000000001/run'
};
this.navigate(req, done);
});
it('should send a 200 status', function () {
assert.strictEqual(this.last.status, 200);
});
it('should redirect me to the task page', function () {
assert.strictEqual(this.last.request.uri.pathname, '/abc000000000000000000001');
});
it('should display a success message', function () {
var alert = this.last.dom.querySelectorAll('[data-test=alert]')[0];
assert.isDefined(alert);
assert.match(alert.textContent, /new results are being generated/i);
});
});

51
test/functional/setup.js Normal file
View File

@@ -0,0 +1,51 @@
// 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/>.
/* global afterEach, before */
/* jshint maxlen: false, maxstatements: false */
'use strict';
var config = require('../../config/test.json');
var createNavigator = require('./helper/navigate');
var createWebserviceClient = require('./helper/webservice');
var loadFixtures = require('pa11y-webservice/data/fixture/load');
var request = require('request');
// Run before all tests
before(function (done) {
this.baseUrl = 'http://localhost:' + config.port;
this.last = {};
this.navigate = createNavigator(this.baseUrl, this.last);
this.webservice = createWebserviceClient(config);
assertTestAppIsRunning(this.baseUrl, function () {
loadFixtures('test', config.webservice, done);
});
});
// Run after each test
afterEach(function (done) {
loadFixtures('test', config.webservice, done);
});
// Check that the test application is running, and exit if not
function assertTestAppIsRunning (url, done) {
request(url, function (err) {
if (err) {
console.error('Error: Test app not started; run with `grunt start-test`');
process.exit(1);
}
done();
});
}

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
<div class="col-md-8"> <div class="col-md-8">
<h1>Hmmm, this page indicates a 404 error.</h1> <h1>Hmmm, this page indicates a 404 error.</h1>
<p class="h2">That is techy babble for "We couldn't find the page you were looking for".</h2> <p class="h2">That is techy babble for "We couldn't find the page you were looking for".</h2>

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
<div class="col-md-8"> <div class="col-md-8">
<h1>Eeek! 500 error. This is serious.</h1> <h1>Eeek! 500 error. This is serious.</h1>
<p class="h2">There isn't much you can do about this.</h2> <p class="h2">There isn't much you can do about this.</h2>

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var moment = require('moment'); var moment = require('moment');
@@ -17,4 +32,9 @@ function helper (register) {
return moment(context).fromNow(); return moment(context).fromNow();
}); });
register('date-timestamp', function (context) {
return moment(context).valueOf();
});
} }

12
view/helper/string.js Normal file
View File

@@ -0,0 +1,12 @@
'use strict';
module.exports = helper;
function helper (register) {
// Convert a string to lower-case
register('lowercase', function (context) {
return context.toLowerCase();
});
}

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
module.exports = helper; module.exports = helper;
@@ -6,7 +21,7 @@ function helper (register) {
// 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) { register('simplify-url', function (context) {
return context.replace(/^https?:\/\//i, '').replace(/\/$/, ''); return context.replace(/^https?:\/\//i, '').replace(/\/$/, '').toLowerCase();
}); });
} }

View File

@@ -1,7 +1,23 @@
{{!
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/>.
}}
{{#content "title"}}pa11y-dashboard{{/content}} {{#content "title"}}pa11y-dashboard{{/content}}
{{#if siteMessage}} {{#if siteMessage}}
<div class="col-md-12 clearfix"> <div class="col-md-12 clearfix" data-test="alert">
<div class="alert alert-info site-message"> <div class="alert alert-info site-message">
<h3 class="crunch-top"><span class="pull-left glyphicon glyphicon-exclamation-sign"></span> Important</h3> <h3 class="crunch-top"><span class="pull-left glyphicon glyphicon-exclamation-sign"></span> Important</h3>
<p class="h5">{{siteMessage}}</p> <p class="h5">{{siteMessage}}</p>
@@ -10,7 +26,7 @@
{{/if}} {{/if}}
{{#deleted}} {{#deleted}}
<div class="col-md-12 clearfix"> <div class="col-md-12 clearfix" data-test="alert">
<div class="alert alert-info"> <div class="alert alert-info">
<button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button> <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
<strong>Bye Bye URL</strong> <strong>Bye Bye URL</strong>

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="{{lang}}" class="no-javascript"> <html lang="{{lang}}" class="no-javascript">
<head> <head>
@@ -17,7 +33,7 @@
<meta name="viewport" content="width=device-width"/> <meta name="viewport" content="width=device-width"/>
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Lato:400,700,900,400italic"/> <link rel="stylesheet" href="//fonts.googleapis.com/css?family=Lato:400,700,900,400italic"/>
<link rel="stylesheet" href="/css/site.min.css"/> <link rel="stylesheet" href="/css/site.min.css?v={{version}}"/>
</head> </head>
<!--[if IE 7]><body class="ie7"><![endif]--> <!--[if IE 7]><body class="ie7"><![endif]-->
@@ -47,7 +63,7 @@
{{> page-footer}} {{> page-footer}}
<script type="text/javascript" src="/js/site.min.js"></script> <script type="text/javascript" src="/js/site.min.js?v={{version}}"></script>
<!--[if lte IE 8]><script language="javascript" type="text/javascript" src="/js/vendor/flot/excanvas.min.js"></script><![endif]--> <!--[if lte IE 8]><script language="javascript" type="text/javascript" src="/js/vendor/flot/excanvas.min.js"></script><![endif]-->
</body> </body>

View File

@@ -1,16 +1,32 @@
{{!
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/>.
}}
{{#content "title"}} {{#content "title"}}
Add a new URL Add a new URL
{{/content}} {{/content}}
<form role="form" class="col-md-12" action="/new" method="post"> <form role="form" class="col-md-12" action="/new" method="post" data-test="new-url-form">
<div class="legend"> <div class="legend">
<h1 class="h2 crunch-top">Add a new URL</h1> <h1 class="h2 crunch-top">Add a new URL</h1>
</div> </div>
{{#error}} {{#error}}
<div class="col-md-12 clearfix"> <div class="col-md-12 clearfix" data-test="error">
<div class="alert alert-danger"> <div class="alert alert-danger">
<strong>Oh my gosh!</strong> <strong>Oh my gosh!</strong>
<p>{{.}}</p> <p>{{.}}</p>

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
{{#unless isHomePage}} {{#unless isHomePage}}
<div class="container"> <div class="container">
<div class="row"> <div class="row">
@@ -7,13 +23,16 @@
{{#if isNewTaskPage}} {{#if isNewTaskPage}}
<li class="active">Add URL</li> <li class="active">Add URL</li>
{{/if}} {{/if}}
{{#if isTaskPage}} {{#if isTaskPage}}
<li class="active">{{task.name}}</li> <li class="active">{{task.name}}</li>
{{/if}} {{/if}}
{{#if isTaskSubPage}}
<li><a href="{{task.href}}">{{task.name}}</a></li>
{{/if}}
{{#if isResultPage}} {{#if isResultPage}}
<li><a href="{{task.href}}">{{task.name}}</a></li> <li><a href="{{task.href}}">{{task.name}}</a></li>
<li class="active">Results for {{date-format mainResult.date format="DD MMM YYYY"}}</li> <li class="active">Results for {{date-format mainResult.date format="DD MMM YYYY"}}</li>
{{/if}} {{/if}}
</ol> </ol>
</div> </div>
</div> </div>

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
<div class="col-md-12 clearfix"> <div class="col-md-12 clearfix">
<div class="graph-container graph-spacer ruled"> <div class="graph-container graph-spacer ruled">
<div data-role="graph" class="graph"></div> <div data-role="graph" class="graph"></div>
@@ -23,7 +39,7 @@
<tbody> <tbody>
{{#results}} {{#results}}
<tr data-role="url-stats"> <tr data-role="url-stats">
<td data-role="category">{{date-format date format="DD MMM YYYY"}}</td> <td data-value="{{date-timestamp date}}" data-role="date">{{date-format date format="DD MMM YYYY"}}</td>
<td class="text-center" data-label="error">{{count.error}}</td> <td class="text-center" data-label="error">{{count.error}}</td>
<td class="text-center" data-label="warning">{{count.warning}}</td> <td class="text-center" data-label="warning">{{count.warning}}</td>
<td class="text-center" data-label="notice">{{count.notice}}</td> <td class="text-center" data-label="notice">{{count.notice}}</td>
@@ -32,4 +48,4 @@
</tbody> </tbody>
</table> </table>
</div> </div>
</div> </div>

View File

@@ -1,10 +1,26 @@
{{!
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/>.
}}
<footer> <footer>
<div class="footer" role="contentinfo"> <div class="footer" role="contentinfo">
<div class="container"> <div class="container">
<div class="col-md-7"> <div class="col-md-5">
<small>&copy; {{year}} Nature Publishing Group.<br/>pa11y dashboard is licensed under the GNU General Public License 3.0.<br/>Version {{version}}</small> <small>&copy; {{year}} Nature Publishing Group.<br/>pa11y dashboard is licensed under the GNU General Public License 3.0.<br/>Version {{version}}</small>
</div> </div>
<div class="col-md-5 clearfix"> <div class="col-md-7 clearfix">
<ul class="crunch-bottom floated-list nav"> <ul class="crunch-bottom floated-list nav">
<li> <li>
<a href="{{repo}}">GitHub Repo</a> <a href="{{repo}}">GitHub Repo</a>
@@ -15,8 +31,11 @@
<li> <li>
<a href="http://www.w3.org/TR/WCAG20/">WCAG 2.0 spec</a> <a href="http://www.w3.org/TR/WCAG20/">WCAG 2.0 spec</a>
</li> </li>
<li>
<a href="http://squizlabs.github.io/HTML_CodeSniffer/">HTML_CodeSniffer</a>
</li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>
</footer> </footer>

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
<header> <header>
<div role="banner" class="header"> <div role="banner" class="header">
<div class="container"> <div class="container">

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
<div class="col-md-12"> <div class="col-md-12">
<div class="ruled task-header"> <div class="ruled task-header">
<div class="row clearfix"> <div class="row clearfix">

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
<div class="col-md-12 zfix"> <div class="col-md-12 zfix">
<div class="row"> <div class="row">
<div class="col-md-4 col-md-offset-4 col-sm-6 col-sm-offset-3"> <div class="col-md-4 col-md-offset-4 col-sm-6 col-sm-offset-3">

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
<div class="col-md-3 aside"> <div class="col-md-3 aside">
<div class="row"> <div class="row">
<div id="top" class="col-md-12 col-sm-6 col-xs-12"> <div id="top" class="col-md-12 col-sm-6 col-xs-12">
@@ -12,42 +28,49 @@
<div class="action-buttons col-md-12 col-sm-6 clearfix"> <div class="action-buttons col-md-12 col-sm-6 clearfix">
<div class="row"> <div class="row">
<div class="col-md-12 col-sm-6 col-xs-12"> <div class="col-md-12 col-sm-6 col-xs-12">
<a href="{{mainResult.hrefCsv}}" class="btn-full-width btn btn-default">Download CSV <span class="glyphicon glyphicon-download"></span></a> <a href="{{mainResult.hrefCsv}}" class="btn-full-width btn btn-default" data-test="download-csv">
Download CSV <span class="glyphicon glyphicon-download"></span>
</a>
</div> </div>
<div class="col-md-12 col-sm-6 col-xs-12"> <div class="col-md-12 col-sm-6 col-xs-12">
<a href="{{mainResult.hrefJson}}" class="btn-full-width btn btn-default">Download JSON <span class="glyphicon glyphicon-download"></span></a> <a href="{{mainResult.hrefJson}}" class="btn-full-width btn btn-default" data-test="download-json">
Download JSON <span class="glyphicon glyphicon-download"></span>
</a>
</div> </div>
</div> </div>
</div>
</div>
<div class="row">
<div class="col-md-12 col-sm-12 clearfix">
<div class="well">
<h4 class="crunch-top">View results in browser</h4>
<p class="crunch-bottom">pa11y uses HTML_CodeSniffer to find accessibility issues. <a href="http://squizlabs.github.io/HTML_CodeSniffer/">Use their bookmarklet</a> to view results on the page you are testing.</p>
</div>
</div> </div>
</div> </div>
<!-- ########### Functionality to be done ############# -->
<!-- List of other URLs -->
<!-- <div class="other-tasks well">
<h4 class="crunch-top ruled-sm">Your other tracked URLs</h4>
<p>No other URLs</p>
<ul class="list-unstyled crunch-bottom">
<li><a href="empty-task">rowanmanning.com</a></li>
<li><a href="task">nature.com</a></li>
</ul>
</div> -->
<!-- ##################### End ######################## -->
</div> </div>
<div class="col-md-9"> <div class="col-md-9">
{{#if mainResult.count.error}} {{#if mainResult.count.error}}
<div class="heading label-danger showing first" id="errors"> <div class="heading label-danger showing first" id="errors" data-test="task-errors">
<span data-role="expander" class="pull-right expander"> - </span> <span data-role="expander" class="pull-right expander"> - </span>
Errors ( {{mainResult.count.error}} ) Errors ( {{mainResult.count.error}} )
</div> </div>
<div class="task-danger tasks-list collapse clearfix in"> <div class="task-danger tasks-list collapse clearfix in">
<ul class="list-unstyled"> <ul class="list-unstyled">
{{#mainResult.errors}} {{#mainResult.errors}}
<li> <li>
<p class="crunch rule-name">{{code}} <span class="badge">{{count}}</span></p> <p class="crunch rule-name">{{code}} <span class="badge">{{count}}</span></p>
<p>{{message}}</p> <p>{{message}}</p>
{{#unless readonly}}
{{#if ../../isTaskPage}}
<form action="{{../../../task.hrefIgnore}}" method="post">
<input type="hidden" name="rule" value="{{code}}"/>
<input type="submit" class="btn btn-sm" value="Ignore rule"/>
</form>
{{/if}}
{{/unless}}
</li> </li>
{{/mainResult.errors}} {{/mainResult.errors}}
</ul> </ul>
@@ -58,7 +81,7 @@
{{/if}} {{/if}}
{{#if mainResult.count.warning}} {{#if mainResult.count.warning}}
<div class="heading label-warning" id="warnings"> <div class="heading label-warning" id="warnings" data-test="task-warnings">
<span data-role="expander" class="pull-right expander"> + </span> <span data-role="expander" class="pull-right expander"> + </span>
Warnings ( {{mainResult.count.warning}} ) Warnings ( {{mainResult.count.warning}} )
</div> </div>
@@ -68,6 +91,14 @@
<li> <li>
<p class="crunch rule-name">{{code}} <span class="badge">{{count}}</span></p> <p class="crunch rule-name">{{code}} <span class="badge">{{count}}</span></p>
<p>{{message}}</p> <p>{{message}}</p>
{{#unless readonly}}
{{#if ../../isTaskPage}}
<form action="{{../../../task.hrefIgnore}}" method="post">
<input type="hidden" name="rule" value="{{code}}"/>
<input type="submit" class="btn btn-sm" value="Ignore rule"/>
</form>
{{/if}}
{{/unless}}
</li> </li>
{{/mainResult.warnings}} {{/mainResult.warnings}}
</ul> </ul>
@@ -79,7 +110,7 @@
{{/if}} {{/if}}
{{#if mainResult.count.notice}} {{#if mainResult.count.notice}}
<div class="heading label-info" id="notices"> <div class="heading label-info" id="notices" data-test="task-notices">
<span data-role="expander" class="pull-right expander"> + </span> <span data-role="expander" class="pull-right expander"> + </span>
Notices ( {{mainResult.count.notice}} ) Notices ( {{mainResult.count.notice}} )
</div> </div>
@@ -89,6 +120,14 @@
<li> <li>
<p class="crunch rule-name">{{code}} <span class="badge">{{count}}</span></p> <p class="crunch rule-name">{{code}} <span class="badge">{{count}}</span></p>
<p>{{message}}</p> <p>{{message}}</p>
{{#unless readonly}}
{{#if ../../isTaskPage}}
<form action="{{../../../task.hrefIgnore}}" method="post">
<input type="hidden" name="rule" value="{{code}}"/>
<input type="submit" class="btn btn-sm" value="Ignore rule"/>
</form>
{{/if}}
{{/unless}}
</li> </li>
{{/mainResult.notices}} {{/mainResult.notices}}
</ul> </ul>
@@ -98,25 +137,33 @@
<p class="heading label-info" id="notices">Well done! You have 0 notices. <span class="glyphicon glyphicon-ok pull-right"></span></p> <p class="heading label-info" id="notices">Well done! You have 0 notices. <span class="glyphicon glyphicon-ok pull-right"></span></p>
{{/if}} {{/if}}
{{#if task.ignore.length}} {{#if mainResult.ignore.length}}
<div class="heading label-default"> <div class="heading label-default">
<span data-role="expander" class="pull-right expander"> + </span> <span data-role="expander" class="pull-right expander"> + </span>
Ignored Rules Ignored Rules ( {{mainResult.ignore.length}} )
</div> </div>
<div class="task-default tasks-list collapse clearfix"> <div class="task-default tasks-list collapse clearfix">
<ul class="list-unstyled"> <ul class="list-unstyled">
{{#task.ignore}} {{#mainResult.ignore}}
<li> <li>
<p class="crunch rule-name">{{name}}</p> <p class="crunch rule-name">{{name}}</p>
{{#if description}} {{#if description}}
<p>{{description}}</p> <p>{{description}}</p>
{{/if}} {{/if}}
{{#unless readonly}}
{{#if ../../isTaskPage}}
<form action="{{../../../task.hrefUnignore}}" method="post">
<input type="hidden" name="rule" value="{{name}}"/>
<input type="submit" class="btn btn-sm" value="Unignore rule"/>
</form>
{{/if}}
{{/unless}}
</li> </li>
{{/task.ignore}} {{/mainResult.ignore}}
</ul> </ul>
<a class="pull-right" href="#top" data-role="top">Back to top</a> <a class="pull-right" href="#top" data-role="top">Back to top</a>
</div> </div>
{{else}} {{else}}
<p class="heading label-default">You have no ignored rules.</p> <p class="heading label-default">You have no ignored rules.</p>
{{/if}} {{/if}}
</div> </div>

View File

@@ -1,4 +1,20 @@
<div class="col-md-12"> {{!
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/>.
}}
<div class="col-md-12 zfix">
<div class="ruled task-header"> <div class="ruled task-header">
<div class="row clearfix"> <div class="row clearfix">
<div class="col-md-9 col-sm-9"> <div class="col-md-9 col-sm-9">
@@ -7,7 +23,15 @@
</div> </div>
<div class="col-md-3 col-sm-3 text-right run-details"> <div class="col-md-3 col-sm-3 text-right run-details">
{{#unless readonly}} {{#unless readonly}}
<a href="{{task.hrefRun}}" class="btn btn-success">Run <span class="glyphicon glyphicon-play"></span></a> <div class="btn-group">
<button type="button" class="btn btn-info dropdown-toggle" data-toggle="dropdown">Options <span class="caret"></span></button>
<ul class="dropdown-menu pull-right" role="menu">
<li><a href="/{{task.id}}/edit">Edit this task</a></li>
<li><a href="/{{task.id}}/delete">Delete this task</a></li>
<li class="divider"></li>
<li><a href="{{task.hrefRun}}" data-test="run-task">Run pa11y</a></li>
</ul>
</div>
{{/unless}} {{/unless}}
{{#if mainResult}} {{#if mainResult}}
<div class="date">Last run : {{date-format mainResult.date format="DD MMM YYYY"}}</div> <div class="date">Last run : {{date-format mainResult.date format="DD MMM YYYY"}}</div>
@@ -17,4 +41,4 @@
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -1,40 +1,75 @@
{{!
This file is part of pa11y-dashboard.
<ul class="list-unstyled clearfix crunch-bottom"> pa11y-dashboard is free software: you can redistribute it and/or modify
<li class="col-md-4 col-sm-6 task-card add-task"> it under the terms of the GNU General Public License as published by
{{#if readonly}} the Free Software Foundation, either version 3 of the License, or
{{! TODO PERRY: make this look disabled }} (at your option) any later version.
<span class="well task-card-link crunch-bottom">
<p class="h3 crunch">Add new URL</p> pa11y-dashboard is distributed in the hope that it will be useful,
<p class="supersize-me crunch">+</p> but WITHOUT ANY WARRANTY; without even the implied warranty of
</span> MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
{{else}} GNU General Public License for more details.
<a class="well task-card-link crunch-bottom" data-role="add-task" href="/new">
<p class="h3 crunch">Add new URL</p> You should have received a copy of the GNU General Public License
<p class="supersize-me crunch">+</p> along with pa11y-dashboard. If not, see <http://www.gnu.org/licenses/>.
</a> }}
{{/if}}
</li> <div data-control="task-list">
{{#each tasks}}
<li class="col-md-4 col-sm-6 task-card"> <div class="col-md-6 col-md-offset-3 filter-toggle no-js-hide text-center">
<a class="well task-card-link crunch-bottom" title="Details for URL {{simplify-url url}}" href="{{href}}"> <label for="filter-input" class="filter-trigger" data-toggle="collapse" data-target="#filter-input">Filter<span class="glyphicon glyphicon-filter"></span>
<p class="h3">{{name}}</p> </label>
<p class="h5">({{standard}})</p> <div id="filter-input" class="collapse">
{{#if lastResult}} <input class="form-control" id="task-filter" type="text" data-role="input" placeholder="Type filter term (name or standard)"/>
<ul class="clearfix list-unstyled floated-list task-stats"> </div>
{{#lastResult}} </div>
<li class="danger" title="Number of errors ({{count.error}})">{{count.error}}<span class="stat-type">Errors</span></li>
<li class="warning" title="Number of warnings ({{count.warning}})">{{count.warning}}<span class="stat-type">Warnings</span></li> <ul class="list-unstyled clearfix crunch-bottom">
<li class="info last" title="Number of notices ({{count.notice}})">{{count.notice}}<span class="stat-type">Notices</span></li> <li class="col-md-4 col-sm-6 task-card add-task">
{{/lastResult}} {{#if readonly}}
</ul> <span class="well task-card-link crunch-bottom">
Last run {{date-format lastResult.date format="DD MMM YYYY"}} <p class="h3 crunch">Add new URL</p>
{{else}} <p class="supersize-me crunch">+</p>
<p class="no-results">No results</p> </span>
{{/if}} {{else}}
</a> <a class="well task-card-link crunch-bottom" data-role="add-task" href="/new" data-test="add-task">
{{#unless ../readonly}} <p class="h3 crunch">Add new URL</p>
<a title="Delete this URL" class="delete-button" href="{{hrefDelete}}"><span class="sr-only">Delete </span><span class="glyphicon glyphicon-remove"></span></a> <p class="supersize-me crunch">+</p>
{{/unless}} </a>
{{/if}}
</li> </li>
{{/each}} {{#each tasks}}
</ul> <li class="col-md-4 col-sm-6 task-card" data-test="task" data-role="task" data-keywords="{{lowercase name}} {{lowercase standard}} {{simplify-url url}}">
<a class="well task-card-link crunch-bottom" title="Details for URL {{simplify-url url}}" href="{{href}}">
<p class="h3">{{name}}</p>
<p class="h5">({{standard}})</p>
{{#if lastResult}}
<ul class="clearfix list-unstyled floated-list task-stats">
{{#lastResult}}
<li class="danger" title="Number of errors ({{count.error}})">{{count.error}}<span class="stat-type">Errors</span></li>
<li class="warning" title="Number of warnings ({{count.warning}})">{{count.warning}}<span class="stat-type">Warnings</span></li>
<li class="info last" title="Number of notices ({{count.notice}})">{{count.notice}}<span class="stat-type">Notices</span></li>
{{/lastResult}}
</ul>
Last run {{date-format lastResult.date format="DD MMM YYYY"}}
{{else}}
<p class="no-results">No results</p>
{{/if}}
</a>
{{#unless ../readonly}}
<div class="btn-group options-button text-right">
<button type="button" class="btn btn-info btn-xs dropdown-toggle" data-toggle="dropdown"><span class="sr-only">Options</span><span class="glyphicon glyphicon-cog"></span></button>
<ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{href}}/edit">Edit this task</a></li>
<li><a href="{{href}}/delete">Delete this task</a></li>
<li class="divider"></li>
<li><a href="{{href}}/run" data-test="run-task">Run pa11y</a></li>
</ul>
</div>
{{/unless}}
</li>
{{/each}}
</ul>
</div>

40
view/presenter/ignore.js Normal file
View File

@@ -0,0 +1,40 @@
// 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 standardsArray = require('../../data/standards')();
var rules = createStandardDescriptionMap(standardsArray);
module.exports = presentIgnoreRules;
function presentIgnoreRules (ignore) {
return ignore.map(function (name) {
return {
name: name,
description: rules[name]
};
});
}
function createStandardDescriptionMap (standards) {
var map = {};
standards.forEach(function (standard) {
standard.rules.forEach(function (rule) {
map[rule.name] = rule.description;
});
});
return map;
}

View File

@@ -1,3 +1,18 @@
// 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'; 'use strict';
var _ = require('underscore'); var _ = require('underscore');

View File

@@ -1,6 +1,22 @@
// 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'; 'use strict';
var _ = require('underscore'); var _ = require('underscore');
var presentIgnoreRules = require('./ignore');
module.exports = presentResult; module.exports = presentResult;
@@ -14,6 +30,9 @@ function presentResult (result) {
// Parse date // Parse date
result.date = new Date(result.date); result.date = new Date(result.date);
// Enhance the ignored rules
result.ignore = presentIgnoreRules(result.ignore);
// Split out message types // Split out message types
if (result.results) { if (result.results) {
var groupedByType = _.groupBy(result.results, 'type'); var groupedByType = _.groupBy(result.results, 'type');

View File

@@ -1,9 +1,23 @@
// 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'; 'use strict';
var _ = require('underscore'); var _ = require('underscore');
var presentIgnoreRules = require('./ignore');
var presentResult = require('./result'); var presentResult = require('./result');
var standardsArray = require('../../data/standards')();
var rules = createStandardDescriptionMap(standardsArray);
module.exports = presentTask; module.exports = presentTask;
@@ -14,14 +28,12 @@ function presentTask (task) {
task.hrefDelete = '/' + task.id + '/delete'; task.hrefDelete = '/' + task.id + '/delete';
task.hrefRun = '/' + task.id + '/run'; task.hrefRun = '/' + task.id + '/run';
task.hrefJson = '/' + task.id + '.json'; task.hrefJson = '/' + task.id + '.json';
task.hrefEdit = '/' + task.id + '/edit';
task.hrefIgnore = '/' + task.id + '/ignore';
task.hrefUnignore = '/' + task.id + '/unignore';
// Enhance the ignored rules // Enhance the ignored rules
task.ignore = task.ignore.map(function (name) { task.ignore = presentIgnoreRules(task.ignore);
return {
name: name,
description: rules[name]
};
});
// Present the last result if present // Present the last result if present
if (task.last_result) { if (task.last_result) {
@@ -31,13 +43,3 @@ function presentTask (task) {
return task; return task;
} }
function createStandardDescriptionMap (standards) {
var map = {};
standards.forEach(function (standard) {
standard.rules.forEach(function (rule) {
map[rule.name] = rule.description;
});
});
return map;
}

View File

@@ -1,3 +1,19 @@
{{!
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/>.
}}
{{#content "title"}} {{#content "title"}}
{{task.name}} - {{simplify-url task.url}} ({{task.standard}}) - {{date-format mainResult.date format="DD MMM YYYY"}} {{task.name}} - {{simplify-url task.url}} ({{task.standard}}) - {{date-format mainResult.date format="DD MMM YYYY"}}

View File

@@ -1,9 +1,25 @@
{{!
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/>.
}}
{{#content "title"}} {{#content "title"}}
Delete {{task.url}} ({{task.standard}}) Delete {{task.url}} ({{task.standard}})
{{/content}} {{/content}}
<form class="col-md-12" action="{{task.hrefDelete}}" method="post"> <form class="col-md-12" action="{{task.hrefDelete}}" method="post" data-test="delete-url-form">
<div class="legend"> <div class="legend">
<legend>Delete URL ({{simplify-url task.url}})</legend> <legend>Delete URL ({{simplify-url task.url}})</legend>
</div> </div>

103
view/task/edit.html Normal file
View File

@@ -0,0 +1,103 @@
{{!
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/>.
}}
{{#content "title"}}
Edit URL
{{/content}}
{{#edited}}
<div class="col-md-12 clearfix" data-test="alert">
<div class="alert alert-success">
<button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
<strong>Success!</strong>
<p>Your changes have been saved.</p>
</div>
</div>
{{/edited}}
<form role="form" class="col-md-12" action="/{{task.id}}/edit" method="post" data-test="edit-url-form">
<div class="legend">
<h1 class="h2 crunch-top">Edit URL</h1>
</div>
{{#error}}
<div class="col-md-12 clearfix" data-test="error">
<div class="row">
<div class="alert alert-danger">
<strong>Oh my gosh!</strong>
<p>{{.}}</p>
</div>
</div>
</div>
{{/error}}
<div class="form-group clearfix">
<div class="row">
<div class="col-md-8 col-sm-8 col-xs-10">
<label class="control-label" for="new-task-name">Name</label>
<input class="form-control" id="new-task-name" type="text" placeholder="E.g. My Home Page" name="name" value="{{task.name}}"/>
</div>
</div>
</div>
<div class="form-group clearfix">
<div class="row">
<div class="col-md-8 col-sm-8 col-xs-10">
<label class="control-label" for="new-task-url">URL</label>
<input class="form-control" id="new-task-url" type="url" placeholder="E.g. http://mysite.com/" name="url" value="{{task.url}}" disabled/>
</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-standard">Standard</label>
<select data-role="new-task-select" class="form-control" id="new-task-standard" name="standard" disabled>
{{#standards}}
<option {{#selected}}selected{{/selected}}>{{title}}</option>
{{/standards}}
</select>
</div>
</div>
</div>
<p class="control-label"><b>Ignore these rules</b> <a target="_blank" href="https://github.com/nature/pa11y/wiki/HTML-CodeSniffer-Rules">(full list of rules here)</a></p>
<div class="standards-lists">
{{#standards}}
<div data-role="standards-list" data-attr="{{title}}" class="form-group">
<p class="control-label rules-list-title ruled"><b>{{title}} Rules</b></p>
<ul class="list-unstyled">
{{#rules}}
<li>
<input class="pull-left" id="{{name}}" type="checkbox" name="ignore[]" value="{{name}}" {{#ignored}}checked{{/ignored}}/>
<label for="{{name}}" title="{{description}}" data-role="rules-tooltip" class="checkbox">
{{name}}
</label>
</li>
{{/rules}}
</ul>
</div>
{{/standards}}
</div>
<button type="submit" class="btn btn-success">Save changes <span class="glyphicon glyphicon-save"></span></button>
<a href="/{{task.id}}/edit" class="btn btn-primary">Undo <span class="glyphicon glyphicon-refresh"></span></a>
</form>

View File

@@ -1,10 +1,26 @@
{{!
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/>.
}}
{{#content "title"}} {{#content "title"}}
{{task.name}} - {{simplify-url task.url}} ({{task.standard}}) {{task.name}} - {{simplify-url task.url}} ({{task.standard}})
{{/content}} {{/content}}
{{#added}} {{#added}}
<div class="col-md-12 clearfix"> <div class="col-md-12 clearfix" data-test="alert">
<div class="alert alert-success"> <div class="alert alert-success">
<button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button> <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
<strong>Whoop whoop!</strong> <strong>Whoop whoop!</strong>
@@ -14,7 +30,7 @@
{{/added}} {{/added}}
{{#running}} {{#running}}
<div class="col-md-12 clearfix"> <div class="col-md-12 clearfix" data-test="alert">
<div class="alert alert-success"> <div class="alert alert-success">
<button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button> <button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
<strong>New results incoming!</strong> <strong>New results incoming!</strong>
@@ -26,10 +42,36 @@
</div> </div>
{{/running}} {{/running}}
{{#ruleIgnored}}
<div class="col-md-12 clearfix" data-test="alert">
<div class="alert alert-success">
<button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
<strong>Rule ignored!</strong>
<p>
You've ignored an accessibility rule for this URL.
<a href="{{task.hrefRun}}">Click here to generate results with the ignored rule excluded</a>
</p>
</div>
</div>
{{/ruleIgnored}}
{{#ruleUnignored}}
<div class="col-md-12 clearfix" data-test="alert">
<div class="alert alert-success">
<button aria-hidden="true" data-dismiss="alert" class="close" type="button">×</button>
<strong>Rule unignored!</strong>
<p>
You've removed an ignored accessibility rule for this URL.
<a href="{{task.hrefRun}}">Click here to generate results with the ignored rule included again</a>
</p>
</div>
</div>
{{/ruleUnignored}}
{{> task-header}} {{> task-header}}
{{#if results}} {{#if results}}
{{#unless hasOneResult}} {{#unless hasOneResult}}
{{> graph}} {{> graph}}
{{/unless}} {{/unless}}
@@ -41,11 +83,11 @@
{{#if mainResult}} {{#if mainResult}}
{{> result}} {{> result}}
{{else}} {{else}}
<div class="col-md-12"> <div class="col-md-12" data-test="alert">
<div class="alert alert-info"> <div class="alert alert-info">
<h4>There are no results to show</h4> <h4>There are no results to show</h4>
<p>pa11y has not been run against this URL yet so there are no results to show.</p> <p>pa11y has not been run against this URL yet so there are no results to show.</p>
<p><a href="{{task.hrefRun}}">Click here to generate results for this URL</a>.</p> <p><a href="{{task.hrefRun}}">Click here to generate results for this URL</a>.</p>
</div> </div>
</div> </div>
{{/if}} {{/if}}