Skip to content

Commit

Permalink
feat(generator): generate assertion for Object Type Application
Browse files Browse the repository at this point in the history
  • Loading branch information
victor-homyakov committed Jan 18, 2017
1 parent a639564 commit 890134f
Show file tree
Hide file tree
Showing 3 changed files with 115 additions and 90 deletions.
82 changes: 44 additions & 38 deletions src/AssertGenerator.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,29 +88,32 @@ export default class AssertGenerator {
}, typeNode);
const generator = new Generator(node);
const tagType = typeNode.type.type;
if (tagType === "NameExpression") {
return NameExpression(node, generator);
} else if (tagType === "AllLiteral") {
return AllLiteral(node, generator);
} else if (tagType === "FunctionType") {
return FunctionType(node, generator);
} else if (tagType === "RecordType") {
return RecordType(node, generator);
} else if (tagType === "UnionType") {
return UnionType(node, generator);
} else if (tagType === "TypeApplication") {
return TypeApplication(node, generator);
} else if (tagType === "RestType") {
return RestType(node, generator);
} else if (tagType === "NullableType") {
return NullableType(node, generator);
} else if (tagType === "NonNullableType") {
return NonNullableType(node, generator);
switch (tagType) {
case "NameExpression":
return NameExpression(node, generator);
case "AllLiteral":
return AllLiteral(node, generator);
case "FunctionType":
return FunctionType(node, generator);
case "RecordType":
return RecordType(node, generator);
case "UnionType":
return UnionType(node, generator);
case "TypeApplication":
return TypeApplication(node, generator);
case "RestType":
return RestType(node, generator);
case "NullableType":
return NullableType(node, generator);
case "NonNullableType":
return NonNullableType(node, generator);
default:
return;
}
}

