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