diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..060ef00 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,8 @@ +{ + "extends": [ + "plugin:cypress-dev/general" + ], + "env": { + "node": true + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f336a59 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.DS_Store +node_modules +.vscode-test/ +.vsix diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..0915202 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the extensions.json format + "recommendations": [ + "dbaeumer.vscode-eslint" + ] +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0d96d50 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +// A launch configuration that launches the extension inside a new window +{ + "version": "0.1.0", + "configurations": [ + { + "name": "Launch Extension", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": ["--extensionDevelopmentPath=${workspaceRoot}" ], + "stopOnEntry": false + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..20af2f6 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +// Place your settings in this file to overwrite default and user settings. +{ +} \ No newline at end of file diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..499648c --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,7 @@ +.vscode/** +.vscode-test/** +test/** +.gitignore +jsconfig.json +vsc-extension-quickstart.md +.eslintrc.json diff --git a/README.md b/README.md new file mode 100644 index 0000000..b07a88c --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# VSCode Test Utils + +Test utilities extension for [VSCode](https://code.visualstudio.com/. Currently supports easily adding and removing `.only` and `.skip` modifiers with keyboard shortcuts or the command palette. + +## Available Commands + +| Shortcut | Action | Mnemonic | +| -------- | ------ | -------- | +| `ctrl-alt-a ctrl-alt-o` | Add a `.only` to the current line if there's a `describe`, `context`, or `it` | `a`dd `o`nly | +| `ctrl-alt-r ctrl-alt-o` | Remove the `.only` from the current line | `r`emove `o`nly | +| `ctrl-alt-x ctrl-alt-o` | Remove all `.only`s from the current file | e`x`terminate `o`nlys | +| `ctrl-alt-m ctrl-alt-o` | Remove all `.only`s from the current file and add a `.only` to the current line if there's a `describe`, `context`, or `it` | `m`ove `o`nly | +| `ctrl-alt-a ctrl-alt-s` | Add a `.skip` to the current line if there's a `describe`, `context`, or `it` | `a`dd `s`kip | +| `ctrl-alt-r ctrl-alt-s` | Remove the `.skip` from the current line | `r`emove `s`kip | +| `ctrl-alt-x ctrl-alt-s` | Remove all `.skip`s from the current file | e`x`terminate `s`kips | + +## About + +The cursor can be anywhere on the line for the `add` and `move` commands. The shortcuts are meant to be used hold `alt` and `ctrl`, then pressing the letters in succession. So `ctrl-alt-a ctrl-alt-o` means holding `alt` + `ctrl`, pressing `a`, then pressing `o`. diff --git a/extension.js b/extension.js new file mode 100644 index 0000000..40eb1c0 --- /dev/null +++ b/extension.js @@ -0,0 +1,97 @@ +const { commands, Position, Range, window } = require('vscode') + +const onlyRegex = /(describe|context|it)\.only/g +const skipRegex = /(describe|context|it)\.skip/g +const modifiableRunnableRegex = /(describe|context|it)(?=[ (])/ + +const getLineText = (editor, selection) => { + const startLine = selection.start.line + const range = editor.document.lineAt(startLine).range + return editor.document.getText(range) +} + +const editEachSelection = (run) => { + const editor = window.activeTextEditor + if (!editor) return + const selections = editor.selections + if (!selections || !selections.length) return + + return editor.edit((edit) => { + selections.reverse().forEach((selection) => { + run(editor, edit, selection) + }) + }) +} + +const addToSelections = (textToAdd) => () => { + return editEachSelection((editor, edit, selection) => { + const text = getLineText(editor, selection) + const match = text.match(modifiableRunnableRegex) + + if (!match) return + + const matchingWord = match[1] + const index = text.indexOf(matchingWord) + matchingWord.length + + const position = new Position(selection.start.line, index) + edit.insert(position, textToAdd) + }) +} + +const removeFromSelections = (textToRemove) => () => { + editEachSelection((editor, edit, selection) => { + const text = getLineText(editor, selection) + const index = text.indexOf(textToRemove) + if (index < 0) return + + const start = new Position(selection.start.line, index) + const end = new Position(selection.start.line, index + textToRemove.length) + const range = new Range(start, end) + edit.delete(range) + }) +} + +const removeAll = (regex) => () => { + const editor = window.activeTextEditor + if (!editor) return + + const start = new Position(0, 0) + const lastLine = editor.document.lineCount - 1 + const end = new Position(lastLine, editor.document.lineAt(lastLine).range.end.character) + const range = new Range(start, end) + + const text = editor.document.getText(range) + + return editor.edit((edit) => { + const withTextRemoved = text.replace(regex, (__, runnable) => { + return runnable + }) + edit.replace(range, withTextRemoved) + }) +} + +const moveOnly = () => { + return removeAll(onlyRegex)().then(() => { + return addToSelections('.only')() + }) +} + +module.exports = { + activate (context) { + const addOnlyCommand = commands.registerCommand('extension.addOnly', addToSelections('.only')) + context.subscriptions.push(addOnlyCommand) + const removeOnlyCommand = commands.registerCommand('extension.removeOnly', removeFromSelections('.only')) + context.subscriptions.push(removeOnlyCommand) + const removeAllOnlysCommand = commands.registerCommand('extension.removeAllOnlys', removeAll(onlyRegex)) + context.subscriptions.push(removeAllOnlysCommand) + const moveOnlyCommand = commands.registerCommand('extension.moveOnly', moveOnly) + context.subscriptions.push(moveOnlyCommand) + + const addSkipCommand = commands.registerCommand('extension.addSkip', addToSelections('.skip')) + context.subscriptions.push(addSkipCommand) + const removeSkipCommand = commands.registerCommand('extension.removeSkip', removeFromSelections('.skip')) + context.subscriptions.push(removeSkipCommand) + const removeAllSkipsCommand = commands.registerCommand('extension.removeAllSkips', removeAll(skipRegex)) + context.subscriptions.push(removeAllSkipsCommand) + }, +} diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..b7caa7d --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "lib": [ + "es6" + ] + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..c5d40b8 --- /dev/null +++ b/package.json @@ -0,0 +1,89 @@ +{ + "name": "test-utils", + "displayName": "Test Utils", + "description": "Add, remove, and move .only in tests", + "version": "0.0.1", + "publisher": "chrisbreiding", + "engines": { + "vscode": "^1.17.0" + }, + "scripts": { + "lint": "eslint --fix *.js" + }, + "activationEvents": [ + "onCommand:extension.addOnly", + "onCommand:extension.removeOnly", + "onCommand:extension.removeAllOnlys", + "onCommand:extension.moveOnly", + "onCommand:extension.addSkip", + "onCommand:extension.removeSkip", + "onCommand:extension.removeAllSkips" + ], + "main": "./extension", + "contributes": { + "commands": [ + { + "command": "extension.addOnly", + "title": "Add only" + },{ + "command": "extension.removeOnly", + "title": "Remove only" + },{ + "command": "extension.removeAllOnlys", + "title": "Remove all onlys" + },{ + "command": "extension.moveOnly", + "title": "Move only" + },{ + "command": "extension.addSkip", + "title": "Add skip" + },{ + "command": "extension.removeSkip", + "title": "Remove skip" + },{ + "command": "extension.removeAllSkips", + "title": "Remove all skips" + } + ], + "keybindings": [ + { + "command": "extension.addOnly", + "key": "ctrl+alt+a ctrl+alt+o", + "when": "editorTextFocus" + },{ + "command": "extension.removeOnly", + "key": "ctrl+alt+r ctrl+alt+o", + "when": "editorTextFocus" + },{ + "command": "extension.removeAllOnlys", + "key": "ctrl+alt+x ctrl+alt+o", + "when": "editorTextFocus" + },{ + "command": "extension.moveOnly", + "key": "ctrl+alt+m ctrl+alt+o", + "when": "editorTextFocus" + },{ + "command": "extension.addSkip", + "key": "ctrl+alt+a ctrl+alt+s", + "when": "editorTextFocus" + },{ + "command": "extension.removeSkip", + "key": "ctrl+alt+r ctrl+alt+s", + "when": "editorTextFocus" + },{ + "command": "extension.removeAllSkips", + "key": "ctrl+alt+x ctrl+alt+s", + "when": "editorTextFocus" + } + ] + }, + "devDependencies": { + "typescript": "^2.5.2", + "vscode": "^1.1.5", + "mocha": "^3.5.0", + "eslint": "^4.6.1", + "eslint-plugin-cypress-dev": "^1.0.0", + "@types/node": "^7.0.0", + "@types/mocha": "^2.2.42" + } +} \ No newline at end of file