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

1

6

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.

Pay answered 13/5, 2017 at 0:0 Comment(0)
V
1

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;
}
Villanovan answered 23/5, 2017 at 22:33 Comment(1)
A valiant attempt, and probably good enough for my purposes. I imagine if there were a better way, someone else might have replied by now...Pay

© 2022 - 2024 — McMap. All rights reserved.