1 |
|
2 |
|
3 |
|
4 | const K = require('@kmu/kcore')
|
5 |
|
6 |
|
7 | const JSDOM = require('jsdom').JSDOM
|
8 | const dom = new JSDOM('')
|
9 | const window = dom.window
|
10 | const document = window.document
|
11 |
|
12 | global.window = window
|
13 | global.document = document
|
14 |
|
15 | window.K = K
|
16 |
|
17 | require('../src/KNateHtmlStatic')
|
18 | require('../src/KNateAbstractDom')
|
19 | require('../src/KNateHtmlDom')
|
20 | require('should')
|
21 |
|
22 |
|
23 | K.Elem = {
|
24 | isElem: function (o)
|
25 | {
|
26 | if (o.elem) {
|
27 |
|
28 | return false;
|
29 | } else {
|
30 |
|
31 | return true;
|
32 | }
|
33 | },
|
34 |
|
35 | isText: function (o)
|
36 | {
|
37 | return false;
|
38 | }
|
39 | }
|
40 |
|
41 |
|
42 | K.updateProperty = function (o, key, newContent) {
|
43 | if (o[key] != newContent) {
|
44 | o[key] = newContent
|
45 | }
|
46 | }
|
47 |
|
48 | function TestNateHtml(domMode) {
|
49 | let nRoot = null
|
50 | let nRootDiv = null
|
51 | let title = null
|
52 | let titlePrefix = null
|
53 |
|
54 | if (domMode) {
|
55 |
|
56 | nRoot = K.NateHtmlDom(document.body)
|
57 | title = 'NateHtmlDom'
|
58 | titlePrefix = 'DOM: '
|
59 | } else {
|
60 |
|
61 | nRoot = K.NateHtmlElem()
|
62 | title = 'NateHtmlStatic'
|
63 | titlePrefix = 'Static: '
|
64 | }
|
65 |
|
66 | const _suite = (title, cb) => {
|
67 | suite(titlePrefix + title, cb)
|
68 | }
|
69 |
|
70 | const _reset = () => {
|
71 |
|
72 | if (domMode) {
|
73 | nRoot.deleteAllChildren()
|
74 | } else {
|
75 | nRoot = K.NateHtmlElem()
|
76 | }
|
77 | }
|
78 |
|
79 | const _verifyResultHtml = (expectedHtml) => {
|
80 | if (domMode) {
|
81 | nRoot.elem.innerHTML.should.be.eql(expectedHtml)
|
82 | } else {
|
83 | nRoot.render().should.be.eql(expectedHtml)
|
84 | }
|
85 | }
|
86 |
|
87 | _suite('Simple HTML document', () => {
|
88 | test('Empty HTML (set function)', () => {
|
89 | const nHtml = K.NateHtmlDocument()
|
90 | nHtml.set('lang', 'pl')
|
91 | const htmlAsText = nHtml.render();
|
92 | htmlAsText.should.be.eql('<!DOCTYPE html><html lang="pl"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=Edge"/><meta name="viewport" content="width=device-width, initial-scale=1"><style></style></head><body></body></html>')
|
93 | });
|
94 |
|
95 | test('Empty HTML (init list)', () => {
|
96 | const nHtml = K.NateHtmlDocument(null, {lang: 'la'})
|
97 | const htmlAsText = nHtml.render();
|
98 | htmlAsText.should.be.eql('<!DOCTYPE html><html lang="la"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=Edge"/><meta name="viewport" content="width=device-width, initial-scale=1"><style></style></head><body></body></html>')
|
99 | });
|
100 | })
|
101 |
|
102 | _suite(title, () => {
|
103 | _suite('Namespace', () => {
|
104 | let nElem1 = null
|
105 | let nElem2 = null
|
106 |
|
107 | let namespaceA = null
|
108 | let namespaceB = null
|
109 | let namespaceC = null
|
110 |
|
111 | const _testSetter = (nNode, key, value) => {
|
112 | nNode[key] = value
|
113 | }
|
114 |
|
115 | test('Init', () => {
|
116 | _reset()
|
117 |
|
118 |
|
119 | namespaceA = nRoot.getNamespace().createExtension('testA')
|
120 | namespaceB = nRoot.getNamespace().createExtension('testB')
|
121 |
|
122 | namespaceA.addSetter('testKeyA1', _testSetter)
|
123 | namespaceA.addSetter('testKeyA2', _testSetter)
|
124 |
|
125 | namespaceB.addSetter('testKeyB1', _testSetter)
|
126 | namespaceB.addSetter('testKeyB2', _testSetter)
|
127 | })
|
128 |
|
129 | test('Create element via namespace directly', () => {
|
130 | _reset()
|
131 |
|
132 |
|
133 | nElem1 = namespaceA.createNate(nRoot)
|
134 | nElem1.set('testKeyA1', 'valueA1.1')
|
135 | nElem1.set('testKeyA2', 'valueA2.1')
|
136 |
|
137 |
|
138 | nElem1.getNamespace().should.be.eql(namespaceA)
|
139 | nElem1.testKeyA1.should.be.eql('valueA1.1')
|
140 | nElem1.testKeyA2.should.be.eql('valueA2.1')
|
141 | })
|
142 |
|
143 | test('Inherit namespace from parent', () => {
|
144 |
|
145 | nElem2 = nElem1.newDiv()
|
146 | nElem2.getNamespace().should.be.eql(namespaceA)
|
147 |
|
148 |
|
149 | nElem1.getNamespace().should.be.eql(namespaceA)
|
150 | nElem1.testKeyA1.should.be.eql('valueA1.1')
|
151 | nElem1.testKeyA2.should.be.eql('valueA2.1')
|
152 |
|
153 |
|
154 | nElem2.set('testKeyA1', 'valueA1.2')
|
155 | nElem2.set('testKeyA2', 'valueA2.2')
|
156 |
|
157 | nElem2.testKeyA1.should.be.eql('valueA1.2')
|
158 | nElem2.testKeyA2.should.be.eql('valueA2.2')
|
159 | })
|
160 |
|
161 | test('Assign namespace directly', () => {
|
162 |
|
163 | nElem1.assignNamespace(namespaceB)
|
164 | nElem1.getNamespace().should.be.eql(namespaceB)
|
165 | nElem2.getNamespace().should.be.eql(namespaceA)
|
166 |
|
167 |
|
168 | nElem1.set('testKeyB1', 'valueB1.3')
|
169 | nElem1.set('testKeyB2', 'valueB2.3')
|
170 |
|
171 | nElem1.testKeyA1.should.be.eql('valueA1.1')
|
172 | nElem1.testKeyA2.should.be.eql('valueA2.1')
|
173 | nElem1.testKeyB1.should.be.eql('valueB1.3')
|
174 | nElem1.testKeyB2.should.be.eql('valueB2.3')
|
175 |
|
176 |
|
177 | nElem2.set('testKeyA1', 'valueA1.4')
|
178 | nElem2.set('testKeyA2', 'valueA2.4')
|
179 |
|
180 | nElem2.testKeyA1.should.be.eql('valueA1.4')
|
181 | nElem2.testKeyA2.should.be.eql('valueA2.4')
|
182 | })
|
183 |
|
184 | test('Create and delete functions', () => {
|
185 | const namespace = K.BSC.NateNamespace();
|
186 | namespace.addCreateFunction('tag1', function() {
|
187 | const rv = this.getNamespace().createNate()
|
188 | rv._createdBy_ = 'createTag1'
|
189 | return rv
|
190 | })
|
191 |
|
192 | namespace.addCreateFunction('tag2', function() {
|
193 | const rv = this.getNamespace().createNate()
|
194 | rv._createdBy_ = 'createTag2'
|
195 | return rv
|
196 | })
|
197 |
|
198 | namespace.addDeleteFunction('tag1', function() {this._deletedBy_ = 'deleteTag1'})
|
199 | namespace.addDeleteFunction('tag2', function() {this._deletedBy_ = 'deleteTag2'})
|
200 |
|
201 | const nRoot = namespace.createNate()
|
202 | const nTag1 = nRoot.newTag1()
|
203 | const nTag2 = nRoot.newTag2()
|
204 |
|
205 | nTag1.delete()
|
206 | nTag2.delete()
|
207 |
|
208 | nTag1._createdBy_.should.be.eql('createTag1')
|
209 | nTag2._createdBy_.should.be.eql('createTag2')
|
210 |
|
211 | nTag1._deletedBy_.should.be.eql('deleteTag1')
|
212 | nTag2._deletedBy_.should.be.eql('deleteTag2')
|
213 | })
|
214 | })
|
215 |
|
216 | _suite('Single tags (generic)', () => {
|
217 | const _createOneTagTest = (tagName, createFunction, expectedHtml) => {
|
218 | test(createFunction, () => {
|
219 | _reset()
|
220 | nRoot[createFunction]().setText('text')
|
221 | _verifyResultHtml(expectedHtml)
|
222 | })
|
223 |
|
224 | test('createNate(' + tagName + ')', () => {
|
225 | _reset()
|
226 | nRoot.newNate(tagName).setText('text')
|
227 | _verifyResultHtml(expectedHtml)
|
228 | })
|
229 | }
|
230 |
|
231 | _createOneTagTest('b' , 'newB' , '<b>text</b>')
|
232 | _createOneTagTest('br' , 'newBr' , '<br>')
|
233 | _createOneTagTest('div' , 'newDiv' , '<div>text</div>')
|
234 | _createOneTagTest('span' , 'newSpan' , '<span>text</span>')
|
235 | _createOneTagTest('canvas' , 'newCanvas' , '<canvas>text</canvas>')
|
236 | _createOneTagTest('h1' , 'newH1' , '<h1>text</h1>')
|
237 | _createOneTagTest('h2' , 'newH2' , '<h2>text</h2>')
|
238 | _createOneTagTest('h3' , 'newH3' , '<h3>text</h3>')
|
239 | _createOneTagTest('label' , 'newLabel' , '<label>text</label>')
|
240 | _createOneTagTest('ul' , 'newUl' , '<ul>text</ul>')
|
241 | _createOneTagTest('li' , 'newLi' , '<li>text</li>')
|
242 | _createOneTagTest('p' , 'newP' , '<p>text</p>')
|
243 | _createOneTagTest('pre' , 'newPre' , '<pre>text</pre>')
|
244 | _createOneTagTest('option' , 'newOption' , '<option>text</option>')
|
245 | _createOneTagTest('optGroup' , 'newOptGroup' , '<optgroup>text</optgroup>')
|
246 | _createOneTagTest('textArea' , 'newTextArea' , '<textarea>text</textarea>')
|
247 | _createOneTagTest('table' , 'newTable' , '<table>text</table>')
|
248 | _createOneTagTest('tBody' , 'newTBody' , '<tbody>text</tbody>')
|
249 | _createOneTagTest('tr' , 'newTr' , '<tr>text</tr>')
|
250 | _createOneTagTest('td' , 'newTd' , '<td>text</td>')
|
251 | _createOneTagTest('a' , 'newA' , '<a>text</a>')
|
252 |
|
253 | _createOneTagTest('inputCheckbox' , 'newInputCheckbox' , '<input type="checkbox">')
|
254 | _createOneTagTest('inputPassword' , 'newInputPassword' , '<input type="password">')
|
255 | _createOneTagTest('inputRadio' , 'newInputRadio' , '<input type="radio">')
|
256 | _createOneTagTest('inputText' , 'newInputText' , '<input type="text">')
|
257 | })
|
258 |
|
259 | test('Special property: textContent #1 (set function)', () => {
|
260 | _reset()
|
261 | const nDiv = nRoot.newDiv()
|
262 |
|
263 | nDiv.set('textContent', 'this is some pure text content')
|
264 | _verifyResultHtml('<div>this is some pure text content</div>')
|
265 |
|
266 |
|
267 | nDiv.set('textContent', 'this is HTML escaped content: <>&\'"')
|
268 | _verifyResultHtml('<div>this is HTML escaped content: <>&\'"</div>')
|
269 |
|
270 |
|
271 | nDiv.set('textContent', 'line1\nline2')
|
272 | _verifyResultHtml('<div>line1\nline2</div>')
|
273 | })
|
274 |
|
275 | test('Special property: textContent #2 (init list)', () => {
|
276 |
|
277 | _reset()
|
278 | let nDiv = nRoot.newDiv({
|
279 | textContent: 'this is some another pure text content'
|
280 | })
|
281 | _verifyResultHtml('<div>this is some another pure text content</div>')
|
282 |
|
283 |
|
284 | _reset()
|
285 | nDiv = nRoot.newDiv({
|
286 | textContent: 'this is another HTML escaped content: <>&\'"'
|
287 | })
|
288 | _verifyResultHtml('<div>this is another HTML escaped content: <>&\'"</div>')
|
289 |
|
290 |
|
291 | _reset()
|
292 | nDiv = nRoot.newDiv({
|
293 | textContent: 'line10\nline20'
|
294 | })
|
295 | _verifyResultHtml('<div>line10\nline20</div>')
|
296 | })
|
297 |
|
298 | if (domMode) {
|
299 |
|
300 |
|
301 | test('Special property: innerText (SKIPPED!)', () => {
|
302 | })
|
303 |
|
304 | } else {
|
305 |
|
306 | test('Special property: innerText', () => {
|
307 | _reset()
|
308 | const nDiv = nRoot.newDiv()
|
309 |
|
310 | nDiv.set('innerText', 'this is some pure text content')
|
311 | _verifyResultHtml('<div>this is some pure text content</div>')
|
312 |
|
313 |
|
314 | nDiv.set('innerText', 'this is HTML escaped content: <>&\'"')
|
315 | _verifyResultHtml('<div>this is HTML escaped content: <>&\'"</div>')
|
316 |
|
317 |
|
318 | nDiv.set('innerText', 'line1\nline2')
|
319 | _verifyResultHtml('<div>line1<br>line2</div>')
|
320 |
|
321 |
|
322 | let nateError = null
|
323 | try {
|
324 | nDiv.newDiv()
|
325 | } catch (_nateError) {
|
326 | nateError = _nateError
|
327 | }
|
328 | nateError.getErrorCode().should.be.eql('nateChildNotAllowed')
|
329 | })
|
330 | }
|
331 |
|
332 | test('Special property: innerHTML', () => {
|
333 | _reset()
|
334 | const nDiv = nRoot.newDiv()
|
335 |
|
336 |
|
337 | nDiv.set('innerHTML', 'this is some pure text content')
|
338 | _verifyResultHtml('<div>this is some pure text content</div>')
|
339 |
|
340 |
|
341 | nDiv.set('innerHTML', 'this is HTML content: <b class="test-class">TEST1 & TEST2</b>')
|
342 | _verifyResultHtml('<div>this is HTML content: <b class="test-class">TEST1 & TEST2</b></div>')
|
343 |
|
344 |
|
345 | nDiv.set('innerHTML', 'line1\nline2')
|
346 | _verifyResultHtml('<div>line1\nline2</div>')
|
347 |
|
348 |
|
349 |
|
350 | let nateError = null
|
351 | try {
|
352 | nDiv.newSpan()
|
353 | } catch (_nateError) {
|
354 | nateError = _nateError
|
355 | }
|
356 | nateError.getErrorCode().should.be.eql('nateChildNotAllowed')
|
357 |
|
358 |
|
359 | _verifyResultHtml('<div>line1\nline2</div>')
|
360 | })
|
361 |
|
362 | test('Create nate child when direct content exists (should fail)', () => {
|
363 |
|
364 | _reset()
|
365 | const nDiv = nRoot.newDiv().set('innerHTML', 'test-html-content')
|
366 |
|
367 |
|
368 |
|
369 | let nateError = null
|
370 | try {
|
371 | nDiv.newSpan()
|
372 | } catch (_nateError) {
|
373 | nateError = _nateError
|
374 | }
|
375 | nateError.getErrorCode().should.be.eql('nateChildNotAllowed')
|
376 |
|
377 |
|
378 | nDiv.set('innerHTML', null)
|
379 | nDiv.newSpan()
|
380 |
|
381 | _verifyResultHtml('<div><span></span></div>')
|
382 | })
|
383 |
|
384 | test('Set content directly when nate children exists (should fail)', () => {
|
385 | _reset()
|
386 |
|
387 | const nDiv = nRoot.newDiv()
|
388 | nDiv.newDiv()
|
389 |
|
390 |
|
391 | let nateError = null
|
392 | try {
|
393 | nDiv.set('innerHTML', 'test-html-content')
|
394 | } catch (_nateError) {
|
395 | nateError = _nateError
|
396 | }
|
397 | nateError.getErrorCode().should.be.eql('nateChildrenAlreadyExists')
|
398 |
|
399 |
|
400 | nateError = null
|
401 | try {
|
402 | nDiv.set('textContent', 'test-text-content')
|
403 | } catch (_nateError) {
|
404 | nateError = _nateError
|
405 | }
|
406 | nateError.getErrorCode().should.be.eql('nateChildrenAlreadyExists')
|
407 |
|
408 |
|
409 | nDiv.deleteAllChildren()
|
410 | nDiv.set('innerHTML', 'test-html-content')
|
411 | _verifyResultHtml('<div>test-html-content</div>')
|
412 | })
|
413 |
|
414 | test('Raw text', () => {
|
415 | _reset()
|
416 | nRoot.newTxt('This is a line.')
|
417 | nRoot.br()
|
418 | nRoot.newTxt('And this is one more line.')
|
419 |
|
420 | _verifyResultHtml('This is a line.<br>And this is one more line.')
|
421 | })
|
422 |
|
423 | test('Meta tag', () => {
|
424 | _reset()
|
425 |
|
426 | nRoot.newMeta()
|
427 | .set('name', 'description')
|
428 | .set('content', 'This is test meta tag')
|
429 |
|
430 | _verifyResultHtml('<meta name="description" content="This is test meta tag">')
|
431 | })
|
432 |
|
433 | test('Simple table', () => {
|
434 |
|
435 |
|
436 |
|
437 | _reset()
|
438 |
|
439 | const nTable = nRoot.newTable()
|
440 |
|
441 | const nRowA = nTable.newTr()
|
442 | const nRowB = nTable.newTr()
|
443 | const nRowC = nTable.newTr()
|
444 |
|
445 | nRowA.newTh().newTxt('A1')
|
446 | nRowA.newTh().newTxt('A2')
|
447 | nRowA.newTh().newTxt('A3')
|
448 |
|
449 | nRowB.newTd().newTxt('B1')
|
450 | nRowB.newTd().newTxt('B2')
|
451 | nRowB.newTd().newTxt('B3')
|
452 |
|
453 | nRowC.newTd().newTxt('C1')
|
454 | nRowC.newTd().newTxt('C2')
|
455 | nRowC.newTd().newTxt('C3')
|
456 |
|
457 | _verifyResultHtml(
|
458 | '<table>' +
|
459 | '<tr><th>A1</th><th>A2</th><th>A3</th></tr>' +
|
460 | '<tr><td>B1</td><td>B2</td><td>B3</td></tr>' +
|
461 | '<tr><td>C1</td><td>C2</td><td>C3</td></tr>' +
|
462 | '</table>')
|
463 | })
|
464 |
|
465 | test('Table with rowspan', () => {
|
466 |
|
467 |
|
468 |
|
469 | _reset()
|
470 |
|
471 | const nTable = nRoot.newTable()
|
472 |
|
473 | const nRowA = nTable.newTr()
|
474 | const nRowB = nTable.newTr()
|
475 | const nRowC = nTable.newTr()
|
476 |
|
477 | nRowA.newTh().newTxt('A1')
|
478 | nRowA.newTh().set('rowSpan', 3).newTxt('2')
|
479 | nRowA.newTh().newTxt('A3')
|
480 |
|
481 | nRowB.newTd().newTxt('B1')
|
482 | nRowB.newTd().newTxt('B3')
|
483 |
|
484 | nRowC.newTd().newTxt('C1')
|
485 | nRowC.newTd().newTxt('C3')
|
486 |
|
487 | _verifyResultHtml(
|
488 | '<table>' +
|
489 | '<tr><th>A1</th><th rowspan="3">2</th><th>A3</th></tr>' +
|
490 | '<tr><td>B1</td>' + '<td>B3</td></tr>' +
|
491 | '<tr><td>C1</td>' + '<td>C3</td></tr>' +
|
492 | '</table>')
|
493 | })
|
494 |
|
495 | test('Table with colspan', () => {
|
496 |
|
497 |
|
498 |
|
499 | _reset()
|
500 |
|
501 | const nTable = nRoot.newTable()
|
502 |
|
503 | const nRowA = nTable.newTr()
|
504 | const nRowB = nTable.newTr()
|
505 | const nRowC = nTable.newTr()
|
506 |
|
507 | nRowA.newTh().newTxt('A1')
|
508 | nRowA.newTh().newTxt('A2')
|
509 | nRowA.newTh().newTxt('A3')
|
510 |
|
511 | nRowB.newTd().set('colSpan', 3).newTxt('B')
|
512 |
|
513 | nRowC.newTd().newTxt('C1')
|
514 | nRowC.newTd().newTxt('C2')
|
515 | nRowC.newTd().newTxt('C3')
|
516 |
|
517 | _verifyResultHtml(
|
518 | '<table>' +
|
519 | '<tr><th>A1</th><th>A2</th><th>A3</th></tr>' +
|
520 | '<tr><td colspan="3">B</td></tr>' +
|
521 | '<tr><td>C1</td><td>C2</td><td>C3</td></tr>' +
|
522 | '</table>')
|
523 | })
|
524 |
|
525 | _suite('Generic attributes', () => {
|
526 | const arrayOfAttributes = [
|
527 | {key: 'id' , value: 'this-is-the-item'},
|
528 | {key: 'class' , value: 'this-is-the-class'},
|
529 | {key: 'cols' , value: '1'},
|
530 | {key: 'name' , value: 'this-is-the-name'},
|
531 | {key: 'rows' , value: '2'},
|
532 | {key: 'wrap' , value: 'hard'},
|
533 | {key: 'itemscope' , value: 'this-is-the-itemscope'},
|
534 | {key: 'itemtype' , value: 'this-is-the-itemtype'},
|
535 | {key: 'itemprop' , value: 'this-is-the-itemprop'},
|
536 | {key: 'content' , value: 'this-is-the-content'},
|
537 | {key: 'style' , value: 'color: red; background-color: green;'},
|
538 |
|
539 | ]
|
540 |
|
541 | for (const idx in arrayOfAttributes) {
|
542 | const item = arrayOfAttributes[idx]
|
543 | const key = item.key
|
544 | const value = item.value
|
545 |
|
546 | test(key, () => {
|
547 | _reset()
|
548 | nRoot.newDiv().set(key, value).setText('text')
|
549 | _verifyResultHtml('<div ' + key + '="' + value + '">text</div>')
|
550 | })
|
551 | }
|
552 | })
|
553 |
|
554 | test('Apply many attributes to one element', () => {
|
555 | _reset()
|
556 |
|
557 |
|
558 | const nDiv = nRoot.newDiv()
|
559 | _verifyResultHtml('<div></div>')
|
560 |
|
561 |
|
562 | nDiv.set('id', 'this-is-the-id')
|
563 | _verifyResultHtml('<div id="this-is-the-id"></div>')
|
564 |
|
565 |
|
566 | nDiv.set('class', 'this-is-the-class1')
|
567 | _verifyResultHtml('<div id="this-is-the-id" class="this-is-the-class1"></div>')
|
568 |
|
569 |
|
570 | nDiv.set('name', 'jenny')
|
571 | _verifyResultHtml('<div id="this-is-the-id" class="this-is-the-class1" name="jenny"></div>')
|
572 |
|
573 |
|
574 | nDiv.set('class', 'this-is-the-class2')
|
575 | _verifyResultHtml('<div id="this-is-the-id" class="this-is-the-class2" name="jenny"></div>')
|
576 | })
|
577 |
|
578 | test('Set CSS text directly', () => {
|
579 | _reset()
|
580 | nRoot.newDiv().set('cssText', 'color: blue;')
|
581 | _verifyResultHtml('<div style="color: blue;"></div>')
|
582 | })
|
583 |
|
584 | test('Read-only input', () => {
|
585 | _reset()
|
586 | nRoot.newInputText().set('readOnly', true)
|
587 | _verifyResultHtml('<input type="text" readonly="">')
|
588 | })
|
589 |
|
590 | test('Checkbox #1: Simple checkbox with label', () => {
|
591 | _reset()
|
592 |
|
593 |
|
594 | nRoot.newLabel().set('for', 'john').newTxt('This is a checkbox')
|
595 | nRoot.newInputCheckbox().set('name', 'john')
|
596 |
|
597 | _verifyResultHtml(
|
598 | '<label for="john">This is a checkbox</label>' +
|
599 | '<input type="checkbox" name="john">')
|
600 | })
|
601 |
|
602 | test('Checkbox #2: Updated checked state on-the-fly', () => {
|
603 | _reset()
|
604 |
|
605 |
|
606 | const nCheckbox = nRoot.newInputCheckbox()
|
607 | _verifyResultHtml('<input type="checkbox">')
|
608 |
|
609 |
|
610 | if (domMode) {
|
611 |
|
612 | nCheckbox.set('checked', true)
|
613 | nCheckbox.elem.checked.should.be.eql(true)
|
614 |
|
615 | nCheckbox.set('checked', false)
|
616 | nCheckbox.elem.checked.should.be.eql(false)
|
617 | } else {
|
618 |
|
619 | nCheckbox.set('checked', true)
|
620 | _verifyResultHtml('<input type="checkbox" checked="checked">')
|
621 |
|
622 | nCheckbox.set('checked', false)
|
623 | _verifyResultHtml('<input type="checkbox">')
|
624 | }
|
625 | })
|
626 |
|
627 | test('Event handlers', () => {
|
628 | _reset()
|
629 | const _onclickCb = function() {alert('This is onclick handler!')}
|
630 | const _onscrollCb = function() {alert('This is onscroll handler!')}
|
631 |
|
632 | const nDiv = nRoot.newDiv()
|
633 | .set('onclick', _onclickCb)
|
634 | .set('onscroll', _onscrollCb)
|
635 |
|
636 | if (domMode) {
|
637 |
|
638 | nDiv.elem.onclick.should.be.eql(_onclickCb)
|
639 | nDiv.elem.onscroll.should.be.eql(_onscrollCb)
|
640 | } else {
|
641 |
|
642 | nRoot.render().should.be.eql(
|
643 | '<div' +
|
644 | ' onclick="alert(\'This is onclick handler!\')"' +
|
645 | ' onscroll="alert(\'This is onscroll handler!\')"' +
|
646 | '></div>')
|
647 | }
|
648 | })
|
649 |
|
650 | test('Simple link', () => {
|
651 | _reset()
|
652 |
|
653 | const nLink = nRoot.newA().set('href', 'http://ke.mu').setText('Kemu Studio')
|
654 | _verifyResultHtml('<a href="http://ke.mu">Kemu Studio</a>')
|
655 |
|
656 |
|
657 | nLink.set('target', '_blank')
|
658 | _verifyResultHtml('<a href="http://ke.mu" target="_blank">Kemu Studio</a>')
|
659 |
|
660 |
|
661 | nLink.set('href', 'http://calculla.com')
|
662 | _verifyResultHtml('<a href="http://calculla.com" target="_blank">Kemu Studio</a>')
|
663 | })
|
664 |
|
665 | test('Simple image', () => {
|
666 | _reset()
|
667 |
|
668 | const nImg = nRoot.newImg().set('src', 'http://calculla.com/g/calculla_logo_32.svg')
|
669 | _verifyResultHtml('<img src="http://calculla.com/g/calculla_logo_32.svg">')
|
670 |
|
671 |
|
672 | nImg.set('src', 'http://calculla.pl/g/calculla_logo_32.svg')
|
673 | _verifyResultHtml('<img src="http://calculla.pl/g/calculla_logo_32.svg">')
|
674 | })
|
675 |
|
676 | test('Select input #1: optgroups', () => {
|
677 | _reset()
|
678 | const nSelect = nRoot.newSelect()
|
679 |
|
680 | const nGroupA = nSelect.newOptGroup().set('label', 'A')
|
681 | nGroupA.newOption().set('value', 'value-a1').setText('A1')
|
682 | nGroupA.newOption().set('value', 'value-a2').setText('A2')
|
683 | nGroupA.newOption().set('value', 'value-a3').setText('A3')
|
684 |
|
685 | const nGroupB = nSelect.newOptGroup().set('label', 'B')
|
686 | nGroupB.newOption().set('value', 'value-b1').setText('B1')
|
687 | nGroupB.newOption().set('value', 'value-b2').setText('B2')
|
688 | nGroupB.newOption().set('value', 'value-b3').setText('B3')
|
689 |
|
690 | const nGroupC = nSelect.newOptGroup().set('label', 'C')
|
691 | nGroupC.newOption().set('value', 'value-c1').setText('C1')
|
692 | nGroupC.newOption().set('value', 'value-c2').setText('C2')
|
693 | nGroupC.newOption().set('value', 'value-c3').setText('C3')
|
694 |
|
695 | _verifyResultHtml(
|
696 | '<select>' +
|
697 | '<optgroup label="A">' +
|
698 | '<option value="value-a1">A1</option>' +
|
699 | '<option value="value-a2">A2</option>' +
|
700 | '<option value="value-a3">A3</option>' +
|
701 | '</optgroup>' +
|
702 | '<optgroup label="B">' +
|
703 | '<option value="value-b1">B1</option>' +
|
704 | '<option value="value-b2">B2</option>' +
|
705 | '<option value="value-b3">B3</option>' +
|
706 | '</optgroup>' +
|
707 | '<optgroup label="C">' +
|
708 | '<option value="value-c1">C1</option>' +
|
709 | '<option value="value-c2">C2</option>' +
|
710 | '<option value="value-c3">C3</option>' +
|
711 | '</optgroup>' +
|
712 | '</select>')
|
713 | })
|
714 |
|
715 | test('Select input #2: set selected item on-the-fly', () => {
|
716 | _reset()
|
717 |
|
718 |
|
719 | const nSelect = nRoot.newSelect()
|
720 | const nOption1 = nSelect.newOption().set('value', 'value1').setText('option1')
|
721 | const nOption2 = nSelect.newOption().set('value', 'value2').setText('option2')
|
722 | const nOption3 = nSelect.newOption().set('value', 'value3').setText('option3')
|
723 |
|
724 | _verifyResultHtml(
|
725 | '<select>' +
|
726 | '<option value="value1">option1</option>' +
|
727 | '<option value="value2">option2</option>' +
|
728 | '<option value="value3">option3</option>' +
|
729 | '</select>')
|
730 |
|
731 |
|
732 | nOption2.set('selected', true)
|
733 |
|
734 | if (domMode) {
|
735 |
|
736 | nOption1.elem.selected.should.be.eql(false)
|
737 | nOption2.elem.selected.should.be.eql(true)
|
738 | nOption3.elem.selected.should.be.eql(false)
|
739 | } else {
|
740 |
|
741 | _verifyResultHtml(
|
742 | '<select>' +
|
743 | '<option value="value1">option1</option>' +
|
744 | '<option value="value2" selected="selected">option2</option>' +
|
745 | '<option value="value3">option3</option>' +
|
746 | '</select>')
|
747 | }
|
748 | })
|
749 |
|
750 | _suite('Style attributes', () => {
|
751 | const arrayOfAttributes = [
|
752 |
|
753 | {key: 'cursor', value: 'pointer'},
|
754 | {key: 'float', value: 'left'},
|
755 | {key: 'height', value: '1px'},
|
756 | {key: 'width', value: '2px'},
|
757 | {key: 'left', value: '3px'},
|
758 | {key: 'top', value: '4px'},
|
759 | {key: 'right', value: '5px'},
|
760 | {key: 'bottom', value: '6px'},
|
761 | {key: 'margin', value: '7px'},
|
762 | {key: 'padding', value: '8px'},
|
763 | {key: 'overflow', value: 'hidden'},
|
764 | {key: 'font', value: '12px 12px'},
|
765 | {key: 'position', value: 'absolute'},
|
766 | {key: 'visibility', value: 'none'},
|
767 | {key: 'color', value: 'red'},
|
768 | {key: 'border', value: '8px solid green'},
|
769 | {key: 'display', value: 'block'},
|
770 |
|
771 |
|
772 | {key: 'overflowX' , cssKey: 'overflow-x' , value: 'auto'},
|
773 | {key: 'overflowY' , cssKey: 'overflow-y' , value: 'visible'},
|
774 | {key: 'zIndex' , cssKey: 'z-index' , value: '1234'},
|
775 | {key: 'textAlign' , cssKey: 'text-align' , value: 'center'},
|
776 | {key: 'verticalAlign' , cssKey: 'vertical-align' , value: 'bottom'},
|
777 | {key: 'backgroundColor' , cssKey: 'background-color' , value: 'blue'},
|
778 | {key: 'borderCollapse' , cssKey: 'border-collapse' , value: 'collapse'},
|
779 | {key: 'fontFamily' , cssKey: 'font-family' , value: 'arial'},
|
780 | {key: 'fontSize' , cssKey: 'font-size' , value: '50em'},
|
781 | {key: 'fontStyle' , cssKey: 'font-style' , value: 'italic'},
|
782 | {key: 'fontWeight' , cssKey: 'font-weight' , value: 'bold'}
|
783 | ]
|
784 |
|
785 | for (const idx in arrayOfAttributes) {
|
786 | const item = arrayOfAttributes[idx]
|
787 | const key = item.key
|
788 | const value = item.value
|
789 | const cssKey = item.cssKey ? item.cssKey : item.key
|
790 |
|
791 | test(key, () => {
|
792 | _reset()
|
793 | nRoot.newDiv().set(key, value).setText('text')
|
794 | _verifyResultHtml('<div style="' + cssKey + ': ' + value + ';">text</div>')
|
795 | })
|
796 | }
|
797 | })
|
798 |
|
799 | _suite('Dataset #1: Single attribute', () => {
|
800 | const _createTestGood = (key, jsKey, value) => {
|
801 | test('"' + key + '" => "' + value + '"', () => {
|
802 | _reset()
|
803 | nRoot.newDiv().setData(key, value)
|
804 | _verifyResultHtml('<div data-' + jsKey + '="' + value + '"></div>')
|
805 | })
|
806 | }
|
807 |
|
808 | const _createTestBadKey = (key) => {
|
809 | const keyForPreview = key ? JSON.stringify(key) : '' + key
|
810 | test(keyForPreview, () => {
|
811 | _reset()
|
812 | try {
|
813 | nRoot.newDiv().setData(key, 'value')
|
814 | }
|
815 | catch(err) {
|
816 | err.getErrorCode().should.be.eql('invalidKey')
|
817 | }
|
818 | })
|
819 | }
|
820 |
|
821 |
|
822 | _suite('Good examples', () => {
|
823 | _createTestGood('key' , 'key' , 'value')
|
824 | _createTestGood('123' , '123' , 'value')
|
825 | _createTestGood('_' , '_' , 'value')
|
826 | _createTestGood('_key', '_key' , 'value')
|
827 |
|
828 | _createTestGood('key' , 'key' , 'VALUE')
|
829 | _createTestGood('key' , 'key' , '123')
|
830 | _createTestGood('key' , 'key' , 'this-is-some-value')
|
831 | _createTestGood('key' , 'key' , '3.14')
|
832 | _createTestGood('key' , 'key' , '')
|
833 |
|
834 | _createTestGood('KEY' , 'key' , 'value')
|
835 | _createTestGood('KeY' , 'key' , 'value')
|
836 | _createTestGood('kEy' , 'key' , 'value')
|
837 | _createTestGood('key_abc', 'key_abc', 'value')
|
838 | _createTestGood('key-abc', 'key-abc', 'value')
|
839 | _createTestGood('key.abc', 'key.abc', 'value')
|
840 | _createTestGood('key:abc', 'key:abc', 'value')
|
841 | _createTestGood('a.b.c.d', 'a.b.c.d', 'value')
|
842 | })
|
843 |
|
844 |
|
845 | _suite('Bad keys', () => {
|
846 | _createTestBadKey()
|
847 | _createTestBadKey('')
|
848 | _createTestBadKey(' ')
|
849 | _createTestBadKey('a b')
|
850 | _createTestBadKey('.')
|
851 | _createTestBadKey(',')
|
852 | _createTestBadKey('-')
|
853 | _createTestBadKey(null)
|
854 | _createTestBadKey(123)
|
855 | _createTestBadKey(3.14)
|
856 | _createTestBadKey([])
|
857 | _createTestBadKey([1234])
|
858 | _createTestBadKey({})
|
859 | _createTestBadKey({x:1234})
|
860 | })
|
861 | })
|
862 | })
|
863 | }
|
864 |
|
865 | TestNateHtml(false)
|
866 | TestNateHtml(true)
|