var boundary = "1234567891011121314151617"; // fn(body) should create a Body subclass with content body treated as // FormData and return it. function testFormDataParsing(fn) { function makeTest(shouldPass, input, testFn) { var obj = fn(input); return obj.formData().then( function (fd) { ok(shouldPass, "Expected test to be valid FormData for " + input); if (testFn) { return testFn(fd); } return undefined; }, function (e) { if (shouldPass) { ok(false, "Expected test to pass for " + input); } else { ok(e.name == "TypeError", "Error should be a TypeError."); } } ); } // [shouldPass?, input, testFn] var tests = [ [ true, boundary + '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", function (fd) { is(fd.get("greeting"), '"hello"'); }, ], [ false, // Invalid disposition. boundary + '\r\nContent-Disposition: form-datafoobar; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ true, "--" + boundary + '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", function (fd) { is(fd.get("greeting"), '"hello"'); }, ], [false, boundary + "\r\n\r\n" + boundary + "-"], [ false, // No valid ending. boundary + "\r\n\r\n" + boundary, ], [ false, // One '-' prefix is not allowed. 2 or none. "-" + boundary + '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, "invalid" + boundary + '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, boundary + "suffix" + '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, boundary + "suffix" + '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, // Partial boundary boundary.substr(3) + '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, boundary + // Missing '\n' at beginning. '\rContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, boundary + // No form-data. '\r\nContent-Disposition: mixed; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, boundary + // No headers. '\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, boundary + // No content-disposition. '\r\nContent-Dispositypo: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, boundary + // No name. '\r\nContent-Disposition: form-data;\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, boundary + // Missing empty line between headers and body. '\r\nContent-Disposition: form-data; name="greeting"\r\n"hello"\r\n' + boundary + "-", ], [ false, // Empty entry followed by valid entry. boundary + "\r\n\r\n" + boundary + '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n"hello"\r\n' + boundary + "-", ], [ false, boundary + // Header followed by empty line, but empty body not followed by // newline. '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n' + boundary + "-", ], [ true, boundary + // Empty body followed by newline. '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n\r\n' + boundary + "-", function (fd) { is(fd.get("greeting"), "", "Empty value is allowed."); }, ], [ false, boundary + // Value is boundary itself. '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n' + boundary + "\r\n" + boundary + "-", ], [ false, boundary + // Variant of above with no valid ending boundary. '\r\nContent-Disposition: form-data; name="greeting"\r\n\r\n' + boundary, ], [ true, boundary + // Unquoted filename with empty body. '\r\nContent-Disposition: form-data; name="file"; filename=file1.txt\r\n\r\n\r\n' + boundary + "-", function (fd) { var f = fd.get("file"); ok( f instanceof File, "Entry with filename attribute should be read as File." ); is(f.name, "file1.txt", "Filename should match."); is(f.type, "text/plain", "Default content-type should be text/plain."); return readAsText(f).then(function (text) { is(text, "", "File should be empty."); }); }, ], [ true, boundary + // Quoted filename with empty body. '\r\nContent-Disposition: form-data; name="file"; filename="file1.txt"\r\n\r\n\r\n' + boundary + "-", function (fd) { var f = fd.get("file"); ok( f instanceof File, "Entry with filename attribute should be read as File." ); is(f.name, "file1.txt", "Filename should match."); is(f.type, "text/plain", "Default content-type should be text/plain."); return readAsText(f).then(function (text) { is(text, "", "File should be empty."); }); }, ], [ false, boundary + // Invalid filename '\r\nContent-Disposition: form-data; name="file"; filename="[\n@;xt"\r\n\r\n\r\n' + boundary + "-", ], [ true, boundary + '\r\nContent-Disposition: form-data; name="file"; filename="[@;xt"\r\n\r\n\r\n' + boundary + "-", function (fd) { var f = fd.get("file"); ok( f instanceof File, "Entry with filename attribute should be read as File." ); is(f.name, "[@", "Filename should match."); }, ], [ true, boundary + '\r\nContent-Disposition: form-data; name="file"; filename="file with spaces"\r\n\r\n\r\n' + boundary + "-", function (fd) { var f = fd.get("file"); ok( f instanceof File, "Entry with filename attribute should be read as File." ); is(f.name, "file with spaces", "Filename should match."); }, ], [ true, boundary + "\r\n" + 'Content-Disposition: form-data; name="file"; filename="xml.txt"\r\n' + "content-type : application/xml\r\n" + "\r\n" + "foobar\r\n\r\n\r\n" + boundary + "-", function (fd) { var f = fd.get("file"); ok( f instanceof File, "Entry with filename attribute should be read as File." ); is(f.name, "xml.txt", "Filename should match."); is( f.type, "application/xml", "content-type should be application/xml." ); return readAsText(f).then(function (text) { is( text, "foobar\r\n\r\n", "File should have correct text." ); }); }, ], ]; var promises = []; for (var i = 0; i < tests.length; ++i) { var test = tests[i]; promises.push(makeTest(test[0], test[1], test[2])); } return Promise.all(promises); } function makeRequest(body) { var req = new Request("", { method: "post", body, headers: { "Content-Type": "multipart/form-data; boundary=" + boundary, }, }); return req; } function makeResponse(body) { var res = new Response(body, { headers: { "Content-Type": "multipart/form-data; boundary=" + boundary, }, }); return res; } function runTest() { return Promise.all([ testFormDataParsing(makeRequest), testFormDataParsing(makeResponse), ]); }