blob: 3635da73b75400ed3699576e2eac6329c39bba1b [file] [log] [blame]
"use strict";
// Different sizes of payloads to test.
var smallPayloadSize = 10;
var mediumPayloadSize = 10000;
var largePayloadSize = 50000;
var maxPayloadSize = 65536; // The maximum payload size allowed for a beacon request.
// String payloads of various sizes sent by sendbeacon. The format of the payloads is a string:
// <numberOfCharacters>:<numberOfCharacters *'s>
// ex. "10:**********"
var smallPayload = smallPayloadSize + ":" + Array(smallPayloadSize).fill('*').join("");
var mediumPayload = mediumPayloadSize + ":" + Array(mediumPayloadSize).fill('*').join("");
var largePayload = largePayloadSize + ":" + Array(largePayloadSize).fill('*').join("");
// Subtract 6 from maxPayloadSize because 65536 is 5 digits, plus 1 more for the ':'
var maxPayload = (maxPayloadSize - 6) + ":" + Array(maxPayloadSize - 6).fill('*').join("")
// Test case definitions.
// name: String containing the unique name of the test case.
// data: Payload object to send through sendbeacon.
var noDataTest = { name: "NoData" };
var nullDataTest = { name: "NullData", data: null };
var undefinedDataTest = { name: "UndefinedData", data: undefined };
var smallStringTest = { name: "SmallString", data: smallPayload };
var mediumStringTest = { name: "MediumString", data: mediumPayload };
var largeStringTest = { name: "LargeString", data: largePayload };
var maxStringTest = { name: "MaxString", data: maxPayload };
var emptyBlobTest = { name: "EmptyBlob", data: new Blob() };
var smallBlobTest = { name: "SmallBlob", data: new Blob([smallPayload]) };
var mediumBlobTest = { name: "MediumBlob", data: new Blob([mediumPayload]) };
var largeBlobTest = { name: "LargeBlob", data: new Blob([largePayload]) };
var maxBlobTest = { name: "MaxBlob", data: new Blob([maxPayload]) };
var emptyBufferSourceTest = { name: "EmptyBufferSource", data: new Uint8Array() };
var smallBufferSourceTest = { name: "SmallBufferSource", data: CreateArrayBufferFromPayload(smallPayload) };
var mediumBufferSourceTest = { name: "MediumBufferSource", data: CreateArrayBufferFromPayload(mediumPayload) };
var largeBufferSourceTest = { name: "LargeBufferSource", data: CreateArrayBufferFromPayload(largePayload) };
var maxBufferSourceTest = { name: "MaxBufferSource", data: CreateArrayBufferFromPayload(maxPayload) };
var emptyFormDataTest = { name: "EmptyFormData", data: CreateEmptyFormDataPayload() };
var smallFormDataTest = { name: "SmallFormData", data: CreateFormDataFromPayload(smallPayload) };
var mediumFormDataTest = { name: "MediumFormData", data: CreateFormDataFromPayload(mediumPayload) };
var largeFormDataTest = { name: "LargeFormData", data: CreateFormDataFromPayload(largePayload) };
var smallSafeContentTypeEncodedTest = { name: "SmallSafeContentTypeEncoded", data: new Blob([smallPayload], { type: 'application/x-www-form-urlencoded' }) };
var smallSafeContentTypeFormTest = { name: "SmallSafeContentTypeForm", data: new FormData() };
var smallSafeContentTypeTextTest = { name: "SmallSafeContentTypeText", data: new Blob([smallPayload], { type: 'text/plain' }) };
var smallCORSContentTypeTextTest = { name: "SmallCORSContentTypeText", data: new Blob([smallPayload], { type: 'text/html' }) };
// We don't test maxFormData because the extra multipart separators make it difficult to
// calculate a maxPayload.
// Test case suites.
// Due to quota limits we split the max payload tests into their own bucket.
var stringTests = [noDataTest, nullDataTest, undefinedDataTest, smallStringTest, mediumStringTest, largeStringTest];
var stringMaxTest = [maxStringTest];
var blobTests = [emptyBlobTest, smallBlobTest, mediumBlobTest, largeBlobTest];
var blobMaxTest = [maxBlobTest];
var bufferSourceTests = [emptyBufferSourceTest, smallBufferSourceTest, mediumBufferSourceTest, largeBufferSourceTest];
var bufferSourceMaxTest = [maxBufferSourceTest];
var formDataTests = [emptyFormDataTest, smallFormDataTest, mediumFormDataTest, largeFormDataTest];
var formDataMaxTest = [largeFormDataTest];
var contentTypeTests = [smallSafeContentTypeEncodedTest,smallSafeContentTypeFormTest,smallSafeContentTypeTextTest,smallCORSContentTypeTextTest];
var allTests = [].concat(stringTests, stringMaxTest, blobTests, blobMaxTest, bufferSourceTests, bufferSourceMaxTest, formDataTests, formDataMaxTest, contentTypeTests);
// This special cross section of test cases is meant to provide a slimmer but reasonably-
// representative set of tests for parameterization across variables (e.g. redirect codes,
// cors modes, etc.)
var sampleTests = [noDataTest, nullDataTest, undefinedDataTest, smallStringTest, smallBlobTest, smallBufferSourceTest, smallFormDataTest, smallSafeContentTypeEncodedTest, smallSafeContentTypeFormTest, smallSafeContentTypeTextTest];
var preflightTests = [smallCORSContentTypeTextTest];
// Helper function to create an ArrayBuffer representation of a string.
function CreateArrayBufferFromPayload(payload) {
var length = payload.length;
var buffer = new Uint8Array(length);
for (var i = 0; i < length; i++) {
buffer[i] = payload.charCodeAt(i);
}
return buffer;
}
// Helper function to create an empty FormData object.
function CreateEmptyFormDataPayload() {
if (self.document === undefined) {
return null;
}
return new FormData();
}
// Helper function to create a FormData representation of a string.
function CreateFormDataFromPayload(payload) {
if (self.document === undefined) {
return null;
}
var formData = new FormData();
formData.append("payload", payload);
return formData;
}
// Schedules async_test's for each of the test cases, treating them as a single session,
// and wires up the continueAfterSendingBeacon() and waitForResults() calls.
// Parameters:
// testCases: An array of test cases.
// suffix [optional]: A string used for the suffix for each test case name.
// buildUrl [optional]: A function that returns a beacon URL given an id.
// sendData [optional]: A function that sends the beacon with given a URL and payload.
function runTests(testCases, suffix = '', buildUrl = self.buildUrl, sendData = self.sendData) {
for (const testCase of testCases) {
const id = token();
async_test((test) => {
const url = buildUrl(id);
assert_true(sendData(url, testCase.data), 'sendBeacon should succeed');
waitForResult(id).then(() => test.done(), test.step_func((e) => {throw e;}));
}, `Verify 'navigator.sendbeacon()' successfully sends for variant: ${testCase.name}${suffix}`);
};
}
function buildUrl(id) {
const baseUrl = "http://{{host}}:{{ports[http][0]}}";
return `${baseUrl}/beacon/resources/beacon.py?cmd=store&id=${id}`;
}
// Sends the beacon for a single test. This step is factored into its own function so that
// it can be called from a web worker. It does not check for results.
// Note: do not assert from this method, as when called from a worker, we won't have the
// full testharness.js test context. Instead return 'false', and the main scope will fail
// the test.
// Returns the result of the 'sendbeacon()' function call, true or false.
function sendData(url, payload) {
return self.navigator.sendBeacon(url, payload);
}
// Poll the server for the test result.
async function waitForResult(id) {
const url = `resources/beacon.py?cmd=stat&id=${id}`;
for (let i = 0; i < 30; ++i) {
const response = await fetch(url);
const text = await response.text();
const results = JSON.parse(text);
if (results.length === 0) {
await new Promise(resolve => step_timeout(resolve, 100));
continue;
}
assert_equals(results.length, 1, `bad response: '${text}'`);;
// null JSON values parse as null, not undefined
assert_equals(results[0].error, null, "'sendbeacon' data must not fail validation");
return;
}
assert_true(false, 'timeout');
}
// Creates an iframe on the document's body and runs the sample tests from the iframe.
// The iframe is navigated immediately after it sends the data, and the window verifies
// that the data is still successfully sent.
function runSendInIframeAndNavigateTests() {
var iframe = document.createElement("iframe");
iframe.id = "iframe";
iframe.onload = function() {
// Clear our onload handler to prevent re-running the tests as we navigate away.
iframe.onload = null;
function sendData(url, payload) {
return iframe.contentWindow.navigator.sendBeacon(url, payload);
}
runTests(sampleTests, '-NAVIGATE', self.buildUrl, sendData);
// Now navigate ourselves.
iframe.contentWindow.location = "http://{{host}}:{{ports[http][0]}}/";
};
iframe.srcdoc = '<html></html>';
document.body.appendChild(iframe);
}