@@ -8,6 +8,7 @@ const thisFilePath = fileURLToPath(import.meta.url);
88const thisDir = path . dirname ( thisFilePath ) ;
99const repoRoot = path . resolve ( thisDir , '../../../../' ) ;
1010const cliEntry = path . join ( repoRoot , 'apps' , 'cli' , 'src' , 'node-entry.js' ) ;
11+ const utf8BomBytes = [ 0xef , 0xbb , 0xbf ] ;
1112
1213function runCli ( args ) {
1314 return spawnSync ( 'node' , [ cliEntry , 'generate' , ...args ] , {
@@ -16,6 +17,23 @@ function runCli(args) {
1617 } ) ;
1718}
1819
20+ async function withTempOutputPath ( name , run ) {
21+ const outputPath = path . join ( os . tmpdir ( ) , `anywaydata-cli-${ name } -${ Date . now ( ) } .csv` ) ;
22+ try {
23+ await run ( outputPath ) ;
24+ } finally {
25+ try {
26+ await fs . unlink ( outputPath ) ;
27+ } catch {
28+ // Ignore missing file cleanup races.
29+ }
30+ }
31+ }
32+
33+ function hasUtf8Bom ( buffer ) {
34+ return utf8BomBytes . every ( ( byte , index ) => buffer [ index ] === byte ) ;
35+ }
36+
1937test ( 'integration: writes generated content to stdout when no output file is provided' , ( ) => {
2038 const inputPath = path . join ( repoRoot , 'apps' , 'cli' , 'examples' , 'company-literal.txt' ) ;
2139 const result = runCli ( [ '-i' , inputPath , '-n' , '2' , '-f' , 'csv' ] ) ;
@@ -50,3 +68,49 @@ test('integration: writes generated content to file and keeps stdout progress-on
5068 }
5169 }
5270} ) ;
71+
72+ test ( 'integration: writes a UTF-8 BOM when --bom is true' , async ( ) => {
73+ const inputPath = path . join ( repoRoot , 'apps' , 'cli' , 'examples' , 'company-literal.txt' ) ;
74+ await withTempOutputPath ( 'bom-only' , async ( outputPath ) => {
75+ const result = runCli ( [ '-i' , inputPath , '-n' , '2' , '-f' , 'csv' , '-o' , outputPath , '--bom' ] ) ;
76+
77+ expect ( result . status ) . toBe ( 0 ) ;
78+ const writtenBuffer = await fs . readFile ( outputPath ) ;
79+ expect ( hasUtf8Bom ( writtenBuffer ) ) . toBe ( true ) ;
80+ } ) ;
81+ } ) ;
82+
83+ test ( 'integration: does not write a UTF-8 BOM by default' , async ( ) => {
84+ const inputPath = path . join ( repoRoot , 'apps' , 'cli' , 'examples' , 'company-literal.txt' ) ;
85+ await withTempOutputPath ( 'default-no-bom' , async ( outputPath ) => {
86+ const result = runCli ( [ '-i' , inputPath , '-n' , '2' , '-f' , 'csv' , '-o' , outputPath ] ) ;
87+
88+ expect ( result . status ) . toBe ( 0 ) ;
89+ const writtenBuffer = await fs . readFile ( outputPath ) ;
90+ expect ( hasUtf8Bom ( writtenBuffer ) ) . toBe ( false ) ;
91+ } ) ;
92+ } ) ;
93+
94+ test ( 'integration: writes CRLF line endings only when requested' , async ( ) => {
95+ const inputPath = path . join ( repoRoot , 'apps' , 'cli' , 'examples' , 'company-literal.txt' ) ;
96+ await withTempOutputPath ( 'crlf-only' , async ( outputPath ) => {
97+ const result = runCli ( [ '-i' , inputPath , '-n' , '2' , '-f' , 'csv' , '-o' , outputPath , '--line-endings' , 'crlf' ] ) ;
98+
99+ expect ( result . status ) . toBe ( 0 ) ;
100+ const written = await fs . readFile ( outputPath , 'utf8' ) ;
101+ expect ( written ) . toContain ( '\r\n' ) ;
102+ expect ( written . replace ( / \r \n / g, '' ) ) . not . toContain ( '\n' ) ;
103+ } ) ;
104+ } ) ;
105+
106+ test ( 'integration: writes LF line endings only when requested' , async ( ) => {
107+ const inputPath = path . join ( repoRoot , 'apps' , 'cli' , 'examples' , 'company-literal.txt' ) ;
108+ await withTempOutputPath ( 'lf-only' , async ( outputPath ) => {
109+ const result = runCli ( [ '-i' , inputPath , '-n' , '2' , '-f' , 'csv' , '-o' , outputPath , '--line-endings' , 'lf' ] ) ;
110+
111+ expect ( result . status ) . toBe ( 0 ) ;
112+ const written = await fs . readFile ( outputPath , 'utf8' ) ;
113+ expect ( written ) . toContain ( '\n' ) ;
114+ expect ( written ) . not . toContain ( '\r\n' ) ;
115+ } ) ;
116+ } ) ;
0 commit comments