-
Notifications
You must be signed in to change notification settings - Fork 33
/
Copy pathcomponent.js
202 lines (161 loc) · 6.61 KB
/
component.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* eslint-env mocha */
import '../../lib/isorender/dom-shims';
import {expect, config} from 'chai';
import {SimpleApp} from '../fixtures/simple-app';
import {NestedApp, NestedChild} from '../fixtures/nested-app';
import {AttrsReflectionApp} from '../fixtures/attrs-reflection-app';
import {BadAttrsSchemaApp} from '../fixtures/bad-attrs-schema-app';
import nextAnimationFrame from './nextAnimationFrame';
import {compactHtml} from '../utils';
config.truncateThreshold = 0; // nicer deep equal errors
customElements.define(`nested-app`, NestedApp);
customElements.define(`nested-child`, NestedChild);
customElements.define(`simple-app`, SimpleApp);
customElements.define(`attrs-reflection-app`, AttrsReflectionApp);
describe(`Server-side component renderer`, function () {
it(`can register and create components with document.createElement`, function () {
const el = document.createElement(`simple-app`);
expect(el.state).to.eql({foo: `bar`, baz: `qux`});
});
it(`supports class instantiation`, function () {
const el = new SimpleApp();
expect(el.state).to.eql({foo: `bar`, baz: `qux`});
});
it(`renders a simple component`, async function () {
const el = new SimpleApp();
el.connectedCallback();
await nextAnimationFrame();
const html = el.innerHTML;
expect(html.toLowerCase()).to.contain(`<div class="foo">`);
expect(html).to.contain(`Value of foo: bar`);
expect(html).to.contain(`Foo capitalized: Bar`);
});
it(`renders updates`, async function () {
const el = new SimpleApp();
el.connectedCallback();
await nextAnimationFrame();
expect(el.textContent).to.contain(`Value of foo: bar`);
expect(el.textContent).to.contain(`Foo capitalized: Bar`);
el.update({foo: `new value`});
await nextAnimationFrame();
expect(el.textContent).to.contain(`Value of foo: new value`);
expect(el.textContent).to.contain(`Foo capitalized: New value`);
});
it(`renders nested components`, async function () {
const el = new NestedApp();
el.connectedCallback();
await nextAnimationFrame();
// check DOM structure
expect(el.childNodes).to.have.lengthOf(1);
expect(el.childNodes[0].className).to.equal(`nested-foo`);
expect(el.childNodes[0].childNodes).to.have.lengthOf(2);
const nestedChild = el.childNodes[0].childNodes[1];
expect(nestedChild.childNodes).to.have.lengthOf(1);
expect(nestedChild.childNodes[0].className).to.equal(`nested-foo-child`);
expect(nestedChild.childNodes[0].childNodes).to.have.lengthOf(3);
// check content/HTML output
const html = el.innerHTML;
expect(html.toLowerCase()).to.contain(`<div class="nested-foo">`);
expect(html).to.contain(`Nested app: test`);
expect(html.toLowerCase()).to.contain(`<div class="nested-foo-child">`);
expect(html).to.contain(`parent title: test`);
expect(html).to.contain(`animal: llama`);
expect(html).to.contain(`child animal: fox`);
});
it(`updates nested components`, async function () {
const el = new NestedApp();
el.connectedCallback();
await nextAnimationFrame();
const nestedChild = el.childNodes[0].childNodes[1];
expect(nestedChild.state.title).to.equal(`test`);
nestedChild.update({title: `meow`});
await nextAnimationFrame();
expect(el.state.title).to.equal(`meow`);
expect(el.innerHTML).to.contain(`Nested app: meow`);
expect(nestedChild.innerHTML).to.contain(`parent title: meow`);
el.update({title: `something else`});
await nextAnimationFrame();
expect(nestedChild.innerHTML).to.contain(`parent title: something else`);
});
it(`renders attributes`, async function () {
const el = new AttrsReflectionApp();
el.connectedCallback();
await nextAnimationFrame();
expect(el.innerHTML).to.equal(
compactHtml(`
<div class="attrs-reflection-app">
<p>str-attr: "hello"</p>
<p>bool-attr: false</p>
<p>number-attr: 0</p>
<p>json-attr: null</p>
</div>
`),
);
});
it(`reacts to attribute updates`, async function () {
const el = new AttrsReflectionApp();
el.connectedCallback();
await nextAnimationFrame();
el.setAttribute(`str-attr`, `world`);
el.setAttribute(`bool-attr`, `false`);
el.setAttribute(`number-attr`, `500843`);
el.setAttribute(`json-attr`, `{"foo": "bae"}`);
expect(el.attr(`str-attr`)).to.equal(`world`);
expect(el.attr(`bool-attr`)).to.equal(false);
expect(el.attr(`number-attr`)).to.equal(500843);
expect(el.attr(`json-attr`)).to.deep.equal({foo: `bae`});
await nextAnimationFrame();
expect(el.innerHTML).to.equal(
compactHtml(`
<div class="attrs-reflection-app">
<p>str-attr: "world"</p>
<p>bool-attr: false</p>
<p>number-attr: 500843</p>
<p>json-attr: {"foo":"bae"}</p>
</div>
`),
);
});
it(`handles malformed attribute updates`, async function () {
const el = new AttrsReflectionApp();
el.setAttribute(`str-attr`, `💩🤒🤢☠️ -> 👻🎉💐🎊😱😍`);
el.setAttribute(`bool-attr`, ``);
el.setAttribute(`number-attr`, `500843 abra cadabra`);
el.setAttribute(`json-attr`, `{"foo": not %%^ json`);
el.connectedCallback();
expect(el.attrs()).to.deep.equal({
'str-attr': `💩🤒🤢☠️ -> 👻🎉💐🎊😱😍`,
'bool-attr': true,
'number-attr': null,
'json-attr': null,
});
await nextAnimationFrame();
expect(el.innerHTML).to.equal(
compactHtml(`
<div class="attrs-reflection-app">
<p>str-attr: "💩🤒🤢☠️ -> 👻🎉💐🎊😱😍"</p>
<p>bool-attr: true</p>
<p>number-attr: null</p>
<p>json-attr: null</p>
</div>
`),
);
});
it(`throws error for invalid attr access`, async function () {
const el = document.createElement(`attrs-reflection-app`);
el.connectedCallback();
expect(() => el.attr(`bad-attr`)).to.throw(`${el}: attr 'bad-attr' is not defined in attrsSchema`);
});
it(`throws error for invalid value in an enum attr`, function () {
const el = document.createElement(`attrs-reflection-app`);
el.connectedCallback();
expect(() => el.setAttribute(`str-attr`, `boo!`)).to.throw(
`Invalid value: 'boo!' for attr: str-attr in element attrs-reflection-app. Only ('hello' | 'world' | '💩🤒🤢☠️ -> 👻🎉💐🎊😱😍') is valid.`,
);
});
it(`throws error if there is a malformed attrsSchema type`, function () {
expect(() => new BadAttrsSchemaApp()).to.throw(
`Invalid type: bool for attr: bad-attr in BadAttrsSchemaApp attrsSchema. Only ('string' | 'boolean' | 'number' | 'json') is valid.`,
);
});
});