Skip to content

Commit

Permalink
jest-editor-support: Add support for Windows Subsystem for Linux in R…
Browse files Browse the repository at this point in the history
…unner

* Add useWsl configuration flag to project worksapce
* Translate windows paths in call args to POSIX paths valid in WSL

see jest-community/vscode-jest#331
  • Loading branch information
Mojadev authored and mojadev committed Oct 14, 2018
1 parent f00fbec commit f76ae0c
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 18 deletions.
15 changes: 9 additions & 6 deletions packages/jest-editor-support/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export interface Options {
testNamePattern?: string;
testFileNamePattern?: string;
shell?: boolean;
useWsl?: boolean;
}

export class Runner extends EventEmitter {
Expand All @@ -38,8 +39,8 @@ export class Settings extends EventEmitter {
getConfig(completed: Function): void;
jestVersionMajor: number | null;
settings: {
testRegex: string,
testMatch: string[],
testRegex: string;
testMatch: string[];
};
}

Expand All @@ -51,13 +52,15 @@ export class ProjectWorkspace {
localJestMajorVersin: number,
collectCoverage?: boolean,
debug?: boolean,
useWsl?: boolean,
);
pathToJest: string;
pathToConfig: string;
rootPath: string;
localJestMajorVersion: number;
collectCoverage?: boolean;
debug?: boolean;
useWsl?: boolean;
}

export interface IParseResults {
Expand Down Expand Up @@ -188,9 +191,9 @@ export class Snapshot {
}

type FormattedTestResults = {
testResults: TestResult[]
}
testResults: TestResult[];
};

type TestResult = {
name: string
}
name: string;
};
1 change: 1 addition & 0 deletions packages/jest-editor-support/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"babel-traverse": "^6.14.1",
"babylon": "^6.14.1",
"jest-snapshot": "^23.6.0"
"wsl-path": "^1.0.3"
},
"typings": "index.d.ts"
}
27 changes: 25 additions & 2 deletions packages/jest-editor-support/src/Process.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

import {ChildProcess, spawn} from 'child_process';
import ProjectWorkspace from './project_workspace';
import {windowsToWslSync} from 'wsl-path';
import type {SpawnOptions} from './types';

