Giter VIP home page Giter VIP logo

jest-environment-vscode-extension's Introduction

jest-environment-vscode-extension

๐ŸŽช The best way to run and write tests for your VSCode extension

Key Features:

  • run tests using Jest
  • built-in API making tests simpler to write and read
  • zero JS configuration

Setup

1 - Install the following packages:

npm install jest jest-environment-vscode-extension @types/jest @types/jest-environment-vscode-extension --save-dev

2 - On .vscode/tasks.json, add the following within the tasks array:

{
  "label": "create-test-workspace-folder",
  "type": "shell",
  "command": "mkdir",
  "args": ["-p", "test-workspace"],
  "presentation": {
    "reveal": "silent",
    "revealProblems": "onProblem"
  }
},
{
  "label": "remove-test-workspace-folder",
  "type": "shell",
  "command": "rm",
  "args": ["-rf", "test-workspace"],
  "presentation": {
    "reveal": "silent",
    "revealProblems": "onProblem"
  }
},
{
  "label": "insert-monkey-patch-allow-mocks",
  "type": "shell",
  "command": "node ./node_modules/.bin/insert-monkey-patch-allow-mocks ${workspaceFolder}",
  "presentation": {
    "reveal": "silent",
    "revealProblems": "onProblem"
  },
},
{
  "label": "drop-monkey-patch-allow-mocks",
  "type": "shell",
  "command": "node ./node_modules/.bin/drop-monkey-patch-allow-mocks ${workspaceFolder}",
  "presentation": {
    "reveal": "silent",
    "revealProblems": "onProblem"
  }
},
{
  "label": "pre-run-tests",
  "dependsOrder": "sequence",
  "dependsOn": [
    "remove-test-workspace-folder",
    "create-test-workspace-folder",
    "build",
    "insert-monkey-patch-allow-mocks"
  ],
  "presentation": {
    "reveal": "silent",
    "revealProblems": "onProblem"
  }
},
{
  "label": "post-run-tests",
  "dependsOn": [
    "remove-test-workspace-folder",
    "drop-monkey-patch-allow-mocks"
  ],
  "presentation": {
    "reveal": "silent",
    "revealProblems": "onProblem"
  }
}

3 - On .vscode/launch.json, add the following within the configurations array:

{
  "name": "Test Extension - No Workspace",
  "preLaunchTask": "pre-run-tests",
  "postDebugTask": "post-run-tests",
  "type": "extensionHost",
  "request": "launch",
  "runtimeExecutable": "${execPath}",
  "args": [
    "/no-workspace",
    "--disable-extensions",
    "--extensionDevelopmentPath=${workspaceFolder}",
    "--extensionTestsPath=${workspaceFolder}/node_modules/.bin/vscode-tests-runner"
  ],
  "env": {
    "VSCODE_TESTS_PATH": "${workspaceFolder}/out/tests/no-workspace/"
  },
  "outFiles": ["${workspaceFolder}/out/tests/**/*.js"]
},
{
  "name": "Test Extension - With Workspace",
  "preLaunchTask": "pre-run-tests",
  "postDebugTask": "post-run-tests",
  "type": "extensionHost",
  "request": "launch",
  "runtimeExecutable": "${execPath}",
  "args": [
    "${workspaceFolder}/test-workspace",
    "--disable-extensions",
    "--extensionDevelopmentPath=${workspaceFolder}",
    "--extensionTestsPath=${workspaceFolder}/node_modules/.bin/vscode-tests-runner"
  ],
  "env": {
    "VSCODE_TESTS_PATH": "${workspaceFolder}/out/tests/with-workspace/"
  },
  "outFiles": ["${workspaceFolder}/out/tests/**/*.js"]
}

4 - Now, write your tests that depend on a workspace within tests/with-workspace. And if it doesn't need it, you can write them within tests/no-workspace.

Setup finished! ๐ŸŽ‰

Now you can run the tests using VSCode:

Running on CI

Running by VSCode is great for development since it's quick and can use breakpoints. But we need to do one more step to can run on CI.

1 - On package.json, add the following within the scripts object:

"tests:ci:no-workspace": "vscode-electron-starter no-workspace insiders out/tests/no-workspace",
"tests:ci:with-workspace": "vscode-electron-starter with-workspace insiders out/tests/with-workspace"

The penultimate parameter is the VSCode version being used. You can use stable, insiders, or a version number (e.g., 1.32.0). The last parameter is the path of the test.

2 - Now you can call these scripts on CI. Following, a script to run on GitHub actions:

on:
  push:
    branches:

jobs:
  test:
    name: Test
    strategy:
      matrix:
        os: [ubuntu-latest]
    runs-on: ${{ matrix.os }}
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Setup Node.js
        uses: actions/setup-node@v1
        with:
          node-version: 16
      - name: Install dependencies
        run: npm install
      - name: Build
        run: npm run build
      - name: Run test - No workspace
        uses: GabrielBB/[email protected]
        with:
          run: npm run tests:ci:no-workspace
      - name: Run test - With workspace
        uses: GabrielBB/[email protected]
        with:
          run: npm run tests:ci:with-workspace

