44

I've got a (desired) structure like this:

- tsconfig.json
- src
    - app.ts
- tests
    - appTest.ts
    - appTest.js
- dist
    - app.js

If there was no tests folder, a tsconfig.json like this would work fine:

{
    "compilerOptions": {
        "outDir":"dist"
    },
    "include" :[
        "src/**/*.ts"
    ]
}

However, if I add tests/**/*.ts to the include element, it also compiles my test files into dist and changes its folder structure (understandably, but undesirably).

Can I tell TypeScript compiler to include test files in the project to support things like refactoring but omit them from output to dist? Specifically, I'd like the .js to be compiled in the tests directory as suggested in the structure above.

Borek Bernard
  • 50,745
  • 59
  • 165
  • 240
  • 1
    [this is how I do it](https://github.com/MeirionHughes/aurelia-template-lint/blob/develop/gulpfile.js) - basically use gulp for compiling; replace the import paths to use the /dist rather than source for compiled tests. Works well, you get refactoring, and you get breakpoints in vscode on both the source and specs. – Meirion Hughes Oct 29 '16 at 22:18
  • Sounds good, could you convert it to an answer so that I can upvote it? :) – Borek Bernard Oct 29 '16 at 22:29
  • 1
    Possible duplicate of [Setting up tsconfig with spec/test folder](https://stackoverflow.com/questions/35470511/setting-up-tsconfig-with-spec-test-folder) – tkruse Nov 29 '18 at 02:30
  • Useful answer to the similar question: https://stackoverflow.com/a/61153019/3082178. – AKd Nov 19 '20 at 11:17

6 Answers6

13

You may use rootDirs option within tsconfig.json such as:

{
  "compilerOptions": {
    "rootDirs": [
      "src",
      "tests"
    ]
  }
}

This can be looked up at Typescript documents, on this page (search for Virtual Directories with rootDirs subtitle): Module Resolution

mcku
  • 1,351
  • 12
  • 23
  • This does not provide the desired outcome. The author states that he wants the directory structure in `dist` to be equivalent to the one inside of `src`. – fgblomqvist May 02 '22 at 14:58
10

Leave out the tests from tsconfig. Use mocha for testing, it does not need tsconfig to find the tests.

In package.config, use something like

"mocha": "mocha test/ --compilers ts:ts-node/register --recursive"

In tsconfig.json, do

"include": [
    "src/**/*.ts"
],

See Setting up tsconfig with spec/test folder

tkruse
  • 10,222
  • 7
  • 53
  • 80
  • I tried this solution with `mocha 6.1.4` and received the following error message: `ERROR: --compilers is DEPRECATED and no longer supported.` See https://git.io/vdcSr for migration information. – Jason Oct 07 '19 at 18:36
  • I used the following command: `mocha test/ --require ts-node/register --recursive` – Jason Oct 07 '19 at 18:50
8

How about separation of the tests completely?
Something like:

- scripts
    - tsconfig.json
    - src
        - app.ts
    - dist
        - app.js
- tests
    - tsconfig.json
    - src
        - appTest.ts
    - bin
        - appTest.js

Then scripts/tsconfig.json will look like:

"compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    ...
}

And tests/tsconfig.json will look like:

"compilerOptions": {
    "outDir": "bin",
    "rootDir": "src",
    ...
}

Edit

I checked this solution in webstorm, and there are two options:

(1) tests files importing from scripts/dist:

scripts/tsconfig.json:

"compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true
}

tests/tsconfig.json:

"compilerOptions": {
    "outDir": "bin",
    "rootDir": "src"
}

tests/src/apptest.ts:

import * as app from "../../scripts/dist/app";

...

result in tests/bin will look like:

- tests
    - bin
        - apptest.js

And when you refactor in scripts, let's say scripts/src/app.ts then it indeed has no effect on tests/src/apptest.ts, but compiling it will fail because of the refactor in the source.
So you'll know that you need to change the test files (though it won't be automatic).

(2) tests files importing from scripts/src:

The scripts/tsconfig.json file doesn't need to have the declaration option on because we'll use the source directly.

