summaryrefslogtreecommitdiffstats
path: root/devtools/client/webconsole/test/browser/browser_webconsole_console_table.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/webconsole/test/browser/browser_webconsole_console_table.js')
-rw-r--r--devtools/client/webconsole/test/browser/browser_webconsole_console_table.js504
1 files changed, 504 insertions, 0 deletions
diff --git a/devtools/client/webconsole/test/browser/browser_webconsole_console_table.js b/devtools/client/webconsole/test/browser/browser_webconsole_console_table.js
new file mode 100644
index 0000000000..c64381520b
--- /dev/null
+++ b/devtools/client/webconsole/test/browser/browser_webconsole_console_table.js
@@ -0,0 +1,504 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Check console.table calls with all the test cases shown
+// in the MDN doc (https://developer.mozilla.org/en-US/docs/Web/API/Console/table)
+
+const TEST_URI =
+ "http://example.com/browser/devtools/client/webconsole/" +
+ "test/browser/test-console-table.html";
+
+add_task(async function () {
+ const hud = await openNewTabAndConsole(TEST_URI);
+
+ function Person(firstName, lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+
+ const holeyArray = [];
+ holeyArray[1] = "apples";
+ holeyArray[3] = "oranges";
+ holeyArray[6] = "bananas";
+
+ const testCases = [
+ {
+ info: "Testing when data argument is an array",
+ input: ["apples", "oranges", "bananas"],
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "apples"],
+ ["1", "oranges"],
+ ["2", "bananas"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is an holey array",
+ input: holeyArray,
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", ""],
+ ["1", "apples"],
+ ["2", ""],
+ ["3", "oranges"],
+ ["4", ""],
+ ["5", ""],
+ ["6", "bananas"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument has holey array",
+ // eslint-disable-next-line no-sparse-arrays
+ input: [[1, , 2]],
+ expected: {
+ columns: ["(index)", "0", "1", "2"],
+ rows: [["0", "1", "", "2"]],
+ },
+ },
+ {
+ info: "Testing when data argument is an object",
+ input: new Person("John", "Smith"),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["firstName", "John"],
+ ["lastName", "Smith"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is an array of arrays",
+ input: [
+ ["Jane", "Doe"],
+ ["Emily", "Jones"],
+ ],
+ expected: {
+ columns: ["(index)", "0", "1"],
+ rows: [
+ ["0", "Jane", "Doe"],
+ ["1", "Emily", "Jones"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is an array of objects",
+ input: [
+ new Person("Jack", "Foo"),
+ new Person("Emma", "Bar"),
+ new Person("Michelle", "Rax"),
+ ],
+ expected: {
+ columns: ["(index)", "firstName", "lastName"],
+ rows: [
+ ["0", "Jack", "Foo"],
+ ["1", "Emma", "Bar"],
+ ["2", "Michelle", "Rax"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is an object whose properties are objects",
+ input: {
+ father: new Person("Darth", "Vader"),
+ daughter: new Person("Leia", "Organa"),
+ son: new Person("Luke", "Skywalker"),
+ },
+ expected: {
+ columns: ["(index)", "firstName", "lastName"],
+ rows: [
+ ["father", "Darth", "Vader"],
+ ["daughter", "Leia", "Organa"],
+ ["son", "Luke", "Skywalker"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Set",
+ input: new Set(["a", "b", "c"]),
+ expected: {
+ columns: ["(iteration index)", "Values"],
+ rows: [
+ ["0", "a"],
+ ["1", "b"],
+ ["2", "c"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Map",
+ input: new Map([
+ ["key-a", "value-a"],
+ ["key-b", "value-b"],
+ ]),
+ expected: {
+ columns: ["(iteration index)", "Key", "Values"],
+ rows: [
+ ["0", "key-a", "value-a"],
+ ["1", "key-b", "value-b"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Int8Array",
+ input: new Int8Array([1, 2, 3, 4]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1"],
+ ["1", "2"],
+ ["2", "3"],
+ ["3", "4"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Uint8Array",
+ input: new Uint8Array([1, 2, 3, 4]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1"],
+ ["1", "2"],
+ ["2", "3"],
+ ["3", "4"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Int16Array",
+ input: new Int16Array([1, 2, 3, 4]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1"],
+ ["1", "2"],
+ ["2", "3"],
+ ["3", "4"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Uint16Array",
+ input: new Uint16Array([1, 2, 3, 4]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1"],
+ ["1", "2"],
+ ["2", "3"],
+ ["3", "4"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Int32Array",
+ input: new Int32Array([1, 2, 3, 4]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1"],
+ ["1", "2"],
+ ["2", "3"],
+ ["3", "4"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Uint32Array",
+ input: new Uint32Array([1, 2, 3, 4]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1"],
+ ["1", "2"],
+ ["2", "3"],
+ ["3", "4"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Float32Array",
+ input: new Float32Array([1, 2, 3, 4]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1"],
+ ["1", "2"],
+ ["2", "3"],
+ ["3", "4"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Float64Array",
+ input: new Float64Array([1, 2, 3, 4]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1"],
+ ["1", "2"],
+ ["2", "3"],
+ ["3", "4"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a Uint8ClampedArray",
+ input: new Uint8ClampedArray([1, 2, 3, 4]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1"],
+ ["1", "2"],
+ ["2", "3"],
+ ["3", "4"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a BigInt64Array",
+ // eslint-disable-next-line no-undef
+ input: new BigInt64Array([1n, 2n, 3n, 4n]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1n"],
+ ["1", "2n"],
+ ["2", "3n"],
+ ["3", "4n"],
+ ],
+ },
+ },
+ {
+ info: "Testing when data argument is a BigUint64Array",
+ // eslint-disable-next-line no-undef
+ input: new BigUint64Array([1n, 2n, 3n, 4n]),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "1n"],
+ ["1", "2n"],
+ ["2", "3n"],
+ ["3", "4n"],
+ ],
+ },
+ },
+ {
+ info: "Testing restricting the columns displayed",
+ input: [new Person("Sam", "Wright"), new Person("Elena", "Bartz")],
+ headers: ["firstName"],
+ expected: {
+ columns: ["(index)", "firstName"],
+ rows: [
+ ["0", "Sam"],
+ ["1", "Elena"],
+ ],
+ },
+ },
+ {
+ info: "Testing nested object with falsy values",
+ input: [
+ { a: null, b: false, c: undefined, d: 0 },
+ { b: null, c: false, d: undefined, e: 0 },
+ ],
+ expected: {
+ columns: ["(index)", "a", "b", "c", "d", "e"],
+ rows: [
+ ["0", "null", "false", "undefined", "0", ""],
+ ["1", "", "null", "false", "undefined", "0"],
+ ],
+ },
+ },
+ {
+ info: "Testing invalid headers",
+ input: ["apples", "oranges", "bananas"],
+ headers: [[]],
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: [
+ ["0", "apples"],
+ ["1", "oranges"],
+ ["2", "bananas"],
+ ],
+ },
+ },
+ {
+ info: "Testing overflow-y",
+ input: Array.from({ length: 50 }, (_, i) => `item-${i}`),
+ expected: {
+ columns: ["(index)", "Values"],
+ rows: Array.from({ length: 50 }, (_, i) => [i.toString(), `item-${i}`]),
+ overflow: true,
+ },
+ },
+ {
+ info: "Testing table with expandable objects",
+ input: [{ a: { b: 34 } }],
+ expected: {
+ columns: ["(index)", "a"],
+ rows: [["0", "Object { b: 34 }"]],
+ },
+ async additionalTest(node) {
+ info("Check that object in a cell can be expanded");
+ const objectNode = node.querySelector(".tree .node");
+ objectNode.click();
+ await waitFor(() => node.querySelectorAll(".tree .node").length === 3);
+ const nodes = node.querySelectorAll(".tree .node");
+ ok(nodes[1].textContent.includes("b: 34"));
+ ok(nodes[2].textContent.includes("<prototype>"));
+ },
+ },
+ {
+ info: "Testing max columns",
+ input: [
+ Array.from({ length: 30 }).reduce((acc, _, i) => {
+ return {
+ ...acc,
+ ["item" + i]: i,
+ };
+ }, {}),
+ ],
+ expected: {
+ // We show 21 columns at most
+ columns: [
+ "(index)",
+ ...Array.from({ length: 20 }, (_, i) => `item${i}`),
+ ],
+ rows: [[0, ...Array.from({ length: 20 }, (_, i) => i)]],
+ },
+ },
+ {
+ info: "Testing performance entries",
+ input: "PERFORMANCE_ENTRIES",
+ headers: [
+ "name",
+ "entryType",
+ "initiatorType",
+ "connectStart",
+ "connectEnd",
+ "fetchStart",
+ ],
+ expected: {
+ columns: [
+ "(index)",
+ "initiatorType",
+ "fetchStart",
+ "connectStart",
+ "connectEnd",
+ "name",
+ "entryType",
+ ],
+ rows: [[0, "navigation", /\d+/, /\d+/, /\d+/, TEST_URI, "navigation"]],
+ },
+ },
+ ];
+
+ await SpecialPowers.spawn(
+ gBrowser.selectedBrowser,
+ [testCases.map(({ input, headers }) => ({ input, headers }))],
+ function (tests) {
+ tests.forEach(test => {
+ let { input, headers } = test;
+ if (input === "PERFORMANCE_ENTRIES") {
+ input =
+ content.wrappedJSObject.performance.getEntriesByType("navigation");
+ }
+ content.wrappedJSObject.doConsoleTable(input, headers);
+ });
+ }
+ );
+ const messages = await waitFor(async () => {
+ const msgs = await findAllMessagesVirtualized(hud);
+ if (msgs.length === testCases.length) {
+ return msgs;
+ }
+ return null;
+ });
+ for (const [index, testCase] of testCases.entries()) {
+ // Refresh the reference to the message, as it may have been scrolled out of existence.
+ const node = await findMessageVirtualizedById({
+ hud,
+ messageId: messages[index].getAttribute("data-message-id"),
+ });
+ await testItem(testCase, node.querySelector(".consoletable"));
+ }
+});
+
+async function testItem(testCase, tableNode) {
+ info(testCase.info);
+
+ const ths = Array.from(tableNode.querySelectorAll("th"));
+ const trs = Array.from(tableNode.querySelectorAll("tbody tr"));
+
+ is(
+ JSON.stringify(ths.map(column => column.textContent)),
+ JSON.stringify(testCase.expected.columns),
+ `${testCase.info} | table has the expected columns`
+ );
+
+ is(
+ trs.length,
+ testCase.expected.rows.length,
+ `${testCase.info} | table has the expected number of rows`
+ );
+
+ testCase.expected.rows.forEach((expectedRow, rowIndex) => {
+ const rowCells = Array.from(trs[rowIndex].querySelectorAll("td")).map(
+ x => x.textContent
+ );
+
+ const isRegex = x => x && x.constructor.name === "RegExp";
+ const hasRegExp = expectedRow.find(isRegex);
+ if (hasRegExp) {
+ is(
+ rowCells.length,
+ expectedRow.length,
+ `${testCase.info} | row ${rowIndex} has the expected number of cell`
+ );
+ rowCells.forEach((cell, i) => {
+ const expected = expectedRow[i];
+ const info = `${testCase.info} | row ${rowIndex} cell ${i} has the expected content`;
+
+ if (isRegex(expected)) {
+ ok(expected.test(cell), info);
+ } else {
+ is(cell, `${expected}`, info);
+ }
+ });
+ } else {
+ is(
+ rowCells.join(" | "),
+ expectedRow.join(" | "),
+ `${testCase.info} | row has the expected content`
+ );
+ }
+ });
+
+ if (testCase.expected.overflow) {
+ ok(
+ tableNode.isConnected,
+ "Node must be connected to test overflow. It is likely scrolled out of view."
+ );
+ const tableWrapperNode = tableNode.closest(".consoletable-wrapper");
+ Assert.greater(
+ tableWrapperNode.scrollHeight,
+ tableWrapperNode.clientHeight,
+ testCase.info + " table overflows"
+ );
+ Assert.notStrictEqual(
+ getComputedStyle(tableWrapperNode).overflowY,
+ "hidden",
+ "table can be scrolled"
+ );
+ }
+
+ if (typeof testCase.additionalTest === "function") {
+ await testCase.additionalTest(tableNode);
+ }
+}