aurelia-templating
Version:
An extensible HTML templating engine supporting databinding, custom elements, attached behaviors and more.
197 lines (163 loc) • 7.61 kB
JavaScript
import './setup';
import {ViewCompiler} from '../src/view-compiler';
import {ViewResources} from '../src/view-resources';
import { HtmlBehaviorResource } from '../src/html-behavior';
import { BindingLanguage } from '../src/binding-language';
class MockBindingLanguage {
inspectAttribute(resources, elementName, attrName, attrValue) {
return { attrName, attrValue };
}
createAttributeInstruction(resources, element, info, existingInstruction) {
}
createLetExpressions(resources, element, existingExpressions) {
existingExpressions = existingExpressions || [];
existingExpressions.push({ createBinding() {} });
return existingExpressions;
}
inspectTextContent(resources, value) {
}
}
describe('ViewCompiler', () => {
var viewCompiler, language, resources;
beforeEach(() => {
language = new MockBindingLanguage();
viewCompiler = new ViewCompiler(language);
resources = new ViewResources(new ViewResources(), 'app.html');
});
describe('compile', () => {
it('compiles an empty template', () => {
var template = document.createElement('template'),
node = document.createDocumentFragment(),
factory;
template.appendChild(node);
factory = viewCompiler.compile(template, resources, null);
expect(factory).not.toBe(null);
});
it('throws on compile template within svg namespace', () => {
var template = document.createElementNS("http://www.w3.org/2000/svg", 'template'),
node = document.createDocumentFragment();
template.appendChild(node);
var compileFunc = () => {
viewCompiler.compile(template, resources, null)
};
expect(compileFunc).toThrow();
});
});
describe('compileNode', () => {
it('concatenates adjacent text nodes', () => {
var instructions = [], parentInjectorId = 'root', targetLightDOM = true,
node, parentNode;
parentNode = document.createElement('div');
node = document.createTextNode('Hello');
parentNode.appendChild(node);
parentNode.appendChild(document.createTextNode(' '));
parentNode.appendChild(document.createTextNode('World'));
parentNode.appendChild(document.createTextNode('!'));
spyOn(language, 'inspectTextContent');
node = viewCompiler._compileNode(node, resources, instructions, parentNode, parentInjectorId, targetLightDOM);
expect(language.inspectTextContent).toHaveBeenCalledWith(resources, 'Hello World!');
expect(node).toBe(null);
});
it('does not concatenate non-adjacent text nodes', () => {
var instructions = [], parentInjectorId = 'root', targetLightDOM = true,
node, parentNode, nextNode;
parentNode = document.createElement('div');
node = document.createTextNode('Hello');
parentNode.appendChild(node);
parentNode.appendChild(document.createTextNode(' '));
nextNode = document.createElement('em');
nextNode.textContent = 'World';
parentNode.appendChild(nextNode);
parentNode.appendChild(document.createTextNode('!'));
spyOn(language, 'inspectTextContent');
node = viewCompiler._compileNode(node, resources, instructions, parentNode, parentInjectorId, targetLightDOM);
expect(language.inspectTextContent).toHaveBeenCalledWith(resources, 'Hello ');
expect(node).toBe(nextNode);
});
it('clears class attributes containing interpolation expressions', () => {
var instructions = [], parentInjectorId = 'root', targetLightDOM = true,
node = document.createElement('div'), parentNode = null;
node.setAttribute('class', 'foo ${bar} baz');
spyOn(language, 'inspectAttribute').and.returnValue({
attrName: 'class',
expression: {attrToRemove: 'class'},
command: null
});
spyOn(language, 'createAttributeInstruction').and.returnValue({
attributes: {
'class': {
discrete: true,
attrToRemove: 'class'
}
}, attrName: 'class'
});
viewCompiler._compileNode(node, resources, instructions, parentNode, parentInjectorId, targetLightDOM);
expect(node.className).toBe('au-target');
});
it('does not clear class attributes with no interpolation expressions', () => {
var instructions = [], parentInjectorId = 'root', targetLightDOM = true,
node = document.createElement('div'), parentNode = null;
node.setAttribute('class', 'foo bar baz');
node.setAttribute('class.bind', 'someProperty');
spyOn(language, 'inspectAttribute').and.callFake((resources, attrName, attrValue) => {
if (attrName === 'class') {
return {attrName: 'class', expression: null, command: null}
} else {
return {attrName: 'class', expression: null, command: 'bind'};
}
});
spyOn(language, 'createAttributeInstruction').and.callFake((resources, node, info) => {
if (info.command) {
return {attributes: {'class': {discrete: true}}, attrName: 'class'};
} else {
return null;
}
});
viewCompiler._compileNode(node, resources, instructions, parentNode, parentInjectorId, targetLightDOM);
expect(node.className).toBe('foo bar baz au-target');
});
describe('<let/>', () => {
it('treats like normal element when there is no binding language', () => {
const fragment = createFragment('<div><let>');
let instructions = { };
spyOn(viewCompiler.bindingLanguage, 'createLetExpressions').and.callThrough();
viewCompiler._compileNode(fragment, resources, instructions, null, 'root', true);
expect(Object.keys(instructions).length).toBe(1, 'It should have had 1 instruction');
expect(viewCompiler.bindingLanguage.createLetExpressions).toHaveBeenCalled();
});
describe('backward compat', () => {
it('does nothing if there is custom <let/> element', () => {
let instructions = { };
const fragment = createFragment('<div><let foo="bar">');
const letMeta = new HtmlBehaviorResource();
resources.getElement = name => name === 'let' ? letMeta : null;
viewCompiler._compileNode(fragment, resources, instructions, null, 'root', true);
expect(Object.keys(instructions).length).toBe(1, 'It should have had 1 instruction with let ce');
let instruction;
for (let id in instructions) {
instruction = instructions[id];
break;
}
expect(instruction.letElement).toBe(false, 'It should have not been let Element instruction');
expect(instruction.behaviorInstructions[0].type).toBe(letMeta, 'It should have been the letMeta instance');
});
it('does nothing if there is no binding language implementation for <let/>', () => {
let instructions = { };
const fragment = createFragment('<div><let>');
resources.getBindingLanguage = () => Object.assign(
viewCompiler.bindingLanguage,
{
createLetExpressions: BindingLanguage.prototype.createLetExpressions
});
viewCompiler._compileNode(fragment, resources, instructions, null, 'root', true);
expect(Object.keys(instructions).length).toBe(0, 'It should have had no instruction');
});
});
});
});
function createFragment(html) {
const parser = document.createElement('div');
parser.innerHTML = `<template>${html}</template>`;
return parser.firstElementChild.content;
}
});