Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(stub-deps): ability to stub dependencies for ComponentTester #89

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions dist/amd/compile-spy.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
define(["require", "exports", "aurelia-templating", "aurelia-dependency-injection", "aurelia-logging", "aurelia-pal"], function (require, exports, aurelia_templating_1, aurelia_dependency_injection_1, aurelia_logging_1, aurelia_pal_1) {
define(["require", "exports", "aurelia-templating", "aurelia-logging", "aurelia-pal"], function (require, exports, aurelia_templating_1, aurelia_logging_1, aurelia_pal_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
Expand All @@ -21,9 +21,14 @@ define(["require", "exports", "aurelia-templating", "aurelia-dependency-injectio
function CompileSpy(element, instruction) {
aurelia_logging_1.getLogger('compile-spy').info(element.toString(), instruction);
}
/**
* @internal
*/
CompileSpy.inject = function () {
return [aurelia_pal_1.DOM.Element, aurelia_templating_1.TargetInstruction];
};
CompileSpy = __decorate([
aurelia_templating_1.customAttribute('compile-spy'),
aurelia_dependency_injection_1.inject(aurelia_pal_1.DOM.Element, aurelia_templating_1.TargetInstruction)
aurelia_templating_1.customAttribute('compile-spy')
], CompileSpy);
return CompileSpy;
}());
Expand Down
43 changes: 42 additions & 1 deletion dist/amd/component-tester.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,16 @@
import { Aurelia, FrameworkConfiguration } from 'aurelia-framework';
/**
* Staging Component helpers to bring up basic setup for a test with ComponentTester
*/
export declare class StageComponent {
/**
* Create a ComponentTester for the given resources
*/
static withResources<T = any>(resources?: string | string[]): ComponentTester<T>;
/**
* Create a ComponentTester instance for the given html
*/
static inView<T = any>(html: string): ComponentTester<T>;
}
export declare class ComponentTester<T = any> {
bind: (bindingContext: {}) => Promise<void>;
Expand All @@ -14,15 +24,39 @@ export declare class ComponentTester<T = any> {
private bindingContext;
private rootView;
private host;
private stubbed;
private disposed;
constructor();
/**
* Apply standard configuration to an Aurelia instance.
*/
configure(aurelia: Aurelia): FrameworkConfiguration;
/**
* Register a configure function to be applied when this ComponentTester initializes
*/
bootstrap(configure: (aurelia: Aurelia) => FrameworkConfiguration): void;
/**
* Register resources to be applied globally when this ComponentTester initializes
*/
withResources(resources: string | string[]): ComponentTester<T>;
/**
* Register test application html to create, when this ComponentTester initializes
*/
inView(html: string): ComponentTester<T>;
/**
* Register an object to be used as root viewModel, when this ComponentTester initializes
*/
boundTo(bindingContext: {}): ComponentTester<T>;
manuallyHandleLifecycle(): ComponentTester<T>;
/**
* Initializes the test scene, with given bootstrapping function
*/
create(bootstrap: (configure: (aurelia: Aurelia) => Promise<void>) => Promise<void>): Promise<void>;
/**
* Dispose this ComponentTester instance, detaching the view and unbind all bindings
*/
dispose(): Element;
private _prepareLifecycle();
private _prepareLifecycle;
waitForElement(selector: string, options?: {
present?: boolean;
interval?: number;
Expand All @@ -33,4 +67,11 @@ export declare class ComponentTester<T = any> {
interval?: number;
timeout?: number;
}): Promise<NodeListOf<Element>>;
/**
* Register dependencies to be ignored while loading dependencies for custom element.
*
* Only works with dependencies registered via `<require from="...">` usage
* Dependencies are expected to be in absolute path
*/
ignoreDependencies(...deps: string[]): this;
}
197 changes: 178 additions & 19 deletions dist/amd/component-tester.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,97 @@
define(["require", "exports", "aurelia-templating", "./wait"], function (require, exports, aurelia_templating_1, wait_1) {
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());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
define(["require", "exports", "aurelia-templating", "aurelia-loader", "./wait"], function (require, exports, aurelia_templating_1, aurelia_loader_1, wait_1) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**
* Staging Component helpers to bring up basic setup for a test with ComponentTester
*/
var StageComponent = /** @class */ (function () {
function StageComponent() {
}
/**
* Create a ComponentTester for the given resources
*/
StageComponent.withResources = function (resources) {
if (resources === void 0) { resources = []; }
return new ComponentTester().withResources(resources);
};
/**
* Create a ComponentTester instance for the given html
*/
StageComponent.inView = function (html) {
return new ComponentTester().inView(html);
};
return StageComponent;
}());
exports.StageComponent = StageComponent;
var ComponentTester = /** @class */ (function () {
function ComponentTester() {
this.resources = [];
this.stubbed = new Set();
}
/**
* Apply standard configuration to an Aurelia instance.
*/
ComponentTester.prototype.configure = function (aurelia) {
return aurelia.use.standardConfiguration();
};
/**
* Register a configure function to be applied when this ComponentTester initializes
*/
ComponentTester.prototype.bootstrap = function (configure) {
this.configure = configure;
};
/**
* Register resources to be applied globally when this ComponentTester initializes
*/
ComponentTester.prototype.withResources = function (resources) {
this.resources = resources;
return this;
};
/**
* Register test application html to create, when this ComponentTester initializes
*/
ComponentTester.prototype.inView = function (html) {
this.html = html;
return this;
};
/**
* Register an object to be used as root viewModel, when this ComponentTester initializes
*/
ComponentTester.prototype.boundTo = function (bindingContext) {
this.bindingContext = bindingContext;
return this;
Expand All @@ -37,35 +100,89 @@ define(["require", "exports", "aurelia-templating", "./wait"], function (require
this._prepareLifecycle();
return this;
};
/**
* Initializes the test scene, with given bootstrapping function
*/
ComponentTester.prototype.create = function (bootstrap) {
var _this = this;
return bootstrap(function (aurelia) {
return Promise.resolve(_this.configure(aurelia)).then(function () {
if (_this.resources) {
aurelia.use.globalResources(_this.resources);
}
return aurelia.start().then(function () {
_this.host = document.createElement('div');
_this.host.innerHTML = _this.html;
document.body.appendChild(_this.host);
return aurelia.enhance(_this.bindingContext, _this.host).then(function () {
_this.rootView = aurelia.root;
_this.element = _this.host.firstElementChild;
if (aurelia.root.controllers.length) {
_this.viewModel = aurelia.root.controllers[0].viewModel;
return bootstrap(function (aurelia) { return __awaiter(_this, void 0, void 0, function () {
var loader, newLoader, resources, component_1, originalLoadtemplate_1, rootView;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
loader = aurelia.loader;
newLoader = new loader.constructor();
// remove reference to old loader
aurelia.container.unregister(aurelia_loader_1.Loader);
// register new loader in both property loader and container
// property for framework configuration
aurelia.loader = newLoader;
// container for templating resources/templating
aurelia.container.registerInstance(aurelia_loader_1.Loader, newLoader);
return [4 /*yield*/, this.configure(aurelia)];
case 1:
_a.sent();
if (this.resources) {
resources = Array.isArray(this.resources) ? this.resources : [this.resources];
aurelia.use.globalResources(resources.filter(function (r) { return typeof r === 'string' ? !_this.stubbed.has(r) : true; }));
}
return new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 0); });
});
});
if (this.stubbed.size > 0) {
component_1 = this;
originalLoadtemplate_1 = aurelia.loader.loadTemplate;
// overriding loadTemplate method of loader to filter stubbed dependencies
aurelia.loader.loadTemplate = function (url) {
return __awaiter(this, void 0, void 0, function () {
var entry;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, originalLoadtemplate_1.call(this, url)];
case 1:
entry = _a.sent();
entry.dependencies = entry.dependencies.filter(function (d) { return !component_1.stubbed.has(d.src); });
return [2 /*return*/, entry];
}
});
});
};
overrideHtmlBehaviorResourceLoad();
}
return [4 /*yield*/, aurelia.start()];
case 2:
_a.sent();
this.host = document.createElement('div');
this.host.innerHTML = this.html;
document.body.appendChild(this.host);
return [4 /*yield*/, aurelia.enhance(this.bindingContext, this.host)];
case 3:
_a.sent();
rootView = aurelia.root;
this.rootView = rootView;
this.element = this.host.firstElementChild;
if (rootView.controllers.length) {
this.viewModel = rootView.controllers[0].viewModel;
}
return [2 /*return*/, new Promise(function (resolve) { return setTimeout(function () { return resolve(); }, 0); })];
}
});
});
}); });
};
/**
* Dispose this ComponentTester instance, detaching the view and unbind all bindings
*/
ComponentTester.prototype.dispose = function () {
if (this.host === undefined || this.rootView === undefined) {
throw new Error('Cannot call ComponentTester.dispose() before ComponentTester.create()');
}
if (this.disposed === true) {
throw new Error('This ComponentTester instance has already been disposed. Did you call dispose() twice?');
}
this.disposed = true;
this.rootView.detached();
this.rootView.unbind();
if (this.stubbed.size > 0) {
restoreHtmlBehaviorResourceLoad();
}
return this.host.parentNode.removeChild(this.host);
};
ComponentTester.prototype._prepareLifecycle = function () {
Expand Down Expand Up @@ -110,7 +227,49 @@ define(["require", "exports", "aurelia-templating", "./wait"], function (require
var _this = this;
return wait_1.waitFor(function () { return _this.element.querySelectorAll(selector); }, options);
};
/**
* Register dependencies to be ignored while loading dependencies for custom element.
*
* Only works with dependencies registered via `<require from="...">` usage
* Dependencies are expected to be in absolute path
*/
ComponentTester.prototype.ignoreDependencies = function () {
var deps = [];
for (var _i = 0; _i < arguments.length; _i++) {
deps[_i] = arguments[_i];
}
for (var _a = 0, deps_1 = deps; _a < deps_1.length; _a++) {
var dep = deps_1[_a];
this.stubbed.add(dep);
}
return this;
};
return ComponentTester;
}());
exports.ComponentTester = ComponentTester;
var originalLoad = aurelia_templating_1.HtmlBehaviorResource.prototype.load;
var overrideHtmlBehaviorResourceLoad = function () {
if (aurelia_templating_1.HtmlBehaviorResource.prototype.load === originalLoad) {
aurelia_templating_1.HtmlBehaviorResource.prototype.load = function (container,
// tslint:disable-next-line:ban-types
target, loadContext, viewStrategy) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
// reset every load
// so that subsequent load of the same class in different tests won't get old view factory
// with modified dependencies
confirm.log(this, viewFactory);
debugger;
this.viewFactory = undefined;
return [2 /*return*/, originalLoad.call(this, container, target, loadContext, viewStrategy, true)];
});
});
};
}
};
var restoreHtmlBehaviorResourceLoad = function () {
if (aurelia_templating_1.HtmlBehaviorResource.prototype.load !== originalLoad) {
aurelia_templating_1.HtmlBehaviorResource.prototype.load = originalLoad;
}
};
});
2 changes: 1 addition & 1 deletion dist/amd/view-spy.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export declare class ViewSpy {
* Creates a new instance of ViewSpy.
*/
constructor();
private _log(lifecycleName, context?);
private _log;
/**
* Invoked when the target view is created.
* @param view The target view.
Expand Down
17 changes: 10 additions & 7 deletions dist/amd/wait.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
var __assign = (this && this.__assign) || Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
define(["require", "exports"], function (require, exports) {
"use strict";
Expand Down
Loading