1- import { stripVTControlCharacters } from 'node:util' ;
21import { testSuite , expect } from 'manten' ;
32import { createFixture } from 'fs-fixture' ;
3+ import ansiEscapes from 'ansi-escapes' ;
4+ import yoctocolors from 'yoctocolors' ;
45import { node } from '../utils/node.js' ;
56import { tempDir } from '../utils/temp-dir.js' ;
67
78export default testSuite ( ( { describe } ) => {
89 describe ( 'ANSI control codes' , ( { test } ) => {
9- test ( 'uses ANSI clear and cursor movement codes' , async ( ) => {
10+ test ( 'uses ANSI clear and cursor movement codes during spinner animation ' , async ( { onTestFail } ) => {
1011 await using fixture = await createFixture ( {
1112 'test.mjs' : `
1213 import task from '#tasuku';
1314 import { setTimeout } from 'node:timers/promises';
1415
1516 await task('Task', async () => {
16- await setTimeout(200 );
17+ await setTimeout(100 );
1718 });
1819 ` ,
1920 } , { tempDir } ) ;
2021
2122 const result = await node ( fixture . getPath ( 'test.mjs' ) ) ;
23+ onTestFail ( ( ) => { console . log ( result ) ; } ) ;
24+ expect ( result . stderr ) . toBe ( '' ) ;
2225
23- // Task completes successfully
24- expect ( stripVTControlCharacters ( result . output ) . includes ( 'Task' ) ) . toBe ( true ) ;
26+ // Loading state: yellow spinner
27+ expect ( result . stdout ) . toContain ( yoctocolors . yellow ( '⠋' ) ) ;
2528
26- // ANSI codes for line clearing and cursor movement
27- // \x1B[2K = clear line
28- // \x1B[1A = move cursor up one line
29- expect ( result . output ) . toContain ( '\u001B[2K' ) ;
30- expect ( result . output ) . toContain ( '\u001B[1A' ) ;
31- } ) ;
32-
33- test ( 'ANSI clear codes present during spinner animation' , async ( ) => {
34- await using fixture = await createFixture ( {
35- 'test.mjs' : `
36- import task from '#tasuku';
37- import { setTimeout } from 'node:timers/promises';
38-
39- await task('Task', async () => {
40- await setTimeout(200);
41- });
42- ` ,
43- } , { tempDir } ) ;
44-
45- const result = await node ( fixture . getPath ( 'test.mjs' ) ) ;
46-
47- // Verify task completed
48- expect ( stripVTControlCharacters ( result . output ) . includes ( 'Task' ) ) . toBe ( true ) ;
29+ // ANSI codes used for animation
30+ const eraseLineCount = result . stdout . split ( ansiEscapes . eraseLine ) . length - 1 ;
31+ const cursorUpCount = result . stdout . split ( ansiEscapes . cursorUp ( ) ) . length - 1 ;
32+ expect ( eraseLineCount ) . toBeGreaterThanOrEqual ( 1 ) ; // At least one clear
33+ expect ( cursorUpCount ) . toBeGreaterThanOrEqual ( 1 ) ; // At least one cursor move
4934
50- // ANSI codes for cursor movement and line clearing during spinner updates
51- expect ( result . output ) . toContain ( '\u001B[2K' ) ; // clear line
52- expect ( result . output ) . toContain ( '\u001B[1A ' ) ; // move up
35+ // Final state: green checkmark
36+ expect ( result . stdout ) . toContain ( yoctocolors . green ( '✔' ) ) ;
37+ expect ( result . stdout ) . toContain ( 'Task ' ) ;
5338 } ) ;
5439
5540 test ( 'multiple line clears for multiple tasks' , async ( ) => {
@@ -67,16 +52,19 @@ export default testSuite(({ describe }) => {
6752 } , { tempDir } ) ;
6853
6954 const result = await node ( fixture . getPath ( 'test.mjs' ) ) ;
70- const textOutput = stripVTControlCharacters ( result . output ) ;
71-
72- // All task names appear
73- expect ( textOutput . includes ( 'one' ) ) . toBe ( true ) ;
74- expect ( textOutput . includes ( 'two' ) ) . toBe ( true ) ;
75- expect ( textOutput . includes ( 'three' ) ) . toBe ( true ) ;
76-
77- // Multiple ANSI clear codes for updating multiple tasks
78- expect ( result . output ) . toContain ( '\u001B[2K' ) ;
79- expect ( result . output ) . toContain ( '\u001B[1A' ) ;
55+ expect ( result . stderr ) . toBe ( '' ) ;
56+
57+ // All task names appear with checkmarks
58+ expect ( result . stdout ) . toContain ( 'one' ) ;
59+ expect ( result . stdout ) . toContain ( 'two' ) ;
60+ expect ( result . stdout ) . toContain ( 'three' ) ;
61+ expect ( result . stdout ) . toContain ( yoctocolors . green ( '✔' ) ) ;
62+
63+ // Count ANSI codes - should appear multiple times for multiple task updates
64+ const eraseLineCount = result . stdout . split ( ansiEscapes . eraseLine ) . length - 1 ;
65+ const cursorUpCount = result . stdout . split ( ansiEscapes . cursorUp ( ) ) . length - 1 ;
66+ expect ( eraseLineCount ) . toBeGreaterThan ( 1 ) ; // Multiple line clears
67+ expect ( cursorUpCount ) . toBeGreaterThan ( 1 ) ; // Multiple cursor moves
8068 } ) ;
8169
8270 test ( 'clearing task triggers ANSI clear codes' , async ( ) => {
@@ -94,18 +82,24 @@ export default testSuite(({ describe }) => {
9482 });
9583
9684 task1.clear();
97-
98- // Give renderer time to update
99- await setTimeout(50);
10085 ` ,
10186 } , { tempDir } ) ;
10287
10388 const result = await node ( fixture . getPath ( 'test.mjs' ) ) ;
104-
105- // Should contain ANSI clear codes (line clear and cursor up)
106- // These codes are used to clear Task 1 from the screen
107- expect ( result . output ) . toContain ( '\u001B[2K' ) ; // clear line
108- expect ( result . output ) . toContain ( '\u001B[1A' ) ; // move cursor up
89+ expect ( result . stderr ) . toBe ( '' ) ;
90+
91+ // Both tasks appear with checkmarks initially
92+ expect ( result . stdout ) . toContain ( 'Task 1' ) ;
93+ expect ( result . stdout ) . toContain ( 'Task 2' ) ;
94+ expect ( result . stdout ) . toContain ( yoctocolors . green ( '✔' ) ) ;
95+
96+ // Clearing triggers additional ANSI codes for re-rendering
97+ const eraseLineCount = result . stdout . split ( ansiEscapes . eraseLine ) . length - 1 ;
98+ const cursorUpCount = result . stdout . split ( ansiEscapes . cursorUp ( ) ) . length - 1 ;
99+ // Multiple clears for task updates + clear operation
100+ expect ( eraseLineCount ) . toBeGreaterThan ( 1 ) ;
101+ // Multiple cursor moves
102+ expect ( cursorUpCount ) . toBeGreaterThan ( 1 ) ;
109103 } ) ;
110104 } ) ;
111105} ) ;
0 commit comments