1 | 'use strict';
|
2 |
|
3 |
|
4 | var path = require('path');
|
5 | var zlib = require('zlib');
|
6 | var nock = require('nock');
|
7 | var gruntMock = require('gruntmock');
|
8 | var checkPages = require('../tasks/checkPages.js');
|
9 |
|
10 |
|
11 | nock.disableNetConnect();
|
12 |
|
13 |
|
14 | function 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 |
|
32 |
|
33 | function 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 | }
|
41 | function 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 | }
|
49 | function 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 |
|
65 | exports.checkPages = {
|
66 |
|
67 |
|
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 |
|
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 |
|
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);
|
385 |
|
386 |
|
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 |
|
400 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 |
|
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 | };
|