import { describe, test } from "bun:test"; import assert from "node:assert"; import url from "node:url"; describe("url.format", () => { test("slightly wonky content", () => { // Formatting tests to verify that it'll format slightly wonky content to a // valid URL. const formatTests = { "http://example.com?": { href: "http://example.com/?", protocol: "http:", slashes: true, host: "example.com", hostname: "example.com", search: "?", query: {}, pathname: "/", }, "http://example.com?foo=bar#frag": { href: "http://example.com/?foo=bar#frag", protocol: "http:", host: "example.com", hostname: "example.com", hash: "#frag", search: "?foo=bar", query: "foo=bar", pathname: "/", }, "http://example.com?foo=@bar#frag": { href: "http://example.com/?foo=@bar#frag", protocol: "http:", host: "example.com", hostname: "example.com", hash: "#frag", search: "?foo=@bar", query: "foo=@bar", pathname: "/", }, "http://example.com?foo=/bar/#frag": { href: "http://example.com/?foo=/bar/#frag", protocol: "http:", host: "example.com", hostname: "example.com", hash: "#frag", search: "?foo=/bar/", query: "foo=/bar/", pathname: "/", }, "http://example.com?foo=?bar/#frag": { href: "http://example.com/?foo=?bar/#frag", protocol: "http:", host: "example.com", hostname: "example.com", hash: "#frag", search: "?foo=?bar/", query: "foo=?bar/", pathname: "/", }, "http://example.com#frag=?bar/#frag": { href: "http://example.com/#frag=?bar/#frag", protocol: "http:", host: "example.com", hostname: "example.com", hash: "#frag=?bar/#frag", pathname: "/", }, 'http://google.com" onload="alert(42)/': { href: "http://google.com/%22%20onload=%22alert(42)/", protocol: "http:", host: "google.com", pathname: "/%22%20onload=%22alert(42)/", }, "http://a.com/a/b/c?s#h": { href: "http://a.com/a/b/c?s#h", protocol: "http", host: "a.com", pathname: "a/b/c", hash: "h", search: "s", }, "xmpp:isaacschlueter@jabber.org": { href: "xmpp:isaacschlueter@jabber.org", protocol: "xmpp:", host: "jabber.org", auth: "isaacschlueter", hostname: "jabber.org", }, "http://atpass:foo%40bar@127.0.0.1/": { href: "http://atpass:foo%40bar@127.0.0.1/", auth: "atpass:foo@bar", hostname: "127.0.0.1", protocol: "http:", pathname: "/", }, "http://atslash%2F%40:%2F%40@foo/": { href: "http://atslash%2F%40:%2F%40@foo/", auth: "atslash/@:/@", hostname: "foo", protocol: "http:", pathname: "/", }, "svn+ssh://foo/bar": { href: "svn+ssh://foo/bar", hostname: "foo", protocol: "svn+ssh:", pathname: "/bar", slashes: true, }, "dash-test://foo/bar": { href: "dash-test://foo/bar", hostname: "foo", protocol: "dash-test:", pathname: "/bar", slashes: true, }, "dash-test:foo/bar": { href: "dash-test:foo/bar", hostname: "foo", protocol: "dash-test:", pathname: "/bar", }, "dot.test://foo/bar": { href: "dot.test://foo/bar", hostname: "foo", protocol: "dot.test:", pathname: "/bar", slashes: true, }, "dot.test:foo/bar": { href: "dot.test:foo/bar", hostname: "foo", protocol: "dot.test:", pathname: "/bar", }, // IPv6 support "coap:u:p@[::1]:61616/.well-known/r?n=Temperature": { href: "coap:u:p@[::1]:61616/.well-known/r?n=Temperature", protocol: "coap:", auth: "u:p", hostname: "::1", port: "61616", pathname: "/.well-known/r", search: "n=Temperature", }, "coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton": { href: "coap:[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616/s/stopButton", protocol: "coap", host: "[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:61616", pathname: "/s/stopButton", }, // TODO: Support this. // // "http://[::]/": { // href: "http://[::]/", // protocol: "http:", // hostname: "[::]", // pathname: "/", // }, // Encode context-specific delimiters in path and query, but do not touch // other non-delimiter chars like `%`. // // `#`,`?` in path "/path/to/%%23%3F+=&.txt?foo=theA1#bar": { href: "/path/to/%%23%3F+=&.txt?foo=theA1#bar", pathname: "/path/to/%#?+=&.txt", query: { foo: "theA1", }, hash: "#bar", }, // `#`,`?` in path + `#` in query "/path/to/%%23%3F+=&.txt?foo=the%231#bar": { href: "/path/to/%%23%3F+=&.txt?foo=the%231#bar", pathname: "/path/to/%#?+=&.txt", query: { foo: "the#1", }, hash: "#bar", }, // `#` in path end + `#` in query "/path/to/%%23?foo=the%231#bar": { href: "/path/to/%%23?foo=the%231#bar", pathname: "/path/to/%#", query: { foo: "the#1", }, hash: "#bar", }, // `?` and `#` in path and search "http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag": { href: "http://ex.com/foo%3F100%m%23r?abc=the%231?&foo=bar#frag", protocol: "http:", hostname: "ex.com", hash: "#frag", search: "?abc=the#1?&foo=bar", pathname: "/foo?100%m#r", }, // `?` and `#` in search only "http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag": { href: "http://ex.com/fooA100%mBr?abc=the%231?&foo=bar#frag", protocol: "http:", hostname: "ex.com", hash: "#frag", search: "?abc=the#1?&foo=bar", pathname: "/fooA100%mBr", }, // TODO: Support these. // // // Multiple `#` in search // "http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag": { // href: "http://example.com/?foo=bar%231%232%233&abc=%234%23%235#frag", // protocol: "http:", // slashes: true, // host: "example.com", // hostname: "example.com", // hash: "#frag", // search: "?foo=bar#1#2#3&abc=#4##5", // query: {}, // pathname: "/", // }, // More than 255 characters in hostname which exceeds the limit // [`http://${"a".repeat(255)}.com/node`]: { // href: "http:///node", // protocol: "http:", // slashes: true, // host: "", // hostname: "", // pathname: "/node", // path: "/node", // }, // Greater than or equal to 63 characters after `.` in hostname // [`http://www.${"z".repeat(63)}example.com/node`]: { // href: `http://www.${"z".repeat(63)}example.com/node`, // protocol: "http:", // slashes: true, // host: `www.${"z".repeat(63)}example.com`, // hostname: `www.${"z".repeat(63)}example.com`, // pathname: "/node", // path: "/node", // }, // https://github.com/nodejs/node/issues/3361 // "file:///home/user": { // href: "file:///home/user", // protocol: "file", // pathname: "/home/user", // path: "/home/user", // }, // surrogate in auth "http://%F0%9F%98%80@www.example.com/": { href: "http://%F0%9F%98%80@www.example.com/", protocol: "http:", auth: "\uD83D\uDE00", hostname: "www.example.com", pathname: "/", }, }; for (const u in formatTests) { const expect = formatTests[u].href; delete formatTests[u].href; const actual = url.format(u); const actualObj = url.format(formatTests[u]); assert.strictEqual(actual, expect, `wonky format(${u}) == ${expect}\nactual:${actual}`); assert.strictEqual( actualObj, expect, `wonky format(${JSON.stringify(formatTests[u])}) == ${expect}\nactual: ${actualObj}`, ); } }); });