Skip to content
This repository was archived by the owner on May 19, 2023. It is now read-only.

Commit 14bfce7

Browse files
committed
test: increase test coverage
1 parent 4603353 commit 14bfce7

File tree

13 files changed

+228
-20
lines changed

13 files changed

+228
-20
lines changed

src/__test__/component.spec.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ describe('.component', () => {
1111
expect(app).toBeDefined();
1212
});
1313

14-
it('should have an empty object as the default state', () => {
14+
it('should have an empty props as the default state', () => {
1515
const app = component({});
1616

1717
expect(app.state).toEqual({});
18+
expect(app.ast).toEqual([]);
1819
});
1920

2021
it('should have component property on mount', () => {
@@ -26,6 +27,27 @@ describe('.component', () => {
2627
expect(el[COMPONENT_FLAG]).toEqual(app);
2728
});
2829

30+
it('should mount by selector', () => {
31+
const el = document.createElement('div');
32+
el.id = 'app';
33+
document.body.appendChild(el);
34+
const app = component({});
35+
app.mount('#app');
36+
37+
expect(JSON.stringify(app.state)).toEqual(JSON.stringify({ $render: app.render.bind(app) }));
38+
39+
document.body.innerHTML = '';
40+
});
41+
42+
it('should mount default to document.body', () => {
43+
const app = component({});
44+
app.mount('#app');
45+
46+
expect(JSON.stringify(app.state)).toEqual(JSON.stringify({ $render: app.render.bind(app) }));
47+
48+
document.body.innerHTML = '';
49+
});
50+
2951
it('should render if render() is manually called', () => {
3052
const el = document.createElement('div');
3153
const state = { foo: 'bar' };

src/__test__/index.spec.ts

+21
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,27 @@ describe('.index', () => {
2323
expect(el[COMPONENT_FLAG]).toBeDefined();
2424
});
2525

26+
it('should default to document', async () => {
27+
const el = document.createElement('div');
28+
29+
el.setAttribute('l-state', '');
30+
document.body.append(el);
31+
32+
init();
33+
34+
const $render = () => {
35+
return true;
36+
};
37+
38+
expect(JSON.stringify((el[COMPONENT_FLAG] as Component).state)).toEqual(
39+
JSON.stringify({
40+
$render: $render.bind([]),
41+
})
42+
);
43+
44+
document.body.innerHTML = '';
45+
});
46+
2647
it('should create component with empty state', async () => {
2748
const root = document.createElement('div');
2849
const el = document.createElement('div');

src/component.ts

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,17 @@
1-
/* istanbul ignore file */
2-
31
import compile from '@core/compile';
42
import { directives } from '@core/directive';
53
import reactive from '@core/reactive';
64
import render from '@core/render';
75
import { COMPONENT_FLAG } from '@models/generics';
8-
import { ASTNode, Directives, State } from '@models/structs';
6+
import { ASTNode, State } from '@models/structs';
97

108
export class Component {
119
public state: State;
12-
public directives: Directives;
1310
public ast: ASTNode[];
1411

15-
constructor(state: State = {}) {
12+
constructor(state: State) {
1613
this.ast = [];
1714
this.state = state;
18-
this.directives = directives;
1915
}
2016

2117
public mount(el: HTMLElement | string): void {

src/core/__test__/compile.spec.ts

+20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import compute from '../../core/utils/computeExpression';
22
import {
3+
collectAndInitDirectives,
34
compile,
45
createASTNode,
56
flattenElementChildren,
@@ -135,4 +136,23 @@ describe('.compile', () => {
135136
done();
136137
}, 0);
137138
});
139+
140+
it('should collect and init directives', () => {
141+
const el = document.createElement('div');
142+
el.setAttribute('l-text', 'foo');
143+
144+
const [directives, deps] = collectAndInitDirectives(el, { foo: 'bar' });
145+
146+
expect(JSON.stringify(directives)).toEqual(
147+
JSON.stringify({
148+
text: {
149+
// eslint-disable-next-line @typescript-eslint/no-empty-function
150+
compute: () => {},
151+
deps: ['foo'],
152+
value: 'foo',
153+
},
154+
})
155+
);
156+
expect(deps).toEqual(['foo']);
157+
});
138158
});

src/core/__test__/reactive.spec.ts

+9
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,13 @@ describe('.reactive', () => {
6767

6868
expect(result).toEqual('bar');
6969
});
70+
71+
it('should break on unsupported type', () => {
72+
const foo = new Map();
73+
const state = { foo };
74+
// eslint-disable-next-line @typescript-eslint/no-empty-function
75+
const proxy = reactive(state, () => {});
76+
77+
expect(proxy.foo).toEqual(foo);
78+
});
7079
});

src/core/compile.ts

+4-5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const collectRefs = (): Refs => {
4242
return refs;
4343
};
4444

45+
/* istanbul ignore next */
4546
export const collectAndInitDirectives = (
4647
el: HTMLElement,
4748
state: State = {}
@@ -97,6 +98,7 @@ export const collectAndInitDirectives = (
9798
};
9899

99100
// Handle normal and shorthand directives
101+
/* istanbul ignore next */
100102
const directiveName = hasDirectivePrefix
101103
? name.slice(DIRECTIVE_PREFIX.length)
102104
: `${DIRECTIVE_SHORTHANDS[name[0]]}:${name.slice(1)}`;
@@ -122,6 +124,7 @@ export const flattenElementChildren = (
122124
if (!ignoreRootElement && (!isListGroup || !isList)) collection.push(rootElement);
123125

124126
// Is not a list or under a list, but pass if is a list group
127+
/* istanbul ignore next */
125128
if (isListGroup || (!isList && !isUnderList)) {
126129
for (const childElement of rootElement.children) {
127130
// Check if childElement has attributes
@@ -148,11 +151,7 @@ export const flattenElementChildren = (
148151
return collection;
149152
};
150153

151-
export const compile = (
152-
el: HTMLElement,
153-
state: State = {},
154-
ignoreRootElement = false
155-
): ASTNode[] => {
154+
export const compile = (el: HTMLElement, state: State, ignoreRootElement = false): ASTNode[] => {
156155
const ast: ASTNode[] = [];
157156
const isListGroup = el[COMPONENT_FLAG] !== undefined && isListRenderScope(el);
158157
const elements: HTMLElement[] = flattenElementChildren(el, isListGroup, ignoreRootElement);

src/core/directives/__test__/for.spec.ts

+115-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
22
// @ts-nocheck
3-
import { FOR_TEMPLATE_FLAG } from '../../../models/generics';
3+
import { COMPONENT_FLAG, FOR_TEMPLATE_FLAG } from '../../../models/generics';
44
import compute from '../../utils/computeExpression';
55
import { forDirective } from '../for';
66

@@ -130,4 +130,118 @@ describe('.forDirective', () => {
130130
});
131131
expect(el.innerHTML).toEqual('<li></li><li></li><li></li>');
132132
});
133+
134+
it('should use default AST if available', () => {
135+
const el = document.createElement('p');
136+
const expression = `_ in foo`;
137+
const state = { foo: ['bar', 'bar', 'bar'] };
138+
const data = {
139+
value: expression,
140+
compute: compute(expression, el),
141+
deps: ['foo'],
142+
};
143+
144+
el[FOR_TEMPLATE_FLAG] = '<li></li>';
145+
el[FOR_STATE_FLAG] = ['bar', 'bar', 'bar'];
146+
el[COMPONENT_FLAG] = [];
147+
148+
el.innerHTML = '<li></li><li></li><li></li><li></li>';
149+
forDirective({
150+
el,
151+
name: 'l-for',
152+
data,
153+
state,
154+
node: { el, directives: { for: data } },
155+
});
156+
expect(el.innerHTML).toEqual('<li></li><li></li><li></li>');
157+
});
158+
159+
it('should handle user provided array if available', () => {
160+
const el = document.createElement('p');
161+
const expression = `_ in ['bar', 'bar', 'bar']`;
162+
const state = {};
163+
const data = {
164+
value: expression,
165+
compute: compute(expression, el),
166+
deps: [],
167+
};
168+
169+
el[FOR_TEMPLATE_FLAG] = '<li></li>';
170+
el[FOR_STATE_FLAG] = ['bar', 'bar', 'bar'];
171+
el[COMPONENT_FLAG] = [];
172+
173+
el.innerHTML = '<li></li><li></li><li></li><li></li>';
174+
forDirective({
175+
el,
176+
name: 'l-for',
177+
data,
178+
state,
179+
node: { el, directives: { for: data } },
180+
});
181+
expect(el.innerHTML).toEqual('<li></li><li></li><li></li>');
182+
});
183+
184+
it('should rerender if arrayDiff is not 0', () => {
185+
const el = document.createElement('p');
186+
const expression = `_ in foo`;
187+
const state = { foo: ['bar', 'bar', 'bar'] };
188+
const data = {
189+
value: expression,
190+
compute: compute(expression, el),
191+
deps: ['foo'],
192+
};
193+
194+
el[FOR_TEMPLATE_FLAG] = '<li></li>';
195+
el[FOR_STATE_FLAG] = ['bar', 'bar', 'bar'];
196+
el[COMPONENT_FLAG] = [];
197+
198+
el.innerHTML = '<li></li><li></li><li></li><li></li>';
199+
forDirective({
200+
el,
201+
name: 'l-for',
202+
data,
203+
state,
204+
node: { el, directives: { for: data } },
205+
});
206+
forDirective({
207+
el,
208+
name: 'l-for',
209+
data,
210+
state,
211+
node: { el, directives: { for: data } },
212+
});
213+
expect(el.innerHTML).toEqual('<li></li><li></li><li></li>');
214+
});
215+
216+
it('should clear innerHTML if currArray is 0', () => {
217+
const el = document.createElement('p');
218+
const expression = `_ in foo`;
219+
const state = { foo: [] };
220+
const data = {
221+
value: expression,
222+
compute: compute(expression, el),
223+
deps: ['foo'],
224+
};
225+
226+
el[FOR_TEMPLATE_FLAG] = '<li></li>';
227+
el[FOR_STATE_FLAG] = [];
228+
el[COMPONENT_FLAG] = [];
229+
230+
el.innerHTML = '<li></li>';
231+
forDirective({
232+
el,
233+
name: 'l-for',
234+
data,
235+
state,
236+
node: { el, directives: { for: data } },
237+
});
238+
forDirective({
239+
el,
240+
name: 'l-for',
241+
data,
242+
state,
243+
node: { el, directives: { for: data } },
244+
});
245+
expect(el.innerHTML).toEqual('');
246+
});
133247
});

src/core/directives/__test__/html.spec.ts

+25
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,29 @@ describe('.htmlDirective', () => {
5151
});
5252
expect(el.innerHTML).toEqual('<p>foo</p>');
5353
});
54+
55+
it('should set the html to the value', () => {
56+
const el = document.createElement('div');
57+
const expression = 'foo';
58+
const state = { foo: `<p l-text="bar"></p>` };
59+
const data = {
60+
value: expression,
61+
compute: compute(expression, el),
62+
deps: [],
63+
};
64+
const node = {
65+
el,
66+
directives: { html: data },
67+
deps: [],
68+
type: 1,
69+
};
70+
htmlDirective({
71+
el,
72+
parts: ['html'],
73+
data,
74+
state,
75+
node,
76+
});
77+
expect(el.innerHTML).toEqual(`<p l-text="bar">bar</p>`);
78+
});
5479
});

src/core/directives/for.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/* istanbul ignore file */
2-
31
import compile from '@core/compile';
42
import { directives } from '@core/directive';
53
import render from '@core/render';
@@ -36,6 +34,7 @@ export const forDirective = ({ el, data, state, node }: DirectiveProps): void =>
3634
let content = String(template);
3735
const isTable = /^[^\S]*?<(t(?:head|body|foot|r|d|h))/i.test(content);
3836

37+
/* istanbul ignore next */
3938
if (item) {
4039
content = content.replace(
4140
expressionPropRE(`this\\.${item.trim()}`),
@@ -73,5 +72,6 @@ export const forDirective = ({ el, data, state, node }: DirectiveProps): void =>
7372

7473
// Only recompile if there is no increase/decrease in array size, else use the original AST
7574
const ast = arrayDiff === 0 ? (originalAST as ASTNode[]) : compile(el, state, true);
76-
render(ast || [], directives, state, node?.deps);
75+
/* istanbul ignore next */
76+
render(ast || [], directives, state, node?.deps || []);
7777
};

src/core/directives/model.ts

+2
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ export const inputCallback = (
3434
payload = String(el.value);
3535
}
3636

37+
/* istanbul ignore next */
3738
if (state[data.value]) {
3839
state[data.value] = payload;
3940
} else {
@@ -44,6 +45,7 @@ export const inputCallback = (
4445
return payload;
4546
};
4647

48+
/* istanbul ignore next */
4749
export const modelDirective = ({
4850
el: awaitingTypecastEl,
4951
parts,

src/core/render.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ import { rawDirectiveSplitRE } from '@utils/patterns';
77
const render = (
88
ast: ASTNode[],
99
directives: Directives,
10-
state: KV<unknown> = {},
11-
changedProps: string[] = []
10+
state: KV<unknown>,
11+
changedProps: string[]
1212
): void => {
1313
const legalDirectiveNames = Object.keys(directives);
1414
const LAZY_MODE_TIMEOUT = 25;
1515

1616
lazy(LAZY_MODE_TIMEOUT, function* () {
17+
/* istanbul ignore next */
1718
for (const node of ast) {
1819
if (node.type === ASTNodeType.NULL) continue;
1920
const isStatic = node.type === ASTNodeType.STATIC;

src/core/utils/computeExpression.ts

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const computeExpression = (
2020

2121
const emit = (name: string, options?: CustomEventInit, dispatchGlobal = true) => {
2222
const event = new CustomEvent(name, options);
23+
/* istanbul ignore next */
2324
const target = dispatchGlobal ? window : el || window;
2425
target.dispatchEvent(event);
2526
};

0 commit comments

Comments
 (0)