mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Switch to function spies
This commit is contained in:
parent
41567b2261
commit
fa428356d5
@ -80,6 +80,7 @@
|
||||
"travis-fold": "latest",
|
||||
"ts-node": "latest",
|
||||
"tslint": "latest",
|
||||
"typemock": "file:scripts/typemock",
|
||||
"typescript": "next",
|
||||
"vinyl": "latest",
|
||||
"xml2js": "^0.4.19"
|
||||
|
||||
@ -23,7 +23,6 @@ gulp.task("test", ["build"], () => gulp
|
||||
.src(["dist/tests/index.js"], { read: false })
|
||||
.pipe(mocha({ reporter: "dot" })));
|
||||
|
||||
|
||||
gulp.task("watch", ["test"], () => gulp.watch(["src/**/*"], ["test"]));
|
||||
gulp.task("watch", () => gulp.watch(["src/**/*"], ["test"]));
|
||||
|
||||
gulp.task("default", ["test"]);
|
||||
@ -2,19 +2,21 @@
|
||||
* Represents an argument condition used during verification.
|
||||
*/
|
||||
export class Arg {
|
||||
private _condition: (value: any, args: ReadonlyArray<any>, index: number) => { valid: boolean, next?: number };
|
||||
private _validate: (value: any) => boolean;
|
||||
private _message: string;
|
||||
private _rest: boolean;
|
||||
|
||||
private constructor(condition: (value: any, args: ReadonlyArray<any>, index: number) => { valid: boolean, next?: number }, message: string) {
|
||||
this._condition = condition;
|
||||
private constructor(condition: (value: any) => boolean, message: string, rest = false) {
|
||||
this._validate = condition;
|
||||
this._message = message;
|
||||
this._rest = rest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows any value.
|
||||
*/
|
||||
public static any<T = any>(): T & Arg {
|
||||
return <any>new Arg(() => ({ valid: true }), `any`);
|
||||
return <any>new Arg(() => true, `any`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -22,14 +24,14 @@ export class Arg {
|
||||
* @param match The condition used to match the value.
|
||||
*/
|
||||
public static is<T = any>(match: (value: T) => boolean): T & Arg {
|
||||
return <any>new Arg(value => ({ valid: match(value) }), `is`);
|
||||
return <any>new Arg(match, `is`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows only a null value.
|
||||
*/
|
||||
public static null<T = any>(): T & Arg {
|
||||
return <any>new Arg(value => ({ valid: value === null }), `null`);
|
||||
return <any>new Arg(value => value === null, `null`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -43,7 +45,7 @@ export class Arg {
|
||||
* Allows only an undefined value.
|
||||
*/
|
||||
public static undefined<T = any>(): T & Arg {
|
||||
return <any>new Arg(value => ({ valid: value === undefined }), `undefined`);
|
||||
return <any>new Arg(value => value === undefined, `undefined`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,20 +69,24 @@ export class Arg {
|
||||
return Arg.not(Arg.nullOrUndefined());
|
||||
}
|
||||
|
||||
public static optional<T = any>(condition: T | T & Arg): T & Arg {
|
||||
return Arg.or(condition, Arg.undefined());
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows any value within the provided range.
|
||||
* @param min The minimum value.
|
||||
* @param max The maximum value.
|
||||
*/
|
||||
public static between<T = any>(min: T, max: T): T & Arg {
|
||||
return <any>new Arg(value => ({ valid: min <= value && value <= max }), `between ${min} and ${max}`);
|
||||
return <any>new Arg(value => min <= value && value <= max, `between ${min} and ${max}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows any value in the provided array.
|
||||
*/
|
||||
public static in<T = any>(values: T[]): T & Arg {
|
||||
return <any>new Arg(value => ({ valid: values.indexOf(value) > -1 }), `in ${values.join(", ")}`);
|
||||
return <any>new Arg(value => values.indexOf(value) > -1, `in ${values.join(", ")}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,19 +100,26 @@ export class Arg {
|
||||
* Allows any value that matches the provided pattern.
|
||||
*/
|
||||
public static match<T = any>(pattern: RegExp): T & Arg {
|
||||
return <any>new Arg(value => ({ valid: pattern.test(value) }), `matches ${pattern}`);
|
||||
return <any>new Arg(value => pattern.test(value), `matches ${pattern}`);
|
||||
}
|
||||
|
||||
public static startsWith(text: string): string & Arg {
|
||||
return <any>new Arg(value => ({ valid: String(value).startsWith(text) }), `starts with ${text}`);
|
||||
return <any>new Arg(value => typeof value === "string" && value.startsWith(text), `starts with ${text}`);
|
||||
}
|
||||
|
||||
public static endsWith(text: string): string & Arg {
|
||||
return <any>new Arg(value => ({ valid: String(value).endsWith(text) }), `ends with ${text}`);
|
||||
return <any>new Arg(value => typeof value === "string" && value.endsWith(text), `ends with ${text}`);
|
||||
}
|
||||
|
||||
public static includes(text: string): string & Arg {
|
||||
return <any>new Arg(value => ({ valid: String(value).includes(text) }), `contains ${text}`);
|
||||
public static includes(value: string): string & string[] & Arg;
|
||||
public static includes<T>(value: T): T[] & Arg;
|
||||
public static includes<T>(value: T): Arg {
|
||||
return new Arg(value_ => Array.isArray(value_) ? value_.includes(value) : typeof value_ === "string" && value_.includes("" + value), `contains ${value}`);
|
||||
}
|
||||
|
||||
public static array<T>(values: (T | T & Arg)[]): T[] & Arg {
|
||||
const conditions = values.map(Arg.from);
|
||||
return <any>new Arg(value => value.length === conditions.length && Arg.validateAll(conditions, value), `array [${conditions.join(", ")}]`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -142,7 +155,7 @@ export class Arg {
|
||||
*/
|
||||
public static typeof<T = any>(tag: string): T & Arg;
|
||||
public static typeof(tag: string): any {
|
||||
return <any>new Arg(value => ({ valid: typeof value === tag }), `typeof ${tag}`);
|
||||
return <any>new Arg(value => typeof value === tag, `typeof ${tag}`);
|
||||
}
|
||||
|
||||
public static string() { return this.typeof("string"); }
|
||||
@ -157,21 +170,21 @@ export class Arg {
|
||||
* @param type The expected constructor.
|
||||
*/
|
||||
public static instanceof<TClass extends { new (...args: any[]): object; prototype: object; }>(type: TClass): TClass["prototype"] & Arg {
|
||||
return <any>new Arg(value => ({ valid: value instanceof type }), `instanceof ${type.name}`);
|
||||
return <any>new Arg(value => value instanceof type, `instanceof ${type.name}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows any value that has the provided property names in its prototype chain.
|
||||
*/
|
||||
public static has<T>(...names: string[]): T & Arg {
|
||||
return <any>new Arg(value => ({ valid: names.filter(name => name in value).length === names.length }), `has ${names.join(", ")}`);
|
||||
return <any>new Arg(value => names.filter(name => name in value).length === names.length, `has ${names.join(", ")}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows any value that has the provided property names on itself but not its prototype chain.
|
||||
*/
|
||||
public static hasOwn<T>(...names: string[]): T & Arg {
|
||||
return <any>new Arg(value => ({ valid: names.filter(name => Object.prototype.hasOwnProperty.call(value, name)).length === names.length }), `hasOwn ${names.join(", ")}`);
|
||||
return <any>new Arg(value => names.filter(name => Object.prototype.hasOwnProperty.call(value, name)).length === names.length, `hasOwn ${names.join(", ")}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -180,21 +193,11 @@ export class Arg {
|
||||
*/
|
||||
public static rest<T>(condition?: T | (T & Arg)): T & Arg {
|
||||
if (condition === undefined) {
|
||||
return <any>new Arg((_, args) => ({ valid: true, next: args.length }), `rest`);
|
||||
return <any>new Arg(() => true, `rest`, /*rest*/ true);
|
||||
}
|
||||
|
||||
const arg = Arg.from(condition);
|
||||
return <any>new Arg(
|
||||
(_, args, index) => {
|
||||
while (index < args.length) {
|
||||
const { valid, next } = Arg.validate(arg, args, index);
|
||||
if (!valid) return { valid: false };
|
||||
index = typeof next === "undefined" ? index + 1 : next;
|
||||
}
|
||||
return { valid: true, next: index };
|
||||
},
|
||||
`rest ${arg._message}`
|
||||
);
|
||||
return <any>new Arg(value => arg._validate(value), `rest ${arg._message}`, /*rest*/ true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -202,10 +205,7 @@ export class Arg {
|
||||
*/
|
||||
public static not<T = any>(value: T | (T & Arg)): T & Arg {
|
||||
const arg = Arg.from(value);
|
||||
return <any>new Arg((value, args, index) => {
|
||||
const result = arg._condition(value, args, index);
|
||||
return { valid: !result.valid, next: result.next };
|
||||
}, `not ${arg._message}`);
|
||||
return <any>new Arg(value => !arg._validate(value), `not ${arg._message}`);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -213,20 +213,15 @@ export class Arg {
|
||||
*/
|
||||
public static and<T = any>(...args: ((T & Arg) | T)[]): T & Arg {
|
||||
const conditions = args.map(Arg.from);
|
||||
return <any>new Arg((value, args, index) => {
|
||||
for (const condition of conditions) {
|
||||
const result = condition._condition(value, args, index);
|
||||
if (!result.valid) return { valid: false };
|
||||
}
|
||||
return { valid: true };
|
||||
}, conditions.map(condition => condition._message).join(" and "));
|
||||
return <any>new Arg(value => conditions.every(condition => condition._validate(value)), conditions.map(condition => condition._message).join(" and "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines conditions, where no condition may be `true`.
|
||||
*/
|
||||
public static nand<T = any>(...args: ((T & Arg) | T)[]): T & Arg {
|
||||
return this.not(this.and(...args));
|
||||
const conditions = args.map(Arg.from);
|
||||
return <any>new Arg(value => !conditions.every(condition => condition._validate(value)), "not " + conditions.map(condition => condition._message).join(" and "));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -234,20 +229,15 @@ export class Arg {
|
||||
*/
|
||||
public static or<T = any>(...args: ((T & Arg) | T)[]): T & Arg {
|
||||
const conditions = args.map(Arg.from);
|
||||
return <any>new Arg((value, args, index) => {
|
||||
for (const condition of conditions) {
|
||||
const result = condition._condition(value, args, index);
|
||||
if (result.valid) return { valid: true };
|
||||
}
|
||||
return { valid: false };
|
||||
}, conditions.map(condition => condition._message).join(" or "));
|
||||
return <any>new Arg(value => conditions.some(condition => condition._validate(value)), conditions.map(condition => condition._message).join(" or "));
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines conditions, where all conditions must be `true`.
|
||||
*/
|
||||
public static nor<T = any>(...args: ((T & Arg) | T)[]): T & Arg {
|
||||
return this.not(this.or(...args));
|
||||
const conditions = args.map(Arg.from);
|
||||
return <any>new Arg(value => !conditions.some(condition => condition._validate(value)), "neither " + conditions.map(condition => condition._message).join(" nor "));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -256,25 +246,40 @@ export class Arg {
|
||||
* @returns The condition
|
||||
*/
|
||||
public static from<T>(value: T): T & Arg {
|
||||
if (value instanceof Arg) {
|
||||
return value;
|
||||
}
|
||||
return value instanceof Arg ? value :
|
||||
value === undefined ? Arg.undefined() :
|
||||
value === null ? Arg.null() :
|
||||
<any>new Arg(v => is(v, value), JSON.stringify(value));
|
||||
}
|
||||
|
||||
return <any>new Arg(v => ({ valid: is(v, value) }), JSON.stringify(value));
|
||||
/**
|
||||
* Validates an argument against a condition
|
||||
* @param condition The condition to validate.
|
||||
* @param arg The argument to validate against the condition.
|
||||
*/
|
||||
public static validate(condition: Arg, arg: any): boolean {
|
||||
return condition._validate(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the arguments against the condition.
|
||||
* @param args The arguments for the execution
|
||||
* @param index The current index into the `args` array
|
||||
* @returns An object that specifies whether the condition is `valid` and what the `next` index should be.
|
||||
* @param conditions The conditions to validate.
|
||||
* @param args The arguments for the execution.
|
||||
*/
|
||||
public static validate(arg: Arg, args: ReadonlyArray<any>, index: number): { valid: boolean, next?: number } {
|
||||
const value = index >= 0 && index < args.length ? args[index] : undefined;
|
||||
const { valid, next } = arg._condition(value, args, index);
|
||||
return valid
|
||||
? { valid: true, next: next === undefined ? index + 1 : next }
|
||||
: { valid: false };
|
||||
public static validateAll(conditions: ReadonlyArray<Arg>, args: ReadonlyArray<any>): boolean {
|
||||
const length = Math.max(conditions.length, args.length);
|
||||
let conditionIndex = 0;
|
||||
let argIndex = 0;
|
||||
while (argIndex < length) {
|
||||
const condition = conditionIndex < conditions.length ? conditions[conditionIndex] : undefined;
|
||||
const arg = argIndex < args.length ? args[argIndex] : undefined;
|
||||
if (!condition) return false;
|
||||
if (argIndex >= args.length && condition._rest) return true;
|
||||
if (!condition._validate(arg)) return false;
|
||||
if (!condition._rest) conditionIndex++;
|
||||
argIndex++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -1,6 +1,25 @@
|
||||
export { Arg } from "./arg";
|
||||
export { Times } from "./times";
|
||||
export { Mock, Returns, Throws } from "./mock";
|
||||
export { Spy, Callable, Constructable } from "./spy";
|
||||
export { Stub } from "./stub";
|
||||
export { Timers, Timer, Timeout, Interval, Immediate, AnimationFrame } from "./timers";
|
||||
export { Mock, Spy, Returns, Throws, ThisArg, Callback, Fallback, Setup, Callable, Constructable } from "./mock";
|
||||
export { Inject } from "./inject";
|
||||
export { Timers, Timer, Timeout, Interval, Immediate, AnimationFrame } from "./timers";
|
||||
|
||||
import { Mock, Spy, Callable, Constructable } from "./mock";
|
||||
|
||||
/**
|
||||
* Creates a spy on an object or function.
|
||||
*/
|
||||
export function spy<T extends Callable | Constructable = Callable & Constructable>(): Mock<T>;
|
||||
/**
|
||||
* Creates a spy on an object or function.
|
||||
*/
|
||||
export function spy<T extends object>(target: T): Mock<T>;
|
||||
/**
|
||||
* Installs a spy on a method of an object. Use `revoke()` on the result to reset the spy.
|
||||
* @param object The object containing a method.
|
||||
* @param propertyKey The name of the method on the object.
|
||||
*/
|
||||
export function spy<T extends { [P in K]: (...args: any[]) => any }, K extends keyof T>(object: T, propertyKey: K): Spy<T, K>;
|
||||
export function spy<T extends { [P in K]: (...args: any[]) => any }, K extends keyof T>(object?: T, propertyKey?: K) {
|
||||
return object === undefined ? Mock.spy() : propertyKey === undefined ? Mock.spy(object) : Mock.spy(object, propertyKey);
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* Temporarily injects a value into an object property
|
||||
*/
|
||||
export class Stub<T, K extends keyof T> {
|
||||
export class Inject<T extends object, K extends keyof T> {
|
||||
private _target: T;
|
||||
private _key: K;
|
||||
private _value: any;
|
||||
@ -28,11 +28,11 @@ export class Stub<T, K extends keyof T> {
|
||||
return this._key;
|
||||
}
|
||||
|
||||
public get stubValue(): T[K] {
|
||||
public get injectedValue(): T[K] {
|
||||
return this._installed ? this.currentValue : this._value;
|
||||
}
|
||||
|
||||
public set stubValue(value: T[K]) {
|
||||
public set injectedValue(value: T[K]) {
|
||||
if (this._installed) {
|
||||
this._target[this._key] = value;
|
||||
}
|
||||
@ -79,8 +79,8 @@ export class Stub<T, K extends keyof T> {
|
||||
this._originalValue = null;
|
||||
}
|
||||
|
||||
public static exec<T, K extends keyof T, V>(target: T, propertyKey: K, value: T[K], action: () => V) {
|
||||
const stub = new Stub<T, K>(target, propertyKey, value);
|
||||
public static exec<T extends object, K extends keyof T, V>(target: T, propertyKey: K, value: T[K], action: () => V) {
|
||||
const stub = new Inject<T, K>(target, propertyKey, value);
|
||||
return stub.exec(action);
|
||||
}
|
||||
|
||||
@ -1,28 +1,53 @@
|
||||
import { Times } from "./times";
|
||||
import { Arg } from "./arg";
|
||||
import { Inject } from "./inject";
|
||||
|
||||
const weakHandler = new WeakMap<object, MockHandler<object>>();
|
||||
const weakMock = new WeakMap<object, Mock<object>>();
|
||||
|
||||
function noop() {}
|
||||
const empty = {};
|
||||
|
||||
function getHandler(value: object) {
|
||||
return weakHandler.get(value);
|
||||
export type Callable = (...args: any[]) => any;
|
||||
|
||||
export type Constructable = new (...args: any[]) => any;
|
||||
|
||||
export interface ThisArg {
|
||||
this: any;
|
||||
}
|
||||
|
||||
export interface Returns<U> {
|
||||
returns: U;
|
||||
return: U;
|
||||
}
|
||||
|
||||
export interface Fallback {
|
||||
fallback: true;
|
||||
}
|
||||
|
||||
export interface Throws {
|
||||
throws: any;
|
||||
throw: any;
|
||||
}
|
||||
|
||||
export interface Callback {
|
||||
callback: Callable;
|
||||
}
|
||||
|
||||
export type Setup<U> =
|
||||
| Returns<U> & (ThisArg & Callback | ThisArg | Callback)
|
||||
| Returns<U>
|
||||
| Throws & (ThisArg & Callback | ThisArg | Callback)
|
||||
| Throws
|
||||
| Fallback & (ThisArg & Callback | ThisArg | Callback)
|
||||
| Fallback
|
||||
| ThisArg & Callback
|
||||
| ThisArg
|
||||
| Callback;
|
||||
|
||||
/**
|
||||
* A mock version of another oject
|
||||
*/
|
||||
export class Mock<T extends object> {
|
||||
private _target: T;
|
||||
private _handler = new MockHandler<T>();
|
||||
private _handler: MockHandler<T>;
|
||||
private _proxy: T;
|
||||
private _revoke: () => void;
|
||||
|
||||
@ -32,13 +57,16 @@ export class Mock<T extends object> {
|
||||
* @param setups Optional setups to use
|
||||
*/
|
||||
constructor(target: T = <T>{}, setups?: Partial<T>) {
|
||||
this._target = target;
|
||||
this._handler = typeof target === "function"
|
||||
? new MockFunctionHandler<T & (Callable | Constructable)>()
|
||||
: new MockHandler<T>();
|
||||
|
||||
const { proxy, revoke } = Proxy.revocable<T>(this._target, this._handler);
|
||||
const { proxy, revoke } = Proxy.revocable<T>(target, this._handler);
|
||||
this._proxy = proxy;
|
||||
this._revoke = revoke;
|
||||
|
||||
weakHandler.set(proxy, this._handler);
|
||||
weakMock.set(proxy, this);
|
||||
|
||||
if (setups) {
|
||||
this.setup(setups);
|
||||
@ -48,24 +76,66 @@ export class Mock<T extends object> {
|
||||
/**
|
||||
* Gets the mock version of the target
|
||||
*/
|
||||
public get value(): T {
|
||||
public get proxy(): T {
|
||||
return this._proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty Mock object.
|
||||
*/
|
||||
public static object<T extends object = any>() {
|
||||
return new Mock(<T>{});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty Mock function.
|
||||
*/
|
||||
public static function<T extends Callable | Constructable = Callable & Constructable>() {
|
||||
return new Mock(<T>function () {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function spy.
|
||||
*/
|
||||
public static spy<T extends Callable | Constructable = Callable & Constructable>(): Mock<T>;
|
||||
/**
|
||||
* Creates a spy on an object or function.
|
||||
*/
|
||||
public static spy<T extends object>(target: T): Mock<T>;
|
||||
/**
|
||||
* Installs a spy on a method of an object. Use `revoke()` on the result to reset the spy.
|
||||
* @param object The object containing a method.
|
||||
* @param propertyKey The name of the method on the object.
|
||||
*/
|
||||
public static spy<T extends { [P in K]: (...args: any[]) => any }, K extends keyof T>(object: T, propertyKey: K): Spy<T, K>;
|
||||
public static spy<T extends { [P in K]: (...args: any[]) => any }, K extends keyof T>(object?: T, propertyKey?: K) {
|
||||
return object !== undefined && propertyKey !== undefined
|
||||
? new Spy(object, propertyKey)
|
||||
: new Mock(object || noop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the mock for an object.
|
||||
* @param target The target.
|
||||
*/
|
||||
public static from<T extends object>(target: T) {
|
||||
return <Mock<T> | undefined>weakMock.get(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs setup of the mock object, overriding the target object's functionality with that provided by the setup
|
||||
* @param callback A function used to set up a method result.
|
||||
* @param result An object used to describe the result of the method.
|
||||
* @returns This mock instance.
|
||||
*/
|
||||
public setup<U = any>(callback: (value: T) => U, result?: Returns<U> | Throws): Mock<T>;
|
||||
public setup<U = any>(callback: (value: T) => U, result?: Setup<U>): this;
|
||||
/**
|
||||
* Performs setup of the mock object, overriding the target object's functionality with that provided by the setup
|
||||
* @param setups An object whose members are used instead of the target object.
|
||||
* @returns This mock instance.
|
||||
*/
|
||||
public setup(setups: Partial<T>): Mock<T>;
|
||||
public setup<U>(setup: Partial<T> | ((value: T) => U), result?: Returns<U> | Throws): Mock<T> {
|
||||
public setup(setups: Partial<T>): this;
|
||||
public setup<U>(setup: Partial<T> | ((value: T) => U), result?: Setup<U>): this {
|
||||
if (typeof setup === "function") {
|
||||
this._handler.setupCall(setup, result);
|
||||
}
|
||||
@ -75,187 +145,259 @@ export class Mock<T extends object> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs verification that a specific action occurred at least once.
|
||||
* @param callback A callback that simulates the expected action.
|
||||
* @param message An optional message to use if verification fails.
|
||||
* @returns This mock instance.
|
||||
*/
|
||||
public verify<U>(callback: (value: T) => U, message?: string): this;
|
||||
/**
|
||||
* Performs verification that a specific action occurred.
|
||||
* @param callback A callback that simulates the expected action.
|
||||
* @param times The number of times the action should have occurred.
|
||||
* @param message An optional message to use if verification fails.
|
||||
* @returns This mock instance.
|
||||
*/
|
||||
public verify<U>(callback: (value: T) => U, times: Times, message?: string): this;
|
||||
/**
|
||||
* Performs verification that a specific action occurred.
|
||||
* @param callback A callback that simulates the expected action.
|
||||
* @param times The number of times the action should have occurred.
|
||||
* @returns This mock instance.
|
||||
*/
|
||||
public verify(callback: (value: T) => any, times: Times): Mock<T> {
|
||||
public verify<U>(callback: (value: T) => U, times?: Times | string, message?: string): this {
|
||||
if (typeof times === "string") {
|
||||
message = times;
|
||||
times = undefined;
|
||||
}
|
||||
if (times === undefined) {
|
||||
times = Times.atLeastOnce();
|
||||
}
|
||||
this._handler.verify(callback, times);
|
||||
return this;
|
||||
}
|
||||
|
||||
public revoke() {
|
||||
weakMock.delete(this._proxy);
|
||||
weakHandler.delete(this._proxy);
|
||||
this._handler.revoke();
|
||||
this._revoke();
|
||||
}
|
||||
}
|
||||
|
||||
class Setup {
|
||||
public recording: Recording;
|
||||
public result: Partial<Returns<any> & Throws> | undefined;
|
||||
export class Spy<T extends { [P in K]: (...args: any[]) => any }, K extends keyof T> extends Mock<T[K]> {
|
||||
private _spy: Inject<any, any> | undefined;
|
||||
|
||||
constructor (recording: Recording, result?: Returns<any> | Throws) {
|
||||
this.recording = recording;
|
||||
this.result = result;
|
||||
constructor(target: T, propertyKey: K) {
|
||||
super(target[propertyKey]);
|
||||
this._spy = new Inject(target, propertyKey, this.proxy);
|
||||
this._spy.install();
|
||||
}
|
||||
|
||||
public static evaluate(setups: ReadonlyArray<Setup> | undefined, trap: string, args: any[], newTarget?: any) {
|
||||
if (setups) {
|
||||
for (let i = setups.length - 1; i >= 0; i--) {
|
||||
const setup = setups[i];
|
||||
if (setup.recording.trap === trap &&
|
||||
setup.recording.newTarget === newTarget &&
|
||||
setup.matchArguments(args)) {
|
||||
return setup.getResult();
|
||||
}
|
||||
}
|
||||
public get installed() {
|
||||
return this._spy ? this._spy.installed : false;
|
||||
}
|
||||
|
||||
public install() {
|
||||
if (!this._spy) throw new Error("Cannot install a revoked spy.");
|
||||
this._spy.install();
|
||||
return this;
|
||||
}
|
||||
|
||||
public uninstall() {
|
||||
if (this._spy) this._spy.uninstall();
|
||||
return this;
|
||||
}
|
||||
|
||||
public revoke() {
|
||||
if (this._spy) {
|
||||
this._spy.uninstall();
|
||||
this._spy = undefined;
|
||||
}
|
||||
throw new Error("No matching setups.");
|
||||
}
|
||||
|
||||
public matchArguments(args: any[]) {
|
||||
return this.recording.matchArguments(args);
|
||||
}
|
||||
|
||||
public getResult() {
|
||||
if (this.result) {
|
||||
if (this.result.throws) {
|
||||
throw this.result.throws;
|
||||
}
|
||||
return this.result.returns;
|
||||
}
|
||||
return undefined;
|
||||
super.revoke();
|
||||
}
|
||||
}
|
||||
|
||||
class Recording {
|
||||
public static readonly noThisArg = {};
|
||||
public readonly trap: string;
|
||||
public readonly name: PropertyKey | undefined;
|
||||
public readonly args: ReadonlyArray<any>;
|
||||
public readonly thisArg: any;
|
||||
public readonly argArray: ReadonlyArray<any>;
|
||||
public readonly newTarget: any;
|
||||
public readonly result: Partial<Returns<any> & Throws & Fallback> | undefined;
|
||||
public readonly callback: Callable | undefined;
|
||||
|
||||
private _thisCondition: Arg | undefined;
|
||||
private _newTargetCondition: Arg | undefined;
|
||||
private _conditions: ReadonlyArray<Arg> | undefined;
|
||||
|
||||
constructor(trap: string, name: PropertyKey | undefined, args: ReadonlyArray<any>, newTarget?: any) {
|
||||
constructor(trap: string, name: PropertyKey | undefined, thisArg: any, argArray: ReadonlyArray<any>, newTarget: any, result: Partial<Returns<any> & Throws & Fallback> | undefined, callback: Callable | undefined) {
|
||||
this.trap = trap;
|
||||
this.name = name;
|
||||
this.args = args || [];
|
||||
this.thisArg = thisArg;
|
||||
this.argArray = argArray || [];
|
||||
this.newTarget = newTarget;
|
||||
this.result = result;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
public get conditions() {
|
||||
return this._conditions || (this._conditions = this.args.map(Arg.from));
|
||||
public get thisCondition() {
|
||||
return this._thisCondition || (this._thisCondition = this.thisArg === Recording.noThisArg ? Arg.any() : Arg.from(this.thisArg));
|
||||
}
|
||||
|
||||
public get newTargetCondition() {
|
||||
return this._newTargetCondition || (this._newTargetCondition = Arg.from(this.newTarget));
|
||||
}
|
||||
|
||||
public get argConditions() {
|
||||
return this._conditions || (this._conditions = this.argArray.map(Arg.from));
|
||||
}
|
||||
|
||||
public get kind() {
|
||||
switch (this.trap) {
|
||||
case "apply": return "function";
|
||||
case "construct": return "function";
|
||||
case "invoke": return "method";
|
||||
case "get": return "property";
|
||||
case "set": return "property";
|
||||
}
|
||||
}
|
||||
|
||||
public static select(setups: ReadonlyArray<Recording>, kind: Recording["kind"], name: PropertyKey | undefined) {
|
||||
return setups.filter(setup => setup.kind === kind && setup.name === name);
|
||||
}
|
||||
|
||||
public static evaluate(setups: ReadonlyArray<Recording> | undefined, trap: string, name: PropertyKey | undefined, thisArg: any, argArray: any[], newTarget: any, fallback: () => any) {
|
||||
if (setups && setups.length > 0) {
|
||||
for (const setup of setups) {
|
||||
if (setup.match(trap, name, thisArg, argArray, newTarget)) {
|
||||
const callback = setup.callback;
|
||||
if (callback) {
|
||||
Reflect.apply(callback, thisArg, argArray);
|
||||
}
|
||||
|
||||
const result = setup.getResult(fallback);
|
||||
return trap === "set" ? true : result;
|
||||
}
|
||||
}
|
||||
return trap === "set" ? false : undefined;
|
||||
}
|
||||
return fallback();
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return `${this.trap} ${this.name || ""}(${this.conditions.join(", ")})${this.newTarget ? ` [${this.newTarget.name}]` : ``}`;
|
||||
return `${this.trap} ${this.name || ""}(${this.argConditions.join(", ")})${this.newTarget ? ` [${this.newTarget.name}]` : ``}`;
|
||||
}
|
||||
|
||||
public match(trap: string, name: PropertyKey | undefined, thisArg: any, argArray: any, newTarget: any) {
|
||||
return this.trap === trap
|
||||
&& this.name === name
|
||||
&& Arg.validate(this.thisCondition, thisArg)
|
||||
&& Arg.validateAll(this.argConditions, argArray)
|
||||
&& Arg.validate(this.newTargetCondition, newTarget);
|
||||
}
|
||||
|
||||
public matchRecording(recording: Recording) {
|
||||
if (recording.trap !== this.trap ||
|
||||
recording.name !== this.name ||
|
||||
recording.newTarget !== this.newTarget) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.matchArguments(recording.args);
|
||||
return this.match(recording.trap, recording.name, recording.thisArg, recording.argArray, recording.newTarget)
|
||||
&& this.matchResult(recording.result);
|
||||
}
|
||||
|
||||
public matchArguments(args: ReadonlyArray<any>) {
|
||||
let argi = 0;
|
||||
while (argi < this.conditions.length) {
|
||||
const condition = this.conditions[argi];
|
||||
const { valid, next } = Arg.validate(condition, args, argi);
|
||||
if (!valid) {
|
||||
return false;
|
||||
}
|
||||
argi = typeof next === "number" ? next : argi + 1;
|
||||
}
|
||||
if (argi < args.length) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
private matchThisArg(thisArg: any) {
|
||||
return thisArg === Recording.noThisArg
|
||||
|| Arg.validate(this.thisCondition, thisArg);
|
||||
}
|
||||
|
||||
private matchResult(result: Partial<Returns<any> & Throws> | undefined) {
|
||||
return !this.result
|
||||
|| this.result.return === (result && result.return)
|
||||
&& this.result.throw === (result && result.throw);
|
||||
}
|
||||
|
||||
private getResult(fallback: () => any) {
|
||||
if (hasOwn(this.result, "throw")) throw this.result.throw;
|
||||
if (hasOwn(this.result, "return")) return this.result.return;
|
||||
if (hasOwn(this.result, "fallback")) return this.result.fallback ? fallback() : undefined;
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
class MockHandler<T extends object> implements ProxyHandler<T> {
|
||||
private readonly overrides = Object.create(null);
|
||||
private readonly recordings: Recording[] = [];
|
||||
private readonly selfSetups: Setup[] = [];
|
||||
private readonly memberSetups = new Map<PropertyKey, Setup[]>();
|
||||
private readonly methodTargets = new WeakMap<Function, Function>();
|
||||
private readonly methodProxies = new Map<PropertyKey, Function>();
|
||||
private readonly methodRevocations = new Set<() => void>();
|
||||
protected readonly overrides = Object.create(null);
|
||||
protected readonly recordings: Recording[] = [];
|
||||
protected readonly setups: Recording[] = [];
|
||||
protected readonly methodTargets = new WeakMap<Function, Function>();
|
||||
protected readonly methodProxies = new Map<PropertyKey, Function>();
|
||||
protected readonly methodRevocations = new Set<() => void>();
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
public apply(target: T | Function, thisArg: any, argArray: any[]): any {
|
||||
if (typeof target === "function") {
|
||||
this.recordings.push(new Recording("apply", undefined, argArray));
|
||||
return this.selfSetups.length > 0
|
||||
? Setup.evaluate(this.selfSetups, "apply", argArray)
|
||||
: Reflect.apply(target, thisArg, argArray);
|
||||
public get(target: T, name: PropertyKey, receiver: any = target): any {
|
||||
const setups = Recording.select(this.setups, "property", name);
|
||||
const result: Partial<Returns<any> & Throws> = {};
|
||||
const recording = new Recording("get", name, target, [], /*newTarget*/ undefined, result, /*callback*/ undefined);
|
||||
this.recordings.push(recording);
|
||||
try {
|
||||
const value = Recording.evaluate(setups, "get", name, receiver, [], /*newTarget*/ undefined,
|
||||
() => Reflect.get(this.getTarget(target, name), name, receiver));
|
||||
return typeof value === "function" ? this.getMethod(name, value) : value;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public construct(target: T | Function, argArray: any[], newTarget?: any): any {
|
||||
if (typeof target === "function") {
|
||||
this.recordings.push(new Recording("construct", undefined, argArray, newTarget));
|
||||
return this.selfSetups.length > 0
|
||||
? Setup.evaluate(this.selfSetups, "construct", argArray, newTarget)
|
||||
: Reflect.construct(target, argArray, newTarget);
|
||||
catch (e) {
|
||||
throw result.throw = e;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public get(target: T, name: PropertyKey, receiver: any): any {
|
||||
this.recordings.push(new Recording("get", name, []));
|
||||
const value = Reflect.get(this.getTarget(target, name), name, receiver);
|
||||
return typeof value === "function" ? this.getMethod(name, value) : value;
|
||||
}
|
||||
|
||||
public set(target: T, name: PropertyKey, value: any, receiver: any): boolean {
|
||||
this.recordings.push(new Recording("set", name, [value]));
|
||||
public set(target: T, name: PropertyKey, value: any, receiver: any = target): boolean {
|
||||
if (typeof value === "function" && this.methodTargets.has(value)) {
|
||||
value = this.methodTargets.get(value);
|
||||
}
|
||||
|
||||
return Reflect.set(this.getTarget(target, name), name, value, receiver);
|
||||
const setups = Recording.select(this.setups, "property", name);
|
||||
const result: Partial<Returns<any> & Throws> = {};
|
||||
const recording = new Recording("set", name, target, [value], /*newTarget*/ undefined, result, /*callback*/ undefined);
|
||||
this.recordings.push(recording);
|
||||
try {
|
||||
const success = Recording.evaluate(setups, "set", name, receiver, [value], /*newTarget*/ undefined,
|
||||
() => Reflect.set(this.getTarget(target, name), name, value, receiver));
|
||||
result.return = undefined;
|
||||
return success;
|
||||
}
|
||||
catch (e) {
|
||||
throw result.throw = e;
|
||||
}
|
||||
}
|
||||
|
||||
public invoke(proxy: T, name: PropertyKey, method: Function, argArray: any[]): any {
|
||||
this.recordings.push(new Recording("invoke", name, argArray));
|
||||
return Reflect.apply(method, proxy, argArray);
|
||||
const setups = Recording.select(this.setups, "method", name);
|
||||
const result: Partial<Returns<any> & Throws> = {};
|
||||
const recording = new Recording("invoke", name, proxy, argArray, /*newTarget*/ undefined, result, /*callback*/ undefined);
|
||||
this.recordings.push(recording);
|
||||
try {
|
||||
return Recording.evaluate(setups, "invoke", name, proxy, argArray, /*newTarget*/ undefined,
|
||||
() => Reflect.apply(method, proxy, argArray));
|
||||
}
|
||||
catch (e) {
|
||||
throw result.throw = e;
|
||||
}
|
||||
}
|
||||
|
||||
public setupCall(callback: (value: any) => any, result: Returns<any> | Throws | undefined) {
|
||||
const recording = capture(callback);
|
||||
if (recording.name === undefined) {
|
||||
this.selfSetups.push(new Setup(recording, result));
|
||||
public setupCall(callback: (value: any) => any, result: Setup<any> | undefined) {
|
||||
const recording = this.capture(callback, result);
|
||||
const existing = this.setups.find(setup => setup.name === recording.name);
|
||||
if (existing) {
|
||||
if (existing.kind !== recording.kind) {
|
||||
throw new Error(`Cannot mix method and property setups for the same member name.`);
|
||||
}
|
||||
}
|
||||
else {
|
||||
let setups = this.memberSetups.get(recording.name);
|
||||
if (!setups) {
|
||||
this.memberSetups.set(recording.name, setups = []);
|
||||
if (recording.trap === "invoke") {
|
||||
this.defineMethod(recording.name);
|
||||
}
|
||||
else {
|
||||
this.defineAccessor(recording.name);
|
||||
}
|
||||
else if (recording.name !== undefined) {
|
||||
if (recording.kind === "method") {
|
||||
this.defineMethod(recording.name);
|
||||
}
|
||||
else {
|
||||
if ((setups[0].recording.trap === "invoke") !== (recording.trap === "invoke")) {
|
||||
throw new Error(`Cannot mix method and acessor setups for the same property.`);
|
||||
}
|
||||
else if (recording.kind === "property") {
|
||||
this.defineAccessor(recording.name);
|
||||
}
|
||||
}
|
||||
|
||||
setups.push(new Setup(recording, result));
|
||||
}
|
||||
this.setups.push(recording);
|
||||
}
|
||||
|
||||
public setupMembers(setup: object) {
|
||||
@ -270,8 +412,8 @@ class MockHandler<T extends object> implements ProxyHandler<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public verify(callback: (value: T) => any, times: Times): void {
|
||||
const expectation = capture(callback);
|
||||
public verify<U>(callback: (value: T) => U, times: Times, message?: string): void {
|
||||
const expectation = this.capture(callback, /*result*/ undefined);
|
||||
|
||||
let count: number = 0;
|
||||
for (const recording of this.recordings) {
|
||||
@ -280,7 +422,7 @@ class MockHandler<T extends object> implements ProxyHandler<T> {
|
||||
}
|
||||
}
|
||||
|
||||
times.check(count, `An error occured when verifying expectation: ${expectation}`);
|
||||
times.check(count, message || `An error occured when verifying expectation: ${expectation}`);
|
||||
}
|
||||
|
||||
public getTarget(target: T, name: PropertyKey) {
|
||||
@ -307,28 +449,80 @@ class MockHandler<T extends object> implements ProxyHandler<T> {
|
||||
}
|
||||
}
|
||||
|
||||
protected capture<U>(callback: (value: T) => U, result: Setup<any> | undefined) {
|
||||
return this.captureCore(<T>empty, new CapturingHandler<T, U>(result), callback);
|
||||
}
|
||||
|
||||
protected captureCore<T extends object, U>(target: T, handler: CapturingHandler<T, U>, callback: (value: T) => U): Recording {
|
||||
const { proxy, revoke } = Proxy.revocable<T>(target, handler);
|
||||
try {
|
||||
callback(proxy);
|
||||
if (!handler.recording) {
|
||||
throw new Error("Nothing was captured.");
|
||||
}
|
||||
return handler.recording;
|
||||
}
|
||||
finally {
|
||||
revoke();
|
||||
}
|
||||
}
|
||||
|
||||
private defineMethod(name: PropertyKey) {
|
||||
const setups = this.memberSetups;
|
||||
const setups = this.setups;
|
||||
this.setupMembers({
|
||||
[name](...args: any[]) {
|
||||
return Setup.evaluate(setups.get(name), "invoke", args);
|
||||
[name](...argArray: any[]) {
|
||||
return Recording.evaluate(setups, "invoke", name, this, argArray, /*newTarget*/ undefined, noop);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private defineAccessor(name: PropertyKey) {
|
||||
const setups = this.memberSetups;
|
||||
const setups = this.setups;
|
||||
this.setupMembers({
|
||||
get [name]() {
|
||||
return Setup.evaluate(setups.get(name), "get", []);
|
||||
return Recording.evaluate(setups, "get", name, this, [], /*newTarget*/ undefined, noop);
|
||||
},
|
||||
set [name](value: any) {
|
||||
Setup.evaluate(setups.get(name), "set", [value]);
|
||||
Recording.evaluate(setups, "set", name, this, [value], /*newTarget*/ undefined, noop);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class MockFunctionHandler<T extends Callable | Constructable> extends MockHandler<T> {
|
||||
public apply(target: T, thisArg: any, argArray: any[]): any {
|
||||
const setups = Recording.select(this.setups, "function", /*name*/ undefined);
|
||||
const result: Partial<Returns<any> & Throws> = {};
|
||||
const recording = new Recording("apply", /*name*/ undefined, thisArg, argArray, /*newTarget*/ undefined, result, /*callback*/ undefined);
|
||||
this.recordings.push(recording);
|
||||
try {
|
||||
return Recording.evaluate(setups, "apply", /*name*/ undefined, thisArg, argArray, /*newTarget*/ undefined,
|
||||
() => Reflect.apply(target, thisArg, argArray));
|
||||
}
|
||||
catch (e) {
|
||||
throw result.throw = e;
|
||||
}
|
||||
}
|
||||
|
||||
public construct(target: T, argArray: any[], newTarget?: any): any {
|
||||
const setups = Recording.select(this.setups, "function", /*name*/ undefined);
|
||||
const result: Partial<Returns<any> & Throws> = {};
|
||||
const recording = new Recording("construct", /*name*/ undefined, /*thisArg*/ undefined, argArray, newTarget, result, /*callback*/ undefined);
|
||||
this.recordings.push(recording);
|
||||
try {
|
||||
return Recording.evaluate(setups, "construct", /*name*/ undefined, /*thisArg*/ undefined, argArray, newTarget,
|
||||
() => Reflect.construct(target, argArray, newTarget));
|
||||
}
|
||||
catch (e) {
|
||||
throw result.throw = e;
|
||||
}
|
||||
}
|
||||
|
||||
protected capture<U>(callback: (value: T) => U, result: Returns<any> & ThisArg | Returns<any> | Throws & ThisArg | Throws | ThisArg | undefined) {
|
||||
return this.captureCore(<T>noop, new CapturingFunctionHandler<T, U>(result), callback);
|
||||
}
|
||||
}
|
||||
|
||||
class MethodHandler {
|
||||
public name: PropertyKey;
|
||||
|
||||
@ -337,58 +531,53 @@ class MethodHandler {
|
||||
}
|
||||
|
||||
public apply(target: Function, thisArgument: any, argumentsList: any[]): any {
|
||||
const handler = getHandler(thisArgument);
|
||||
const handler = weakHandler.get(thisArgument);
|
||||
return handler
|
||||
? handler.invoke(thisArgument, this.name, target, argumentsList)
|
||||
: Reflect.apply(target, thisArgument, argumentsList);
|
||||
}
|
||||
}
|
||||
|
||||
class CapturingHandler {
|
||||
class CapturingHandler<T extends object, U> implements ProxyHandler<T> {
|
||||
public recording: Recording | undefined;
|
||||
|
||||
private _name: PropertyKey;
|
||||
private _method: Function;
|
||||
protected readonly callback: Callable | undefined;
|
||||
protected readonly thisArg: any;
|
||||
protected readonly result: Returns<U> | Throws | Fallback | undefined;
|
||||
|
||||
constructor() {
|
||||
this._method = (...args: any[]) => {
|
||||
this.recording = new Recording("invoke", this._name, args);
|
||||
};
|
||||
constructor(result: Partial<Returns<U> & Throws & ThisArg & Callback & Fallback> | undefined) {
|
||||
this.thisArg = hasOwn(result, "this") ? result.this : Recording.noThisArg;
|
||||
this.callback = hasOwn(result, "callback") ? result.callback : undefined;
|
||||
this.result = hasOwn(result, "return") ? { return: result.return } :
|
||||
hasOwn(result, "throw") ? { throw: result.throw } :
|
||||
hasOwn(result, "fallback") && result.fallback ? { fallback: true } :
|
||||
undefined;
|
||||
}
|
||||
|
||||
public apply(_target: object, _thisArg: any, argArray: any[]): any {
|
||||
this.recording = new Recording("apply", /*name*/ undefined, argArray);
|
||||
return undefined;
|
||||
public get(_target: T, name: PropertyKey, _receiver: any): any {
|
||||
this.recording = new Recording("get", name, this.thisArg, [], /*newTarget*/ undefined, this.result, this.callback);
|
||||
return (...argArray: any[]) => { this.recording = new Recording("invoke", name, this.thisArg, argArray, /*newTarget*/ undefined, this.result, this.callback); };
|
||||
}
|
||||
|
||||
public construct(_target: object, argArray: any[], newTarget?: any): any {
|
||||
this.recording = new Recording("construct", /*name*/ undefined, argArray, newTarget);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public get(_target: object, name: PropertyKey, _receiver: any): any {
|
||||
this.recording = new Recording("get", name, []);
|
||||
this._name = name;
|
||||
return this._method;
|
||||
}
|
||||
|
||||
public set(_target: object, name: PropertyKey, value: any, _receiver: any): boolean {
|
||||
this.recording = new Recording("set", name, [value]);
|
||||
public set(_target: T, name: PropertyKey, value: any, _receiver: any): boolean {
|
||||
this.recording = new Recording("set", name, this.thisArg, [value], /*newTarget*/ undefined, this.result, this.callback);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function capture<T, U>(callback: (value: T) => U): Recording {
|
||||
const handler = new CapturingHandler();
|
||||
const { proxy, revoke } = Proxy.revocable<any>(noop, handler);
|
||||
try {
|
||||
callback(proxy);
|
||||
if (!handler.recording) {
|
||||
throw new Error("Nothing was captured.");
|
||||
}
|
||||
return handler.recording;
|
||||
class CapturingFunctionHandler<T extends Callable | Constructable, U> extends CapturingHandler<T, U> {
|
||||
public apply(_target: T, _thisArg: any, argArray: any[]): any {
|
||||
this.recording = new Recording("apply", /*name*/ undefined, this.thisArg, argArray, /*newTarget*/ undefined, this.result, this.callback);
|
||||
return undefined;
|
||||
}
|
||||
finally {
|
||||
revoke();
|
||||
|
||||
public construct(_target: T, argArray: any[], newTarget?: any): any {
|
||||
this.recording = new Recording("construct", /*name*/ undefined, /*thisArg*/ undefined, argArray, newTarget, this.result, this.callback);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function hasOwn<T extends object, K extends keyof T>(object: Partial<T> | undefined, key: K): object is (T | T & never) & { [P in K]: T[P] } {
|
||||
return object !== undefined
|
||||
&& Object.prototype.hasOwnProperty.call(object, key);
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
import { Mock } from "./mock";
|
||||
import { Times } from "./times";
|
||||
import { Arg } from "./arg";
|
||||
|
||||
function noop() {}
|
||||
|
||||
export type Callable = ((...args: any[]) => any);
|
||||
|
||||
export type Constructable = (new (...args: any[]) => any);
|
||||
|
||||
export class Spy<T extends Callable | Constructable = Callable & Constructable> {
|
||||
private _mock: Mock<T>;
|
||||
|
||||
constructor(target = <T>noop) {
|
||||
this._mock = new Mock<T>(target);
|
||||
}
|
||||
|
||||
public get value(): T {
|
||||
return this._mock.value;
|
||||
}
|
||||
|
||||
public verify(callback: (value: T) => any, times: Times): this {
|
||||
this._mock.verify(callback, times);
|
||||
return this;
|
||||
}
|
||||
|
||||
public called(times: Times): this {
|
||||
return this.verify(_ => (<Callable>_)(Arg.rest()), times);
|
||||
}
|
||||
|
||||
public constructed(times: Times): this {
|
||||
return this.verify(_ => new (<Constructable>_)(Arg.rest()), times);
|
||||
}
|
||||
|
||||
public revoke(): void {
|
||||
this._mock.revoke();
|
||||
}
|
||||
}
|
||||
@ -9,11 +9,10 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.any());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["a"], 0);
|
||||
const result = Arg.validate(target, "a");
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.strictEqual(result.next, 1);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -32,21 +31,20 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.is(value => value === "a"));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["a"], 0);
|
||||
const result = Arg.validate(target, "a");
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.strictEqual(result.next, 1);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.is(value => value === "a"));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["b"], 0);
|
||||
const result = Arg.validate(target, "b");
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -65,21 +63,20 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.notNull());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [{}], 0);
|
||||
const result = Arg.validate(target, {});
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.strictEqual(result.next, 1);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.notNull());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [null], 0);
|
||||
const result = Arg.validate(target, null);
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -98,21 +95,20 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.null());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [null], 0);
|
||||
const result = Arg.validate(target, null);
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.strictEqual(result.next, 1);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.null());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [{}], 0);
|
||||
const result = Arg.validate(target, {});
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -131,21 +127,20 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.notUndefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [{}], 0);
|
||||
const result = Arg.validate(target, {});
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.strictEqual(result.next, 1);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.notUndefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [undefined], 0);
|
||||
const result = Arg.validate(target, undefined);
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -164,21 +159,20 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.undefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [undefined], 0);
|
||||
const result = Arg.validate(target, undefined);
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.strictEqual(result.next, 1);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.undefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [{}], 0);
|
||||
const result = Arg.validate(target, {});
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -197,31 +191,30 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.notNullOrUndefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [{}], 0);
|
||||
const result = Arg.validate(target, {});
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.strictEqual(result.next, 1);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid (null)", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.notNullOrUndefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [null], 0);
|
||||
const result = Arg.validate(target, null);
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("invalid (undefined)", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.notNullOrUndefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [undefined], 0);
|
||||
const result = Arg.validate(target, undefined);
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -240,32 +233,30 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.nullOrUndefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [null], 0);
|
||||
const result = Arg.validate(target, null);
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.strictEqual(result.next, 1);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("valid (undefined)", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.nullOrUndefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [undefined], 0);
|
||||
const result = Arg.validate(target, undefined);
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.strictEqual(result.next, 1);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.nullOrUndefined());
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [{}], 0);
|
||||
const result = Arg.validate(target, {});
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -284,26 +275,26 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.between(1, 3));
|
||||
|
||||
// act
|
||||
const min = Arg.validate(target, [1], 0);
|
||||
const mid = Arg.validate(target, [2], 0);
|
||||
const max = Arg.validate(target, [3], 0);
|
||||
const min = Arg.validate(target, 1);
|
||||
const mid = Arg.validate(target, 2);
|
||||
const max = Arg.validate(target, 3);
|
||||
|
||||
// assert
|
||||
assert.isTrue(min.valid);
|
||||
assert.isTrue(mid.valid);
|
||||
assert.isTrue(max.valid);
|
||||
assert.isTrue(min);
|
||||
assert.isTrue(mid);
|
||||
assert.isTrue(max);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.between(1, 3));
|
||||
|
||||
// act
|
||||
const before = Arg.validate(target, [0], 0);
|
||||
const after = Arg.validate(target, [4], 0);
|
||||
const before = Arg.validate(target, 0);
|
||||
const after = Arg.validate(target, 4);
|
||||
|
||||
// assert
|
||||
assert.isFalse(before.valid);
|
||||
assert.isFalse(after.valid);
|
||||
assert.isFalse(before);
|
||||
assert.isFalse(after);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -322,20 +313,20 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.in(["a", "b"]));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["a"], 0);
|
||||
const result = Arg.validate(target, "a");
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.in(["a", "b"]));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["c"], 0);
|
||||
const result = Arg.validate(target, "c");
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -354,20 +345,20 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.notIn(["a", "b"]));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["c"], 0);
|
||||
const result = Arg.validate(target, "c");
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.notIn(["a", "b"]));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["a"], 0);
|
||||
const result = Arg.validate(target, "a");
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -386,20 +377,20 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.match(/^a$/));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["a"], 0);
|
||||
const result = Arg.validate(target, "a");
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.match(/^a$/));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["b"], 0);
|
||||
const result = Arg.validate(target, "b");
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -418,20 +409,20 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.typeof("number"));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [1], 0);
|
||||
const result = Arg.validate(target, 1);
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.typeof("number"));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["a"], 0);
|
||||
const result = Arg.validate(target, "a");
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -451,10 +442,10 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.instanceof(C));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [new C()], 0);
|
||||
const result = Arg.validate(target, new C());
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
@ -462,10 +453,10 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.instanceof(C));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [{}], 0);
|
||||
const result = Arg.validate(target, {});
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -485,22 +476,22 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.has("a"));
|
||||
|
||||
// act
|
||||
const own = Arg.validate(target, [{ a: 1 }], 0);
|
||||
const proto = Arg.validate(target, [{ __proto__: { a: 1 } }], 0);
|
||||
const own = Arg.validate(target, { a: 1 });
|
||||
const proto = Arg.validate(target, { __proto__: { a: 1 } });
|
||||
|
||||
// assert
|
||||
assert.isTrue(own.valid);
|
||||
assert.isTrue(proto.valid);
|
||||
assert.isTrue(own);
|
||||
assert.isTrue(proto);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.has("a"));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [{ b: 1 }], 0);
|
||||
const result = Arg.validate(target, { b: 1 });
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -519,22 +510,22 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.hasOwn("a"));
|
||||
|
||||
// act
|
||||
const own = Arg.validate(target, [{ a: 1 }], 0);
|
||||
const own = Arg.validate(target, { a: 1 });
|
||||
|
||||
// assert
|
||||
assert.isTrue(own.valid);
|
||||
assert.isTrue(own);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.hasOwn("a"));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, [{ b: 1 }], 0);
|
||||
const proto = Arg.validate(target, [{ __proto__: { a: 1 } }], 0);
|
||||
const result = Arg.validate(target, { b: 1 });
|
||||
const proto = Arg.validate(target, { __proto__: { a: 1 } });
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(proto.valid);
|
||||
assert.isFalse(result);
|
||||
assert.isFalse(proto);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -554,14 +545,12 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.rest());
|
||||
|
||||
// act
|
||||
const empty = Arg.validate(target, [], 0);
|
||||
const multiple = Arg.validate(target, ["a", "b"], 0);
|
||||
const empty = Arg.validateAll([target], []);
|
||||
const multiple = Arg.validateAll([target], ["a", "b"]);
|
||||
|
||||
// assert
|
||||
assert.isTrue(empty.valid);
|
||||
assert.strictEqual(empty.next, 0);
|
||||
assert.isTrue(multiple.valid);
|
||||
assert.strictEqual(multiple.next, 2);
|
||||
assert.isTrue(empty);
|
||||
assert.isTrue(multiple);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -580,24 +569,22 @@ describe("arg", () => {
|
||||
const target = Arg.from(Arg.rest(Arg.typeof("string")));
|
||||
|
||||
// act
|
||||
const empty = Arg.validate(target, [], 0);
|
||||
const multiple = Arg.validate(target, ["a", "b"], 0);
|
||||
const empty = Arg.validateAll([target], []);
|
||||
const multiple = Arg.validateAll([target], ["a", "b"]);
|
||||
|
||||
// assert
|
||||
assert.isTrue(empty.valid);
|
||||
assert.strictEqual(empty.next, 0);
|
||||
assert.isTrue(multiple.valid);
|
||||
assert.strictEqual(multiple.next, 2);
|
||||
assert.isTrue(empty);
|
||||
assert.isTrue(multiple);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from(Arg.rest(Arg.typeof("string")));
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["a", 1], 0);
|
||||
const result = Arg.validateAll([target], ["a", 1]);
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
@ -617,20 +604,20 @@ describe("arg", () => {
|
||||
const target = Arg.from("a");
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["a"], 0);
|
||||
const result = Arg.validate(target, "a");
|
||||
|
||||
// assert
|
||||
assert.isTrue(result.valid);
|
||||
assert.isTrue(result);
|
||||
});
|
||||
it("invalid", () => {
|
||||
// arrange
|
||||
const target = Arg.from("a");
|
||||
|
||||
// act
|
||||
const result = Arg.validate(target, ["b"], 0);
|
||||
const result = Arg.validate(target, "b");
|
||||
|
||||
// assert
|
||||
assert.isFalse(result.valid);
|
||||
assert.isFalse(result);
|
||||
});
|
||||
it("toString", () => {
|
||||
// arrange
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import "./argTests";
|
||||
import "./timesTests";
|
||||
import "./mockTests";
|
||||
import "./stubTests";
|
||||
import "./injectTests";
|
||||
import "./timersTests";
|
||||
79
scripts/typemock/src/tests/injectTests.ts
Normal file
79
scripts/typemock/src/tests/injectTests.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import "./sourceMapSupport";
|
||||
import { Mock } from "../mock";
|
||||
import { Inject } from "../inject";
|
||||
import { Times } from "../times";
|
||||
import { assert } from "chai";
|
||||
|
||||
describe("inject", () => {
|
||||
it("install replaces value", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const inject = new Inject(mock.proxy, "a", 2);
|
||||
|
||||
// act
|
||||
inject.install();
|
||||
|
||||
// assert
|
||||
mock.verify(_ => _.a = 2, Times.once());
|
||||
});
|
||||
it("install is installed", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const inject = new Inject(mock.proxy, "a", 2);
|
||||
|
||||
// act
|
||||
inject.install();
|
||||
|
||||
// assert
|
||||
assert.isTrue(inject.installed);
|
||||
});
|
||||
it("install twice only installs once", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const inject = new Inject(mock.proxy, "a", 2);
|
||||
|
||||
// act
|
||||
inject.install();
|
||||
inject.install();
|
||||
|
||||
// assert
|
||||
mock.verify(_ => _.a = 2, Times.once());
|
||||
});
|
||||
it("uninstall restores value", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const inject = new Inject(mock.proxy, "a", 2);
|
||||
inject.install();
|
||||
|
||||
// act
|
||||
inject.uninstall();
|
||||
|
||||
// assert
|
||||
mock.verify(_ => _.a = 1, Times.once());
|
||||
});
|
||||
it("uninstall is not installed", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const inject = new Inject(mock.proxy, "a", 2);
|
||||
inject.install();
|
||||
|
||||
// act
|
||||
inject.uninstall();
|
||||
|
||||
// assert
|
||||
assert.isFalse(inject.installed);
|
||||
});
|
||||
it("uninstall twice only uninstalls once", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const inject = new Inject(mock.proxy, "a", 2);
|
||||
inject.install();
|
||||
|
||||
// act
|
||||
inject.uninstall();
|
||||
inject.uninstall();
|
||||
|
||||
// assert
|
||||
mock.verify(_ => _.a = 1, Times.once());
|
||||
});
|
||||
});
|
||||
@ -1,262 +1,363 @@
|
||||
import "./sourceMapSupport";
|
||||
import { Mock } from "../mock";
|
||||
import { Stub } from "../stub";
|
||||
import { Inject } from "../inject";
|
||||
import { Arg } from "../arg";
|
||||
import { Times } from "../times";
|
||||
import { recordError } from "./utils";
|
||||
import { assert } from "chai";
|
||||
|
||||
describe("mock", () => {
|
||||
it("mock get with no setups", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target);
|
||||
describe("no setup", () => {
|
||||
it("get (exists)", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target);
|
||||
|
||||
// act
|
||||
const result = mock.value.a;
|
||||
// act
|
||||
const result = mock.proxy.a;
|
||||
|
||||
// assert
|
||||
assert.equal(1, result);
|
||||
});
|
||||
it("mock setup property get with return", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, { get a() { return 2; } });
|
||||
|
||||
// act
|
||||
const result = mock.value.a;
|
||||
|
||||
// assert
|
||||
assert.equal(2, result);
|
||||
});
|
||||
it("mock setup property get with throw", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const error = new Error("error");
|
||||
const mock = new Mock(target, { get a(): number { throw error; } });
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.value.a);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(error, e);
|
||||
});
|
||||
it("mock setup property set", () => {
|
||||
// arrange
|
||||
let _a: number | undefined;
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, { set a(value: number) { _a = value; } });
|
||||
|
||||
// act
|
||||
mock.value.a = 2;
|
||||
|
||||
// assert
|
||||
assert.equal(2, _a);
|
||||
assert.equal(1, target.a);
|
||||
});
|
||||
it("mock setup property set with throw", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const error = new Error("error");
|
||||
const mock = new Mock(target, { set a(value: number) { throw error; } });
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.value.a = 2);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(error, e);
|
||||
});
|
||||
it("mock setup method call no setups", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const mock = new Mock(target);
|
||||
|
||||
// act
|
||||
const result = mock.value.a();
|
||||
|
||||
// assert
|
||||
assert.equal(1, result);
|
||||
});
|
||||
it("mock setup method callback", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const mock = new Mock(target, { a() { return 2; } });
|
||||
|
||||
// act
|
||||
const result = mock.value.a();
|
||||
|
||||
// assert
|
||||
assert.equal(2, result);
|
||||
});
|
||||
it("mock setup method callback throws", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const error = new Error("error");
|
||||
const mock = new Mock(target, { a(): number { throw error; } });
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.value.a());
|
||||
|
||||
// assert
|
||||
assert.strictEqual(error, e);
|
||||
});
|
||||
it("mock setup new property", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, <any>{ b: 2 });
|
||||
|
||||
// act
|
||||
const result = (<any>mock.value).b;
|
||||
|
||||
// assert
|
||||
assert.equal(2, result);
|
||||
});
|
||||
it("mock setup new method", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, <any>{ b() { return 2; } });
|
||||
|
||||
// act
|
||||
const result = (<any>mock.value).b();
|
||||
|
||||
// assert
|
||||
assert.equal(2, result);
|
||||
});
|
||||
it("mock verify get no setups, not called throws", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target);
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a, Times.once()));
|
||||
|
||||
// assert
|
||||
assert.instanceOf(e, Error);
|
||||
});
|
||||
it("mock verify get no setups, called passes", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target);
|
||||
const result = mock.value.a;
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a, Times.once()));
|
||||
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
});
|
||||
it("mock verify setup get, called passes", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, { get a() { return 2 } });
|
||||
const result = mock.value.a;
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a, Times.once()));
|
||||
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
});
|
||||
it("mock verify method no setups, not called throws", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const mock = new Mock(target);
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a(), Times.once()));
|
||||
|
||||
// assert
|
||||
assert.instanceOf(e, Error);
|
||||
});
|
||||
it("mock verify method no setups, called passes", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const mock = new Mock(target);
|
||||
const result = mock.value.a();
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a(), Times.once()));
|
||||
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
});
|
||||
it("mock verify setup method, called passes", () => {
|
||||
// arrange
|
||||
const target = { a(x: number) { return x + 1; } };
|
||||
const mock = new Mock(target, {
|
||||
a(x: number) {
|
||||
return x + 2;
|
||||
}
|
||||
// assert
|
||||
assert.equal(result, 1);
|
||||
});
|
||||
const result = mock.value.a(3);
|
||||
it("get (missing)", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a?: number }>();
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a(Arg.number()), Times.once()));
|
||||
// act
|
||||
const result = mock.proxy.a;
|
||||
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
// assert
|
||||
assert.isUndefined(result);
|
||||
});
|
||||
it("set (exists)", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target);
|
||||
|
||||
// act
|
||||
mock.proxy.a = 2;
|
||||
const result = mock.proxy.a;
|
||||
|
||||
// assert
|
||||
assert.equal(result, 2);
|
||||
});
|
||||
it("set (missing)", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a?: number }>();
|
||||
|
||||
// act
|
||||
mock.proxy.a = 2;
|
||||
const result = mock.proxy.a;
|
||||
|
||||
// assert
|
||||
assert.equal(result, 2);
|
||||
});
|
||||
it("method (existing)", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const mock = new Mock(target);
|
||||
|
||||
// act
|
||||
const result = mock.proxy.a();
|
||||
|
||||
// assert
|
||||
assert.equal(1, result);
|
||||
});
|
||||
});
|
||||
it("mock setup method using callback", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a(x: number): number; }>();
|
||||
mock.setup(_ => _.a(1), { returns: 2 });
|
||||
|
||||
// act
|
||||
const result = mock.value.a(1);
|
||||
describe("setup", () => {
|
||||
describe("using object", () => {
|
||||
it("get", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, { get a() { return 2; } });
|
||||
|
||||
// assert
|
||||
assert.strictEqual(result, 2);
|
||||
// act
|
||||
const result = mock.proxy.a;
|
||||
|
||||
// assert
|
||||
assert.equal(2, result);
|
||||
});
|
||||
it("get with throw", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const error = new Error("error");
|
||||
const mock = new Mock(target, { get a(): number { throw error; } });
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.proxy.a);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(error, e);
|
||||
});
|
||||
it("set", () => {
|
||||
// arrange
|
||||
let _a: number | undefined;
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, { set a(value: number) { _a = value; } });
|
||||
|
||||
// act
|
||||
mock.proxy.a = 2;
|
||||
|
||||
// assert
|
||||
assert.equal(2, _a);
|
||||
assert.equal(1, target.a);
|
||||
});
|
||||
it("set with throw", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const error = new Error("error");
|
||||
const mock = new Mock(target, { set a(value: number) { throw error; } });
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.proxy.a = 2);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(error, e);
|
||||
});
|
||||
it("method", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const mock = new Mock(target, { a() { return 2; } });
|
||||
|
||||
// act
|
||||
const result = mock.proxy.a();
|
||||
|
||||
// assert
|
||||
assert.equal(2, result);
|
||||
});
|
||||
it("method throws", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const error = new Error("error");
|
||||
const mock = new Mock(target, { a(): number { throw error; } });
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.proxy.a());
|
||||
|
||||
// assert
|
||||
assert.strictEqual(error, e);
|
||||
});
|
||||
it("new property", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, <any>{ b: 2 });
|
||||
|
||||
// act
|
||||
const result = (<any>mock.proxy).b;
|
||||
|
||||
// assert
|
||||
assert.equal(2, result);
|
||||
});
|
||||
it("new method", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, <any>{ b() { return 2; } });
|
||||
|
||||
// act
|
||||
const result = (<any>mock.proxy).b();
|
||||
|
||||
// assert
|
||||
assert.equal(2, result);
|
||||
});
|
||||
});
|
||||
describe("using callback", () => {
|
||||
it("get only", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a: number }>();
|
||||
mock.setup(_ => _.a, { return: 2 });
|
||||
|
||||
// act
|
||||
const result1 = mock.proxy.a;
|
||||
const err = recordError(() => mock.proxy.a = 3);
|
||||
const result2 = mock.proxy.a;
|
||||
|
||||
// assert
|
||||
assert.strictEqual(result1, 2);
|
||||
assert.strictEqual(result2, 2);
|
||||
assert.instanceOf(err, Error);
|
||||
});
|
||||
it("set only", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a: number }>();
|
||||
mock.setup(_ => _.a = 2);
|
||||
|
||||
// act
|
||||
const result = mock.proxy.a;
|
||||
const err2 = recordError(() => mock.proxy.a = 2);
|
||||
const err3 = recordError(() => mock.proxy.a = 3);
|
||||
|
||||
// assert
|
||||
assert.isUndefined(result);
|
||||
assert.isUndefined(err2);
|
||||
assert.instanceOf(err3, Error);
|
||||
});
|
||||
it("get and set", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a: number }>();
|
||||
mock.setup(_ => _.a, { return: 2 });
|
||||
mock.setup(_ => _.a = Arg.any());
|
||||
|
||||
// act
|
||||
const result = mock.proxy.a;
|
||||
mock.proxy.a = 3;
|
||||
|
||||
// assert
|
||||
assert.strictEqual(result, 2);
|
||||
});
|
||||
it("method", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a(x: number): number; }>();
|
||||
mock.setup(_ => _.a(1), { return: 2 });
|
||||
|
||||
// act
|
||||
const result = mock.proxy.a(1);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(result, 2);
|
||||
});
|
||||
it("function", () => {
|
||||
// arrange
|
||||
const mock = new Mock<(x: number) => number>(x => 0);
|
||||
mock.setup(_ => _(Arg.number()), { return: 2 });
|
||||
|
||||
// act
|
||||
const result = mock.proxy(1);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(result, 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
it("mock setup setter/getter using callback", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a: number }>();
|
||||
mock.setup(_ => _.a, { returns: 2 });
|
||||
mock.setup(_ => _.a = Arg.any());
|
||||
|
||||
// act
|
||||
const result = mock.value.a;
|
||||
mock.value.a = 3;
|
||||
describe("verify", () => {
|
||||
describe("no setup", () => {
|
||||
describe("get", () => {
|
||||
it("not called throws", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(result, 2);
|
||||
});
|
||||
it("mock setup getter only using callback", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a: number }>();
|
||||
mock.setup(_ => _.a, { returns: 2 });
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a, Times.once()));
|
||||
|
||||
// act
|
||||
const result = mock.value.a;
|
||||
const err = recordError(() => mock.value.a = 3);
|
||||
// assert
|
||||
assert.instanceOf(e, Error);
|
||||
});
|
||||
it("called passes", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target);
|
||||
const result = mock.proxy.a;
|
||||
|
||||
// assert
|
||||
assert.strictEqual(result, 2);
|
||||
assert.instanceOf(err, Error);
|
||||
});
|
||||
it("mock setup setter only using callback", () => {
|
||||
// arrange
|
||||
const mock = new Mock<{ a: number }>();
|
||||
mock.setup(_ => _.a = 2);
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a, Times.once()));
|
||||
|
||||
// act
|
||||
const err1 = recordError(() => mock.value.a);
|
||||
const err2 = recordError(() => mock.value.a = 2);
|
||||
const err3 = recordError(() => mock.value.a = 3);
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
});
|
||||
});
|
||||
describe("set", () => {
|
||||
it("not called throws", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target);
|
||||
|
||||
// assert
|
||||
assert.instanceOf(err1, Error);
|
||||
assert.isUndefined(err2);
|
||||
assert.instanceOf(err3, Error);
|
||||
});
|
||||
it("mock setup function only using callback", () => {
|
||||
// arrange
|
||||
const mock = new Mock<(x: number) => number>(x => 0);
|
||||
mock.setup(_ => _(Arg.number()), { returns: 2 });
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a = 2, Times.once()));
|
||||
|
||||
// act
|
||||
const result = mock.value(1);
|
||||
// assert
|
||||
assert.instanceOf(e, Error);
|
||||
});
|
||||
it("called passes", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target);
|
||||
mock.proxy.a = 2;
|
||||
|
||||
// assert
|
||||
assert.strictEqual(result, 2);
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a = 2, Times.once()));
|
||||
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
});
|
||||
});
|
||||
describe("method", () => {
|
||||
it("not called throws", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const mock = new Mock(target);
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a(), Times.once()));
|
||||
|
||||
// assert
|
||||
assert.instanceOf(e, Error);
|
||||
});
|
||||
it("called passes", () => {
|
||||
// arrange
|
||||
const target = { a() { return 1; } };
|
||||
const mock = new Mock(target);
|
||||
const result = mock.proxy.a();
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a(), Times.once()));
|
||||
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
});
|
||||
});
|
||||
describe("function", () => {
|
||||
it("not called throws", () => {
|
||||
// arrange
|
||||
const mock = Mock.function();
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _(), Times.once()));
|
||||
|
||||
// assert
|
||||
assert.instanceOf(e, Error);
|
||||
});
|
||||
it("called passes", () => {
|
||||
// arrange
|
||||
const mock = Mock.function();
|
||||
const result = mock.proxy();
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _(), Times.once()));
|
||||
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
});
|
||||
});
|
||||
});
|
||||
it("setup get, called passes", () => {
|
||||
// arrange
|
||||
const target = { a: 1 };
|
||||
const mock = new Mock(target, { get a() { return 2 } });
|
||||
const result = mock.proxy.a;
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a, Times.once()));
|
||||
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
});
|
||||
it("setup method, called passes", () => {
|
||||
// arrange
|
||||
const target = { a(x: number) { return x + 1; } };
|
||||
const mock = new Mock(target, {
|
||||
a(x: number) {
|
||||
return x + 2;
|
||||
}
|
||||
});
|
||||
const result = mock.proxy.a(3);
|
||||
|
||||
// act
|
||||
const e = recordError(() => mock.verify(_ => _.a(Arg.number()), Times.once()));
|
||||
|
||||
// assert
|
||||
assert.isUndefined(e);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,79 +0,0 @@
|
||||
import "./sourceMapSupport";
|
||||
import { Mock } from "../mock";
|
||||
import { Stub } from "../stub";
|
||||
import { Times } from "../times";
|
||||
import { assert } from "chai";
|
||||
|
||||
describe("stub", () => {
|
||||
it("stub install replaces value", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const stub = new Stub(mock.value, "a", 2);
|
||||
|
||||
// act
|
||||
stub.install();
|
||||
|
||||
// assert
|
||||
mock.verify(_ => _.a = 2, Times.once());
|
||||
});
|
||||
it("stub install is installed", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const stub = new Stub(mock.value, "a", 2);
|
||||
|
||||
// act
|
||||
stub.install();
|
||||
|
||||
// assert
|
||||
assert.isTrue(stub.installed);
|
||||
});
|
||||
it("stub install twice only installs once", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const stub = new Stub(mock.value, "a", 2);
|
||||
|
||||
// act
|
||||
stub.install();
|
||||
stub.install();
|
||||
|
||||
// assert
|
||||
mock.verify(_ => _.a = 2, Times.once());
|
||||
});
|
||||
it("stub uninstall restores value", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const stub = new Stub(mock.value, "a", 2);
|
||||
stub.install();
|
||||
|
||||
// act
|
||||
stub.uninstall();
|
||||
|
||||
// assert
|
||||
mock.verify(_ => _.a = 1, Times.once());
|
||||
});
|
||||
it("stub uninstall is not installed", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const stub = new Stub(mock.value, "a", 2);
|
||||
stub.install();
|
||||
|
||||
// act
|
||||
stub.uninstall();
|
||||
|
||||
// assert
|
||||
assert.isFalse(stub.installed);
|
||||
});
|
||||
it("stub uninstall twice only uninstalls once", () => {
|
||||
// arrange
|
||||
const mock = new Mock({ a: 1 });
|
||||
const stub = new Stub(mock.value, "a", 2);
|
||||
stub.install();
|
||||
|
||||
// act
|
||||
stub.uninstall();
|
||||
stub.uninstall();
|
||||
|
||||
// assert
|
||||
mock.verify(_ => _.a = 1, Times.once());
|
||||
});
|
||||
});
|
||||
@ -1,5 +1,5 @@
|
||||
import "./sourceMapSupport";
|
||||
import { Spy } from "../spy";
|
||||
import { Mock } from "../mock";
|
||||
import { Arg } from "../arg";
|
||||
import { Times } from "../times";
|
||||
import { Timers } from "../timers";
|
||||
@ -10,52 +10,52 @@ describe("timers", () => {
|
||||
it("set adds entry, does not invoke", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
const handle = target.setImmediate(spy.value);
|
||||
const handle = target.setImmediate(spy.proxy);
|
||||
const pending = target.getPending();
|
||||
|
||||
// assert
|
||||
assert.strictEqual(pending.length, 1);
|
||||
assert.strictEqual(pending[0].kind, "immediate");
|
||||
assert.isDefined(handle);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("set/clear", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
const handle = target.setImmediate(spy.value);
|
||||
const handle = target.setImmediate(spy.proxy);
|
||||
target.clearImmedate(handle);
|
||||
const pending = target.getPending();
|
||||
|
||||
// assert
|
||||
assert.strictEqual(pending.length, 0);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("set one and execute", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
target.setImmediate(spy.value);
|
||||
target.setImmediate(spy.proxy);
|
||||
const count = target.executeImmediates();
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 1);
|
||||
spy.called(Times.once());
|
||||
spy.verify(_ => _(), Times.once());
|
||||
});
|
||||
it("set one with arg and execute", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
target.setImmediate(spy.value, "a");
|
||||
target.setImmediate(spy.proxy, "a");
|
||||
const count = target.executeImmediates();
|
||||
|
||||
// assert
|
||||
@ -65,108 +65,108 @@ describe("timers", () => {
|
||||
it("nested with maxDepth = 0", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy(() => { target.setImmediate(spy.value); });
|
||||
const spy = Mock.spy(() => { target.setImmediate(spy.proxy); });
|
||||
|
||||
// act
|
||||
target.setImmediate(spy.value);
|
||||
target.setImmediate(spy.proxy);
|
||||
const count = target.executeImmediates(/*maxDepth*/ 0);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 1);
|
||||
spy.called(Times.once());
|
||||
spy.verify(_ => _(), Times.once());
|
||||
});
|
||||
it("nested with maxDepth = 1", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy(() => { target.setImmediate(spy.value); });
|
||||
const spy = Mock.spy(() => { target.setImmediate(spy.proxy); });
|
||||
|
||||
// act
|
||||
target.setImmediate(spy.value);
|
||||
target.setImmediate(spy.proxy);
|
||||
const count = target.executeImmediates(/*maxDepth*/ 1);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 2);
|
||||
spy.called(Times.exactly(2));
|
||||
spy.verify(_ => _(), Times.exactly(2));
|
||||
});
|
||||
});
|
||||
describe("timeout", () => {
|
||||
it("set adds entry, does not invoke", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
const handle = target.setTimeout(spy.value, 0);
|
||||
const handle = target.setTimeout(spy.proxy, 0);
|
||||
const pending = target.getPending();
|
||||
|
||||
// assert
|
||||
assert.strictEqual(pending.length, 1);
|
||||
assert.strictEqual(pending[0].kind, "timeout");
|
||||
assert.isDefined(handle);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("set/clear", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
const handle = target.setTimeout(spy.value, 0);
|
||||
const handle = target.setTimeout(spy.proxy, 0);
|
||||
target.clearTimeout(handle);
|
||||
const pending = target.getPending();
|
||||
|
||||
// assert
|
||||
assert.strictEqual(pending.length, 0);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("set adds future entry, advance prior to due does not invoke", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
target.setTimeout(spy.value, 10);
|
||||
target.setTimeout(spy.proxy, 10);
|
||||
const count = target.advance(9);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 0);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("set adds future entry, advance to due invokes", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
target.setTimeout(spy.value, 10);
|
||||
target.setTimeout(spy.proxy, 10);
|
||||
const count = target.advance(10);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 1);
|
||||
spy.called(Times.once());
|
||||
spy.verify(_ => _(), Times.once());
|
||||
});
|
||||
it("5 nested sets throttle", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy(() => { target.setTimeout(spy.value, 0); });
|
||||
const spy = new Mock(() => { target.setTimeout(spy.proxy, 0); });
|
||||
|
||||
// act
|
||||
target.setTimeout(spy.value, 0);
|
||||
target.setTimeout(spy.proxy, 0);
|
||||
const count = target.advance(1);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 5);
|
||||
spy.called(Times.exactly(5));
|
||||
spy.verify(_ => _(), Times.exactly(5));
|
||||
});
|
||||
});
|
||||
describe("interval", () => {
|
||||
it("set adds entry, does not invoke", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
const handle = target.setInterval(spy.value, 0);
|
||||
const handle = target.setInterval(spy.proxy, 0);
|
||||
const pending = target.getPending({ kind: "interval", ms: 10 });
|
||||
|
||||
// assert
|
||||
@ -174,132 +174,132 @@ describe("timers", () => {
|
||||
assert.strictEqual(pending[0].kind, "interval");
|
||||
assert.strictEqual(pending[0].interval, 10);
|
||||
assert.isDefined(handle);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("set/clear", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
const handle = target.setInterval(spy.value, 0);
|
||||
const handle = target.setInterval(spy.proxy, 0);
|
||||
target.clearInterval(handle);
|
||||
const pending = target.getPending({ kind: "interval", ms: 10 });
|
||||
|
||||
// assert
|
||||
assert.strictEqual(pending.length, 0);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("set adds future entry, advance prior to due does not invoke", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
target.setInterval(spy.value, 10);
|
||||
target.setInterval(spy.proxy, 10);
|
||||
const count = target.advance(9);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 0);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("set adds future entry, advance to due invokes", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
target.setInterval(spy.value, 10);
|
||||
target.setInterval(spy.proxy, 10);
|
||||
const count = target.advance(10);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 1);
|
||||
spy.called(Times.once());
|
||||
spy.verify(_ => _(), Times.once());
|
||||
});
|
||||
it("set adds future entry, advance to due twice invokes twice", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
target.setInterval(spy.value, 10);
|
||||
target.setInterval(spy.proxy, 10);
|
||||
const count = target.advance(20);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 2);
|
||||
spy.called(Times.exactly(2));
|
||||
spy.verify(_ => _(), Times.exactly(2));
|
||||
});
|
||||
it("set adds future entry, remove before second due time", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy(() => { target.clearInterval(handle); });
|
||||
const spy = new Mock(() => { target.clearInterval(handle); });
|
||||
|
||||
// act
|
||||
const handle = target.setInterval(spy.value, 10);
|
||||
const handle = target.setInterval(spy.proxy, 10);
|
||||
const count = target.advance(20);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 1);
|
||||
spy.called(Times.exactly(1));
|
||||
spy.verify(_ => _(), Times.exactly(1));
|
||||
});
|
||||
});
|
||||
describe("frame", () => {
|
||||
it("request adds entry, does not invoke", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
const handle = target.requestAnimationFrame(spy.value);
|
||||
const handle = target.requestAnimationFrame(spy.proxy);
|
||||
const pending = target.getPending({ ms: 16 });
|
||||
|
||||
// assert
|
||||
assert.strictEqual(pending.length, 1);
|
||||
assert.strictEqual(pending[0].kind, "frame");
|
||||
assert.isDefined(handle);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("request/cancel", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
const handle = target.requestAnimationFrame(spy.value);
|
||||
const handle = target.requestAnimationFrame(spy.proxy);
|
||||
target.cancelAnimationFrame(handle);
|
||||
const pending = target.getPending();
|
||||
|
||||
// assert
|
||||
assert.strictEqual(pending.length, 0);
|
||||
spy.called(Times.none());
|
||||
spy.verify(_ => _(), Times.none());
|
||||
});
|
||||
it("request and advance past one frame", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
target.requestAnimationFrame(spy.value);
|
||||
target.requestAnimationFrame(spy.proxy);
|
||||
const count = target.advance(16);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 1);
|
||||
spy.called(Times.once());
|
||||
spy.verify(_ => _(Arg.number()), Times.once());
|
||||
});
|
||||
it("requests clamped to 16ms", () => {
|
||||
// arrange
|
||||
const target = new Timers();
|
||||
const spy = new Spy();
|
||||
const spy = Mock.function();
|
||||
|
||||
// act
|
||||
target.requestAnimationFrame(spy.value);
|
||||
target.requestAnimationFrame(spy.proxy);
|
||||
target.advance(10);
|
||||
target.requestAnimationFrame(spy.value);
|
||||
target.requestAnimationFrame(spy.proxy);
|
||||
const count = target.advance(16);
|
||||
|
||||
// assert
|
||||
assert.strictEqual(count, 2);
|
||||
spy.called(Times.exactly(2));
|
||||
spy.verify(_ => _(Arg.number()), Times.exactly(2));
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -3,12 +3,12 @@
|
||||
/// <reference path="./vfs.ts" />
|
||||
/// <reference path="./typemock.ts" />
|
||||
|
||||
// NOTE: The contents of this file are all exported from the namespace 'mocks'. This is to
|
||||
// NOTE: The contents of this file are all exported from the namespace 'fakes'. This is to
|
||||
// support the eventual conversion of harness into a modular system.
|
||||
|
||||
// harness mocks
|
||||
namespace mocks {
|
||||
export interface MockServerHostOptions {
|
||||
// harness fakes
|
||||
namespace fakes {
|
||||
export interface FakeServerHostOptions {
|
||||
/**
|
||||
* The `VirtualFleSystem` to use. If not specified, a new case-sensitive `VirtualFileSystem`
|
||||
* is created.
|
||||
@ -32,7 +32,7 @@ namespace mocks {
|
||||
lib?: boolean;
|
||||
}
|
||||
|
||||
export class MockServerHost implements ts.server.ServerHost, ts.FormatDiagnosticsHost {
|
||||
export class FakeServerHost implements ts.server.ServerHost, ts.FormatDiagnosticsHost {
|
||||
public static readonly defaultExecutingFilePath = "/.ts/tsc.js";
|
||||
public static readonly defaultCurrentDirectory = "/";
|
||||
public static readonly safeListPath = "/safelist.json";
|
||||
@ -67,16 +67,16 @@ namespace mocks {
|
||||
private readonly _executingFilePath: string;
|
||||
private readonly _getCanonicalFileName: (file: string) => string;
|
||||
|
||||
constructor(options: MockServerHostOptions = {}) {
|
||||
constructor(options: FakeServerHostOptions = {}) {
|
||||
const {
|
||||
vfs: _vfs = {},
|
||||
executingFilePath = MockServerHost.defaultExecutingFilePath,
|
||||
executingFilePath = FakeServerHost.defaultExecutingFilePath,
|
||||
newLine = "\n",
|
||||
safeList = false,
|
||||
lib = false
|
||||
} = options;
|
||||
|
||||
const { currentDirectory = MockServerHost.defaultCurrentDirectory, useCaseSensitiveFileNames = false } = _vfs;
|
||||
const { currentDirectory = FakeServerHost.defaultCurrentDirectory, useCaseSensitiveFileNames = false } = _vfs;
|
||||
|
||||
this.vfs = _vfs instanceof vfs.VirtualFileSystem ? _vfs :
|
||||
new vfs.VirtualFileSystem(currentDirectory, useCaseSensitiveFileNames);
|
||||
@ -87,11 +87,11 @@ namespace mocks {
|
||||
this._getCanonicalFileName = ts.createGetCanonicalFileName(this.useCaseSensitiveFileNames);
|
||||
|
||||
if (safeList) {
|
||||
this.vfs.addFile(MockServerHost.safeListPath, MockServerHost.safeListContent);
|
||||
this.vfs.addFile(FakeServerHost.safeListPath, FakeServerHost.safeListContent);
|
||||
}
|
||||
|
||||
if (lib) {
|
||||
this.vfs.addFile(MockServerHost.libPath, MockServerHost.libContent);
|
||||
this.vfs.addFile(FakeServerHost.libPath, FakeServerHost.libContent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,7 +139,7 @@ namespace mocks {
|
||||
|
||||
public exit(exitCode?: number) {
|
||||
this.exitCode = exitCode;
|
||||
throw MockServerHost.processExitSentinel;
|
||||
throw FakeServerHost.processExitSentinel;
|
||||
}
|
||||
// #endregion DirectoryStructureHost members
|
||||
|
||||
@ -241,7 +241,7 @@ namespace mocks {
|
||||
this.timers.advanceToEnd();
|
||||
}
|
||||
catch (e) {
|
||||
if (e !== MockServerHost.processExitSentinel) {
|
||||
if (e !== FakeServerHost.processExitSentinel) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@ -73,7 +73,7 @@
|
||||
"../services/codefixes/disableJsDiagnostics.ts",
|
||||
|
||||
"harness.ts",
|
||||
|
||||
|
||||
"core.ts",
|
||||
"utils.ts",
|
||||
"typemock.ts",
|
||||
@ -82,7 +82,7 @@
|
||||
"vpath.ts",
|
||||
"vfs.ts",
|
||||
"compiler.ts",
|
||||
"mocks.ts",
|
||||
"fakes.ts",
|
||||
|
||||
"virtualFileSystemWithWatch.ts",
|
||||
"sourceMapRecorder.ts",
|
||||
|
||||
@ -7,92 +7,40 @@
|
||||
|
||||
// typemock library
|
||||
namespace typemock {
|
||||
type Imported<T extends Function> = T["prototype"];
|
||||
const module = require("typemock");
|
||||
typemock.Arg = module.Arg;
|
||||
typemock.Times = module.Times;
|
||||
typemock.Mock = module.Mock;
|
||||
typemock.Spy = module.Spy;
|
||||
typemock.Inject = module.Inject;
|
||||
typemock.Timers = module.Timers;
|
||||
typemock.spy = module.spy;
|
||||
}
|
||||
|
||||
function unwrap<T>(module: any, _: () => PromiseLike<T>): T { return module; }
|
||||
|
||||
const module = unwrap(require("../../scripts/typemock"), () => import("../../scripts/typemock"));
|
||||
|
||||
export const Arg = module.Arg;
|
||||
|
||||
export interface Arg extends Imported<typeof Arg> {
|
||||
}
|
||||
|
||||
export interface Returns<U> {
|
||||
returns: U;
|
||||
}
|
||||
|
||||
export interface Throws {
|
||||
throws: any;
|
||||
}
|
||||
|
||||
export const Mock = module.Mock;
|
||||
|
||||
export interface Mock<T> extends Imported<typeof Mock> {
|
||||
readonly value: T;
|
||||
setup<U = any>(callback: (value: T) => U, result?: Returns<U> | Throws): Mock<T>;
|
||||
setup(setups: Partial<T>): Mock<T>;
|
||||
verify(callback: (value: T) => any, times: Times): Mock<T>;
|
||||
}
|
||||
|
||||
export type Callable = ((...args: any[]) => any);
|
||||
|
||||
export type Constructable = (new (...args: any[]) => any);
|
||||
|
||||
export const Spy = module.Spy;
|
||||
|
||||
export interface Spy<T extends Callable | Constructable = Callable & Constructable> extends Imported<typeof Spy> {
|
||||
readonly value: T;
|
||||
verify(callback: (value: T) => any, times: Times): this;
|
||||
}
|
||||
|
||||
export const Times = module.Times;
|
||||
|
||||
export interface Times extends Imported<typeof Times> {
|
||||
}
|
||||
|
||||
export interface Immediate {
|
||||
readonly kind: "immediate";
|
||||
readonly handle: number;
|
||||
readonly callback: (...args: any[]) => void;
|
||||
readonly args: ReadonlyArray<any>;
|
||||
}
|
||||
|
||||
export interface Timeout {
|
||||
readonly kind: "timeout";
|
||||
readonly handle: number;
|
||||
readonly callback: (...args: any[]) => void;
|
||||
readonly args: ReadonlyArray<any>;
|
||||
}
|
||||
|
||||
export interface Interval {
|
||||
readonly kind: "interval";
|
||||
readonly handle: number;
|
||||
readonly callback: (...args: any[]) => void;
|
||||
readonly args: ReadonlyArray<any>;
|
||||
readonly interval: number;
|
||||
}
|
||||
|
||||
export interface AnimationFrame {
|
||||
readonly kind: "frame";
|
||||
readonly handle: number;
|
||||
readonly callback: (time: number) => void;
|
||||
}
|
||||
|
||||
export declare type Timer = Immediate | Timeout | Interval | AnimationFrame;
|
||||
|
||||
export const Timers = module.Timers;
|
||||
|
||||
export interface Timers extends Imported<typeof Timers> {
|
||||
}
|
||||
|
||||
export const Stub = module.Stub;
|
||||
|
||||
export interface Stub<T, K extends keyof T> extends Imported<typeof Stub> {
|
||||
readonly target: T;
|
||||
readonly key: K;
|
||||
stubValue: T[K];
|
||||
readonly originalValue: T[K];
|
||||
readonly currentValue: T[K];
|
||||
declare module "typemock_" {
|
||||
import * as typemock_ from "typemock";
|
||||
global {
|
||||
namespace typemock {
|
||||
export import Arg = typemock_.Arg;
|
||||
export import Times = typemock_.Times;
|
||||
export import Mock = typemock_.Mock;
|
||||
export import Spy = typemock_.Spy;
|
||||
export import Returns = typemock_.Returns;
|
||||
export import Throws = typemock_.Throws;
|
||||
export import ThisArg = typemock_.ThisArg;
|
||||
export import Callback = typemock_.Callback;
|
||||
export import Fallback = typemock_.Fallback;
|
||||
export import Setup = typemock_.Setup;
|
||||
export import Callable = typemock_.Callable;
|
||||
export import Constructable = typemock_.Constructable;
|
||||
export import Inject = typemock_.Inject;
|
||||
export import Timers = typemock_.Timers;
|
||||
export import Timer = typemock_.Timer;
|
||||
export import Timeout = typemock_.Timeout;
|
||||
export import Interval = typemock_.Interval;
|
||||
export import Immediate = typemock_.Immediate;
|
||||
export import AnimationFrame = typemock_.AnimationFrame;
|
||||
export import spy = typemock_.spy;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
/// <reference path="./tsserverProjectSystem.ts" />
|
||||
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
|
||||
/// <reference path="../vfs.ts" />
|
||||
/// <reference path="../mocks.ts" />
|
||||
/// <reference path="../fakes.ts" />
|
||||
|
||||
namespace ts.projectSystem {
|
||||
const nullCancellationToken = server.nullCancellationToken;
|
||||
@ -62,7 +62,7 @@ namespace ts.projectSystem {
|
||||
|
||||
describe("for configured projects", () => {
|
||||
it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -109,7 +109,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should be up-to-date with the reference map changes", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -176,7 +176,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should be up-to-date with changes made in non-open files", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -211,7 +211,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should be up-to-date with deleted files", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -245,7 +245,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should be up-to-date with newly created files", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -279,7 +279,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should detect changes in non-root files", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; let y = Foo();`);
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ "compileOnSave": true, "files": ["${file1Consumer1.path}"] }`);
|
||||
@ -323,7 +323,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should return all files if a global file changed shape", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -352,7 +352,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should return empty array if CompileOnSave is not enabled", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{}`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -371,7 +371,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should save when compileOnSave is enabled in base tsconfig.json", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ "extends": "/a/tsconfig.json" }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -391,7 +391,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should always return the file itself if '--isolatedModules' is specified", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ "compileOnSave": true, "compilerOptions": { "isolatedModules": true } }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -416,7 +416,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should always return the file itself if '--out' or '--outFile' is specified", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ "compileOnSave": true, "compilerOptions": { "module": "system", "outFile": "/a/b/out.js" } }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -441,7 +441,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should return cascaded affected file list", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const file1Consumer1 = host.vfs.addFile("/a/b/file1Consumer1.ts", `import {Foo} from "./moduleFile1"; export var y = 10;`);
|
||||
@ -481,7 +481,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should work fine for files with circular references", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const file1 = host.vfs.addFile("/a/b/file1.ts", `/// <reference path="./file2.ts" />\nexport var t1 = 10;`);
|
||||
const file2 = host.vfs.addFile("/a/b/file2.ts", `/// <reference path="./file1.ts" />\nexport var t2 = 10;`);
|
||||
@ -496,7 +496,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should return results for all projects if not specifying projectFileName", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const file1 = host.vfs.addFile("/a/b/file1.ts", `export var t = 10;`);
|
||||
const file2 = host.vfs.addFile("/a/b/file2.ts", `import {t} from "./file1"; var t2 = 11;`);
|
||||
const file3 = host.vfs.addFile("/a/c/file2.ts", `import {t} from "../b/file1"; var t3 = 11;`);
|
||||
@ -516,7 +516,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should detect removed code file", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const moduleFile1 = host.vfs.addFile("/a/b/moduleFile1.ts", `export function Foo() { };`);
|
||||
const referenceFile1 = host.vfs.addFile("/a/b/referenceFile1.ts", `/// <reference path="./moduleFile1.ts" />\nexport var x = Foo();`);
|
||||
@ -537,7 +537,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should detect non-existing code file", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{ compileOnSave": true }`);
|
||||
const referenceFile1 = host.vfs.addFile("/a/b/referenceFile1.ts", `/// <reference path="./moduleFile2.ts" />\nexport var x = Foo();`);
|
||||
|
||||
@ -558,7 +558,7 @@ namespace ts.projectSystem {
|
||||
test("\r\n");
|
||||
|
||||
function test(newLine: "\r\n" | "\n") {
|
||||
const host = new mocks.MockServerHost({ newLine });
|
||||
const host = new fakes.FakeServerHost({ newLine });
|
||||
const f = host.vfs.addFile("/a/app.ts", `var x = 1;${newLine}var y = 2;`);
|
||||
|
||||
const session = createSession(host);
|
||||
@ -572,7 +572,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should emit specified file", () => {
|
||||
const host = new mocks.MockServerHost({ newLine: "\r\n" });
|
||||
const host = new fakes.FakeServerHost({ newLine: "\r\n" });
|
||||
const file1 = host.vfs.addFile("/a/b/f1.ts", `export function Foo() { return 10; }`);
|
||||
const file2 = host.vfs.addFile("/a/b/f2.ts", `import {Foo} from "./f1"; let y = Foo();`);
|
||||
const configFile = host.vfs.addFile("/a/b/tsconfig.json", `{}`);
|
||||
@ -593,7 +593,7 @@ namespace ts.projectSystem {
|
||||
it("shoud not emit js files in external projects", () => {
|
||||
const externalProjectName = "/a/b/externalproject";
|
||||
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const file1 = host.vfs.addFile("/a/b/file1.ts", `consonle.log('file1');`);
|
||||
// file2 has errors. The emit should not be blocked.
|
||||
const file2 = host.vfs.addFile("/a/b/file2.js", `console.log'file2');`);
|
||||
@ -626,7 +626,7 @@ namespace ts.projectSystem {
|
||||
it("should use project root as current directory so that compile on save results in correct file mapping", () => {
|
||||
const externalProjectName = "/root/TypeScriptProject3/TypeScriptProject3/TypeScriptProject3.csproj";
|
||||
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const file1 = host.vfs.addFile("/root/TypeScriptProject3/TypeScriptProject3/Foo.ts", `consonle.log('file1');`);
|
||||
host.vfs.addFile(libFile.path, libFile.content);
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="./tsserverProjectSystem.ts" />
|
||||
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
|
||||
/// <reference path="../mocks.ts" />
|
||||
/// <reference path="../fakes.ts" />
|
||||
|
||||
namespace ts.projectSystem {
|
||||
describe("Project errors", () => {
|
||||
@ -31,7 +31,7 @@ namespace ts.projectSystem {
|
||||
}
|
||||
|
||||
it("external project - diagnostics for missing files", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true, lib: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true, lib: true });
|
||||
host.vfs.addFile("/a/b/app.ts", ``);
|
||||
|
||||
const projectFileName = "/a/b/test.csproj";
|
||||
@ -66,7 +66,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("configured projects - diagnostics for missing files", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true, lib: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true, lib: true });
|
||||
host.vfs.addFile("/a/b/app.ts", ``);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "files": ["app.ts", "applib.ts"] }`);
|
||||
|
||||
@ -88,7 +88,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("configured projects - diagnostics for corrupted config 1", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
host.vfs.addFile("/a/b/app.ts", ``);
|
||||
host.vfs.addFile("/a/b/lib.ts", ``);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", ` "files": ["app.ts", "lib.ts"] }`);
|
||||
@ -122,7 +122,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("configured projects - diagnostics for corrupted config 2", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
host.vfs.addFile("/a/b/app.ts", ``);
|
||||
host.vfs.addFile("/a/b/lib.ts", ``);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "files": ["app.ts", "lib.ts"] }`);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\..\harness\harnessLanguageService.ts" />
|
||||
/// <reference path="../fakes.ts" />
|
||||
|
||||
namespace ts {
|
||||
|
||||
@ -915,7 +916,7 @@ namespace ts {
|
||||
}
|
||||
|
||||
function verifyProgram(vfs: vfs.VirtualFileSystem, rootFiles: string[], options: CompilerOptions, configFile: string) {
|
||||
const watchingSystemHost = createWatchingSystemHost(new mocks.MockServerHost({ vfs }));
|
||||
const watchingSystemHost = createWatchingSystemHost(new fakes.FakeServerHost({ vfs }));
|
||||
verifyProgramWithoutConfigFile(watchingSystemHost, rootFiles, options);
|
||||
verifyProgramWithConfigFile(watchingSystemHost, configFile);
|
||||
}
|
||||
@ -997,7 +998,7 @@ namespace ts {
|
||||
`export default classD;`);
|
||||
const configFile = fs.addFile("/src/tsconfig.json",
|
||||
JSON.stringify({ compilerOptions, include: ["packages/**/ *.ts"] }));
|
||||
const watchingSystemHost = createWatchingSystemHost(new mocks.MockServerHost({ vfs: fs }));
|
||||
const watchingSystemHost = createWatchingSystemHost(new fakes.FakeServerHost({ vfs: fs }));
|
||||
verifyProgramWithConfigFile(watchingSystemHost, configFile.path);
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\..\compiler\watch.ts" />
|
||||
/// <reference path="..\virtualFileSystemWithWatch.ts" />
|
||||
/// <reference path="../mocks.ts" />
|
||||
/// <reference path="../fakes.ts" />
|
||||
/// <reference path="../vpath.ts" />
|
||||
|
||||
namespace ts.tscWatch {
|
||||
import theory = utils.theory;
|
||||
import Spy = typemock.Spy;
|
||||
import spy = typemock.spy;
|
||||
import Arg = typemock.Arg;
|
||||
import Times = typemock.Times;
|
||||
|
||||
@ -63,7 +63,7 @@ namespace ts.tscWatch {
|
||||
return result;
|
||||
}
|
||||
|
||||
function checkOutputErrors(host: mocks.MockServerHost, errors: ReadonlyArray<Diagnostic>, isInitial?: true, skipWaiting?: true) {
|
||||
function checkOutputErrors(host: fakes.FakeServerHost, errors: ReadonlyArray<Diagnostic>, isInitial?: true, skipWaiting?: true) {
|
||||
const outputs = host.getOutput();
|
||||
const expectedOutputCount = (isInitial ? 0 : 1) + errors.length + (skipWaiting ? 0 : 1);
|
||||
assert.equal(outputs.length, expectedOutputCount, "Outputs = " + outputs.toString());
|
||||
@ -82,17 +82,17 @@ namespace ts.tscWatch {
|
||||
host.clearOutput();
|
||||
}
|
||||
|
||||
function assertDiagnosticAt(host: mocks.MockServerHost, outputAt: number, diagnostic: Diagnostic) {
|
||||
function assertDiagnosticAt(host: fakes.FakeServerHost, outputAt: number, diagnostic: Diagnostic) {
|
||||
const output = host.getOutput()[outputAt];
|
||||
assert.equal(output, formatDiagnostic(diagnostic, host), "outputs[" + outputAt + "] is " + output);
|
||||
}
|
||||
|
||||
function assertWatchDiagnosticAt(host: mocks.MockServerHost, outputAt: number, diagnosticMessage: DiagnosticMessage) {
|
||||
function assertWatchDiagnosticAt(host: fakes.FakeServerHost, outputAt: number, diagnosticMessage: DiagnosticMessage) {
|
||||
const output = host.getOutput()[outputAt];
|
||||
assert.isTrue(endsWith(output, getWatchDiagnosticWithoutDate(host, diagnosticMessage)), "outputs[" + outputAt + "] is " + output);
|
||||
}
|
||||
|
||||
function getWatchDiagnosticWithoutDate(host: mocks.MockServerHost, diagnosticMessage: DiagnosticMessage) {
|
||||
function getWatchDiagnosticWithoutDate(host: fakes.FakeServerHost, diagnosticMessage: DiagnosticMessage) {
|
||||
return ` - ${flattenDiagnosticMessageText(getLocaleSpecificMessage(diagnosticMessage), host.newLine)}${host.newLine + host.newLine + host.newLine}`;
|
||||
}
|
||||
|
||||
@ -159,12 +159,12 @@ namespace ts.tscWatch {
|
||||
|
||||
describe("program updates", () => {
|
||||
it("create watch without config file", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/c/app.ts", `import {f} from "./module"\nconsole.log(f)`);
|
||||
host.vfs.addFile("/a/b/c/module.d.ts", `export let x: number`);
|
||||
|
||||
const watch = createWatchModeWithoutConfigFile(["/a/b/c/app.ts"], host);
|
||||
checkProgramActualFiles(watch(), ["/a/b/c/app.ts", mocks.MockServerHost.libPath, "/a/b/c/module.d.ts"]);
|
||||
checkProgramActualFiles(watch(), ["/a/b/c/app.ts", fakes.FakeServerHost.libPath, "/a/b/c/module.d.ts"]);
|
||||
|
||||
// TODO: Should we watch creation of config files in the root file's file hierarchy?
|
||||
|
||||
@ -174,7 +174,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("can handle tsconfig file name with difference casing", () => {
|
||||
const host = new mocks.MockServerHost({ vfs: { useCaseSensitiveFileNames: false } });
|
||||
const host = new fakes.FakeServerHost({ vfs: { useCaseSensitiveFileNames: false } });
|
||||
host.vfs.addFile("/a/b/app.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "include": ["app.ts"] }`);
|
||||
|
||||
@ -183,7 +183,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("create configured project without file list", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {}, "exclude": ["e"] }`);
|
||||
host.vfs.addFile("/a/b/c/f1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/d/f2.ts", `let y = 1`);
|
||||
@ -195,9 +195,9 @@ namespace ts.tscWatch {
|
||||
|
||||
const watch = ts.createWatchModeWithConfigFile(configFileResult, {}, watchingSystemHost);
|
||||
|
||||
checkProgramActualFiles(watch(), ["/a/b/c/f1.ts", mocks.MockServerHost.libPath, "/a/b/d/f2.ts"]);
|
||||
checkProgramActualFiles(watch(), ["/a/b/c/f1.ts", fakes.FakeServerHost.libPath, "/a/b/d/f2.ts"]);
|
||||
checkProgramRootFiles(watch(), ["/a/b/c/f1.ts", "/a/b/d/f2.ts"]);
|
||||
checkWatchedFiles(host, ["/a/b/tsconfig.json", "/a/b/c/f1.ts", "/a/b/d/f2.ts", mocks.MockServerHost.libPath]);
|
||||
checkWatchedFiles(host, ["/a/b/tsconfig.json", "/a/b/c/f1.ts", "/a/b/d/f2.ts", fakes.FakeServerHost.libPath]);
|
||||
checkWatchedDirectories(host, ["/a/b", "/a/b/node_modules/@types"], /*recursive*/ true);
|
||||
});
|
||||
|
||||
@ -206,7 +206,7 @@ namespace ts.tscWatch {
|
||||
// });
|
||||
|
||||
it("add new files to a configured program without file list", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/commonFile1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{}`);
|
||||
|
||||
@ -222,7 +222,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should ignore non-existing files specified in the config file", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/commonFile1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/commonFile2.ts", `let y = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {}, "files": ["commonFile1.ts", "commonFile3.ts"] }`);
|
||||
@ -233,7 +233,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("handle recreated files correctly", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/commonFile1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/commonFile2.ts", `let y = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{}`);
|
||||
@ -255,13 +255,13 @@ namespace ts.tscWatch {
|
||||
it("handles the missing files - that were added to program because they were added with ///<ref", () => {
|
||||
const file1Content = `/// <reference path="commonFile2.ts"/>\nlet x = y`;
|
||||
|
||||
const host = new mocks.MockServerHost({ lib: true, vfs: { useCaseSensitiveFileNames: false } });
|
||||
const host = new fakes.FakeServerHost({ lib: true, vfs: { useCaseSensitiveFileNames: false } });
|
||||
host.vfs.addFile("/a/b/commonFile1.ts", file1Content);
|
||||
|
||||
const watch = createWatchModeWithoutConfigFile(["/a/b/commonFile1.ts"], host);
|
||||
|
||||
checkProgramRootFiles(watch(), ["/a/b/commonFile1.ts"]);
|
||||
checkProgramActualFiles(watch(), ["/a/b/commonFile1.ts", mocks.MockServerHost.libPath]);
|
||||
checkProgramActualFiles(watch(), ["/a/b/commonFile1.ts", fakes.FakeServerHost.libPath]);
|
||||
checkOutputErrors(host, [
|
||||
createFileNotFoundDiagnostic(watch(), "/a/b/commonFile1.ts", file1Content, "commonFile2.ts", "/a/b/commonFile2.ts"),
|
||||
createCannotFindNameDiagnostic(watch(), "/a/b/commonFile1.ts", file1Content, "y"),
|
||||
@ -270,12 +270,12 @@ namespace ts.tscWatch {
|
||||
host.vfs.addFile("/a/b/commonFile2.ts", `let y = 1`);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkProgramRootFiles(watch(), ["/a/b/commonFile1.ts"]);
|
||||
checkProgramActualFiles(watch(), ["/a/b/commonFile1.ts", mocks.MockServerHost.libPath, "/a/b/commonFile2.ts"]);
|
||||
checkProgramActualFiles(watch(), ["/a/b/commonFile1.ts", fakes.FakeServerHost.libPath, "/a/b/commonFile2.ts"]);
|
||||
checkOutputErrors(host, emptyArray);
|
||||
});
|
||||
|
||||
it("should reflect change in config file", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/commonFile1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/commonFile2.ts", `let y = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {}, "files": ["/a/b/commonFile1.ts", "/a/b/commonFile2.ts"] }`);
|
||||
@ -289,7 +289,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("files explicitly excluded in config file", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/commonFile1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/commonFile2.ts", `let y = 1`);
|
||||
host.vfs.addFile("/a/c/excludedFile1.ts", `let t = 1;`);
|
||||
@ -300,7 +300,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should properly handle module resolution changes in config file", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/file1.ts", `import { T } from "module1";`);
|
||||
host.vfs.addFile("/a/b/node_modules/module1.ts", `export interface T {}`);
|
||||
host.vfs.addFile("/a/module1.ts", `export interface T {}`);
|
||||
@ -317,7 +317,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should tolerate config file errors and still try to build a project", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/commonFile1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/commonFile2.ts", `let y = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json",
|
||||
@ -334,7 +334,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("changes in files are reflected in project structure", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/f1.ts", `export * from "./f2"`);
|
||||
host.vfs.addFile("/a/b/f2.ts", `export let x = 1`);
|
||||
host.vfs.addFile("/a/c/f3.ts", `export let y = 1;`);
|
||||
@ -350,7 +350,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("deleted files affect project structure", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/f1.ts", `export * from "./f2"`);
|
||||
host.vfs.addFile("/a/b/f2.ts", `export * from "../c/f3"`);
|
||||
host.vfs.addFile("/a/c/f3.ts", `export let y = 1;`);
|
||||
@ -364,7 +364,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("deleted files affect project structure - 2", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/f1.ts", `export * from "./f2"`);
|
||||
host.vfs.addFile("/a/b/f2.ts", `export * from "../c/f3"`);
|
||||
host.vfs.addFile("/a/c/f3.ts", `export let y = 1;`);
|
||||
@ -378,7 +378,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("config file includes the file", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/f1.ts", `export let x = 5`);
|
||||
host.vfs.addFile("/a/c/f2.ts", `import {x} from "../b/f1"`);
|
||||
host.vfs.addFile("/a/c/f3.ts", `export let y = 1`);
|
||||
@ -390,7 +390,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("correctly migrate files between projects", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/f1.ts",
|
||||
`export * from "../c/f2";\n` +
|
||||
`export * from "../d/f3";`);
|
||||
@ -409,7 +409,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("can correctly update configured project when set of root files has changed (new file on disk)", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/f1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {} }`);
|
||||
|
||||
@ -423,7 +423,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("can correctly update configured project when set of root files has changed (new file in list of files)", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/f1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/f2.ts", `let y = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {}, "files": ["f1.ts"] }`);
|
||||
@ -438,7 +438,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("can update configured project when set of root files was not changed", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/f1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/f2.ts", `let y = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {}, "files": ["f1.ts", "f2.ts"] }`);
|
||||
@ -453,14 +453,14 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("config file is deleted", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/f1.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/f2.ts", `let y = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {} }`);
|
||||
|
||||
const watch = createWatchModeWithConfigFile("/a/b/tsconfig.json", host);
|
||||
|
||||
checkProgramActualFiles(watch(), ["/a/b/f1.ts", "/a/b/f2.ts", mocks.MockServerHost.libPath]);
|
||||
checkProgramActualFiles(watch(), ["/a/b/f1.ts", "/a/b/f2.ts", fakes.FakeServerHost.libPath]);
|
||||
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
|
||||
|
||||
host.vfs.removeFile("/a/b/tsconfig.json");
|
||||
@ -473,7 +473,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("Proper errors: document is not contained in project", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/b/app.ts", ``);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{`);
|
||||
|
||||
@ -482,7 +482,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("correctly handles changes in lib section of config file", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/.ts/lib.es5.d.ts", `declare const eval: any`);
|
||||
host.vfs.addFile("/.ts/lib.es2015.promise.d.ts", `declare class Promise<T> {}`);
|
||||
host.vfs.addFile("/src/app.ts", `var x: Promise<string>;`);
|
||||
@ -497,7 +497,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should handle non-existing directories in config file", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
host.vfs.addFile("/a/src/app.ts", `let x = 1;`);
|
||||
host.vfs.addFile("/a/tsconfig.json", `{ "compilerOptions": {}, "include": ["src/**/*", "notexistingfolder/*"] }`);
|
||||
|
||||
@ -507,7 +507,7 @@ namespace ts.tscWatch {
|
||||
|
||||
it("rename a module file and rename back should restore the states for inferred projects", () => {
|
||||
const file1Content = `import * as T from "./moduleFile"; T.bar();`;
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/file1.ts", file1Content);
|
||||
host.vfs.addFile("/a/b/moduleFile.ts", `export function bar() { };`);
|
||||
|
||||
@ -531,7 +531,7 @@ namespace ts.tscWatch {
|
||||
|
||||
it("rename a module file and rename back should restore the states for configured projects", () => {
|
||||
const file1Content = `import * as T from "./moduleFile"; T.bar();`;
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/file1.ts", file1Content);
|
||||
host.vfs.addFile("/a/b/moduleFile.ts", `export function bar() { };`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{}`);
|
||||
@ -555,7 +555,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("types should load from config file path if config exists", () => {
|
||||
const host = new mocks.MockServerHost({ vfs: { currentDirectory: "/a/c" }});
|
||||
const host = new fakes.FakeServerHost({ vfs: { currentDirectory: "/a/c" } });
|
||||
host.vfs.addDirectory("/a/c");
|
||||
host.vfs.addFile("/a/b/app.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": { "types": ["node"], "typeRoots": [] } }`);
|
||||
@ -567,7 +567,7 @@ namespace ts.tscWatch {
|
||||
|
||||
it("add the missing module file for inferred project: should remove the `module not found` error", () => {
|
||||
const file1Content = `import * as T from "./moduleFile"; T.bar();`;
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/file1.ts", file1Content);
|
||||
|
||||
const watch = createWatchModeWithoutConfigFile(["/a/b/file1.ts"], host);
|
||||
@ -584,7 +584,7 @@ namespace ts.tscWatch {
|
||||
|
||||
it("Configure file diagnostics events are generated when the config file has errors", () => {
|
||||
const configFileContent = `{ "compilerOptions": { "foo": "bar", "allowJS": true } }`;
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/app.ts", `let x = 10`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", configFileContent);
|
||||
|
||||
@ -596,7 +596,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("If config file doesnt have errors, they are not reported", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/app.ts", `let x = 10`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {} }`);
|
||||
|
||||
@ -605,7 +605,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("Reports errors when the config file changes", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/app.ts", `let x = 10`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {} }`);
|
||||
|
||||
@ -627,16 +627,16 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("non-existing directories listed in config file input array should be tolerated without crashing the server", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/file1.ts", `let t = 10;`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": {}, "include": ["app/*", "test/**/*", "something"] }`);
|
||||
|
||||
const watch = createWatchModeWithConfigFile("/a/b/tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [mocks.MockServerHost.libPath]);
|
||||
checkProgramActualFiles(watch(), [fakes.FakeServerHost.libPath]);
|
||||
});
|
||||
|
||||
it("non-existing directories listed in config file input array should be able to handle @types if input file list is empty", () => {
|
||||
const host = new mocks.MockServerHost({ vfs: { currentDirectory: "/a/" } });
|
||||
const host = new fakes.FakeServerHost({ vfs: { currentDirectory: "/a/" } });
|
||||
host.vfs.addFile("/a/app.ts", `let x = 1`);
|
||||
host.vfs.addFile("/a/tsconfig.json", `{ "compilerOptions": {}, "files": [] }`);
|
||||
host.vfs.addFile("/a/node_modules/@types/typings/index.d.ts", `export * from "./lib"`);
|
||||
@ -648,11 +648,11 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should support files without extensions", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/compile", `let x = 1`);
|
||||
|
||||
const watch = createWatchModeWithoutConfigFile(["/a/compile"], host, { allowNonTsExtensions: true });
|
||||
checkProgramActualFiles(watch(), ["/a/compile", mocks.MockServerHost.libPath]);
|
||||
checkProgramActualFiles(watch(), ["/a/compile", fakes.FakeServerHost.libPath]);
|
||||
});
|
||||
|
||||
it("Options Diagnostic locations reported correctly with changes in configFile contents when options change", () => {
|
||||
@ -675,7 +675,7 @@ namespace ts.tscWatch {
|
||||
` }\n` +
|
||||
`}`;
|
||||
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/b/app.ts", `let x = 10`);
|
||||
host.vfs.addFile("/a/b/tsconfig.json", configFileContentWithComment);
|
||||
|
||||
@ -713,13 +713,7 @@ namespace ts.tscWatch {
|
||||
]
|
||||
});
|
||||
|
||||
const filesWritten = new Map<string, number>();
|
||||
const host = new class extends mocks.MockServerHost {
|
||||
writeFile(path: string, data: string) {
|
||||
filesWritten.set(path, (filesWritten.get(path) || 0) + 1);
|
||||
super.writeFile(path, data);
|
||||
}
|
||||
};
|
||||
const host = new fakes.FakeServerHost();
|
||||
|
||||
host.vfs.addFile("/a/b/output/AnotherDependency/file1.d.ts", `declare namespace Common.SomeComponent.DynamicMenu { enum Z { Full = 0, Min = 1, Average = 2, } }`);
|
||||
host.vfs.addFile("/a/b/dependencies/file2.d.ts", `declare namespace Dependencies.SomeComponent { export class SomeClass { version: string; } }`);
|
||||
@ -727,20 +721,23 @@ namespace ts.tscWatch {
|
||||
host.vfs.addFile("/a/b/project/src/main2.ts", `namespace main.file4 { import DynamicMenu = Common.SomeComponent.DynamicMenu; export function foo(a: DynamicMenu.z) { } }`);
|
||||
host.vfs.addFile("/a/b/project/tsconfig.json", configContent);
|
||||
|
||||
const writeFileSpy = spy(host, "writeFile");
|
||||
|
||||
createWatchModeWithConfigFile("/a/b/project/tsconfig.json", host);
|
||||
|
||||
if (useOutFile) {
|
||||
// Only out file
|
||||
assert.equal(filesWritten.size, 1);
|
||||
writeFileSpy
|
||||
.verify(_ => _("/a/b/output/common.js", Arg.string(), Arg.any()), Times.once())
|
||||
.verify(_ => _(Arg.string(), Arg.string(), Arg.any()), Times.once())
|
||||
.revoke();
|
||||
}
|
||||
else {
|
||||
// main.js and main2.js
|
||||
assert.equal(filesWritten.size, 2);
|
||||
writeFileSpy
|
||||
.verify(_ => _("/a/b/output/main.js", Arg.string(), Arg.any()), Times.once())
|
||||
.verify(_ => _("/a/b/output/main2.js", Arg.string(), Arg.any()), Times.once())
|
||||
.verify(_ => _(Arg.string(), Arg.string(), Arg.any()), Times.exactly(2))
|
||||
.revoke();
|
||||
}
|
||||
|
||||
filesWritten.forEach((value, key) => {
|
||||
assert.equal(value, 1, "Key: " + key);
|
||||
});
|
||||
}
|
||||
|
||||
it("with --outFile and multiple declaration files in the program", () => {
|
||||
@ -753,22 +750,22 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
describe("emit", () => {
|
||||
function writeFile(host: mocks.MockServerHost, path: string, content: string) {
|
||||
function writeFile(host: fakes.FakeServerHost, path: string, content: string) {
|
||||
host.vfs.writeFile(path, content);
|
||||
}
|
||||
|
||||
function writeConfigFile(host: mocks.MockServerHost, path: string, config: any = {}) {
|
||||
function writeConfigFile(host: fakes.FakeServerHost, path: string, config: any = {}) {
|
||||
const compilerOptions = (config.compilerOptions || (config.compilerOptions = {}));
|
||||
compilerOptions.listEmittedFiles = true;
|
||||
writeFile(host, path, JSON.stringify(config));
|
||||
}
|
||||
|
||||
function waitAndCheckAffectedFiles(host: mocks.MockServerHost, affectedFiles: ReadonlyArray<string>, unaffectedFiles?: ReadonlyArray<string>) {
|
||||
function waitAndCheckAffectedFiles(host: fakes.FakeServerHost, affectedFiles: ReadonlyArray<string>, unaffectedFiles?: ReadonlyArray<string>) {
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
checkAffectedFiles(host, affectedFiles, unaffectedFiles);
|
||||
}
|
||||
|
||||
function checkAffectedFiles(host: mocks.MockServerHost, affectedFiles: ReadonlyArray<string>, unaffectedFiles?: ReadonlyArray<string>) {
|
||||
function checkAffectedFiles(host: fakes.FakeServerHost, affectedFiles: ReadonlyArray<string>, unaffectedFiles?: ReadonlyArray<string>) {
|
||||
affectedFiles = getEmittedLines(affectedFiles, host, formatOutputFile);
|
||||
checkOutputContains(host, affectedFiles);
|
||||
if (unaffectedFiles) {
|
||||
@ -788,13 +785,13 @@ namespace ts.tscWatch {
|
||||
const file2OutputPath = "/a/b.js";
|
||||
const commonOutputPaths: ReadonlyArray<string> = [file1OutputPath, file2OutputPath];
|
||||
|
||||
function writeCommonFiles(host: mocks.MockServerHost) {
|
||||
function writeCommonFiles(host: fakes.FakeServerHost) {
|
||||
writeFile(host, file1Path, `let x = 1`);
|
||||
writeFile(host, file2Path, `let y = 1`);
|
||||
}
|
||||
|
||||
it("if neither is set", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath);
|
||||
|
||||
@ -806,7 +803,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("if --out is set", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath, { compilerOptions: { out: "/a/out.js" } });
|
||||
|
||||
@ -818,7 +815,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("if --outFile is set", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath, { compilerOptions: { outFile: "/a/out.js" } });
|
||||
|
||||
@ -851,7 +848,7 @@ namespace ts.tscWatch {
|
||||
globalFile3OutputPath
|
||||
];
|
||||
|
||||
function writeCommonFiles(host: mocks.MockServerHost, files?: string[]) {
|
||||
function writeCommonFiles(host: fakes.FakeServerHost, files?: string[]) {
|
||||
if (!files || ts.contains(files, moduleFile1Path)) {
|
||||
writeFile(host, moduleFile1Path, `export function Foo() { };`);
|
||||
}
|
||||
@ -870,7 +867,7 @@ namespace ts.tscWatch {
|
||||
}
|
||||
|
||||
it("should contains only itself if a module file's shape didn't change, and all files referencing it if its shape changed", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath);
|
||||
|
||||
@ -887,7 +884,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should be up-to-date with the reference map changes", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath);
|
||||
|
||||
@ -917,7 +914,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should be up-to-date with deleted files", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath);
|
||||
|
||||
@ -933,7 +930,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should be up-to-date with newly created files", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath);
|
||||
|
||||
@ -946,7 +943,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should detect changes in non-root files", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host, [file1Consumer1Path, moduleFile1Path]);
|
||||
writeConfigFile(host, configFilePath, { files: [file1Consumer1Path] });
|
||||
|
||||
@ -963,7 +960,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should return all files if a global file changed shape", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath);
|
||||
|
||||
@ -976,7 +973,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should always return the file itself if '--isolatedModules' is specified", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath, { compilerOptions: { isolatedModules: true } });
|
||||
|
||||
@ -989,7 +986,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should always return the file itself if '--out' or '--outFile' is specified", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath, { compilerOptions: { module: "system", outFile: "/a/b/out.js" } });
|
||||
|
||||
@ -1001,7 +998,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should return cascaded affected file list", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeCommonFiles(host);
|
||||
writeConfigFile(host, configFilePath);
|
||||
|
||||
@ -1009,7 +1006,7 @@ namespace ts.tscWatch {
|
||||
checkAffectedFiles(host, commonOutputPaths);
|
||||
|
||||
writeFile(host, "/a/b/file1Consumer1Consumer1.ts", `import {y} from "./file1Consumer1";`);
|
||||
writeFile(host, file1Consumer1Path, `import {Foo} from "./moduleFile1"; export var y = 10; export var T: number;`)
|
||||
writeFile(host, file1Consumer1Path, `import {Foo} from "./moduleFile1"; export var y = 10; export var T: number;`);
|
||||
waitAndCheckAffectedFiles(host, [file1Consumer1OutputPath, "/a/b/file1Consumer1Consumer1.js"], commonOutputPaths);
|
||||
|
||||
// Doesn't change the shape of file1Consumer1
|
||||
@ -1017,14 +1014,14 @@ namespace ts.tscWatch {
|
||||
waitAndCheckAffectedFiles(host, [moduleFile1OutputPath, file1Consumer1OutputPath, file1Consumer2OutputPath], commonOutputPaths);
|
||||
|
||||
// Change both files before the timeout
|
||||
writeFile(host, file1Consumer1Path, `import {Foo} from "./moduleFile1"; export var y = 10; export var T: number; export var T2: number;`)
|
||||
writeFile(host, file1Consumer1Path, `import {Foo} from "./moduleFile1"; export var y = 10; export var T: number; export var T2: number;`);
|
||||
writeFile(host, moduleFile1Path, `export var T2: number;export function Foo() { };`);
|
||||
waitAndCheckAffectedFiles(host, [moduleFile1OutputPath, file1Consumer1OutputPath, file1Consumer2OutputPath, "/a/b/file1Consumer1Consumer1.js"], commonOutputPaths);
|
||||
});
|
||||
|
||||
it("should work fine for files with circular references", () => {
|
||||
// TODO: do not exit on such errors? Just continue to watch the files for update in watch mode
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeFile(host, "/a/b/file1.ts", `/// <reference path="./file2.ts" />\nexport var t1 = 10;`);
|
||||
writeFile(host, "/a/b/file2.ts", `/// <reference path="./file1.ts" />\nexport var t2 = 10;`);
|
||||
writeConfigFile(host, configFilePath);
|
||||
@ -1037,7 +1034,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should detect removed code file", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeFile(host, "/a/b/referenceFile1.ts", `/// <reference path="./moduleFile1.ts" />\nexport var x = Foo();`);
|
||||
writeCommonFiles(host, [moduleFile1Path]);
|
||||
writeConfigFile(host, configFilePath);
|
||||
@ -1050,7 +1047,7 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should detect non-existing code file", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
writeFile(host, "/a/b/referenceFile1.ts", `/// <reference path="./moduleFile2.ts" />\nexport var x = Foo();`);
|
||||
writeConfigFile(host, configFilePath);
|
||||
|
||||
@ -1071,7 +1068,7 @@ namespace ts.tscWatch {
|
||||
{ title: "\\r\\n", args: ["\r\n"] },
|
||||
{ title: "\\n", args: ["\n"] }
|
||||
], (newLine: "\r\n" | "\n") => {
|
||||
const host = new mocks.MockServerHost({ newLine });
|
||||
const host = new fakes.FakeServerHost({ newLine });
|
||||
writeFile(host, "/a/app.ts", `var x = 1;${newLine}var y = 2;`);
|
||||
|
||||
createWatchModeWithoutConfigFile(["/a/app.ts"], host, { listEmittedFiles: true });
|
||||
@ -1088,80 +1085,64 @@ namespace ts.tscWatch {
|
||||
});
|
||||
|
||||
it("should emit specified file", () => {
|
||||
const filesWritten = new Set<string>();
|
||||
const host = new class extends mocks.MockServerHost {
|
||||
writeFile(path: string, content: string) {
|
||||
filesWritten.add(path);
|
||||
super.writeFile(path, content);
|
||||
}
|
||||
}({ newLine: "\r\n" });
|
||||
const host = new fakes.FakeServerHost({ newLine: "\r\n" });
|
||||
|
||||
writeFile(host, "/a/b/f1.ts", `export function Foo() { return 10; }`);
|
||||
writeFile(host, "/a/b/f2.ts", `import {Foo} from "./f1"; export let y = Foo();`);
|
||||
writeFile(host, "/a/b/f3.ts", `import {y} from "./f2"; let x = y;`);
|
||||
writeConfigFile(host, "/a/b/tsconfig.json");
|
||||
|
||||
const writeFileSpy1 = spy(host, "writeFile");
|
||||
|
||||
createWatchModeWithConfigFile("/a/b/tsconfig.json", host);
|
||||
checkAffectedFiles(host, ["/a/b/f1.js", "/a/b/f2.js", "/a/b/f3.js"]);
|
||||
|
||||
assert.isTrue(filesWritten.has("/a/b/f1.js"));
|
||||
assert.strictEqual(host.readFile("/a/b/f1.js"), `"use strict";\r\nexports.__esModule = true;\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\n`);
|
||||
writeFileSpy1
|
||||
.verify(_ => _("/a/b/f1.js", `"use strict";\r\nexports.__esModule = true;\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\n`, Arg.any()), Times.once())
|
||||
.verify(_ => _("/a/b/f2.js", `"use strict";\r\nexports.__esModule = true;\r\nvar f1_1 = require("./f1");\r\nexports.y = f1_1.Foo();\r\n`, Arg.any()), Times.once())
|
||||
.verify(_ => _("/a/b/f3.js", `"use strict";\r\nexports.__esModule = true;\r\nvar f2_1 = require("./f2");\r\nvar x = f2_1.y;\r\n`, Arg.any()), Times.once())
|
||||
.revoke();
|
||||
|
||||
assert.isTrue(filesWritten.has("/a/b/f2.js"));
|
||||
assert.strictEqual(host.readFile("/a/b/f2.js"), `"use strict";\r\nexports.__esModule = true;\r\nvar f1_1 = require("./f1");\r\nexports.y = f1_1.Foo();\r\n`);
|
||||
|
||||
assert.isTrue(filesWritten.has("/a/b/f3.js"));
|
||||
assert.strictEqual(host.readFile("/a/b/f3.js"), `"use strict";\r\nexports.__esModule = true;\r\nvar f2_1 = require("./f2");\r\nvar x = f2_1.y;\r\n`);
|
||||
|
||||
filesWritten.clear();
|
||||
const writeFileSpy2 = spy(host, "writeFile");
|
||||
|
||||
writeFile(host, "/a/b/f1.ts", `export function Foo() { return 10; }export function foo2() { return 2; }`);
|
||||
waitAndCheckAffectedFiles(host, ["/a/b/f1.js", "/a/b/f2.js"], ["/a/b/f3.js"]);
|
||||
|
||||
assert.isTrue(filesWritten.has("/a/b/f1.js"));
|
||||
assert.strictEqual(host.readFile("/a/b/f1.js"), `"use strict";\r\nexports.__esModule = true;\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\nfunction foo2() { return 2; }\r\nexports.foo2 = foo2;\r\n`);
|
||||
|
||||
assert.isTrue(filesWritten.has("/a/b/f2.js"));
|
||||
assert.strictEqual(host.readFile("/a/b/f2.js"), `"use strict";\r\nexports.__esModule = true;\r\nvar f1_1 = require("./f1");\r\nexports.y = f1_1.Foo();\r\n`);
|
||||
|
||||
assert.isFalse(filesWritten.has("/a/b/f3.js"));
|
||||
writeFileSpy2
|
||||
.verify(_ => _("/a/b/f1.js", `"use strict";\r\nexports.__esModule = true;\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\nfunction foo2() { return 2; }\r\nexports.foo2 = foo2;\r\n`, Arg.any()), Times.once())
|
||||
.verify(_ => _("/a/b/f2.js", `"use strict";\r\nexports.__esModule = true;\r\nvar f1_1 = require("./f1");\r\nexports.y = f1_1.Foo();\r\n`, Arg.any()), Times.once())
|
||||
.verify(_ => _("/a/b/f3.js", Arg.string(), Arg.any()), Times.none())
|
||||
.revoke();
|
||||
});
|
||||
|
||||
it("Elides const enums correctly in incremental compilation", () => {
|
||||
const filesWritten = new Set<string>();
|
||||
const host = new class extends mocks.MockServerHost {
|
||||
writeFile(path: string, content: string) {
|
||||
filesWritten.add(path);
|
||||
super.writeFile(path, content);
|
||||
}
|
||||
}({ lib: true, newLine: "\n" });
|
||||
const host = new fakes.FakeServerHost({ lib: true, newLine: "\n" });
|
||||
|
||||
writeFile(host, "/user/someone/projects/myproject/file1.ts", `export const enum E1 { V = 1 }`);
|
||||
writeFile(host, "/user/someone/projects/myproject/file2.ts", `import { E1 } from "./file1"; export const enum E2 { V = E1.V }`);
|
||||
writeFile(host, "/user/someone/projects/myproject/file3.ts", `import { E2 } from "./file2"; const v: E2 = E2.V;`);
|
||||
|
||||
const writeFileSpy1 = spy(host, "writeFile");
|
||||
|
||||
createWatchModeWithoutConfigFile(["/user/someone/projects/myproject/file1.ts", "/user/someone/projects/myproject/file2.ts", "/user/someone/projects/myproject/file3.ts"], host, { listEmittedFiles: true });
|
||||
checkAffectedFiles(host, ["/user/someone/projects/myproject/file1.js", "/user/someone/projects/myproject/file2.js", "/user/someone/projects/myproject/file3.js"]);
|
||||
|
||||
assert.isTrue(filesWritten.has("/user/someone/projects/myproject/file1.js"));
|
||||
assert.strictEqual(host.readFile("/user/someone/projects/myproject/file1.js"), `"use strict";\nexports.__esModule = true;\n`);
|
||||
writeFileSpy1
|
||||
.verify(_ => _("/user/someone/projects/myproject/file1.js", `"use strict";\nexports.__esModule = true;\n`, Arg.any()), Times.once())
|
||||
.verify(_ => _("/user/someone/projects/myproject/file2.js", `"use strict";\nexports.__esModule = true;\n`, Arg.any()), Times.once())
|
||||
.verify(_ => _("/user/someone/projects/myproject/file3.js", `"use strict";\nexports.__esModule = true;\nvar v = 1 /* V */;\n`, Arg.any()), Times.once())
|
||||
.revoke();
|
||||
|
||||
assert.isTrue(filesWritten.has("/user/someone/projects/myproject/file2.js"));
|
||||
assert.strictEqual(host.readFile("/user/someone/projects/myproject/file2.js"), `"use strict";\nexports.__esModule = true;\n`);
|
||||
|
||||
assert.isTrue(filesWritten.has("/user/someone/projects/myproject/file3.js"));
|
||||
assert.strictEqual(host.readFile("/user/someone/projects/myproject/file3.js"), `"use strict";\nexports.__esModule = true;\nvar v = 1 /* V */;\n`);
|
||||
|
||||
filesWritten.clear();
|
||||
const writeFileSpy2 = spy(host, "writeFile");
|
||||
|
||||
writeFile(host, "/user/someone/projects/myproject/file1.ts", `export const enum E1 { V = 1 }function foo2() { return 2; }`);
|
||||
waitAndCheckAffectedFiles(host, ["/user/someone/projects/myproject/file1.js"], ["/user/someone/projects/myproject/file2.js", "/user/someone/projects/myproject/file3.js"]);
|
||||
|
||||
assert.isTrue(filesWritten.has("/user/someone/projects/myproject/file1.js"));
|
||||
assert.strictEqual(host.readFile("/user/someone/projects/myproject/file1.js"), `"use strict";\nexports.__esModule = true;\nfunction foo2() { return 2; }\n`);
|
||||
|
||||
assert.isFalse(filesWritten.has("/user/someone/projects/myproject/file2.js"));
|
||||
assert.isFalse(filesWritten.has("/user/someone/projects/myproject/file3.js"));
|
||||
writeFileSpy2
|
||||
.verify(_ => _("/user/someone/projects/myproject/file1.js", `"use strict";\nexports.__esModule = true;\nfunction foo2() { return 2; }\n`, Arg.any()), Times.once())
|
||||
.verify(_ => _("/user/someone/projects/myproject/file2.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/user/someone/projects/myproject/file3.js", Arg.string(), Arg.any()), Times.none())
|
||||
.revoke();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1171,11 +1152,10 @@ namespace ts.tscWatch {
|
||||
const rootContent1 = `import {x} from "f1"`;
|
||||
const importedContent = `foo()`;
|
||||
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/d/f0.ts", rootContent1);
|
||||
host.vfs.addFile("/a/f1.ts", importedContent);
|
||||
|
||||
const fileExists = host.fileExists;
|
||||
const watch = createWatchModeWithoutConfigFile(["/a/d/f0.ts"], host, { module: ModuleKind.AMD });
|
||||
|
||||
// ensure that imported file was found
|
||||
@ -1185,8 +1165,7 @@ namespace ts.tscWatch {
|
||||
], /*isInitial*/ true);
|
||||
|
||||
// spy on calls to fileExists to make sure that disk is not touched
|
||||
const fileExistsSpy1 = new Spy(fileExists);
|
||||
host.fileExists = fileExistsSpy1.value;
|
||||
const fileExistsSpy1 = spy(host, "fileExists");
|
||||
|
||||
// write file and trigger synchronization
|
||||
const rootContent2 = `import {x} from "f1"\nvar x: string = 1;`;
|
||||
@ -1194,7 +1173,9 @@ namespace ts.tscWatch {
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
// verify fileExists was not called.
|
||||
fileExistsSpy1.verify(_ => _(Arg.any()), Times.none());
|
||||
fileExistsSpy1
|
||||
.verify(_ => _(Arg.any()), Times.none())
|
||||
.revoke();
|
||||
|
||||
// ensure file has correct number of errors after edit
|
||||
checkOutputErrors(host, [
|
||||
@ -1204,8 +1185,7 @@ namespace ts.tscWatch {
|
||||
]);
|
||||
|
||||
// spy on calls to fileExists to make sure LSHost only searches for 'f2'
|
||||
const fileExistsSpy2 = new Spy(fileExists);
|
||||
host.fileExists = fileExistsSpy2.value;
|
||||
const fileExistsSpy2 = spy(host, "fileExists");
|
||||
|
||||
// write file and trigger synchronization
|
||||
const rootContent3 = `import {x} from "f2"`;
|
||||
@ -1215,7 +1195,8 @@ namespace ts.tscWatch {
|
||||
// verify fileExists was called correctly
|
||||
fileExistsSpy2
|
||||
.verify(_ => _(Arg.includes("/f2.")), Times.atLeastOnce())
|
||||
.verify(_ => _(Arg.not(Arg.includes("/f2."))), Times.none());
|
||||
.verify(_ => _(Arg.not(Arg.includes("/f2."))), Times.none())
|
||||
.revoke();
|
||||
|
||||
// ensure file has correct number of errors after edit
|
||||
checkOutputErrors(host, [
|
||||
@ -1223,8 +1204,7 @@ namespace ts.tscWatch {
|
||||
]);
|
||||
|
||||
// spy on calls to fileExists to make sure LSHost only searches for 'f1'
|
||||
const fileExistsSpy3 = new Spy(fileExists);
|
||||
host.fileExists = fileExistsSpy3.value;
|
||||
const fileExistsSpy3 = spy(host, "fileExists");
|
||||
|
||||
// write file and trigger synchronization
|
||||
const rootContent4 = `import {x} from "f1"`;
|
||||
@ -1234,7 +1214,8 @@ namespace ts.tscWatch {
|
||||
// verify fileExists was called correctly
|
||||
fileExistsSpy3
|
||||
.verify(_ => _(Arg.includes("/f1.")), Times.atLeastOnce())
|
||||
.verify(_ => _(Arg.not(Arg.includes("/f1."))), Times.none());
|
||||
.verify(_ => _(Arg.not(Arg.includes("/f1."))), Times.none())
|
||||
.revoke();
|
||||
|
||||
checkOutputErrors(host, [
|
||||
createFileIsNotAModuleDiagnostic(watch(), "/a/d/f0.ts", rootContent1, "f1", "/a/f1.ts"),
|
||||
@ -1245,34 +1226,34 @@ namespace ts.tscWatch {
|
||||
it("loads missing files from disk", () => {
|
||||
const rootContent1 = `import {x} from "bar"`;
|
||||
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/foo.ts", rootContent1);
|
||||
|
||||
const fileExists = host.fileExists;
|
||||
|
||||
// spy on calls to fileExists when starting watch mode
|
||||
const fileExistsSpy1 = new Spy(fileExists);
|
||||
host.fileExists = fileExistsSpy1.value;
|
||||
const fileExistsSpy1 = spy(host, "fileExists");
|
||||
|
||||
const watch = createWatchModeWithoutConfigFile(["/a/foo.ts"], host, { module: ModuleKind.AMD });
|
||||
|
||||
// verify fileExists was called correctly
|
||||
fileExistsSpy1.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce());
|
||||
fileExistsSpy1
|
||||
.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce())
|
||||
.revoke();
|
||||
|
||||
checkOutputErrors(host, [
|
||||
createCannotFindModuleDiagnostic(watch(), "/a/foo.ts", rootContent1, "bar")
|
||||
], /*isInitial*/ true);
|
||||
|
||||
// spy on calls to fileExists after synchronization is triggered
|
||||
const fileExistsSpy2 = new Spy(fileExists);
|
||||
host.fileExists = fileExistsSpy2.value;
|
||||
const fileExistsSpy2 = spy(host, "fileExists");
|
||||
|
||||
host.vfs.writeFile("/a/foo.ts", `import {y} from "bar"`);
|
||||
host.vfs.writeFile("/a/bar.d.ts", `export const y = 1;`);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
// verify fileExists was called correctly
|
||||
fileExistsSpy2.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce());
|
||||
fileExistsSpy2
|
||||
.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce())
|
||||
.revoke();
|
||||
|
||||
checkOutputErrors(host, emptyArray);
|
||||
});
|
||||
@ -1281,53 +1262,54 @@ namespace ts.tscWatch {
|
||||
const rootContent = `import {x} from "bar"`;
|
||||
const importedContent = `export const y = 1;export const x = 10;`;
|
||||
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/foo.ts", rootContent);
|
||||
host.vfs.addFile("/a/bar.d.ts", importedContent);
|
||||
|
||||
const fileExists = host.fileExists;
|
||||
|
||||
// spy on fileExists when starting watch mode
|
||||
const fileExistsSpy1 = new Spy(fileExists);
|
||||
host.fileExists = fileExistsSpy1.value;
|
||||
const fileExistsSpy1 = spy(host, "fileExists");
|
||||
|
||||
const watch = createWatchModeWithoutConfigFile(["/a/foo.ts"], host, { module: ModuleKind.AMD });
|
||||
|
||||
// verify fileExists was called correctly
|
||||
fileExistsSpy1.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce());
|
||||
fileExistsSpy1
|
||||
.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce())
|
||||
.revoke();
|
||||
|
||||
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
|
||||
|
||||
// spy on fileExists when triggering synchronization
|
||||
const fileExistsSpy2 = new Spy(fileExists);
|
||||
host.fileExists = fileExistsSpy2.value;
|
||||
const fileExistsSpy2 = spy(host, "fileExists");
|
||||
|
||||
host.vfs.removeFile("/a/bar.d.ts");
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
// verify fileExists was called correctly
|
||||
fileExistsSpy2.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce());
|
||||
fileExistsSpy2
|
||||
.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce())
|
||||
.revoke();
|
||||
|
||||
checkOutputErrors(host, [
|
||||
createCannotFindModuleDiagnostic(watch(), "/a/foo.ts", rootContent, "bar")
|
||||
]);
|
||||
|
||||
// spy on fileExists when triggering synchronization
|
||||
const fileExistsSpy3 = new Spy(fileExists);
|
||||
host.fileExists = fileExistsSpy3.value;
|
||||
const fileExistsSpy3 = spy(host, "fileExists");
|
||||
|
||||
host.vfs.writeFile("/a/bar.d.ts", importedContent);;
|
||||
host.vfs.writeFile("/a/bar.d.ts", importedContent);
|
||||
host.checkTimeoutQueueLengthAndRun(1);
|
||||
|
||||
// verify fileExists was called correctly.
|
||||
fileExistsSpy3.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce());
|
||||
fileExistsSpy3
|
||||
.verify(_ => _(Arg.includes("/bar.")), Times.atLeastOnce())
|
||||
.revoke();
|
||||
|
||||
checkOutputErrors(host, emptyArray);
|
||||
});
|
||||
|
||||
it("works when module resolution changes to ambient module", () => {
|
||||
const rootContent = `import * as fs from "fs";`;
|
||||
const host = new mocks.MockServerHost({ vfs: { currentDirectory: "/a/b" }, lib: true });
|
||||
const host = new fakes.FakeServerHost({ vfs: { currentDirectory: "/a/b" }, lib: true });
|
||||
host.vfs.addFile("/a/b/foo.ts", rootContent);
|
||||
|
||||
const watch = createWatchModeWithoutConfigFile(["/a/b/foo.ts"], host, { });
|
||||
@ -1346,7 +1328,7 @@ namespace ts.tscWatch {
|
||||
const rootContent = `import * as fs from "fs";\nimport * as u from "url";`;
|
||||
const fileContent1 = `declare module "url" {\n export interface Url {\n href?: string;\n }\n}`;
|
||||
|
||||
const host = new mocks.MockServerHost({ vfs: { currentDirectory: "/a/b" }, lib: true });
|
||||
const host = new fakes.FakeServerHost({ vfs: { currentDirectory: "/a/b" }, lib: true });
|
||||
host.vfs.addFile("/a/b/foo.ts", rootContent);
|
||||
host.vfs.addFile("/a/b/bar.d.ts", fileContent1);
|
||||
|
||||
@ -1368,7 +1350,7 @@ namespace ts.tscWatch {
|
||||
const file2Content = `import module11 = require("module1");\nmodule11("hello");`;
|
||||
const file2Output = `"use strict";\nexports.__esModule = true;\nvar module11 = require("module1");\nmodule11("hello");\n`;
|
||||
|
||||
const host = new mocks.MockServerHost({ vfs: { currentDirectory: "/a/b/projects/myProject/" }, lib: true });
|
||||
const host = new fakes.FakeServerHost({ vfs: { currentDirectory: "/a/b/projects/myProject/" }, lib: true });
|
||||
host.vfs.addFile("/a/b/projects/myProject/src/file1.ts", file1Content);
|
||||
host.vfs.addFile("/a/b/projects/myProject/src/file2.ts", file2Content);
|
||||
host.vfs.addFile("/a/b/projects/myProject/node_modules/module1/index.js", `module.exports = options => { return options.toString(); }`);
|
||||
@ -1382,18 +1364,15 @@ namespace ts.tscWatch {
|
||||
}
|
||||
}));
|
||||
|
||||
const writeFile = host.writeFile;
|
||||
|
||||
// spy on calls to writeFile when starting watch mode
|
||||
const writeFileSpy1 = new Spy(writeFile);
|
||||
host.writeFile = writeFileSpy1.value;
|
||||
const writeFileSpy1 = spy(host, "writeFile");
|
||||
|
||||
const watch = createWatchModeWithConfigFile("/a/b/projects/myProject/src/tsconfig.json", host);
|
||||
checkProgramActualFiles(watch(), [
|
||||
"/a/b/projects/myProject/src/file1.ts",
|
||||
"/a/b/projects/myProject/src/file2.ts",
|
||||
"/a/b/projects/myProject/node_modules/module1/index.js",
|
||||
mocks.MockServerHost.libPath
|
||||
fakes.FakeServerHost.libPath
|
||||
]);
|
||||
checkOutputErrors(host, emptyArray, /*isInitial*/ true);
|
||||
|
||||
@ -1401,16 +1380,11 @@ namespace ts.tscWatch {
|
||||
writeFileSpy1
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/file1.js", file1Output, Arg.any()), Times.once())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/file2.js", file2Output, Arg.any()), Times.once())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/index.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/src/index.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/src/file1.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/src/file2.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/lib.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/lib.d.ts", Arg.string(), Arg.any()), Times.none());
|
||||
.verify(_ => _(Arg.nor("/a/b/projects/myProject/dist/file1.js", "/a/b/projects/myProject/dist/file2.js"), Arg.string(), Arg.any()), Times.none())
|
||||
.revoke();
|
||||
|
||||
// spy on calls to writeFile when triggering synchronization
|
||||
const writeFileSpy2 = new Spy(writeFile);
|
||||
host.writeFile = writeFileSpy2.value;
|
||||
const writeFileSpy2 = spy(host, "writeFile");
|
||||
|
||||
host.vfs.writeFile("/a/b/projects/myProject/src/file1.ts", file1Content + "\n;");
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
@ -1418,26 +1392,21 @@ namespace ts.tscWatch {
|
||||
"/a/b/projects/myProject/src/file1.ts",
|
||||
"/a/b/projects/myProject/src/file2.ts",
|
||||
"/a/b/projects/myProject/node_modules/module1/index.js",
|
||||
mocks.MockServerHost.libPath
|
||||
fakes.FakeServerHost.libPath
|
||||
]);
|
||||
checkOutputErrors(host, emptyArray);
|
||||
|
||||
// verify writeFile was called correctly
|
||||
writeFileSpy2
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/file1.js", file1Output + ";\n", Arg.any()), Times.once())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/file2.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/index.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/src/index.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/src/file1.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/src/file2.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/lib.js", Arg.string(), Arg.any()), Times.none())
|
||||
.verify(_ => _("/a/b/projects/myProject/dist/lib.d.ts", Arg.string(), Arg.any()), Times.none());
|
||||
.verify(_ => _(Arg.not("/a/b/projects/myProject/dist/file1.js"), Arg.string(), Arg.any()), Times.none())
|
||||
.revoke();
|
||||
});
|
||||
});
|
||||
|
||||
describe("with when module emit is specified as node", () => {
|
||||
it("when instead of filechanged recursive directory watcher is invoked", () => {
|
||||
const host = new mocks.MockServerHost({ lib: true });
|
||||
const host = new fakes.FakeServerHost({ lib: true });
|
||||
host.vfs.addFile("/a/rootFolder/project/Scripts/TypeScript.ts", `var z = 10;`);
|
||||
host.vfs.addFile("/a/rootFolder/project/Scripts/Javascript.js", `var zz = 10;`);
|
||||
host.vfs.addFile("/a/rootFolder/project/tsconfig.json", JSON.stringify({
|
||||
@ -1451,13 +1420,12 @@ namespace ts.tscWatch {
|
||||
],
|
||||
}));
|
||||
|
||||
const writeFile = host.writeFile;
|
||||
const watch = createWatchModeWithConfigFile("/a/rootFolder/project/tsconfig.json", host);
|
||||
|
||||
checkProgramActualFiles(watch(), [
|
||||
"/a/rootFolder/project/Scripts/TypeScript.ts",
|
||||
"/a/rootFolder/project/Scripts/Javascript.js",
|
||||
mocks.MockServerHost.libPath
|
||||
fakes.FakeServerHost.libPath
|
||||
]);
|
||||
|
||||
|
||||
@ -1465,8 +1433,7 @@ namespace ts.tscWatch {
|
||||
host.vfs.removeFile("/a/rootFolder/project/Scripts/TypeScript.ts");
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
|
||||
const writeFileSpy1 = new Spy(writeFile);
|
||||
host.writeFile = writeFileSpy1.value;
|
||||
const writeFileSpy1 = spy(host, "writeFile");
|
||||
|
||||
host.vfs.writeFile("/a/rootFolder/project/Scripts/TypeScript.ts", `var zz30 = 100;`);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
@ -1474,10 +1441,12 @@ namespace ts.tscWatch {
|
||||
checkProgramActualFiles(watch(), [
|
||||
"/a/rootFolder/project/Scripts/TypeScript.ts",
|
||||
"/a/rootFolder/project/Scripts/Javascript.js",
|
||||
mocks.MockServerHost.libPath
|
||||
fakes.FakeServerHost.libPath
|
||||
]);
|
||||
|
||||
writeFileSpy1.verify(_ => _("/a/rootFolder/project/Static/scripts/TypeScript.js", `var zz30 = 100;\n`, Arg.any()), Times.once());
|
||||
writeFileSpy1
|
||||
.verify(_ => _("/a/rootFolder/project/Static/scripts/TypeScript.js", `var zz30 = 100;\n`, Arg.any()), Times.once())
|
||||
.revoke();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -99,7 +99,7 @@ namespace ts.projectSystem {
|
||||
this.addPostExecAction("success", cb);
|
||||
}
|
||||
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings) {
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
this.projectService.updateTypingsForProject(response);
|
||||
}
|
||||
|
||||
@ -5583,7 +5583,7 @@ namespace ts.projectSystem {
|
||||
content: "export class Cookie { }"
|
||||
};
|
||||
const es2016LibFile: FileOrFolder = {
|
||||
path: "/a/lib/lib.es2016.full.d.ts",
|
||||
path: "/.ts/lib.es2016.full.d.ts",
|
||||
content: libFile.content
|
||||
};
|
||||
const typeRoots = ["types", "node_modules/@types"];
|
||||
|
||||
@ -3,9 +3,13 @@
|
||||
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
|
||||
/// <reference path="../vfs.ts" />
|
||||
/// <reference path="../typemock.ts" />
|
||||
/// <reference path="../mocks.ts" />
|
||||
/// <reference path="../fakes.ts" />
|
||||
|
||||
namespace ts.projectSystem {
|
||||
import spy = typemock.spy;
|
||||
import Arg = typemock.Arg;
|
||||
import Times = typemock.Times;
|
||||
|
||||
import TI = server.typingsInstaller;
|
||||
import validatePackageName = JsTyping.validatePackageName;
|
||||
import PackageNameValidationResult = JsTyping.PackageNameValidationResult;
|
||||
@ -40,19 +44,14 @@ namespace ts.projectSystem {
|
||||
}
|
||||
}
|
||||
|
||||
function executeCommand(self: Installer, host: TestServerHost | mocks.MockServerHost, installedTypings: string[] | string, typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void {
|
||||
function executeCommand(self: Installer, host: fakes.FakeServerHost, installedTypings: string[] | string, typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void {
|
||||
self.addPostExecAction(installedTypings, success => {
|
||||
for (const file of typingFiles) {
|
||||
if (host instanceof mocks.MockServerHost) {
|
||||
if (typeof file.content === "string") {
|
||||
host.vfs.addFile(file.path, file.content, { overwrite: true });
|
||||
}
|
||||
else {
|
||||
host.vfs.addDirectory(file.path);
|
||||
}
|
||||
if (typeof file.content === "string") {
|
||||
host.vfs.addFile(file.path, file.content, { overwrite: true });
|
||||
}
|
||||
else {
|
||||
host.ensureFileOrFolder(file);
|
||||
host.vfs.addDirectory(file.path);
|
||||
}
|
||||
}
|
||||
cb(success);
|
||||
@ -75,27 +74,24 @@ namespace ts.projectSystem {
|
||||
|
||||
describe("local module", () => {
|
||||
it("should not be picked up", () => {
|
||||
const host = new mocks.MockServerHost();
|
||||
const host = new fakes.FakeServerHost();
|
||||
const f1 = host.vfs.addFile("/a/app.js", `const c = require('./config');`);
|
||||
const f2 = host.vfs.addFile("/a/config.js", `export let x = 1`);
|
||||
const config = host.vfs.addFile("/a/jsconfig.json", `{ "typeAcquisition": { "enable": true }, "compilerOptions": { "moduleResolution": "commonjs } }`);
|
||||
host.vfs.addFile("/cache/node_modules/@types/config/index.d.ts", `export let y: number;`);
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("config"), globalTypingsCacheLocation: "/cache" });
|
||||
}
|
||||
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, _cb: TI.RequestCompletedAction) {
|
||||
assert(false, "should not be called");
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { typesRegistry: createTypesRegistry("config"), globalTypingsCacheLocation: "/cache" });
|
||||
const installWorkerSpy = spy(installer, "installWorker");
|
||||
|
||||
const service = createProjectService(host, { typingsInstaller: installer });
|
||||
service.openClientFile(f1.path);
|
||||
service.checkNumberOfProjects({ configuredProjects: 1 });
|
||||
checkProjectActualFiles(configuredProjectAt(service, 0), [f1.path, f2.path, config.path]);
|
||||
installer.installAll(0);
|
||||
|
||||
installWorkerSpy
|
||||
.verify(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), Times.none())
|
||||
.revoke();
|
||||
});
|
||||
});
|
||||
|
||||
@ -106,22 +102,20 @@ namespace ts.projectSystem {
|
||||
content: "declare const $: { x: number }"
|
||||
};
|
||||
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const file1 = host.vfs.addFile("/a/b/app.js", ``);
|
||||
const tsconfig = host.vfs.addFile("/a/b/tsconfig.json", `{ "compilerOptions": { "allowJs": true }, "typeAcquisition": { "enable": true } }`);
|
||||
host.vfs.addFile("/a/b/package.json", `{ "name": "test", "dependencies": { "jquery": "^3.1.0" } }`);
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jquery];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jquery];
|
||||
executeCommand(installer, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
|
||||
projectService.openClientFile(file1.path);
|
||||
@ -135,6 +129,8 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path, tsconfig.path]);
|
||||
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("inferred project (typings installed)", () => {
|
||||
@ -143,21 +139,19 @@ namespace ts.projectSystem {
|
||||
content: "declare const $: { x: number }"
|
||||
};
|
||||
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const file1 = host.vfs.addFile("/a/b/app.js", ``);
|
||||
host.vfs.addFile("/a/b/package.json", `{ "name": "test", "dependencies": { "jquery": "^3.1.0" } }`);
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jquery];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jquery];
|
||||
executeCommand(installer, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
|
||||
projectService.openClientFile(file1.path);
|
||||
@ -170,21 +164,16 @@ namespace ts.projectSystem {
|
||||
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path]);
|
||||
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("external project - no type acquisition, no .d.ts/js files", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const file1 = host.vfs.addFile("/a/b/app.ts", ``);
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host);
|
||||
}
|
||||
|
||||
enqueueInstallTypingsRequest() {
|
||||
assert(false, "auto discovery should not be enabled");
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host);
|
||||
const enqueueInstallTypingsRequestSpy = spy(installer, "enqueueInstallTypingsRequest");
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
@ -198,19 +187,18 @@ namespace ts.projectSystem {
|
||||
// by default auto discovery will kick in if project contain only .js/.d.ts files
|
||||
// in this case project contain only ts files - no auto discovery
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
|
||||
enqueueInstallTypingsRequestSpy
|
||||
.verify(_ => _(Arg.any(), Arg.any(), Arg.any()), Times.none(), "auto discovery should not be enabled")
|
||||
.revoke();
|
||||
});
|
||||
|
||||
it("external project - no auto in typing acquisition, no .d.ts/js files", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const file1 = host.vfs.addFile("/a/b/app.ts", ``);
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest() {
|
||||
assert(false, "auto discovery should not be enabled");
|
||||
}
|
||||
};
|
||||
|
||||
const installer = new Installer(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
const enqueueInstallTypingsRequestSpy = spy(installer, "enqueueInstallTypingsRequest");
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
@ -224,10 +212,14 @@ namespace ts.projectSystem {
|
||||
// by default auto discovery will kick in if project contain only .js/.d.ts files
|
||||
// in this case project contain only ts files - no auto discovery even if type acquisition is set
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
|
||||
enqueueInstallTypingsRequestSpy
|
||||
.verify(_ => _(Arg.any(), Arg.any(), Arg.any()), Times.none(), "auto discovery should not be enabled")
|
||||
.revoke();
|
||||
});
|
||||
|
||||
it("external project - autoDiscovery = true, no .d.ts/js files", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const file1 = host.vfs.addFile("/a/b/app.ts", ``);
|
||||
|
||||
const jquery = {
|
||||
@ -235,21 +227,16 @@ namespace ts.projectSystem {
|
||||
content: "declare const $: { x: number }"
|
||||
};
|
||||
|
||||
let enqueueIsCalled = false;
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
enqueueIsCalled = true;
|
||||
super.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/node"];
|
||||
const typingFiles = [jquery];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
const enqueueInstallTypingsRequestSpy = spy(installer, "enqueueInstallTypingsRequest");
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jquery];
|
||||
executeCommand(installer, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
@ -260,11 +247,16 @@ namespace ts.projectSystem {
|
||||
typeAcquisition: { enable: true, include: ["jquery"] }
|
||||
});
|
||||
|
||||
assert.isTrue(enqueueIsCalled, "expected enqueueIsCalled to be true");
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
// auto is set in type acquisition - use it even if project contains only .ts files
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
|
||||
enqueueInstallTypingsRequestSpy
|
||||
.verify(_ => _(Arg.any(), Arg.any(), Arg.any()), Times.atLeastOnce())
|
||||
.revoke();
|
||||
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("external project - no type acquisition, with only js, jsx, d.ts files", () => {
|
||||
@ -272,7 +264,7 @@ namespace ts.projectSystem {
|
||||
// 1. react typings are installed for .jsx
|
||||
// 2. loose files names are matched against safe list for typings if
|
||||
// this is a JS project (only js, jsx, d.ts files are present)
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const file1 = host.vfs.addFile("/a/b/lodash.js", ``);
|
||||
const file2 = host.vfs.addFile("/a/b/file2.jsx", ``);
|
||||
const file3 = host.vfs.addFile("/a/b/file3.d.ts", ``);
|
||||
@ -287,16 +279,15 @@ namespace ts.projectSystem {
|
||||
content: "declare const lodash: { x: number }"
|
||||
};
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("lodash", "react") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/lodash", "@types/react"];
|
||||
const typingFiles = [lodash, react];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { typesRegistry: createTypesRegistry("lodash", "react") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
const installedTypings = ["@types/lodash", "@types/react"];
|
||||
const typingFiles = [lodash, react];
|
||||
executeCommand(installer, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
@ -317,28 +308,24 @@ namespace ts.projectSystem {
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, lodash.path, react.path]);
|
||||
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("external project - no type acquisition, with js & ts files", () => {
|
||||
// Tests:
|
||||
// 1. No typings are included for JS projects when the project contains ts files
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const file1 = host.vfs.addFile("/a/b/jquery.js", ``);
|
||||
const file2 = host.vfs.addFile("/a/b/file2.ts", ``);
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
super.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings: string[] = [];
|
||||
const typingFiles: FileOrFolder[] = [];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, [], [], cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
@ -357,6 +344,7 @@ namespace ts.projectSystem {
|
||||
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [file2.path]);
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("external project - with type acquisition, with only js, d.ts files", () => {
|
||||
@ -364,7 +352,7 @@ namespace ts.projectSystem {
|
||||
// 1. Safelist matching, type acquisition includes/excludes and package.json typings are all acquired
|
||||
// 2. Types for safelist matches are not included when they also appear in the type acquisition exclude list
|
||||
// 3. Multiple includes and excludes are respected in type acquisition
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const file1 = host.vfs.addFile("/a/b/lodash.js", ``);
|
||||
const file2 = host.vfs.addFile("/a/b/commander.js", ``);
|
||||
const file3 = host.vfs.addFile("/a/b/file3.d.ts", ``);
|
||||
@ -388,16 +376,15 @@ namespace ts.projectSystem {
|
||||
content: "declare const moment: { x: number }"
|
||||
};
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery", "commander", "moment", "express") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment"];
|
||||
const typingFiles = [commander, express, jquery, moment];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { typesRegistry: createTypesRegistry("jquery", "commander", "moment", "express") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment"];
|
||||
const typingFiles = [commander, express, jquery, moment];
|
||||
executeCommand(installer, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
@ -418,10 +405,11 @@ namespace ts.projectSystem {
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, commander.path, express.path, jquery.path, moment.path]);
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("Throttle - delayed typings to install", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const lodashJs = host.vfs.addFile("/a/b/lodash.js", ``);
|
||||
const commanderJs = host.vfs.addFile("/a/b/commander.js", ``);
|
||||
const file3 = host.vfs.addFile("/a/b/file3.d.ts", ``);
|
||||
@ -450,15 +438,14 @@ namespace ts.projectSystem {
|
||||
};
|
||||
|
||||
const typingFiles = [commander, express, jquery, moment, lodash];
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { throttleLimit: 3, typesRegistry: createTypesRegistry("commander", "express", "jquery", "moment", "lodash") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment", "@types/lodash"];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { typesRegistry: createTypesRegistry("commander", "express", "jquery", "moment", "lodash") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment", "@types/lodash"];
|
||||
executeCommand(installer, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectFileName = "/a/app/test.csproj";
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
@ -481,10 +468,11 @@ namespace ts.projectSystem {
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [lodashJs.path, commanderJs.path, file3.path, commander.path, express.path, jquery.path, moment.path, lodash.path]);
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("Throttle - delayed run install requests", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const lodashJs = host.vfs.addFile("/a/b/lodash.js", ``);
|
||||
const commanderJs = host.vfs.addFile("/a/b/commander.js", ``);
|
||||
const file3 = host.vfs.addFile("/a/b/file3.d.ts", ``);
|
||||
@ -521,21 +509,18 @@ namespace ts.projectSystem {
|
||||
typings: typingsName("gulp")
|
||||
};
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { throttleLimit: 1, typesRegistry: createTypesRegistry("commander", "jquery", "lodash", "cordova", "gulp", "grunt") });
|
||||
}
|
||||
installWorker(_requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
let typingFiles: (FileOrFolder & { typings: string })[] = [];
|
||||
if (args.indexOf(typingsName("commander")) >= 0) {
|
||||
typingFiles = [commander, jquery, lodash, cordova];
|
||||
const installer = new Installer(host, { throttleLimit: 1, typesRegistry: createTypesRegistry("commander", "jquery", "lodash", "cordova", "gulp", "grunt") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.includes(typingsName("commander")), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, [commander.typings, jquery.typings, lodash.typings, cordova.typings], [commander, jquery, lodash, cordova], cb);
|
||||
}
|
||||
else {
|
||||
typingFiles = [grunt, gulp];
|
||||
})
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, [grunt.typings, gulp.typings], [grunt, gulp], cb);
|
||||
}
|
||||
executeCommand(this, host, typingFiles.map(f => f.typings), typingFiles, cb);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Create project #1 with 4 typings
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
@ -576,6 +561,7 @@ namespace ts.projectSystem {
|
||||
host.checkTimeoutQueueLengthAndRun(3);
|
||||
checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path, commander.path, jquery.path, lodash.path, cordova.path]);
|
||||
checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path]);
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("configured projects discover from node_modules", () => {
|
||||
@ -583,7 +569,7 @@ namespace ts.projectSystem {
|
||||
path: "/tmp/node_modules/@types/jquery/index.d.ts",
|
||||
content: ""
|
||||
};
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const app = host.vfs.addFile("/app.js", ``);
|
||||
const jsconfig = host.vfs.addFile("/jsconfig.json", `{}`);
|
||||
host.vfs.addFile("/node_modules/jquery/index.js", ``);
|
||||
@ -591,17 +577,13 @@ namespace ts.projectSystem {
|
||||
// Should not search deeply in node_modules.
|
||||
host.vfs.addFile("/node_modules/jquery/nested/package.json", `{ "name": "nested" }`);
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery", "nested") });
|
||||
}
|
||||
installWorker(_requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
assert.deepEqual(args, [`@types/jquery@ts${versionMajorMinor}`]);
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jqueryDTS];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery", "nested") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.array([`@types/jquery@ts${versionMajorMinor}`]), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, ["@types/jquery"], [jqueryDTS], cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
|
||||
projectService.openClientFile(app.path);
|
||||
@ -615,10 +597,13 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path, jsconfig.path]);
|
||||
installWorkerSpy
|
||||
.verify(_ => _(Arg.any(), Arg.not(Arg.array([`@types/jquery@ts${versionMajorMinor}`])), Arg.any(), Arg.any()), Times.none())
|
||||
.revoke();
|
||||
});
|
||||
|
||||
it("configured projects discover from bower_components", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const app = host.vfs.addFile("/app.js", ``);
|
||||
const jsconfig = host.vfs.addFile("/jsconfig.json", `{}`);
|
||||
host.vfs.addFile("/bower_components/jquery/index.js", ``);
|
||||
@ -629,16 +614,13 @@ namespace ts.projectSystem {
|
||||
content: ""
|
||||
};
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jqueryDTS];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, ["@types/jquery"], [jqueryDTS], cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
|
||||
projectService.openClientFile(app.path);
|
||||
@ -653,10 +635,11 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path, jsconfig.path]);
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("configured projects discover from bower.json", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const app = host.vfs.addFile("/app.js", ``);
|
||||
const jsconfig = host.vfs.addFile("/jsconfig.json", `{}`);
|
||||
host.vfs.addFile("/bower.json", `{ "dependencies": { "jquery": "^3.1.0" } }`);
|
||||
@ -666,16 +649,13 @@ namespace ts.projectSystem {
|
||||
content: ""
|
||||
};
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jqueryDTS];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, ["@types/jquery"], [jqueryDTS], cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectService = createProjectService(host, { useSingleInferredProject: true, typingsInstaller: installer });
|
||||
projectService.openClientFile(app.path);
|
||||
@ -689,10 +669,11 @@ namespace ts.projectSystem {
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path, jsconfig.path]);
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("Malformed package.json should be watched", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const f = host.vfs.addFile("/a/b/app.js", `var x = 1`);
|
||||
host.vfs.addFile("/a/b/package.json", `{ "dependencies": { "co } }`);
|
||||
|
||||
@ -702,16 +683,13 @@ namespace ts.projectSystem {
|
||||
content: "export let x: number"
|
||||
};
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/commander"];
|
||||
const typingFiles = [commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, ["@types/commander"], [commander], cb);
|
||||
}
|
||||
});
|
||||
|
||||
const service = createProjectService(host, { typingsInstaller: installer });
|
||||
service.openClientFile(f.path);
|
||||
@ -729,10 +707,11 @@ namespace ts.projectSystem {
|
||||
|
||||
service.checkNumberOfProjects({ inferredProjects: 1 });
|
||||
checkProjectActualFiles(service.inferredProjects[0], [f.path, commander.path]);
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("should install typings for unresolved imports", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const file = host.vfs.addFile("/a/b/app.js",
|
||||
`import * as fs from "fs";\n` +
|
||||
`import * as commander from "commander";`);
|
||||
@ -747,16 +726,13 @@ namespace ts.projectSystem {
|
||||
content: "export let y: string"
|
||||
};
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("node", "commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/node", "@types/commander"];
|
||||
const typingFiles = [node, commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("node", "commander") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, ["@types/node", "@types/commander"], [node, commander], cb);
|
||||
}
|
||||
});
|
||||
|
||||
const service = createProjectService(host, { typingsInstaller: installer });
|
||||
service.openClientFile(file.path);
|
||||
@ -770,10 +746,11 @@ namespace ts.projectSystem {
|
||||
assert.isTrue(host.fileExists(commander.path), "typings for 'commander' should be created");
|
||||
|
||||
checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path]);
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("should pick typing names from non-relative unresolved imports", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const f1 = host.vfs.addFile("/a/b/app.js",
|
||||
`import * as a from "foo/a/a";\n` +
|
||||
`import * as b from "foo/a/b";\n` +
|
||||
@ -783,14 +760,13 @@ namespace ts.projectSystem {
|
||||
`import * as e from "@bar/common/apps";\n` +
|
||||
`import * as f from "./lib"`);
|
||||
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("foo") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
executeCommand(this, host, ["foo"], [], cb);
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("foo") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, ["foo"], [], cb);
|
||||
}
|
||||
});
|
||||
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
@ -805,10 +781,11 @@ namespace ts.projectSystem {
|
||||
);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
installWorkerSpy.revoke();
|
||||
});
|
||||
|
||||
it("cached unresolved typings are not recomputed if program structure did not change", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const session = createSession(host);
|
||||
const f = {
|
||||
path: "/a/app.js",
|
||||
@ -879,25 +856,23 @@ namespace ts.projectSystem {
|
||||
|
||||
describe("Invalid package names", () => {
|
||||
it("should not be installed", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const f1 = host.vfs.addFile("/a/b/app.js", `let x = 1`);
|
||||
host.vfs.addFile("/a/b/package.json", `{ "dependencies": { "; say ‘Hello from TypeScript!’ #": "0.0.x" } }`);
|
||||
|
||||
const messages: string[] = [];
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp" }, { isEnabled: () => true, writeLine: msg => messages.push(msg) });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, _cb: TI.RequestCompletedAction) {
|
||||
assert(false, "runCommand should not be invoked");
|
||||
}
|
||||
};
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: "/tmp" }, { isEnabled: () => true, writeLine: msg => messages.push(msg) });
|
||||
const installWorkerSpy = spy(installer, "installWorker");
|
||||
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
assert.isTrue(messages.indexOf("Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name");
|
||||
|
||||
installWorkerSpy
|
||||
.verify(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), Times.none())
|
||||
.revoke();
|
||||
});
|
||||
});
|
||||
|
||||
@ -905,7 +880,7 @@ namespace ts.projectSystem {
|
||||
const emptySafeList = emptyMap;
|
||||
|
||||
it("should use mappings from safe list", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const app = host.vfs.addFile("/a/b/app.js", ``);
|
||||
const jquery = host.vfs.addFile("/a/b/jquery.js", ``);
|
||||
const chroma = host.vfs.addFile("/a/b/chroma.min.js", ``);
|
||||
@ -925,7 +900,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should return node for core modules", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const f = host.vfs.addFile("/a/b/app.js", ``);
|
||||
|
||||
const cache = createMap<string>();
|
||||
@ -942,7 +917,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should use cached locations", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const f = host.vfs.addFile("/a/b/app.js", ``);
|
||||
const node = host.vfs.addFile("/a/b/node.d.ts", ``);
|
||||
|
||||
@ -958,7 +933,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
it("should search only 2 levels deep", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const app = host.vfs.addFile("/app.js");
|
||||
host.vfs.addFile("/node_modules/a/package.json", `{ "name": "a" }`);
|
||||
host.vfs.addFile("/node_modules/a/b/package.json", `{ "name": "b" }`);
|
||||
@ -982,7 +957,7 @@ namespace ts.projectSystem {
|
||||
|
||||
describe("telemetry events", () => {
|
||||
it("should be received", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const f1 = host.vfs.addFile("/a/app.js", ``);
|
||||
host.vfs.addFile("/a/package.json", `{ "dependencies": { "commander": "1.0.0" } }`);
|
||||
|
||||
@ -992,35 +967,30 @@ namespace ts.projectSystem {
|
||||
content: "export let x: number"
|
||||
};
|
||||
|
||||
let seenTelemetryEvent = false;
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/commander"];
|
||||
const typingFiles = [commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
if (response.kind === server.EventBeginInstallTypes) {
|
||||
return;
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, ["@types/commander"], [commander], cb);
|
||||
}
|
||||
if (response.kind === server.EventEndInstallTypes) {
|
||||
assert.deepEqual(response.packagesToInstall, [typingsName("commander")]);
|
||||
seenTelemetryEvent = true;
|
||||
return;
|
||||
}
|
||||
super.sendResponse(response);
|
||||
}
|
||||
};
|
||||
});
|
||||
const sendResponseSpy = spy(installer, "sendResponse")
|
||||
.setup(_ => _(Arg.is(response => response.kind === server.EventBeginInstallTypes)))
|
||||
.setup(_ => _(Arg.is(response => response.kind === server.EventEndInstallTypes)))
|
||||
.setup(_ => _(Arg.any()), { fallback: true });
|
||||
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
assert.isTrue(seenTelemetryEvent);
|
||||
installWorkerSpy.revoke();
|
||||
sendResponseSpy
|
||||
.verify(_ => _(Arg.is(response => response.kind === server.EventEndInstallTypes &&
|
||||
response.packagesToInstall.length === 1 &&
|
||||
response.packagesToInstall[0] === typingsName("commander"))))
|
||||
.revoke();
|
||||
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]);
|
||||
@ -1029,7 +999,7 @@ namespace ts.projectSystem {
|
||||
|
||||
describe("progress notifications", () => {
|
||||
it("should be sent for success", () => {
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const f1 = host.vfs.addFile("/a/app.js", ``);
|
||||
host.vfs.addFile("/a/package.json", `{ "dependencies": { "commander": "1.0.0" } }`);
|
||||
|
||||
@ -1041,35 +1011,29 @@ namespace ts.projectSystem {
|
||||
|
||||
let beginEvent: server.BeginInstallTypes;
|
||||
let endEvent: server.EndInstallTypes;
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/commander"];
|
||||
const typingFiles = [commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
if (response.kind === server.EventBeginInstallTypes) {
|
||||
beginEvent = response;
|
||||
return;
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, ["@types/commander"], [commander], cb);
|
||||
}
|
||||
if (response.kind === server.EventEndInstallTypes) {
|
||||
endEvent = response;
|
||||
return;
|
||||
}
|
||||
super.sendResponse(response);
|
||||
}
|
||||
};
|
||||
});
|
||||
const sendResponseSpy = spy(installer, "sendResponse")
|
||||
.setup(_ => _(Arg.is(response => response.kind === server.EventBeginInstallTypes)), { callback: response => { beginEvent = response; } })
|
||||
.setup(_ => _(Arg.is(response => response.kind === server.EventEndInstallTypes)), { callback: response => { endEvent = response; } })
|
||||
.setup(_ => _(Arg.any()), { fallback: true });
|
||||
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
assert.isTrue(!!beginEvent);
|
||||
assert.isTrue(!!endEvent);
|
||||
installWorkerSpy.revoke();
|
||||
sendResponseSpy
|
||||
.verify(_ => _(Arg.is(response => response.kind === server.EventBeginInstallTypes)))
|
||||
.verify(_ => _(Arg.is(response => response.kind === server.EventEndInstallTypes)))
|
||||
.revoke();
|
||||
|
||||
assert.isTrue(beginEvent.eventId === endEvent.eventId);
|
||||
assert.isTrue(endEvent.installSuccess);
|
||||
host.checkTimeoutQueueLengthAndRun(2);
|
||||
@ -1079,40 +1043,37 @@ namespace ts.projectSystem {
|
||||
|
||||
it("should be sent for error", () => {
|
||||
// const host = createServerHost([f1, packageFile]);
|
||||
const host = new mocks.MockServerHost({ safeList: true });
|
||||
const host = new fakes.FakeServerHost({ safeList: true });
|
||||
const f1 = host.vfs.addFile("/a/app.js", ``);
|
||||
host.vfs.addFile("/a/package.json", `{ "dependencies": { "commander": "1.0.0" } }`);
|
||||
|
||||
const cachePath = "/a/cache/";
|
||||
|
||||
let beginEvent: server.BeginInstallTypes;
|
||||
let endEvent: server.EndInstallTypes;
|
||||
const installer = new class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) {
|
||||
executeCommand(this, host, "", [], cb);
|
||||
}
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
if (response.kind === server.EventBeginInstallTypes) {
|
||||
beginEvent = response;
|
||||
return;
|
||||
const installer = new Installer(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
const installWorkerSpy = spy(installer, "installWorker")
|
||||
.setup(_ => _(Arg.any(), Arg.any(), Arg.any(), Arg.any()), {
|
||||
callback: (_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction) => {
|
||||
executeCommand(installer, host, "", [], cb);
|
||||
}
|
||||
if (response.kind === server.EventEndInstallTypes) {
|
||||
endEvent = response;
|
||||
return;
|
||||
}
|
||||
super.sendResponse(response);
|
||||
}
|
||||
};
|
||||
});
|
||||
const sendResponseSpy = spy(installer, "sendResponse")
|
||||
.setup(_ => _(Arg.is(response => response.kind === server.EventBeginInstallTypes)), { callback: response => { beginEvent = response; } })
|
||||
.setup(_ => _(Arg.is(response => response.kind === server.EventEndInstallTypes)), { callback: response => { endEvent = response; } })
|
||||
.setup(_ => _(Arg.any()), { fallback: true });
|
||||
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
assert.isTrue(!!beginEvent);
|
||||
assert.isTrue(!!endEvent);
|
||||
installWorkerSpy.revoke();
|
||||
sendResponseSpy
|
||||
.verify(_ => _(Arg.is(response => response.kind === server.EventBeginInstallTypes)))
|
||||
.verify(_ => _(Arg.is(response => response.kind === server.EventEndInstallTypes)))
|
||||
.revoke();
|
||||
|
||||
assert.isTrue(beginEvent.eventId === endEvent.eventId);
|
||||
assert.isFalse(endEvent.installSuccess);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
|
||||
@ -209,7 +209,7 @@ namespace vfs {
|
||||
}
|
||||
|
||||
private get watchedDirectoriesPrivate() {
|
||||
return this._watchedDirectories || (this._watchedDirectories = new core.KeyedCollection<string, DirectoryWatcherEntryArray>(this.pathComparer))
|
||||
return this._watchedDirectories || (this._watchedDirectories = new core.KeyedCollection<string, DirectoryWatcherEntryArray>(this.pathComparer));
|
||||
}
|
||||
|
||||
private get watchedRecursiveDirectoriesSetPrivate() {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/// <reference path="harness.ts" />
|
||||
/// <reference path="./mocks.ts" />
|
||||
/// <reference path="./fakes.ts" />
|
||||
|
||||
// TODO(rbuckton): Migrate this to use vfs.
|
||||
|
||||
@ -41,34 +41,6 @@ interface Array<T> {}`
|
||||
useWindowsStylePaths?: boolean;
|
||||
}
|
||||
|
||||
export function createWatchedSystem(fileOrFolderList: ReadonlyArray<FileOrFolder>, params: TestServerHostCreationParameters = {}) {
|
||||
// const host = new mocks.MockServerHost({
|
||||
// vfs: {
|
||||
// currentDirectory: params.currentDirectory,
|
||||
// useCaseSensitiveFileNames: params.useCaseSensitiveFileNames
|
||||
// },
|
||||
// executingFilePath: params.executingFilePath,
|
||||
// newLine: params.newLine as "\r\n" | "\n"
|
||||
// });
|
||||
// for (const entry of fileOrFolderList) {
|
||||
// if (typeof entry.content === "string") {
|
||||
// host.vfs.addFile(entry.path, entry.content);
|
||||
// }
|
||||
// else {
|
||||
// host.vfs.addDirectory(entry.path);
|
||||
// }
|
||||
// }
|
||||
// if (params.useWindowsStylePaths) throw new Error("Not supported");
|
||||
const host = new TestServerHost(/*withSafelist*/ false,
|
||||
params.useCaseSensitiveFileNames !== undefined ? params.useCaseSensitiveFileNames : false,
|
||||
params.executingFilePath || getExecutingFilePathFromLibFile(),
|
||||
params.currentDirectory || "/",
|
||||
fileOrFolderList,
|
||||
params.newLine,
|
||||
params.useWindowsStylePaths);
|
||||
return host;
|
||||
}
|
||||
|
||||
export function createServerHost(fileOrFolderList: ReadonlyArray<FileOrFolder>, params?: TestServerHostCreationParameters): TestServerHost {
|
||||
if (!params) {
|
||||
params = {};
|
||||
@ -174,22 +146,22 @@ interface Array<T> {}`
|
||||
}
|
||||
}
|
||||
|
||||
export function checkWatchedFiles(host: TestServerHost | mocks.MockServerHost, expectedFiles: string[]) {
|
||||
if (host instanceof mocks.MockServerHost) {
|
||||
export function checkWatchedFiles(host: TestServerHost | fakes.FakeServerHost, expectedFiles: string[]) {
|
||||
if (host instanceof fakes.FakeServerHost) {
|
||||
return checkSortedSet(host.vfs.watchedFiles, expectedFiles);
|
||||
}
|
||||
|
||||
checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles);
|
||||
}
|
||||
|
||||
export function checkWatchedDirectories(host: TestServerHost | mocks.MockServerHost, expectedDirectories: string[], recursive = false) {
|
||||
if (host instanceof mocks.MockServerHost) {
|
||||
export function checkWatchedDirectories(host: TestServerHost | fakes.FakeServerHost, expectedDirectories: string[], recursive = false) {
|
||||
if (host instanceof fakes.FakeServerHost) {
|
||||
return checkSortedSet(recursive ? host.vfs.watchedRecursiveDirectories : host.vfs.watchedNonRecursiveDirectories, expectedDirectories);
|
||||
}
|
||||
checkMapKeys(`watchedDirectories${recursive ? " recursive" : ""}`, recursive ? host.watchedDirectoriesRecursive : host.watchedDirectories, expectedDirectories);
|
||||
}
|
||||
|
||||
export function checkOutputContains(host: TestServerHost | mocks.MockServerHost, expected: ReadonlyArray<string>) {
|
||||
export function checkOutputContains(host: TestServerHost | fakes.FakeServerHost, expected: ReadonlyArray<string>) {
|
||||
const mapExpected = arrayToSet(expected);
|
||||
const mapSeen = createMap<true>();
|
||||
for (const f of host.getOutput()) {
|
||||
@ -202,7 +174,7 @@ interface Array<T> {}`
|
||||
assert.equal(mapExpected.size, 0, `Output has missing ${JSON.stringify(flatMapIter(mapExpected.keys(), key => key))} in ${JSON.stringify(host.getOutput())}`);
|
||||
}
|
||||
|
||||
export function checkOutputDoesNotContain(host: TestServerHost | mocks.MockServerHost, expectedToBeAbsent: string[] | ReadonlyArray<string>) {
|
||||
export function checkOutputDoesNotContain(host: TestServerHost | fakes.FakeServerHost, expectedToBeAbsent: string[] | ReadonlyArray<string>) {
|
||||
const mapExpectedToBeAbsent = arrayToSet(expectedToBeAbsent);
|
||||
for (const f of host.getOutput()) {
|
||||
assert.isFalse(mapExpectedToBeAbsent.has(f), `Contains ${f} in ${JSON.stringify(host.getOutput())}`);
|
||||
|
||||
@ -537,7 +537,7 @@ namespace ts.server {
|
||||
}
|
||||
}
|
||||
|
||||
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings): void {
|
||||
updateTypingsForProject(response: SetTypings | InvalidateCachedTypings | BeginInstallTypes | EndInstallTypes): void {
|
||||
const project = this.findProject(response.projectName);
|
||||
if (!project) {
|
||||
return;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user