/**
Expand All @@ -27,16 +28,21 @@ export const createProcess = (
// any other bits into the args
const runtimeExecutable = workspace.pathToJest;
const parameters = runtimeExecutable.split(' ');
const command = parameters[0];
const initialArgs = parameters.slice(1);
const runtimeArgs = [].concat(initialArgs, args);
let command = parameters[0];
let runtimeArgs = [].concat(initialArgs, args);

// If a path to configuration file was defined, push it to runtimeArgs
if (workspace.pathToConfig) {
runtimeArgs.push('--config');
runtimeArgs.push(workspace.pathToConfig);
}

if (workspace.useWsl) {
runtimeArgs = [command, ...runtimeArgs.map(convertWslPath)];
command = 'wsl';
}

// To use our own commands in create-react, we need to tell the command that
// we're in a CI environment, or it will always append --watch
const env = process.env;
Expand All @@ -56,3 +62,20 @@ export const createProcess = (

return spawn(command, runtimeArgs, spawnOptions);
};

const convertWslPath = (maybePath: string): string => {
if (!/^\w:\\/.test(maybePath)) {
return maybePath;
}
// not every string containing a windows delimiter needs to be a
// path, but if it starts with C:\ or similar the chances are very high
try {
return windowsToWslSync(maybePath);
} catch (exception) {
console.log(
`Tried to translate ${maybePath} but received exception`,
exception,
);
return maybePath;
}
};
85 changes: 75 additions & 10 deletions packages/jest-editor-support/src/__tests__/process.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,30 @@
'use strict';

jest.mock('child_process');

jest.mock('wsl-path');
import {createProcess} from '../Process';
import {spawn} from 'child_process';
import * as wslPath from 'wsl-path';

describe('createProcess', () => {
afterEach(() => {
jest.resetAllMocks();
});

it('spawns the process', () => {
const workspace: any = {pathToJest: ''};
const workspace: any = {
pathToJest: '',
};
const args = [];
createProcess(workspace, args);

expect(spawn).toBeCalled();
});

it('spawns the command from workspace.pathToJest', () => {
const workspace: any = {pathToJest: 'jest'};
const workspace: any = {
pathToJest: 'jest',
};
const args = [];
createProcess(workspace, args);

Expand All @@ -37,7 +42,9 @@ describe('createProcess', () => {
});

it('spawns the first arg from workspace.pathToJest split on " "', () => {
const workspace: any = {pathToJest: 'npm test --'};
const workspace: any = {
pathToJest: 'npm test --',
};
const args = [];
createProcess(workspace, args);

Expand All @@ -60,7 +67,9 @@ describe('createProcess', () => {
});

it('appends args', () => {
const workspace: any = {pathToJest: 'npm test --'};
const workspace: any = {
pathToJest: 'npm test --',
};
const args = ['--option', 'value', '--another'];
createProcess(workspace, args);

Expand All @@ -86,9 +95,13 @@ describe('createProcess', () => {
});

it('defines the "CI" environment variable', () => {
const expected = Object.assign({}, process.env, {CI: 'true'});
const expected = Object.assign({}, process.env, {
CI: 'true',
});

const workspace: any = {pathToJest: ''};
const workspace: any = {
pathToJest: '',
};
const args = [];
createProcess(workspace, args);

Expand All @@ -107,7 +120,9 @@ describe('createProcess', () => {
});

it('should not set the "shell" property when "options" are not provided', () => {
const workspace: any = {pathToJest: ''};
const workspace: any = {
pathToJest: '',
};
const args = [];
createProcess(workspace, args);

Expand All @@ -116,11 +131,61 @@ describe('createProcess', () => {

it('should set the "shell" property when "options" are provided', () => {
const expected = {};
const workspace: any = {pathToJest: ''};
const workspace: any = {
pathToJest: '',
};
const args = [];
const options: any = {shell: expected};
const options: any = {
shell: expected,
};
createProcess(workspace, args, options);

expect(spawn.mock.calls[0][2].shell).toBe(expected);
});

it('should prepend wsl when useWsl is set in the ProjectWorkspace', () => {
const workspace: any = {
pathToJest: 'npm run jest',
useWsl: true,
};
const args = [];
const options: any = {
shell: true,
};
createProcess(workspace, args, options);

expect(spawn.mock.calls[0][0]).toEqual('wsl');
});

it('should keep the original command in the spawn arguments when using wsl', () => {
const expected = ['npm', 'run', 'jest'];
const workspace: any = {
pathToJest: expected.join(' '),
useWsl: true,
};
const args = [];
const options: any = {
shell: true,
};
createProcess(workspace, args, options);

expect(spawn.mock.calls[0][1]).toEqual(expected);
});

it('should translate file paths in the spawn command into the wsl context', () => {
const expected = ['npm', 'run', '/mnt/c/Users/Bob/path'];
wslPath.windowsToWslSync = jest.fn(() => expected[2]);

const workspace: any = {
pathToJest: 'npm run C:\\Users\\Bob\\path',
useWsl: true,
};
const args = [];
const options: any = {
shell: true,
};
createProcess(workspace, args, options);

expect(spawn.mock.calls[0][1]).toEqual(expected);
});
});
4 changes: 4 additions & 0 deletions packages/jest-editor-support/src/project_workspace.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,23 @@ export default class ProjectWorkspace {
*/
debug: ?boolean;

useWsl: ?boolean;

constructor(
rootPath: string,
pathToJest: string,
pathToConfig: string,
localJestMajorVersion: number,
collectCoverage: ?boolean,
debug: ?boolean,
useWsl: ?boolean,
) {
this.rootPath = rootPath;
this.pathToJest = pathToJest;
this.pathToConfig = pathToConfig;
this.localJestMajorVersion = localJestMajorVersion;
this.collectCoverage = collectCoverage;
this.debug = debug;
this.useWsl = useWsl;
}
}

0 comments on commit f76ae0c

Please sign in to comment.