mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' into disambiguating
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
// @module: system
|
||||
// @isolatedModules: true
|
||||
|
||||
import alias = require('foo');
|
||||
import cls = alias.Class;
|
||||
export import cls2 = alias.Class;
|
||||
|
||||
let x = new alias.Class();
|
||||
let y = new cls();
|
||||
let z = new cls2();
|
||||
|
||||
module M {
|
||||
export import cls = alias.Class;
|
||||
let x = new alias.Class();
|
||||
let y = new cls();
|
||||
let z = new cls2();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// @module: system
|
||||
// @isolatedModules: true
|
||||
|
||||
import {alias} from "foo";
|
||||
import cls = alias.Class;
|
||||
export import cls2 = alias.Class;
|
||||
|
||||
let x = new alias.Class();
|
||||
let y = new cls();
|
||||
let z = new cls2();
|
||||
|
||||
module M {
|
||||
export import cls = alias.Class;
|
||||
let x = new alias.Class();
|
||||
let y = new cls();
|
||||
let z = new cls2();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
class C extends null {
|
||||
constructor() {
|
||||
super();
|
||||
return Object.create(null);
|
||||
}
|
||||
}
|
||||
|
||||
class D extends null {
|
||||
constructor() {
|
||||
return Object.create(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
//@module: amd
|
||||
//@filename: m1.ts
|
||||
|
||||
function foo() {}
|
||||
module foo {
|
||||
const enum E { X }
|
||||
}
|
||||
|
||||
export = foo
|
||||
@@ -0,0 +1,9 @@
|
||||
//@module: amd
|
||||
//@filename: m1.ts
|
||||
|
||||
class foo {}
|
||||
module foo {
|
||||
const enum E { X }
|
||||
}
|
||||
|
||||
export = foo
|
||||
@@ -0,0 +1,9 @@
|
||||
//@module: amd
|
||||
//@filename: m1.ts
|
||||
|
||||
enum foo { A }
|
||||
module foo {
|
||||
const enum E { X }
|
||||
}
|
||||
|
||||
export = foo
|
||||
@@ -0,0 +1,13 @@
|
||||
//@module: amd
|
||||
//@filename: m1.ts
|
||||
|
||||
module foo {
|
||||
const enum E { X }
|
||||
}
|
||||
|
||||
module foo {
|
||||
var x = 1;
|
||||
}
|
||||
|
||||
|
||||
export = foo
|
||||
@@ -0,0 +1,9 @@
|
||||
//@module: amd
|
||||
//@filename: m1.ts
|
||||
//@preserveConstEnums: true
|
||||
|
||||
module foo {
|
||||
const enum E { X }
|
||||
}
|
||||
|
||||
export = foo
|
||||
@@ -0,0 +1,17 @@
|
||||
// @target: ES5
|
||||
// @sourcemap: false
|
||||
// @declaration: false
|
||||
// @module: commonjs
|
||||
|
||||
export default class A
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public B()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// @target: ES5
|
||||
// @sourcemap: false
|
||||
// @declaration: false
|
||||
// @module: commonjs
|
||||
|
||||
export default 1;
|
||||
@@ -0,0 +1,7 @@
|
||||
// @target: ES5
|
||||
// @sourcemap: false
|
||||
// @declaration: false
|
||||
// @module: commonjs
|
||||
|
||||
export default "test";
|
||||
export var __esModule = 1;
|
||||
@@ -0,0 +1,18 @@
|
||||
// @target: ES5
|
||||
// @sourcemap: false
|
||||
// @declaration: false
|
||||
// @module: commonjs
|
||||
|
||||
export default class A
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public B()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
export var __esModule = 1;
|
||||
@@ -0,0 +1,8 @@
|
||||
// @target: ES5
|
||||
// @sourcemap: false
|
||||
// @declaration: false
|
||||
// @module: commonjs
|
||||
|
||||
export default function () {
|
||||
return "test";
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// @target: ES5
|
||||
// @sourcemap: false
|
||||
// @declaration: false
|
||||
// @module: commonjs
|
||||
|
||||
export default "test";
|
||||
var __esModule = 1;
|
||||
@@ -0,0 +1,17 @@
|
||||
// @target: ES5
|
||||
// @sourcemap: false
|
||||
// @declaration: false
|
||||
// @module: system
|
||||
|
||||
export default class A
|
||||
{
|
||||
constructor ()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public B()
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
@@ -3,4 +3,4 @@
|
||||
// @target: es6
|
||||
|
||||
// @filename: file1.ts
|
||||
export var x;
|
||||
export var x = 1;
|
||||
@@ -0,0 +1,14 @@
|
||||
//@jsx: preserve
|
||||
//@module: commonjs
|
||||
|
||||
//@filename: component.d.ts
|
||||
declare module "Test" {
|
||||
export default class Text { }
|
||||
}
|
||||
|
||||
//@filename: consumer.tsx
|
||||
/// <reference path="component.d.ts" />
|
||||
import Test from 'Test';
|
||||
|
||||
let x = Test; // emit test_1.default
|
||||
<anything attr={Test} />; // ?
|
||||
@@ -0,0 +1,23 @@
|
||||
//@jsx: preserve
|
||||
//@module: commonjs
|
||||
|
||||
//@filename: component.d.ts
|
||||
declare module JSX {
|
||||
interface ElementAttributesProperty { props; }
|
||||
}
|
||||
declare module React {
|
||||
class Component<T, U> { }
|
||||
}
|
||||
declare module "BaseComponent" {
|
||||
var base: React.Component<any, {}>;
|
||||
export = base;
|
||||
}
|
||||
|
||||
//@filename: consumer.tsx
|
||||
/// <reference path="component.d.ts" />
|
||||
import BaseComponent = require('BaseComponent');
|
||||
class TestComponent extends React.Component<any, {}> {
|
||||
render() {
|
||||
return <BaseComponent />;
|
||||
}
|
||||
}
|
||||
@@ -383,6 +383,7 @@ module Underscore {
|
||||
evaluate?: RegExp;
|
||||
interpolate?: RegExp;
|
||||
escape?: RegExp;
|
||||
variable?: string;
|
||||
}
|
||||
|
||||
export interface Static {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// @module: commonjs
|
||||
// @target: ES5
|
||||
|
||||
// @filename: m1.ts
|
||||
export default function Decl() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
export interface Decl {
|
||||
p1: number;
|
||||
p2: number;
|
||||
}
|
||||
|
||||
export namespace Decl {
|
||||
export var x = 10;
|
||||
export var y = 20;
|
||||
|
||||
interface I {
|
||||
}
|
||||
}
|
||||
|
||||
// @filename: m2.ts
|
||||
import Entity from "m1"
|
||||
|
||||
Entity();
|
||||
|
||||
var x: Entity;
|
||||
var y: Entity.I;
|
||||
|
||||
Entity.x;
|
||||
Entity.y;
|
||||
@@ -0,0 +1,26 @@
|
||||
// @module: commonjs
|
||||
// @target: ES5
|
||||
|
||||
// @filename: m1.ts
|
||||
export default class Decl {
|
||||
}
|
||||
|
||||
export interface Decl {
|
||||
p1: number;
|
||||
p2: number;
|
||||
}
|
||||
|
||||
export namespace Decl {
|
||||
interface I {
|
||||
}
|
||||
}
|
||||
|
||||
// @filename: m2.ts
|
||||
import Entity from "m1"
|
||||
|
||||
Entity();
|
||||
|
||||
var x: Entity;
|
||||
var y: Entity.I;
|
||||
var z = new Entity();
|
||||
var sum = z.p1 + z.p2
|
||||
@@ -0,0 +1,26 @@
|
||||
// @module: commonjs
|
||||
// @target: ES5
|
||||
|
||||
// @filename: m1.ts
|
||||
export default class Decl {
|
||||
}
|
||||
|
||||
interface Decl {
|
||||
p1: number;
|
||||
p2: number;
|
||||
}
|
||||
|
||||
namespace Decl {
|
||||
interface I {
|
||||
}
|
||||
}
|
||||
|
||||
// @filename: m2.ts
|
||||
import Entity from "m1"
|
||||
|
||||
Entity();
|
||||
|
||||
var x: Entity;
|
||||
var y: Entity.I;
|
||||
var z = new Entity();
|
||||
var sum = z.p1 + z.p2
|
||||
@@ -0,0 +1,15 @@
|
||||
// @module: commonjs
|
||||
// @target: ES5
|
||||
|
||||
export default function Foo() {
|
||||
}
|
||||
|
||||
namespace Foo {
|
||||
export var x;
|
||||
}
|
||||
|
||||
interface Foo {
|
||||
}
|
||||
|
||||
export interface Foo {
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// @module: commonjs
|
||||
// @target: ES5
|
||||
|
||||
// @filename: m1.ts
|
||||
export default class foo {
|
||||
|
||||
}
|
||||
|
||||
export default function bar() {
|
||||
|
||||
}
|
||||
|
||||
var x = 10;
|
||||
export default x;
|
||||
|
||||
// @filename: m2.ts
|
||||
import Entity from "m1"
|
||||
|
||||
Entity();
|
||||
@@ -0,0 +1,16 @@
|
||||
// @module: commonjs
|
||||
// @target: ES5
|
||||
|
||||
// @filename: m1.ts
|
||||
export default function foo() {
|
||||
|
||||
}
|
||||
|
||||
export default function bar() {
|
||||
|
||||
}
|
||||
|
||||
// @filename: m2.ts
|
||||
import Entity from "m1"
|
||||
|
||||
Entity();
|
||||
@@ -1,7 +1,8 @@
|
||||
/// <reference path="fourslash.ts" />
|
||||
|
||||
//// /*1*/function parseInt(s/*2*/:string):number;
|
||||
|
||||
goTo.marker('2');
|
||||
edit.deleteAtCaret(':string'.length);
|
||||
goTo.marker('1');
|
||||
edit.insert('declare ');
|
||||
|
||||
edit.insert('declare ');
|
||||
@@ -11,7 +11,6 @@
|
||||
////var a2 = { name: 'bob', age: 18, address: 'springfield' };
|
||||
////var b2 = { name: 'jim', age: 20, dob: new Date() };
|
||||
////var /*3*/c2 = [a2, b2];
|
||||
////var /*4*/c2a = [a2, b2, a1];
|
||||
|
||||
////interface I {
|
||||
//// name: string;
|
||||
@@ -19,13 +18,13 @@
|
||||
////}
|
||||
|
||||
////var i: I;
|
||||
////var /*5*/c3 = [a2, b2, i];
|
||||
////var /*4*/c3 = [i, a];
|
||||
|
||||
goTo.marker('1');
|
||||
verify.quickInfoIs('var c: {\n name: string;\n age: number;\n}[]');
|
||||
|
||||
goTo.marker('2');
|
||||
verify.quickInfoIs('var c1: {\n name: string;\n age: number;\n}[]');
|
||||
verify.quickInfoIs('var c1: ({\n name: string;\n age: number;\n} | {\n name: string;\n age: number;\n dob: Date;\n})[]');
|
||||
|
||||
goTo.marker('3');
|
||||
verify.quickInfoIs('var c2: ({\n\
|
||||
@@ -39,8 +38,5 @@ verify.quickInfoIs('var c2: ({\n\
|
||||
})[]');
|
||||
|
||||
goTo.marker('4');
|
||||
verify.quickInfoIs('var c2a: {\n name: string;\n age: number;\n}[]');
|
||||
|
||||
goTo.marker('5');
|
||||
verify.quickInfoIs('var c3: I[]');
|
||||
|
||||
|
||||
@@ -15,6 +15,6 @@
|
||||
////x./**/
|
||||
|
||||
goTo.marker();
|
||||
verify.memberListContains("commonProperty", "(property) commonProperty: string | number");
|
||||
verify.memberListContains("commonProperty", "(property) commonProperty: number | string");
|
||||
verify.memberListContains("commonFunction", "(method) commonFunction(): number");
|
||||
verify.memberListCount(2);
|
||||
@@ -15,6 +15,6 @@
|
||||
////x.commonProperty./**/
|
||||
|
||||
goTo.marker();
|
||||
verify.memberListContains("toString", "(method) toString(): string");
|
||||
verify.memberListContains("valueOf", "(method) valueOf(): string | number");
|
||||
verify.memberListContains("toString", "(property) toString: ((radix?: number) => string) | (() => string)");
|
||||
verify.memberListContains("valueOf", "(method) valueOf(): number | string");
|
||||
verify.memberListCount(2);
|
||||
@@ -3,10 +3,10 @@
|
||||
//// class A {
|
||||
//// foo: string;
|
||||
//// }
|
||||
//// class C extends A {
|
||||
//// baz: string;
|
||||
//// class C {
|
||||
//// foo: string;
|
||||
//// }
|
||||
//// var xs /*1*/ = [(x: A) => { return x.foo; }, (x: C) => { return x.baz; }];
|
||||
//// var xs /*1*/ = [(x: A) => { return x.foo; }, (x: C) => { return x.foo; }];
|
||||
//// xs.forEach(y => y(new /*2*/A()));
|
||||
|
||||
verify.numberOfErrorsInCurrentFile(0);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
////var x4: I = [a, b, c];
|
||||
////var /*4*/r4 = x4[1];
|
||||
|
||||
////var /*5*/x5 = [a, b, c, d];
|
||||
////var /*5*/x5 = [a, b];
|
||||
////var /*6*/r5 = x5[1];
|
||||
|
||||
// the above code should have a couple errors that will need to be updated with appropriate new (non-error) code and quick info checks
|
||||
@@ -43,7 +43,21 @@ goTo.marker('4');
|
||||
verify.quickInfoIs('var r4: C');
|
||||
|
||||
goTo.marker('5');
|
||||
verify.quickInfoIs('var x5: C[]');
|
||||
verify.quickInfoIs('var x5: ({\n\
|
||||
name: string;\n\
|
||||
age: number;\n\
|
||||
} | {\n\
|
||||
name: string;\n\
|
||||
age: number;\n\
|
||||
dob: Date;\n\
|
||||
})[]');
|
||||
|
||||
goTo.marker('6');
|
||||
verify.quickInfoIs('var r5: C');
|
||||
verify.quickInfoIs('var r5: {\n\
|
||||
name: string;\n\
|
||||
age: number;\n\
|
||||
} | {\n\
|
||||
name: string;\n\
|
||||
age: number;\n\
|
||||
dob: Date;\n\
|
||||
}');
|
||||
|
||||
@@ -29,7 +29,34 @@
|
||||
// type 'fs.' as an alternate way of accessing the top-level objects
|
||||
// (e.g. 'fs.goTo.eof();')
|
||||
|
||||
//---------------------------------------
|
||||
// For API editors:
|
||||
// When editting this file, and only while editing this file, enable the reference comments
|
||||
// and comment out the declarations in this section to get proper type information.
|
||||
// Undo these changes before compiling/committing/editing any other fourslash tests.
|
||||
// The test suite will likely crash if you try 'jake runtests' with reference comments enabled.
|
||||
//
|
||||
// Explanation:
|
||||
// We want type-completion while we edit this file, but at compile time/while editting fourslash tests,
|
||||
// we don't want to include the following reference because we are compiling this file in "--out" mode and don't want to rope
|
||||
// in the entire codebase into the compilation each fourslash test. Additionally, we don't want to expose the
|
||||
// src/harness/fourslash.ts API's (or the rest of the compiler) because they are unstable and complicate the
|
||||
// fourslash testing DSL. Finally, in this case, runtime reflection is (much) faster.
|
||||
//
|
||||
// TODO: figure out a better solution to the API exposure problem.
|
||||
|
||||
// /// <reference path="../../../built/local/typescriptServices.d.ts"/>
|
||||
// /// <reference path="../../../src/harness/fourslash.ts"/>
|
||||
|
||||
declare var FourSlash;
|
||||
module ts {
|
||||
export interface SymbolDisplayPart {
|
||||
text: string;
|
||||
kind: string;
|
||||
}
|
||||
}
|
||||
|
||||
//---------------------------------------------
|
||||
|
||||
// Return code used by getEmitOutput function to indicate status of the function
|
||||
// It is a duplicate of the one in types.ts to expose it to testcases in fourslash
|
||||
@@ -42,7 +69,6 @@ enum EmitReturnStatus {
|
||||
}
|
||||
|
||||
module FourSlashInterface {
|
||||
declare var FourSlash;
|
||||
|
||||
export interface Marker {
|
||||
fileName: string;
|
||||
@@ -201,15 +227,6 @@ module FourSlashInterface {
|
||||
FourSlash.currentTestState.verifyReferencesAtPositionListContains(range.fileName, range.start, range.end, isWriteAccess);
|
||||
}
|
||||
|
||||
public implementorsCountIs(count: number) {
|
||||
FourSlash.currentTestState.verifyImplementorsCountIs(count);
|
||||
}
|
||||
|
||||
// Add tests for this.
|
||||
public currentParameterIsVariable() {
|
||||
FourSlash.currentTestState.verifyCurrentParameterIsVariable(!this.negative);
|
||||
}
|
||||
|
||||
public signatureHelpPresent() {
|
||||
FourSlash.currentTestState.verifySignatureHelpPresent(!this.negative);
|
||||
}
|
||||
@@ -361,14 +378,11 @@ module FourSlashInterface {
|
||||
FourSlash.currentTestState.verifyNoMatchingBracePosition(bracePosition);
|
||||
}
|
||||
|
||||
public setVerifyDocComments(val: boolean) {
|
||||
FourSlash.currentTestState.setVerifyDocComments(val);
|
||||
}
|
||||
|
||||
public getScriptLexicalStructureListCount(count: number) {
|
||||
FourSlash.currentTestState.verifyGetScriptLexicalStructureListCount(count);
|
||||
}
|
||||
|
||||
// TODO: figure out what to do with the unused arguments.
|
||||
public getScriptLexicalStructureListContains(
|
||||
name: string,
|
||||
kind: string,
|
||||
@@ -376,13 +390,7 @@ module FourSlashInterface {
|
||||
parentName?: string,
|
||||
isAdditionalSpan?: boolean,
|
||||
markerPosition?: number) {
|
||||
FourSlash.currentTestState.verifGetScriptLexicalStructureListContains(
|
||||
name,
|
||||
kind,
|
||||
fileName,
|
||||
parentName,
|
||||
isAdditionalSpan,
|
||||
markerPosition);
|
||||
FourSlash.currentTestState.verifyGetScriptLexicalStructureListContains(name, kind);
|
||||
}
|
||||
|
||||
public navigationItemsListCount(count: number, searchValue: string, matchKind?: string) {
|
||||
@@ -698,12 +706,7 @@ module fs {
|
||||
export var format = new FourSlashInterface.format();
|
||||
export var cancellation = new FourSlashInterface.cancellation();
|
||||
}
|
||||
module ts {
|
||||
export interface SymbolDisplayPart {
|
||||
text: string;
|
||||
kind: string;
|
||||
}
|
||||
}
|
||||
|
||||
function verifyOperationIsCancelled(f) {
|
||||
FourSlash.verifyOperationIsCancelled(f);
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ goTo.marker('15');
|
||||
verify.quickInfoIs('var r4a: Collection<number, any>');
|
||||
|
||||
goTo.marker('17');
|
||||
verify.quickInfoIs('var r5a: Collection<T, V>'); // This is actually due to an error because toFixed does not return a Date
|
||||
verify.quickInfoIs('var r5a: Collection<number, Date>');
|
||||
|
||||
goTo.marker('18');
|
||||
verify.quickInfoIs('var r5b: Collection<number, Date>');
|
||||
@@ -122,10 +122,10 @@ goTo.marker('20');
|
||||
verify.quickInfoIs('var r6b: Collection<Collection<number, number>, Date>');
|
||||
|
||||
goTo.marker('21');
|
||||
verify.quickInfoIs('var r7a: Collection<T, V>'); // This call is an error because y.foo() does not return a string
|
||||
verify.quickInfoIs('var r7a: Collection<number, string>');
|
||||
|
||||
goTo.marker('22');
|
||||
verify.quickInfoIs('var r7b: Collection<T, V>'); // This call is an error because y.foo() does not return a string
|
||||
verify.quickInfoIs('var r7b: Collection<number, string>');
|
||||
|
||||
goTo.marker('23');
|
||||
verify.quickInfoIs('var r8a: Collection<number, string>');
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
////var /*4*/r4 = _./*41*/all([<any>true], _.identity);
|
||||
|
||||
goTo.marker('1');
|
||||
verify.quickInfoIs('var r: string | number | boolean');
|
||||
verify.quickInfoIs('var r: boolean | number | string');
|
||||
goTo.marker('11');
|
||||
verify.quickInfoIs('(method) Underscore.Static.all<string | number | boolean>(list: (string | number | boolean)[], iterator?: Underscore.Iterator<string | number | boolean, boolean>, context?: any): string | number | boolean');
|
||||
verify.quickInfoIs('(method) Underscore.Static.all<boolean | number | string>(list: (boolean | number | string)[], iterator?: Underscore.Iterator<boolean | number | string, boolean>, context?: any): boolean | number | string');
|
||||
|
||||
goTo.marker('2');
|
||||
verify.quickInfoIs('var r2: boolean');
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
////var /*4*/r4 = _./*41*/all([<any>true], _.identity);
|
||||
|
||||
goTo.marker('1');
|
||||
verify.quickInfoIs('var r: string | number | boolean');
|
||||
verify.quickInfoIs('var r: boolean | number | string');
|
||||
goTo.marker('11');
|
||||
verify.quickInfoIs('(method) Underscore.Static.all<string | number | boolean>(list: (string | number | boolean)[], iterator?: Underscore.Iterator<string | number | boolean, boolean>, context?: any): string | number | boolean');
|
||||
verify.quickInfoIs('(method) Underscore.Static.all<boolean | number | string>(list: (boolean | number | string)[], iterator?: Underscore.Iterator<boolean | number | string, boolean>, context?: any): boolean | number | string');
|
||||
|
||||
goTo.marker('2');
|
||||
verify.quickInfoIs('var r2: boolean');
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
goTo.marker("propertyReference");
|
||||
verify.definitionCountIs(2);
|
||||
goTo.definition(0);
|
||||
verify.caretAtMarker("propertyDefinition1");
|
||||
verify.caretAtMarker("propertyDefinition2");
|
||||
|
||||
goTo.marker("propertyReference");
|
||||
goTo.definition(1);
|
||||
verify.caretAtMarker("propertyDefinition2");
|
||||
verify.caretAtMarker("propertyDefinition1");
|
||||
|
||||
@@ -21,7 +21,7 @@ verify.quickInfoIs('var x: One | Two');
|
||||
|
||||
|
||||
goTo.marker("2");
|
||||
verify.quickInfoIs('(property) commonProperty: string | number');
|
||||
verify.quickInfoIs('(property) commonProperty: number | string');
|
||||
|
||||
goTo.marker("3");
|
||||
verify.quickInfoIs('(method) commonFunction(): number');
|
||||
|
||||
@@ -21,7 +21,7 @@ verify.quickInfoIs('var x: One | Two');
|
||||
|
||||
|
||||
goTo.marker("2");
|
||||
verify.quickInfoIs('(property) commonProperty: string | number');
|
||||
verify.quickInfoIs('(property) commonProperty: number | string');
|
||||
|
||||
goTo.marker("3");
|
||||
verify.quickInfoIs('(method) commonFunction(): number');
|
||||
|
||||
@@ -0,0 +1,447 @@
|
||||
/// <reference path="..\..\..\src\harness\harness.ts" />
|
||||
|
||||
namespace ts.server {
|
||||
let lastWrittenToHost: string;
|
||||
const mockHost: ServerHost = {
|
||||
args: [],
|
||||
newLine: "\n",
|
||||
useCaseSensitiveFileNames: true,
|
||||
write(s): void { lastWrittenToHost = s; },
|
||||
readFile(): string { return void 0; },
|
||||
writeFile(): void {},
|
||||
resolvePath(): string { return void 0; },
|
||||
fileExists: () => false,
|
||||
directoryExists: () => false,
|
||||
createDirectory(): void {},
|
||||
getExecutingFilePath(): string { return void 0; },
|
||||
getCurrentDirectory(): string { return void 0; },
|
||||
readDirectory(): string[] { return []; },
|
||||
exit(): void {}
|
||||
};
|
||||
const mockLogger: Logger = {
|
||||
close(): void {},
|
||||
isVerbose(): boolean { return false; },
|
||||
loggingEnabled(): boolean { return false; },
|
||||
perftrc(s: string): void {},
|
||||
info(s: string): void {},
|
||||
startGroup(): void {},
|
||||
endGroup(): void {},
|
||||
msg(s: string, type?: string): void {},
|
||||
};
|
||||
|
||||
describe("the Session class", () => {
|
||||
let session: Session;
|
||||
let lastSent: protocol.Message;
|
||||
|
||||
beforeEach(() => {
|
||||
session = new Session(mockHost, Buffer.byteLength, process.hrtime, mockLogger);
|
||||
session.send = (msg: protocol.Message) => {
|
||||
lastSent = msg;
|
||||
};
|
||||
});
|
||||
|
||||
describe("executeCommand", () => {
|
||||
it("should throw when commands are executed with invalid arguments", () => {
|
||||
const req: protocol.FileRequest = {
|
||||
command: CommandNames.Open,
|
||||
seq: 0,
|
||||
type: "command",
|
||||
arguments: {
|
||||
file: undefined
|
||||
}
|
||||
};
|
||||
|
||||
expect(() => session.executeCommand(req)).to.throw();
|
||||
});
|
||||
it("should output an error response when a command does not exist", () => {
|
||||
const req: protocol.Request = {
|
||||
command: "foobar",
|
||||
seq: 0,
|
||||
type: "command"
|
||||
};
|
||||
|
||||
session.executeCommand(req);
|
||||
|
||||
expect(lastSent).to.deep.equal(<protocol.Response>{
|
||||
command: CommandNames.Unknown,
|
||||
type: "response",
|
||||
seq: 0,
|
||||
message: "Unrecognized JSON command: foobar",
|
||||
request_seq: 0,
|
||||
success: false
|
||||
});
|
||||
});
|
||||
it("should return a tuple containing the response and if a response is required on success", () => {
|
||||
const req: protocol.ConfigureRequest = {
|
||||
command: CommandNames.Configure,
|
||||
seq: 0,
|
||||
type: "command",
|
||||
arguments: {
|
||||
hostInfo: "unit test",
|
||||
formatOptions: {
|
||||
newLineCharacter: "`n"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
expect(session.executeCommand(req)).to.deep.equal({
|
||||
responseRequired: false
|
||||
});
|
||||
expect(lastSent).to.deep.equal({
|
||||
command: CommandNames.Configure,
|
||||
type: "response",
|
||||
success: true,
|
||||
request_seq: 0,
|
||||
seq: 0,
|
||||
body: undefined
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("onMessage", () => {
|
||||
it("should not throw when commands are executed with invalid arguments", () => {
|
||||
let i = 0;
|
||||
for (name in CommandNames) {
|
||||
if (!Object.prototype.hasOwnProperty.call(CommandNames, name)) {
|
||||
continue;
|
||||
}
|
||||
const req: protocol.Request = {
|
||||
command: name,
|
||||
seq: i++,
|
||||
type: "command"
|
||||
};
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = {};
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = null;
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = "";
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = 0;
|
||||
session.onMessage(JSON.stringify(req));
|
||||
req.seq = i++;
|
||||
req.arguments = [];
|
||||
session.onMessage(JSON.stringify(req));
|
||||
}
|
||||
session.onMessage("GARBAGE NON_JSON DATA");
|
||||
});
|
||||
it("should output the response for a correctly handled message", () => {
|
||||
const req: protocol.ConfigureRequest = {
|
||||
command: CommandNames.Configure,
|
||||
seq: 0,
|
||||
type: "command",
|
||||
arguments: {
|
||||
hostInfo: "unit test",
|
||||
formatOptions: {
|
||||
newLineCharacter: "`n"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
session.onMessage(JSON.stringify(req));
|
||||
|
||||
expect(lastSent).to.deep.equal(<protocol.ConfigureResponse>{
|
||||
command: CommandNames.Configure,
|
||||
type: "response",
|
||||
success: true,
|
||||
request_seq: 0,
|
||||
seq: 0,
|
||||
body: undefined
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("send", () => {
|
||||
it("is an overrideable handle which sends protocol messages over the wire", () => {
|
||||
const msg = {seq: 0, type: "none"};
|
||||
const strmsg = JSON.stringify(msg);
|
||||
const len = 1 + Buffer.byteLength(strmsg, "utf8");
|
||||
const resultMsg = `Content-Length: ${len}\r\n\r\n${strmsg}\n`;
|
||||
|
||||
session.send = Session.prototype.send;
|
||||
assert(session.send);
|
||||
expect(session.send(msg)).to.not.exist;
|
||||
expect(lastWrittenToHost).to.equal(resultMsg);
|
||||
});
|
||||
});
|
||||
|
||||
describe("addProtocolHandler", () => {
|
||||
it("can add protocol handlers", () => {
|
||||
const respBody = {
|
||||
item: false
|
||||
};
|
||||
const command = "newhandle";
|
||||
const result = {
|
||||
response: respBody,
|
||||
responseRequired: true
|
||||
};
|
||||
|
||||
session.addProtocolHandler(command, (req) => result);
|
||||
|
||||
expect(session.executeCommand({
|
||||
command,
|
||||
seq: 0,
|
||||
type: "command"
|
||||
})).to.deep.equal(result);
|
||||
});
|
||||
it("throws when a duplicate handler is passed", () => {
|
||||
const respBody = {
|
||||
item: false
|
||||
};
|
||||
const resp = {
|
||||
response: respBody,
|
||||
responseRequired: true
|
||||
};
|
||||
const command = "newhandle";
|
||||
|
||||
session.addProtocolHandler(command, (req) => resp);
|
||||
|
||||
expect(() => session.addProtocolHandler(command, (req) => resp))
|
||||
.to.throw(`Protocol handler already exists for command "${command}"`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("event", () => {
|
||||
it("can format event responses and send them", () => {
|
||||
const evt = "notify-test";
|
||||
const info = {
|
||||
test: true
|
||||
};
|
||||
|
||||
session.event(info, evt);
|
||||
|
||||
expect(lastSent).to.deep.equal({
|
||||
type: "event",
|
||||
seq: 0,
|
||||
event: evt,
|
||||
body: info
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("output", () => {
|
||||
it("can format command responses and send them", () => {
|
||||
const body = {
|
||||
block: {
|
||||
key: "value"
|
||||
}
|
||||
};
|
||||
const command = "test";
|
||||
|
||||
session.output(body, command);
|
||||
|
||||
expect(lastSent).to.deep.equal({
|
||||
seq: 0,
|
||||
request_seq: 0,
|
||||
type: "response",
|
||||
command,
|
||||
body: body,
|
||||
success: true
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("how Session is extendable via subclassing", () => {
|
||||
class TestSession extends Session {
|
||||
lastSent: protocol.Message;
|
||||
customHandler = "testhandler";
|
||||
constructor() {
|
||||
super(mockHost, Buffer.byteLength, process.hrtime, mockLogger);
|
||||
this.addProtocolHandler(this.customHandler, () => {
|
||||
return {response: undefined, responseRequired: true};
|
||||
});
|
||||
}
|
||||
send(msg: protocol.Message) {
|
||||
this.lastSent = msg;
|
||||
}
|
||||
};
|
||||
|
||||
it("can override methods such as send", () => {
|
||||
const session = new TestSession();
|
||||
const body = {
|
||||
block: {
|
||||
key: "value"
|
||||
}
|
||||
};
|
||||
const command = "test";
|
||||
|
||||
session.output(body, command);
|
||||
|
||||
expect(session.lastSent).to.deep.equal({
|
||||
seq: 0,
|
||||
request_seq: 0,
|
||||
type: "response",
|
||||
command,
|
||||
body: body,
|
||||
success: true
|
||||
});
|
||||
});
|
||||
it("can add and respond to new protocol handlers", () => {
|
||||
const session = new TestSession();
|
||||
|
||||
expect(session.executeCommand({
|
||||
seq: 0,
|
||||
type: "command",
|
||||
command: session.customHandler
|
||||
})).to.deep.equal({
|
||||
response: undefined,
|
||||
responseRequired: true
|
||||
});
|
||||
});
|
||||
it("has access to the project service", () => {
|
||||
class ServiceSession extends TestSession {
|
||||
constructor() {
|
||||
super();
|
||||
assert(this.projectService);
|
||||
expect(this.projectService).to.be.instanceOf(ProjectService);
|
||||
}
|
||||
};
|
||||
new ServiceSession();
|
||||
});
|
||||
});
|
||||
|
||||
describe("an example of using the Session API to create an in-process server", () => {
|
||||
class InProcSession extends Session {
|
||||
private queue: protocol.Request[] = [];
|
||||
constructor(private client: InProcClient) {
|
||||
super(mockHost, Buffer.byteLength, process.hrtime, mockLogger);
|
||||
this.addProtocolHandler("echo", (req: protocol.Request) => ({
|
||||
response: req.arguments,
|
||||
responseRequired: true
|
||||
}));
|
||||
}
|
||||
|
||||
send(msg: protocol.Message) {
|
||||
this.client.handle(msg);
|
||||
}
|
||||
|
||||
enqueue(msg: protocol.Request) {
|
||||
this.queue.unshift(msg);
|
||||
}
|
||||
|
||||
handleRequest(msg: protocol.Request) {
|
||||
let response: protocol.Response;
|
||||
try {
|
||||
({response} = this.executeCommand(msg));
|
||||
}
|
||||
catch (e) {
|
||||
this.output(undefined, msg.command, msg.seq, e.toString());
|
||||
return;
|
||||
}
|
||||
if (response) {
|
||||
this.output(response, msg.command, msg.seq);
|
||||
}
|
||||
}
|
||||
|
||||
consumeQueue() {
|
||||
while (this.queue.length > 0) {
|
||||
const elem = this.queue.pop();
|
||||
this.handleRequest(elem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InProcClient {
|
||||
private server: InProcSession;
|
||||
private seq = 0;
|
||||
private callbacks: ts.Map<(resp: protocol.Response) => void> = {};
|
||||
private eventHandlers: ts.Map<(args: any) => void> = {};
|
||||
|
||||
handle(msg: protocol.Message): void {
|
||||
if (msg.type === "response") {
|
||||
const response = <protocol.Response>msg;
|
||||
if (this.callbacks[response.request_seq]) {
|
||||
this.callbacks[response.request_seq](response);
|
||||
delete this.callbacks[response.request_seq];
|
||||
}
|
||||
}
|
||||
else if (msg.type === "event") {
|
||||
const event = <protocol.Event>msg;
|
||||
this.emit(event.event, event.body);
|
||||
}
|
||||
}
|
||||
|
||||
emit(name: string, args: any): void {
|
||||
if (this.eventHandlers[name]) {
|
||||
this.eventHandlers[name](args);
|
||||
}
|
||||
}
|
||||
|
||||
on(name: string, handler: (args: any) => void): void {
|
||||
this.eventHandlers[name] = handler;
|
||||
}
|
||||
|
||||
connect(session: InProcSession): void {
|
||||
this.server = session;
|
||||
}
|
||||
|
||||
execute(command: string, args: any, callback: (resp: protocol.Response) => void): void {
|
||||
if (!this.server) {
|
||||
return;
|
||||
}
|
||||
this.seq++;
|
||||
this.server.enqueue({
|
||||
seq: this.seq,
|
||||
type: "command",
|
||||
command,
|
||||
arguments: args
|
||||
});
|
||||
this.callbacks[this.seq] = callback;
|
||||
}
|
||||
};
|
||||
|
||||
it("can be constructed and respond to commands", (done) => {
|
||||
const cli = new InProcClient();
|
||||
const session = new InProcSession(cli);
|
||||
const toEcho = {
|
||||
data: true
|
||||
};
|
||||
const toEvent = {
|
||||
data: false
|
||||
};
|
||||
let responses = 0;
|
||||
|
||||
// Connect the client
|
||||
cli.connect(session);
|
||||
|
||||
// Add an event handler
|
||||
cli.on("testevent", (eventinfo) => {
|
||||
expect(eventinfo).to.equal(toEvent);
|
||||
responses++;
|
||||
expect(responses).to.equal(1);
|
||||
});
|
||||
|
||||
// Trigger said event from the server
|
||||
session.event(toEvent, "testevent");
|
||||
|
||||
// Queue an echo command
|
||||
cli.execute("echo", toEcho, (resp) => {
|
||||
assert(resp.success, resp.message);
|
||||
responses++;
|
||||
expect(responses).to.equal(2);
|
||||
expect(resp.body).to.deep.equal(toEcho);
|
||||
});
|
||||
|
||||
// Queue a configure command
|
||||
cli.execute("configure", {
|
||||
hostInfo: "unit test",
|
||||
formatOptions: {
|
||||
newLineCharacter: "`n"
|
||||
}
|
||||
}, (resp) => {
|
||||
assert(resp.success, resp.message);
|
||||
responses++;
|
||||
expect(responses).to.equal(3);
|
||||
done();
|
||||
});
|
||||
|
||||
// Consume the queue and trigger the callbacks
|
||||
session.consumeQueue();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -2,58 +2,120 @@
|
||||
|
||||
module ts {
|
||||
describe("Transpile", () => {
|
||||
|
||||
function runTest(input: string, compilerOptions: ts.CompilerOptions = {}, fileName?: string, moduleName?: string, expectedOutput?: string, expectedDiagnosticCodes: number[] = []): void {
|
||||
let diagnostics: Diagnostic[] = [];
|
||||
let result = transpile(input, compilerOptions, fileName || "file.ts", diagnostics, moduleName);
|
||||
|
||||
|
||||
interface TranspileTestSettings {
|
||||
options?: TranspileOptions;
|
||||
expectedOutput?: string;
|
||||
expectedDiagnosticCodes?: number[];
|
||||
}
|
||||
|
||||
function checkDiagnostics(diagnostics: Diagnostic[], expectedDiagnosticCodes?: number[]) {
|
||||
if(!expectedDiagnosticCodes) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (let i = 0; i < expectedDiagnosticCodes.length; i++) {
|
||||
assert.equal(expectedDiagnosticCodes[i], diagnostics[i] && diagnostics[i].code, `Could not find expeced diagnostic.`);
|
||||
}
|
||||
assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected");
|
||||
|
||||
if (expectedOutput !== undefined) {
|
||||
assert.equal(result, expectedOutput);
|
||||
}
|
||||
assert.equal(diagnostics.length, expectedDiagnosticCodes.length, "Resuting diagnostics count does not match expected");
|
||||
}
|
||||
|
||||
function test(input: string, testSettings: TranspileTestSettings): void {
|
||||
let diagnostics: Diagnostic[] = [];
|
||||
|
||||
let transpileOptions: TranspileOptions = testSettings.options || {};
|
||||
let transpileResult = transpile(input, transpileOptions.compilerOptions, transpileOptions.fileName, diagnostics, transpileOptions.moduleName);
|
||||
|
||||
transpileOptions.reportDiagnostics = true;
|
||||
let transpileModuleResult = transpileModule(input, transpileOptions);
|
||||
|
||||
checkDiagnostics(diagnostics, testSettings.expectedDiagnosticCodes);
|
||||
checkDiagnostics(transpileModuleResult.diagnostics, testSettings.expectedDiagnosticCodes);
|
||||
|
||||
if (testSettings.expectedOutput !== undefined) {
|
||||
assert.equal(transpileResult, testSettings.expectedOutput);
|
||||
assert.equal(transpileModuleResult.outputText, testSettings.expectedOutput);
|
||||
}
|
||||
|
||||
// check source maps
|
||||
if (!transpileOptions.compilerOptions) {
|
||||
transpileOptions.compilerOptions = {};
|
||||
}
|
||||
|
||||
if (!transpileOptions.fileName) {
|
||||
transpileOptions.fileName = "file.ts";
|
||||
}
|
||||
|
||||
transpileOptions.compilerOptions.sourceMap = true;
|
||||
let transpileModuleResultWithSourceMap = transpileModule(input, transpileOptions);
|
||||
assert.isTrue(transpileModuleResultWithSourceMap.sourceMapText !== undefined);
|
||||
|
||||
let expectedSourceMapFileName = removeFileExtension(transpileOptions.fileName) + ".js.map";
|
||||
let expectedSourceMappingUrlLine = `//# sourceMappingURL=${expectedSourceMapFileName}`;
|
||||
|
||||
if (testSettings.expectedOutput !== undefined) {
|
||||
assert.equal(transpileModuleResultWithSourceMap.outputText, testSettings.expectedOutput + expectedSourceMappingUrlLine);
|
||||
}
|
||||
else {
|
||||
// expected output is not set, just verify that output text has sourceMappingURL as a last line
|
||||
let output = transpileModuleResultWithSourceMap.outputText;
|
||||
assert.isTrue(output.length >= expectedSourceMappingUrlLine.length);
|
||||
if (output.length === expectedSourceMappingUrlLine.length) {
|
||||
assert.equal(output, expectedSourceMappingUrlLine);
|
||||
}
|
||||
else {
|
||||
let suffix = getNewLineCharacter(transpileOptions.compilerOptions) + expectedSourceMappingUrlLine
|
||||
assert.isTrue(output.indexOf(suffix, output.length - suffix.length) !== -1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
it("Generates correct compilerOptions diagnostics", () => {
|
||||
// Expecting 5047: "Option 'isolatedModules' can only be used when either option'--module' is provided or option 'target' is 'ES6' or higher."
|
||||
runTest(`var x = 0;`, {}, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ [5047]);
|
||||
test(`var x = 0;`, { expectedDiagnosticCodes: [5047] });
|
||||
});
|
||||
|
||||
it("Generates no diagnostics with valid inputs", () => {
|
||||
// No errors
|
||||
runTest(`var x = 0;`, { module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []);
|
||||
test(`var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS } } });
|
||||
});
|
||||
|
||||
it("Generates no diagnostics for missing file references", () => {
|
||||
runTest(`/// <reference path="file2.ts" />
|
||||
var x = 0;`,
|
||||
{ module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []);
|
||||
test(`/// <reference path="file2.ts" />
|
||||
var x = 0;`,
|
||||
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
|
||||
});
|
||||
|
||||
it("Generates no diagnostics for missing module imports", () => {
|
||||
runTest(`import {a} from "module2";`,
|
||||
{ module: ModuleKind.CommonJS }, /*fileName*/ undefined,/*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []);
|
||||
test(`import {a} from "module2";`,
|
||||
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
|
||||
});
|
||||
|
||||
it("Generates expected syntactic diagnostics", () => {
|
||||
runTest(`a b`,
|
||||
{ module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ [1005]); /// 1005: ';' Expected
|
||||
test(`a b`,
|
||||
{ options: { compilerOptions: { module: ModuleKind.CommonJS } }, expectedDiagnosticCodes: [1005] }); /// 1005: ';' Expected
|
||||
});
|
||||
|
||||
it("Does not generate semantic diagnostics", () => {
|
||||
runTest(`var x: string = 0;`,
|
||||
{ module: ModuleKind.CommonJS }, /*fileName*/ undefined, /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/ []);
|
||||
test(`var x: string = 0;`,
|
||||
{ options: { compilerOptions: { module: ModuleKind.CommonJS } } });
|
||||
});
|
||||
|
||||
it("Generates module output", () => {
|
||||
runTest(`var x = 0;`, { module: ModuleKind.AMD }, /*fileName*/ undefined, /*moduleName*/undefined, `define(["require", "exports"], function (require, exports) {\r\n var x = 0;\r\n});\r\n`);
|
||||
test(`var x = 0;`,
|
||||
{
|
||||
options: { compilerOptions: { module: ModuleKind.AMD } },
|
||||
expectedOutput: `define(["require", "exports"], function (require, exports) {\r\n var x = 0;\r\n});\r\n`
|
||||
});
|
||||
});
|
||||
|
||||
it("Uses correct newLine character", () => {
|
||||
runTest(`var x = 0;`, { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed }, /*fileName*/ undefined, /*moduleName*/undefined, `var x = 0;\n`, /*expectedDiagnosticCodes*/ []);
|
||||
test(`var x = 0;`,
|
||||
{
|
||||
options: { compilerOptions: { module: ModuleKind.CommonJS, newLine: NewLineKind.LineFeed } },
|
||||
expectedOutput: `var x = 0;\n`
|
||||
});
|
||||
});
|
||||
|
||||
it("Sets module name", () => {
|
||||
@@ -66,12 +128,15 @@ var x = 0;`,
|
||||
` }\n` +
|
||||
` }\n` +
|
||||
`});\n`;
|
||||
runTest("var x = 1;", { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, /*fileName*/ undefined, "NamedModule", output)
|
||||
test("var x = 1;",
|
||||
{
|
||||
options: { compilerOptions: { module: ModuleKind.System, newLine: NewLineKind.LineFeed }, moduleName: "NamedModule" },
|
||||
expectedOutput: output
|
||||
})
|
||||
});
|
||||
|
||||
it("No extra errors for file without extension", () => {
|
||||
runTest(`var x = 0;`, { module: ModuleKind.CommonJS }, "file", /*moduleName*/undefined, /*expectedOutput*/ undefined, /*expectedDiagnosticCodes*/[]);
|
||||
test(`var x = 0;`, { options: { compilerOptions: { module: ModuleKind.CommonJS }, fileName: "file" } });
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user