Comparing version 1.0.1 to 1.0.3
@@ -129,2 +129,7 @@ import * as Express from "express"; | ||
} | ||
export declare class KwyjiboControllerTreeNode { | ||
controller: KwyjiboController; | ||
childs: KwyjiboControllerTreeNode[]; | ||
constructor(controller: KwyjiboController); | ||
} | ||
export declare type KwyjiboControllerMap = { | ||
@@ -140,2 +145,3 @@ [key: string]: KwyjiboController; | ||
mountpoints: KwyjiboMountpoint[]; | ||
controllersTree: KwyjiboControllerTreeNode[]; | ||
getController(ctr: Function): KwyjiboController; | ||
@@ -142,0 +148,0 @@ getOrInsertController(ctr: Function): KwyjiboController; |
@@ -12,2 +12,3 @@ "use strict"; | ||
const U = require("./utils"); | ||
const T = require("./testing"); | ||
/** | ||
@@ -243,2 +244,9 @@ * Contains context for the current call . | ||
exports.KwyjiboController = KwyjiboController; | ||
class KwyjiboControllerTreeNode { | ||
constructor(controller) { | ||
this.childs = []; | ||
this.controller = controller; | ||
} | ||
} | ||
exports.KwyjiboControllerTreeNode = KwyjiboControllerTreeNode; | ||
class KwyjiboControllersState { | ||
@@ -248,2 +256,3 @@ constructor() { | ||
this.mountpoints = []; | ||
this.controllersTree = []; | ||
} | ||
@@ -268,3 +277,41 @@ getController(ctr) { | ||
exports.globalKCState = new KwyjiboControllersState(); | ||
function mountMethod(controller, instance, methodKey, method) { | ||
function addChildsToTreeNode(node) { | ||
for (let mp of exports.globalKCState.mountpoints) { | ||
if (node.controller.ctr.toString() === mp.dstCtr.toString()) { | ||
let child = new KwyjiboControllerTreeNode(exports.globalKCState.getController(mp.ctr)); | ||
addChildsToTreeNode(child); | ||
node.childs.push(child); | ||
} | ||
} | ||
} | ||
function buildControllersTree() { | ||
for (let ck in exports.globalKCState.controllers) { | ||
let c = exports.globalKCState.controllers[ck]; | ||
if (c.childController === false) { | ||
let node = new KwyjiboControllerTreeNode(c); | ||
addChildsToTreeNode(node); | ||
exports.globalKCState.controllersTree.push(node); | ||
} | ||
} | ||
} | ||
function indexAutogenerator(controller, childs) { | ||
return (req, res) => { | ||
let content = "<html><head></head><body><pre> Autogenerated Index (Only in dev env) <br /><br />"; | ||
for (let child of childs) { | ||
content += `[Controller] <a href=".${child.controller.path}/">${child.controller.path}</a><br />`; | ||
} | ||
content += "<br />"; | ||
if (controller != undefined) { | ||
for (let mk in controller.methods) { | ||
for (let mmp of controller.methods[mk].methodMountpoints) { | ||
content += `[${mmp.httpMethod.toUpperCase()}] <a href=".${mmp.path}/">${mmp.path}</a><br />`; | ||
} | ||
} | ||
} | ||
content += "</pre></body></html>"; | ||
res.send(content); | ||
}; | ||
} | ||
function mountMethod(controller, instance, methodKey) { | ||
let method = controller.methods[methodKey]; | ||
if (method.explicitlyDeclared === false) { | ||
@@ -276,5 +323,5 @@ U.defaultWarn(`Method ${methodKey} was not explicitaly declared with a decorator. Defaulting to GET@/${methodKey}`); | ||
let callback = (req, res, next) => { | ||
let context = new Context(); | ||
let runner = () => __awaiter(this, void 0, void 0, function* () { | ||
let ret; | ||
let context = new Context(); | ||
if (method.expressCompatible) { | ||
@@ -298,29 +345,25 @@ ret = instance[methodKey](req, res, next); | ||
} | ||
context.dispose(); | ||
}); | ||
runner().catch((err) => { next(err); }); | ||
runner().then(() => { context.dispose(); }) | ||
.catch((err) => { context.dispose(); next(err); }); | ||
}; | ||
controller.router[mp.httpMethod](mp.path, ...method.middleware, callback); | ||
controller.router[mp.httpMethod](U.UrlJoin(mp.path, "/"), ...method.middleware, callback); | ||
} | ||
} | ||
function indexAutogenerator(mountpoint, childControllerPaths) { | ||
return (req, res) => { | ||
let content = "<html><head></head><body><pre> Autogenerated Index (Only in dev env) <br /><br />"; | ||
let curPath = (mountpoint instanceof KwyjiboController) ? mountpoint.path : mountpoint; | ||
for (let child of childControllerPaths) { | ||
content += `[Controller] <a href="${U.UrlJoin("/", curPath, child).substr(1)}">${child}</a><br />`; | ||
function useRouterAtPathStrict(baseRouter, basePath, router) { | ||
if (basePath.substring(basePath.length - 1) === "/") { | ||
basePath = basePath.substr(0, basePath.length - 1); | ||
} | ||
let strictPath = U.UrlJoin(basePath, "/"); | ||
baseRouter.use(strictPath, (req, res, next) => { | ||
if (req.originalUrl.substring(req.originalUrl.length - basePath.length) === basePath) { | ||
res.redirect(strictPath); | ||
} | ||
content += "<br />"; | ||
if (mountpoint instanceof KwyjiboController) { | ||
for (let mk in mountpoint.methods) { | ||
for (let mmp of mountpoint.methods[mk].methodMountpoints) { | ||
content += `[${mmp.httpMethod.toUpperCase()}] <a href="${U.UrlJoin("/", curPath, mmp.path).substr(1)}">${mmp.path}</a><br />`; | ||
} | ||
} | ||
else { | ||
next(); | ||
} | ||
content += "</pre></body></html>"; | ||
res.send(content); | ||
}; | ||
}, router); | ||
} | ||
function addControllerRecursive(app, controller) { | ||
function createRouterRecursive(app, controllerNode) { | ||
let controller = controllerNode.controller; | ||
if (controller.mountCondition === false) { | ||
@@ -338,16 +381,15 @@ return undefined; | ||
for (let mk in controller.methods) { | ||
mountMethod(controller, instance, mk, controller.methods[mk]); | ||
mountMethod(controller, instance, mk); | ||
} | ||
let childControllerPaths = []; | ||
for (let mp of exports.globalKCState.mountpoints) { | ||
if (controller.ctr.toString() === mp.dstCtr.toString()) { | ||
let nc = addControllerRecursive(app, exports.globalKCState.getController(mp.ctr)); | ||
if (nc != undefined) { | ||
controller.router.use(nc.path, nc.router); | ||
childControllerPaths.push(nc.path); | ||
} | ||
for (let child of controllerNode.childs) { | ||
let nc = createRouterRecursive(app, child); | ||
if (nc != undefined) { | ||
useRouterAtPathStrict(controller.router, nc.path, nc.router); | ||
} | ||
} | ||
if (controller.generateTestRunnerPaths) { | ||
T.injectTestRunnerMiddleware(controller); | ||
} | ||
if (process.env.NODE_ENV === "development") { | ||
controller.router.get("/", indexAutogenerator(controller, childControllerPaths)); | ||
controller.router.get("/", indexAutogenerator(controller, controllerNode.childs)); | ||
} | ||
@@ -358,18 +400,13 @@ return controller; | ||
rootPath = rootPath || "/"; | ||
//A method without paths, defaults to get with the name of the method as path | ||
let childControllerPaths = []; | ||
for (let ck in exports.globalKCState.controllers) { | ||
let c = exports.globalKCState.controllers[ck]; | ||
if (c.childController === false) { | ||
let nc = addControllerRecursive(app, c); | ||
if (nc != undefined) { | ||
app.use(U.UrlJoin(rootPath, nc.path), nc.router); | ||
childControllerPaths.push(nc.path); | ||
} | ||
buildControllersTree(); | ||
for (let node of exports.globalKCState.controllersTree) { | ||
let nc = createRouterRecursive(app, node); | ||
if (nc != undefined) { | ||
useRouterAtPathStrict(app, U.UrlJoin(rootPath, nc.path), nc.router); | ||
} | ||
} | ||
if (process.env.NODE_ENV === "development") { | ||
app.get(rootPath, indexAutogenerator(rootPath, childControllerPaths)); | ||
app.get(rootPath, indexAutogenerator(undefined, exports.globalKCState.controllersTree)); | ||
} | ||
} | ||
exports.addControllersToExpressApp = addControllersToExpressApp; |
@@ -1,3 +0,3 @@ | ||
export * from './controller'; | ||
export * from './testing'; | ||
export * from './utils'; | ||
export * from "./controller"; | ||
export * from "./testing"; | ||
export * from "./utils"; |
@@ -5,4 +5,4 @@ "use strict"; | ||
} | ||
__export(require('./controller')); | ||
__export(require('./testing')); | ||
__export(require('./utils')); | ||
__export(require("./controller")); | ||
__export(require("./testing")); | ||
__export(require("./utils")); |
@@ -0,1 +1,2 @@ | ||
import * as C from "./controller"; | ||
/********************************************************* | ||
@@ -47,6 +48,19 @@ * Class Decorators | ||
}; | ||
export declare class KwyjiboTestResult { | ||
fixtureDesc: string; | ||
fixtureKey: string; | ||
testDesc: string; | ||
testKey: string; | ||
passed: boolean; | ||
message: string; | ||
} | ||
export declare class KwyjiboTestsState { | ||
fixtures: KwyjiboFixtureMap; | ||
getOrInsertFixture(ctr: Function): KwyjiboFixture; | ||
generateCompleteFixtureMetadata(): Object; | ||
static getFixtureHashId(fixture: KwyjiboFixture): string; | ||
generateRainbowTables(): Object; | ||
run(testsToRun?: Object): Promise<KwyjiboTestResult[]>; | ||
} | ||
export declare let globalKTState: KwyjiboTestsState; | ||
export declare function injectTestRunnerMiddleware(controller: C.KwyjiboController): void; |
@@ -0,5 +1,15 @@ | ||
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator.throw(value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments)).next()); | ||
}); | ||
}; | ||
const Crypto = require("crypto"); | ||
const U = require("./utils"); | ||
/********************************************************* | ||
* Class Decorators | ||
*********************************************************/ | ||
"use strict"; | ||
/** | ||
@@ -75,2 +85,5 @@ * Registers a fixture of tests with the global test runner. | ||
exports.KwyjiboFixture = KwyjiboFixture; | ||
class KwyjiboTestResult { | ||
} | ||
exports.KwyjiboTestResult = KwyjiboTestResult; | ||
class KwyjiboTestsState { | ||
@@ -88,4 +101,310 @@ constructor() { | ||
} | ||
generateCompleteFixtureMetadata() { | ||
let metadata = {}; | ||
for (let fxk in this.fixtures) { | ||
let fixture = this.fixtures[fxk]; | ||
let fxkHash = KwyjiboTestsState.getFixtureHashId(fixture); | ||
metadata[fxkHash] = {}; | ||
for (let tk in fixture.tests) { | ||
metadata[fxkHash][tk] = true; | ||
} | ||
} | ||
return metadata; | ||
} | ||
static getFixtureHashId(fixture) { | ||
let hasher = Crypto.createHash("sha256"); | ||
hasher.update(fixture.ctr.toString()); | ||
return fixture.ctr.name + "_" + hasher.digest("hex").substr(0, 8); | ||
} | ||
generateRainbowTables() { | ||
let rt = {}; | ||
for (let fxk in this.fixtures) { | ||
let fixture = this.fixtures[fxk]; | ||
let fxkHash = KwyjiboTestsState.getFixtureHashId(fixture); | ||
rt[fxkHash] = fixture; | ||
} | ||
return rt; | ||
} | ||
run(testsToRun) { | ||
return __awaiter(this, void 0, Promise, function* () { | ||
if (testsToRun == undefined) { | ||
testsToRun = this.generateCompleteFixtureMetadata(); | ||
} | ||
let testResults = []; | ||
let rainbowTables = this.generateRainbowTables(); | ||
for (let fxk in testsToRun) { | ||
let fixture = rainbowTables[fxk]; | ||
let fi = Reflect.construct(fixture.ctr, []); | ||
for (let mn of fixture.runBeforeMethods) { | ||
let r = fi[mn](); | ||
if (r instanceof Promise) { | ||
yield r; | ||
} | ||
} | ||
for (let mn in testsToRun[fxk]) { | ||
if (testsToRun[fxk][mn] === true && fixture.tests[mn] != undefined && fi[mn] != undefined) { | ||
let result = new KwyjiboTestResult(); | ||
result.fixtureKey = fxk; | ||
result.fixtureDesc = fixture.humanReadableName; | ||
result.testKey = mn; | ||
result.testDesc = fixture.tests[mn].humanReadableName; | ||
try { | ||
let r = fi[mn](); | ||
if (r instanceof Promise) { | ||
yield r; | ||
} | ||
result.passed = true; | ||
result.message = ""; | ||
} | ||
catch (err) { | ||
result.passed = false; | ||
if (err instanceof Error) { | ||
result.message = JSON.stringify({ name: err.name, message: err.message, stack: err.stack }); | ||
} | ||
else if (err instanceof Object) { | ||
result.message = JSON.stringify(err); | ||
} | ||
else if (typeof (err) === "string") { | ||
result.message = err; | ||
} | ||
else { | ||
result.message = err.toString(); | ||
} | ||
} | ||
testResults.push(result); | ||
} | ||
} | ||
for (let mn of fixture.runAfterMethods) { | ||
let r = fi[mn](); | ||
if (r instanceof Promise) { | ||
yield r; | ||
} | ||
} | ||
} | ||
return testResults; | ||
}); | ||
} | ||
} | ||
exports.KwyjiboTestsState = KwyjiboTestsState; | ||
exports.globalKTState = new KwyjiboTestsState(); | ||
const defaultCSS = ` | ||
body { | ||
font-family:verdana; | ||
} | ||
tr { | ||
padding:5px; | ||
} | ||
tr a { | ||
color:#000000; | ||
} | ||
tr.header { | ||
background-color:#AAAAAA; | ||
} | ||
tr.header-allpassed { | ||
background-color:#00AA00; | ||
} | ||
tr.header-somefailed { | ||
background-color:#AA0000; | ||
} | ||
tr.header-somefailed a{ | ||
color:#FFFFFF; | ||
} | ||
button#runButton { | ||
width:100%; | ||
font-size:18px; | ||
font-weight:bold; | ||
} | ||
.flex-container { | ||
display: flex; | ||
flex-direction: column; | ||
flex-wrap: nowrap; | ||
justify-content: flex-start; | ||
align-content: stretch; | ||
align-items: center; | ||
} | ||
.flex-item { | ||
order: 0; | ||
flex: 0 1 auto; | ||
align-self: auto; | ||
} | ||
`; | ||
function generateInteractiveTestRunnerMiddleware(useFixture) { | ||
return (req, res) => { | ||
let content = ` | ||
<html> | ||
<head> | ||
<style> | ||
${defaultCSS} | ||
</style> | ||
</head> | ||
<body class="flex-container"> | ||
<script> | ||
function init() { | ||
var runners = Array.from(document.querySelectorAll('input.runner')); | ||
runners.forEach(function(runner){ | ||
runner.onchange = function(e) { | ||
var childs = Array.from(document.querySelectorAll('input.' + e.target.id)); | ||
childs.forEach(function(child){ | ||
child.checked = e.target.checked; | ||
}); | ||
}; | ||
}); | ||
document.getElementById('runButton').onclick=submit; | ||
} | ||
function submit() { | ||
var runners = Array.from(document.querySelectorAll('input.runner')); | ||
var toSubmit = {}; | ||
runners.forEach(function(runner){ | ||
document.getElementById(runner.id+"-header").className="header"; | ||
var childs = Array.from(document.querySelectorAll('input.' + runner.id)); | ||
childs.forEach(function(child){ | ||
var curTD = document.getElementById(runner.id+"_"+child.id+"_result"); | ||
if(child.checked) { | ||
if(toSubmit[runner.id]==undefined) toSubmit[runner.id]={}; | ||
toSubmit[runner.id][child.id]=true; | ||
curTD.innerHTML="🕒"; | ||
curTD.title="Loading..."; | ||
} else { | ||
curTD.innerHTML=""; | ||
curTD.title=""; | ||
} | ||
}); | ||
}); | ||
var xhr = new XMLHttpRequest(); | ||
xhr.open("POST", "${useFixture == undefined ? "" : "../../"}some", true); | ||
xhr.setRequestHeader('Content-Type', 'application/json; charset=UTF-8'); | ||
// send the collected data as JSON | ||
xhr.send(JSON.stringify(toSubmit)); | ||
xhr.onloadend = function () { | ||
var results = JSON.parse(this.responseText); | ||
var fixturePass = {}; | ||
results.forEach(function(result){ | ||
if(fixturePass[result.fixtureKey]==undefined) { | ||
fixturePass[result.fixtureKey]=true; | ||
} | ||
var rTD = document.getElementById(result.fixtureKey+"_"+result.testKey+"_result"); | ||
rTD.innerHTML = result.passed ? '✅' : '❌'; | ||
if(!result.passed) { | ||
fixturePass[result.fixtureKey]=false; | ||
} | ||
rTD.title = result.message; | ||
}); | ||
for(var fxk in fixturePass) { | ||
var fh = document.getElementById(fxk+"-header"); | ||
fh.className = fixturePass[fxk]? "header-allpassed" : "header-somefailed"; | ||
} | ||
}; | ||
} | ||
window.onload=init; | ||
</script> | ||
<div class="flex-item"> | ||
<center> | ||
<h2> Interactive Tests Runner </h2> | ||
`; | ||
let useFixtureHash = undefined; | ||
if (useFixture == undefined) { | ||
content += ` | ||
To run all the tests programatically use: <a href="all">/all</a><br /> | ||
To run some of the tests programatically send a JSON payload via POST to: <a href="some">/some</a><br /> | ||
(You can see how the JSON payload should look at <a href="metadata">/metadata</a>)<br /><br /> | ||
`; | ||
} | ||
else { | ||
content += ` | ||
To run all the tests programatically use: <a href="all">/all</a><br /> | ||
To run some of the tests programatically, see <a href="../../">tests home</a><br /><br /> | ||
`; | ||
useFixtureHash = KwyjiboTestsState.getFixtureHashId(useFixture); | ||
} | ||
content += `<table>`; | ||
for (let fxk in exports.globalKTState.fixtures) { | ||
let fixture = exports.globalKTState.fixtures[fxk]; | ||
let fxkHash = KwyjiboTestsState.getFixtureHashId(fixture); | ||
if (useFixtureHash != undefined && fxkHash !== useFixtureHash) { | ||
continue; | ||
} | ||
content += `<tr class="header" id="${fxkHash}-header" > | ||
<td><input class="runner" id="${fxkHash}" type="checkbox" checked /></td> | ||
<td colspan="2"><a href="fixture/${fxkHash}/">${fixture.humanReadableName}</a></td></tr>`; | ||
for (let tk in fixture.tests) { | ||
let test = fixture.tests[tk]; | ||
content += `<tr><td><input class="${fxkHash}" id="${tk}" type="checkbox" checked /></td> | ||
<td text-align="left">${test.humanReadableName}</td><td id="${fxkHash}_${tk}_result"></td></tr>`; | ||
} | ||
} | ||
content += `<tr><td colspan="3"></td></tr><tr><td colspan="3"><button id="runButton">Run Tests</button></td></tr>`; | ||
content += "</table>"; | ||
content += "</center></div></body></html>"; | ||
res.send(content); | ||
}; | ||
} | ||
function runSetOfTestsAndFillResponse(testsToRun, res) { | ||
return __awaiter(this, void 0, Promise, function* () { | ||
let results = yield exports.globalKTState.run(testsToRun); | ||
let failed = false; | ||
for (let result of results) { | ||
if (result.passed === false) { | ||
failed = true; | ||
} | ||
} | ||
res.statusCode = failed ? 418 : 200; | ||
res.json(results); | ||
}); | ||
} | ||
function injectTestRunnerMiddleware(controller) { | ||
/* | ||
Available endpoints: | ||
- /: interactive shell | ||
- /all: run all tests unattended | ||
- /metadata: get json metadata for available tests. | ||
- /some: post a json to run only a subset of tests. | ||
- /fixture/[FixtureName]: link to all tests in fixture (interactive shell) | ||
- /fixture/[FixtureName]/all | ||
*/ | ||
controller.router.get("/all", (req, res) => __awaiter(this, void 0, void 0, function* () { | ||
yield runSetOfTestsAndFillResponse(undefined, res); | ||
})); | ||
controller.router.post("/some", (req, res) => __awaiter(this, void 0, void 0, function* () { | ||
yield runSetOfTestsAndFillResponse(req.body, res); | ||
})); | ||
controller.router.get("/metadata", (req, res) => { | ||
res.json(exports.globalKTState.generateCompleteFixtureMetadata()); | ||
}); | ||
for (let fxk in exports.globalKTState.fixtures) { | ||
let fixture = exports.globalKTState.fixtures[fxk]; | ||
let fxkHash = KwyjiboTestsState.getFixtureHashId(fixture); | ||
controller.router.get(U.UrlJoin("/fixture/", fxkHash, "/"), generateInteractiveTestRunnerMiddleware(fixture)); | ||
controller.router.get(U.UrlJoin("/fixture/", fxkHash, "/all"), (req, res) => __awaiter(this, void 0, void 0, function* () { | ||
let metadata = exports.globalKTState.generateCompleteFixtureMetadata(); | ||
let thisFixtureSet = {}; | ||
for (let mk in metadata) { | ||
if (mk === fxkHash) { | ||
thisFixtureSet[mk] = metadata[mk]; | ||
} | ||
} | ||
yield runSetOfTestsAndFillResponse(thisFixtureSet, res); | ||
})); | ||
} | ||
controller.router.get("/", generateInteractiveTestRunnerMiddleware()); | ||
} | ||
exports.injectTestRunnerMiddleware = injectTestRunnerMiddleware; |
{ | ||
"name": "kwyjibo", | ||
"version": "1.0.1", | ||
"version": "1.0.3", | ||
"description": "A set of Typescript Decorators and helpers to write better node.js+Express applications.", | ||
@@ -5,0 +5,0 @@ "main": "js/index.js", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No README
QualityPackage does not have a README. This may indicate a failed publish or a low quality package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
42434
1039
0
12
2
0