repl: fix REPL completion under unary expressions · nodejs/node@a414c1e

1+

'use strict';

2+3+

const common = require('../common');

4+

const assert = require('assert');

5+

const repl = require('repl');

6+

const { describe, it } = require('node:test');

7+8+

// This test verifies that tab completion works correctly with unary expressions

9+

// like delete, typeof, void, etc. This is a regression test for the issue where

10+

// typing "delete globalThis._" and then backspacing and typing "globalThis"

11+

// would cause "globalThis is not defined" error.

12+13+

describe('REPL tab completion with unary expressions', () => {

14+

it('should handle delete operator correctly', (t, done) => {

15+

const r = repl.start({

16+

prompt: '',

17+

input: process.stdin,

18+

output: process.stdout,

19+

terminal: false,

20+

});

21+22+

// Test delete with member expression

23+

r.complete(

24+

'delete globalThis._',

25+

common.mustSucceed((completions) => {

26+

assert.strictEqual(completions[1], 'globalThis._');

27+28+

// Test delete with identifier

29+

r.complete(

30+

'delete globalThis',

31+

common.mustSucceed((completions) => {

32+

assert.strictEqual(completions[1], 'globalThis');

33+

r.close();

34+

done();

35+

})

36+

);

37+

})

38+

);

39+

});

40+41+

it('should handle typeof operator correctly', (t, done) => {

42+

const r = repl.start({

43+

prompt: '',

44+

input: process.stdin,

45+

output: process.stdout,

46+

terminal: false,

47+

});

48+49+

r.complete(

50+

'typeof globalThis',

51+

common.mustSucceed((completions) => {

52+

assert.strictEqual(completions[1], 'globalThis');

53+

r.close();

54+

done();

55+

})

56+

);

57+

});

58+59+

it('should handle void operator correctly', (t, done) => {

60+

const r = repl.start({

61+

prompt: '',

62+

input: process.stdin,

63+

output: process.stdout,

64+

terminal: false,

65+

});

66+67+

r.complete(

68+

'void globalThis',

69+

common.mustSucceed((completions) => {

70+

assert.strictEqual(completions[1], 'globalThis');

71+

r.close();

72+

done();

73+

})

74+

);

75+

});

76+77+

it('should handle other unary operators correctly', (t, done) => {

78+

const r = repl.start({

79+

prompt: '',

80+

input: process.stdin,

81+

output: process.stdout,

82+

terminal: false,

83+

});

84+85+

const unaryOperators = [

86+

'!globalThis',

87+

'+globalThis',

88+

'-globalThis',

89+

'~globalThis',

90+

];

91+92+

let testIndex = 0;

93+94+

function testNext() {

95+

if (testIndex >= unaryOperators.length) {

96+

r.close();

97+

done();

98+

return;

99+

}

100+101+

const testCase = unaryOperators[testIndex++];

102+

r.complete(

103+

testCase,

104+

common.mustSucceed((completions) => {

105+

assert.strictEqual(completions[1], 'globalThis');

106+

testNext();

107+

})

108+

);

109+

}

110+111+

testNext();

112+

});

113+114+

it('should still evaluate globalThis correctly after unary expression completion', (t, done) => {

115+

const r = repl.start({

116+

prompt: '',

117+

input: process.stdin,

118+

output: process.stdout,

119+

terminal: false,

120+

});

121+122+

// First trigger completion with delete

123+

r.complete(

124+

'delete globalThis._',

125+

common.mustSucceed(() => {

126+

// Then evaluate globalThis

127+

r.eval(

128+

'globalThis',

129+

r.context,

130+

'test.js',

131+

common.mustSucceed((result) => {

132+

assert.strictEqual(typeof result, 'object');

133+

assert.ok(result !== null);

134+

r.close();

135+

done();

136+

})

137+

);

138+

})

139+

);

140+

});

141+

});