diff options
Diffstat (limited to '')
-rw-r--r-- | test/fetch/data-uri.js | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/test/fetch/data-uri.js b/test/fetch/data-uri.js new file mode 100644 index 0000000..6191bfe --- /dev/null +++ b/test/fetch/data-uri.js @@ -0,0 +1,214 @@ +'use strict' + +const { test } = require('tap') +const { + URLSerializer, + collectASequenceOfCodePoints, + stringPercentDecode, + parseMIMEType, + collectAnHTTPQuotedString +} = require('../../lib/fetch/dataURL') +const { fetch } = require('../..') + +test('https://url.spec.whatwg.org/#concept-url-serializer', (t) => { + t.test('url scheme gets appended', (t) => { + const url = new URL('https://www.google.com/') + const serialized = URLSerializer(url) + + t.ok(serialized.startsWith(url.protocol)) + t.end() + }) + + t.test('non-null url host with authentication', (t) => { + const url = new URL('https://username:password@google.com') + const serialized = URLSerializer(url) + + t.ok(serialized.includes(`//${url.username}:${url.password}`)) + t.ok(serialized.endsWith('@google.com/')) + t.end() + }) + + t.test('null url host', (t) => { + for (const url of ['web+demo:/.//not-a-host/', 'web+demo:/path/..//not-a-host/']) { + t.equal( + URLSerializer(new URL(url)), + 'web+demo:/.//not-a-host/' + ) + } + + t.end() + }) + + t.test('url with query works', (t) => { + t.equal( + URLSerializer(new URL('https://www.google.com/?fetch=undici')), + 'https://www.google.com/?fetch=undici' + ) + + t.end() + }) + + t.test('exclude fragment', (t) => { + t.equal( + URLSerializer(new URL('https://www.google.com/#frag')), + 'https://www.google.com/#frag' + ) + + t.equal( + URLSerializer(new URL('https://www.google.com/#frag'), true), + 'https://www.google.com/' + ) + + t.end() + }) + + t.end() +}) + +test('https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points', (t) => { + const input = 'text/plain;base64,' + const position = { position: 0 } + const result = collectASequenceOfCodePoints( + (char) => char !== ';', + input, + position + ) + + t.strictSame(result, 'text/plain') + t.strictSame(position.position, input.indexOf(';')) + t.end() +}) + +test('https://url.spec.whatwg.org/#string-percent-decode', (t) => { + t.test('encodes %{2} in range properly', (t) => { + const input = '%FF' + const percentDecoded = stringPercentDecode(input) + + t.same(percentDecoded, new Uint8Array([255])) + t.end() + }) + + t.test('encodes %{2} not in range properly', (t) => { + const input = 'Hello %XD World' + const percentDecoded = stringPercentDecode(input) + const expected = [...input].map(c => c.charCodeAt(0)) + + t.same(percentDecoded, expected) + t.end() + }) + + t.test('normal string works', (t) => { + const input = 'Hello world' + const percentDecoded = stringPercentDecode(input) + const expected = [...input].map(c => c.charCodeAt(0)) + + t.same(percentDecoded, Uint8Array.from(expected)) + t.end() + }) + + t.end() +}) + +test('https://mimesniff.spec.whatwg.org/#parse-a-mime-type', (t) => { + t.same(parseMIMEType('text/plain'), { + type: 'text', + subtype: 'plain', + parameters: new Map(), + essence: 'text/plain' + }) + + t.same(parseMIMEType('text/html;charset="shift_jis"iso-2022-jp'), { + type: 'text', + subtype: 'html', + parameters: new Map([['charset', 'shift_jis']]), + essence: 'text/html' + }) + + t.same(parseMIMEType('application/javascript'), { + type: 'application', + subtype: 'javascript', + parameters: new Map(), + essence: 'application/javascript' + }) + + t.end() +}) + +test('https://fetch.spec.whatwg.org/#collect-an-http-quoted-string', (t) => { + // https://fetch.spec.whatwg.org/#example-http-quoted-string + t.test('first', (t) => { + const position = { position: 0 } + + t.strictSame(collectAnHTTPQuotedString('"\\', { + position: 0 + }), '"\\') + t.strictSame(collectAnHTTPQuotedString('"\\', position, true), '\\') + t.strictSame(position.position, 2) + t.end() + }) + + t.test('second', (t) => { + const position = { position: 0 } + const input = '"Hello" World' + + t.strictSame(collectAnHTTPQuotedString(input, { + position: 0 + }), '"Hello"') + t.strictSame(collectAnHTTPQuotedString(input, position, true), 'Hello') + t.strictSame(position.position, 7) + t.end() + }) + + t.end() +}) + +// https://github.com/nodejs/undici/issues/1574 +test('too long base64 url', async (t) => { + const inputStr = 'a'.repeat(1 << 20) + const base64 = Buffer.from(inputStr).toString('base64') + const dataURIPrefix = 'data:application/octet-stream;base64,' + const dataURL = dataURIPrefix + base64 + try { + const res = await fetch(dataURL) + const buf = await res.arrayBuffer() + const outputStr = Buffer.from(buf).toString('ascii') + t.same(outputStr, inputStr) + } catch (e) { + t.fail(`failed to fetch ${dataURL}`) + } +}) + +test('https://domain.com/#', (t) => { + t.plan(1) + const domain = 'https://domain.com/#a' + const serialized = URLSerializer(new URL(domain)) + t.equal(serialized, domain) +}) + +test('https://domain.com/?', (t) => { + t.plan(1) + const domain = 'https://domain.com/?a=b' + const serialized = URLSerializer(new URL(domain)) + t.equal(serialized, domain) +}) + +// https://github.com/nodejs/undici/issues/2474 +test('hash url', (t) => { + t.plan(1) + const domain = 'https://domain.com/#a#b' + const url = new URL(domain) + const serialized = URLSerializer(url, true) + t.equal(serialized, url.href.substring(0, url.href.length - url.hash.length)) +}) + +// https://github.com/nodejs/undici/issues/2474 +test('data url that includes the hash', async (t) => { + t.plan(1) + const dataURL = 'data:,node#js#' + try { + const res = await fetch(dataURL) + t.equal(await res.text(), 'node') + } catch (error) { + t.fail(`failed to fetch ${dataURL}`) + } +}) |