Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

/* Any copyright is dedicated to the Public Domain.
"use strict";
// Check console.table calls with all the test cases shown
const TEST_URI =
"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);
}
}