appblocks
Version:
A lightweight javascript library for building micro apps for the front-end.
308 lines (268 loc) • 9.96 kB
JavaScript
import { AppBlock } from 'src/core.js';
import { createMockAppBlockConfig, createMockTemplate, resetDOM } from 'tests/fixtures/mockData.js';
describe('Filters in attributes', () => {
afterEach(() => resetDOM());
test('should apply filter in class attribute', () => {
const tpl = createMockTemplate('<div class="{data.status|statusClass}"></div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { status: 'active' },
filters: {
statusClass: (comp, v) => `status-${v}`
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('class')).toBe('status-active');
});
test('should apply filter in id attribute', () => {
const tpl = createMockTemplate('<div id="{data.name|slugify}"></div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { name: 'My Element' },
filters: {
slugify: (comp, v) => v.toLowerCase().replace(/\s+/g, '-')
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('id')).toBe('my-element');
});
test('should apply filter in style attribute', () => {
const tpl = createMockTemplate('<div style="border: 5px solid {data.color|hexColor}"></div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { color: 'red' },
filters: {
hexColor: (comp, v) => {
const colors = { red: '#FF0000', blue: '#0000FF', green: '#00FF00' };
return colors[v] || '#000000';
}
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('style')).toBe('border: 5px solid #FF0000');
});
test('should apply filter in src attribute', () => {
const tpl = createMockTemplate('<img src="{data.image|cdn}" alt="Test">');
const config = createMockAppBlockConfig({
template: tpl,
data: { image: 'photo.jpg' },
filters: {
cdn: (comp, v) => `https://cdn.example.com/${v}`
}
});
const app = new AppBlock(config);
const img = app.el.querySelector('img');
expect(img.getAttribute('src')).toBe('https://cdn.example.com/photo.jpg');
});
test('should apply filter in alt attribute', () => {
const tpl = createMockTemplate('<img src="test.jpg" alt="{data.name|capitalize}">');
const config = createMockAppBlockConfig({
template: tpl,
data: { name: 'my image' },
filters: {
capitalize: (comp, v) => v.charAt(0).toUpperCase() + v.slice(1)
}
});
const app = new AppBlock(config);
const img = app.el.querySelector('img');
expect(img.getAttribute('alt')).toBe('My image');
});
test('should apply filter in href attribute', () => {
const tpl = createMockTemplate('<a href="{data.page|urlify}">Link</a>');
const config = createMockAppBlockConfig({
template: tpl,
data: { page: 'about us' },
filters: {
urlify: (comp, v) => `/${v.replace(/\s+/g, '-').toLowerCase()}`
}
});
const app = new AppBlock(config);
const link = app.el.querySelector('a');
expect(link.getAttribute('href')).toBe('/about-us');
});
test('should apply filter in data-* attributes', () => {
const tpl = createMockTemplate('<div data-value="{data.num|double}"></div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { num: 5 },
filters: {
double: (comp, v) => v * 2
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('data-value')).toBe('10');
});
test('should apply filter in title attribute', () => {
const tpl = createMockTemplate('<div title="{data.tooltip|wrap}">Hover me</div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { tooltip: 'info' },
filters: {
wrap: (comp, v) => `Tooltip: ${v}`
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('title')).toBe('Tooltip: info');
});
test('should apply filter in value attribute', () => {
const tpl = createMockTemplate('<input type="text" value="{data.text|trim}">');
const config = createMockAppBlockConfig({
template: tpl,
data: { text: ' hello ' },
filters: {
trim: (comp, v) => String(v).trim()
}
});
const app = new AppBlock(config);
const input = app.el.querySelector('input');
expect(input.getAttribute('value')).toBe('hello');
});
test('should apply filter in placeholder attribute', () => {
const tpl = createMockTemplate('<input type="text" placeholder="{data.hint|uppercase}">');
const config = createMockAppBlockConfig({
template: tpl,
data: { hint: 'enter name' },
filters: {
uppercase: (comp, v) => String(v).toUpperCase()
}
});
const app = new AppBlock(config);
const input = app.el.querySelector('input');
expect(input.getAttribute('placeholder')).toBe('ENTER NAME');
});
test('should chain filters in attributes', () => {
const tpl = createMockTemplate('<div class="{data.status|validate|prefix}"></div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { status: 'ok' },
filters: {
validate: (comp, v) => v || 'unknown',
prefix: (comp, v) => `state-${v}`
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('class')).toBe('state-ok');
});
test('should apply multiple filters to different attributes on same element', () => {
const tpl = createMockTemplate(`
<div
class="{data.type|typeClass}"
id="{data.name|slugify}"
data-count="{data.num|double}">
</div>
`);
const config = createMockAppBlockConfig({
template: tpl,
data: { type: 'primary', name: 'My Div', num: 10 },
filters: {
typeClass: (comp, v) => `type-${v}`,
slugify: (comp, v) => v.toLowerCase().replace(/\s+/g, '-'),
double: (comp, v) => v * 2
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('class')).toBe('type-primary');
expect(div.getAttribute('id')).toBe('my-div');
expect(div.getAttribute('data-count')).toBe('20');
});
test('should apply filters in attributes within c-for', () => {
const tpl = createMockTemplate(`
<div c-for="item in data.items" class="{item.status|statusClass}">
{item.name}
</div>
`);
const config = createMockAppBlockConfig({
template: tpl,
data: {
items: [
{ name: 'Item 1', status: 'active' },
{ name: 'Item 2', status: 'inactive' }
]
},
filters: {
statusClass: (comp, v) => `item-${v}`
}
});
const app = new AppBlock(config);
const divs = app.el.querySelectorAll('div');
expect(divs[0].getAttribute('class')).toBe('item-active');
expect(divs[1].getAttribute('class')).toBe('item-inactive');
});
test('should handle mixed static and filtered content in attributes', () => {
const tpl = createMockTemplate('<div class="base {data.extra|modifier}"></div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { extra: 'special' },
filters: {
modifier: (comp, v) => `mod-${v}`
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('class')).toBe('base mod-special');
});
test('should apply filter to multiple placeholders in same attribute', () => {
const tpl = createMockTemplate('<div style="width: {data.width|px}; height: {data.height|px}"></div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { width: 100, height: 200 },
filters: {
px: (comp, v) => `${v}px`
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('style')).toBe('width: 100px; height: 200px');
});
test('should update filtered attributes when data changes', () => {
const tpl = createMockTemplate('<div class="{data.theme|themeClass}"></div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { theme: 'light' },
filters: {
themeClass: (comp, v) => `theme-${v}`
}
});
const app = new AppBlock(config);
let div = app.el.querySelector('div');
expect(div.getAttribute('class')).toBe('theme-light');
// Update data
app.data.theme = 'dark';
app.render();
div = app.el.querySelector('div');
expect(div.getAttribute('class')).toBe('theme-dark');
});
test('should apply filter in aria-label attribute', () => {
const tpl = createMockTemplate('<button aria-label="{data.action|capitalize}">Click</button>');
const config = createMockAppBlockConfig({
template: tpl,
data: { action: 'submit form' },
filters: {
capitalize: (comp, v) => v.charAt(0).toUpperCase() + v.slice(1)
}
});
const app = new AppBlock(config);
const button = app.el.querySelector('button');
expect(button.getAttribute('aria-label')).toBe('Submit form');
});
test('should handle filter that accesses comp.data in attribute', () => {
const tpl = createMockTemplate('<div class="{data.base|withPrefix}"></div>');
const config = createMockAppBlockConfig({
template: tpl,
data: { base: 'value', prefix: 'custom' },
filters: {
withPrefix: (comp, v) => `${comp.data.prefix}-${v}`
}
});
const app = new AppBlock(config);
const div = app.el.querySelector('div');
expect(div.getAttribute('class')).toBe('custom-value');
});
});