UNPKG

soda-test

Version:

Package for Unit and API tests

468 lines 18.3 kB
"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