@@ -1336,6 +1336,128 @@ describe('InputPrompt', () => {
1336
1336
expect ( frame ) . toContain ( `hello 👍${ chalk . inverse ( ' ' ) } ` ) ;
1337
1337
unmount ( ) ;
1338
1338
} ) ;
1339
+
1340
+ it ( 'should display cursor on an empty line' , async ( ) => {
1341
+ mockBuffer . text = '' ;
1342
+ mockBuffer . lines = [ '' ] ;
1343
+ mockBuffer . viewportVisualLines = [ '' ] ;
1344
+ mockBuffer . visualCursor = [ 0 , 0 ] ;
1345
+
1346
+ const { stdout, unmount } = renderWithProviders (
1347
+ < InputPrompt { ...props } /> ,
1348
+ ) ;
1349
+ await wait ( ) ;
1350
+
1351
+ const frame = stdout . lastFrame ( ) ;
1352
+ expect ( frame ) . toContain ( chalk . inverse ( ' ' ) ) ;
1353
+ unmount ( ) ;
1354
+ } ) ;
1355
+
1356
+ it ( 'should display cursor on a space between words' , async ( ) => {
1357
+ mockBuffer . text = 'hello world' ;
1358
+ mockBuffer . lines = [ 'hello world' ] ;
1359
+ mockBuffer . viewportVisualLines = [ 'hello world' ] ;
1360
+ mockBuffer . visualCursor = [ 0 , 5 ] ; // cursor on the space
1361
+
1362
+ const { stdout, unmount } = renderWithProviders (
1363
+ < InputPrompt { ...props } /> ,
1364
+ ) ;
1365
+ await wait ( ) ;
1366
+
1367
+ const frame = stdout . lastFrame ( ) ;
1368
+ expect ( frame ) . toContain ( `hello${ chalk . inverse ( ' ' ) } world` ) ;
1369
+ unmount ( ) ;
1370
+ } ) ;
1371
+
1372
+ it ( 'should display cursor in the middle of a line in a multiline block' , async ( ) => {
1373
+ const text = 'first line\nsecond line\nthird line' ;
1374
+ mockBuffer . text = text ;
1375
+ mockBuffer . lines = text . split ( '\n' ) ;
1376
+ mockBuffer . viewportVisualLines = text . split ( '\n' ) ;
1377
+ mockBuffer . visualCursor = [ 1 , 3 ] ; // cursor on 'o' in 'second'
1378
+ mockBuffer . visualToLogicalMap = [
1379
+ [ 0 , 0 ] ,
1380
+ [ 1 , 0 ] ,
1381
+ [ 2 , 0 ] ,
1382
+ ] ;
1383
+
1384
+ const { stdout, unmount } = renderWithProviders (
1385
+ < InputPrompt { ...props } /> ,
1386
+ ) ;
1387
+ await wait ( ) ;
1388
+
1389
+ const frame = stdout . lastFrame ( ) ;
1390
+ expect ( frame ) . toContain ( `sec${ chalk . inverse ( 'o' ) } nd line` ) ;
1391
+ unmount ( ) ;
1392
+ } ) ;
1393
+
1394
+ it ( 'should display cursor at the beginning of a line in a multiline block' , async ( ) => {
1395
+ const text = 'first line\nsecond line' ;
1396
+ mockBuffer . text = text ;
1397
+ mockBuffer . lines = text . split ( '\n' ) ;
1398
+ mockBuffer . viewportVisualLines = text . split ( '\n' ) ;
1399
+ mockBuffer . visualCursor = [ 1 , 0 ] ; // cursor on 's' in 'second'
1400
+ mockBuffer . visualToLogicalMap = [
1401
+ [ 0 , 0 ] ,
1402
+ [ 1 , 0 ] ,
1403
+ ] ;
1404
+
1405
+ const { stdout, unmount } = renderWithProviders (
1406
+ < InputPrompt { ...props } /> ,
1407
+ ) ;
1408
+ await wait ( ) ;
1409
+
1410
+ const frame = stdout . lastFrame ( ) ;
1411
+ expect ( frame ) . toContain ( `${ chalk . inverse ( 's' ) } econd line` ) ;
1412
+ unmount ( ) ;
1413
+ } ) ;
1414
+
1415
+ it ( 'should display cursor at the end of a line in a multiline block' , async ( ) => {
1416
+ const text = 'first line\nsecond line' ;
1417
+ mockBuffer . text = text ;
1418
+ mockBuffer . lines = text . split ( '\n' ) ;
1419
+ mockBuffer . viewportVisualLines = text . split ( '\n' ) ;
1420
+ mockBuffer . visualCursor = [ 0 , 10 ] ; // cursor after 'first line'
1421
+ mockBuffer . visualToLogicalMap = [
1422
+ [ 0 , 0 ] ,
1423
+ [ 1 , 0 ] ,
1424
+ ] ;
1425
+
1426
+ const { stdout, unmount } = renderWithProviders (
1427
+ < InputPrompt { ...props } /> ,
1428
+ ) ;
1429
+ await wait ( ) ;
1430
+
1431
+ const frame = stdout . lastFrame ( ) ;
1432
+ expect ( frame ) . toContain ( `first line${ chalk . inverse ( ' ' ) } ` ) ;
1433
+ unmount ( ) ;
1434
+ } ) ;
1435
+
1436
+ it ( 'should display cursor on a blank line in a multiline block' , async ( ) => {
1437
+ const text = 'first line\n\nthird line' ;
1438
+ mockBuffer . text = text ;
1439
+ mockBuffer . lines = text . split ( '\n' ) ;
1440
+ mockBuffer . viewportVisualLines = text . split ( '\n' ) ;
1441
+ mockBuffer . visualCursor = [ 1 , 0 ] ; // cursor on the blank line
1442
+ mockBuffer . visualToLogicalMap = [
1443
+ [ 0 , 0 ] ,
1444
+ [ 1 , 0 ] ,
1445
+ [ 2 , 0 ] ,
1446
+ ] ;
1447
+
1448
+ const { stdout, unmount } = renderWithProviders (
1449
+ < InputPrompt { ...props } /> ,
1450
+ ) ;
1451
+ await wait ( ) ;
1452
+
1453
+ const frame = stdout . lastFrame ( ) ;
1454
+ const lines = frame ! . split ( '\n' ) ;
1455
+ // The line with the cursor should just be an inverted space inside the box border
1456
+ expect (
1457
+ lines . find ( ( l ) => l . includes ( chalk . inverse ( ' ' ) ) ) ,
1458
+ ) . not . toBeUndefined ( ) ;
1459
+ unmount ( ) ;
1460
+ } ) ;
1339
1461
} ) ;
1340
1462
1341
1463
describe ( 'multiline rendering' , ( ) => {
@@ -1966,6 +2088,33 @@ describe('InputPrompt', () => {
1966
2088
expect ( stdout . lastFrame ( ) ) . toMatchSnapshot ( ) ;
1967
2089
unmount ( ) ;
1968
2090
} ) ;
2091
+
2092
+ it ( 'should not show inverted cursor when shell is focused' , async ( ) => {
2093
+ props . isEmbeddedShellFocused = true ;
2094
+ props . focus = false ;
2095
+ const { stdout, unmount } = renderWithProviders (
2096
+ < InputPrompt { ...props } /> ,
2097
+ ) ;
2098
+ await wait ( ) ;
2099
+ expect ( stdout . lastFrame ( ) ) . not . toContain ( `{chalk.inverse(' ')}` ) ;
2100
+ // This snapshot is good to make sure there was an input prompt but does
2101
+ // not show the inverted cursor because snapshots do not show colors.
2102
+ expect ( stdout . lastFrame ( ) ) . toMatchSnapshot ( ) ;
2103
+ unmount ( ) ;
2104
+ } ) ;
2105
+ } ) ;
2106
+
2107
+ it ( 'should still allow input when shell is not focused' , async ( ) => {
2108
+ const { stdin, unmount } = renderWithProviders ( < InputPrompt { ...props } /> , {
2109
+ shellFocus : false ,
2110
+ } ) ;
2111
+ await wait ( ) ;
2112
+
2113
+ stdin . write ( 'a' ) ;
2114
+ await wait ( ) ;
2115
+
2116
+ expect ( mockBuffer . handleInput ) . toHaveBeenCalled ( ) ;
2117
+ unmount ( ) ;
1969
2118
} ) ;
1970
2119
} ) ;
1971
2120
function clean ( str : string | undefined ) : string {
0 commit comments