Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sm/fix-table-stack-error #432

Merged
merged 5 commits into from Jun 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions .mocharc.json
Expand Up @@ -9,5 +9,9 @@
"timeout": 60000,
"watch-extensions": [
"ts"
],
"watch-files": [
"src",
"test"
]
}
51 changes: 26 additions & 25 deletions src/cli-ux/styled/table.ts
Expand Up @@ -18,10 +18,10 @@ class Table<T extends Record<string, unknown>> {
// assign columns
this.columns = Object.keys(columns).map((key: string) => {
const col = columns[key]
const extended = col.extended || false
const get = col.get || ((row: any) => row[key])
const extended = col.extended ?? false
const get = col.get ?? ((row: any) => row[key])
const header = typeof col.header === 'string' ? col.header : capitalize(key.replace(/_/g, ' '))
const minWidth = Math.max(col.minWidth || 0, sw(header) + 1)
const minWidth = Math.max(col.minWidth ?? 0, sw(header) + 1)

return {
extended,
Expand All @@ -39,9 +39,9 @@ class Table<T extends Record<string, unknown>> {
output: csv ? 'csv' : output,
extended,
filter,
'no-header': options['no-header'] || false,
'no-truncate': options['no-truncate'] || false,
printLine: printLine || ((s: any) => process.stdout.write(s + '\n')),
'no-header': options['no-header'] ?? false,
'no-truncate': options['no-truncate'] ?? false,
printLine: printLine ?? ((s: any) => process.stdout.write(s + '\n')),
rowStart: ' ',
sort,
title,
Expand Down Expand Up @@ -144,7 +144,7 @@ class Table<T extends Record<string, unknown>> {
return columns.reduce((obj, col) => {
return {
...obj,
[col.key]: d[col.key] || '',
[col.key]: d[col.key] ?? '',
}
}, {})
})
Expand Down Expand Up @@ -174,27 +174,18 @@ class Table<T extends Record<string, unknown>> {

private outputTable() {
// tslint:disable-next-line:no-this-assignment
const {data, columns, options} = this

const {data, options} = this
// column truncation
//
// find max width for each column
for (const col of columns) {
// convert multi-line cell to single longest line
// for width calculations
const widthData = data.map((row: any) => {
const d = row[col.key]
const manyLines = d.split('\n')
if (manyLines.length > 1) {
return '*'.repeat(Math.max(...manyLines.map((r: string) => sw(r))))
}

return d
})
const widths = ['.'.padEnd(col.minWidth! - 1), col.header, ...widthData.map((row: any) => row)].map(r => sw(r))
col.maxWidth = Math.max(...widths) + 1
col.width = col.maxWidth!
}
const columns = this.columns.map(c => {
const maxWidth = Math.max(sw('.'.padEnd(c.minWidth! - 1)), sw(c.header), getWidestColumnWith(data, c.key)) + 1
return {
...c,
maxWidth,
width: maxWidth,
}
})

// terminal width
const maxWidth = stdtermwidth - 2
Expand Down Expand Up @@ -382,3 +373,13 @@ export namespace table {
printLine?(s: any): any;
}
}

const getWidestColumnWith = (data: any[], columnKey: string): number => {
return data.reduce((previous, current) => {
const d = current[columnKey]
// convert multi-line cell to single longest line
// for width calculations
const manyLines = (d as string).split('\n')
return Math.max(previous, manyLines.length > 1 ? Math.max(...manyLines.map((r: string) => sw(r))) : sw(d))
}, 0)
}
34 changes: 34 additions & 0 deletions test/cli-ux/styled/table.e2e.ts
@@ -0,0 +1,34 @@
import {expect, fancy} from 'fancy-test'
import {CliUx} from '../../../src'

describe('styled/table', () => {
describe('scale tests', () => {
const bigRows = 150_000
fancy
.stdout()
.end('very tall tables don\'t exceed stack depth', output => {
const data = Array.from({length: bigRows}).fill({id: '123', name: 'foo', value: 'bar'}) as Record<string, unknown>[]
const tallColumns = {
id: {header: 'ID'},
name: {},
value: {header: 'TEST'},
}

CliUx.ux.table(data, tallColumns)
expect(output.stdout).to.include('ID')
})

fancy
.stdout()
.end('very tall, wide tables don\'t exceed stack depth', output => {
const columns = 100
const row = Object.fromEntries(Array.from({length: columns}).map((_, i) => [`col${i}`, 'foo']))
const data = Array.from({length: bigRows}).fill(row) as Record<string, unknown>[]
const bigColumns = Object.fromEntries(Array.from({length: columns}).map((_, i) => [`col${i}`, {header: `col${i}`.toUpperCase()}]))

CliUx.ux.table(data, bigColumns)
expect(output.stdout).to.include('COL1')
})
})
})

1 change: 1 addition & 0 deletions test/cli-ux/styled/table.test.ts
Expand Up @@ -319,3 +319,4 @@ describe('styled/table', () => {
})
})
})

2 changes: 1 addition & 1 deletion test/integration/plugins.e2e.ts
Expand Up @@ -6,7 +6,7 @@ describe('oclif plugins', () => {
let executor: Executor
before(async () => {
executor = await setup(__filename, {
repo: 'git@github.com:oclif/hello-world.git',
repo: 'https://github.com/oclif/hello-world',
plugins: [
'@oclif/plugin-autocomplete',
'@oclif/plugin-commands',
Expand Down
2 changes: 1 addition & 1 deletion test/integration/sf.e2e.ts
Expand Up @@ -15,7 +15,7 @@ describe('Salesforce CLI (sf)', () => {
let executor: Executor
before(async () => {
process.env.SFDX_TELEMETRY_DISABLE_ACKNOWLEDGEMENT = 'true'
executor = await setup(__filename, {repo: 'git@github.com:salesforcecli/cli.git'})
executor = await setup(__filename, {repo: 'https://github.com/salesforcecli/cli'})
})

it('should show custom help', async () => {
Expand Down