soda-test
Version:
Package for Unit and API tests
468 lines • 18.3 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestDescribe = exports.Sequensal = exports.TestContext = exports.setJest = void 0;
const testSuite_1 = require("./testSuite");
const sinon_1 = require("sinon");
const sinons_1 = require("./sinons");
const testbed_1 = require("./testbed");
let isJest = false;
function setJest() { isJest = true; }
exports.setJest = setJest;
function keysof(obj) {
if (!obj)
return [];
return Object.keys(obj);
}
class ExecutableBase {
executeAsync() {
const rv = this.execute();
if (rv === undefined) {
// method was aynced, resove the promise
return new Promise((resolve) => resolve());
}
return rv;
}
}
const STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
const ARGUMENT_NAMES = /([^\s,]+)/g;
function getParamNames(func) {
const fnStr = func.toString().replace(STRIP_COMMENTS, '');
let result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
if (result === null)
result = [];
return result;
}
class TestStep extends ExecutableBase {
get UseDone() {
return this.useDone;
}
doneReset() {
this.donePromise = new Promise((resolve) => {
this.doneResolve = resolve;
});
}
constructor(target, method, sinons) {
super();
this.target = target;
this.method = method;
this.sinons = sinons;
// if there is a first argumet and it is not a spy, it is the done
this.useDone = method.length > 0 && sinons[0] === undefined;
this.arguments = [];
if (this.useDone)
this.arguments.push(() => this.doneResolve());
for (const parameterIndex of keysof(this.sinons)) {
const index = Number(parameterIndex);
while (this.arguments.length < index) {
this.arguments.push(null);
}
}
}
getArguments(paramNames) {
const prevSinons = {};
for (const parameterIndex of keysof(this.sinons)) {
const index = Number(parameterIndex);
const sinonInfo = this.sinons[parameterIndex];
const sinon = (0, sinons_1.createSinon)(sinonInfo, prevSinons);
prevSinons[paramNames[index]] = sinon;
this.arguments[index] = sinon;
}
return this.arguments;
}
cleanup() {
for (const parameterIndex of keysof(this.sinons)) {
const index = Number(parameterIndex);
if (this.arguments[index]['restore'])
this.arguments[index]['restore']();
this.arguments[index] = null;
}
if (this.useDone) {
this.arguments[0] = null;
}
}
execute() {
return __awaiter(this, void 0, void 0, function* () {
if (this.useDone)
this.doneReset();
const rv = this.method.apply(this.target, this.getArguments(getParamNames(this.method)));
if (this.useDone) {
yield this.donePromise;
}
else if (rv !== undefined) {
yield rv;
}
this.cleanup();
});
}
}
class TestIt extends ExecutableBase {
constructor(name, inner, _pending = false) {
super();
this.name = name;
this.inner = inner;
this._pending = _pending;
this.update();
}
get pending() {
return this._pending;
}
set pending(value) {
this._pending = value;
this.update();
}
executePending() {
testSuite_1.default.it(this.name);
}
executeJestPending() {
testSuite_1.default.it['skip'](this.name, () => { });
}
executeRegular() {
testSuite_1.default.it(this.name, () => this.inner.execute());
}
update() {
if (this.pending) {
if (isJest) {
this.execute = this.executeJestPending;
}
else {
this.execute = this.executePending;
}
}
else {
this.execute = this.executeRegular;
}
}
execute() {
// placeholder
}
}
function executeMethod(method, target) {
return __awaiter(this, void 0, void 0, function* () {
const result = method.apply(target);
if (result && result.then) {
return yield result;
}
else {
return result;
}
});
}
class TestBefore extends ExecutableBase {
constructor(beforeInner, beforeLast, target) {
super();
this.beforeInner = beforeInner;
this.beforeLast = beforeLast;
this.target = target;
}
execute() {
testSuite_1.default.before(() => __awaiter(this, void 0, void 0, function* () {
if (this.beforeInner) {
for (const func of this.beforeInner) {
yield executeMethod(func, this.target);
}
}
if (this.beforeLast) {
yield executeMethod(this.beforeLast, this.target);
}
}));
}
}
class TestAfter extends ExecutableBase {
constructor(afterFirst, afterInner, target) {
super();
this.afterFirst = afterFirst;
this.afterInner = afterInner;
this.target = target;
}
execute() {
testSuite_1.default.after(() => __awaiter(this, void 0, void 0, function* () {
if (this.afterFirst) {
yield executeMethod(this.afterFirst, this.target);
}
if (this.afterInner) {
for (const func of this.afterInner) {
yield executeMethod(func, this.target);
}
}
}));
}
}
class TestBeforeEach extends ExecutableBase {
constructor(beforeEachInner, beforeEachLast, target) {
super();
this.beforeEachInner = beforeEachInner;
this.beforeEachLast = beforeEachLast;
this.target = target;
}
execute() {
testSuite_1.default.beforeEach(() => __awaiter(this, void 0, void 0, function* () {
if (this.beforeEachInner) {
for (const func of this.beforeEachInner) {
yield executeMethod(func, this.target);
}
}
if (this.beforeEachLast) {
yield executeMethod(this.beforeEachLast, this.target);
}
}));
}
}
class TestAfterEach extends ExecutableBase {
constructor(afterEachFirst, afterEachInner, target) {
super();
this.afterEachFirst = afterEachFirst;
this.afterEachInner = afterEachInner;
this.target = target;
}
execute() {
testSuite_1.default.afterEach(() => __awaiter(this, void 0, void 0, function* () {
if (this.afterEachFirst) {
yield executeMethod(this.afterEachFirst, this.target);
}
if (this.afterEachInner) {
for (const func of this.afterEachInner) {
yield executeMethod(func, this.target);
}
}
}));
}
}
class TestContext extends ExecutableBase {
constructor(name, inner) {
super();
this.name = name;
this.inner = inner;
}
execute() {
testSuite_1.default.describe(this.name, () => this.inner.execute());
}
}
exports.TestContext = TestContext;
class Sequensal extends ExecutableBase {
constructor() {
super(...arguments);
this.steps = [];
}
push(step) {
this.steps.push(step);
}
getLength() {
return this.steps.length;
}
execute() {
if (!this.steps)
return;
this.steps.forEach((step) => step.execute());
}
addControlMethods(methods, instance) {
if (methods.beforeInner || methods.beforeLast) {
this.push(new TestBefore(methods.beforeInner, methods.beforeLast, instance));
}
if (methods.afterFirst || methods.afterInner) {
this.push(new TestAfter(methods.afterFirst, methods.afterInner, instance));
}
if (methods.beforeEachInner || methods.beforeEachLast) {
this.push(new TestBeforeEach(methods.beforeEachInner, methods.beforeEachLast, instance));
}
if (methods.afterEachFirst || methods.afterEachInner) {
this.push(new TestAfterEach(methods.afterEachFirst, methods.afterEachInner, instance));
}
}
addItsAndCases(itsAndCases, instance) {
for (const name of keysof(itsAndCases)) {
const it = itsAndCases[name];
if (it && it.method) {
this.push(new TestIt(it.itText, new TestStep(instance, it.method, it.sinons), it.pending));
continue;
}
// check if it was a case
const tcase = itsAndCases[name];
if (tcase && tcase.its) {
const steps = new Sequensal();
steps.addControlMethods({
beforeInner: [function () {
let maxInstanceIndex = 0;
for (const it of this.its) {
if (it.instanceIndex > maxInstanceIndex)
maxInstanceIndex = it.instanceIndex;
}
this.instances = [];
for (let i = 0; i <= maxInstanceIndex; i++) {
this.instances.push(this.stepsFactory());
}
}]
}, tcase);
for (const it of tcase.its) {
steps.push(new TestIt(it.itText, new TestStep(tcase, it.method, it.sinons), it.pending));
}
this.push(new TestContext(tcase.caseText, steps));
continue;
}
}
}
}
exports.Sequensal = Sequensal;
class TestDescribe extends ExecutableBase {
execute() {
testSuite_1.default.describe(this.name, () => this.mainExecution.execute());
}
constructor(name, info, constructorFunc) {
super();
this.name = name;
this.info = info;
this.constructorFunc = constructorFunc;
this.defineGlobalControlMethods();
this.createInstance();
this.createSandboxes();
this.createMainSequence();
this.createSinonsMembers();
this.defineMainControlMethodsItsAndCases();
this.defineContextControlMethodsItsAndCases();
}
defineGlobalControlMethods() {
// look for after and before methods:
const beforeMethod = this.constructorFunc.prototype.before;
const afterMethod = this.constructorFunc.prototype.after;
const beforeEachMethod = this.constructorFunc.prototype.beforeEach;
const afterEachMethod = this.constructorFunc.prototype.afterEach;
// define execution of contexts
if (beforeMethod && !this.info.uncontext.contextControlMethods.beforeLast) {
this.info.uncontext.contextControlMethods.beforeLast = beforeMethod;
}
if (afterMethod && !this.info.uncontext.contextControlMethods.afterFirst) {
this.info.uncontext.contextControlMethods.afterFirst = afterMethod;
}
if (beforeEachMethod && !this.info.uncontext.contextControlMethods.beforeEachLast) {
this.info.uncontext.contextControlMethods.beforeEachLast = beforeEachMethod;
}
if (afterEachMethod && !this.info.uncontext.contextControlMethods.afterEachFirst) {
this.info.uncontext.contextControlMethods.afterEachFirst = afterEachMethod;
}
}
createInstance() {
this.instance = Reflect.construct(this.constructorFunc, []);
}
createSandboxes() {
for (let i = 1; i < this.info.sandboxes.length; i++) {
this.instance[this.info.sandboxes[i]] = (0, sinon_1.createSandbox)();
}
}
createMainSequence() {
this.mainExecution = new Sequensal();
}
defineMainControlMethodsItsAndCases() {
// TODO: check if there are testbed code to execute
const initTestBed = (0, testbed_1.getInitTestBedFunction)();
if (initTestBed) {
this.mainExecution.addControlMethods({
beforeInner: [initTestBed]
}, this.instance);
}
// update main control methods
this.mainExecution.addControlMethods(this.info.uncontext.contextControlMethods, this.instance);
// define execution of uncontexts
this.mainExecution.addItsAndCases(this.info.uncontext.itsAndCases, this.instance);
}
defineContextControlMethodsItsAndCases() {
// go over contexts
for (const contextName of keysof(this.info.contexts)) {
const context = this.info.contexts[contextName];
const contextSequence = new Sequensal();
contextSequence.addControlMethods(context.contextControlMethods, this.instance);
contextSequence.addItsAndCases(context.itsAndCases, this.instance);
if (contextSequence.getLength() > 0) {
this.mainExecution.push(new TestContext(context.contextText, contextSequence));
}
}
}
createSinonsMembers() {
const info = this.info;
// create sinons members
if (keysof(info.sinons).length > 0) {
const { initMethods, rapupMethods } = this.createInitAndRapupMethods(info);
this.addInitMethodIntoBeforeEachControlMethods(info, initMethods);
this.addRapupMethodsIntoAfterEachControlMethods(info, rapupMethods);
}
}
createInitAndRapupMethods(info) {
const initMethods = {};
const rapupMethods = {};
for (const sinonName of keysof(info.sinons)) {
const sinonContext = (info.sinons[sinonName].context) ? info.sinons[sinonName].context : "";
if (!initMethods[sinonContext]) {
initMethods[sinonContext] = [];
initMethods[sinonContext].push({
global: info.sinons[sinonName].global,
func: function () {
initMethods[sinonContext]['_sinons'] = {};
}
});
}
if (!rapupMethods[sinonContext]) {
rapupMethods[sinonContext] = [];
}
initMethods[sinonContext].push({
global: info.sinons[sinonName].global,
func: function () {
this[sinonName] = (0, sinons_1.createSinon)(info.sinons[sinonName], initMethods[sinonContext]['_sinons']);
initMethods[sinonContext]['_sinons'] = initMethods[sinonContext]['_sinons'] || {};
initMethods[sinonContext]['_sinons'][sinonName] = this[sinonName];
}
});
rapupMethods[sinonContext].push({
global: info.sinons[sinonName].global,
func: function () {
if (this[sinonName] && this[sinonName].restore) {
this[sinonName].restore();
}
delete this[sinonName];
}
});
}
return { initMethods, rapupMethods };
}
addInitMethodIntoBeforeEachControlMethods(info, initMethods) {
for (const sinonContext of keysof(initMethods)) {
const describeControlMethods = info.getContext(sinonContext).contextControlMethods;
for (const { global, func } of initMethods[sinonContext]) {
if (global) {
describeControlMethods.beforeInner = describeControlMethods.beforeInner || [];
describeControlMethods.beforeInner.push(func);
}
else {
describeControlMethods.beforeEachInner = describeControlMethods.beforeEachInner || [];
describeControlMethods.beforeEachInner.push(func);
}
}
}
}
addRapupMethodsIntoAfterEachControlMethods(info, rapupMethods) {
// add code to resotre everything in the afterEach/after methods:
for (let i = keysof(rapupMethods).length - 1; i >= 0; i--) {
const sinonContext = keysof(rapupMethods)[i];
const describeControlMethods = info.getContext(sinonContext).contextControlMethods;
const contextRapupMethods = rapupMethods[sinonContext];
for (const { global, func } of contextRapupMethods) {
if (global) {
describeControlMethods.afterInner = describeControlMethods.afterInner || [];
describeControlMethods.afterInner.push(func);
}
else {
describeControlMethods.afterEachInner = describeControlMethods.afterEachInner || [];
describeControlMethods.afterEachInner.push(func);
}
}
}
}
}
exports.TestDescribe = TestDescribe;
//# sourceMappingURL=executables.js.map