• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"use strict";
2
3const kTestChars = "ABC~‾¥≈¤・・•∙·☼★星��星★☼·∙•・・¤≈¥‾~XYZ";
4
5// formDataPostFileUploadTest - verifies multipart upload structure and
6// numeric character reference replacement for filenames, field names,
7// and field values using FormData and fetch().
8//
9// Uses /fetch/api/resources/echo-content.py to echo the upload
10// POST (unlike in send-file-form-helper.js, here we expect all
11// multipart/form-data request bodies to be UTF-8, so we don't need to
12// escape controls and non-ASCII bytes).
13//
14// Fields in the parameter object:
15//
16// - fileNameSource: purely explanatory and gives a clue about which
17//   character encoding is the source for the non-7-bit-ASCII parts of
18//   the fileBaseName, or Unicode if no smaller-than-Unicode source
19//   contains all the characters. Used in the test name.
20// - fileBaseName: the not-necessarily-just-7-bit-ASCII file basename
21//   used for the constructed test file. Used in the test name.
22const formDataPostFileUploadTest = ({
23  fileNameSource,
24  fileBaseName,
25}) => {
26  promise_test(async (testCase) => {
27    const formData = new FormData();
28    let file = new Blob([kTestChars], { type: "text/plain" });
29    try {
30      // Switch to File in browsers that allow this
31      file = new File([file], fileBaseName, { type: file.type });
32    } catch (ignoredException) {
33    }
34
35    // Used to verify that the browser agrees with the test about
36    // field value replacement and encoding independently of file system
37    // idiosyncracies.
38    formData.append("filename", fileBaseName);
39
40    // Same, but with name and value reversed to ensure field names
41    // get the same treatment.
42    formData.append(fileBaseName, "filename");
43
44    formData.append("file", file, fileBaseName);
45
46    const formDataText = await (await fetch(
47      `/fetch/api/resources/echo-content.py`,
48      {
49        method: "POST",
50        body: formData,
51      },
52    )).text();
53    const formDataLines = formDataText.split("\r\n");
54    if (formDataLines.length && !formDataLines[formDataLines.length - 1]) {
55      --formDataLines.length;
56    }
57    assert_greater_than(
58      formDataLines.length,
59      2,
60      `${fileBaseName}: multipart form data must have at least 3 lines: ${
61        JSON.stringify(formDataText)
62      }`,
63    );
64    const boundary = formDataLines[0];
65    assert_equals(
66      formDataLines[formDataLines.length - 1],
67      boundary + "--",
68      `${fileBaseName}: multipart form data must end with ${boundary}--: ${
69        JSON.stringify(formDataText)
70      }`,
71    );
72
73    const asName = fileBaseName.replace(/[\r\n"]/g, encodeURIComponent);
74    const expectedText = [
75      boundary,
76      'Content-Disposition: form-data; name="filename"',
77      "",
78      fileBaseName,
79      boundary,
80      `Content-Disposition: form-data; name="${asName}"`,
81      "",
82      "filename",
83      boundary,
84      `Content-Disposition: form-data; name="file"; ` +
85      `filename="${asName}"`,
86      "Content-Type: text/plain",
87      "",
88      kTestChars,
89      boundary + "--",
90    ].join("\r\n");
91
92    assert_true(
93      formDataText.startsWith(expectedText),
94      `Unexpected multipart-shaped form data received:\n${formDataText}\nExpected:\n${expectedText}`,
95    );
96  }, `Upload ${fileBaseName} (${fileNameSource}) in fetch with FormData`);
97};
98