X-Git-Url: http://git.squeep.com/?p=squeep-web-linking;a=blobdiff_plain;f=test%2Flib%2Frfc8288-web-linking.js;fp=test%2Flib%2Frfc8288-web-linking.js;h=6bdc1884cf5f5f1ab1a93b049b530b5fce1d23d9;hp=0000000000000000000000000000000000000000;hb=3436c07c25324507228f3d538d345ea35751c623;hpb=b36198e0a3d21413d75a501ffd52f0fb20188a31 diff --git a/test/lib/rfc8288-web-linking.js b/test/lib/rfc8288-web-linking.js new file mode 100644 index 0000000..6bdc188 --- /dev/null +++ b/test/lib/rfc8288-web-linking.js @@ -0,0 +1,275 @@ +/* eslint-env mocha */ +'use strict'; + +const assert = require('assert'); + +describe('RFC 8288 Header parser', function () { + const { parse, SyntaxError: PEGSyntaxError } = require('../../lib/rfc8288-web-linking'); + + function checkParser(linkHeader, expectedResult) { + const result = parse(linkHeader.replace(/\r?\n|\r/gm, '')); + assert.deepStrictEqual(result, expectedResult); + } + + describe('§3.3 Relation Type', function () { + it('Must ignore occurrences after the first', function () { + const linkHeader = [ + ';', + 'rel="start"; rel="ignore"; foo', + ].join('\n'); + const expectedResult = [ + { + target: 'http://example.org/', + attributes: [ + { + extended: false, + name: 'rel', + value: 'start', + }, + { + extended: false, + name: 'foo', + value: null, + }, + ], + }, + ]; + checkParser(linkHeader, expectedResult); + }); + }); + + describe('§3.5 Link Header Field Examples', function () { + it('a', function () { + const linkHeader = [ + '; rel="previous";', + 'title="previous chapter"', + ].join('\n'); + const expectedResult = [ + { + target: 'http://example.com/TheBook/chapter2', + attributes: [ + { + extended: false, + name: 'rel', + value: 'previous', + }, + { + extended: false, + name: 'title', + value: 'previous chapter', + }, + ], + }, + ]; + checkParser(linkHeader, expectedResult); + }); + it('b', function () { + const linkHeader = '; rel="http://example.net/foo"'; + const expectedResult = [ + { + target: '/', + attributes: [ + { + extended: false, + name: 'rel', + value: 'http://example.net/foo', + }, + ], + }, + ]; + checkParser(linkHeader, expectedResult); + }); + it('c', function () { + const linkHeader = '; rel="copyright"; anchor="#foo"'; + const expectedResult = [ + { + target: '/terms', + attributes: [ + { + extended: false, + name: 'rel', + value: 'copyright', + }, + { + extended: false, + name: 'anchor', + value: '#foo', + }, + ], + }, + ]; + checkParser(linkHeader, expectedResult); + }); + it('d', function () { + const linkHeader = [ + ';', + 'rel="previous"; title*=UTF-8\'de\'letztes%20Kapitel,', + ';', + 'rel="next"; title*=UTF-8\'de\'n%c3%a4chstes%20Kapitel', + ].join('\n'); + const expectedResult = [ + { + target: '/TheBook/chapter2', + attributes: [ + { + extended: false, + name: 'rel', + value: 'previous', + }, + { + extended: true, + name: 'title*', + value: 'UTF-8\'de\'letztes%20Kapitel', + }, + ], + }, + { + target: '/TheBook/chapter4', + attributes: [ + { + extended: false, + name: 'rel', + value: 'next', + }, + { + extended: true, + name: 'title*', + value: 'UTF-8\'de\'n%c3%a4chstes%20Kapitel', + }, + ], + }, + ]; + checkParser(linkHeader, expectedResult); + }); + it('e', function () { + const linkHeader = [ + ';', + 'rel="start http://example.net/relation/other"', + ].join('\n'); + const expectedResult = [ + { + target: 'http://example.org/', + attributes: [ + { + extended: false, + name: 'rel', + value: 'start http://example.net/relation/other', + }, + ], + }, + ]; + checkParser(linkHeader, expectedResult); + }); + it('f', function () { + const linkHeader = [ + '; rel="start",', + ' ; rel="index"', + ].join('\n'); + const expectedResult = [ + { + target: 'https://example.org/', + attributes: [ + { + extended: false, + name: 'rel', + value: 'start', + }, + ], + }, + { + target: 'https://example.org/index', + attributes: [ + { + extended: false, + name: 'rel', + value: 'index', + }, + ], + }, + ]; + checkParser(linkHeader, expectedResult); + }); + }); // §3.5 Link Header Field Examples + + describe('Unusual Cases', function () { + it('handles uris with semicolons', function () { + const linkHeader = [ + '; rel="self";', + ].join('\n'); + const expectedResult = [ + { + target: 'http://example.com/strange;url;indeed', + attributes: [ + { + extended: false, + name: 'rel', + value: 'self', + }, + ], + }, + ]; + checkParser(linkHeader, expectedResult); + }); + it('handles value-less attributes', function () { + const linkHeader = [ + '; special; rel="special";', + ].join('\n'); + const expectedResult = [ + { + target: '/', + attributes: [ + { + extended: false, + name: 'special', + value: null, + }, + { + extended: false, + name: 'rel', + value: 'special', + }, + ], + }, + ]; + checkParser(linkHeader, expectedResult); + }); + }); // Unusual Cases + + describe('Extended Values', function () { + it('decodes extended value', function () { + const extendedValue = 'UTF-8\'\'%c2%a3%20and%20%e2%82%ac%20rates'; + const expectedResult = { + encoding: 'UTF-8', + language: null, + value: '£ and € rates', + }; + const result = parse(extendedValue, { startRule: 'extendedValue' }); + assert.deepStrictEqual(result, expectedResult); + }); + }); // Extended Values + + describe('Failures', function () { + it('does not parse invalid header', function () { + const linkHeader = 'not a link header'; + const expectedResult = []; + try { + checkParser(linkHeader, expectedResult); + assert.fail('unexpected success'); + } catch (e) { + assert.strictEqual(e.name, 'SyntaxError'); + } + }); + it('does not parse partial invalid header', function () { + const linkHeader = '; rel="valid", not a link header'; + const expectedResult = []; + try { + checkParser(linkHeader, expectedResult); + assert.fail('unexpected success'); + } catch (e) { + assert(e instanceof PEGSyntaxError); + assert.strictEqual(e.name, 'SyntaxError'); + } + }); + }); // Failures + +}); \ No newline at end of file