mirror of
https://github.com/denoland/deno.git
synced 2024-10-29 08:58:01 -04:00
check runtime FormData
and Headers
params (#1415)
This commit is contained in:
parent
9bfe9a005b
commit
5e518b7417
5 changed files with 197 additions and 6 deletions
|
@ -3,6 +3,7 @@ import * as domTypes from "./dom_types";
|
||||||
import * as blob from "./blob";
|
import * as blob from "./blob";
|
||||||
import * as file from "./file";
|
import * as file from "./file";
|
||||||
import { DomIterableMixin } from "./mixins/dom_iterable";
|
import { DomIterableMixin } from "./mixins/dom_iterable";
|
||||||
|
import { requiredArguments } from "./util";
|
||||||
|
|
||||||
const dataSymbol = Symbol("data");
|
const dataSymbol = Symbol("data");
|
||||||
|
|
||||||
|
@ -18,6 +19,8 @@ class FormDataBase {
|
||||||
append(name: string, value: string): void;
|
append(name: string, value: string): void;
|
||||||
append(name: string, value: blob.DenoBlob, filename?: string): void;
|
append(name: string, value: blob.DenoBlob, filename?: string): void;
|
||||||
append(name: string, value: string | blob.DenoBlob, filename?: string): void {
|
append(name: string, value: string | blob.DenoBlob, filename?: string): void {
|
||||||
|
requiredArguments("FormData.append", arguments.length, 2);
|
||||||
|
name = String(name);
|
||||||
if (value instanceof blob.DenoBlob) {
|
if (value instanceof blob.DenoBlob) {
|
||||||
const dfile = new file.DenoFile([value], filename || name);
|
const dfile = new file.DenoFile([value], filename || name);
|
||||||
this[dataSymbol].push([name, dfile]);
|
this[dataSymbol].push([name, dfile]);
|
||||||
|
@ -31,6 +34,8 @@ class FormDataBase {
|
||||||
* formData.delete('name');
|
* formData.delete('name');
|
||||||
*/
|
*/
|
||||||
delete(name: string): void {
|
delete(name: string): void {
|
||||||
|
requiredArguments("FormData.delete", arguments.length, 1);
|
||||||
|
name = String(name);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < this[dataSymbol].length) {
|
while (i < this[dataSymbol].length) {
|
||||||
if (this[dataSymbol][i][0] === name) {
|
if (this[dataSymbol][i][0] === name) {
|
||||||
|
@ -47,6 +52,8 @@ class FormDataBase {
|
||||||
* formData.getAll('name');
|
* formData.getAll('name');
|
||||||
*/
|
*/
|
||||||
getAll(name: string): domTypes.FormDataEntryValue[] {
|
getAll(name: string): domTypes.FormDataEntryValue[] {
|
||||||
|
requiredArguments("FormData.getAll", arguments.length, 1);
|
||||||
|
name = String(name);
|
||||||
const values = [];
|
const values = [];
|
||||||
for (const entry of this[dataSymbol]) {
|
for (const entry of this[dataSymbol]) {
|
||||||
if (entry[0] === name) {
|
if (entry[0] === name) {
|
||||||
|
@ -63,6 +70,8 @@ class FormDataBase {
|
||||||
* formData.get('name');
|
* formData.get('name');
|
||||||
*/
|
*/
|
||||||
get(name: string): domTypes.FormDataEntryValue | null {
|
get(name: string): domTypes.FormDataEntryValue | null {
|
||||||
|
requiredArguments("FormData.get", arguments.length, 1);
|
||||||
|
name = String(name);
|
||||||
for (const entry of this[dataSymbol]) {
|
for (const entry of this[dataSymbol]) {
|
||||||
if (entry[0] === name) {
|
if (entry[0] === name) {
|
||||||
return entry[1];
|
return entry[1];
|
||||||
|
@ -78,23 +87,53 @@ class FormDataBase {
|
||||||
* formData.has('name');
|
* formData.has('name');
|
||||||
*/
|
*/
|
||||||
has(name: string): boolean {
|
has(name: string): boolean {
|
||||||
|
requiredArguments("FormData.has", arguments.length, 1);
|
||||||
|
name = String(name);
|
||||||
return this[dataSymbol].some(entry => entry[0] === name);
|
return this[dataSymbol].some(entry => entry[0] === name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets a new value for an existing key inside a `FormData` object, or
|
/** Sets a new value for an existing key inside a `FormData` object, or
|
||||||
* adds the key/value if it does not already exist.
|
* adds the key/value if it does not already exist.
|
||||||
|
* ref: https://xhr.spec.whatwg.org/#dom-formdata-set
|
||||||
*
|
*
|
||||||
* formData.set('name', 'value');
|
* formData.set('name', 'value');
|
||||||
*/
|
*/
|
||||||
set(name: string, value: string): void;
|
set(name: string, value: string): void;
|
||||||
set(name: string, value: blob.DenoBlob, filename?: string): void;
|
set(name: string, value: blob.DenoBlob, filename?: string): void;
|
||||||
set(name: string, value: string | blob.DenoBlob, filename?: string): void {
|
set(name: string, value: string | blob.DenoBlob, filename?: string): void {
|
||||||
this.delete(name);
|
requiredArguments("FormData.set", arguments.length, 2);
|
||||||
if (value instanceof blob.DenoBlob) {
|
name = String(name);
|
||||||
const dfile = new file.DenoFile([value], filename || name);
|
|
||||||
this[dataSymbol].push([name, dfile]);
|
// If there are any entries in the context object’s entry list whose name
|
||||||
} else {
|
// is name, replace the first such entry with entry and remove the others
|
||||||
this[dataSymbol].push([name, String(value)]);
|
let found = false;
|
||||||
|
let i = 0;
|
||||||
|
while (i < this[dataSymbol].length) {
|
||||||
|
if (this[dataSymbol][i][0] === name) {
|
||||||
|
if (!found) {
|
||||||
|
if (value instanceof blob.DenoBlob) {
|
||||||
|
const dfile = new file.DenoFile([value], filename || name);
|
||||||
|
this[dataSymbol][i][1] = dfile;
|
||||||
|
} else {
|
||||||
|
this[dataSymbol][i][1] = String(value);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
} else {
|
||||||
|
this[dataSymbol].splice(i, 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, append entry to the context object’s entry list.
|
||||||
|
if (!found) {
|
||||||
|
if (value instanceof blob.DenoBlob) {
|
||||||
|
const dfile = new file.DenoFile([value], filename || name);
|
||||||
|
this[dataSymbol].push([name, dfile]);
|
||||||
|
} else {
|
||||||
|
this[dataSymbol].push([name, String(value)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,3 +96,73 @@ test(function formDataParamsForEachSuccess() {
|
||||||
});
|
});
|
||||||
assertEqual(callNum, init.length);
|
assertEqual(callNum, init.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(function formDataParamsArgumentsCheck() {
|
||||||
|
const methodRequireOneParam = ["delete", "getAll", "get", "has", "forEach"];
|
||||||
|
|
||||||
|
const methodRequireTwoParams = ["append", "set"];
|
||||||
|
|
||||||
|
methodRequireOneParam.forEach(method => {
|
||||||
|
const formData = new FormData();
|
||||||
|
let hasThrown = 0;
|
||||||
|
let errMsg = "";
|
||||||
|
try {
|
||||||
|
formData[method]();
|
||||||
|
hasThrown = 1;
|
||||||
|
} catch (err) {
|
||||||
|
errMsg = err.message;
|
||||||
|
if (err instanceof TypeError) {
|
||||||
|
hasThrown = 2;
|
||||||
|
} else {
|
||||||
|
hasThrown = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEqual(hasThrown, 2);
|
||||||
|
assertEqual(
|
||||||
|
errMsg,
|
||||||
|
`FormData.${method} requires at least 1 argument, but only 0 present`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
methodRequireTwoParams.forEach(method => {
|
||||||
|
const formData = new FormData();
|
||||||
|
let hasThrown = 0;
|
||||||
|
let errMsg = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
formData[method]();
|
||||||
|
hasThrown = 1;
|
||||||
|
} catch (err) {
|
||||||
|
errMsg = err.message;
|
||||||
|
if (err instanceof TypeError) {
|
||||||
|
hasThrown = 2;
|
||||||
|
} else {
|
||||||
|
hasThrown = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEqual(hasThrown, 2);
|
||||||
|
assertEqual(
|
||||||
|
errMsg,
|
||||||
|
`FormData.${method} requires at least 2 arguments, but only 0 present`
|
||||||
|
);
|
||||||
|
|
||||||
|
hasThrown = 0;
|
||||||
|
errMsg = "";
|
||||||
|
try {
|
||||||
|
formData[method]("foo");
|
||||||
|
hasThrown = 1;
|
||||||
|
} catch (err) {
|
||||||
|
errMsg = err.message;
|
||||||
|
if (err instanceof TypeError) {
|
||||||
|
hasThrown = 2;
|
||||||
|
} else {
|
||||||
|
hasThrown = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEqual(hasThrown, 2);
|
||||||
|
assertEqual(
|
||||||
|
errMsg,
|
||||||
|
`FormData.${method} requires at least 2 arguments, but only 1 present`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||||
import * as domTypes from "./dom_types";
|
import * as domTypes from "./dom_types";
|
||||||
import { DomIterableMixin } from "./mixins/dom_iterable";
|
import { DomIterableMixin } from "./mixins/dom_iterable";
|
||||||
|
import { requiredArguments } from "./util";
|
||||||
|
|
||||||
// From node-fetch
|
// From node-fetch
|
||||||
// Copyright (c) 2016 David Frank. MIT License.
|
// Copyright (c) 2016 David Frank. MIT License.
|
||||||
|
@ -75,6 +76,7 @@ class HeadersBase {
|
||||||
|
|
||||||
// ref: https://fetch.spec.whatwg.org/#concept-headers-append
|
// ref: https://fetch.spec.whatwg.org/#concept-headers-append
|
||||||
append(name: string, value: string): void {
|
append(name: string, value: string): void {
|
||||||
|
requiredArguments("Headers.append", arguments.length, 2);
|
||||||
const [newname, newvalue] = this._normalizeParams(name, value);
|
const [newname, newvalue] = this._normalizeParams(name, value);
|
||||||
this._validateName(newname);
|
this._validateName(newname);
|
||||||
this._validateValue(newvalue);
|
this._validateValue(newvalue);
|
||||||
|
@ -84,12 +86,14 @@ class HeadersBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(name: string): void {
|
delete(name: string): void {
|
||||||
|
requiredArguments("Headers.delete", arguments.length, 1);
|
||||||
const [newname] = this._normalizeParams(name);
|
const [newname] = this._normalizeParams(name);
|
||||||
this._validateName(newname);
|
this._validateName(newname);
|
||||||
this[headerMap].delete(newname);
|
this[headerMap].delete(newname);
|
||||||
}
|
}
|
||||||
|
|
||||||
get(name: string): string | null {
|
get(name: string): string | null {
|
||||||
|
requiredArguments("Headers.get", arguments.length, 1);
|
||||||
const [newname] = this._normalizeParams(name);
|
const [newname] = this._normalizeParams(name);
|
||||||
this._validateName(newname);
|
this._validateName(newname);
|
||||||
const value = this[headerMap].get(newname);
|
const value = this[headerMap].get(newname);
|
||||||
|
@ -97,12 +101,14 @@ class HeadersBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
has(name: string): boolean {
|
has(name: string): boolean {
|
||||||
|
requiredArguments("Headers.has", arguments.length, 1);
|
||||||
const [newname] = this._normalizeParams(name);
|
const [newname] = this._normalizeParams(name);
|
||||||
this._validateName(newname);
|
this._validateName(newname);
|
||||||
return this[headerMap].has(newname);
|
return this[headerMap].has(newname);
|
||||||
}
|
}
|
||||||
|
|
||||||
set(name: string, value: string): void {
|
set(name: string, value: string): void {
|
||||||
|
requiredArguments("Headers.set", arguments.length, 2);
|
||||||
const [newname, newvalue] = this._normalizeParams(name, value);
|
const [newname, newvalue] = this._normalizeParams(name, value);
|
||||||
this._validateName(newname);
|
this._validateName(newname);
|
||||||
this._validateValue(newvalue);
|
this._validateValue(newvalue);
|
||||||
|
|
|
@ -228,3 +228,73 @@ test(function headerIllegalReject() {
|
||||||
// 'o k' is valid value but invalid name
|
// 'o k' is valid value but invalid name
|
||||||
new Headers({ "He-y": "o k" });
|
new Headers({ "He-y": "o k" });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(function headerParamsArgumentsCheck() {
|
||||||
|
const methodRequireOneParam = ["delete", "get", "has", "forEach"];
|
||||||
|
|
||||||
|
const methodRequireTwoParams = ["append", "set"];
|
||||||
|
|
||||||
|
methodRequireOneParam.forEach(method => {
|
||||||
|
const headers = new Headers();
|
||||||
|
let hasThrown = 0;
|
||||||
|
let errMsg = "";
|
||||||
|
try {
|
||||||
|
headers[method]();
|
||||||
|
hasThrown = 1;
|
||||||
|
} catch (err) {
|
||||||
|
errMsg = err.message;
|
||||||
|
if (err instanceof TypeError) {
|
||||||
|
hasThrown = 2;
|
||||||
|
} else {
|
||||||
|
hasThrown = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEqual(hasThrown, 2);
|
||||||
|
assertEqual(
|
||||||
|
errMsg,
|
||||||
|
`Headers.${method} requires at least 1 argument, but only 0 present`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
methodRequireTwoParams.forEach(method => {
|
||||||
|
const headers = new Headers();
|
||||||
|
let hasThrown = 0;
|
||||||
|
let errMsg = "";
|
||||||
|
|
||||||
|
try {
|
||||||
|
headers[method]();
|
||||||
|
hasThrown = 1;
|
||||||
|
} catch (err) {
|
||||||
|
errMsg = err.message;
|
||||||
|
if (err instanceof TypeError) {
|
||||||
|
hasThrown = 2;
|
||||||
|
} else {
|
||||||
|
hasThrown = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEqual(hasThrown, 2);
|
||||||
|
assertEqual(
|
||||||
|
errMsg,
|
||||||
|
`Headers.${method} requires at least 2 arguments, but only 0 present`
|
||||||
|
);
|
||||||
|
|
||||||
|
hasThrown = 0;
|
||||||
|
errMsg = "";
|
||||||
|
try {
|
||||||
|
headers[method]("foo");
|
||||||
|
hasThrown = 1;
|
||||||
|
} catch (err) {
|
||||||
|
errMsg = err.message;
|
||||||
|
if (err instanceof TypeError) {
|
||||||
|
hasThrown = 2;
|
||||||
|
} else {
|
||||||
|
hasThrown = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEqual(hasThrown, 2);
|
||||||
|
assertEqual(
|
||||||
|
errMsg,
|
||||||
|
`Headers.${method} requires at least 2 arguments, but only 1 present`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { DomIterable } from "../dom_types";
|
import { DomIterable } from "../dom_types";
|
||||||
import { globalEval } from "../global_eval";
|
import { globalEval } from "../global_eval";
|
||||||
|
import { requiredArguments } from "../util";
|
||||||
|
|
||||||
// if we import it directly from "globals" it will break the unit tests so we
|
// if we import it directly from "globals" it will break the unit tests so we
|
||||||
// have to grab a reference to the global scope a different way
|
// have to grab a reference to the global scope a different way
|
||||||
|
@ -52,6 +53,11 @@ export function DomIterableMixin<K, V, TBase extends Constructor>(
|
||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
thisArg?: any
|
thisArg?: any
|
||||||
): void {
|
): void {
|
||||||
|
requiredArguments(
|
||||||
|
`${this.constructor.name}.forEach`,
|
||||||
|
arguments.length,
|
||||||
|
1
|
||||||
|
);
|
||||||
callbackfn = callbackfn.bind(thisArg == null ? window : Object(thisArg));
|
callbackfn = callbackfn.bind(thisArg == null ? window : Object(thisArg));
|
||||||
for (const [key, value] of (this as any)[dataSymbol]) {
|
for (const [key, value] of (this as any)[dataSymbol]) {
|
||||||
callbackfn(value, key, this);
|
callbackfn(value, key, this);
|
||||||
|
|
Loading…
Reference in a new issue