| 'use strict' |
| |
| const test = require('tape') |
| const fastURI = require('..') |
| |
| const PATH_RESERVED = "!$&'()*+,;=:@/" |
| |
| function percentEncode (character, lowerCase) { |
| const hex = character.charCodeAt(0).toString(16).padStart(2, '0') |
| return '%' + (lowerCase ? hex : hex.toUpperCase()) |
| } |
| |
| test('normalize preserves literal and escaped reserved path characters', (t) => { |
| t.equal( |
| fastURI.normalize('http://example.com/a;b'), |
| 'http://example.com/a;b', |
| 'literal semicolon remains literal' |
| ) |
| t.equal( |
| fastURI.normalize('http://example.com/a%3ab'), |
| 'http://example.com/a%3Ab', |
| 'escaped colon remains escaped and its hex is uppercased' |
| ) |
| |
| for (const character of PATH_RESERVED) { |
| const literal = `http://example.com/a${character}b` |
| const escaped = `http://example.com/a${percentEncode(character, true)}b` |
| const normalizedEscape = `http://example.com/a${percentEncode(character, false)}b` |
| |
| t.equal(fastURI.normalize(literal), literal, `preserves literal ${character}`) |
| t.equal(fastURI.normalize(escaped), normalizedEscape, `preserves escaped ${character}`) |
| } |
| |
| t.equal( |
| fastURI.normalize('http://example.com/a/./café'), |
| 'http://example.com/a/caf%C3%A9', |
| 'removes real dot segments and UTF-8 encodes raw non-ASCII' |
| ) |
| t.equal( |
| fastURI.normalize('http://example.com/a/%2e%2e/b'), |
| 'http://example.com/a/%2E%2E/b', |
| 'preserves escaped dots as path data' |
| ) |
| t.equal( |
| fastURI.normalize('http://example.com/Kſ'), |
| 'http://example.com/%E2%84%AA%C5%BF', |
| 'UTF-8 encodes Unicode characters that case-fold to ASCII' |
| ) |
| t.end() |
| }) |
| |
| test('serialize uses the RFC 3986 path character set without opening escapes', (t) => { |
| const rawPath = `/a${PATH_RESERVED}b` |
| t.equal( |
| fastURI.serialize({ scheme: 'http', host: 'example.com', path: rawPath }), |
| `http://example.com${rawPath}`, |
| 'keeps all legal raw path characters' |
| ) |
| |
| const escapedPath = Array.from(PATH_RESERVED, (character) => percentEncode(character, true)).join('') |
| const normalizedEscapedPath = Array.from(PATH_RESERVED, (character) => percentEncode(character, false)).join('') |
| t.equal( |
| fastURI.serialize({ scheme: 'http', host: 'example.com', path: '/' + escapedPath }), |
| 'http://example.com/' + normalizedEscapedPath, |
| 'uppercases valid escapes without decoding reserved characters' |
| ) |
| |
| t.equal( |
| fastURI.serialize({ scheme: 'http', host: 'example.com', path: '/a?b#c[d]' }), |
| 'http://example.com/a%3Fb%23c%5Bd%5D', |
| 'encodes characters that would leave the path component' |
| ) |
| t.equal( |
| fastURI.serialize({ path: 'a:b/c:d' }), |
| 'a%3Ab/c:d', |
| 'only escapes a colon where path-noscheme requires it' |
| ) |
| t.equal( |
| fastURI.serialize({ path: './a:b/c:d' }), |
| 'a%3Ab/c:d', |
| 'escapes a first-segment colon exposed by dot-segment removal' |
| ) |
| t.equal( |
| fastURI.normalize('./a:b'), |
| 'a%3Ab', |
| 'normalization keeps a relative path from becoming a scheme' |
| ) |
| t.equal( |
| fastURI.serialize({ path: '/Kſ' }), |
| '/%E2%84%AA%C5%BF', |
| 'UTF-8 encodes Unicode characters that case-fold to ASCII' |
| ) |
| t.end() |
| }) |
| |
| test('equal distinguishes escaped reserved path data from literal syntax', (t) => { |
| for (const character of PATH_RESERVED) { |
| const literal = `http://example.com/a${character}b` |
| const escaped = `http://example.com/a${percentEncode(character, false)}b` |
| t.equal(fastURI.equal(literal, escaped, {}), false, `distinguishes literal and escaped ${character}`) |
| } |
| |
| t.equal( |
| fastURI.equal('./a:b', 'a:b', {}), |
| false, |
| 'does not equate a relative path with an absolute URI after removing dot segments' |
| ) |
| t.end() |
| }) |