packages/__tests__/src/3-runtime-html/template-compiler.hooks.spec.ts
import {
Registration
} from '@aurelia/kernel';
import {
CustomElement,
CustomElementDefinition,
} from '@aurelia/runtime-html';
import {
ITemplateCompilerHooks,
templateCompilerHooks,
TemplateCompilerHooks,
} from '@aurelia/template-compiler';
import {
assert,
createFixture,
TestContext
} from '@aurelia/testing';
describe('3-runtime-html/template-compiler.hooks.spec.ts', function () {
it('compiles with child hooks', async function () {
const { appHost, startPromise, tearDown } = createFixture(
`<my-el>`,
class App { },
[
CustomElement.define({
name: 'my-el',
template: '<input >',
dependencies: [TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input').setAttribute('value.bind', 'value');
}
})]
}, class MyEll {
public value = 'hello';
})
]
);
await startPromise;
assert.strictEqual(appHost.querySelector('input').value, 'hello');
await tearDown();
});
it('compiles with root hooks', async function () {
const { appHost, startPromise, tearDown } = createFixture(
`<my-el>`,
class App { },
[
CustomElement.define({
name: 'my-el',
template: '<input >',
dependencies: []
}, class MyEll {
public value = 'hello';
}),
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input')?.setAttribute('value.bind', 'value');
}
})
]
);
await startPromise;
assert.strictEqual(appHost.querySelector('input').value, 'hello');
await tearDown();
});
it('does not compiles with hooks from parent', async function () {
const { appHost, startPromise, tearDown } = createFixture(
`<parent>`,
class App { },
[
CustomElement.define({
name: 'parent',
template: '<child>',
dependencies: [
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input')?.setAttribute('value.bind', 'value');
}
}),
CustomElement.define({
name: 'child',
template: '<input>',
dependencies: [
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
assert.strictEqual(template.content.querySelector('input').getAttribute('value.bind'), null);
template.content.querySelector('input')?.setAttribute('value.bind', 'value2');
}
}),
]
}, class Child {
public value = 'hello';
public value2 = 'hello 2';
})
]
}, class Parent {
}),
]
);
await startPromise;
assert.strictEqual(appHost.querySelector('input').value, 'hello 2');
await tearDown();
});
it('gets all hooks registered in child', async function () {
const { appHost, startPromise, tearDown } = createFixture(
`<my-el>`,
class App { },
[
CustomElement.define({
name: 'my-el',
template: '<input >',
dependencies: [
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input').setAttribute('value.bind', 'value');
}
}),
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input').setAttribute('id.bind', 'value');
}
}),
]
}, class MyEll {
public value = 'hello';
})
]
);
await startPromise;
assert.strictEqual(appHost.querySelector('input').value, 'hello');
assert.strictEqual(appHost.querySelector('input').id, 'hello');
await tearDown();
});
it('gets all hooks registered in root', async function () {
const { appHost, startPromise, tearDown } = createFixture(
`<my-el>`,
class App { },
[
CustomElement.define({
name: 'my-el',
template: '<input >',
dependencies: [
]
}, class MyEll {
public value = 'hello';
}),
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input')?.setAttribute('value.bind', 'value');
}
}),
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input')?.setAttribute('id.bind', 'value');
}
}),
]
);
await startPromise;
assert.strictEqual(appHost.querySelector('input').value, 'hello');
assert.strictEqual(appHost.querySelector('input').id, 'hello');
await tearDown();
});
it('gets all hooks registered in root and child', async function () {
const { appHost, startPromise, tearDown } = createFixture(
`<my-el>`,
class App { },
[
CustomElement.define({
name: 'my-el',
template: '<input >',
dependencies: [
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input')?.setAttribute('data-id-1.bind', 'value');
}
}),
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input')?.setAttribute('data-id-2.bind', 'value');
}
}),
]
}, class MyEll {
public value = 'hello';
}),
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input')?.setAttribute('data-id-3.bind', 'value');
}
}),
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input')?.setAttribute('data-id-4.bind', 'value');
}
}),
]
);
await startPromise;
assert.strictEqual(appHost.querySelector('input').getAttribute('data-id-1'), 'hello');
assert.strictEqual(appHost.querySelector('input').getAttribute('data-id-2'), 'hello');
assert.strictEqual(appHost.querySelector('input').getAttribute('data-id-3'), 'hello');
assert.strictEqual(appHost.querySelector('input').getAttribute('data-id-4'), 'hello');
await tearDown();
});
it('calls hooks in child before root', async function () {
const { appHost, startPromise, tearDown } = createFixture(
`<my-el>`,
class App { },
[
CustomElement.define({
name: 'my-el',
template: '<input >',
dependencies: [
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input')?.setAttribute('data-id-2.bind', 'value');
}
}),
]
}, class MyEll {
public value = 'hello';
}),
TemplateCompilerHooks.define(class {
public compiling(template: HTMLTemplateElement) {
const input = template.content.querySelector('input');
input?.setAttribute('data-id-1.bind', 'value');
if (input) {
assert.strictEqual(input.getAttribute('data-id-2.bind'), 'value');
}
}
}),
]
);
await startPromise;
assert.strictEqual(appHost.querySelector('input').getAttribute('data-id-1'), 'hello');
assert.strictEqual(appHost.querySelector('input').getAttribute('data-id-2'), 'hello');
await tearDown();
});
it('works with decorator @templateCompilerHooks (no paren)', async function () {
@templateCompilerHooks
class Hooks {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input').setAttribute('value.bind', 'value');
}
}
const { appHost, startPromise, tearDown } = createFixture(
`<my-el>`,
class App { },
[
CustomElement.define({
name: 'my-el',
template: '<input >',
dependencies: [Hooks]
}, class MyEll {
public value = 'hello';
})
]
);
await startPromise;
assert.strictEqual(appHost.querySelector('input').value, 'hello');
await tearDown();
});
it('works with decorator @templateCompilerHooks() (with paren)', async function () {
@templateCompilerHooks()
class Hooks {
public compiling(template: HTMLTemplateElement) {
template.content.querySelector('input').setAttribute('value.bind', 'value');
}
}
const { appHost, startPromise, tearDown } = createFixture(
`<my-el>`,
class App { },
[
CustomElement.define({
name: 'my-el',
template: '<input >',
dependencies: [Hooks]
}, class MyEll {
public value = 'hello';
})
]
);
await startPromise;
assert.strictEqual(appHost.querySelector('input').value, 'hello');
await tearDown();
});
describe('[UNIT]', function () {
function createFixture() {
const ctx = TestContext.create();
const container = ctx.container;
const sut = ctx.templateCompiler;
return { ctx, container, sut };
}
it('invokes before compile hooks', function () {
const template = `<template></template>`;
const { container, sut } = createFixture();
let hookCallCount = 0;
container.register(Registration.instance(ITemplateCompilerHooks, {
compiling(template: HTMLElement) {
hookCallCount++;
template.setAttribute('data-hello', 'world');
}
}));
const definition = sut.compile(CustomElementDefinition.create({ name: 'lorem-ipsum', template }), container);
assert.strictEqual(hookCallCount, 1);
assert.strictEqual((definition.template as Element).getAttribute('data-hello'), 'world');
});
it('invokes all hooks', function () {
const template = `<template></template>`;
const { container, sut } = createFixture();
let hookCallCount = 0;
container.register(Registration.instance(ITemplateCompilerHooks, {
compiling(template: HTMLElement) {
hookCallCount++;
template.setAttribute('data-hello', 'world');
}
}));
container.register(Registration.instance(ITemplateCompilerHooks, {
compiling(template: HTMLElement) {
hookCallCount++;
template.setAttribute('data-world', 'hello');
}
}));
const definition = sut.compile(CustomElementDefinition.create({ name: 'lorem-ipsum', template }), container);
assert.strictEqual(hookCallCount, 2);
assert.strictEqual((definition.template as Element).getAttribute('data-hello'), 'world');
assert.strictEqual((definition.template as Element).getAttribute('data-world'), 'hello');
});
it('does not throw if the compile hooks does not have any hooks', function () {
const template = `<template></template>`;
const { container, sut } = createFixture();
container.register(Registration.instance(ITemplateCompilerHooks, {}));
assert.doesNotThrow(() => sut.compile(CustomElementDefinition.create({ name: 'lorem-ipsum', template }), container));
});
it('invokes hooks with resources semantic - only leaf', function () {
const template = `<template></template>`;
const { container, sut } = createFixture();
let hookCallCount = 0;
const createResolver = () => Registration.instance(ITemplateCompilerHooks, {
compiling(template: HTMLElement) {
hookCallCount++;
template.setAttribute('data-hello', 'world');
}
});
const middleContainer = container.createChild();
const leafContainer = middleContainer.createChild();
middleContainer.register(createResolver());
leafContainer.register(createResolver());
const definition = sut.compile(CustomElementDefinition.create({ name: 'lorem-ipsum', template }), leafContainer);
assert.strictEqual(hookCallCount, 1);
assert.strictEqual((definition.template as Element).getAttribute('data-hello'), 'world');
});
it('invokes hooks with resources semantic - leaf + root', function () {
const template = `<template></template>`;
const { container, sut } = createFixture();
let hookCallCount = 0;
const createResolver = (value: string) => Registration.instance(ITemplateCompilerHooks, {
compiling(template: HTMLElement) {
hookCallCount++;
template.setAttribute(`data-${value}`, value);
}
});
const middleContainer = container.createChild();
const leafContainer = middleContainer.createChild();
container.register(createResolver('root'));
middleContainer.register(createResolver('middle'));
leafContainer.register(createResolver('leaf'));
const definition = sut.compile(CustomElementDefinition.create({ name: 'lorem-ipsum', template }), leafContainer);
assert.strictEqual(hookCallCount, 2);
assert.strictEqual((definition.template as Element).getAttribute('data-root'), 'root');
assert.strictEqual((definition.template as Element).getAttribute('data-middle'), null);
assert.strictEqual((definition.template as Element).getAttribute('data-leaf'), 'leaf');
});
});
});