1
1
var q = require ( 'q' ) ,
2
2
fs = require ( 'fs' ) ,
3
3
path = require ( 'path' ) ,
4
- _ = require ( 'lodash' ) ;
4
+ request = require ( 'request' ) ,
5
+ Entities = require ( 'html-entities' ) . XmlEntities ;
5
6
6
7
/**
7
- * You can enable this plugin in your config file:
8
- *
9
- * // The Chrome Accessibility Developer Tools are currently
10
- * // the only integration option.
8
+ * You can audit your website against the Chrome Accessibility Developer Tools,
9
+ * Tenon.io, or both by enabling this plugin in your config file:
11
10
*
11
+ * // Chrome Accessibility Developer Tools:
12
12
* exports.config = {
13
13
* ...
14
14
* plugins: [{
@@ -17,12 +17,37 @@ var q = require('q'),
17
17
* }]
18
18
* }
19
19
*
20
+ * // Tenon.io:
21
+ *
22
+ * // Read about the Tenon.io settings and API requirements:
23
+ * // -http://tenon.io/documentation/overview.php
24
+ * // -http://tenon.io/documentation/understanding-request-parameters.php
25
+ *
26
+ * exports.config = {
27
+ * ...
28
+ * plugins: [{
29
+ * tenonIO: {
30
+ * options: {
31
+ * key: 'YOUR_API_KEY_HERE', // only required parameter
32
+ * level: 'AA' // WCAG AA OR AAA,
33
+ * filter: '0', // one of 0, 20, 40, 60, 80, 100, defaults to 0
34
+ * certainty: '0', // one of 0, 20, 40, 60, 80, 100, defaults to 0
35
+ * store: '0', // 0 or 1, whether Tenon should store your results, depends on your membership plan
36
+ * },
37
+ * printAll: false, // whether the plugin should log API response
38
+ * },
39
+ * chromeA11YDevTools: false,
40
+ * path: 'node_modules/protractor.plugins/accessiblity'
41
+ * }]
42
+ * }
43
+ *
20
44
*/
21
45
22
46
var AUDIT_FILE = path . join ( __dirname , '../../node_modules/accessibility-developer-tools/dist/js/axs_testing.js' ) ;
47
+ var TENON_URL = 'http://www.tenon.io/api/' ;
23
48
24
49
/**
25
- * Checks the information returned by the accessibility audit and
50
+ * Checks the information returned by the accessibility audit(s) and
26
51
* displays passed/failed results as console output.
27
52
*
28
53
* @param {Object } config The configuration file for the accessibility plugin
@@ -32,76 +57,186 @@ var AUDIT_FILE = path.join(__dirname, '../../node_modules/accessibility-develope
32
57
*/
33
58
function teardown ( config ) {
34
59
60
+ var audits = [ ] ;
61
+
35
62
if ( config . chromeA11YDevTools ) {
63
+ audits . push ( runChromeDevTools ( config ) ) ;
64
+ }
65
+ if ( config . tenonIO ) {
66
+ audits . push ( runTenonIO ( config ) ) ;
67
+ }
68
+ return q . all ( audits ) . then ( function ( ) {
69
+ return outputResults ( ) ;
70
+ } ) ;
71
+ }
36
72
37
- var data = fs . readFileSync ( AUDIT_FILE , 'utf-8' ) ;
38
- data = data + ' return axs.Audit.run();' ;
73
+ var testOut = { failedCount : 0 , specResults : [ ] } ;
74
+ var entities = new Entities ( ) ;
39
75
40
- var testOut = { failedCount : 0 , specResults : [ ] } ,
41
- elementPromises = [ ] ;
76
+ /**
77
+ * Audits page source against the Tenon API, if configured. Requires an API key:
78
+ * more information about licensing and configuration available at
79
+ * http://tenon.io/documentation/overview.php.
80
+ *
81
+ * @param {Object } config The configuration file for the accessibility plugin
82
+ * @return {q.Promise } A promise which resolves to the results of any passed or
83
+ * failed tests
84
+ * @private
85
+ */
86
+ function runTenonIO ( config ) {
87
+ var options = config . tenonIO . options ;
88
+
89
+ return browser . driver . getPageSource ( ) . then ( function ( source ) {
90
+ // setup response as a deferred promise
91
+ var deferred = q . defer ( ) ;
92
+ request . post ( {
93
+ url : TENON_URL ,
94
+ form : {
95
+ key : options . key ,
96
+ src : source ,
97
+ level : options . level ,
98
+ filter : options . filter ,
99
+ certainty : options . certainty ,
100
+ store : options . store
101
+ }
102
+ } ,
103
+ function ( err , httpResponse , body ) {
104
+ if ( err ) return resolve . reject ( new Error ( err ) ) ;
105
+ else return deferred . resolve ( JSON . parse ( body ) ) ;
106
+ } ) ;
42
107
43
- return browser . executeScript_ ( data , 'a11y developer tool rules' ) . then ( function ( results ) {
108
+ return deferred . promise . then ( function ( response ) {
109
+ return processTenonResults ( response ) ;
110
+ } ) ;
111
+ } ) ;
44
112
45
- var audit = results . map ( function ( result ) {
46
- var DOMElements = result . elements ;
47
- if ( DOMElements !== undefined ) {
113
+ function processTenonResults ( response ) {
114
+ var numResults = response . resultSet . length ;
48
115
49
- DOMElements . forEach ( function ( elem ) {
50
- // get elements from WebDriver, add to promises array
51
- elementPromises . push (
52
- elem . getOuterHtml ( ) . then ( function ( text ) {
53
- return {
54
- code : result . rule . code ,
55
- list : text
56
- } ;
57
- } )
58
- ) ;
59
- } ) ;
60
- result . elementCount = DOMElements . length ;
61
- }
62
- return result ;
116
+ testOut . failedCount = numResults ;
117
+
118
+ var testHeader = 'Tenon.io - ' ;
119
+
120
+ if ( numResults === 0 ) {
121
+ return testOut . specResults . push ( {
122
+ description : testHeader + 'All tests passed!' ,
123
+ assertions : [ {
124
+ passed : true ,
125
+ errorMsg : ''
126
+ } ] ,
127
+ duration : 1
128
+ } ) ;
129
+ }
130
+
131
+ if ( config . tenonIO . printAll ) {
132
+ console . log ( '\x1b[32m' , testHeader + 'API response' , '\x1b[39m' ) ;
133
+ console . log ( response ) ;
134
+ }
135
+
136
+ return response . resultSet . forEach ( function ( result ) {
137
+ var errorMsg = result . errorDescription + '\n\n' +
138
+ '\t\t' + entities . decode ( result . errorSnippet ) +
139
+ '\n\n\t\t' + result . ref + '\n' ;
140
+
141
+ testOut . specResults . push ( {
142
+ description : testHeader + result . errorTitle ,
143
+ assertions : [ {
144
+ passed : false ,
145
+ errorMsg : errorMsg
146
+ } ] ,
147
+ duration : 1
63
148
} ) ;
149
+ } ) ;
150
+ }
151
+ }
64
152
65
- // Wait for element names to be fetched
66
- return q . all ( elementPromises ) . then ( function ( elementFailures ) {
67
-
68
- audit . forEach ( function ( result , index ) {
69
- if ( result . result === 'FAIL' ) {
70
- result . passed = false ;
71
- testOut . failedCount ++ ;
72
-
73
- var label = result . elementCount === 1 ? ' element ' : ' elements ' ;
74
- result . output = '\n\t\t' + result . elementCount + label + 'failed:' ;
75
-
76
- // match elements returned via promises
77
- // by their failure codes
78
- elementFailures . forEach ( function ( element , index ) {
79
- if ( element . code === result . rule . code ) {
80
- result . output += '\n\t\t' + elementFailures [ index ] . list ;
81
- }
82
- } ) ;
83
- result . output += '\n\n\t\t' + result . rule . url ;
84
- }
85
- else {
86
- result . passed = true ;
87
- result . output = '' ;
88
- }
89
-
90
- testOut . specResults . push ( {
91
- description : result . rule . heading ,
92
- assertions : [ {
93
- passed : result . passed ,
94
- errorMsg : result . output
95
- } ] ,
96
- duration : 1
97
- } ) ;
153
+ /**
154
+ * Audits page source against the Chrome Accessibility Developer Tools, if configured.
155
+ *
156
+ * @param {Object } config The configuration file for the accessibility plugin
157
+ * @return {q.Promise } A promise which resolves to the results of any passed or
158
+ * failed tests
159
+ * @private
160
+ */
161
+ function runChromeDevTools ( ) {
162
+
163
+ var data = fs . readFileSync ( AUDIT_FILE , 'utf-8' ) ;
164
+ data = data + ' return axs.Audit.run();' ;
165
+
166
+ var elementPromises = [ ] ,
167
+ elementStringLength = 200 ;
168
+
169
+ var testHeader = 'Chrome A11Y - ' ;
170
+
171
+ return browser . executeScript_ ( data , 'a11y developer tool rules' ) . then ( function ( results ) {
172
+
173
+ var audit = results . map ( function ( result ) {
174
+ var DOMElements = result . elements ;
175
+ if ( DOMElements !== undefined ) {
176
+
177
+ DOMElements . forEach ( function ( elem ) {
178
+ // get elements from WebDriver, add to promises array
179
+ elementPromises . push (
180
+ elem . getOuterHtml ( ) . then ( function ( text ) {
181
+ return {
182
+ code : result . rule . code ,
183
+ list : text . substring ( 0 , elementStringLength )
184
+ } ;
185
+ } )
186
+ ) ;
98
187
} ) ;
188
+ result . elementCount = DOMElements . length ;
189
+ }
190
+ return result ;
191
+ } ) ;
192
+
193
+ // Wait for element names to be fetched
194
+ return q . all ( elementPromises ) . then ( function ( elementFailures ) {
195
+
196
+ return audit . forEach ( function ( result , index ) {
197
+ if ( result . result === 'FAIL' ) {
198
+ result . passed = false ;
199
+ testOut . failedCount ++ ;
99
200
100
- if ( ( testOut . failedCount > 0 ) || ( testOut . specResults . length > 0 ) ) {
101
- return testOut ;
201
+ var label = result . elementCount === 1 ? ' element ' : ' elements ' ;
202
+ result . output = '\n\t\t' + result . elementCount + label + 'failed:' ;
203
+
204
+ // match elements returned via promises
205
+ // by their failure codes
206
+ elementFailures . forEach ( function ( element , index ) {
207
+ if ( element . code === result . rule . code ) {
208
+ result . output += '\n\t\t' + elementFailures [ index ] . list ;
209
+ }
210
+ } ) ;
211
+ result . output += '\n\n\t\t' + result . rule . url ;
212
+ }
213
+ else {
214
+ result . passed = true ;
215
+ result . output = '' ;
102
216
}
217
+
218
+ testOut . specResults . push ( {
219
+ description : testHeader + result . rule . heading ,
220
+ assertions : [ {
221
+ passed : result . passed ,
222
+ errorMsg : result . output
223
+ } ] ,
224
+ duration : 1
225
+ } ) ;
103
226
} ) ;
104
227
} ) ;
228
+ } ) ;
229
+ }
230
+
231
+ /**
232
+ * Output results from either plugin configuration.
233
+ *
234
+ * @return {object } testOut An object containing number of failures and spec results
235
+ * @private
236
+ */
237
+ function outputResults ( ) {
238
+ if ( ( testOut . failedCount > 0 ) || ( testOut . specResults . length > 0 ) ) {
239
+ return testOut ;
105
240
}
106
241
}
107
242
0 commit comments