1+ import QRCode from "qrcode" ;
12import { describe , expect , it } from "vitest" ;
23import { renderQrTerminal } from "./qr-terminal.ts" ;
34
5+ const ansiSgr = new RegExp ( `${ String . fromCharCode ( 0x1b ) } \\[[0-9;]*m` , "g" ) ;
6+ const compactMarginModules = 1 ;
7+
8+ function visibleLines ( output : string ) : string [ ] {
9+ return output
10+ . split ( / \r ? \n / )
11+ . map ( ( line ) => line . replace ( ansiSgr , "" ) )
12+ . filter ( ( line ) => line . length > 0 ) ;
13+ }
14+
15+ function maxVisibleWidth ( output : string ) : number {
16+ return Math . max ( ...visibleLines ( output ) . map ( ( line ) => line . length ) ) ;
17+ }
18+
19+ function decodeCompactBlock ( char : string ) : [ boolean , boolean ] {
20+ if ( char === "█" ) {
21+ return [ true , true ] ;
22+ }
23+ if ( char === "▀" ) {
24+ return [ true , false ] ;
25+ }
26+ if ( char === "▄" ) {
27+ return [ false , true ] ;
28+ }
29+ if ( char === " " ) {
30+ return [ false , false ] ;
31+ }
32+ throw new Error ( `Unexpected compact QR character: ${ char } ` ) ;
33+ }
34+
35+ function decodeCompactQr ( output : string , size : number ) : boolean [ ] {
36+ const decoded = Array . from ( { length : size * size } , ( ) => false ) ;
37+ visibleLines ( output ) . forEach ( ( line , lineIndex ) => {
38+ Array . from ( line ) . forEach ( ( char , columnIndex ) => {
39+ const x = columnIndex - compactMarginModules ;
40+ const topY = lineIndex * 2 - compactMarginModules ;
41+ const [ top , bottom ] = decodeCompactBlock ( char ) ;
42+ for ( const [ y , value ] of [
43+ [ topY , top ] ,
44+ [ topY + 1 , bottom ] ,
45+ ] as const ) {
46+ if ( x >= 0 && x < size && y >= 0 && y < size ) {
47+ decoded [ y * size + x ] = value ;
48+ }
49+ }
50+ } ) ;
51+ } ) ;
52+ return decoded ;
53+ }
54+
455describe ( "renderQrTerminal (real qrcode runtime)" , ( ) => {
556 it ( "keeps per-row ANSI sequence counts in line with typical rows" , async ( ) => {
657 const sample = "https://wa.me/login/2@SAMPLE-TOKEN-1234567890ABCDEF" ;
758 const rendered = await renderQrTerminal ( sample ) ;
8- const ansiSgr = new RegExp ( `${ String . fromCharCode ( 0x1b ) } \\[[0-9;]*m` , "g" ) ;
959 const escCounts = rendered
1060 . split ( / \r ? \n / )
1161 . map ( ( line ) => ( line . match ( ansiSgr ) ?? [ ] ) . length )
@@ -17,4 +67,14 @@ describe("renderQrTerminal (real qrcode runtime)", () => {
1767 expect ( median ) . toBeGreaterThan ( 0 ) ;
1868 expect ( max ) . toBeLessThanOrEqual ( median * 6 ) ;
1969 } ) ;
70+
71+ it ( "renders compact output from the same QR matrix" , async ( ) => {
72+ const sample = "https://wa.me/login/2@SAMPLE-TOKEN-1234567890ABCDEF" ;
73+ const qr = QRCode . create ( sample ) ;
74+ const full = await renderQrTerminal ( sample ) ;
75+ const compact = await renderQrTerminal ( sample , { small : true } ) ;
76+
77+ expect ( maxVisibleWidth ( compact ) ) . toBeLessThan ( maxVisibleWidth ( full ) ) ;
78+ expect ( decodeCompactQr ( compact , qr . modules . size ) ) . toEqual ( Array . from ( qr . modules . data , Boolean ) ) ;
79+ } ) ;
2080} ) ;
0 commit comments