UNPKG

37 kBJavaScriptView Raw
1'use strict';
2
3// Requires
4var path = require('path');
5var zlib = require('zlib');
6var nock = require('nock');
7var gruntMock = require('gruntmock');
8var checkPages = require('../tasks/checkPages.js');
9
10// Block all unexpected network calls
11nock.disableNetConnect();
12
13// Verify a task's output
14function testOutput(test, ok, error) {
15 return function(err, mock) {
16 test.equal(mock.logOk.length, ok.length, 'Wrong logOk count');
17 test.equal(mock.logError.length, error.length, 'Wrong logError count');
18 if (err) {
19 test.equal(err.message, error.slice(-1), 'Wrong exception text');
20 }
21 while (mock.logOk.length && ok.length) {
22 test.equal(mock.logOk.shift().replace(/\(\d+ms\)/g, '(00ms)'), ok.shift(), 'Wrong logOk item');
23 }
24 while (mock.logError.length && error.length) {
25 test.equal(mock.logError.shift().replace(/\(\d+ms\)/g, '(00ms)'), error.shift(), 'Wrong logError item');
26 }
27 test.done();
28 };
29}
30
31/* Helpers for mocking HTTP requests */
32
33function nockFiles(files, base, headers) {
34 var scope = nock(base || 'http://example.com');
35 files.forEach(function(file) {
36 scope
37 .get('/' + file)
38 .replyWithFile(200, path.join(__dirname, file.split('?')[0]), headers);
39 });
40}
41function nockLinks(links, base) {
42 var scope = nock(base || 'http://example.com');
43 links.forEach(function(link) {
44 scope
45 .head('/' + link)
46 .reply(200);
47 });
48}
49function nockRedirect(link, status, noRedirects, noLocation) {
50 var slashLink = '/' + link;
51 var scope = nock('http://example.com')
52 .head(slashLink)
53 .reply(status, '', noLocation ? null : { 'Location': slashLink + '_redirected' });
54 if (noRedirects) {
55 scope
56 .get(slashLink)
57 .reply(status, '', noLocation ? null : { 'Location': slashLink + '_redirected' });
58 } else {
59 scope
60 .head(slashLink + '_redirected')
61 .reply(200);
62 }
63}
64
65exports.checkPages = {
66
67 // Task parameters
68
69 filesPresent: function(test) {
70 test.expect(4);
71 var mock = gruntMock.create({ files: [ { src: ['file'] } ] });
72 mock.invoke(checkPages, testOutput(test,
73 [],
74 ['checkPages task does not use files; remove the files parameter']));
75 },
76
77 pageUrlsMissing: function(test) {
78 test.expect(4);
79 var mock = gruntMock.create();
80 mock.invoke(checkPages, testOutput(test,
81 [],
82 ['pageUrls option is not present; it should be an array of URLs']));
83 },
84
85 pageUrlsWrongType: function(test) {
86 test.expect(4);
87 var mock = gruntMock.create({ options: { pageUrls: 'string' } });
88 mock.invoke(checkPages, testOutput(test,
89 [],
90 ['pageUrls option is invalid; it should be an array of URLs']));
91 },
92
93 linksToIgnoreWrongType: function(test) {
94 test.expect(4);
95 var mock = gruntMock.create({ options: { pageUrls: [], linksToIgnore: 'string' } });
96 mock.invoke(checkPages, testOutput(test,
97 [],
98 ['linksToIgnore option is invalid; it should be an array']));
99 },
100
101 maxResponseTimeWrongType: function(test) {
102 test.expect(4);
103 var mock = gruntMock.create({ options: { pageUrls: [], maxResponseTime: 'string' } });
104 mock.invoke(checkPages, testOutput(test,
105 [],
106 ['maxResponseTime option is invalid; it should be a positive number']));
107 },
108
109 userAgentWrongType: function(test) {
110 test.expect(4);
111 var mock = gruntMock.create({ options: { pageUrls: [], userAgent: 5 } });
112 mock.invoke(checkPages, testOutput(test,
113 [],
114 ['userAgent option is invalid; it should be a string or null']));
115 },
116
117 // Basic functionality
118
119 pageUrlsEmpty: function(test) {
120 test.expect(2);
121 var mock = gruntMock.create({ options: {
122 pageUrls: []
123 }});
124 mock.invoke(checkPages, testOutput(test,
125 [],
126 []));
127 },
128
129 pageUrlsValid: function(test) {
130 test.expect(5);
131 nockFiles(['validPage.html', 'externalLink.html', 'localLinks.html']);
132 nock('http://example.com')
133 .get('/redirect')
134 .reply(301, '', { 'Location': 'http://example.com/redirect2' })
135 .get('/redirect2')
136 .reply(301, '', { 'Location': 'http://example.com/localLinks.html' });
137 var mock = gruntMock.create({ options: {
138 pageUrls: ['http://example.com/validPage.html',
139 'http://example.com/externalLink.html',
140 'http://example.com/redirect']
141 }});
142 mock.invoke(checkPages, testOutput(test,
143 ['Page: http://example.com/validPage.html (00ms)',
144 'Page: http://example.com/externalLink.html (00ms)',
145 'Page: http://example.com/redirect -> http://example.com/localLinks.html (00ms)'],
146 []));
147 },
148
149 pageUrlsNotFound: function(test) {
150 test.expect(5);
151 nock('http://example.com').get('/notFound').reply(404);
152 var mock = gruntMock.create({ options: {
153 pageUrls: ['http://example.com/notFound']
154 }});
155 mock.invoke(checkPages, testOutput(test,
156 [],
157 ['Bad page (404): http://example.com/notFound (00ms)',
158 '1 issue, see above. (Set options.summary for a summary.)']));
159 },
160
161 pageUrlsMultiple: function(test) {
162 test.expect(9);
163 nockFiles(['validPage.html', 'externalLink.html', 'validPage.html']);
164 nock('http://example.com').get('/notFound').reply(404);
165 nock('http://example.com').get('/serverError').reply(500);
166 var mock = gruntMock.create({ options: {
167 pageUrls: ['http://example.com/validPage.html',
168 'http://example.com/notFound',
169 'http://example.com/externalLink.html',
170 'http://example.com/serverError',
171 'http://example.com/validPage.html']
172 }});
173 mock.invoke(checkPages, testOutput(test,
174 ['Page: http://example.com/validPage.html (00ms)',
175 'Page: http://example.com/externalLink.html (00ms)',
176 'Page: http://example.com/validPage.html (00ms)'],
177 ['Bad page (404): http://example.com/notFound (00ms)',
178 'Bad page (500): http://example.com/serverError (00ms)',
179 '2 issues, see above. (Set options.summary for a summary.)']));
180 },
181
182 // checkLinks functionality
183
184 checkLinksValid: function(test) {
185 test.expect(19);
186 nockFiles(['validPage.html']);
187 nockLinks([
188 'link0', 'link1', 'link3', 'link4', 'link5',
189 'link6', 'link7', 'link8', 'link9', 'link10',
190 'link11', 'link12', 'link13']);
191 nockRedirect('movedPermanently', 301);
192 nockRedirect('movedTemporarily', 302);
193 nockLinks(['link2'], 'http://example.org');
194 var mock = gruntMock.create({ options: {
195 pageUrls: ['http://example.com/validPage.html'],
196 checkLinks: true
197 }});
198 mock.invoke(checkPages, testOutput(test,
199 ['Page: http://example.com/validPage.html (00ms)',
200 'Link: http://example.com/link1 (00ms)',
201 'Link: http://example.org/link2 (00ms)',
202 'Link: http://example.com/movedPermanently (00ms)',
203 'Link: http://example.com/movedTemporarily (00ms)',
204 'Link: http://example.com/link3 (00ms)',
205 'Link: http://example.com/link4 (00ms)',
206 'Link: http://example.com/link5 (00ms)',
207 'Link: http://example.com/link6 (00ms)',
208 'Link: http://example.com/link7 (00ms)',
209 'Link: http://example.com/link8 (00ms)',
210 'Link: http://example.com/link0 (00ms)',
211 'Link: http://example.com/link9 (00ms)',
212 'Link: http://example.com/link10 (00ms)',
213 'Link: http://example.com/link11 (00ms)',
214 'Link: http://example.com/link12 (00ms)',
215 'Link: http://example.com/link13 (00ms)'],
216 []));
217 },
218
219 checkRelativeLinksValid: function(test) {
220 test.expect(9);
221 nockFiles(['dir/relativePage.html']);
222 nockLinks([
223 'dir/link0', 'dir/link1', 'link2',
224 'dir/sub/link3', 'dir/sub/link4', 'link5']);
225 var mock = gruntMock.create({ options: {
226 pageUrls: ['http://example.com/dir/relativePage.html'],
227 checkLinks: true
228 }});
229 mock.invoke(checkPages, testOutput(test,
230 ['Page: http://example.com/dir/relativePage.html (00ms)',
231 'Link: http://example.com/dir/link0 (00ms)',
232 'Link: http://example.com/dir/link1 (00ms)',
233 'Link: http://example.com/link2 (00ms)',
234 'Link: http://example.com/dir/sub/link3 (00ms)',
235 'Link: http://example.com/dir/sub/link4 (00ms)',
236 'Link: http://example.com/link5 (00ms)'],
237 []));
238 },
239
240 checkRelativeLinksValidAfterRedirectToFile: function(test) {
241 test.expect(9);
242 nock('http://example.com')
243 .get('/dir')
244 .reply(301, '', { 'Location': 'http://example.com/dir/relativePage.html' });
245 nockFiles(['dir/relativePage.html']);
246 nockLinks([
247 'dir/link0', 'dir/link1', 'link2',
248 'dir/sub/link3', 'dir/sub/link4', 'link5']);
249 var mock = gruntMock.create({ options: {
250 pageUrls: ['http://example.com/dir'],
251 checkLinks: true
252 }});
253 mock.invoke(checkPages, testOutput(test,
254 ['Page: http://example.com/dir -> http://example.com/dir/relativePage.html (00ms)',
255 'Link: http://example.com/dir/link0 (00ms)',
256 'Link: http://example.com/dir/link1 (00ms)',
257 'Link: http://example.com/link2 (00ms)',
258 'Link: http://example.com/dir/sub/link3 (00ms)',
259 'Link: http://example.com/dir/sub/link4 (00ms)',
260 'Link: http://example.com/link5 (00ms)'],
261 []));
262 },
263
264 checkRelativeLinksValidAfterRedirectToDirectory: function(test) {
265 test.expect(9);
266 nock('http://example.com')
267 .get('/dir')
268 .reply(301, '', { 'Location': 'http://example.com/dir/' })
269 .get('/dir/')
270 .replyWithFile(200, path.join(__dirname, 'dir/relativePage.html'));
271 nockLinks([
272 'dir/link0', 'dir/link1', 'link2',
273 'dir/sub/link3', 'dir/sub/link4', 'link5']);
274 var mock = gruntMock.create({ options: {
275 pageUrls: ['http://example.com/dir'],
276 checkLinks: true
277 }});
278 mock.invoke(checkPages, testOutput(test,
279 ['Page: http://example.com/dir -> http://example.com/dir/ (00ms)',
280 'Link: http://example.com/dir/link0 (00ms)',
281 'Link: http://example.com/dir/link1 (00ms)',
282 'Link: http://example.com/link2 (00ms)',
283 'Link: http://example.com/dir/sub/link3 (00ms)',
284 'Link: http://example.com/dir/sub/link4 (00ms)',
285 'Link: http://example.com/link5 (00ms)'],
286 []));
287 },
288
289 checkLinksInvalid: function(test) {
290 test.expect(10);
291 nockFiles(['brokenLinks.html']);
292 nockLinks(['link0', 'link1', 'link2']);
293 nock('http://example.com')
294 .head('/broken0').reply(404)
295 .get('/broken0').reply(404)
296 .head('/broken1').reply(500)
297 .get('/broken1').reply(500);
298 var mock = gruntMock.create({ options: {
299 pageUrls: ['http://example.com/brokenLinks.html'],
300 checkLinks: true
301 }});
302 mock.invoke(checkPages, testOutput(test,
303 ['Page: http://example.com/brokenLinks.html (00ms)',
304 'Link: http://example.com/link0 (00ms)',
305 'Link: http://example.com/link1 (00ms)',
306 'Link: http://example.com/link2 (00ms)'],
307 ['Bad link (404): http://example.com/broken0 (00ms)',
308 'Bad link (500): http://example.com/broken1 (00ms)',
309 '2 issues, see above. (Set options.summary for a summary.)']));
310 },
311
312 checkLinksRetryWhenHeadFails: function(test) {
313 test.expect(4);
314 nockFiles(['retryWhenHeadFails.html']);
315 nock('http://example.com')
316 .head('/link').reply(500)
317 .get('/link').reply(200);
318 var mock = gruntMock.create({ options: {
319 pageUrls: ['http://example.com/retryWhenHeadFails.html'],
320 checkLinks: true
321 }});
322 mock.invoke(checkPages, testOutput(test,
323 ['Page: http://example.com/retryWhenHeadFails.html (00ms)',
324 'Link: http://example.com/link (00ms)'],
325 []));
326 },
327
328 checkLinksonlySameDomain: function(test) {
329 test.expect(4);
330 nockFiles(['externalLink.html']);
331 nockLinks(['link']);
332 var mock = gruntMock.create({ options: {
333 pageUrls: ['http://example.com/externalLink.html'],
334 checkLinks: true,
335 onlySameDomain: true
336 }});
337 mock.invoke(checkPages, testOutput(test,
338 ['Page: http://example.com/externalLink.html (00ms)',
339 'Link: http://example.com/link (00ms)'],
340 []));
341 },
342
343 checkLinksNoRedirects: function(test) {
344 test.expect(7);
345 nockFiles(['redirectLink.html']);
346 nockRedirect('movedPermanently', 301, true);
347 nockRedirect('movedTemporarily', 302, true, true);
348 var mock = gruntMock.create({ options: {
349 pageUrls: ['http://example.com/redirectLink.html'],
350 checkLinks: true,
351 noRedirects: true
352 }});
353 mock.invoke(checkPages, testOutput(test,
354 ['Page: http://example.com/redirectLink.html (00ms)'],
355 ['Redirected link (301): http://example.com/movedPermanently -> /movedPermanently_redirected (00ms)',
356 'Redirected link (302): http://example.com/movedTemporarily -> [Missing Location header] (00ms)',
357 '2 issues, see above. (Set options.summary for a summary.)']));
358 },
359
360 checkLinksLinksToIgnore: function(test) {
361 test.expect(6);
362 nockFiles(['ignoreLinks.html']);
363 nockLinks(['link0', 'link1', 'link2']);
364 var mock = gruntMock.create({ options: {
365 pageUrls: ['http://example.com/ignoreLinks.html'],
366 checkLinks: true,
367 linksToIgnore: ['http://example.com/ignore0', 'http://example.com/ignore1']
368 }});
369 mock.invoke(checkPages, testOutput(test,
370 ['Page: http://example.com/ignoreLinks.html (00ms)',
371 'Link: http://example.com/link0 (00ms)',
372 'Link: http://example.com/link1 (00ms)',
373 'Link: http://example.com/link2 (00ms)'],
374 []));
375 },
376
377 checkLinksNoLocalLinks: function(test) {
378 test.expect(16);
379 nockFiles(['localLinks.html']);
380 nock('http://localhost').head('/').reply(200);
381 nock('http://example.com').head('/').reply(200);
382 nock('http://127.0.0.1').head('/').reply(200);
383 nock('http://169.254.1.1').head('/').reply(200);
384 nock('http://localhost').head('/').reply(200); // [::1]
385 // nock('http://[ff02::1]').head('/').reply(200); // IPV6 unsupported by nock?
386 // nock('http://[0000:0000:0000:0000:0000:0000:0000:0001]').head('/').reply(200);
387 var mock = gruntMock.create({ options: {
388 pageUrls: ['http://example.com/localLinks.html'],
389 checkLinks: true,
390 noLocalLinks: true
391 }});
392 mock.invoke(checkPages, testOutput(test,
393 ['Page: http://example.com/localLinks.html (00ms)',
394 'Link: http://localhost/ (00ms)',
395 'Link: http://example.com/ (00ms)',
396 'Link: http://127.0.0.1/ (00ms)',
397 'Link: http://169.254.1.1/ (00ms)',
398 'Link: http://[::1]/ (00ms)'],
399 // 'Link: http://[ff02::1]/ (00ms)',
400 // 'Link: http://[0000:0000:0000:0000:0000:0000:0000:0001]/ (00ms)',
401 [
402 'Local link: http://localhost/',
403 'Local link: http://127.0.0.1/',
404 'Local link: http://[::1]/',
405 'Link error (Nock: Not allow net connect for "ff02:80"): http://[ff02::1]/ (00ms)',
406 'Local link: http://[0000:0000:0000:0000:0000:0000:0000:0001]/',
407 'Link error (Nock: Not allow net connect for "0000:80"): http://[0000:0000:0000:0000:0000:0000:0000:0001]/ (00ms)',
408 '6 issues, see above. (Set options.summary for a summary.)']));
409 },
410
411 checkLinksQueryHashes: function(test) {
412 test.expect(36);
413 zlib.gzip('Compressed content', function(err, buf) {
414 if (!err) {
415 nock('http://example.com')
416 .get('/compressed?crc32=3477f8a8')
417 .reply(200, [buf], {
418 'Content-Encoding': 'gzip'
419 });
420 nockFiles([
421 'queryHashes.html',
422 'brokenLinks.html?md5=abcd',
423 'externalLink.html?md5=9357B8FD6A13B3D1A6DBC00E6445E4FF',
424 'ignoreLinks.html?md5=4f47458e34bc855a46349c1335f58cc3',
425 'invalidEntity.html?field1=value&md5=fa3e4d3dc439fdb42d86855e516a92aa&field2=value',
426 'localLinks.html?crc32=abcd',
427 'multipleErrors.html?crc32=F88F0D21',
428 'redirectLink.html?crc32=4363890c',
429 'retryWhenHeadFails.html?sha1=abcd',
430 'unclosedElement.html?sha1=1D9E557D3B99507E8582E67F235D3DE6DFA3717A',
431 'unclosedImg.html?sha1=9511fa1a787d021bdf3aa9538029a44209fb5c4c',
432 'validPage.html?field1=value&sha1=8ac1573c31b4f6132834523ac08de21c54138236&md5=abcd&crc32=abcd&field2=value']);
433 nock('http://example.com').get('/noBytes.txt?crc32=00000000').reply(200, '', { 'Content-Type': 'application/octet-stream' });
434 nockFiles(['allBytes.txt?sha1=88d103ba1b5db29a2d83b92d09a725cb6d2673f9'], null, { 'Content-Type': 'application/octet-stream' });
435 nockFiles(['image.png?md5=e3ece6e91045f18ce18ac25455524cd0'], null, { 'Content-Type': 'image/png' });
436 nockFiles(['image.png?key=value']);
437 var mock = gruntMock.create({ options: {
438 pageUrls: ['http://example.com/queryHashes.html'],
439 checkLinks: true,
440 queryHashes: true
441 }});
442 mock.invoke(checkPages, testOutput(test,
443 ['Page: http://example.com/queryHashes.html (00ms)',
444 'Link: http://example.com/brokenLinks.html?md5=abcd (00ms)',
445 'Link: http://example.com/externalLink.html?md5=9357B8FD6A13B3D1A6DBC00E6445E4FF (00ms)',
446 'Hash: http://example.com/externalLink.html?md5=9357B8FD6A13B3D1A6DBC00E6445E4FF',
447 'Link: http://example.com/ignoreLinks.html?md5=4f47458e34bc855a46349c1335f58cc3 (00ms)',
448 'Hash: http://example.com/ignoreLinks.html?md5=4f47458e34bc855a46349c1335f58cc3',
449 'Link: http://example.com/invalidEntity.html?field1=value&md5=fa3e4d3dc439fdb42d86855e516a92aa&field2=value (00ms)',
450 'Hash: http://example.com/invalidEntity.html?field1=value&md5=fa3e4d3dc439fdb42d86855e516a92aa&field2=value',
451 'Link: http://example.com/localLinks.html?crc32=abcd (00ms)',
452 'Link: http://example.com/multipleErrors.html?crc32=F88F0D21 (00ms)',
453 'Hash: http://example.com/multipleErrors.html?crc32=F88F0D21',
454 'Link: http://example.com/redirectLink.html?crc32=4363890c (00ms)',
455 'Hash: http://example.com/redirectLink.html?crc32=4363890c',
456 'Link: http://example.com/retryWhenHeadFails.html?sha1=abcd (00ms)',
457 'Link: http://example.com/unclosedElement.html?sha1=1D9E557D3B99507E8582E67F235D3DE6DFA3717A (00ms)',
458 'Hash: http://example.com/unclosedElement.html?sha1=1D9E557D3B99507E8582E67F235D3DE6DFA3717A',
459 'Link: http://example.com/unclosedImg.html?sha1=9511fa1a787d021bdf3aa9538029a44209fb5c4c (00ms)',
460 'Hash: http://example.com/unclosedImg.html?sha1=9511fa1a787d021bdf3aa9538029a44209fb5c4c',
461 'Link: http://example.com/validPage.html?field1=value&sha1=8ac1573c31b4f6132834523ac08de21c54138236&md5=abcd&crc32=abcd&field2=value (00ms)',
462 'Hash: http://example.com/validPage.html?field1=value&sha1=8ac1573c31b4f6132834523ac08de21c54138236&md5=abcd&crc32=abcd&field2=value',
463 'Link: http://example.com/noBytes.txt?crc32=00000000 (00ms)',
464 'Hash: http://example.com/noBytes.txt?crc32=00000000',
465 'Link: http://example.com/allBytes.txt?sha1=88d103ba1b5db29a2d83b92d09a725cb6d2673f9 (00ms)',
466 'Hash: http://example.com/allBytes.txt?sha1=88d103ba1b5db29a2d83b92d09a725cb6d2673f9',
467 'Link: http://example.com/image.png?md5=e3ece6e91045f18ce18ac25455524cd0 (00ms)',
468 'Hash: http://example.com/image.png?md5=e3ece6e91045f18ce18ac25455524cd0',
469 'Link: http://example.com/image.png?key=value (00ms)',
470 'Link: http://example.com/compressed?crc32=3477f8a8 (00ms)',
471 'Hash: http://example.com/compressed?crc32=3477f8a8'],
472 ['Hash error (7f5a1ac1e6dc59679f36482973efc871): http://example.com/brokenLinks.html?md5=abcd',
473 'Hash error (73fb7b7a): http://example.com/localLinks.html?crc32=abcd',
474 'Hash error (1353361bfade29f3684fe17c8b388dadbc49cb6d): http://example.com/retryWhenHeadFails.html?sha1=abcd',
475 '3 issues, see above. (Set options.summary for a summary.)']));
476 }
477 });
478 },
479
480 checkLinksInvalidProtocol: function(test) {
481 test.expect(3);
482 nockFiles(['invalidProtocol.html']);
483 var mock = gruntMock.create({ options: {
484 pageUrls: ['http://example.com/invalidProtocol.html'],
485 checkLinks: true
486 }});
487 mock.invoke(checkPages, testOutput(test,
488 ['Page: http://example.com/invalidProtocol.html (00ms)'],
489 []));
490 },
491
492 checkLinksMultiplePages: function(test) {
493 test.expect(11);
494 nockFiles(['externalLink.html', 'redirectLink.html', 'ignoreLinks.html']);
495 nockLinks(['link', 'link0', 'link1', 'link2']);
496 nockRedirect('movedPermanently', 301);
497 nockRedirect('movedTemporarily', 302);
498 var mock = gruntMock.create({ options: {
499 pageUrls: ['http://example.com/externalLink.html',
500 'http://example.com/redirectLink.html',
501 'http://example.com/ignoreLinks.html'],
502 checkLinks: true,
503 onlySameDomain: true,
504 linksToIgnore: ['http://example.com/ignore0', 'http://example.com/ignore1']
505 }});
506 mock.invoke(checkPages, testOutput(test,
507 ['Page: http://example.com/externalLink.html (00ms)',
508 'Link: http://example.com/link (00ms)',
509 'Page: http://example.com/redirectLink.html (00ms)',
510 'Link: http://example.com/movedPermanently (00ms)',
511 'Link: http://example.com/movedTemporarily (00ms)',
512 'Page: http://example.com/ignoreLinks.html (00ms)',
513 'Link: http://example.com/link0 (00ms)',
514 'Link: http://example.com/link1 (00ms)',
515 'Link: http://example.com/link2 (00ms)'],
516 []));
517 },
518
519 // checkXhtml functionality
520
521 checkXhtmlValid: function(test) {
522 test.expect(3);
523 nockFiles(['validPage.html']);
524 var mock = gruntMock.create({ options: {
525 pageUrls: ['http://example.com/validPage.html'],
526 checkXhtml: true
527 }});
528 mock.invoke(checkPages, testOutput(test,
529 ['Page: http://example.com/validPage.html (00ms)'],
530 []));
531 },
532
533 checkXhtmlUnclosedElement: function(test) {
534 test.expect(6);
535 nockFiles(['unclosedElement.html']);
536 var mock = gruntMock.create({ options: {
537 pageUrls: ['http://example.com/unclosedElement.html'],
538 checkXhtml: true
539 }});
540 mock.invoke(checkPages, testOutput(test,
541 ['Page: http://example.com/unclosedElement.html (00ms)'],
542 ['Unexpected close tag, Line: 5, Column: 7, Char: >',
543 '1 issue, see above. (Set options.summary for a summary.)']));
544 },
545
546 checkXhtmlUnclosedImg: function(test) {
547 test.expect(6);
548 nockFiles(['unclosedImg.html']);
549 var mock = gruntMock.create({ options: {
550 pageUrls: ['http://example.com/unclosedImg.html'],
551 checkXhtml: true
552 }});
553 mock.invoke(checkPages, testOutput(test,
554 ['Page: http://example.com/unclosedImg.html (00ms)'],
555 ['Unexpected close tag, Line: 4, Column: 7, Char: >',
556 '1 issue, see above. (Set options.summary for a summary.)']));
557 },
558
559 checkXhtmlInvalidEntity: function(test) {
560 test.expect(6);
561 nockFiles(['invalidEntity.html']);
562 var mock = gruntMock.create({ options: {
563 pageUrls: ['http://example.com/invalidEntity.html'],
564 checkXhtml: true
565 }});
566 mock.invoke(checkPages, testOutput(test,
567 ['Page: http://example.com/invalidEntity.html (00ms)'],
568 ['Invalid character entity, Line: 3, Column: 21, Char: ;',
569 '1 issue, see above. (Set options.summary for a summary.)']));
570 },
571
572 checkXhtmlMultipleErrors: function(test) {
573 test.expect(7);
574 nockFiles(['multipleErrors.html']);
575 var mock = gruntMock.create({ options: {
576 pageUrls: ['http://example.com/multipleErrors.html'],
577 checkXhtml: true
578 }});
579 mock.invoke(checkPages, testOutput(test,
580 ['Page: http://example.com/multipleErrors.html (00ms)'],
581 ['Invalid character entity, Line: 4, Column: 23, Char: ;',
582 'Unexpected close tag, Line: 5, Column: 6, Char: >',
583 '2 issues, see above. (Set options.summary for a summary.)']));
584 },
585
586 // checkCaching functionality
587
588 checkCachingValid: function(test) {
589 test.expect(3);
590 nockFiles(['validPage.html'], null, {
591 'Cache-Control': 'public, max-age=1000',
592 'ETag': '"123abc"'
593 });
594 var mock = gruntMock.create({ options: {
595 pageUrls: ['http://example.com/validPage.html'],
596 checkCaching: true
597 }});
598 mock.invoke(checkPages, testOutput(test,
599 ['Page: http://example.com/validPage.html (00ms)'],
600 []));
601 },
602
603 checkCachingNoCache: function(test) {
604 test.expect(3);
605 nockFiles(['validPage.html'], null, {
606 'Cache-Control': 'no-cache'
607 });
608 var mock = gruntMock.create({ options: {
609 pageUrls: ['http://example.com/validPage.html'],
610 checkCaching: true
611 }});
612 mock.invoke(checkPages, testOutput(test,
613 ['Page: http://example.com/validPage.html (00ms)'],
614 []));
615 },
616
617 checkCachingWeakEtag: function(test) {
618 test.expect(3);
619 nockFiles(['validPage.html'], null, {
620 'Cache-Control': 'public, max-age=1000',
621 'ETag': 'W/"123abc"'
622 });
623 var mock = gruntMock.create({ options: {
624 pageUrls: ['http://example.com/validPage.html'],
625 checkCaching: true
626 }});
627 mock.invoke(checkPages, testOutput(test,
628 ['Page: http://example.com/validPage.html (00ms)'],
629 []));
630 },
631
632 checkCachingEmptyEtag: function(test) {
633 test.expect(3);
634 nockFiles(['validPage.html'], null, {
635 'Cache-Control': 'public, max-age=1000',
636 'ETag': '""'
637 });
638 var mock = gruntMock.create({ options: {
639 pageUrls: ['http://example.com/validPage.html'],
640 checkCaching: true
641 }});
642 mock.invoke(checkPages, testOutput(test,
643 ['Page: http://example.com/validPage.html (00ms)'],
644 []));
645 },
646
647 checkCachingMissingCacheControl: function(test) {
648 test.expect(6);
649 nockFiles(['validPage.html'], null, {
650 'ETag': '"123abc"'
651 });
652 var mock = gruntMock.create({ options: {
653 pageUrls: ['http://example.com/validPage.html'],
654 checkCaching: true
655 }});
656 mock.invoke(checkPages, testOutput(test,
657 ['Page: http://example.com/validPage.html (00ms)'],
658 ['Missing Cache-Control header in response',
659 '1 issue, see above. (Set options.summary for a summary.)']));
660 },
661
662 checkCachingInvalidCacheControl: function(test) {
663 test.expect(6);
664 nockFiles(['validPage.html'], null, {
665 'Cache-Control': 'invalid',
666 'ETag': '"123abc"'
667 });
668 var mock = gruntMock.create({ options: {
669 pageUrls: ['http://example.com/validPage.html'],
670 checkCaching: true
671 }});
672 mock.invoke(checkPages, testOutput(test,
673 ['Page: http://example.com/validPage.html (00ms)'],
674 ['Invalid Cache-Control header in response: invalid',
675 '1 issue, see above. (Set options.summary for a summary.)']));
676 },
677
678 checkCachingMissingEtag: function(test) {
679 test.expect(6);
680 nockFiles(['validPage.html'], null, {
681 'Cache-Control': 'public, max-age=1000'
682 });
683 var mock = gruntMock.create({ options: {
684 pageUrls: ['http://example.com/validPage.html'],
685 checkCaching: true
686 }});
687 mock.invoke(checkPages, testOutput(test,
688 ['Page: http://example.com/validPage.html (00ms)'],
689 ['Missing ETag header in response',
690 '1 issue, see above. (Set options.summary for a summary.)']));
691 },
692
693 checkCachingInvalidEtag: function(test) {
694 test.expect(6);
695 nockFiles(['validPage.html'], null, {
696 'Cache-Control': 'public, max-age=1000',
697 'ETag': 'invalid'
698 });
699 var mock = gruntMock.create({ options: {
700 pageUrls: ['http://example.com/validPage.html'],
701 checkCaching: true
702 }});
703 mock.invoke(checkPages, testOutput(test,
704 ['Page: http://example.com/validPage.html (00ms)'],
705 ['Invalid ETag header in response: invalid',
706 '1 issue, see above. (Set options.summary for a summary.)']));
707 },
708
709 // checkCompression functionality
710
711 checkCompressionValid: function(test) {
712 test.expect(4);
713 zlib.gzip('<html><body><a href="link">link</a></body></html>', function(err, buf) {
714 if (!err) {
715 nock('http://example.com')
716 .get('/compressed')
717 .reply(200, [buf], {
718 'Content-Encoding': 'gzip'
719 });
720 nockLinks(['link']);
721 var mock = gruntMock.create({ options: {
722 pageUrls: ['http://example.com/compressed'],
723 checkCompression: true,
724 checkLinks: true
725 }});
726 mock.invoke(checkPages, testOutput(test,
727 ['Page: http://example.com/compressed (00ms)',
728 'Link: http://example.com/link (00ms)'],
729 []));
730 }
731 });
732 },
733
734 checkCompressionMissingContentEncoding: function(test) {
735 test.expect(6);
736 nockFiles(['validPage.html']);
737 var mock = gruntMock.create({ options: {
738 pageUrls: ['http://example.com/validPage.html'],
739 checkCompression: true
740 }});
741 mock.invoke(checkPages, testOutput(test,
742 ['Page: http://example.com/validPage.html (00ms)'],
743 ['Missing Content-Encoding header in response',
744 '1 issue, see above. (Set options.summary for a summary.)']));
745 },
746
747 checkCompressionInvalidContentEncoding: function(test) {
748 test.expect(6);
749 nockFiles(['validPage.html'], null, {
750 'Content-Encoding': 'invalid'
751 });
752 var mock = gruntMock.create({ options: {
753 pageUrls: ['http://example.com/validPage.html'],
754 checkCompression: true
755 }});
756 mock.invoke(checkPages, testOutput(test,
757 ['Page: http://example.com/validPage.html (00ms)'],
758 ['Invalid Content-Encoding header in response: invalid',
759 '1 issue, see above. (Set options.summary for a summary.)']));
760 },
761
762 // maxResponseTime functionality
763
764 maxResponseTimeValid: function(test) {
765 test.expect(3);
766 nock('http://example.com')
767 .get('/page')
768 .reply(200, '<html></html>');
769 var mock = gruntMock.create({ options: {
770 pageUrls: ['http://example.com/page'],
771 maxResponseTime: 100
772 }});
773 mock.invoke(checkPages, testOutput(test,
774 ['Page: http://example.com/page (00ms)'],
775 []));
776 },
777
778 maxResponseTimeSlow: function(test) {
779 test.expect(6);
780 nock('http://example.com')
781 .get('/page')
782 .delay(200)
783 .reply(200, '<html></html>');
784 var mock = gruntMock.create({ options: {
785 pageUrls: ['http://example.com/page'],
786 maxResponseTime: 100
787 }});
788 mock.invoke(checkPages, testOutput(test,
789 ['Page: http://example.com/page (00ms)'],
790 ['Page response took more than 100ms to complete',
791 '1 issue, see above. (Set options.summary for a summary.)']));
792 },
793
794 // userAgent functionality
795
796 userAgentValid: function(test) {
797 test.expect(4);
798 nock('http://example.com')
799 .matchHeader('User-Agent', 'custom-user-agent/1.2.3')
800 .get('/page')
801 .reply(200, '<html><body><a href="link">link</a></body></html>');
802 nock('http://example.com')
803 .matchHeader('User-Agent', 'custom-user-agent/1.2.3')
804 .head('/link')
805 .reply(200);
806 var mock = gruntMock.create({ options: {
807 pageUrls: ['http://example.com/page'],
808 checkLinks: true,
809 userAgent: 'custom-user-agent/1.2.3'
810 }});
811 mock.invoke(checkPages, testOutput(test,
812 ['Page: http://example.com/page (00ms)',
813 'Link: http://example.com/link (00ms)'],
814 []));
815 },
816
817 userAgentNull: function(test) {
818 test.expect(4);
819 nock('http://example.com')
820 .matchHeader('User-Agent', function(val) {
821 test.ok(undefined === val);
822 return true;
823 })
824 .get('/page')
825 .reply(200);
826 var mock = gruntMock.create({ options: {
827 pageUrls: ['http://example.com/page'],
828 userAgent: null
829 }});
830 mock.invoke(checkPages, testOutput(test,
831 ['Page: http://example.com/page (00ms)'],
832 []));
833 },
834
835 userAgentEmpty: function(test) {
836 test.expect(4);
837 nock('http://example.com')
838 .matchHeader('User-Agent', function(val) {
839 test.ok(undefined === val);
840 return true;
841 })
842 .get('/page')
843 .reply(200);
844 var mock = gruntMock.create({ options: {
845 pageUrls: ['http://example.com/page'],
846 userAgent: ''
847 }});
848 mock.invoke(checkPages, testOutput(test,
849 ['Page: http://example.com/page (00ms)'],
850 []));
851 },
852
853 // summary functionality
854
855 summary: function(test) {
856 test.expect(16);
857 nockFiles(['multipleErrors.html', 'brokenLinks.html']);
858 nock('http://example.com')
859 .get('/ok').reply(200)
860 .get('/notFound').reply(404)
861 .head('/broken0').reply(404)
862 .get('/broken0').reply(404)
863 .head('/broken1').reply(500)
864 .get('/broken1').reply(500);
865 nockLinks(['link0', 'link1', 'link2']);
866 var mock = gruntMock.create({ options: {
867 pageUrls: ['http://example.com/notFound',
868 'http://example.com/ok',
869 'http://example.com/multipleErrors.html',
870 'http://example.com/brokenLinks.html'],
871 checkLinks: true,
872 checkXhtml: true,
873 summary: true
874 }});
875 mock.invoke(checkPages, testOutput(test,
876 ['Page: http://example.com/ok (00ms)',
877 'Page: http://example.com/multipleErrors.html (00ms)',
878 'Page: http://example.com/brokenLinks.html (00ms)',
879 'Link: http://example.com/link0 (00ms)',
880 'Link: http://example.com/link1 (00ms)',
881 'Link: http://example.com/link2 (00ms)'],
882 ['Bad page (404): http://example.com/notFound (00ms)',
883 'Invalid character entity, Line: 4, Column: 23, Char: ;',
884 'Unexpected close tag, Line: 5, Column: 6, Char: >',
885 'Bad link (404): http://example.com/broken0 (00ms)',
886 'Bad link (500): http://example.com/broken1 (00ms)',
887 'Summary of issues:\n' +
888 ' http://example.com/notFound\n' +
889 ' Bad page (404): http://example.com/notFound (00ms)\n' +
890 ' http://example.com/multipleErrors.html\n' +
891 ' Invalid character entity, Line: 4, Column: 23, Char: ;\n' +
892 ' Unexpected close tag, Line: 5, Column: 6, Char: >\n' +
893 ' http://example.com/brokenLinks.html\n' +
894 ' Bad link (404): http://example.com/broken0 (00ms)\n' +
895 ' Bad link (500): http://example.com/broken1 (00ms)\n',
896 '5 issues, see above.']));
897 },
898
899 // Nock configuration
900
901 requestHeaders: function(test) {
902 test.expect(4);
903 nock('http://example.com')
904 .matchHeader('User-Agent', 'grunt-check-pages/0.6.2')
905 .matchHeader('Cache-Control', 'no-cache')
906 .matchHeader('Pragma', 'no-cache')
907 .get('/page')
908 .reply(200, '<html><body><a href="link">link</a></body></html>');
909 nock('http://example.com')
910 .matchHeader('User-Agent', 'grunt-check-pages/0.6.2')
911 .matchHeader('Cache-Control', 'no-cache')
912 .matchHeader('Pragma', 'no-cache')
913 .head('/link')
914 .reply(200);
915 var mock = gruntMock.create({ options: {
916 pageUrls: ['http://example.com/page'],
917 checkLinks: true
918 }});
919 mock.invoke(checkPages, testOutput(test,
920 ['Page: http://example.com/page (00ms)',
921 'Link: http://example.com/link (00ms)'],
922 []));
923 },
924
925 // Connection errors
926
927 enableDeliberateConnectionErrors: function(test) {
928 test.expect(0);
929 nock.enableNetConnect('localhost');
930 test.done();
931 },
932
933 pageConnectionError: function(test) {
934 test.expect(5);
935 var mock = gruntMock.create({ options: {
936 pageUrls: ['http://localhost:9999/notListening']}});
937 mock.invoke(checkPages, testOutput(test,
938 [],
939 ['Page error (connect ECONNREFUSED): http://localhost:9999/notListening (00ms)',
940 '1 issue, see above. (Set options.summary for a summary.)']));
941 },
942
943 linkConnectionError: function(test) {
944 test.expect(6);
945 nock('http://example.com')
946 .get('/page')
947 .reply(200, '<html><body><a href="http://localhost:9999/notListening">notListening</a></body></html>');
948 var mock = gruntMock.create({ options: {
949 pageUrls: ['http://example.com/page'],
950 checkLinks: true
951 }});
952 mock.invoke(checkPages, testOutput(test,
953 ['Page: http://example.com/page (00ms)'],
954 ['Link error (connect ECONNREFUSED): http://localhost:9999/notListening (00ms)',
955 '1 issue, see above. (Set options.summary for a summary.)']));
956 }
957};