tests/src/apptest.ts:

import * as app from "../../scripts/dist/src";

...

Refactoring the source will change the test files as desired, but the output in tests/bin is this:

- tests
    - bin
        - scripts
            - src
                - app.js
        - tests
            - src
                - apptest.js

If you don't mind this structure for tests/bin then you get what you asked for without the need to use other tools.

Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
1

I've solved this kind of problem by using gulp and separating the compilation into two steps:

var gulp = require('gulp');
var ts = require('gulp-typescript');
var sourcemap = require('gulp-sourcemaps');
var replace = require('gulp-replace');

var path = require('path');
var merge = require('merge2');

var paths = {
  source: "source/",
  output: "dist/",
  spec: "spec/"
}

gulp.task('compile:typescript', function () {
  var project = ts.createProject('tsconfig.json', {
    typescript: require('typescript')
  });

  var tsResult =  gulp.src(paths.source + '**/*.ts')
    .pipe(sourcemap.init())
    .pipe(project());

  return merge([
    tsResult.dts.pipe(gulp.dest(paths.output)),
    tsResult.js //fix issue with relative source path
      .pipe(sourcemap.write('.', { 
        sourceRoot: function (file) {              
          var relative = path.relative(file.path, path.join(__dirname, "source"));
          var relativeSource = path.join(relative, 'source')
          return relativeSource;
        }
      }))
      .pipe(gulp.dest(paths.output))
  ]);
});

gulp.task('compile:tests', function () {
  var project = ts.createProject('tsconfig.json', {
    typescript: require('typescript')
  });

  var tsResult = gulp.src(paths.spec + '**/*spec.ts')
    .pipe(sourcemap.init())
    .pipe(project());

  return tsResult.js //fix issue with relative source path
    .pipe(sourcemap.write('.', {
      sourceRoot: function (file) {
        var relative = path.relative(file.path, path.join(__dirname, "spec"));
        var relativeSource = path.join(relative, 'spec')
        return relativeSource;
      }
    }))
    .pipe(replace(/(require\('\..\/source\/)/g, 'require(\'..\/dist\/'))
    .pipe(gulp.dest(paths.spec));
});

Your test source-code will import from the the actual source files; this means you get correct refactoring.

The source is compiled into the dist folder. The test files are compiled from the spec folder and stored in the same place. During the compiling of the spec files you "fix" the import paths so they link to the compiled output - using gulp-replace.

There are a few other things; prior a recent update to vscode, outDir could only point to one folder. So to get breakpoints on the tests as-well, the spec js had to be right next to the ts files. You could avoid that now if you want, although I've not tested it.

And here is the refactoring:

enter image description here

Meirion Hughes
  • 24,994
  • 12
  • 71
  • 122
1

A simple solution I found was creating an npm script which compiles and removes unwanted files/folders in the lib folder.

Include the following script in your package.json to remove the test folder in your lib directory after a ts compile.

"scripts": {
    "build": "./node_modules/.bin/tsc && rm -r ./lib/test",
},
Leroy Dunn
  • 361
  • 4
  • 7
1

Note: My answer is for if you are using Mocha as your test tool.

The relevant information is hidden on a few pages.

Firstly, on mocha's homepage:

--require , -r Require a module before loading the user interface or test files.

This is useful for: Compilers such as ... TypeScript via ts-node (using --require ts-node/register)

Hence, you should install ts-node via npm install ts-node --save-dev.


Secondly, on mocha's wiki.

I won't quote it, but you have to run:

$ mocha --require ts-node/register "test/**/*.ts"

So your package.json might look like this:

  "scripts": {
    "pretest": "npx tsc",
    "test": "mocha './test/**/*.ts' --require ts-node/register --recursive"
  },

and your tsconfig.json like this:

  "include": [
    "./src"
  ]

You don't need to include your /tests folder into the transpile process. And since you're running directly on the .ts test & source files, no sourcemap is needed; your line numbers and callstack remain usable.

Cardin
  • 5,148
  • 5
  • 36
  • 37