Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to have test framework typescript declarations only resolve within test files in VS Code?

This question applies to each of the following source file organisation strategies:

Tests completely separate

/src
/tests

Tests per feature

/src/feature/
/src/feature/__tests__

Tests per file

/src/feature/foo.ts
/src/feature/foo.test.ts

Installing @mocha/types makes those test-only declarations available as valid identifiers throughout the entire codebase. It's easy enough to update tsconfig.json and specify "types": [] to exclude it, but the moment you manually reference it in just one single file, whether via import 'mocha' or /// <reference types="mocha" />, suddenly it infects the entire codebase again.

Is there any way at all to have type declarations that are only valid for unit tests resolve correctly in test files and appear as invalid in other source files?

Note that I'm using VS Code here. Naturally I could have a separate tsconfig file for external build setups, such as via gulp or whatever, but I'm editing the actual code in VS Code, and the red squigglies and "problems" don't seem to be resolvable. Either I accept invalid autocompletion of unit test identifiers throughout the entire codebase, or my unit tests show up with module resolution errors.

like image 466
Nathan Ridley Avatar asked May 13 '17 00:05

Nathan Ridley


People also ask

Is Vscode written in TypeScript?

Visual Studio Code includes TypeScript language support but does not include the TypeScript compiler, tsc . You will need to install the TypeScript compiler either globally or in your workspace to transpile TypeScript source code to JavaScript ( tsc HelloWorld. ts ). You can test your install by checking the version.


1 Answers

Not an easy one... this is the best I've come up with. You'll not like it, but it works:

Create a file mocha.ts, where you'll want to re-export the runtime-available globals and (the bit you won't like) redefine the typings for them - going as far as you want to go:

declare var global: any;

export const describe = global.describe as (msg: string, cb: () => void) => void;
export const it = global.it as (msg: string, cb: () => void) => void;

Now, just import describe and it from ./mocha and away you go. Doesn't pollute the global namespace.

enter image description here

The type redefine is fugly; but you could simply c&p the relevant parts you want to use from definitely typed. I don't think you'll be able to import the typings directly and not incur the globals because its typed that way.

Something else I looked at was to make use of the mocha "require" as the documentation alludes to you being able to grab describe like so: require('mocha').describe... I tried this (running with mocha cli) and couldn't get it to work. see: http://mochajs.org/#require Still... I think you get hit with the global types anyway.

Addendum

complete mocha.ts with copied typings:

declare var global: any;

export const describe = global.describe as IContextDefinition;
export const it = global.it as ITestDefinition;

// following is from DefinitelyTyped

interface IContextDefinition {
  (description: string, callback: (this: ISuiteCallbackContext) => void): ISuite;
  only(description: string, callback: (this: ISuiteCallbackContext) => void): ISuite;
  skip(description: string, callback: (this: ISuiteCallbackContext) => void): void;
  timeout(ms: number): void;
}

interface ISuiteCallbackContext {
  timeout(ms: number): this;
  retries(n: number): this;
  slow(ms: number): this;
}

interface ISuite {
  parent: ISuite;
  title: string;

  fullTitle(): string;
}

interface ITestDefinition {
  (expectation: string, callback?: (this: ITestCallbackContext, done: MochaDone) => any): ITest;
  only(expectation: string, callback?: (this: ITestCallbackContext, done: MochaDone) => any): ITest;
  skip(expectation: string, callback?: (this: ITestCallbackContext, done: MochaDone) => any): void;
  timeout(ms: number): void;
  state: "failed" | "passed";
}
interface ITestCallbackContext {
  skip(): this;
  timeout(ms: number): this;
  retries(n: number): this;
  slow(ms: number): this;
  [index: string]: any;
}

interface MochaDone {
  (error?: any): any;
}

interface ITest extends IRunnable {
  parent: ISuite;
  pending: boolean;
  state: 'failed' | 'passed' | undefined;

  fullTitle(): string;
}

interface IRunnable {
  title: string;
  fn: Function;
  async: boolean;
  sync: boolean;
  timedOut: boolean;
  timeout(n: number): this;
}
like image 99
Meirion Hughes Avatar answered Oct 05 '22 23:10

Meirion Hughes