Skip to content

Commit 3c2d01b

Browse files
test(editor): fake vscode as an external editor MONGOSH-1011 (#1131)
* test: fake vscode as an external editor MONGOSH-1011 * test: assert extension inside script * test: move expectedExtension to a proper test * test: using flags along with file names * refactor: stringify flags * refactor: use deepStrictEqual and slice * test: check if windows ignores shebang in script * test: fix opening objects * test: refactor unit tests * refactor: change test name and check one windows test * test: return fix for multiline on windows
1 parent 71c89f9 commit 3c2d01b

File tree

5 files changed

+521
-204
lines changed

5 files changed

+521
-204
lines changed

packages/cli-repl/test/e2e-editor.spec.ts

Lines changed: 207 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { promisify } from 'util';
55
import rimraf from 'rimraf';
66

77
import { eventually } from '../../../testing/eventually';
8-
import { useTmpdir, fakeExternalEditor, setTemporaryHomeDirectory } from './repl-helpers';
98
import { TestShell } from './test-shell';
9+
import { useTmpdir, fakeExternalEditor, setTemporaryHomeDirectory } from './repl-helpers';
1010

1111
describe('external editor e2e', () => {
1212
const tmpdir = useTmpdir();
@@ -45,76 +45,224 @@ describe('external editor e2e', () => {
4545
}
4646
});
4747

48-
it('returns a modified identifier for fn', async() => {
49-
const shellOriginalInput = "const fn = function () { console.log(111); }; edit('fn')";
50-
const editorOutput = `function () {
51-
console.log(222);
52-
};`;
53-
const shellModifiedInput = 'fn = function () { console.log(222); };';
54-
const editor = await fakeExternalEditor(editorOutput);
55-
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
56-
57-
expect(result).to.include('"editor" has been changed');
58-
shell.writeInputLine(shellOriginalInput);
59-
await eventually(() => {
60-
shell.assertContainsOutput(shellModifiedInput);
48+
context('when editor is node command', () => {
49+
it('creates a file with .js extension', async() => {
50+
const shellOriginalInput = 'edit 111';
51+
const editorOutput = '222';
52+
const shellModifiedInput = '222';
53+
const editor = await fakeExternalEditor({
54+
output: editorOutput,
55+
expectedExtension: '.js',
56+
tmpdir: tmpdir.path,
57+
name: 'editor',
58+
isNodeCommand: true
59+
});
60+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
61+
62+
expect(result).to.include('"editor" has been changed');
63+
shell.writeInputLine(shellOriginalInput);
64+
await eventually(() => {
65+
shell.assertContainsOutput(shellModifiedInput);
66+
});
6167
});
62-
});
6368

64-
it('returns a modified identifier for var', async() => {
65-
const shellOriginalInput = "const myVar = '111'; edit('myVar')";
66-
const editorOutput = "const myVar = '222';";
67-
const shellModifiedInput = "myVar = '222';";
68-
const editor = await fakeExternalEditor(editorOutput);
69-
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
70-
71-
expect(result).to.include('"editor" has been changed');
72-
shell.writeInputLine(shellOriginalInput);
73-
await eventually(() => {
74-
shell.assertContainsOutput(shellModifiedInput);
69+
it('returns a modified identifier for fn', async() => {
70+
const shellOriginalInput = "const fn = function () { console.log(111); }; edit('fn')";
71+
const editorOutput = `function () {
72+
console.log(222);
73+
};`;
74+
const shellModifiedInput = 'fn = function () { console.log(222); };';
75+
const editor = await fakeExternalEditor({
76+
output: editorOutput,
77+
tmpdir: tmpdir.path,
78+
name: 'editor',
79+
isNodeCommand: true
80+
});
81+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
82+
83+
expect(result).to.include('"editor" has been changed');
84+
shell.writeInputLine(shellOriginalInput);
85+
await eventually(() => {
86+
shell.assertContainsOutput(shellModifiedInput);
87+
});
88+
});
89+
90+
it('returns a modified identifier for var', async() => {
91+
const shellOriginalInput = "const myVar = '111'; edit('myVar')";
92+
const editorOutput = '"222"';
93+
const shellModifiedInput = 'myVar = "222"';
94+
const editor = await fakeExternalEditor({
95+
output: editorOutput,
96+
tmpdir: tmpdir.path,
97+
name: 'editor',
98+
isNodeCommand: true
99+
});
100+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
101+
102+
expect(result).to.include('"editor" has been changed');
103+
shell.writeInputLine(shellOriginalInput);
104+
await eventually(() => {
105+
shell.assertContainsOutput(shellModifiedInput);
106+
});
107+
});
108+
109+
it('returns a modified identifier for a.b.c', async() => {
110+
const shellOriginalInput = "const myObj = { field: { child: 'string value' } }; edit('myObj')";
111+
const editorOutput = `{
112+
"field": {
113+
"child": "new value"
114+
}
115+
}`;
116+
const shellModifiedInput = 'myObj = { "field": { "child": "new value" } }';
117+
const editor = await fakeExternalEditor({
118+
output: editorOutput,
119+
tmpdir: tmpdir.path,
120+
name: 'editor',
121+
isNodeCommand: true
122+
});
123+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
124+
125+
expect(result).to.include('"editor" has been changed');
126+
shell.writeInputLine(shellOriginalInput);
127+
await eventually(() => {
128+
shell.assertContainsOutput(shellModifiedInput);
129+
});
130+
});
131+
132+
it('returns an error when editor exits with exitCode 1', async() => {
133+
const shellOriginalInput = 'edit function() {}';
134+
const editor = await fakeExternalEditor({
135+
tmpdir: tmpdir.path,
136+
name: 'editor',
137+
isNodeCommand: true
138+
});
139+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
140+
141+
expect(result).to.include('"editor" has been changed');
142+
shell.writeInputLine(shellOriginalInput);
143+
await eventually(() => {
144+
shell.assertContainsError('failed with an exit code 1');
145+
});
146+
});
147+
148+
it('opens an empty editor', async() => {
149+
const output = '';
150+
const editor = await fakeExternalEditor({
151+
output,
152+
tmpdir: tmpdir.path,
153+
name: 'editor',
154+
isNodeCommand: true
155+
});
156+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
157+
158+
expect(result).to.include('"editor" has been changed');
159+
shell.writeInputLine('edit');
160+
await eventually(() => {
161+
shell.assertContainsOutput(output);
162+
});
75163
});
76164
});
77165

78-
it('returns a modified identifier for a.b.c', async() => {
79-
const shellOriginalInput = "const myObj = { field: { child: 'string value' } }; edit('myObj')";
80-
const editorOutput = `const myObj = {
81-
field: {
82-
child: 'new value'
166+
context('when editor is executable file', () => {
167+
before(function() {
168+
if (process.platform === 'win32') {
169+
return this.skip(); // Shebangs don't work on windows.
83170
}
84-
};`;
85-
const shellModifiedInput = "myObj = { field: { child: 'new value' } };";
86-
const editor = await fakeExternalEditor(editorOutput);
87-
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
88-
89-
expect(result).to.include('"editor" has been changed');
90-
shell.writeInputLine(shellOriginalInput);
91-
await eventually(() => {
92-
shell.assertContainsOutput(shellModifiedInput);
93171
});
94-
});
95172

96-
it('returns an error when editor exits with exitCode 1', async() => {
97-
const shellOriginalInput = 'edit function() {}';
98-
const editor = await fakeExternalEditor();
99-
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
100-
await shell.executeLine('config.set("showStackTraces", true); print(process.env.PATH);');
173+
context('not vscode', () => {
174+
it('creates a file with .js extension', async() => {
175+
const editorOutput = "const name = 'Succeeded!'";
176+
const shellModifiedInput = "const name = 'Succeeded!'";
177+
const editor = await fakeExternalEditor({
178+
output: editorOutput,
179+
expectedExtension: '.js',
180+
tmpdir: tmpdir.path,
181+
name: 'editor',
182+
isNodeCommand: false,
183+
flags: '--trace-uncaught'
184+
});
185+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
101186

102-
expect(result).to.include('"editor" has been changed');
103-
shell.writeInputLine(shellOriginalInput);
104-
await eventually(() => {
105-
shell.assertContainsError('failed with an exit code 1');
187+
expect(result).to.include('"editor" has been changed');
188+
shell.writeInputLine("const name = 'I want to test a sequence of writeInputLine'");
189+
shell.writeInputLine('edit name');
190+
await eventually(() => {
191+
shell.assertContainsOutput(shellModifiedInput);
192+
});
193+
});
106194
});
107-
});
108195

109-
it('opens an empty editor', async() => {
110-
const output = '';
111-
const editor = await fakeExternalEditor(output);
112-
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
196+
context('vscode', () => {
197+
context('when mongodb extension is installed', () => {
198+
beforeEach(async() => {
199+
// make a fake dir for vscode mongodb extension
200+
await fs.mkdir(path.join(homedir, '.vscode', 'extensions', 'mongodb.mongodb-vscode-0.0.0'), { recursive: true });
201+
});
202+
203+
it('creates a file with .mongodb extension', async() => {
204+
const shellOriginalInput = 'edit 111';
205+
const editorOutput = 'edit 222';
206+
const shellModifiedInput = '222';
207+
const editor = await fakeExternalEditor({
208+
output: editorOutput,
209+
expectedExtension: '.mongodb',
210+
tmpdir: tmpdir.path,
211+
name: 'code',
212+
isNodeCommand: false
213+
});
214+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
215+
216+
expect(result).to.include('"editor" has been changed');
217+
shell.writeInputLine(shellOriginalInput);
218+
await eventually(() => {
219+
shell.assertContainsOutput(shellModifiedInput);
220+
});
221+
});
222+
223+
it('allows using flags along with file names', async() => {
224+
const shellOriginalInput = "edit 'string with whitespaces'";
225+
const editorOutput = "'string with whitespaces and const x = 0;'";
226+
const shellModifiedInput = "'string with whitespaces and const x = 0;'";
227+
const editor = await fakeExternalEditor({
228+
output: editorOutput,
229+
expectedExtension: '.mongodb',
230+
tmpdir: tmpdir.path,
231+
name: 'code',
232+
flags: '--wait',
233+
isNodeCommand: false
234+
});
235+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
236+
237+
expect(result).to.include('"editor" has been changed');
238+
shell.writeInputLine(shellOriginalInput);
239+
await eventually(() => {
240+
shell.assertContainsOutput(shellModifiedInput);
241+
});
242+
});
243+
});
244+
245+
context('when mongodb extension is not installed', () => {
246+
it('creates a file with .js extension', async() => {
247+
const shellOriginalInput = "edit const x = 'y';";
248+
const editorOutput = "const x = 'zyz';";
249+
const shellModifiedInput = "const x = 'zyz';";
250+
const editor = await fakeExternalEditor({
251+
output: editorOutput,
252+
expectedExtension: '.js',
253+
tmpdir: tmpdir.path,
254+
name: 'code',
255+
isNodeCommand: false
256+
});
257+
const result = await shell.executeLine(`config.set("editor", ${JSON.stringify(editor)});`);
113258

114-
expect(result).to.include('"editor" has been changed');
115-
shell.writeInputLine('edit');
116-
await eventually(() => {
117-
shell.assertContainsOutput(output);
259+
expect(result).to.include('"editor" has been changed');
260+
shell.writeInputLine(shellOriginalInput);
261+
await eventually(() => {
262+
shell.assertContainsOutput(shellModifiedInput);
263+
});
264+
});
265+
});
118266
});
119267
});
120268
});

packages/cli-repl/test/repl-helpers.ts

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,29 +77,51 @@ async function readReplLogfile(logPath: string) {
7777
}
7878

7979

80-
const fakeExternalEditor = async(output?: string) => {
81-
const base = path.resolve(__dirname, '..', '..', '..', 'tmp', 'test', `${Date.now()}`, `${Math.random()}`);
82-
const tmpDoc = path.join(base, 'editor-script.js');
80+
const fakeExternalEditor = async(
81+
{ output, expectedExtension, tmpdir, name, flags, isNodeCommand }: {
82+
output?: string,
83+
expectedExtension?: string,
84+
tmpdir: string,
85+
name: string,
86+
flags?: string,
87+
isNodeCommand: boolean
88+
}
89+
) => {
90+
const tmpDoc = path.join(tmpdir, name);
91+
const editor = isNodeCommand ? `node ${tmpDoc}` : tmpDoc;
8392
let script: string;
8493

8594
if (typeof output === 'string') {
86-
script = `(async () => {
95+
script = `#!/usr/bin/env node
96+
(async () => {
8797
const tmpDoc = process.argv[process.argv.length - 1];
8898
const { promises: { writeFile } } = require("fs");
99+
const assert = require("assert");
100+
const path = require("path");
101+
102+
if (${JSON.stringify(expectedExtension ?? '')}) {
103+
assert.strictEqual(path.extname(tmpDoc), ${JSON.stringify(expectedExtension)});
104+
}
105+
106+
if (${JSON.stringify(flags ?? '')}) {
107+
assert.deepStrictEqual((${JSON.stringify(flags)}).split(/\s+/), process.argv.slice(2, -1));
108+
}
109+
89110
await writeFile(tmpDoc, ${JSON.stringify(output)}, { mode: 0o600 });
90-
})()`;
111+
})().catch((err) => { process.nextTick(() => { throw err; }); });`;
91112
} else {
92-
script = 'process.exit(1);';
113+
script = `#!/usr/bin/env node
114+
process.exit(1);`;
93115
}
94116

95117
await fs.mkdir(path.dirname(tmpDoc), { recursive: true, mode: 0o700 });
96-
await fs.writeFile(tmpDoc, script, { mode: 0o600 });
118+
await fs.writeFile(tmpDoc, script, { mode: 0o700 });
97119

98-
return `node ${tmpDoc}`;
120+
return flags ? `${editor} ${flags}` : editor;
99121
};
100122

101123
const setTemporaryHomeDirectory = () => {
102-
const homedir: string = path.resolve(__dirname, '..', '..', '..', 'tmp', `cli-repl-home-${Date.now()}-${Math.random()}`);
124+
const homedir: string = path.resolve(__dirname, '..', '..', '..', 'tmp', 'test', `cli-repl-home-${Date.now()}-${Math.random()}`);
103125
const env: Record<string, string> = { ...process.env, HOME: homedir, USERPROFILE: homedir };
104126

105127
if (process.platform === 'win32') {

0 commit comments

Comments
 (0)