UNPKG

protractor-sync-options-plugin

Version:

Protractor plugin to ignore specific async calls in angular application

280 lines (271 loc) 11.6 kB
import { __awaiter, __generator } from 'tslib'; import { browser } from 'protractor'; /** * set of functions, that can be executed by client/frontend. * every function must be "all inclusive" without any references to outside, * because they are transferred as text to client by selenium. */ var patchTestability_1 = patchTestability; var restoreTestability_1 = restoreTestability; /** * (monkey)patches, if not already patched, angular.testability#whenStable, to be able to ignore some async tasks * @param {IgnoreTask[]} ignoreTasks ignore filter tasks definitions */ function patchTestability(ignoreTasks) { if (!window.getAllAngularTestabilities) { throw Error('Testability not found. Not an angular app?'); } var testability = window.getAllAngularTestabilities()[0]; // TODO all apps var Testability = testability.constructor; var isStableOrig = Testability.prototype.isStable; if (!!isStableOrig.originalFn) { throw Error('Testability already patched'); } var newIsStable = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var isStable = isStableOrig.apply(this, args); var isStableWithoutFiltered = hasOnlyFilteredTasks(testability); return isStable || isStableWithoutFiltered; }; newIsStable.originalFn = isStableOrig; Testability.prototype.isStable = newIsStable; // ----------------------------------------------------- // helpers // ----------------------------------------------------- function matchesSource(/** PendingMacrotask */ task, /** IgnoreTask */ ignoreTask) { return task.source === ignoreTask.source; } function isSourceDefined(/** IgnoreTask */ ignoreTask) { var source = ignoreTask.source; return (typeof source === 'string') || (source instanceof RegExp); } function matchesLocation(/** PendingMacrotask */ task, /** IgnoreTask */ ignoreTask) { var locationFilter = ignoreTask.creationLocation; var callLocations = getCallLocations(task); return callLocations.some(function (callLocation) { if (locationFilter instanceof RegExp) { return callLocation.match(locationFilter); } if (typeof locationFilter === 'string') { return callLocation.includes(locationFilter); } }); } function isLocationDefined(/** IgnoreTask */ ignoreTask) { var loc = ignoreTask.creationLocation; return (typeof loc === 'string') || (loc instanceof RegExp); } /** * @param {PendingMacrotask} task task to check * @param {IgnoreTask} ignoreTask matching conditions * @return {boolean} true if all defined condition properties of `ignoreTask` matches (conjunction) */ function areMatching(task, ignoreTask) { var result = true; if (isSourceDefined(ignoreTask)) { result &= matchesSource(task, ignoreTask); } if (isLocationDefined(ignoreTask)) { result &= matchesLocation(task, ignoreTask); } return result; } function getCallLocations(/** PendingMacrotask */ task) { return task.creationLocation.stack.split(' at '); } function shouldIgnore(/** PendingMacrotask */ t) { return ignoreTasks.some(function (it) { return (areMatching(t, it)); }); } function hasOnlyFilteredTasks(testability) { var trZone = testability['taskTrackingZone']; // private prop TODO use any api? var tasks = trZone.macroTasks; return tasks.every(shouldIgnore); } } /** * restores patched whenStable function to original, if patched. */ function restoreTestability() { if (!window.getAllAngularTestabilities) { throw Error('Testability not found. Not an angular app?'); } var testability = window.getAllAngularTestabilities()[0]; // TODO all apps var Testability = testability.constructor; var isStableFn = Testability.prototype.isStable; if (!isStableFn.originalFn) { throw Error('Cant restore, Testability.whenStable is not patched'); } Testability.prototype.isStable = isStableFn.originalFn; } var clientScripts = { patchTestability: patchTestability_1, restoreTestability: restoreTestability_1 }; /** * makes monkey-patching for protractor and frontend angular.testability * @TODO find a way to use some api instead of monkey-patching */ var Patcher = /** @class */ (function () { function Patcher(browser) { this.browser = browser; } /** * * @param config config to use * @returns `true` in success case, `false` otherwise (e.g. already patched) */ Patcher.prototype.patchClientTestability = function (config) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.browser.executeScript(patchTestability_1, config.ignoreTasks || [])]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * restores patched with {@link this#patchClientTestability} client */ Patcher.prototype.restoreClientTestability = function () { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.browser.executeScript(restoreTestability_1)]; }); }); }; /** * patches {@link browser#waitForAngular} * @returns `true` in success case, `false` otherwise (e.g. already patched) */ Patcher.prototype.patchWaitForAngularEnabled = function (config) { return __awaiter(this, void 0, void 0, function () { var origFn, thisPlugin, newFn; return __generator(this, function (_a) { if (this.isWaitForAngularPatched()) { throw Error('browser.waitForAngularEnabled is already patched'); } origFn = this.browser.constructor.prototype.waitForAngularEnabled; thisPlugin = this; newFn = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: if (!(args[0] === true)) return [3 /*break*/, 2]; return [4 /*yield*/, thisPlugin.patchClientTestability(config).catch(function (e) { console.warn('patched waitForAngularEnabled => patchClientTestability error:', e); // TODO logger })]; case 1: _a.sent(); _a.label = 2; case 2: return [2 /*return*/, origFn.apply(this, args)]; } }); }); }; newFn.orignalFn = origFn; this.browser.constructor.prototype.waitForAngularEnabled = newFn; return [2 /*return*/]; }); }); }; Patcher.prototype.restoreWaitForAngularEnabled = function () { return __awaiter(this, void 0, void 0, function () { var proto; return __generator(this, function (_a) { if (!this.isWaitForAngularPatched()) { throw Error('Cant restore. this.browser.constructor.prototype.waitForAngularEnabled is not patched'); } proto = this.browser.constructor.prototype; proto.waitForAngularEnabled = proto.waitForAngularEnabled.originalFn; return [2 /*return*/]; }); }); }; Patcher.prototype.isWaitForAngularPatched = function () { return !!this.browser.constructor.prototype.waitForAngularEnabled.originalFn; // better check? }; return Patcher; }()); var SyncOptionsPlugin = /** @class */ (function () { function SyncOptionsPlugin() { this.name = 'Protractor Sync Options Plugin'; this.patcher = new Patcher(browser); } SyncOptionsPlugin.prototype.onPageLoad = function (currBrowser) { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.trySafe(function () { return _this.patcher.patchClientTestability(_this.config); })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; SyncOptionsPlugin.prototype.setup = function () { return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: this.patcher.browser = browser; return [4 /*yield*/, this.trySafe(function () { return _this.patcher.patchWaitForAngularEnabled(_this.config); })]; case 1: _a.sent(); return [2 /*return*/]; } }); }); }; /** * safe function call * shows only warning in function error case * @returns `false` if `funcToCall` throws an error, `true` otherwise */ SyncOptionsPlugin.prototype.trySafe = function (funcToCall) { return __awaiter(this, void 0, void 0, function () { var e_1; return __generator(this, function (_a) { switch (_a.label) { case 0: _a.trys.push([0, 2, , 3]); return [4 /*yield*/, funcToCall()]; case 1: _a.sent(); return [2 /*return*/, true]; case 2: e_1 = _a.sent(); console.warn('function call failed', e_1); // TODO logger; return [2 /*return*/, false]; case 3: return [2 /*return*/]; } }); }); }; return SyncOptionsPlugin; }()); // workaround to export the plugin instance from package require var initializedPlugin = new SyncOptionsPlugin(); Object.assign(exports, initializedPlugin); exports.setup = initializedPlugin.setup; exports.onPageLoad = initializedPlugin.onPageLoad; exports.trySafe = initializedPlugin.trySafe; var plugin = exports; // need to usual 'import' initialized plugin /** * Generated bundle index. Do not edit. */ export { Patcher, SyncOptionsPlugin, plugin }; //# sourceMappingURL=protractor-sync-options-plugin.js.map