import { spawnSync } from "bun"; import { describe, expect, test } from "bun:test"; import { bunEnv, bunExe } from "harness"; describe("console.table", () => { test("throws when second arg is invalid", () => { expect(() => console.table({})).not.toThrow(); expect(() => console.table({}, [])).not.toThrow(); // @ts-expect-error expect(() => console.table({}, "invalid")).toThrow(); }); test.each([ [ "not object (number)", { args: () => [42], }, ], [ "not object (string)", { args: () => ["bun"], }, ], [ "object - empty", { args: () => [{}], }, ], [ "object", { args: () => [{ a: 42, b: "bun" }], }, ], [ "array - empty", { args: () => [[]], }, ], [ "array - plain", { args: () => [[42, "bun"]], }, ], [ "array - object", { args: () => [[{ a: 42, b: "bun" }]], }, ], [ "array - objects with diff props", { args: () => [[{ b: "bun" }, { a: 42 }]], }, ], [ "array - mixed", { args: () => [[{ a: 42, b: "bun" }, 42]], }, ], [ "set", { args: () => [new Set([42, "bun"])], }, ], [ "map", { args: () => [ new Map([ ["a", 42], ["b", "bun"], [42, "c"], ]), ], }, ], [ "properties", { args: () => [[{ a: 42, b: "bun" }], ["b", "c", "a"]], }, ], [ "properties - empty", { args: () => [[{ a: 42, b: "bun" }], []], }, ], [ "properties - interesting character", { args: () => [ { a: "_字", }, ], }, ], [ "values - array", { args: () => [ [ { value: { a: 42, b: "bun" } }, { value: [42, "bun"] }, { value: new Set([42, "bun"]) }, { value: new Map([ [42, "bun"], ["bun", 42], ]), }, ], ], }, ], [ "headers object", { args: () => [ new Headers([ ["abc", "bun"], ["potato", "tomato"], ]), ], }, ], [ "number keys", { args: () => [{ test: { "10": 123, "100": 154 } }], }, ], ])("expected output for: %s", (label, { args }) => { const { stdout } = spawnSync({ cmd: [bunExe(), `${import.meta.dir}/console-table-run.ts`, args.toString()], stdout: "pipe", stderr: "inherit", env: bunEnv, }); const actualOutput = stdout.toString(); expect(actualOutput).toMatchSnapshot(); console.log(actualOutput); }); }); test("console.table json fixture", () => { const { stdout } = spawnSync({ cmd: [ bunExe(), `${import.meta.dir}/console-table-run.ts`, `(() => [${JSON.stringify(require("./console-table-json-fixture.json"), null, 2)}])`, ], stdout: "pipe", stderr: "inherit", env: bunEnv, }); const actualOutput = stdout .toString() // todo: fix bug causing this to be necessary: .replaceAll("`", "'"); expect(actualOutput).toMatchSnapshot(); console.log(actualOutput); }); test("console.table ansi colors", () => { const obj = { [ansify("hello")]: ansify("this is a long string with ansi color codes"), [ansify("world")]: ansify("this is another long string with ansi color"), [ansify("foo")]: ansify("bar"), }; function ansify(str: string) { return `\u001b[31m${str}\u001b[39m`; } const { stdout } = spawnSync({ cmd: [bunExe(), `${import.meta.dir}/console-table-run.ts`, `(() => [${JSON.stringify(obj, null, 2)}])`], stdout: "pipe", stderr: "inherit", env: bunEnv, }); const actualOutput = stdout .toString() // todo: fix bug causing this to be necessary: .replaceAll("`", "'"); expect(actualOutput).toMatchSnapshot(); console.log(actualOutput); }); test.skip("console.table character widths", () => { // note: this test cannot be automated because cannot test printed witdhs consistently. // so this test is just meant to be run manually // top ~2000 most used unicode codepoints const str = `~!@#$%^&*()_-+={[}]|:;"'<,>.?/¡¢£¤¥¦§¨©ª«¬ ®¯°±²³´µ¶·¸ʻ¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖרÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿŁłŃńŅņŇňŊŋŌōŎŏŐőŒœŔŕŖŗŘřŚśŜŝŞşŠšŢţŤťŦŧŨũŪūŬŭŮůŰűŴŵŶŷŸŹźŻżŽžſƆƎƜɐɑɒɔɘəɛɜɞɟɡɢɣɤɥɨɪɬɮɯɰɴɵɶɷɸɹʁʇʌʍʎʞΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρςστυφχψωАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюяᴀᴁᴂᴃᴄᴅᴆᴇᴈᴉᴊᴋᴌᴍᴎᴏᴐᴑᴒᴓᴔᴕᴖᴗᴘᴙᴚᴛᴜᴝᴞᴟᴠᴡᴢᴣᴤᴥᴦᴧᴨᴩᴪẞỲỳỴỵỸỹ‐‑‒–—―‖‗‘’‚‛“”„‟†‡•‣․‥…‧‰‱′″‴‵‶‷‸‹›※‼‽‾‿⁀⁁⁂⁃⁄⁅⁆⁇⁈⁉⁊⁋⁌⁍⁎⁏⁐⁑⁒⁓⁔⁕⁗⁰ⁱ⁴⁵⁶⁷⁸⁹⁺⁻⁼⁽⁾ⁿ₀₁₂₃₄₅₆₇₈₉₊₋₌₍₎₠₡₢₣₤₥₦₧₨₩₪₫€₭₮₯₰₱₲₳₴₵₶₷₸₹℀℁ℂ℃℄℅℆ℇ℈℉ℊℋℌℍℎℏℐℑℒℓ℔ℕ№℗℘ℙℚℛℜℝ℞℟℠℡™℣ℤ℥Ω℧ℨ℩Åℬℭ℮ℯℰℱℲℳℴℵℶℷℸ⅁⅂⅃⅄ⅅⅆⅇⅈⅉ⅋ⅎ⅐⅑⅒⅓⅔⅕⅖⅗⅘⅙⅚⅛⅜⅝⅞⅟ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫⅬⅭⅮⅯⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹⅺⅻⅼⅽⅾⅿↄ←↑→↓↔↕↖↗↘↙↚↛↜↝↞↟↠↡↢↣↤↥↦↧↨↩↪↫↬↭↮↯↰↱↲↳↴↵↶↷↸↹↺↻↼↽↾↿⇀⇁⇂⇃⇄⇅⇆⇇⇈⇉⇊⇋⇌⇍⇎⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇚⇛⇜⇝⇞⇟⇠⇡⇢⇣⇤⇥⇦⇧⇨⇩⇪⇫⇬⇭⇮⇯⇰⇱⇲⇳⇴⇵⇶⇷⇸⇹⇺⇻⇼⇽⇾⇿∀∁∂∃∄∅∆∇∈∉∊∋∌∍∎∏∐∑−∓∔∕∖∗∘∙√∛∜∝∞∟∠∡∢∣∤∥∦∧∨∩∪∫∬∭∮∯∰∱∲∳∴∵∶∷∸∹∺∻∼∽∾∿≀≁≂≃≄≅≆≇≈≉≊≋≌≍≎≏≐≑≒≓≔≕≖≗≘≙≚≛≜≝≞≟≠≡≢≣≤≥≦≧≨≩≪≫≬≭≮≯≰≱≲≳≴≵≶≷≸≹≺≻≼≽≾≿⊀⊁⊂⊃⊄⊅⊆⊇⊈⊉⊊⊋⊌⊍⊎⊏⊐⊑⊒⊓⊔⊕⊖⊗⊘⊙⊚⊛⊜⊝⊞⊟⊠⊡⊢⊣⊤⊥⊦⊧⊨⊩⊪⊫⊬⊭⊮⊯⊰⊱⊲⊳⊴⊵⊶⊷⊸⊹⊺⊻⊼⊽⊾⊿⋀⋁⋂⋃⋄⋅⋆⋇⋈⋉⋊⋋⋌⋍⋎⋏⋐⋑⋒⋓⋔⋕⋖⋗⋘⋙⋚⋛⋜⋝⋞⋟⋠⋡⋢⋣⋤⋥⋦⋧⋨⋩⋪⋫⋬⋭⋮⋯⋰⋱⌀⌁⌂⌃⌄⌅⌆⌇⌈⌉⌊⌋⌐⌑⌒⌓⌔⌕⌖⌗⌘⌙⌚⌛⌠⌡⌢⌣⌤⌥⌦⌧⌨⌫⌬⎛⎜⎝⎞⎟⎠⎡⎢⎣⎤⎥⎦⎧⎨⎩⎪⎫⎬⎭⏎⏏⏚⏛⏰⏱⏲⏳␢␣─━│┃┄┅┆┇┈┉┊┋┌┍┎┏┐┑┒┓└┕┖┗┘┙┚┛├┝┞┟┠┡┢┣┤┥┦┧┨┩┪┫┬┭┮┯┰┱┲┳┴┵┶┷┸┹┺┻┼┽┾┿╀╁╂╃╄╅╆╇╈╉╊╋╌╍╎╏═║╒╓╔╕╖╗╘╙╚╛╜╝╞╟╠╡╢╣╤╥╦╧╨╩╪╫╬╭╮╯╰╱╲╳╴╵╶╷╸╹╺╻╼╽╾╿▀▁▂▃▄▅▆▇█▉▊▋▌▍▎▏▐░▒▓▔▕▖▗▘▙▚▛▜▝▞▟■□▢▣▤▥▦▧▨▩▪▫▬▭▮▯▰▱▲△▴▵▶▷▸▹►▻▼▽▾▿◀◁◂◃◄◅◆◇◈◉◊○◌◍◎●◐◑◒◓◔◕◖◗◘◙◚◛◜◝◞◟◠◡◢◣◤◥◦◧◨◩◪◫◬◭◮◯◰◱◲◳◴◵◶◷◸◹◺◻◼◽◾◿☀☁☂☃☄★☆☇☈☉☊☋☌☍☎☏☐☑☒☓☔☕☖☗☘☙☚☛☜☝☞☟☠☡☢☣☤☥☦☧☨☩☪☫☬☭☮☯☰☱☲☳☴☵☶☷☸☹☺☻☼☽☾☿♀♁♂♃♄♅♆♇♈♉♊♋♌♍♎♏♐♑♒♓♔♕♖♗♘♙♚♛♜♝♞♟♠♡♢♣♤♥♦♧♨♩♪♫♬♭♮♯♲♳♴♵♶♷♸♹♺♻♼♽♾♿⚀⚁⚂⚃⚄⚅⚐⚑⚒⚓⚔⚕⚖⚗⚘⚙⚚⚛⚜⚝⚞⚟⚠⚡⚢⚣⚤⚥⚦⚧⚨⚩⚪⚫⚬⚭⚮⚯⚰⚱⚲⚳⚴⚵⚶⚷⚸⚹⚺⚻⚼⛀⛁⛂⛃⛢⛤⛥⛦⛧⛨⛩⛪⛫⛬⛭⛮⛯⛰⛱⛲⛳⛴⛵⛶⛷⛸⛹⛺⛻⛼⛽⛾⛿✁✂✃✄✅✆✇✈✉✊✋✌✍✎✏✐✑✒✓✔✕✖✗✘✙✚✛✜✝✞✟✠✡✢✣✤✥✦✧✨✩✪✫✬✭✮✯✰✱✲✳✴✵✶✷✸✹✺✻✼✽✾✿❀❁❂❃❄❅❆❇❈❉❊❋❌❍❎❏❐❑❒❓❔❕❖❗❘❙❚❛❜❝❞❟❠❡❢❣❤❥❦❧➔➘➙➚➛➜➝➞➟➠➡➢➣➤➥➦➧➨➩➪➫➬➭➮➯➱➲➳➴➵➶➷➸➹➺➻➼➽➾⟰⟱⟲⟳⟴⟵⟶⟷⟸⟹⟺⟻⟼⟽⟾⟿⤀⤁⤂⤃⤄⤅⤆⤇⤈⤉⤊⤋⤌⤍⤎⤏⤐⤑⤒⤓⤔⤕⤖⤗⤘⤙⤚⤛⤜⤝⤞⤟⤠⤡⤢⤣⤤⤥⤦⤧⤨⤩⤪⤫⤬⤭⤮⤯⤰⤱⤲⤳⤴⤵⤶⤷⤸⤹⤺⤻⤼⤽⤾⤿⥀⥁⥂⥃⥄⥅⥆⥇⥈⥉⥊⥋⥌⥍⥎⥏⥐⥑⬀⬁⬂⬃⬄⬅⬆⬇⬈⬉⬊⬋⬌⬍⬎⬏⬐⬑⬒⬓⬔⬕⬖⬗⬘⬙⬚ⱠⱡⱣⱥⱦⱭⱯⱰ⸢⸣⸤⸥⸮〃〄fffiflffifflſtst﴾﴿﷼︐︑︒︓︔︕︖︗︘︙︰︱︲︳︴︵︶︷︸︹︺︻︼︽︾︿﹀﹁﹂﹃﹄﹅﹆﹉﹊﹋﹌﹍﹎﹏﹐﹑﹒﹔﹕﹖﹗﹘﹙﹚﹛﹜﹝﹞﹟﹠﹡﹢﹣﹤﹥﹦﹨﹩﹪﹫\ufeff!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~⦅⦆¢£¬ ̄¦¥₩�`; const { stdout } = spawnSync({ cmd: [ bunExe(), `${import.meta.dir}/console-table-run.ts`, `(() => [${JSON.stringify([...str.matchAll(/.{16}|.+/g)].map(g => g[0].split("")))}])`, ], stdout: "pipe", stderr: "inherit", env: bunEnv, }); const actualOutput = stdout.toString(); console.log(actualOutput); }); test("console.table repeat 50", () => { const expected = `┌───┬───┐ │ │ n │ ├───┼───┤ │ 0 │ 8 │ └───┴───┘ `; const { stdout, stderr } = spawnSync({ cmd: [bunExe(), `${import.meta.dir}/console-table-repeat-50.ts`], stdout: "pipe", stderr: "pipe", env: bunEnv, }); expect(stdout.toString()).toBe(expected.repeat(50)); expect(stderr.toString()).toBe(""); });