url: improve URLSearchParams spec compliance
- Make URLSearchParams constructor spec-compliant - Strip leading `?` in URL#search's setter - Spec-compliant iterable interface - More precise handling of update steps as mandated by the spec - Add class strings to URLSearchParams objects and their prototype - Make sure `this instanceof URLSearchParams` in methods Also included are relevant tests from W3C's Web Platform Tests (https://github.com/w3c/web-platform-tests/tree/master/url). Fixes: https://github.com/nodejs/node/issues/9302 PR-URL: https://github.com/nodejs/node/pull/9484 Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
1b25214001
commit
61d6293033
@ -20,6 +20,11 @@ const kHost = Symbol('host');
|
||||
const kPort = Symbol('port');
|
||||
const kDomain = Symbol('domain');
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-%iteratorprototype%-object
|
||||
const IteratorPrototype = Object.getPrototypeOf(
|
||||
Object.getPrototypeOf([][Symbol.iterator]())
|
||||
);
|
||||
|
||||
function StorageObject() {}
|
||||
StorageObject.prototype = Object.create(null);
|
||||
|
||||
@ -101,7 +106,8 @@ class URL {
|
||||
this[context].query = query;
|
||||
this[context].fragment = fragment;
|
||||
this[context].host = host;
|
||||
this[searchParams] = new URLSearchParams(this);
|
||||
this[searchParams] = new URLSearchParams(query);
|
||||
this[searchParams][context] = this;
|
||||
});
|
||||
}
|
||||
|
||||
@ -318,8 +324,31 @@ class URL {
|
||||
}
|
||||
|
||||
set search(search) {
|
||||
update(this, search);
|
||||
this[searchParams][searchParams] = querystring.parse(this.search);
|
||||
search = String(search);
|
||||
if (search[0] === '?') search = search.slice(1);
|
||||
if (!search) {
|
||||
this[context].query = null;
|
||||
this[context].flags &= ~binding.URL_FLAGS_HAS_QUERY;
|
||||
this[searchParams][searchParams] = {};
|
||||
return;
|
||||
}
|
||||
this[context].query = '';
|
||||
binding.parse(search,
|
||||
binding.kQuery,
|
||||
null,
|
||||
this[context],
|
||||
(flags, protocol, username, password,
|
||||
host, port, path, query, fragment) => {
|
||||
if (flags & binding.URL_FLAGS_FAILED)
|
||||
return;
|
||||
if (query) {
|
||||
this[context].query = query;
|
||||
this[context].flags |= binding.URL_FLAGS_HAS_QUERY;
|
||||
} else {
|
||||
this[context].flags &= ~binding.URL_FLAGS_HAS_QUERY;
|
||||
}
|
||||
});
|
||||
this[searchParams][searchParams] = querystring.parse(search);
|
||||
}
|
||||
|
||||
get hash() {
|
||||
@ -493,76 +522,129 @@ function encodeAuth(str) {
|
||||
return out;
|
||||
}
|
||||
|
||||
function update(url, search) {
|
||||
search = String(search);
|
||||
if (!search) {
|
||||
url[context].query = null;
|
||||
url[context].flags &= ~binding.URL_FLAGS_HAS_QUERY;
|
||||
function update(url, params) {
|
||||
if (!url)
|
||||
return;
|
||||
|
||||
url[context].query = params.toString();
|
||||
}
|
||||
|
||||
function getSearchParamPairs(target) {
|
||||
const obj = target[searchParams];
|
||||
const keys = Object.keys(obj);
|
||||
const values = [];
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
const name = keys[i];
|
||||
const value = obj[name];
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value)
|
||||
values.push([name, item]);
|
||||
} else {
|
||||
values.push([name, value]);
|
||||
}
|
||||
}
|
||||
if (search[0] === '?') search = search.slice(1);
|
||||
url[context].query = '';
|
||||
binding.parse(search,
|
||||
binding.kQuery,
|
||||
null,
|
||||
url[context],
|
||||
(flags, protocol, username, password,
|
||||
host, port, path, query, fragment) => {
|
||||
if (flags & binding.URL_FLAGS_FAILED)
|
||||
return;
|
||||
if (query) {
|
||||
url[context].query = query;
|
||||
url[context].flags |= binding.URL_FLAGS_HAS_QUERY;
|
||||
} else {
|
||||
url[context].flags &= ~binding.URL_FLAGS_HAS_QUERY;
|
||||
}
|
||||
});
|
||||
return values;
|
||||
}
|
||||
|
||||
class URLSearchParams {
|
||||
constructor(url) {
|
||||
this[context] = url;
|
||||
this[searchParams] = querystring.parse(url[context].search || '');
|
||||
constructor(init = '') {
|
||||
if (init instanceof URLSearchParams) {
|
||||
const childParams = init[searchParams];
|
||||
this[searchParams] = Object.assign(Object.create(null), childParams);
|
||||
} else {
|
||||
init = String(init);
|
||||
if (init[0] === '?') init = init.slice(1);
|
||||
this[searchParams] = querystring.parse(init);
|
||||
}
|
||||
|
||||
// "associated url object"
|
||||
this[context] = null;
|
||||
|
||||
// Class string for an instance of URLSearchParams. This is different from
|
||||
// the class string of the prototype object (set below).
|
||||
Object.defineProperty(this, Symbol.toStringTag, {
|
||||
value: 'URLSearchParams',
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
append(name, value) {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
if (arguments.length < 2) {
|
||||
throw new TypeError(
|
||||
'Both `name` and `value` arguments need to be specified');
|
||||
}
|
||||
|
||||
const obj = this[searchParams];
|
||||
name = String(name);
|
||||
value = String(value);
|
||||
var existing = obj[name];
|
||||
if (!existing) {
|
||||
if (existing === undefined) {
|
||||
obj[name] = value;
|
||||
} else if (Array.isArray(existing)) {
|
||||
existing.push(value);
|
||||
} else {
|
||||
obj[name] = [existing, value];
|
||||
}
|
||||
update(this[context], querystring.stringify(obj));
|
||||
update(this[context], this);
|
||||
}
|
||||
|
||||
delete(name) {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
if (arguments.length < 1) {
|
||||
throw new TypeError('The `name` argument needs to be specified');
|
||||
}
|
||||
|
||||
const obj = this[searchParams];
|
||||
name = String(name);
|
||||
delete obj[name];
|
||||
update(this[context], querystring.stringify(obj));
|
||||
update(this[context], this);
|
||||
}
|
||||
|
||||
set(name, value) {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
if (arguments.length < 2) {
|
||||
throw new TypeError(
|
||||
'Both `name` and `value` arguments need to be specified');
|
||||
}
|
||||
|
||||
const obj = this[searchParams];
|
||||
name = String(name);
|
||||
value = String(value);
|
||||
obj[name] = value;
|
||||
update(this[context], querystring.stringify(obj));
|
||||
update(this[context], this);
|
||||
}
|
||||
|
||||
get(name) {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
if (arguments.length < 1) {
|
||||
throw new TypeError('The `name` argument needs to be specified');
|
||||
}
|
||||
|
||||
const obj = this[searchParams];
|
||||
name = String(name);
|
||||
var value = obj[name];
|
||||
return Array.isArray(value) ? value[0] : value;
|
||||
return value === undefined ? null : Array.isArray(value) ? value[0] : value;
|
||||
}
|
||||
|
||||
getAll(name) {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
if (arguments.length < 1) {
|
||||
throw new TypeError('The `name` argument needs to be specified');
|
||||
}
|
||||
|
||||
const obj = this[searchParams];
|
||||
name = String(name);
|
||||
var value = obj[name];
|
||||
@ -570,28 +652,143 @@ class URLSearchParams {
|
||||
}
|
||||
|
||||
has(name) {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
if (arguments.length < 1) {
|
||||
throw new TypeError('The `name` argument needs to be specified');
|
||||
}
|
||||
|
||||
const obj = this[searchParams];
|
||||
name = String(name);
|
||||
return name in obj;
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
const obj = this[searchParams];
|
||||
for (const name in obj) {
|
||||
const value = obj[name];
|
||||
if (Array.isArray(value)) {
|
||||
for (const item of value)
|
||||
yield [name, item];
|
||||
} else {
|
||||
yield [name, value];
|
||||
}
|
||||
// https://heycam.github.io/webidl/#es-iterators
|
||||
// Define entries here rather than [Symbol.iterator] as the function name
|
||||
// must be set to `entries`.
|
||||
entries() {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
|
||||
return createSearchParamsIterator(this, 'key+value');
|
||||
}
|
||||
|
||||
forEach(callback, thisArg = undefined) {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
if (arguments.length < 1) {
|
||||
throw new TypeError('The `callback` argument needs to be specified');
|
||||
}
|
||||
|
||||
let pairs = getSearchParamPairs(this);
|
||||
|
||||
var i = 0;
|
||||
while (i < pairs.length) {
|
||||
const [key, value] = pairs[i];
|
||||
callback.call(thisArg, value, key, this);
|
||||
pairs = getSearchParamPairs(this);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// https://heycam.github.io/webidl/#es-iterable
|
||||
keys() {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
|
||||
return createSearchParamsIterator(this, 'key');
|
||||
}
|
||||
|
||||
values() {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
|
||||
return createSearchParamsIterator(this, 'value');
|
||||
}
|
||||
|
||||
// https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior
|
||||
toString() {
|
||||
if (!this || !(this instanceof URLSearchParams)) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParams');
|
||||
}
|
||||
|
||||
return querystring.stringify(this[searchParams]);
|
||||
}
|
||||
}
|
||||
// https://heycam.github.io/webidl/#es-iterable-entries
|
||||
URLSearchParams.prototype[Symbol.iterator] = URLSearchParams.prototype.entries;
|
||||
Object.defineProperty(URLSearchParams.prototype, Symbol.toStringTag, {
|
||||
value: 'URLSearchParamsPrototype',
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
// https://heycam.github.io/webidl/#dfn-default-iterator-object
|
||||
function createSearchParamsIterator(target, kind) {
|
||||
const iterator = Object.create(URLSearchParamsIteratorPrototype);
|
||||
iterator[context] = {
|
||||
target,
|
||||
kind,
|
||||
index: 0
|
||||
};
|
||||
return iterator;
|
||||
}
|
||||
|
||||
// https://heycam.github.io/webidl/#dfn-iterator-prototype-object
|
||||
const URLSearchParamsIteratorPrototype = Object.setPrototypeOf({
|
||||
next() {
|
||||
if (!this ||
|
||||
Object.getPrototypeOf(this) !== URLSearchParamsIteratorPrototype) {
|
||||
throw new TypeError('Value of `this` is not a URLSearchParamsIterator');
|
||||
}
|
||||
|
||||
const {
|
||||
target,
|
||||
kind,
|
||||
index
|
||||
} = this[context];
|
||||
const values = getSearchParamPairs(target);
|
||||
const len = values.length;
|
||||
if (index >= len) {
|
||||
return {
|
||||
value: undefined,
|
||||
done: true
|
||||
};
|
||||
}
|
||||
|
||||
const pair = values[index];
|
||||
this[context].index = index + 1;
|
||||
|
||||
let result;
|
||||
if (kind === 'key') {
|
||||
result = pair[0];
|
||||
} else if (kind === 'value') {
|
||||
result = pair[1];
|
||||
} else {
|
||||
result = pair;
|
||||
}
|
||||
|
||||
return {
|
||||
value: result,
|
||||
done: false
|
||||
};
|
||||
}
|
||||
}, IteratorPrototype);
|
||||
|
||||
// Unlike interface and its prototype object, both default iterator object and
|
||||
// iterator prototype object of an interface have the same class string.
|
||||
Object.defineProperty(URLSearchParamsIteratorPrototype, Symbol.toStringTag, {
|
||||
value: 'URLSearchParamsIterator',
|
||||
writable: false,
|
||||
enumerable: false,
|
||||
configurable: true
|
||||
});
|
||||
|
||||
URL.originFor = function(url) {
|
||||
if (!(url instanceof URL))
|
||||
|
52
test/parallel/test-whatwg-url-searchparams-append.js
Normal file
52
test/parallel/test-whatwg-url-searchparams-append.js
Normal file
@ -0,0 +1,52 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const URL = require('url').URL;
|
||||
|
||||
const m = new URL('http://example.org');
|
||||
let params = m.searchParams;
|
||||
|
||||
// Until we export URLSearchParams
|
||||
const URLSearchParams = params.constructor;
|
||||
|
||||
// Append same name
|
||||
params = new URLSearchParams();
|
||||
params.append('a', 'b');
|
||||
assert.strictEqual(params + '', 'a=b');
|
||||
params.append('a', 'b');
|
||||
assert.strictEqual(params + '', 'a=b&a=b');
|
||||
params.append('a', 'c');
|
||||
assert.strictEqual(params + '', 'a=b&a=b&a=c');
|
||||
|
||||
// Append empty strings
|
||||
params = new URLSearchParams();
|
||||
params.append('', '');
|
||||
assert.strictEqual(params + '', '=');
|
||||
params.append('', '');
|
||||
assert.strictEqual(params + '', '=&=');
|
||||
|
||||
// Append null
|
||||
params = new URLSearchParams();
|
||||
params.append(null, null);
|
||||
assert.strictEqual(params + '', 'null=null');
|
||||
params.append(null, null);
|
||||
assert.strictEqual(params + '', 'null=null&null=null');
|
||||
|
||||
// Append multiple
|
||||
params = new URLSearchParams();
|
||||
params.append('first', 1);
|
||||
params.append('second', 2);
|
||||
params.append('third', '');
|
||||
params.append('first', 10);
|
||||
assert.strictEqual(true, params.has('first'),
|
||||
'Search params object has name "first"');
|
||||
assert.strictEqual(params.get('first'), '1',
|
||||
'Search params object has name "first" with value "1"');
|
||||
assert.strictEqual(params.get('second'), '2',
|
||||
'Search params object has name "second" with value "2"');
|
||||
assert.strictEqual(params.get('third'), '',
|
||||
'Search params object has name "third" with value ""');
|
||||
params.append('first', 10);
|
||||
assert.strictEqual(params.get('first'), '1',
|
||||
'Search params object has name "first" with value "1"');
|
134
test/parallel/test-whatwg-url-searchparams-constructor.js
Normal file
134
test/parallel/test-whatwg-url-searchparams-constructor.js
Normal file
@ -0,0 +1,134 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const URL = require('url').URL;
|
||||
|
||||
const m = new URL('http://example.org');
|
||||
let params = m.searchParams;
|
||||
|
||||
// Until we export URLSearchParams
|
||||
const URLSearchParams = params.constructor;
|
||||
|
||||
// Basic URLSearchParams construction
|
||||
params = new URLSearchParams();
|
||||
assert.strictEqual(params + '', '');
|
||||
params = new URLSearchParams('');
|
||||
assert.strictEqual(params + '', '');
|
||||
params = new URLSearchParams('a=b');
|
||||
assert.strictEqual(params + '', 'a=b');
|
||||
params = new URLSearchParams(params);
|
||||
assert.strictEqual(params + '', 'a=b');
|
||||
|
||||
// URLSearchParams constructor, empty.
|
||||
assert.throws(() => URLSearchParams(), TypeError,
|
||||
'Calling \'URLSearchParams\' without \'new\' should throw.');
|
||||
// assert.throws(() => new URLSearchParams(DOMException.prototype), TypeError);
|
||||
assert.throws(() => {
|
||||
new URLSearchParams({
|
||||
toString() { throw new TypeError('Illegal invocation'); }
|
||||
});
|
||||
}, TypeError);
|
||||
params = new URLSearchParams('');
|
||||
assert.notEqual(params, null, 'constructor returned non-null value.');
|
||||
// eslint-disable-next-line no-proto
|
||||
assert.strictEqual(params.__proto__, URLSearchParams.prototype,
|
||||
'expected URLSearchParams.prototype as prototype.');
|
||||
params = new URLSearchParams({});
|
||||
// assert.strictEqual(params + '', '%5Bobject+Object%5D=');
|
||||
assert.strictEqual(params + '', '%5Bobject%20Object%5D=');
|
||||
|
||||
// URLSearchParams constructor, string.
|
||||
params = new URLSearchParams('a=b');
|
||||
assert.notEqual(params, null, 'constructor returned non-null value.');
|
||||
assert.strictEqual(true, params.has('a'),
|
||||
'Search params object has name "a"');
|
||||
assert.strictEqual(false, params.has('b'),
|
||||
'Search params object has not got name "b"');
|
||||
params = new URLSearchParams('a=b&c');
|
||||
assert.notEqual(params, null, 'constructor returned non-null value.');
|
||||
assert.strictEqual(true, params.has('a'),
|
||||
'Search params object has name "a"');
|
||||
assert.strictEqual(true, params.has('c'),
|
||||
'Search params object has name "c"');
|
||||
params = new URLSearchParams('&a&&& &&&&&a+b=& c&m%c3%b8%c3%b8');
|
||||
assert.notEqual(params, null, 'constructor returned non-null value.');
|
||||
assert.strictEqual(true, params.has('a'), 'Search params object has name "a"');
|
||||
assert.strictEqual(true, params.has('a b'),
|
||||
'Search params object has name "a b"');
|
||||
assert.strictEqual(true, params.has(' '),
|
||||
'Search params object has name " "');
|
||||
assert.strictEqual(false, params.has('c'),
|
||||
'Search params object did not have the name "c"');
|
||||
assert.strictEqual(true, params.has(' c'),
|
||||
'Search params object has name " c"');
|
||||
assert.strictEqual(true, params.has('møø'),
|
||||
'Search params object has name "møø"');
|
||||
|
||||
// URLSearchParams constructor, object.
|
||||
const seed = new URLSearchParams('a=b&c=d');
|
||||
params = new URLSearchParams(seed);
|
||||
assert.notEqual(params, null, 'constructor returned non-null value.');
|
||||
assert.strictEqual(params.get('a'), 'b');
|
||||
assert.strictEqual(params.get('c'), 'd');
|
||||
assert.strictEqual(false, params.has('d'));
|
||||
// The name-value pairs are copied when created; later updates
|
||||
// should not be observable.
|
||||
seed.append('e', 'f');
|
||||
assert.strictEqual(false, params.has('e'));
|
||||
params.append('g', 'h');
|
||||
assert.strictEqual(false, seed.has('g'));
|
||||
|
||||
// Parse +
|
||||
params = new URLSearchParams('a=b+c');
|
||||
assert.strictEqual(params.get('a'), 'b c');
|
||||
params = new URLSearchParams('a+b=c');
|
||||
assert.strictEqual(params.get('a b'), 'c');
|
||||
|
||||
// Parse space
|
||||
params = new URLSearchParams('a=b c');
|
||||
assert.strictEqual(params.get('a'), 'b c');
|
||||
params = new URLSearchParams('a b=c');
|
||||
assert.strictEqual(params.get('a b'), 'c');
|
||||
|
||||
// Parse %20
|
||||
params = new URLSearchParams('a=b%20c');
|
||||
assert.strictEqual(params.get('a'), 'b c');
|
||||
params = new URLSearchParams('a%20b=c');
|
||||
assert.strictEqual(params.get('a b'), 'c');
|
||||
|
||||
// Parse \0
|
||||
params = new URLSearchParams('a=b\0c');
|
||||
assert.strictEqual(params.get('a'), 'b\0c');
|
||||
params = new URLSearchParams('a\0b=c');
|
||||
assert.strictEqual(params.get('a\0b'), 'c');
|
||||
|
||||
// Parse %00
|
||||
params = new URLSearchParams('a=b%00c');
|
||||
assert.strictEqual(params.get('a'), 'b\0c');
|
||||
params = new URLSearchParams('a%00b=c');
|
||||
assert.strictEqual(params.get('a\0b'), 'c');
|
||||
|
||||
// Parse \u2384 (Unicode Character 'COMPOSITION SYMBOL' (U+2384))
|
||||
params = new URLSearchParams('a=b\u2384');
|
||||
assert.strictEqual(params.get('a'), 'b\u2384');
|
||||
params = new URLSearchParams('a\u2384b=c');
|
||||
assert.strictEqual(params.get('a\u2384b'), 'c');
|
||||
|
||||
// Parse %e2%8e%84 (Unicode Character 'COMPOSITION SYMBOL' (U+2384))
|
||||
params = new URLSearchParams('a=b%e2%8e%84');
|
||||
assert.strictEqual(params.get('a'), 'b\u2384');
|
||||
params = new URLSearchParams('a%e2%8e%84b=c');
|
||||
assert.strictEqual(params.get('a\u2384b'), 'c');
|
||||
|
||||
// Parse \uD83D\uDCA9 (Unicode Character 'PILE OF POO' (U+1F4A9))
|
||||
params = new URLSearchParams('a=b\uD83D\uDCA9c');
|
||||
assert.strictEqual(params.get('a'), 'b\uD83D\uDCA9c');
|
||||
params = new URLSearchParams('a\uD83D\uDCA9b=c');
|
||||
assert.strictEqual(params.get('a\uD83D\uDCA9b'), 'c');
|
||||
|
||||
// Parse %f0%9f%92%a9 (Unicode Character 'PILE OF POO' (U+1F4A9))
|
||||
params = new URLSearchParams('a=b%f0%9f%92%a9c');
|
||||
assert.strictEqual(params.get('a'), 'b\uD83D\uDCA9c');
|
||||
params = new URLSearchParams('a%f0%9f%92%a9b=c');
|
||||
assert.strictEqual(params.get('a\uD83D\uDCA9b'), 'c');
|
44
test/parallel/test-whatwg-url-searchparams-delete.js
Normal file
44
test/parallel/test-whatwg-url-searchparams-delete.js
Normal file
@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const URL = require('url').URL;
|
||||
|
||||
const m = new URL('http://example.org');
|
||||
let params = m.searchParams;
|
||||
|
||||
// Until we export URLSearchParams
|
||||
const URLSearchParams = params.constructor;
|
||||
|
||||
// Delete basics
|
||||
params = new URLSearchParams('a=b&c=d');
|
||||
params.delete('a');
|
||||
assert.strictEqual(params + '', 'c=d');
|
||||
params = new URLSearchParams('a=a&b=b&a=a&c=c');
|
||||
params.delete('a');
|
||||
assert.strictEqual(params + '', 'b=b&c=c');
|
||||
params = new URLSearchParams('a=a&=&b=b&c=c');
|
||||
params.delete('');
|
||||
assert.strictEqual(params + '', 'a=a&b=b&c=c');
|
||||
params = new URLSearchParams('a=a&null=null&b=b');
|
||||
params.delete(null);
|
||||
assert.strictEqual(params + '', 'a=a&b=b');
|
||||
params = new URLSearchParams('a=a&undefined=undefined&b=b');
|
||||
params.delete(undefined);
|
||||
assert.strictEqual(params + '', 'a=a&b=b');
|
||||
|
||||
// Deleting appended multiple
|
||||
params = new URLSearchParams();
|
||||
params.append('first', 1);
|
||||
assert.strictEqual(true, params.has('first'),
|
||||
'Search params object has name "first"');
|
||||
assert.strictEqual(params.get('first'), '1',
|
||||
'Search params object has name "first" with value "1"');
|
||||
params.delete('first');
|
||||
assert.strictEqual(false, params.has('first'),
|
||||
'Search params object has no "first" name');
|
||||
params.append('first', 1);
|
||||
params.append('first', 10);
|
||||
params.delete('first');
|
||||
assert.strictEqual(false, params.has('first'),
|
||||
'Search params object has no "first" name');
|
43
test/parallel/test-whatwg-url-searchparams-foreach.js
Normal file
43
test/parallel/test-whatwg-url-searchparams-foreach.js
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const URL = require('url').URL;
|
||||
|
||||
const m = new URL('http://example.org');
|
||||
let params = m.searchParams;
|
||||
|
||||
// Until we export URLSearchParams
|
||||
const URLSearchParams = params.constructor;
|
||||
|
||||
let a, b, i;
|
||||
|
||||
// ForEach Check
|
||||
params = new URLSearchParams('a=1&b=2&c=3');
|
||||
const keys = [];
|
||||
const values = [];
|
||||
params.forEach(function(value, key) {
|
||||
keys.push(key);
|
||||
values.push(value);
|
||||
});
|
||||
assert.deepStrictEqual(keys, ['a', 'b', 'c']);
|
||||
assert.deepStrictEqual(values, ['1', '2', '3']);
|
||||
|
||||
// For-of Check
|
||||
a = new URL('http://a.b/c?a=1&b=2&c=3&d=4');
|
||||
b = a.searchParams;
|
||||
const c = [];
|
||||
for (i of b) {
|
||||
a.search = 'x=1&y=2&z=3';
|
||||
c.push(i);
|
||||
}
|
||||
assert.deepStrictEqual(c[0], ['a', '1']);
|
||||
assert.deepStrictEqual(c[1], ['y', '2']);
|
||||
assert.deepStrictEqual(c[2], ['z', '3']);
|
||||
|
||||
// empty
|
||||
a = new URL('http://a.b/c');
|
||||
b = a.searchParams;
|
||||
for (i of b) {
|
||||
assert(false, 'should not be reached');
|
||||
}
|
35
test/parallel/test-whatwg-url-searchparams-get.js
Normal file
35
test/parallel/test-whatwg-url-searchparams-get.js
Normal file
@ -0,0 +1,35 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const URL = require('url').URL;
|
||||
|
||||
const m = new URL('http://example.org');
|
||||
let params = m.searchParams;
|
||||
|
||||
// Until we export URLSearchParams
|
||||
const URLSearchParams = params.constructor;
|
||||
|
||||
// Get basics
|
||||
params = new URLSearchParams('a=b&c=d');
|
||||
assert.strictEqual(params.get('a'), 'b');
|
||||
assert.strictEqual(params.get('c'), 'd');
|
||||
assert.strictEqual(params.get('e'), null);
|
||||
params = new URLSearchParams('a=b&c=d&a=e');
|
||||
assert.strictEqual(params.get('a'), 'b');
|
||||
params = new URLSearchParams('=b&c=d');
|
||||
assert.strictEqual(params.get(''), 'b');
|
||||
params = new URLSearchParams('a=&c=d&a=e');
|
||||
assert.strictEqual(params.get('a'), '');
|
||||
|
||||
// More get() basics
|
||||
params = new URLSearchParams('first=second&third&&');
|
||||
assert.notEqual(params, null, 'constructor returned non-null value.');
|
||||
assert.strictEqual(true, params.has('first'),
|
||||
'Search params object has name "first"');
|
||||
assert.strictEqual(params.get('first'), 'second',
|
||||
'Search params object has name "first" with value "second"');
|
||||
assert.strictEqual(params.get('third'), '',
|
||||
'Search params object has name "third" with empty value.');
|
||||
assert.strictEqual(params.get('fourth'), null,
|
||||
'Search params object has no "fourth" name and value.');
|
43
test/parallel/test-whatwg-url-searchparams-getall.js
Normal file
43
test/parallel/test-whatwg-url-searchparams-getall.js
Normal file
@ -0,0 +1,43 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const URL = require('url').URL;
|
||||
|
||||
const m = new URL('http://example.org');
|
||||
let params = m.searchParams;
|
||||
|
||||
// Until we export URLSearchParams
|
||||
const URLSearchParams = params.constructor;
|
||||
|
||||
let matches;
|
||||
|
||||
// getAll() basics
|
||||
params = new URLSearchParams('a=b&c=d');
|
||||
assert.deepStrictEqual(params.getAll('a'), ['b']);
|
||||
assert.deepStrictEqual(params.getAll('c'), ['d']);
|
||||
assert.deepStrictEqual(params.getAll('e'), []);
|
||||
params = new URLSearchParams('a=b&c=d&a=e');
|
||||
assert.deepStrictEqual(params.getAll('a'), ['b', 'e']);
|
||||
params = new URLSearchParams('=b&c=d');
|
||||
assert.deepStrictEqual(params.getAll(''), ['b']);
|
||||
params = new URLSearchParams('a=&c=d&a=e');
|
||||
assert.deepStrictEqual(params.getAll('a'), ['', 'e']);
|
||||
|
||||
// getAll() multiples
|
||||
params = new URLSearchParams('a=1&a=2&a=3&a');
|
||||
assert.strictEqual(true, params.has('a'),
|
||||
'Search params object has name "a"');
|
||||
matches = params.getAll('a');
|
||||
assert(matches && matches.length == 4,
|
||||
'Search params object has values for name "a"');
|
||||
assert.deepStrictEqual(matches, ['1', '2', '3', ''],
|
||||
'Search params object has expected name "a" values');
|
||||
params.set('a', 'one');
|
||||
assert.strictEqual(params.get('a'), 'one',
|
||||
'Search params object has name "a" with value "one"');
|
||||
matches = params.getAll('a');
|
||||
assert(matches && matches.length == 1,
|
||||
'Search params object has values for name "a"');
|
||||
assert.deepStrictEqual(matches, ['one'],
|
||||
'Search params object has expected name "a" values');
|
39
test/parallel/test-whatwg-url-searchparams-has.js
Normal file
39
test/parallel/test-whatwg-url-searchparams-has.js
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const URL = require('url').URL;
|
||||
|
||||
const m = new URL('http://example.org');
|
||||
let params = m.searchParams;
|
||||
|
||||
// Until we export URLSearchParams
|
||||
const URLSearchParams = params.constructor;
|
||||
|
||||
// Has basics
|
||||
params = new URLSearchParams('a=b&c=d');
|
||||
assert.strictEqual(true, params.has('a'));
|
||||
assert.strictEqual(true, params.has('c'));
|
||||
assert.strictEqual(false, params.has('e'));
|
||||
params = new URLSearchParams('a=b&c=d&a=e');
|
||||
assert.strictEqual(true, params.has('a'));
|
||||
params = new URLSearchParams('=b&c=d');
|
||||
assert.strictEqual(true, params.has(''));
|
||||
params = new URLSearchParams('null=a');
|
||||
assert.strictEqual(true, params.has(null));
|
||||
|
||||
// has() following delete()
|
||||
params = new URLSearchParams('a=b&c=d&&');
|
||||
params.append('first', 1);
|
||||
params.append('first', 2);
|
||||
assert.strictEqual(true, params.has('a'),
|
||||
'Search params object has name "a"');
|
||||
assert.strictEqual(true, params.has('c'),
|
||||
'Search params object has name "c"');
|
||||
assert.strictEqual(true, params.has('first'),
|
||||
'Search params object has name "first"');
|
||||
assert.strictEqual(false, params.has('d'),
|
||||
'Search params object has no name "d"');
|
||||
params.delete('first');
|
||||
assert.strictEqual(false, params.has('first'),
|
||||
'Search params object has no name "first"');
|
38
test/parallel/test-whatwg-url-searchparams-set.js
Normal file
38
test/parallel/test-whatwg-url-searchparams-set.js
Normal file
@ -0,0 +1,38 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const URL = require('url').URL;
|
||||
|
||||
const m = new URL('http://example.org');
|
||||
let params = m.searchParams;
|
||||
|
||||
// Until we export URLSearchParams
|
||||
const URLSearchParams = params.constructor;
|
||||
|
||||
// Set basics
|
||||
params = new URLSearchParams('a=b&c=d');
|
||||
params.set('a', 'B');
|
||||
assert.strictEqual(params + '', 'a=B&c=d');
|
||||
params = new URLSearchParams('a=b&c=d&a=e');
|
||||
params.set('a', 'B');
|
||||
assert.strictEqual(params + '', 'a=B&c=d');
|
||||
params.set('e', 'f');
|
||||
assert.strictEqual(params + '', 'a=B&c=d&e=f');
|
||||
|
||||
// URLSearchParams.set
|
||||
params = new URLSearchParams('a=1&a=2&a=3');
|
||||
assert.strictEqual(true, params.has('a'),
|
||||
'Search params object has name "a"');
|
||||
assert.strictEqual(params.get('a'), '1',
|
||||
'Search params object has name "a" with value "1"');
|
||||
params.set('first', 4);
|
||||
assert.strictEqual(true, params.has('a'),
|
||||
'Search params object has name "a"');
|
||||
assert.strictEqual(params.get('a'), '1',
|
||||
'Search params object has name "a" with value "1"');
|
||||
params.set('a', 4);
|
||||
assert.strictEqual(true, params.has('a'),
|
||||
'Search params object has name "a"');
|
||||
assert.strictEqual(params.get('a'), '4',
|
||||
'Search params object has name "a" with value "4"');
|
116
test/parallel/test-whatwg-url-searchparams-stringifier.js
Normal file
116
test/parallel/test-whatwg-url-searchparams-stringifier.js
Normal file
@ -0,0 +1,116 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const URL = require('url').URL;
|
||||
|
||||
const m = new URL('http://example.org');
|
||||
let params = m.searchParams;
|
||||
|
||||
// Until we export URLSearchParams
|
||||
const URLSearchParams = params.constructor;
|
||||
|
||||
// Serialize space
|
||||
// querystring does not currently handle spaces intelligently
|
||||
// params = new URLSearchParams();
|
||||
// params.append('a', 'b c');
|
||||
// assert.strictEqual(params + '', 'a=b+c');
|
||||
// params.delete('a');
|
||||
// params.append('a b', 'c');
|
||||
// assert.strictEqual(params + '', 'a+b=c');
|
||||
|
||||
// Serialize empty value
|
||||
params = new URLSearchParams();
|
||||
params.append('a', '');
|
||||
assert.strictEqual(params + '', 'a=');
|
||||
params.append('a', '');
|
||||
assert.strictEqual(params + '', 'a=&a=');
|
||||
params.append('', 'b');
|
||||
assert.strictEqual(params + '', 'a=&a=&=b');
|
||||
params.append('', '');
|
||||
assert.strictEqual(params + '', 'a=&a=&=b&=');
|
||||
params.append('', '');
|
||||
assert.strictEqual(params + '', 'a=&a=&=b&=&=');
|
||||
|
||||
// Serialize empty name
|
||||
params = new URLSearchParams();
|
||||
params.append('', 'b');
|
||||
assert.strictEqual(params + '', '=b');
|
||||
params.append('', 'b');
|
||||
assert.strictEqual(params + '', '=b&=b');
|
||||
|
||||
// Serialize empty name and value
|
||||
params = new URLSearchParams();
|
||||
params.append('', '');
|
||||
assert.strictEqual(params + '', '=');
|
||||
params.append('', '');
|
||||
assert.strictEqual(params + '', '=&=');
|
||||
|
||||
// Serialize +
|
||||
params = new URLSearchParams();
|
||||
params.append('a', 'b+c');
|
||||
assert.strictEqual(params + '', 'a=b%2Bc');
|
||||
params.delete('a');
|
||||
params.append('a+b', 'c');
|
||||
assert.strictEqual(params + '', 'a%2Bb=c');
|
||||
|
||||
// Serialize =
|
||||
params = new URLSearchParams();
|
||||
params.append('=', 'a');
|
||||
assert.strictEqual(params + '', '%3D=a');
|
||||
params.append('b', '=');
|
||||
assert.strictEqual(params + '', '%3D=a&b=%3D');
|
||||
|
||||
// Serialize &
|
||||
params = new URLSearchParams();
|
||||
params.append('&', 'a');
|
||||
assert.strictEqual(params + '', '%26=a');
|
||||
params.append('b', '&');
|
||||
assert.strictEqual(params + '', '%26=a&b=%26');
|
||||
|
||||
// Serialize *-._
|
||||
params = new URLSearchParams();
|
||||
params.append('a', '*-._');
|
||||
assert.strictEqual(params + '', 'a=*-._');
|
||||
params.delete('a');
|
||||
params.append('*-._', 'c');
|
||||
assert.strictEqual(params + '', '*-._=c');
|
||||
|
||||
// Serialize %
|
||||
params = new URLSearchParams();
|
||||
params.append('a', 'b%c');
|
||||
assert.strictEqual(params + '', 'a=b%25c');
|
||||
params.delete('a');
|
||||
params.append('a%b', 'c');
|
||||
assert.strictEqual(params + '', 'a%25b=c');
|
||||
|
||||
// Serialize \\0
|
||||
params = new URLSearchParams();
|
||||
params.append('a', 'b\0c');
|
||||
assert.strictEqual(params + '', 'a=b%00c');
|
||||
params.delete('a');
|
||||
params.append('a\0b', 'c');
|
||||
assert.strictEqual(params + '', 'a%00b=c');
|
||||
|
||||
// Serialize \uD83D\uDCA9
|
||||
// Unicode Character 'PILE OF POO' (U+1F4A9)
|
||||
params = new URLSearchParams();
|
||||
params.append('a', 'b\uD83D\uDCA9c');
|
||||
assert.strictEqual(params + '', 'a=b%F0%9F%92%A9c');
|
||||
params.delete('a');
|
||||
params.append('a\uD83D\uDCA9b', 'c');
|
||||
assert.strictEqual(params + '', 'a%F0%9F%92%A9b=c');
|
||||
|
||||
// URLSearchParams.toString
|
||||
|
||||
// querystring parses `&&` as {'': ''}
|
||||
// params = new URLSearchParams('a=b&c=d&&e&&');
|
||||
// assert.strictEqual(params.toString(), 'a=b&c=d&e=');
|
||||
|
||||
// querystring does not currently handle spaces intelligently
|
||||
// params = new URLSearchParams('a = b &a=b&c=d%20');
|
||||
// assert.strictEqual(params.toString(), 'a+=+b+&a=b&c=d+');
|
||||
|
||||
// The lone '=' _does_ survive the roundtrip.
|
||||
params = new URLSearchParams('a=&a=b');
|
||||
assert.strictEqual(params.toString(), 'a=&a=b');
|
@ -29,8 +29,21 @@ assert.strictEqual(sp.toString(), serialized);
|
||||
|
||||
assert.strictEqual(m.search, `?${serialized}`);
|
||||
|
||||
assert.strictEqual(sp[Symbol.iterator], sp.entries);
|
||||
|
||||
var key, val, n = 0;
|
||||
for ([key, val] of sp) {
|
||||
assert.strictEqual(key, 'a');
|
||||
assert.strictEqual(val, String(values[n++]));
|
||||
}
|
||||
n = 0;
|
||||
for (key of sp.keys()) {
|
||||
assert.strictEqual(key, 'a');
|
||||
}
|
||||
n = 0;
|
||||
for (val of sp.values()) {
|
||||
assert.strictEqual(val, String(values[n++]));
|
||||
}
|
||||
|
||||
m.search = '?a=a&b=b';
|
||||
assert.strictEqual(sp.toString(), 'a=a&b=b');
|
||||
|
Loading…
x
Reference in New Issue
Block a user