/**
* @param tagNode tagNode is defined by doctorin
* @param tagNode tagNode is defined by doctrine
* @param {CodeGenerator} Generator
* @return {string|undefined} return assertion code string
* Reference https://esdoc.org/tags.html#type-syntax
Expand All @@ -128,24 +131,27 @@ export default class AssertGenerator {
}
const generator = new Generator(tagNode);
const tagType = tagNode.type.type;
if (tagType === "NameExpression") {
return NameExpression(tagNode, generator);
} else if (tagType === "AllLiteral") {
return AllLiteral(tagNode, generator);
} else if (tagType === "FunctionType") {
return FunctionType(tagNode, generator);
} else if (tagType === "RecordType") {
return RecordType(tagNode, generator);
} else if (tagType === "UnionType") {
return UnionType(tagNode, generator);
} else if (tagType === "TypeApplication") {
return TypeApplication(tagNode, generator);
} else if (tagType === "RestType") {
return RestType(tagNode, generator);
} else if (tagType === "NullableType") {
return NullableType(tagNode, generator);
} else if (tagType === "NonNullableType") {
return NonNullableType(tagNode, generator);
switch (tagType) {
case "NameExpression":
return NameExpression(tagNode, generator);
case "AllLiteral":
return AllLiteral(tagNode, generator);
case "FunctionType":
return FunctionType(tagNode, generator);
case "RecordType":
return RecordType(tagNode, generator);
case "UnionType":
return UnionType(tagNode, generator);
case "TypeApplication":
return TypeApplication(tagNode, generator);
case "RestType":
return RestType(tagNode, generator);
case "NullableType":
return NullableType(tagNode, generator);
case "NonNullableType":
return NonNullableType(tagNode, generator);
default:
return;
}
}
};
}
8 changes: 8 additions & 0 deletions src/tag/TypeApplication.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"use strict";
import {Expression} from "./Expression";
// @param {Array.<string>}
// @param {Object<string,number>}
/**
* @return {string|undefined}
*/
Expand All @@ -25,5 +26,12 @@ export function TypeApplication(tag, CodeGenerator) {
} else {
return CodeGenerator.assert(`Array.isArray(${tag.name})`);
}
} else if (expectedType === "Object") {
const applications = tag.type.applications;
if (applications && applications.length === 2) {
const expression = Expression(`${tag.name}`, {name: "object"});
const itemExpression = Expression(`${tag.name}[key]`, applications[1]);
return CodeGenerator.assert(`${expression} && Object.keys(${tag.name}).every(function(key) { return (${itemExpression}); })`);
}
}
}
115 changes: 63 additions & 52 deletions test/create-asserts-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,8 @@ describe("create-assert", function() {
const jsdoc = `/**
* @param x
*/`;
const numberAssertion = createAssertion(jsdoc);
assert(numberAssertion == null);
const assertion = createAssertion(jsdoc);
assert(assertion == null);
});
});
context("when pass @return", function() {
Expand All @@ -103,8 +103,8 @@ describe("create-assert", function() {
const jsdoc = `/**
* @returns {Array}
*/`;
const numberAssertion = createAssertion(jsdoc);
assert(numberAssertion == null);
const assertion = createAssertion(jsdoc);
assert(assertion == null);
});
});
context("when pass jsdoc", function() {
Expand All @@ -126,52 +126,52 @@ describe("create-assert", function() {
const jsdoc = `/**
* @param {number} x
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x === "number"`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x === "number"`);
});
it("should return assert typeof string", function() {
const jsdoc = `/**
* @param {string} x
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x === "string"`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x === "string"`);
});
it("should return assert typeof boolean", function() {
const jsdoc = `/**
* @param {boolean} x
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x === "boolean"`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x === "boolean"`);
});
it("should return assert typeof function", function() {
const jsdoc = `/**
* @param {Function} x
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x === "function"`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x === "function"`);
});
it("should return assert typeof function", function() {
const jsdoc = `/**
* @param {function} x
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x === "function"`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x === "function"`);
});
it("should return assert typeof object", function() {
const jsdoc = `/**
* @param {Object} x
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x === "object"`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x === "object"`);
});
});
context("When pass all type", function() {
it("should return assert AllLiteral ", function() {
const jsdoc = `/**
* @param {*} x - this is ArrayType param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x !== "undefined"`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x !== "undefined"`);
});
});
context("when pass RegExp", function() {
Expand All @@ -194,8 +194,8 @@ describe("create-assert", function() {
const jsdoc = `/**
* @param {CustomType} x - this is ArrayType param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `
const assertion = createAssertion(jsdoc);
astEqual(assertion, `
typeof Symbol === "function" && typeof Symbol.hasInstance === "symbol" && typeof CustomType !== "undefined" && typeof CustomType[Symbol.hasInstance] === "function" ?
CustomType[Symbol.hasInstance](x) :
typeof CustomType === 'undefined' || typeof CustomType !== 'function' || x instanceof CustomType
Expand All @@ -208,26 +208,26 @@ describe("create-assert", function() {
const jsdoc = `/**
* @param {Object.Property} x - this is ArrayType param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, "true");
const assertion = createAssertion(jsdoc);
astEqual(assertion, "true");
});
});
context("when pass Array only", function() {
it("should return Array.isArray(x)", function() {
const jsdoc = `/**
* @param {Array} x - this is ArrayType param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `Array.isArray(x)`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `Array.isArray(x)`);
});
});
context("when pass *[]", function() {
it("should return Array.isArray(x)", function() {
const jsdoc = `/**
* @param {*[]} x
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `Array.isArray(x) && x.every(function (item) {
const assertion = createAssertion(jsdoc);
astEqual(assertion, `Array.isArray(x) && x.every(function (item) {
return typeof Symbol === "function" && typeof Symbol.hasInstance === "symbol" && typeof undefined !== "undefined" && typeof undefined[Symbol.hasInstance] === "function" ?
undefined[Symbol.hasInstance](item) :
typeof undefined === 'undefined' || typeof undefined !== 'function' || item instanceof undefined;
Expand All @@ -239,8 +239,8 @@ describe("create-assert", function() {
const jsdoc = `/**
* @param {number[]} x - this is ArrayType param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `Array.isArray(x) && x.every(function (item) {
const assertion = createAssertion(jsdoc);
astEqual(assertion, `Array.isArray(x) && x.every(function (item) {
return typeof item === 'number';
});`);
});
Expand All @@ -250,8 +250,8 @@ describe("create-assert", function() {
const jsdoc = `/**
* @param {CustomType[]} x - this is ArrayType param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `Array.isArray(x) && x.every(function (item) {
const assertion = createAssertion(jsdoc);
astEqual(assertion, `Array.isArray(x) && x.every(function (item) {
return typeof Symbol === "function" && typeof Symbol.hasInstance === "symbol" && typeof CustomType !== "undefined" && typeof CustomType[Symbol.hasInstance] === "function" ?
CustomType[Symbol.hasInstance](item) :
typeof CustomType === 'undefined' || typeof CustomType !== 'function' || item instanceof CustomType;
Expand All @@ -263,26 +263,26 @@ describe("create-assert", function() {
const jsdoc = `/**
* @param {?number} x - this is nullable param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `(x == null || typeof x === "number")`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `(x == null || typeof x === "number")`);
});
});
context("when pass NonNullableType", function() {
it("should return assert typeof NonNullableType", function() {
const jsdoc = `/**
* @param {!number} x - this is non-nullable param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `(x != null && typeof x === "number")`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `(x != null && typeof x === "number")`);
});
});
context("when pass callback function", function() {
it("should return assert typeof funtion", function() {
const jsdoc = `/**
* @param {function(foo: number, bar: string): boolean} x - this is function param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x === "function"`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x === "function"`);
});

});
Expand All @@ -294,8 +294,8 @@ describe("create-assert", function() {
const jsdoc = `/**
* @param {number|string} x - this is union param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `(typeof x === "number" || typeof x === "string")`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `(typeof x === "number" || typeof x === "string")`);
});
});
context("when pass ...number", function() {
Expand All @@ -305,8 +305,8 @@ describe("create-assert", function() {
* @param {...number} x - this is spread param.
*/`;

const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `Array.isArray(x) && x.every(function (item) {
const assertion = createAssertion(jsdoc);
astEqual(assertion, `Array.isArray(x) && x.every(function (item) {
return typeof item === 'number';
})`);
});
Expand All @@ -316,39 +316,50 @@ describe("create-assert", function() {
const jsdoc = `/**
* @param {{SubscriptionId,Data}} data
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof data !== 'undefined' && typeof data.SubscriptionId !== 'undefined' && typeof data.Data !== 'undefined';`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof data !== 'undefined' && typeof data.SubscriptionId !== 'undefined' && typeof data.Data !== 'undefined';`);
});
it("should assert foo.bar as NullableType ", function() {
const jsdoc = `/**
* @param {{foo: ?number, bar: string}} x - this is object param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x !== 'undefined' && (x.foo == null || typeof x.foo === 'number') && typeof x.bar === 'string';`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x !== 'undefined' && (x.foo == null || typeof x.foo === 'number') && typeof x.bar === 'string';`);
});
it("should return assert foo filed with &&", function() {
it("should return assert foo field with &&", function() {
const jsdoc = `/**
* @param {{foo: number, bar: string}} x - this is object param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x !== 'undefined' && typeof x.foo === 'number' && typeof x.bar === 'string';`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x !== 'undefined' && typeof x.foo === 'number' && typeof x.bar === 'string';`);
});
it("should return assert Custom filed with &&", function() {
it("should return assert Custom field with &&", function() {
const jsdoc = `/**
* @param {{foo: number, bar: RegExp}} x - this is object param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `typeof x !== 'undefined' && typeof x.foo === 'number' && (typeof Symbol === 'function' && typeof Symbol.hasInstance === 'symbol' && typeof RegExp !== 'undefined' && typeof RegExp[Symbol.hasInstance] === 'function' ? RegExp[Symbol.hasInstance](x.bar) : typeof RegExp === 'undefined' || typeof RegExp !== 'function' || x.bar instanceof RegExp);`);
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x !== 'undefined' && typeof x.foo === 'number' && (typeof Symbol === 'function' && typeof Symbol.hasInstance === 'symbol' && typeof RegExp !== 'undefined' && typeof RegExp[Symbol.hasInstance] === 'function' ? RegExp[Symbol.hasInstance](x.bar) : typeof RegExp === 'undefined' || typeof RegExp !== 'function' || x.bar instanceof RegExp);`);
});
});
context("When pass Array.<string>", function() {
it("should return Array.isArray(x) && check every type", function() {
const jsdoc = `/**
* @param {Array.<string>} x - this is Array param.
*/`;
const numberAssertion = createAssertion(jsdoc);
astEqual(numberAssertion, `Array.isArray(x) && x.every(function (item) {
const assertion = createAssertion(jsdoc);
astEqual(assertion, `Array.isArray(x) && x.every(function (item) {
return typeof item === 'string';
});`);
});
});
context("When pass Object<string, number>", function() {
it("should check object itself and every object value", function() {
const jsdoc = `/**
* @param {Object<string, number>} x - this is Object param with string keys and number values.
*/`;
const assertion = createAssertion(jsdoc);
astEqual(assertion, `typeof x === 'object' && Object.keys(x).every(function (key) {
return typeof x[key] === 'number';
});`);
});
});
Expand Down

0 comments on commit 890134f

Please sign in to comment.