Writing your first test using jest-environment-vscode-extension

It's almost the same idea as writing any other test using Jest, but we have a powerful API focused on VSCode.

Let's do a walkthrough writing a simple test. We want to test if the "go to the definition" works well at the second x:

const x = 42
console.log(x)

1 - Firstly, our test doesn't depend on a workspace. Then we'll write it at tests/no-workspace/definitions.test.ts. Usually, a test depends on a workspace if it interacts with other files on the same workspace.

2 - Let's write the test itself:

// get some things from the global variable `vscode`
const { Position, Range } = vscode

describe('#Definition', () => {
  it('on message interpolation', () => {
    // create a new file
    return using({
      files: {
        'index.js': dedent(`
          const x = 42
          console.log(x)
        `),
      }},
      async (mapFileToDoc) => {
        // on the file `index.js`, take the definitions at 1:12 (the `x` within the `console.log`)
        const definitions = await take.definitions(mapFileToDoc['index.js'], new Position(1, 12))

        // assert that it's as the expected
        expect(definitions).toHaveLength(1)
        expect(definitions[0]).toMatchObject({
          originSelectionRange: new Range(new Position(1, 12), new Position(1, 13)),
          targetRange: new Range(new Position(0, 12), new Position(0, 0)),
          targetSelectionRange: new Range(new Position(0, 6), new Position(0, 7)),
        })
      })
  })
})

On the above test, we used some variables injected by jest-environment-vscode-extension: vscode, using, dedent, and take. Think of them as the Jest's describe or it, but focused on helping you while working with VSCode.

Let's talk about them!

API

using

Our most useful function.

It creates the files and, optionally, can mock VSCode's functions. It receives a callback and, when it's finished, clear the files and mocks.

Files

You can create as many files as needed, and their TextDocument is sent to the callback:

using(
  {
    files: {
      'index.js': '"example";',
      'foo.js': '1;',
      'bar.js': '2;',
    },
  },
  async (mapFileToDoc) => {
    mapFileToDoc['index.js'] // TextDocument
    mapFileToDoc['foo.js']   // TextDocument
    mapFileToDoc['bar.js']   // TextDocument
  }
)

Mocks

There are some VSCode features in which we can't manipulate, such as the window.showQuickPick. But no worries! We can easily mock it:

using(
  {
    files: {
      'index.js': '"example";',
    },
    mocks: {
      'window.showQuickPick': async () => 'My Option',
    },
  },
  async (mapFileToDoc) => {

  }
)

Now, if the extension calls window.showQuickPick it'll return Promise<'My Option'>.

But there is a rule to use mocks: You should ensure that the extension is initialized. For example, let's say that your extension is initialized only when there is a .ml file in the workspace:

"activationEvents": [
  "workspaceContains:**/*.ml"
]

So you should run the tests using workspace and create at least one .ml file:

using(
  {
    files: {
      'main.ml': 'let hello () = print_endline "hey there"',
    },
    mocks: {
      'window.showQuickPick': async () => 'My Option',
    },
  },
  async (mapFileToDoc) => {

  }
)

dedent

Function to remove indentation. Helpful with using.

vscode

It's the same vscode used by the extension itself. So you can use it to manipulate the VSCode.

For example, if you want to open and show a document, you should do:

const { workspace, window } = vscode

const doc = await workspace.openTextDocument(mapFileToDoc['index.js'])
await window.showTextDocument(doc)

It doesn't export the types. If you want them, you should do:

import type { Position } from 'vscode'

const printPosition = (position: Position) => {
  console.log({
    line: position.line,
    character: position.character,
  })
}

take

It exposes many helper functions to take values from the VSCode. Just use TypeScript's intellisense to explore what it has.

waitFor

It exposes a helper function to wait for something.

For example, if your extension takes time to initialize, it can be useful:

const waitForDocumentSymbols = async (doc, position) => {
  return await waitFor(async () => {
    const hovers = await take.hovers(doc, position)
    expect(hovers).toHaveLength(1)
    return hovers
  })
}

describe("#Document Symbol", () => {
  it("includes function declaration", () => {
    return using(
      {
        files: {
          'main.ml': 'let hello () = print_endline "hey there"',
        },
      },
      async (mapFileToDoc) => {
        const symbols = await waitForDocumentSymbols(mapFileToDoc['main.ml'])

        expect(symbols[0]).toMatchObject({
            name: 'hello',
            detail: 'unit -> unit',
        })
      }
    )
  })
})

Who is using


vscode-fluent, extension for Fluent, the correct-by-design l10n programming language
Add your project here

jest-environment-vscode-extension's People

Contributors

macabeus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.