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+});