sdc_client 0.57.10 → 0.57.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.idea/misc.xml +2 -2
- package/.idea/workspace.xml +106 -11
- package/.tool-versions +1 -1
- package/SimpleDomControlClient.iml +6 -1
- package/dist/index.js +8 -8
- package/dist/ugly.index.js +1 -1
- package/package.json +3 -1
- package/src/index.js +2 -2
- package/src/simpleDomControl/AbstractSDC.js +9 -0
- package/src/simpleDomControl/sdc_controller.js +17 -1
- package/src/simpleDomControl/sdc_main.js +47 -22
- package/src/simpleDomControl/sdc_socket.js +24 -6
- package/src/simpleDomControl/sdc_test_utils.js +65 -19
- package/src/simpleDomControl/sdc_utils.js +45 -23
- package/src/simpleDomControl/sdc_view.js +202 -14
- package/test/controller.test.js +1 -41
- package/test/utils.js +89 -0
- package/test/view.test.js +141 -0
@@ -56,6 +56,7 @@ export function tagNameToCamelCase(str) {
|
|
56
56
|
str = str.replace(/-./g, letter => `${letter[1].toUpperCase()}`);
|
57
57
|
return str;
|
58
58
|
}
|
59
|
+
|
59
60
|
export function tagNameToReadableName(str) {
|
60
61
|
str = str.replace(/-./g, letter => ` ${letter[1].toUpperCase()}`).replace(/^./g, letter => `${letter.toUpperCase()}`);
|
61
62
|
return str;
|
@@ -83,31 +84,42 @@ const copyProps = (targetClass, sourceClass) => {
|
|
83
84
|
|
84
85
|
/**
|
85
86
|
*
|
86
|
-
* @param {AbstractSDC} baseClass
|
87
|
-
* @param {AbstractSDC} mixins
|
87
|
+
* @param {typeof AbstractSDC} baseClass
|
88
|
+
* @param {typeof AbstractSDC} mixins
|
88
89
|
* @returns {AbstractSDC}
|
89
90
|
*/
|
90
91
|
export function agileAggregation(baseClass, ...mixins) {
|
91
92
|
|
92
|
-
let base =
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
93
|
+
let base = {
|
94
|
+
[baseClass.name]: class {
|
95
|
+
constructor(..._args) {
|
96
|
+
let _mixins = {};
|
97
|
+
mixins.forEach((mixin) => {
|
98
|
+
let newMixin;
|
99
|
+
Object.assign(this, (newMixin = new mixin()));
|
100
|
+
newMixin._tagName = mixin.prototype._tagName;
|
101
|
+
newMixin._isMixin = true;
|
102
|
+
_mixins[mixin.name] = newMixin;
|
103
|
+
});
|
104
|
+
|
105
|
+
Object.assign(this, new baseClass());
|
106
|
+
this._mixins = _mixins;
|
107
|
+
}
|
102
108
|
|
103
|
-
Object.assign(this, new baseClass());
|
104
|
-
this._mixins = _mixins;
|
105
|
-
}
|
106
109
|
|
107
|
-
|
108
|
-
|
110
|
+
static get name() {
|
111
|
+
return baseClass.name;
|
112
|
+
}
|
113
|
+
|
114
|
+
static className() {
|
115
|
+
return this.name
|
116
|
+
}
|
117
|
+
|
118
|
+
get mixins() {
|
119
|
+
return this._mixins;
|
120
|
+
}
|
109
121
|
}
|
110
|
-
};
|
122
|
+
}[baseClass.name];
|
111
123
|
|
112
124
|
copyProps(base, baseClass);
|
113
125
|
|
@@ -141,7 +153,7 @@ export function uploadFileFormData(formData, url, method) {
|
|
141
153
|
cache: false,
|
142
154
|
contentType: false,
|
143
155
|
processData: false,
|
144
|
-
beforeSend: function(xhr, settings) {
|
156
|
+
beforeSend: function (xhr, settings) {
|
145
157
|
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
|
146
158
|
xhr.setRequestHeader("X-CSRFToken", window.CSRF_TOKEN);
|
147
159
|
}
|
@@ -171,8 +183,8 @@ export function checkIfParamNumberBoolOrString(paramElement, controller = null)
|
|
171
183
|
return paramElement;
|
172
184
|
}
|
173
185
|
|
174
|
-
if(controller && typeof controller[paramElement] !== 'undefined') {
|
175
|
-
if(typeof controller[paramElement] === 'function') {
|
186
|
+
if (controller && typeof controller[paramElement] !== 'undefined') {
|
187
|
+
if (typeof controller[paramElement] === 'function') {
|
176
188
|
return controller[paramElement].bind(controller);
|
177
189
|
}
|
178
190
|
return controller[paramElement];
|
@@ -213,7 +225,7 @@ export function clearErrorsInForm($form) {
|
|
213
225
|
}
|
214
226
|
|
215
227
|
export function setErrorsInForm($form, $resForm) {
|
216
|
-
$resForm
|
228
|
+
$resForm = $('<div>').append($resForm);
|
217
229
|
|
218
230
|
$form.find('.has-error').removeClass('has-error').find('.alert-danger').safeRemove();
|
219
231
|
$form.find('.non-field-errors').safeRemove();
|
@@ -233,4 +245,14 @@ export function setErrorsInForm($form, $resForm) {
|
|
233
245
|
});
|
234
246
|
|
235
247
|
return hasNoError;
|
236
|
-
}
|
248
|
+
}
|
249
|
+
|
250
|
+
export function jqueryInsertAt($container, index, $newElement) {
|
251
|
+
let lastIndex = $container.children().size();
|
252
|
+
if (index < lastIndex) {
|
253
|
+
$container.children().eq(index).before($newElement);
|
254
|
+
} else {
|
255
|
+
$container.append($newElement);
|
256
|
+
}
|
257
|
+
return this;
|
258
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import {controllerFactory, runControlFlowFunctions
|
1
|
+
import {controllerFactory, runControlFlowFunctions} from "./sdc_controller.js";
|
2
2
|
import {getUrlParam} from "./sdc_params.js";
|
3
3
|
import {app} from "./sdc_main.js";
|
4
4
|
import {trigger} from "./sdc_events.js";
|
@@ -23,7 +23,7 @@ export function cleanCache() {
|
|
23
23
|
* doms and returns a list of objects containing also the tag name the dom and the tag
|
24
24
|
* names of the super controller
|
25
25
|
*
|
26
|
-
* @param {
|
26
|
+
* @param {$} $container - jQuery container
|
27
27
|
* @param {Array<string>} tagNameList - a string list with tag names.
|
28
28
|
* @param {AbstractSDC} parentController - controller in surrounding
|
29
29
|
* @return {Array} - a array of objects with all register tags found
|
@@ -107,7 +107,7 @@ function loadHTMLFile(path, args, tag, hardReload) {
|
|
107
107
|
const data = err.responseJSON;
|
108
108
|
trigger('_RedirectOnView', data['url-link']);
|
109
109
|
}
|
110
|
-
trigger('navLoaded', {'controller_name': ()=> err.status});
|
110
|
+
trigger('navLoaded', {'controller_name': () => err.status});
|
111
111
|
|
112
112
|
throw `<sdc-error data-code="${err.status}">${err.responseText}</sdc-error>`;
|
113
113
|
});
|
@@ -300,16 +300,17 @@ export function replaceTagElementsInContainer(tagList, $container, parentControl
|
|
300
300
|
});
|
301
301
|
}
|
302
302
|
|
303
|
-
export function reloadMethodHTML(controller) {
|
304
|
-
return _reloadMethodHTML(controller, controller.$container)
|
303
|
+
export function reloadMethodHTML(controller, $container) {
|
304
|
+
return _reloadMethodHTML(controller, $container ?? controller.$container)
|
305
305
|
}
|
306
|
+
|
306
307
|
function _reloadMethodHTML(controller, $dom) {
|
307
308
|
const plist = [];
|
308
309
|
|
309
310
|
$dom.find(`._bind_to_update_handler.sdc_uuid_${controller._uuid}`).each(function () {
|
310
311
|
const $this = $(this);
|
311
312
|
let result = undefined;
|
312
|
-
if($this.hasClass(`_with_handler`)) {
|
313
|
+
if ($this.hasClass(`_with_handler`)) {
|
313
314
|
result = $this.data('handler');
|
314
315
|
} else {
|
315
316
|
let controller_handler = this.tagName.toLowerCase().replace(/^this./, '');
|
@@ -324,18 +325,205 @@ function _reloadMethodHTML(controller, $dom) {
|
|
324
325
|
}
|
325
326
|
if (result !== undefined) {
|
326
327
|
plist.push(Promise.resolve(result).then((x) => {
|
327
|
-
|
328
|
-
$
|
329
|
-
|
330
|
-
|
331
|
-
$this.safeEmpty().text('').append(x);
|
332
|
-
return true;
|
333
|
-
});
|
334
|
-
});
|
328
|
+
let $newContent = $(`<div></div>`);
|
329
|
+
$newContent.append(x);
|
330
|
+
$newContent = $this.clone().empty().append($newContent);
|
331
|
+
return controller.reconcile($newContent, $this);
|
335
332
|
}));
|
336
333
|
}
|
337
334
|
|
338
335
|
});
|
339
336
|
|
340
337
|
return Promise.all(plist);
|
338
|
+
}
|
339
|
+
|
340
|
+
|
341
|
+
function getNodeKey(node) {
|
342
|
+
if (node[0].nodeType === 3) {
|
343
|
+
return `TEXT__${node[0].nodeValue}`;
|
344
|
+
}
|
345
|
+
const res = [node[0].tagName];
|
346
|
+
if (node[0].nodeName === 'INPUT') {
|
347
|
+
[['name', ''], ['type', 'text'], ['id', '']].forEach(([key, defaultValue]) => {
|
348
|
+
const attr = node.attr(key) ?? defaultValue;
|
349
|
+
if (attr) {
|
350
|
+
res.push(attr);
|
351
|
+
}
|
352
|
+
});
|
353
|
+
}
|
354
|
+
return res.join('__');
|
355
|
+
}
|
356
|
+
|
357
|
+
function reconcileTree($element, id = [], parent = null) {
|
358
|
+
id.push(getNodeKey($element));
|
359
|
+
const obj = {
|
360
|
+
$element,
|
361
|
+
id: id.join('::'),
|
362
|
+
depth: id.length,
|
363
|
+
idx: 0,
|
364
|
+
op: null,
|
365
|
+
parent
|
366
|
+
};
|
367
|
+
return [obj].concat($element.contents().toArray().map((x) => reconcileTree($(x), id.slice(), obj)).flat());
|
368
|
+
|
369
|
+
}
|
370
|
+
|
371
|
+
|
372
|
+
export function reconcile($virtualNode, $realNode) {
|
373
|
+
const $old = reconcileTree($realNode);
|
374
|
+
const $new = reconcileTree($virtualNode);
|
375
|
+
$old.map((x, i) => x.idx = i);
|
376
|
+
$new.map((x, i) => x.idx = i);
|
377
|
+
const depth = Math.max(...$new.concat($old).map(x => x.depth));
|
378
|
+
const op_steps = lcbDiff($old, $new, depth);
|
379
|
+
let opIdx = 0;
|
380
|
+
let toRemove = [];
|
381
|
+
|
382
|
+
op_steps.forEach(op_step => {
|
383
|
+
const {op, $element, idx} = op_step;
|
384
|
+
if (op.type === 'keep_counterpart') {
|
385
|
+
|
386
|
+
if (op.counterpart.idx + opIdx !== idx) {
|
387
|
+
const elemBefore = op_step.getBefore();
|
388
|
+
if (!elemBefore) {
|
389
|
+
op_step.getRealParent().$element.prepend(op.counterpart.$element);
|
390
|
+
} else {
|
391
|
+
op.counterpart.$element.insertAfter(elemBefore.$element);
|
392
|
+
}
|
393
|
+
}
|
394
|
+
|
395
|
+
syncAttributes(op.counterpart.$element, $element);
|
396
|
+
if ($element.hasClass(CONTROLLER_CLASS)) {
|
397
|
+
$element.data(DATA_CONTROLLER_KEY).$container = op.counterpart.$element;
|
398
|
+
$element.data(DATA_CONTROLLER_KEY, null);
|
399
|
+
}
|
400
|
+
|
401
|
+
toRemove.push($element);
|
402
|
+
|
403
|
+
} else if (op.type === 'delete') {
|
404
|
+
$element.safeRemove();
|
405
|
+
opIdx--;
|
406
|
+
} else if (op.type === 'insert_ignore') {
|
407
|
+
opIdx++;
|
408
|
+
} else if (op.type === 'insert') {
|
409
|
+
opIdx++;
|
410
|
+
const {after, target} = op_step.op;
|
411
|
+
if (after) {
|
412
|
+
$element.insertAfter(after.$element);
|
413
|
+
} else if (target) {
|
414
|
+
target.$element.prepend($element);
|
415
|
+
}
|
416
|
+
|
417
|
+
}
|
418
|
+
});
|
419
|
+
|
420
|
+
toRemove.forEach(($element) => $element.safeRemove());
|
421
|
+
}
|
422
|
+
|
423
|
+
function syncAttributes($real, $virtual) {
|
424
|
+
const realAttrs = $real[0].attributes ?? [];
|
425
|
+
const virtualAttrs = $virtual[0].attributes ?? [];
|
426
|
+
// Remove missing attrs
|
427
|
+
[...realAttrs].forEach(attr => {
|
428
|
+
if (!$virtual.is(`[${attr.name}]`)) {
|
429
|
+
$real.removeAttr(attr.name);
|
430
|
+
}
|
431
|
+
});
|
432
|
+
|
433
|
+
// Add or update
|
434
|
+
[...virtualAttrs].forEach(attr => {
|
435
|
+
if (!attr.name.startsWith(`data`) && $real.attr(attr.name) !== attr.value) {
|
436
|
+
$real.attr(attr.name, attr.value);
|
437
|
+
}
|
438
|
+
});
|
439
|
+
|
440
|
+
Object.entries($virtual.data()).forEach(([key, value]) => {
|
441
|
+
$real.data(key, value);
|
442
|
+
});
|
443
|
+
}
|
444
|
+
|
445
|
+
/**
|
446
|
+
* LCB (Longest Common Branch) finds matching branches and reserves them!
|
447
|
+
*
|
448
|
+
* @param oldNodes
|
449
|
+
* @param newNodes
|
450
|
+
* @param depth
|
451
|
+
* @returns {*|*[]}
|
452
|
+
*/
|
453
|
+
function lcbDiff(oldNodes, newNodes, depth) {
|
454
|
+
newNodes.filter(x => x.depth === depth && !x.op).forEach((newNode) => {
|
455
|
+
const oldNode = oldNodes.find((tempOldNode) => {
|
456
|
+
return !tempOldNode.op && tempOldNode.id === newNode.id;
|
457
|
+
});
|
458
|
+
|
459
|
+
if (oldNode) {
|
460
|
+
const keepTreeBranch = (oldNode, newNode) => {
|
461
|
+
oldNode.op = {type: 'keep', idx: newNode.idx};
|
462
|
+
newNode.op = {type: 'keep_counterpart', counterpart: oldNode};
|
463
|
+
oldNode = oldNode.parent;
|
464
|
+
if (!oldNode || oldNode.op) {
|
465
|
+
return;
|
466
|
+
}
|
467
|
+
newNode = newNode.parent;
|
468
|
+
keepTreeBranch(oldNode, newNode);
|
469
|
+
|
470
|
+
}
|
471
|
+
keepTreeBranch(oldNode, newNode);
|
472
|
+
}
|
473
|
+
});
|
474
|
+
if (depth > 1) {
|
475
|
+
return lcbDiff(oldNodes, newNodes, depth - 1);
|
476
|
+
}
|
477
|
+
|
478
|
+
oldNodes.forEach((x, i) => {
|
479
|
+
if (!x.op) {
|
480
|
+
const idx = (oldNodes[i - 1]?.op.idx ?? -1) + 1;
|
481
|
+
x.op = {type: 'delete', idx}
|
482
|
+
}
|
483
|
+
});
|
484
|
+
|
485
|
+
function getRealParent(element) {
|
486
|
+
if (!element.parent) {
|
487
|
+
return null;
|
488
|
+
}
|
489
|
+
return element.parent.op.type === 'keep_counterpart' ? element.parent.op.counterpart : element.parent;
|
490
|
+
}
|
491
|
+
|
492
|
+
function getBefore(element, idx) {
|
493
|
+
const startDepth = element.depth;
|
494
|
+
while (idx >= 0 && element.depth >= startDepth) {
|
495
|
+
idx -= 1;
|
496
|
+
element = newNodes[idx];
|
497
|
+
if (element.depth === startDepth) {
|
498
|
+
return element.op.type === 'keep_counterpart' ? element.op.counterpart : element;
|
499
|
+
}
|
500
|
+
}
|
501
|
+
|
502
|
+
return null
|
503
|
+
|
504
|
+
}
|
505
|
+
|
506
|
+
newNodes.forEach((x, i) => {
|
507
|
+
x.getBefore = () => getBefore(x, i);
|
508
|
+
x.getRealParent = () => getRealParent(x);
|
509
|
+
|
510
|
+
if (!x.op) {
|
511
|
+
const target = x.getRealParent();
|
512
|
+
const type = target?.op.type === 'insert' ? 'insert_ignore' : 'insert';
|
513
|
+
x.op = {type, target, after: x.getBefore()}
|
514
|
+
}
|
515
|
+
});
|
516
|
+
|
517
|
+
const tagged = [
|
518
|
+
...oldNodes,
|
519
|
+
...newNodes,
|
520
|
+
];
|
521
|
+
|
522
|
+
|
523
|
+
return tagged.sort((a, b) => {
|
524
|
+
const aVal = a.op?.idx ?? a.idx;
|
525
|
+
const bVal = b.op?.idx ?? b.idx;
|
526
|
+
|
527
|
+
return aVal - bVal;
|
528
|
+
});
|
341
529
|
}
|
package/test/controller.test.js
CHANGED
@@ -9,49 +9,9 @@ import * as sdc_view from '../src/simpleDomControl/sdc_view.js';
|
|
9
9
|
const app = sdc.app;
|
10
10
|
|
11
11
|
import $ from 'jquery';
|
12
|
+
import {TestCtr, TestCtrA} from "./utils.js";
|
12
13
|
window.$ = $;
|
13
14
|
|
14
|
-
const TestControllerInfo = {
|
15
|
-
name: 'TestCtr',
|
16
|
-
tag: 'test-ctr'
|
17
|
-
};
|
18
|
-
|
19
|
-
class TestCtr extends sdc.AbstractSDC {
|
20
|
-
constructor() {
|
21
|
-
super();
|
22
|
-
this.contentUrl = TestControllerInfo.name; //<test-ctr>
|
23
|
-
this.events.unshift({});
|
24
|
-
this.val = 0;
|
25
|
-
this.contentReload = true;
|
26
|
-
}
|
27
|
-
|
28
|
-
sayA() {
|
29
|
-
return 'A'
|
30
|
-
}
|
31
|
-
|
32
|
-
onInit() {}
|
33
|
-
}
|
34
|
-
|
35
|
-
class TestCtrA extends sdc.AbstractSDC {
|
36
|
-
constructor() {
|
37
|
-
super();
|
38
|
-
this.contentUrl = 'TestCtrA'; //<test-ctr-a>
|
39
|
-
this.events.unshift({});
|
40
|
-
this.val = 1;
|
41
|
-
this.val_2 = 2;
|
42
|
-
}
|
43
|
-
|
44
|
-
sayA() {
|
45
|
-
return 'B'
|
46
|
-
}
|
47
|
-
|
48
|
-
sayB() {
|
49
|
-
return 'B'
|
50
|
-
}
|
51
|
-
|
52
|
-
onInit() {}
|
53
|
-
}
|
54
|
-
|
55
15
|
describe('Controller', () => {
|
56
16
|
let ajaxSpy;
|
57
17
|
beforeEach(() => {
|
package/test/utils.js
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
import {AbstractSDC} from "../src/index.js";
|
2
|
+
|
3
|
+
|
4
|
+
export const TestControllerInfo = {
|
5
|
+
name: 'TestCtr',
|
6
|
+
tag: 'test-ctr'
|
7
|
+
};
|
8
|
+
|
9
|
+
export class TestCtr extends AbstractSDC {
|
10
|
+
constructor() {
|
11
|
+
super();
|
12
|
+
this.contentUrl = TestControllerInfo.name; //<test-ctr>
|
13
|
+
this.events.unshift({});
|
14
|
+
this.val = 0;
|
15
|
+
this.contentReload = true;
|
16
|
+
}
|
17
|
+
|
18
|
+
sayA() {
|
19
|
+
return 'A'
|
20
|
+
}
|
21
|
+
|
22
|
+
onInit() {
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
export class TestCtrA extends AbstractSDC {
|
27
|
+
constructor() {
|
28
|
+
super();
|
29
|
+
this.contentUrl = 'TestCtrA'; //<test-ctr-a>
|
30
|
+
this.events.unshift({});
|
31
|
+
this.val = 1;
|
32
|
+
this.val_2 = 2;
|
33
|
+
}
|
34
|
+
|
35
|
+
sayA() {
|
36
|
+
return 'B'
|
37
|
+
}
|
38
|
+
|
39
|
+
sayB() {
|
40
|
+
return 'B'
|
41
|
+
}
|
42
|
+
|
43
|
+
onInit() {
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
export class TestList extends AbstractSDC {
|
48
|
+
constructor() {
|
49
|
+
super();
|
50
|
+
this.contentUrl = 'TestCtrA'; //<test-ctr-a>
|
51
|
+
this.events.unshift({});
|
52
|
+
this.number = 0;
|
53
|
+
}
|
54
|
+
|
55
|
+
onInit(number = 10) {
|
56
|
+
this.number = number;
|
57
|
+
}
|
58
|
+
|
59
|
+
onLoad(html) {
|
60
|
+
$(html).append('<div><this.listview></this.listview></div>');
|
61
|
+
return super.onLoad(html);
|
62
|
+
}
|
63
|
+
|
64
|
+
listview() {
|
65
|
+
const listItems = [];
|
66
|
+
for (let i = 0; i < this.number; i++) {
|
67
|
+
listItems.push(`<test-item data-idx="${i}"></test-item>`);
|
68
|
+
}
|
69
|
+
|
70
|
+
return `<div>${listItems.join('\n')}</div>`;
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
export class TestItem extends AbstractSDC {
|
75
|
+
constructor() {
|
76
|
+
super();
|
77
|
+
this.contentUrl = 'TestCtrA'; //<test-item>
|
78
|
+
this.events.unshift({});
|
79
|
+
}
|
80
|
+
|
81
|
+
onInit(idx) {
|
82
|
+
this.idx = idx;
|
83
|
+
}
|
84
|
+
|
85
|
+
onLoad(html) {
|
86
|
+
$(html).append(`<input name="i_${this.idx}" />`);
|
87
|
+
return super.onLoad(html);
|
88
|
+
}
|
89
|
+
}
|
@@ -0,0 +1,141 @@
|
|
1
|
+
/**
|
2
|
+
* @jest-environment jsdom
|
3
|
+
*/
|
4
|
+
|
5
|
+
import {TestItem, TestList} from "./utils.js";
|
6
|
+
import {reconcile} from "../src/simpleDomControl/sdc_view.js";
|
7
|
+
import {app} from "../src/index.js";
|
8
|
+
import $ from "jquery";
|
9
|
+
|
10
|
+
window.$ = $;
|
11
|
+
|
12
|
+
describe('Test reconcile', () => {
|
13
|
+
|
14
|
+
beforeAll(() => {
|
15
|
+
app.updateJquery();
|
16
|
+
});
|
17
|
+
|
18
|
+
test('Load Content', async () => {
|
19
|
+
const a = '<div>' +
|
20
|
+
'<h1>Test</h1>' +
|
21
|
+
'<ul>' +
|
22
|
+
'<li>B</li>' +
|
23
|
+
'<li><input name="TEST" /></li>' +
|
24
|
+
'<li>D</li>' +
|
25
|
+
'</ul>' +
|
26
|
+
'</div>';
|
27
|
+
|
28
|
+
const b = '<div class="class.1">' +
|
29
|
+
'<p>UPS</p>' +
|
30
|
+
'<p>UPS</p>' +
|
31
|
+
'<h1>Test 1</h1>' +
|
32
|
+
'<ul>' +
|
33
|
+
'<li>A</li>' +
|
34
|
+
'<li>A1 <input name="TEST" type="text"/></li>' +
|
35
|
+
'<li>B</li>' +
|
36
|
+
'<li><input name="TEST" type="text"/></li>' +
|
37
|
+
'<li>D</li>' +
|
38
|
+
'</ul>' +
|
39
|
+
'</div>';
|
40
|
+
const $b = $(b);
|
41
|
+
const $a = $(a);
|
42
|
+
const input_a = $a.find('[name=TEST]')[0]
|
43
|
+
reconcile($b, $a);
|
44
|
+
expect($a.html()).toBe($(b).html());
|
45
|
+
expect($a[0].className).toBe('class.1');
|
46
|
+
expect(input_a).toBe($a.find('[name=TEST]')[0]);
|
47
|
+
|
48
|
+
});
|
49
|
+
|
50
|
+
test('Load Content 2', async () => {
|
51
|
+
const a = '<div>' +
|
52
|
+
'<p>UPS</p>' +
|
53
|
+
'<p>UPS</p>' +
|
54
|
+
'<h1>Test</h1>' +
|
55
|
+
'<ul>' +
|
56
|
+
'<li>B</li>' +
|
57
|
+
'<li><input name="TEST" /></li>' +
|
58
|
+
'<li>D</li>' +
|
59
|
+
'</ul>' +
|
60
|
+
'</div>';
|
61
|
+
|
62
|
+
const b = '<div class="class.1">' +
|
63
|
+
'<h1>Test 1</h1>' +
|
64
|
+
'<ul>' +
|
65
|
+
'<li>A</li>' +
|
66
|
+
'<li>A1 <input name="TEST" type="text"/></li>' +
|
67
|
+
'<li>B</li>' +
|
68
|
+
'<li><input name="TEST" type="text"/></li>' +
|
69
|
+
'<li>D</li>' +
|
70
|
+
'</ul>' +
|
71
|
+
'</div>';
|
72
|
+
const $b = $(b);
|
73
|
+
const $a = $(a);
|
74
|
+
const input_a = $a.find('[name=TEST]')[0]
|
75
|
+
reconcile($b, $a);
|
76
|
+
expect($a.html()).toBe($(b).html());
|
77
|
+
expect($a[0].className).toBe('class.1');
|
78
|
+
expect(input_a).toBe($a.find('[name=TEST]')[0]);
|
79
|
+
|
80
|
+
});
|
81
|
+
|
82
|
+
|
83
|
+
test('Load Content Split', async () => {
|
84
|
+
const a = '<div>' +
|
85
|
+
'<ul>' +
|
86
|
+
'<li>X<input name="TEST" /></li>' +
|
87
|
+
'</ul>' +
|
88
|
+
'</div>';
|
89
|
+
|
90
|
+
const b = '<div class="class.1">' +
|
91
|
+
'<ul>' +
|
92
|
+
'<li>A1 <input name="TEST" type="text"/></li>' +
|
93
|
+
'<li>X</li>' +
|
94
|
+
'</ul>' +
|
95
|
+
'</div>';
|
96
|
+
const $b = $(b);
|
97
|
+
const $a = $(a);
|
98
|
+
const input_a = $a.find('[name=TEST]')[0]
|
99
|
+
reconcile($b, $a);
|
100
|
+
expect($a.html()).toBe($(b).html());
|
101
|
+
expect($a[0].className).toBe('class.1');
|
102
|
+
expect(input_a).toBe($a.find('[name=TEST]')[0]);
|
103
|
+
|
104
|
+
});
|
105
|
+
});
|
106
|
+
|
107
|
+
describe('Controller reconcile', () => {
|
108
|
+
let ajaxSpy;
|
109
|
+
beforeEach(async () => {
|
110
|
+
ajaxSpy = jest.spyOn($, 'ajax');
|
111
|
+
ajaxSpy.mockImplementation(() => {
|
112
|
+
return Promise.resolve('<div></div>');
|
113
|
+
});
|
114
|
+
app.register(TestList);
|
115
|
+
app.register(TestItem);
|
116
|
+
const $body = $('body');
|
117
|
+
const $ctr_div = $(document.createElement('test-list'));
|
118
|
+
$body.append($ctr_div);
|
119
|
+
await app.init_sdc();
|
120
|
+
});
|
121
|
+
|
122
|
+
afterEach(() => {
|
123
|
+
jest.restoreAllMocks();
|
124
|
+
$('body').safeEmpty();
|
125
|
+
});
|
126
|
+
|
127
|
+
test('Load Content Split', async () => {
|
128
|
+
const oldList = $('body').find('input').toArray();
|
129
|
+
const controller = app.getController($('body').children());
|
130
|
+
controller.number = 5;
|
131
|
+
await controller.refresh();
|
132
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
133
|
+
const newList = $('body').find('input').toArray();
|
134
|
+
expect(newList.length).toBe(5);
|
135
|
+
newList.forEach((x, i) => {
|
136
|
+
expect(x).toBe(oldList[i]);
|
137
|
+
});
|
138
|
+
|
139
|
+
|
140
|
+
});
|
141
|
+
});
|