diff options
Diffstat (limited to 'plugin/test')
| -rw-r--r-- | plugin/test/runner.test.js | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/plugin/test/runner.test.js b/plugin/test/runner.test.js new file mode 100644 index 0000000..a22d103 --- /dev/null +++ b/plugin/test/runner.test.js @@ -0,0 +1,117 @@ +'use strict'; + +const assert = require('node:assert/strict'); +const { test } = require('node:test'); +const { spawnSync } = require('node:child_process'); +const { buildRunCommand, registerRunner } = require('../src/runner'); + +test('commands quote paths and only run after changing directory', () => { + assert.equal(buildRunCommand('/tools/my cpsc', '/work/my file.cps', '/work/my project', 'posix'), + "cd '/work/my project' && '/tools/my cpsc' '/work/my file.cps' -r"); + assert.equal(buildRunCommand('C:\\My Tools\\cpsc.exe', 'D:\\My Files\\test.cps', 'D:\\My Files', 'cmd'), + 'cd /d "D:\\My Files" && "C:\\My Tools\\cpsc.exe" "D:\\My Files\\test.cps" -r'); + assert.equal(buildRunCommand('C:\\My Tools\\cpsc.exe', "D:\\Pat's Files\\test.cps", "D:\\Pat's Files", 'powershell'), + "Set-Location -LiteralPath 'D:\\Pat''s Files'; if ($?) { & 'C:\\My Tools\\cpsc.exe' 'D:\\Pat''s Files\\test.cps' -r }"); + assert.throws(() => buildRunCommand('cpsc\nother', 'test.cps', '.', 'posix'), /line breaks/); + if (process.platform !== 'win32') { + const file = "/tmp/Pat's file $(echo unexpected) `echo unexpected`.cps"; + const result = spawnSync('/bin/sh', ['-c', buildRunCommand('/usr/bin/printf', file, '/tmp', 'posix')], { encoding: 'utf8' }); + assert.equal(result.status, 0); + assert.equal(result.stdout, file); + const failed = spawnSync('/bin/sh', ['-c', buildRunCommand('/usr/bin/printf', file, '/missing-cie-run-directory', 'posix')], { encoding: 'utf8' }); + assert.notEqual(failed.status, 0); + assert.equal(failed.stdout, ''); + } +}); + +function harness(platform = 'linux') { + let run, close; + const calls = { saved: 0, terminals: [], errors: [], settings: [] }; + const document = { + languageId: 'cie-pseudocode', isUntitled: false, + uri: { scheme: 'file', fsPath: '/project/sub/my file.cps' }, + async save() { calls.saved++; return true; } + }; + const vscode = { + env: { shell: 'C:\\Windows\\System32\\cmd.exe' }, + commands: { registerCommand(id, callback) { assert.equal(id, 'cie-pseudocode.runFile'); run = callback; return { dispose() {} }; } }, + workspace: { + async openTextDocument() { return document; }, + getWorkspaceFolder() { return { uri: { fsPath: '/project' } }; }, + getConfiguration(section, uri) { + calls.settings.push({ section, uri }); + return { get(key, fallback) { return section === 'cie-pseudocode' ? '/tools/my cpsc' : fallback; } }; + } + }, + window: { + activeTextEditor: { document }, + async showTextDocument(doc) { this.activeTextEditor = { document: doc }; }, + showErrorMessage(message) { calls.errors.push(message); }, + onDidCloseTerminal(callback) { close = callback; return { dispose() {} }; }, + createTerminal(name) { + const terminal = { name, sent: [], show(focus) { this.preserveFocus = focus; }, sendText(text) { this.sent.push(text); }, dispose() {} }; + calls.terminals.push(terminal); + return terminal; + } + } + }; + registerRunner(vscode, { subscriptions: [] }, platform); + return { vscode, document, calls, run: uri => run(uri), close: terminal => close(terminal) }; +} + +test('save before running the whole file, scope settings, reuse and recreate the terminal', async () => { + const h = harness(); + await h.run(h.document.uri); + assert.equal(h.calls.saved, 1); + assert.equal(h.calls.terminals[0].preserveFocus, false); + assert.equal(h.calls.terminals[0].sent[0], "cd '/project' && '/tools/my cpsc' '/project/sub/my file.cps' -r"); + assert.equal(h.calls.settings[0].uri, h.document.uri); + h.close({}); + await h.run(); + assert.equal(h.calls.terminals.length, 1); + h.close(h.calls.terminals[0]); + h.vscode.workspace.getWorkspaceFolder = () => undefined; + await h.run(); + assert.equal(h.calls.terminals.length, 2); + assert.match(h.calls.terminals[1].sent[0], /^cd '\/project\/sub'/); + assert.deepEqual(h.calls.errors, []); +}); + +test('save cancellation and failure do not create a terminal; untitled files use the saved URI', async () => { + const h = harness(); + h.document.isUntitled = true; + h.document.uri = { scheme: 'untitled', fsPath: 'Untitled-1' }; + h.document.save = async () => false; + await h.run(); + assert.equal(h.calls.terminals.length, 0); + h.document.save = async () => { throw new Error('Save failed'); }; + await h.run(); + assert.match(h.calls.errors[0], /Save failed/); + assert.equal(h.calls.terminals.length, 0); + h.document.save = async () => { + h.vscode.window.activeTextEditor = { document: { ...h.document, isUntitled: false, + uri: { scheme: 'file', fsPath: '/project/new file.cps' } } }; + return true; + }; + await h.run(); + assert.match(h.calls.terminals[0].sent[0], /'\/project\/new file.cps' -r$/); +}); + +test('Windows recognizes custom pwsh and cmd profiles and retains the reused terminal shell', async () => { + const h = harness('win32'); + let profile = 'My Shell'; + h.vscode.workspace.getConfiguration = section => ({ get(key, fallback) { + if (section === 'cie-pseudocode') return 'cpsc'; + if (key === 'integrated.defaultProfile.windows') return profile; + if (key === 'integrated.profiles.windows') return { 'My Shell': { path: 'C:\\Program Files\\PowerShell\\7\\pwsh.exe' } }; + return fallback; + } }); + await h.run(); + assert.match(h.calls.terminals[0].sent[0], /^Set-Location.*& 'cpsc'/); + profile = 'Command Prompt'; + await h.run(); + assert.match(h.calls.terminals[0].sent[1], /^Set-Location/); + h.close(h.calls.terminals[0]); + await h.run(); + assert.match(h.calls.terminals[1].sent[0], /^cd \/d .*&& "cpsc"/); +}); |
