diff --git a/packages/polyfills/__tests__/console-itest.js b/packages/polyfills/__tests__/console-itest.js index 069b9fcbd830b7..a02e8fe520e568 100644 --- a/packages/polyfills/__tests__/console-itest.js +++ b/packages/polyfills/__tests__/console-itest.js @@ -18,7 +18,7 @@ const LOG_LEVELS = { describe('console', () => { describe('.table(data, rows)', () => { - it('should print the passed array as a table', () => { + it('should print the passed array as a Markdown table', () => { const originalNativeLoggingHook = global.nativeLoggingHook; const logFn = (global.nativeLoggingHook = jest.fn()); @@ -34,12 +34,12 @@ describe('console', () => { expect(logFn).toHaveBeenCalledTimes(1); expect(logFn.mock.lastCall).toEqual([ ` -name | value --------|------ -First | 500 \u0020 -Second | 600 \u0020 -Third | 700 \u0020 -Fourth | 800 `, +| (index) | name | value | extraValue | +| ------- | -------- | ----- | ---------- | +| 0 | 'First' | 500 | | +| 1 | 'Second' | 600 | | +| 2 | 'Third' | 700 | | +| 3 | 'Fourth' | 800 | true |`, LOG_LEVELS.info, ]); } finally { @@ -47,7 +47,7 @@ Fourth | 800 `, } }); - it('should print the passed dictionary as a table', () => { + it('should print the passed dictionary as a Markdown table', () => { const originalNativeLoggingHook = global.nativeLoggingHook; const logFn = (global.nativeLoggingHook = jest.fn()); @@ -63,12 +63,102 @@ Fourth | 800 `, expect(logFn).toHaveBeenCalledTimes(1); expect(logFn.mock.lastCall).toEqual([ ` -(index) | name | value ---------|--------|------ -first | First | 500 \u0020 -second | Second | 600 \u0020 -third | Third | 700 \u0020 -fourth | Fourth | 800 `, +| (index) | name | value | extraValue | +| ------- | -------- | ----- | ---------- | +| first | 'First' | 500 | | +| second | 'Second' | 600 | | +| third | 'Third' | 700 | | +| fourth | 'Fourth' | 800 | true |`, + LOG_LEVELS.info, + ]); + } finally { + global.nativeLoggingHook = originalNativeLoggingHook; + } + }); + + it('should work with different types of values', () => { + const originalNativeLoggingHook = global.nativeLoggingHook; + const logFn = (global.nativeLoggingHook = jest.fn()); + + // TODO: replace with `beforeEach` when supported. + try { + console.table([ + { + string: '', + number: 0, + boolean: true, + function: () => {}, + object: {a: 1, b: 2}, + null: null, + undefined: undefined, + }, + { + string: 'a', + number: 1, + boolean: true, + function: () => {}, + object: {a: 1, b: 2}, + null: null, + undefined: undefined, + }, + { + string: 'aa', + number: 2, + boolean: false, + function: () => {}, + object: {a: 1, b: 2}, + null: null, + undefined: undefined, + }, + { + string: 'aaa', + number: 3, + boolean: false, + function: () => {}, + object: {a: 1, b: 2}, + null: null, + undefined: undefined, + }, + ]); + + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` +| (index) | string | number | boolean | function | object | null | undefined | +| ------- | ------ | ------ | ------- | -------- | ------ | ---- | --------- | +| 0 | '' | 0 | true | ƒ | {…} | null | undefined | +| 1 | 'a' | 1 | true | ƒ | {…} | null | undefined | +| 2 | 'aa' | 2 | false | ƒ | {…} | null | undefined | +| 3 | 'aaa' | 3 | false | ƒ | {…} | null | undefined |`, + LOG_LEVELS.info, + ]); + } finally { + global.nativeLoggingHook = originalNativeLoggingHook; + } + }); + + it('should print the keys in all the objects', () => { + const originalNativeLoggingHook = global.nativeLoggingHook; + const logFn = (global.nativeLoggingHook = jest.fn()); + + // TODO: replace with `beforeEach` when supported. + try { + console.table([ + {name: 'foo'}, + {name: 'bar', value: 1}, + {value: 2, surname: 'baz'}, + {address: 'other'}, + ]); + + expect(logFn).toHaveBeenCalledTimes(1); + expect(logFn.mock.lastCall).toEqual([ + ` +| (index) | name | value | surname | address | +| ------- | ----- | ----- | ------- | ------- | +| 0 | 'foo' | | | | +| 1 | 'bar' | 1 | | | +| 2 | | 2 | 'baz' | | +| 3 | | | | 'other' |`, LOG_LEVELS.info, ]); } finally { @@ -107,7 +197,7 @@ fourth | Fourth | 800 `, }); // This test is currently failing - it.skip('should print an indices table for an array of empty objects', () => { + it('should print an indices table for an array of empty objects', () => { const originalNativeLoggingHook = global.nativeLoggingHook; const logFn = (global.nativeLoggingHook = jest.fn()); @@ -118,12 +208,12 @@ fourth | Fourth | 800 `, expect(logFn).toHaveBeenCalledTimes(1); expect(logFn.mock.lastCall).toEqual([ ` -(index) -------- -0 \u0020 -1 \u0020 -2 \u0020 -3 `, +| (index) | +| ------- | +| 0 | +| 1 | +| 2 | +| 3 |`, LOG_LEVELS.info, ]); } finally { @@ -147,12 +237,12 @@ fourth | Fourth | 800 `, expect(logFn).toHaveBeenCalledTimes(1); expect(logFn.mock.lastCall).toEqual([ ` -(index) -------- -first \u0020 -second\u0020 -third \u0020 -fourth `, +| (index) | +| ------- | +| first | +| second | +| third | +| fourth |`, LOG_LEVELS.info, ]); } finally { diff --git a/packages/polyfills/console.js b/packages/polyfills/console.js index d459eab17ae8bc..f397228ee92bd2 100644 --- a/packages/polyfills/console.js +++ b/packages/polyfills/console.js @@ -380,7 +380,7 @@ const inspect = (function () { return inspect; })(); -const OBJECT_COLUMN_NAME = '(index)'; +const INDEX_COLUMN_NAME = '(index)'; const LOG_LEVELS = { trace: 0, info: 1, @@ -433,16 +433,46 @@ function repeat(element, n) { }); } +function formatCellValue(cell, key) { + if (key === INDEX_COLUMN_NAME) { + return cell[key]; + } + + if (cell.hasOwnProperty(key)) { + var cellValue = cell[key]; + + switch (typeof cellValue) { + case 'function': + return 'ƒ'; + case 'string': + return "'" + cellValue + "'"; + case 'object': + return cellValue == null ? 'null' : '{…}'; + } + + return String(cellValue); + } + return ''; +} + function consoleTablePolyfill(rows) { // convert object -> array - if (!Array.isArray(rows)) { + if (Array.isArray(rows)) { + rows = rows.map((row, index) => { + var processedRow = {}; + processedRow[INDEX_COLUMN_NAME] = String(index); + Object.assign(processedRow, row); + return processedRow; + }); + } else { var data = rows; rows = []; for (var key in data) { if (data.hasOwnProperty(key)) { - var row = Object.assign({}, data[key]); - row[OBJECT_COLUMN_NAME] = key; - rows.push(row); + var processedRow = {}; + processedRow[INDEX_COLUMN_NAME] = key; + Object.assign(processedRow, data[key]); + rows.push(processedRow); } } } @@ -451,7 +481,12 @@ function consoleTablePolyfill(rows) { return; } - var columns = Object.keys(rows[0]).sort(); + var columns = Array.from( + rows.reduce((columnSet, row) => { + Object.keys(row).forEach(key => columnSet.add(key)); + return columnSet; + }, new Set()), + ); var stringRows = []; var columnWidths = []; @@ -460,7 +495,7 @@ function consoleTablePolyfill(rows) { columns.forEach(function (k, i) { columnWidths[i] = k.length; for (var j = 0; j < rows.length; j++) { - var cellStr = (rows[j][k] || '?').toString(); + var cellStr = formatCellValue(rows[j], k); stringRows[j] = stringRows[j] || []; stringRows[j][i] = cellStr; columnWidths[i] = Math.max(columnWidths[i], cellStr.length); @@ -475,13 +510,13 @@ function consoleTablePolyfill(rows) { return cell + extraSpaces; }); space = space || ' '; - return cells.join(space + '|' + space); + return '| ' + cells.join(space + '|' + space) + ' |'; } var separators = columnWidths.map(function (columnWidth) { return repeat('-', columnWidth).join(''); }); - var separatorRow = joinRow(separators, '-'); + var separatorRow = joinRow(separators); var header = joinRow(columns); var table = [header, separatorRow];