Right now I absolutely despise unit tests! It’s time to ask for advice. I have a “working” solution but it’s a hack and I can’t use any of the waitForElement helpers which I really want to use.
I need to do some work to create the staged component and I’m trying to have a base method that does this for me. This is that code on my base class. You’ll see I can return either the created control (hacked with a timeout) or return the staged prior to create. I also have to cast my component as any, but that is a whole different problem, maybe just a bad definition in the testing module
public static createRendererComponent(json: IControl[], create: boolean = true): Promise<ComponentTester> {
return new Promise<ComponentTester>((resolve) => {
let viewModel = new ControlRenderer();
this.CreateControlSet(json).then((controlSet) => {
viewModel.controlSet = controlSet;
let component: any = StageComponent
.withResources("custom-controls/control-renderer")
.inView("<control-renderer control-set.bind='controlSet'></control-renderer>")
.boundTo(viewModel);
component.bootstrap((aurelia: Aurelia) => {
aurelia.use
.standardConfiguration()
.feature("custom-controls")
.plugin("aurelia-validation")
});
if (create) {
component.create(bootstrap).then(() => {
setTimeout(() => {
resolve(component);
}, 100); // This doesn't work without a timeout!
});
} else {
resolve(component);
}
});
});
}
public static CreateControlSet(json: IControl[]): Promise<ControlSet> {
return new Promise<ControlSet>((resolve) => {
let container = new Container();
let customControlFactory: ControlFactory = container.get(ControlFactory);
configureBindingLanguage({ container });
configureValidation({ container });
let controlFactoryOptions: ControlFactoryOptions = {
controls: <IControl[]>json,
viewStateMode: ViewStateMode.design,
log: false
};
customControlFactory.loadControls(controlFactoryOptions, this).then((controlSet) => {
resolve(controlSet);
});
});
}
My tests look like this
import { ComponentTester } from "aurelia-testing";
import { TestBase } from "../test-base";
describe("String Control - valid", () => {
let component: ComponentTester;
let json = [
{
type: "string",
value: "Testing value",
id: "st01",
validation: {
required: true
}
}
];
beforeEach((done) => {
TestBase.CreateRendererComponent(json).then((c) => {
component = c;
done();
});
});
it("content value", (done) => {
let textbox = document.querySelector<HTMLInputElement>(".e-textbox");
expect(textbox.value).toEqual(json[0].value);
done();
});
it("control exists", (done) => {
const controlElement = document.querySelector(`#${json[0].id}_${json[0].type}_wrapper`);
expect(controlElement !== null).toBe(true);
done();
});
afterEach((done) => {
component.dispose();
done();
});
});
Those tests actually run and run properly. I don’t want that setTimeout in create, but when I do a component.create(bootstrap).then(() => { ...});
I get promise errors. This is the test with the error. In this case by passing false to my CreateRendererComponent it is up to the test to call component.create(). No matter what, I seem to have an issue with component.create not fully finishing before the then()
import { bootstrap } from 'aurelia-bootstrapper';
import { ComponentTester, waitForDocumentElement } from "aurelia-testing";
import { TestBase } from "../test-base";
describe("Label Control", () => {
let component: ComponentTester;
let json = [
{
type: "label",
value: "Custom label <b>Only visible at all modes</b> <span style=\"color:red;\">Custom HTML</span>",
id: "lbl0"
}
];
beforeEach((done) => {
TestBase.CreateRendererComponent(json, false).then((c) => {
component = c;
done();
});
});
it("content value", (done) => {
component.create(bootstrap).then(() => {
component.waitForElement(".label-control-content").then((element) => {
expect(element.innerHTML).toEqual(json[0].value);
done();
})
});
afterEach(() => {
component.dispose();
});
});