UNPKG

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