├── .editorconfig
├── .eslintrc
├── .gitignore
├── .travis.yml
├── .vscode
├── launch.json
└── settings.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── appveyor.yml
├── extension.js
├── jsconfig.json
├── package.json
├── test
├── extension.test.js
├── index.js
└── module-loader.js
├── typings
├── node.d.ts
└── vscode-typings.d.ts
└── vsc-extension-quickstart.md
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | trim_trailing_whitespace = true
--------------------------------------------------------------------------------
/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "eslint:recommended",
4 | "plugin:node/recommended"
5 | ],
6 | "plugins": [
7 | "node"
8 | ],
9 | "env": {
10 | "node": true
11 | },
12 | "rules": {
13 | "node/no-unpublished-require": [
14 | "error",
15 | {
16 | "allowModules": [
17 | "vscode"
18 | ]
19 | }
20 | ],
21 | "node/no-missing-require": [
22 | "error",
23 | {
24 | "allowModules": [
25 | "vscode"
26 | ]
27 | }
28 | ],
29 | "semi": "error",
30 | "no-console": "warn",
31 | "node/no-unsupported-features": [
32 | "error",
33 | {
34 | "version": 5
35 | }
36 | ]
37 | }
38 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.vsix
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | os:
4 | - osx
5 | - linux
6 |
7 | before_install:
8 | - if [ $TRAVIS_OS_NAME == "linux" ]; then
9 | export CXX="g++-4.9" CC="gcc-4.9" DISPLAY=:99.0;
10 | sh -e /etc/init.d/xvfb start;
11 | sleep 3;
12 | fi
13 |
14 | install:
15 | - npm install
16 | - npm run vscode:prepublish
17 |
18 | script:
19 | - npm test --silent
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that launches the extension inside a new window
2 | {
3 | "version": "0.1.0",
4 | "configurations": [
5 | {
6 | "name": "Launch Extension",
7 | "type": "extensionHost",
8 | "request": "launch",
9 | "runtimeExecutable": "${execPath}",
10 | "args": [
11 | "--extensionDevelopmentPath=${workspaceRoot}"
12 | ],
13 | "stopOnEntry": false
14 | },
15 | {
16 | "name": "Launch Tests",
17 | "type": "extensionHost",
18 | "request": "launch",
19 | "runtimeExecutable": "${execPath}",
20 | "args": [
21 | "--extensionDevelopmentPath=${workspaceRoot}",
22 | "--extensionTestsPath=${workspaceRoot}/test"
23 | ],
24 | "stopOnEntry": false
25 | }
26 | ]
27 | }
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | // 既定の設定とユーザー設定を上書きするには、このファイル内に設定を挿入します
2 | {
3 | "typescript.tsdk": "node_modules/typescript/lib"
4 | }
--------------------------------------------------------------------------------
/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | .vscode-test/**
3 | typings/**
4 | test/**
5 | .gitignore
6 | jsconfig.json
7 | vsc-extension-quickstart.md
8 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.2.0 - (2016/11/9)
2 | **Closed issues:**
3 | - Add configuration. `sequence.replaceSelection`. [\#1](https://github.com/tomoki1207/vscode-input-sequence/issues/1)
4 |
5 | ## 0.1.0 - (2016/9/28)
6 | First Release
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 tomoki1207
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vscode-input-sequence
2 |
3 | [](https://travis-ci.org/tomoki1207/vscode-input-sequence)
4 | [](https://ci.appveyor.com/project/tomoki1207/vscode-input-sequence)
5 | [](https://raw.githubusercontent.com/tomoki1207/vscode-input-sequence/master/LICENSE)
6 |
7 | [sequential-number](https://atom.io/packages/sequential-number) for VSCode.
8 |
9 | 
10 |
11 | ## Usage
12 |
13 | ### Default Keymap
14 | ctrl + alt + 0 => Open the input panel !
15 |
16 | ### Syntax Rules
17 |
18 | ` : : `
19 |
20 | | Key | Default | Definition |
21 | | :------------------------------------ | :------ | :---------------------------------------------------------------------------------------------------------------------------------------------- |
22 | | **start** | `""` | It specifies the number that you start typing an integer. |
23 | | **operator** (optional) | `+` | It specifies the generation rules of consecutive numbers in the `+` or `-`. The sign of the increment(`++`) and decrement(`--`) also available. |
24 | | **step** (optional) | `1` | It specifies the integer to be added or subtracted. |
25 | | **digit** (optional) | `0` | It specifies of the number of digits in the integer. |
26 | | **radix** (optional) | `10` | It specifies an integer between 2 and 36 that represents radix. |
27 |
28 | ### More infomation and Examples
29 |
30 | See [sequential-number](https://atom.io/packages/sequential-number).
31 |
32 | ### Configuration
33 |
34 | + `sequence.replaceSelection`
35 | Replace initial selections by sequence or not.
36 | 
37 |
38 | ## LICENSE
39 | [LICENSE](./LICENSE)
40 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | environment:
2 | ELECTRON_RUN_AS_NODE: 1
3 | VSCODE_BUILD_VERBOSE: true
4 |
5 | install:
6 | - ps: Install-Product node 6.6.0 x64
7 | - npm install -g npm
8 | - npm install -g gulp mocha
9 |
10 | build_script:
11 | - npm install
12 |
13 | test_script:
14 | - node --version
15 | - npm --version
16 | - npm test --silent
--------------------------------------------------------------------------------
/extension.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var vscode = require('vscode');
3 | var Range = vscode.Range;
4 | var Position = vscode.Position;
5 | var syntaxRegex = /^([+\-]?[\da-fA-F]+(?:\.\d+)?)\s*([+\-]|(?:\+\+|\-\-))?\s*(\d+)?\s*(?:\:\s*(\d+))?\s*(?:\:\s*(\d+))?$/;
6 |
7 | // this method is called when your extension is activated
8 | function activate(context) {
9 | var insertCmd = vscode.commands.registerCommand('insertSequentialNumbers', function () {
10 | var editor = vscode.window.activeTextEditor;
11 | var initialSelections = editor.selections.sort(sortSelection);
12 | var inputOptions = {};
13 | var undoStopBefore = true;
14 | inputOptions.placeHolder = " : : ";
15 | inputOptions.validateInput = function (param) {
16 | if (param === "") {
17 | perform(initialSelections, editor, {}, { undoStopBefore: undoStopBefore, undoStopAfter: false });
18 | return;
19 | }
20 | var test = parseInput(param);
21 | if (!test) {
22 | return 'Syntax error. The rule is " : : ".';
23 | }
24 | // realtime simulate
25 | perform(initialSelections, editor, test, { undoStopBefore: undoStopBefore, undoStopAfter: false });
26 | undoStopBefore &= false;
27 | };
28 | vscode.window.showInputBox(inputOptions)
29 | .then(function (value) {
30 | if (!value && value !== 0) {
31 | // undo
32 | if (!undoStopBefore) {
33 | vscode.commands.executeCommand("undo");
34 | }
35 | return;
36 | }
37 | // confirm input.
38 | var options = parseInput(value);
39 | perform(initialSelections, editor, options, { undoStopBefore: false, undoStopAfter: true });
40 | });
41 | });
42 |
43 | context.subscriptions.push(insertCmd);
44 | }
45 | exports.activate = activate;
46 |
47 | // this method is called when your extension is deactivated
48 | function deactivate() {
49 | }
50 | exports.deactivate = deactivate;
51 |
52 | function sortSelection(a, b) {
53 | return a.anchor.line - b.anchor.line || a.anchor.character - b.anchor.character;
54 | }
55 |
56 | function perform(initial, editor, options, undoStop) {
57 | var currentSelection = editor.selections.sort(sortSelection);
58 | var replaceSelection = vscode.workspace.getConfiguration("sequence").replaceSelection;
59 | editor.edit(function (builder) {
60 | initial.forEach(function (selection, index) {
61 | var endOfInitialSelection = replaceSelection ? selection.start.character : selection.end.character + (currentSelection[index].start.character - selection.start.character);
62 | builder.replace(new Range(new Position(selection.end.line, endOfInitialSelection), currentSelection[index].end), calculate(index, options));
63 | });
64 | }, undoStop);
65 | }
66 |
67 | function parseInput(input) {
68 | var matches = input.match(syntaxRegex);
69 | if (!matches) {
70 | return null;
71 | }
72 |
73 | var radix = matches[5] ? parseInt(matches[5], 10) : 10;
74 | var start = parseInt(matches[1], radix);
75 | var operator = matches[2] || "+";
76 | var step = isNaN(matches[3]) ? 1 : parseInt(matches[3], 10);
77 |
78 | var digit = parseInt(matches[4], 10);
79 | if (isNaN(digit)) {
80 | digit = (start.toString() === matches[1]) ? 0 : matches[1].length;
81 | if (/^[+\-]/.test(matches[1])) {
82 | digit = Math.max(digit - 1, 0);
83 | }
84 | }
85 | return {
86 | start: start,
87 | digit: digit,
88 | operator: operator,
89 | step: step,
90 | radix: radix,
91 | input: input
92 | };
93 | }
94 |
95 | function calculate(index, options) {
96 | var value = NaN;
97 | switch (options.operator) {
98 | case "++":
99 | value = options.start + index;
100 | break;
101 | case "--":
102 | value = options.start - index;
103 | break;
104 | case "+":
105 | value = options.start + (index * options.step);
106 | break;
107 | case "-":
108 | value = options.start - (index * options.step);
109 | break;
110 | default:
111 | return "";
112 | }
113 |
114 | value = paddingZero(value, options.digit, options.radix);
115 | var hasAlpha = options.input.match(/([a-fA-F])/);
116 | if (hasAlpha) {
117 | // for hex.
118 | return hasAlpha[1] == hasAlpha[1].toLowerCase() ? value.toLowerCase() : value.toUpperCase();
119 | }
120 | return value;
121 | }
122 |
123 | function paddingZero(num, dig, radix) {
124 | if (!dig) {
125 | dig = 0;
126 | }
127 | if (!radix) {
128 | radix = 10;
129 | }
130 | var number = num.toString(radix);
131 | var numAbs = number.replace("-", "");
132 | var digit = Math.max(numAbs.length, dig);
133 | var result = "";
134 | if (0 <= number.indexOf("-")) {
135 | result += "-";
136 | }
137 | result += (Array(digit).join("0") + numAbs).slice(digit * -1);
138 | return result;
139 | }
--------------------------------------------------------------------------------
/jsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "ES5",
5 | "noLib": true
6 | },
7 | "exclude": [
8 | "node_modules"
9 | ]
10 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vscode-input-sequence",
3 | "displayName": "vscode-input-sequence",
4 | "description": "sequential-number in vscode",
5 | "version": "0.2.0",
6 | "publisher": "tomoki1207",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/tomoki1207/vscode-input-sequence.git"
10 | },
11 | "bugs": {
12 | "url": "https://github.com/tomoki1207/vscode-input-sequence/issues"
13 | },
14 | "license": "MIT",
15 | "engines": {
16 | "vscode": "^1.5.0"
17 | },
18 | "categories": [
19 | "Other"
20 | ],
21 | "activationEvents": [
22 | "onCommand:insertSequentialNumbers"
23 | ],
24 | "main": "./extension",
25 | "contributes": {
26 | "commands": [
27 | {
28 | "command": "insertSequentialNumbers",
29 | "title": "Insert Sequential number"
30 | }
31 | ],
32 | "configuration": {
33 | "title": "Input-Sequence configuration",
34 | "properties": {
35 | "sequence.replaceSelection": {
36 | "type": "boolean",
37 | "default": false
38 | }
39 | }
40 | },
41 | "keybindings": [
42 | {
43 | "command": "insertSequentialNumbers",
44 | "key": "ctrl+alt+0",
45 | "mac": "cmd+alt+0",
46 | "when": "editorTextFocus"
47 | }
48 | ]
49 | },
50 | "scripts": {
51 | "postinstall": "node ./node_modules/vscode/bin/install",
52 | "test": "node ./node_modules/vscode/bin/test",
53 | "package": "vsce package"
54 | },
55 | "devDependencies": {
56 | "eslint": "^3.6.0",
57 | "eslint-plugin-node": "^2.1.1",
58 | "typescript": "^2.0.3",
59 | "vm": "^0.1.0",
60 | "vsce": "^1.14.0",
61 | "vscode": "^0.11.0"
62 | }
63 | }
--------------------------------------------------------------------------------
/test/extension.test.js:
--------------------------------------------------------------------------------
1 | /* global suite, test */
2 |
3 | var assert = require('assert');
4 | var vscode = require('vscode');
5 | var ext = require('../extension');
6 | var loadModule = require('./module-loader').loadModule;
7 |
8 | suite("Extension Tests", function () {
9 |
10 | var appContext = loadModule(__dirname + '/../extension.js');
11 |
12 | var invalidParseResult = function (input) {
13 | assert.equal(null, appContext.parseInput(input));
14 | };
15 |
16 | var expectParseResult = function (input, expected) {
17 | var options = appContext.parseInput(input);
18 | assert.ok(options);
19 | var result = expected.map(function (value, index) {
20 | return appContext.calculate(index, options);
21 | });
22 | assert.deepEqual(result, expected);
23 | };
24 |
25 | suite("Invalid input", function () {
26 | test("''", function () {
27 | invalidParseResult("");
28 | });
29 | test("--0011 ", function () {
30 | invalidParseResult("--0011");
31 | });
32 | test("++1", function () {
33 | invalidParseResult("++1");
34 | });
35 | test("+1/", function () {
36 | invalidParseResult("+1/");
37 | });
38 | test("1%1", function () {
39 | invalidParseResult("1%1");
40 | });
41 | test("1*2", function () {
42 | invalidParseResult("1*2");
43 | });
44 | test("-2+++", function () {
45 | invalidParseResult("-2+++");
46 | });
47 | test("+34hoge", function () {
48 | invalidParseResult("+34hoge");
49 | });
50 | test("alphabet", function () {
51 | invalidParseResult("alphabet");
52 | });
53 | });
54 |
55 | suite("Valid input", function () {
56 | suite("+ operator", function () {
57 | test("0", function () {
58 | expectParseResult("0", ["0", "1", "2", "3", "4"]);
59 | });
60 | test("1", function () {
61 | expectParseResult("1", ["1", "2", "3", "4", "5"]);
62 | });
63 | test("-1", function () {
64 | expectParseResult("-1", ["-1", "0", "1", "2", "3"]);
65 | });
66 | test("1 + 2", function () {
67 | expectParseResult("1 + 2", ["1", "3", "5", "7", "9"]);
68 | });
69 | test("5++", function () {
70 | expectParseResult("5++", ["5", "6", "7", "8", "9"]);
71 | });
72 | test("015 + 1", function () {
73 | expectParseResult("015 + 1", ["015", "016", "017", "018", "019"]);
74 | });
75 | test("09 + 65", function () {
76 | expectParseResult("09 + 65", ["09", "74", "139", "204", "269"]);
77 | });
78 | test("-20+12", function () {
79 | expectParseResult("-20+12", ["-20", "-8", "4", "16", "28"]);
80 | });
81 | test("-10 + 1 : 2", function () {
82 | expectParseResult("-10 + 1 : 2", ["-10", "-09", "-08", "-07", "-06"]);
83 | });
84 | test("-9 + 1000:3", function () {
85 | expectParseResult("-9 + 1000:3", ["-009", "991", "1991", "2991", "3991"]);
86 | });
87 | });
88 | suite("- operator", function () {
89 | test("0 -", function () {
90 | expectParseResult("0 -", ["0", "-1", "-2", "-3", "-4"]);
91 | });
92 | test("10 - 3", function () {
93 | expectParseResult("10 - 3", ["10", "7", "4", "1", "-2"]);
94 | });
95 | test("15--", function () {
96 | expectParseResult("15--", ["15", "14", "13", "12", "11"]);
97 | });
98 | test("0020 - 2", function () {
99 | expectParseResult("0020 - 2", ["0020", "0018", "0016", "0014", "0012"]);
100 | });
101 | test("-003120 - 21", function () {
102 | expectParseResult("-003120 - 21", ["-003120", "-003141", "-003162", "-003183", "-003204"]);
103 | });
104 | test("-8 -90 : 2", function () {
105 | expectParseResult("-8 -90 : 2", ["-08", "-98", "-188", "-278", "-368"]);
106 | });
107 | });
108 | suite("radix 2", function () {
109 | test("0 + 1 : 1 : 2", function () {
110 | expectParseResult("0 + 1 : 1 : 2", ["0", "1", "10", "11", "100"]);
111 | });
112 | test("-11 - 2 : 2 : 2", function () {
113 | expectParseResult("-11 - 2 : 2 : 2", ["-11", "-101", "-111", "-1001", "-1011"]);
114 | });
115 | });
116 | suite("radix 8", function () {
117 | test("6 + 1 : 2 : 8", function () {
118 | expectParseResult("6 + 1 : 2 : 8", ["06", "07", "10", "11", "12"]);
119 | });
120 | test("-5 - 5 : 2 : 8", function () {
121 | expectParseResult("-5 - 5 : 2 : 8", ["-05", "-12", "-17", "-24", "-31"]);
122 | });
123 | });
124 | suite("radix 16", function () {
125 | test("a + 6 : 1 : 16", function () {
126 | expectParseResult("a + 6 : 1 : 16", ["a", "10", "16", "1c", "22"]);
127 | });
128 | test("C4b + 9 : 6 : 16", function () {
129 | expectParseResult("C4b + 9 : 6 : 16", ["000C4B", "000C54", "000C5D", "000C66", "000C6F"]);
130 | });
131 | test("b - 12 : 2 : 16", function () {
132 | expectParseResult("b - 12 : 2 : 16", ["0b", "-01", "-0d", "-19", "-25"]);
133 | });
134 | });
135 | });
136 | });
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | //
2 | // PLEASE DO NOT MODIFY / DELETE UNLESS YOU KNOW WHAT YOU ARE DOING
3 | //
4 | // This file is providing the test runner to use when running extension tests.
5 | // By default the test runner in use is Mocha based.
6 | //
7 | // You can provide your own test runner if you want to override it by exporting
8 | // a function run(testRoot: string, clb: (error:Error) => void) that the extension
9 | // host can call to run the tests. The test runner is expected to use console.log
10 | // to report the results back to the caller. When the tests are finished, return
11 | // a possible error to the callback or null if none.
12 |
13 | var testRunner = require('vscode/lib/testrunner');
14 |
15 | // You can directly control Mocha options by uncommenting the following lines
16 | // See https://github.com/mochajs/mocha/wiki/Using-mocha-programmatically#set-options for more info
17 | testRunner.configure({
18 | ui: 'tdd', // the TDD UI is being used in extension.test.js (suite, test, etc.)
19 | useColors: true // colored output from test results
20 | });
21 |
22 | module.exports = testRunner;
--------------------------------------------------------------------------------
/test/module-loader.js:
--------------------------------------------------------------------------------
1 | var vm = require('vm');
2 | var fs = require('fs');
3 | var path = require('path');
4 |
5 | exports.loadModule = function (filePath, mocks) {
6 | mocks = mocks || {};
7 |
8 | var resolveModule = function (module) {
9 | if (module.charAt(0) !== '.')
10 | return module;
11 | return path.resolve(path.dirname(filePath), module);
12 | };
13 |
14 | var exports = {};
15 | var context = {
16 | require: function (name) {
17 | return mocks[name] || require(resolveModule(name));
18 | },
19 | console: console,
20 | exports: exports,
21 | module: {
22 | exports: exports
23 | }
24 | };
25 | vm.runInNewContext(fs.readFileSync(filePath), context);
26 | return context;
27 | };
--------------------------------------------------------------------------------
/typings/node.d.ts:
--------------------------------------------------------------------------------
1 | ///
--------------------------------------------------------------------------------
/typings/vscode-typings.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/vsc-extension-quickstart.md:
--------------------------------------------------------------------------------
1 | # Welcome to your first VS Code Extension
2 |
3 | ## What's in the folder
4 | * This folder contains all of the files necessary for your extension
5 | * `package.json` - this is the manifest file in which you declare your extension and command.
6 | The sample plugin registers a command and defines its title and command name. With this information
7 | VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
8 | * `extension.js` - this is the main file where you will provide the implementation of your command.
9 | The file exports one function, `activate`, which is called the very first time your extension is
10 | activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
11 | We pass the function containing the implementation of the command as the second parameter to
12 | `registerCommand`.
13 |
14 | ## Get up and running straight away
15 | * press `F5` to open a new window with your extension loaded
16 | * run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`
17 | * set breakpoints in your code inside extension.ts to debug your extension
18 | * find output from your extension in the debug console
19 |
20 | ## Make changes
21 | * you can relaunch the extension from the debug toolbar after changing code in `extension.js`
22 | * you can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes
23 |
24 | ## Explore the API
25 | * you can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`
26 |
27 | ## Run tests
28 | * open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`
29 | * press `F5` to run the tests in a new window with your extension loaded
30 | * see the output of the test result in the debug console
31 | * make changes to `test/extension.test.js` or create new test files inside the `test` folder
32 | * by convention, the test runner will only consider files matching the name pattern `**.test.js`
33 | * you can create folders inside the `test` folder to structure your tests any way you want
--------------------------------------------------------------------------------