Skip to content

Commit

Permalink
Improve code coverage (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrix committed May 8, 2018
1 parent 69f8a1f commit 34d39f7
Show file tree
Hide file tree
Showing 40 changed files with 932 additions and 90 deletions.
10 changes: 3 additions & 7 deletions src/Error/BaseError.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
export abstract class BaseError implements Error {
private callStack: any;
public readonly stack: string;

public constructor() {
this.callStack = new Error().stack;
}

public get stack(): string {
return this.callStack;
protected constructor() {
Error.captureStackTrace(this, this.constructor);
}

public abstract get name(): string;
Expand Down
2 changes: 1 addition & 1 deletion src/Error/UnknownNodeError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class UnknownNodeError extends BaseError {
return "UnknownNodeError";
}
public get message(): string {
return `Unknown node "${this.node.getFullText()}`;
return `Unknown node "${this.node.getFullText()}"`;
}

public getNode(): ts.Node {
Expand Down
26 changes: 13 additions & 13 deletions src/NodeParser/EnumNodeParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { Context } from "../NodeParser";
import { SubNodeParser } from "../SubNodeParser";
import { BaseType } from "../Type/BaseType";
import { EnumType, EnumValue } from "../Type/EnumType";
import { UnknownNodeError } from "../Error/UnknownNodeError";
import { assertDefined } from "../Utils/assert";

export class EnumNodeParser implements SubNodeParser {
public constructor(
Expand All @@ -20,24 +22,18 @@ export class EnumNodeParser implements SubNodeParser {

return new EnumType(
`enum-${node.getFullStart()}`,
members.map((member, index) => this.getMemberValue(member, index)),
members.map((member) => this.getMemberValue(member)),
);
}

private getMemberValue(member: ts.EnumMember, index: number): EnumValue {
private getMemberValue(member: ts.EnumMember): EnumValue {
const constantValue = this.typeChecker.getConstantValue(member);
if (constantValue !== undefined) {
return constantValue;
}

const initializer = member.initializer;
if (!initializer) {
return index;
} else if (initializer.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
return member.name.getText();
} else {
return this.parseInitializer(initializer);
}
const initializer = assertDefined(member.initializer);
return this.parseInitializer(initializer);
}
private parseInitializer(initializer: ts.Node): EnumValue {
if (initializer.kind === ts.SyntaxKind.TrueKeyword) {
Expand All @@ -46,16 +42,20 @@ export class EnumNodeParser implements SubNodeParser {
return false;
} else if (initializer.kind === ts.SyntaxKind.NullKeyword) {
return null;
} else if (initializer.kind === ts.SyntaxKind.NumericLiteral) {
return parseFloat((initializer as ts.NumericLiteral).text);
} else if (initializer.kind === ts.SyntaxKind.StringLiteral) {
return (initializer as ts.LiteralLikeNode).text;
return (initializer as ts.StringLiteral).text;
} else if (initializer.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral) {
return (initializer as ts.NoSubstitutionTemplateLiteral).text;
} else if (initializer.kind === ts.SyntaxKind.ParenthesizedExpression) {
return this.parseInitializer((initializer as ts.ParenthesizedExpression).expression);
} else if (initializer.kind === ts.SyntaxKind.AsExpression) {
return this.parseInitializer((initializer as ts.AsExpression).expression);
} else if (initializer.kind === ts.SyntaxKind.TypeAssertionExpression) {
return this.parseInitializer((initializer as ts.TypeAssertion).expression);
} else {
return initializer.getText();
}

throw new UnknownNodeError(initializer);
}
}
4 changes: 1 addition & 3 deletions src/Schema/Definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ export interface Definition {
format?: string;
items?: Definition | Definition[];
minItems?: number;
additionalItems?: {
anyOf: Definition[],
};
additionalItems?: false | Definition;
enum?: (RawType | Definition)[];
default?: RawType | Object;
additionalProperties?: false | Definition;
Expand Down
6 changes: 5 additions & 1 deletion src/TypeFormatter/LiteralUnionTypeFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import { uniqueArray } from "../Utils/uniqueArray";

export class LiteralUnionTypeFormatter implements SubTypeFormatter {
public supportsType(type: UnionType): boolean {
return type instanceof UnionType && this.isLiteralUnion(type);
return (
type instanceof UnionType &&
type.getTypes().length > 0 &&
this.isLiteralUnion(type)
);
}
public getDefinition(type: UnionType): Definition {
const values = uniqueArray(type.getTypes().map((item: LiteralType | NullType) => this.getLiteralValue(item)));
Expand Down
6 changes: 5 additions & 1 deletion src/TypeFormatter/PrimitiveUnionTypeFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import { NullType } from "../Type/NullType";

export class PrimitiveUnionTypeFormatter implements SubTypeFormatter {
public supportsType(type: UnionType): boolean {
return type instanceof UnionType && this.isPrimitiveUnion(type);
return (
type instanceof UnionType &&
type.getTypes().length > 0 &&
this.isPrimitiveUnion(type)
);
}
public getDefinition(type: UnionType): Definition {
return {
Expand Down
24 changes: 16 additions & 8 deletions src/TypeFormatter/TupleTypeFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { SubTypeFormatter } from "../SubTypeFormatter";
import { TupleType } from "../Type/TupleType";
import { BaseType } from "../Type/BaseType";
import { Definition } from "../Schema/Definition";
import { UnionType } from "../Type/UnionType";

export class TupleTypeFormatter implements SubTypeFormatter {
public constructor(
Expand All @@ -14,14 +15,21 @@ export class TupleTypeFormatter implements SubTypeFormatter {
return type instanceof TupleType;
}
public getDefinition(type: TupleType): Definition {
const tupleDefinitions = type.getTypes().map((item) => this.childTypeFormatter.getDefinition(item));

return {
type: "array",
items: tupleDefinitions,
minItems: tupleDefinitions.length,
...(tupleDefinitions.length > 1 ? {additionalItems: {anyOf: tupleDefinitions}} : {}),
};
const itemTypes = type.getTypes();
if (itemTypes.length > 1) {
return {
type: "array",
items: itemTypes.map((item) => this.childTypeFormatter.getDefinition(item)),
minItems: itemTypes.length,
additionalItems: this.childTypeFormatter.getDefinition(new UnionType(itemTypes)),
};
} else {
return {
type: "array",
items: this.childTypeFormatter.getDefinition(itemTypes[0]),
minItems: 1,
};
}
}
public getChildren(type: TupleType): BaseType[] {
return type.getTypes().reduce((result: BaseType[], item) => [
Expand Down
10 changes: 7 additions & 3 deletions src/TypeFormatter/UnionTypeFormatter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ export class UnionTypeFormatter implements SubTypeFormatter {
};
}

return definitions.length > 1 ? {
anyOf: definitions,
} : definitions[0];
if (definitions.length === 0) {
return {not: {}};
} else if (definitions.length === 1) {
return definitions[0];
} else {
return {anyOf: definitions};
}
}
public getChildren(type: UnionType): BaseType[] {
return type.getTypes().reduce((result: BaseType[], item) => [
Expand Down
1 change: 1 addition & 0 deletions test/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe("config", () => {
assertSchema("jsdoc-complex-basic", {type: "MyObject", expose: "export", topRef: true, jsDoc: "basic"});
assertSchema("jsdoc-complex-extended", {type: "MyObject", expose: "export", topRef: true, jsDoc: "extended"});
assertSchema("jsdoc-description-only", {type: "MyObject", expose: "export", topRef: true, jsDoc: "extended"});
assertSchema("jsdoc-empty-or-invalid", {type: "MyObject", expose: "export", topRef: true, jsDoc: "basic"});

assertSchema("jsdoc-inheritance", {type: "MyObject", expose: "export", topRef: true, jsDoc: "extended"});
});
21 changes: 21 additions & 0 deletions test/config/jsdoc-empty-or-invalid/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @title Some title here
* @description Some description here
*/
export interface MyObject {
/**
* @minLength 123
* @default {"abc": 123}
*/
validJsDoc: any;
/**
* @minLength ABC
* @default {"abc": ABC}
*/
invalidJsDoc: any;
/**
* @minLength
* @default
*/
emptyJsDoc: any;
}
25 changes: 25 additions & 0 deletions test/config/jsdoc-empty-or-invalid/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$ref": "#/definitions/MyObject",
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"MyObject": {
"additionalProperties": false,
"description": "Some description here",
"title": "Some title here",
"properties": {
"validJsDoc": {
"default": {"abc": 123},
"minLength": 123
},
"invalidJsDoc": {},
"emptyJsDoc": {}
},
"required": [
"validJsDoc",
"invalidJsDoc",
"emptyJsDoc"
],
"type": "object"
}
}
}
3 changes: 3 additions & 0 deletions test/invalid-data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ describe("invalid-data", () => {
// TODO: template recursive

assertSchema("script-empty", "MyType", `No root type "MyType" found`);
assertSchema("unknown-node-type", "MyType", `Unknown node ""123" + "456""`);
assertSchema("unknown-initializer", "MyType", `Invalid type query " obj"`);

assertSchema("literal-index-type", "MyType", `Unknown node " ["abc", "def"]`);
assertSchema("literal-array-type", "MyType", `Unknown node " ["abc", "def"]`);
assertSchema("literal-object-type", "MyType", `Unknown node " {abc: "def"}`);
Expand Down
2 changes: 2 additions & 0 deletions test/invalid-data/unknown-initializer/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
let obj;
export type MyType = typeof obj;
4 changes: 4 additions & 0 deletions test/invalid-data/unknown-node-type/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum MyType {
ABC = (1 + 2 + 3),
DEF = ("123" + "456") as any,
}
12 changes: 11 additions & 1 deletion test/valid-data.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,16 @@ describe("valid-data", () => {
assertSchema("enums-initialized", "Enum");
assertSchema("enums-compute", "Enum");
assertSchema("enums-mixed", "Enum");
assertSchema("enums-complex", "Enum");
assertSchema("enums-member", "MyObject");

assertSchema("string-literals", "MyObject");
assertSchema("string-literals-inline", "MyObject");
assertSchema("string-literals-null", "MyObject");

assertSchema("literals-number", "MyObject");
assertSchema("literals-boolean", "MyObject");

assertSchema("namespace-deep-1", "RootNamespace.Def");
assertSchema("namespace-deep-2", "RootNamespace.SubNamespace.HelperA");
assertSchema("namespace-deep-3", "RootNamespace.SubNamespace.HelperB");
Expand All @@ -81,7 +85,9 @@ describe("valid-data", () => {
assertSchema("type-aliases-object", "MyAlias");
assertSchema("type-aliases-mixed", "MyObject");
assertSchema("type-aliases-union", "MyUnion");
assertSchema("type-aliases-tuple", "MyTuple");
assertSchema("type-aliases-tuple-1", "MyTuple");
assertSchema("type-aliases-tuple-2", "MyTuple");
assertSchema("type-aliases-tuple-3", "MyTuple");
assertSchema("type-aliases-anonymous", "MyObject");
assertSchema("type-aliases-local-namespace", "MyObject");
assertSchema("type-aliases-recursive-anonymous", "MyAlias");
Expand All @@ -91,6 +97,7 @@ describe("valid-data", () => {
assertSchema("type-primitives", "MyObject");
assertSchema("type-union", "TypeUnion");
assertSchema("type-union-tagged", "Shape");
assertSchema("type-union-primitive", "MyObject");
assertSchema("type-intersection", "MyObject");
assertSchema("type-intersection-additional-props", "MyObject");

Expand All @@ -102,6 +109,8 @@ describe("valid-data", () => {
assertSchema("type-indexed-access-object-2", "MyType");
assertSchema("type-keyof-tuple", "MyType");
assertSchema("type-keyof-object", "MyType");
assertSchema("type-keyof-union", "MyType");
assertSchema("type-keyof-primitive", "MyType");
assertSchema("type-mapped-simple", "MyObject");
assertSchema("type-mapped-index", "MyObject");
assertSchema("type-mapped-literal", "MyObject");
Expand All @@ -115,6 +124,7 @@ describe("valid-data", () => {
assertSchema("generic-anonymous", "MyObject");
assertSchema("generic-recursive", "MyObject");
assertSchema("generic-hell", "MyObject");
assertSchema("generic-names", "MyObject");

assertSchema("nullable-null", "MyObject");

Expand Down
23 changes: 23 additions & 0 deletions test/valid-data/enums-complex/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export enum Enum {
INDEX_1,
INDEX_2,

NUMBER_1 = 10,
NUMBER_2 = 20,

BOOL_1 = true as any,
BOOL_2 = false as any,

STR_1 = "str" as any,
STR_2 = `tpl` as any,

NULL_1 = null,
NULL_2 = null as any,

ANY_1 = <any>true,
ANY_2 = <any>100,
ANY_3 = <any>"any",
ANY_4 = <any>null,

COMPLEX = (<any>"()" as any) as any,
}
28 changes: 28 additions & 0 deletions test/valid-data/enums-complex/schema.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"$ref": "#/definitions/Enum",
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"Enum": {
"type": [
"number",
"boolean",
"string",
"null"
],
"enum": [
0,
1,
10,
20,
true,
false,
"str",
"tpl",
null,
100,
"any",
"()"
]
}
}
}
7 changes: 2 additions & 5 deletions test/valid-data/enums-mixed/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
export enum Enum {
A, // = 0
B = 1,
C = true as any,
D = "str" as any,
E = null,
X = "x",
Y = 100,
}
Loading

0 comments on commit 34d39f7

Please sign in to comment.