From bd908e20791aadb9477cd5219991318eabc2e41c Mon Sep 17 00:00:00 2001 From: Gediminas Zlatkus Date: Sun, 4 Feb 2024 16:19:27 +0200 Subject: [PATCH] Multi root workspaces (#11) * Add multi-root workspace support for runAllTests command * Add multi-root workspace support for runAllTestsInActiveFile command * Add multi-root workspace support for runAllTestsInPath command * Clean up code * Fix broken tests * Update changelog --- CHANGELOG.md | 5 ++ package.json | 12 ++--- src/commands/runAllTestsInPath.js | 8 +++- src/extension.js | 4 +- src/helpers.js | 18 ++++--- src/terminal.js | 19 +++++--- tests/__mocks__/vscode.js | 25 +++------- tests/commands/runAllTestsInPath.test.js | 60 ++++++++++++------------ 8 files changed, 78 insertions(+), 73 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de56b6b..6e318ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Changelog +## Unreleased + +- Added support for [multi-root workspaces](https://code.visualstudio.com/docs/editor/multi-root-workspaces). +- Removed support for settings being overridable at language level. + ## 2023.7.0 - Added "Run Tests" context menu item to file explorer. diff --git a/package.json b/package.json index ff21140..dde9ad2 100644 --- a/package.json +++ b/package.json @@ -57,37 +57,37 @@ "properties": { "justTesting.baseCommand": { "type": "string", - "scope": "language-overridable", + "scope": "resource", "default": "python -m pytest -v", "description": "Base terminal command" }, "justTesting.runAllCommand": { "type": "string", - "scope": "language-overridable", + "scope": "resource", "default": "{base}", "description": "Terminal command for \"Run all tests\"" }, "justTesting.runFileCommand": { "type": "string", - "scope": "language-overridable", + "scope": "resource", "default": "{base} {fileName}", "description": "Terminal command for \"Run all tests in file\"" }, "justTesting.runOnCursorRegex": { "type": "string", - "scope": "language-overridable", + "scope": "resource", "default": "def (test_.+)\\(", "description": "Regular expression for matching closest test name" }, "justTesting.runOnCursorCommand": { "type": "string", - "scope": "language-overridable", + "scope": "resource", "default": "{base} {fileName} -k {testName}", "description": "Terminal command for \"Run test on cursor\"" }, "justTesting.expressions": { "type": "object", - "scope": "language-overridable", + "scope": "resource", "default": {}, "description": "Custom expressions for template variables" }, diff --git a/src/commands/runAllTestsInPath.js b/src/commands/runAllTestsInPath.js index 34b0934..914fb16 100644 --- a/src/commands/runAllTestsInPath.js +++ b/src/commands/runAllTestsInPath.js @@ -1,7 +1,10 @@ +const vscode = require('vscode') + const { runTerminalCommand } = require('../terminal') const helpers = require('../helpers') -function runAllTestsInPath (extensionContext, configuration, uri) { +function runAllTestsInPath (extensionContext, _, uri) { + const configuration = vscode.workspace.getConfiguration('justTesting', uri) const template = configuration.get('runFileCommand') const fileName = helpers.asRelativePath(uri.path) const context = { @@ -12,7 +15,8 @@ function runAllTestsInPath (extensionContext, configuration, uri) { const command = helpers.interpolate(template, context) - runTerminalCommand(extensionContext, command) + const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri) + runTerminalCommand(extensionContext, command, workspaceFolder) } module.exports = { runAllTestsInPath } diff --git a/src/extension.js b/src/extension.js index 2930b73..78c7da0 100644 --- a/src/extension.js +++ b/src/extension.js @@ -2,7 +2,7 @@ const vscode = require('vscode') const { copyTestOnCursor } = require('./commands/copyTestOnCursor') const { ExtensionError } = require('./errors') -const { getConfiguration } = require('./helpers') +const { getActiveConfiguration } = require('./helpers') const { runAllTests } = require('./commands/runAllTests') const { runAllTestsInActiveFile } = require('./commands/runAllTestsInActiveFile') const { runAllTestsInPath } = require('./commands/runAllTestsInPath') @@ -16,7 +16,7 @@ function activate (context) { context.subscriptions.push( vscode.commands.registerCommand(name, (...args) => { try { - callback(context, getConfiguration(), ...args) + callback(context, getActiveConfiguration(), ...args) } catch (e) { if (e instanceof ExtensionError) { vscode.window.showErrorMessage(e.message) diff --git a/src/helpers.js b/src/helpers.js index e1539a5..1b0f475 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -1,19 +1,16 @@ const vscode = require('vscode') -function getActiveLanguageId () { - const editor = vscode.window.activeTextEditor - return editor && editor.document.languageId -} - function pathToModule (path) { const components = path.split('/') const baseName = components.pop() const moduleName = baseName.split('.')[0] - return components.length ? components.join('.') + '.' + moduleName : moduleName + return [...components, moduleName].join('.') } -function getConfiguration () { - return vscode.workspace.getConfiguration('justTesting', { languageId: getActiveLanguageId() }) +function getActiveConfiguration () { + const editor = vscode.window.activeTextEditor + const scope = editor && editor.document.uri + return vscode.workspace.getConfiguration('justTesting', scope) } function interpolate (template, context) { @@ -22,9 +19,10 @@ function interpolate (template, context) { template ) } + module.exports = { - getConfiguration, - asRelativePath: (path) => vscode.workspace.asRelativePath(path), + getActiveConfiguration, + asRelativePath: (path) => vscode.workspace.asRelativePath(path, false), pathToModule, interpolate } diff --git a/src/terminal.js b/src/terminal.js index 08ab9b0..27819f3 100644 --- a/src/terminal.js +++ b/src/terminal.js @@ -1,22 +1,29 @@ const vscode = require('vscode') -const TERMINAL_NAME = 'Just Testing' const LAST_COMMAND = 'lastCommand' -async function runTerminalCommand (extensionContext, command) { +async function runTerminalCommand (extensionContext, command, workspaceFolder = undefined) { await vscode.workspace.saveAll() extensionContext.workspaceState.update(LAST_COMMAND, command) - const terminal = obtainTerminal() + const terminal = obtainTerminal(workspaceFolder || getActiveWorkspaceFolder()) terminal.show(true) terminal.sendText(command) } -function obtainTerminal () { - const terminal = vscode.window.terminals.find(terminal => terminal.name === TERMINAL_NAME) +function obtainTerminal (workspaceFolder) { + const name = `Just Testing: ${workspaceFolder.name}` + const terminal = vscode.window.terminals.find(terminal => terminal.name === name) if (terminal) return terminal + return vscode.window.createTerminal({ name, cwd: workspaceFolder.uri }) +} - return vscode.window.createTerminal(TERMINAL_NAME) +function getActiveWorkspaceFolder () { + const editor = vscode.window.activeTextEditor + if (editor === undefined) { + return vscode.workspace.workspaceFolders[0] + } + return vscode.workspace.getWorkspaceFolder(editor.document.uri) } module.exports = { LAST_COMMAND, runTerminalCommand } diff --git a/tests/__mocks__/vscode.js b/tests/__mocks__/vscode.js index 9528be6..51839c8 100644 --- a/tests/__mocks__/vscode.js +++ b/tests/__mocks__/vscode.js @@ -7,21 +7,6 @@ const _lines = [ ' assert True' ] -const _configuration = { - justTesting: { - python: new Map([ - ['baseCommand', 'pytest'], - ['runFileCommand', '{base} {fileName}'], - ['runOnCursorRegex', 'def (test_.+)\\('], - ['runOnCursorCommand', '{base} {fileName} -k {testName}'], - ['expressions', { - valueExpression: { value: 'static-value' }, - regexExpression: { regex: 'def (.+_test)' } - }] - ]) - } -} - const vscode = { window: { activeTextEditor: { @@ -36,7 +21,7 @@ const vscode = { }, terminals: [ { - name: 'Just Testing', + name: 'Just Testing: foo', show () { this._wasShown = true }, @@ -48,12 +33,16 @@ const vscode = { }, workspace: { - getConfiguration (section, { languageId }) { - return _configuration[section][languageId] + _configuration: { justTesting: {} }, + getConfiguration (section) { + return new Map(Object.entries(this._configuration[section])) }, asRelativePath (path) { return path.replace(/^\/root\//, '') }, + getWorkspaceFolder () { + return { name: 'foo', uri: '/root/' } + }, async saveAll () { return true } } } diff --git a/tests/commands/runAllTestsInPath.test.js b/tests/commands/runAllTestsInPath.test.js index 8068879..d92653b 100644 --- a/tests/commands/runAllTestsInPath.test.js +++ b/tests/commands/runAllTestsInPath.test.js @@ -5,18 +5,20 @@ const { makeExtensionContext } = require('../helpers') beforeEach(() => { vscode.window.terminals[0]._lastCommand = undefined + vscode.workspace._configuration = { + justTesting: { + baseCommand: "pytest", + runFileCommand: "{base} {fileName}", + } + } }) describe('runAllTestsInPath', () => { it('runs tests in a selected file', async () => { const extensionContext = makeExtensionContext() - const configuration = new Map([ - ['baseCommand', 'pytest'], - ['runFileCommand', '{base} {fileName}'] - ]) const uri = { path: '/root/tests/foo/test_bar.py' } - await runAllTestsInPath(extensionContext, configuration, uri) + await runAllTestsInPath(extensionContext, undefined, uri) expect(vscode.window.terminals[0]._lastCommand).toBe('pytest tests/foo/test_bar.py') expect(extensionContext.workspaceState.get('lastCommand')).toBe('pytest tests/foo/test_bar.py') @@ -24,40 +26,40 @@ describe('runAllTestsInPath', () => { it('runs tests in a selected directory', async () => { const extensionContext = makeExtensionContext() - const configuration = new Map([ - ['baseCommand', 'pytest'], - ['runFileCommand', '{base} {fileName}'] - ]) const uri = { path: '/root/tests/foo/' } - await runAllTestsInPath(extensionContext, configuration, uri) + await runAllTestsInPath(extensionContext, undefined, uri) expect(vscode.window.terminals[0]._lastCommand).toBe('pytest tests/foo/') }) - it('runs tests in a selected file as module', async () => { - const extensionContext = makeExtensionContext() - const configuration = new Map([ - ['baseCommand', 'pytest'], - ['runFileCommand', '{base} {module}'] - ]) - const uri = { path: '/root/tests/foo/test_bar.py' } + describe('running tests as module', () => { + beforeEach(() => { + vscode.workspace._configuration = { + justTesting: { + baseCommand: "pytest", + runFileCommand: "{base} {module}", + } + } + }) - await runAllTestsInPath(extensionContext, configuration, uri) + it('runs tests in a selected file as module', async () => { + const extensionContext = makeExtensionContext() + const uri = { path: '/root/tests/foo/test_bar.py' } - expect(vscode.window.terminals[0]._lastCommand).toBe('pytest tests.foo.test_bar') - }) + await runAllTestsInPath(extensionContext, undefined, uri) - it('runs tests in a selected directory as module', async () => { - const extensionContext = makeExtensionContext() - const configuration = new Map([ - ['baseCommand', 'pytest'], - ['runFileCommand', '{base} {module}'] - ]) - const uri = { path: '/root/tests/foo' } + expect(vscode.window.terminals[0]._lastCommand).toBe('pytest tests.foo.test_bar') + }) + + it('runs tests in a selected directory as module', async () => { + const extensionContext = makeExtensionContext() + const uri = { path: '/root/tests/foo' } - await runAllTestsInPath(extensionContext, configuration, uri) + await runAllTestsInPath(extensionContext, undefined, uri) - expect(vscode.window.terminals[0]._lastCommand).toBe('pytest tests.foo') + expect(vscode.window.terminals[0]._lastCommand).toBe('pytest tests.foo') + }) }) + })