@samitouri / QOS-React-2 / commits / 0fed861424

Move merge utility functions from utils to vendor/core

Paul Shen committed Nov 5, 2013 at 16:56 UTC 0fed861424147a6f79f3d6431ed46b89779a0afe
6 files changed -983
src/utils/__tests__/mergeFuncs-test.js deleted
-983
@@ -1,983 +0,0 @@
1 -/**
2 - * Copyright 2013 Facebook, Inc.
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - *
16 - * @emails react-core
17 - */
18 -
19 -"use strict";
20 -
21 -require('mock-modules')
22 - .dontMock('mergeHelpers')
23 - .dontMock('merge')
24 - .dontMock('mergeDeep')
25 - .dontMock('mergeInto')
26 - .dontMock('mergeDeepInto');
27 -
28 -var mergeHelpers;
29 -var merge;
30 -var mergeInto;
31 -var mergeDeep;
32 -var mergeDeepInto;
33 -
34 -var isTerminal;
35 -var MAX_MERGE_DEPTH;
36 -
37 -/**
38 - * Simple helper to traverse an object, calling the callback (cb) on every
39 - * nonTerminal node it finds. It stops once it reaches the maximum depth,
40 - * because we know that the merge functions shouldn't merge past that depth
41 - * anyways. If you don't "stop" once reaching the MAX_MERGE_DEPTH, there won't
42 - * be a practical way to traverse circular structures.
43 - */
44 -var traverseUntilLeaves = function(obj, cb, depth) {
45 - if ((depth || 0) >= MAX_MERGE_DEPTH) {
46 - return;
47 - }
48 - var key;
49 - if (!isTerminal(obj) && !Array.isArray(obj)) {
50 - cb(obj);
51 - for (key in obj) {
52 - if (!obj.hasOwnProperty(key)) {
53 - continue;
54 - }
55 - traverseUntilLeaves(obj[key], cb, (depth || 0) + 1);
56 - }
57 - }
58 -};
59 -
60 -/*
61 - * Checks that the structure of two merged objects are identical. Checks
62 - * MAX_LEVEL because we don't want to block other unit tests if I have an
63 - * infinite loop.
64 - */
65 -var MAX_LEVEL = 50;
66 -var structureEquals = function(one, two, levelParam) {
67 - var level = levelParam || 0;
68 - var oneKey;
69 - var twoKey;
70 - if (level > MAX_LEVEL) {
71 - throw new Error('structure equals computation explored too many levels');
72 - }
73 - if (isTerminal(one) && isTerminal(two)) {
74 - return one === two;
75 - } else if (!isTerminal(one) && !isTerminal(two)) {
76 - var allInOneAreInTwo = true;
77 - for (oneKey in one) {
78 - if (!one.hasOwnProperty(oneKey)) {
79 - continue;
80 - }
81 - if (!structureEquals(one[oneKey], two[oneKey], level + 1)) {
82 - allInOneAreInTwo = false;
83 - }
84 - }
85 - var allInTwoAreInOne = true;
86 - for (twoKey in two) {
87 - if (!two.hasOwnProperty(twoKey)) {
88 - continue;
89 - }
90 - if (!structureEquals(two[twoKey], one[twoKey], level + 1)) {
91 - allInTwoAreInOne = false;
92 - }
93 - }
94 - // Sure, the computations overlap a bit.
95 - return allInOneAreInTwo && allInTwoAreInOne;
96 - } else {
97 - return false;
98 - }
99 -};
100 -
101 -
102 -var makeCircular = function() {
103 - var obj = {};
104 - obj.self = obj;
105 - return obj;
106 -};
107 -
108 -
109 -
110 -/**
111 - * Not a symmetric function. Checks if there are any node references
112 - * (nonTerminal [non-string, non-null, etc]) that exist in inThis. See
113 - * sharesOnlyTerminalMemory.
114 - */
115 -var hasNoNodeReferencesIn = function(obj, inThis) {
116 - traverseUntilLeaves(obj, function(nonTerm) {
117 - traverseUntilLeaves(inThis, function(inThisNonTerm) {
118 - if (inThisNonTerm === nonTerm) {
119 - return false;
120 - }
121 - });
122 - });
123 - return true;
124 -};
125 -
126 -/**
127 - * Checks that two objects share no memory references with each other (except
128 - * possibly at the terminal nodes (such as strings etc)). This is used to test
129 - * isolation of memory. When certain merge operations occur we need to ensure
130 - * that we did not leak references across objects.
131 - */
132 -var sharesOnlyTerminalMemory = function(a, b) {
133 - return hasNoNodeReferencesIn(a, b) && hasNoNodeReferencesIn(b, a);
134 -};
135 -
136 -/**
137 - * Tests all of the merging functions. You provide it data that describes what
138 - * a deep merge looks like and what a shallow merge looks like. It will make
139 - * sure that various input/output contracts are adhered to, in addition to
140 - * ensuring that various guarantees about side-effects are upheld.
141 - * The test data must provide functions createOne/createTwo as a means to
142 - * create test data for objects one/two respsectively - each time these
143 - * functions must return pristine copies so that we can test for side effects
144 - * correctly.
145 - */
146 -var testAllMerges = function(testData) {
147 - var createOne = testData.createOne;
148 - var createTwo = testData.createTwo;
149 - var one;
150 - var two;
151 -
152 - /*
153 - * Test merge()
154 - */
155 - one = createOne();
156 - two = createTwo();
157 - if (testData.mergeShouldThrow) {
158 - expect(function() {
159 - merge(one, two);
160 - }).toThrow();
161 - } else {
162 - var mergeRes = merge(one, two);
163 - if (testData.shallowResultShouldBe) {
164 - expect(structureEquals(
165 - mergeRes,
166 - testData.shallowResultShouldBe)
167 - ).toBe(true);
168 - }
169 - // Ensure the result is not the exact same object memory reference.
170 - expect(mergeRes === one).toBe(false);
171 - }
172 -
173 - /*
174 - * Test mergeInto()
175 - */
176 - one = createOne();
177 - two = createTwo();
178 - if (testData.mergeIntoShouldThrow) {
179 - expect(function() {
180 - mergeInto(one, two);
181 - }).toThrow();
182 - } else {
183 - var mergeIntoRes = mergeInto(one, two);
184 - if (testData.shallowResultShouldBe) {
185 - expect(structureEquals(
186 - one,
187 - testData.shallowResultShouldBe)
188 - ).toBe(true);
189 - }
190 - /*
191 - * Ensure it's the exact same object reference
192 - * TODO: We should ensure that for every object path p in one points to
193 - * the original object reference in one before the mutation.
194 - */
195 - expect(mergeIntoRes === undefined).toBe(true);
196 - }
197 -
198 - /*
199 - * Test mergeDeep()
200 - */
201 - one = createOne();
202 - two = createTwo();
203 - if (testData.mergeDeepShouldThrow) {
204 - expect(function() {
205 - mergeDeep(one, two, testData.arrayStrategy);
206 - }).toThrow();
207 - } else {
208 - var mergeDeepRes = mergeDeep(one, two, testData.arrayStrategy);
209 - if (testData.deepResultShouldBe) {
210 - expect(structureEquals(
211 - mergeDeepRes,
212 - testData.deepResultShouldBe)
213 - ).toBe(true);
214 - }
215 - expect(sharesOnlyTerminalMemory(mergeDeepRes, one)).toBe(true);
216 - expect(sharesOnlyTerminalMemory(mergeDeepRes, two)).toBe(true);
217 - }
218 -
219 - /*
220 - * Test mergeDeepInto()
221 - */
222 - one = createOne();
223 - two = createTwo();
224 - if (testData.mergeDeepIntoShouldThrow) {
225 - expect(function() {
226 - mergeDeepInto(one, two, testData.arrayStrategy);
227 - }).toThrow();
228 - } else {
229 - var mergeDeepIntoRes = mergeDeepInto(one, two, testData.arrayStrategy);
230 - expect(mergeDeepIntoRes).toBe(undefined);
231 - if (testData.deepResultShouldBe) {
232 - expect(structureEquals(
233 - one,
234 - testData.deepResultShouldBe)
235 - ).toBe(true);
236 - }
237 - expect(mergeDeepIntoRes === undefined).toBe(true);
238 - expect(sharesOnlyTerminalMemory(one, two)).toBe(true);
239 - }
240 -
241 -};
242 -
243 -/**
244 - * ReactTextWithEntities is not a React component, but rather a helper function
245 - * that returns an array. You can use that array as a helper function when
246 - * constructing a structure.
247 - */
248 -describe('mergeFuncs', function() {
249 - beforeEach(function() {
250 - require('mock-modules').dumpCache();
251 - mergeHelpers = require('mergeHelpers');
252 - merge = require('merge');
253 - mergeInto = require('mergeInto');
254 - mergeDeep = require('mergeDeep');
255 - mergeDeepInto = require('mergeDeepInto');
256 - isTerminal = mergeHelpers.isTerminal;
257 - MAX_MERGE_DEPTH = mergeHelpers.MAX_MERGE_DEPTH;
258 - });
259 -
260 - var twoLevelsDeep = function() {
261 - return {
262 - shallowObject: {
263 - deepObject: {
264 - a: 'a',
265 - b: 'b'
266 - },
267 - deepObject2: {
268 - one: 'one',
269 - two: 'two'
270 - }
271 - }
272 - };
273 - };
274 -
275 - it('should not throw if first argument is circular', function() {
276 - testAllMerges({
277 - createOne: makeCircular,
278 - createTwo: function() { return {thisOneIsNotCircular: true}; },
279 - mergeShouldThrow: false,
280 - mergeIntoShouldThrow: false,
281 - mergeDeepShouldThrow: true,
282 - mergeDeepIntoShouldThrow: false
283 - });
284 - });
285 -
286 - it('should throw if second argument is circular', function() {
287 - testAllMerges({
288 - createOne: function() { return {thisOneIsNotCircular: true}; },
289 - createTwo: makeCircular,
290 - mergeShouldThrow: false,
291 - mergeIntoShouldThrow: false,
292 - mergeDeepShouldThrow: true,
293 - mergeDeepIntoShouldThrow: true
294 - });
295 - });
296 -
297 - it('should throw if something deep is circular in second param', function() {
298 - testAllMerges({
299 - createOne: function() { return {thisOneIsNotCircular: true}; },
300 - createTwo: function() {
301 - return {
302 - deeper: {
303 - deeper: makeCircular()
304 - }
305 - };
306 - },
307 - mergeShouldThrow: false,
308 - mergeIntoShouldThrow: false,
309 - mergeDeepShouldThrow: true,
310 - mergeDeepIntoShouldThrow: true
311 - });
312 - });
313 -
314 - it('should throw if something deep is circular in first param', function() {
315 - testAllMerges({
316 - createOne: function() {
317 - return {
318 - deeper: {
319 - deeper: makeCircular()
320 - }
321 - };
322 - },
323 - createTwo: function() { return {thisOneIsNotCircular: true}; },
324 - mergeShouldThrow: false,
325 - mergeIntoShouldThrow: false,
326 - mergeDeepShouldThrow: true,
327 - mergeDeepIntoShouldThrow: false
328 - });
329 - });
330 -
331 -
332 - it('should throw exceptions on invalid data/params', function() {
333 - testAllMerges({
334 - createOne: function() { return []; },
335 - createTwo: function() { return {}; },
336 - mergeShouldThrow: true,
337 - mergeIntoShouldThrow: true,
338 - mergeDeepShouldThrow: true,
339 - mergeDeepIntoShouldThrow: true
340 - });
341 - testAllMerges({
342 - createOne: function() { return {}; },
343 - createTwo: function() { return []; },
344 - mergeShouldThrow: true,
345 - mergeIntoShouldThrow: true,
346 - mergeDeepShouldThrow: true,
347 - mergeDeepIntoShouldThrow: true
348 - });
349 - testAllMerges({
350 - createOne: function() { return []; },
351 - createTwo: function() { return {hasDeepStructure: 'yes'}; },
352 - mergeShouldThrow: true,
353 - mergeIntoShouldThrow: true,
354 - mergeDeepShouldThrow: true,
355 - mergeDeepIntoShouldThrow: true
356 - });
357 - testAllMerges({
358 - createOne: function() { return {hasDeepStructure: 'yes'}; },
359 - createTwo: function() { return []; },
360 - mergeShouldThrow: true,
361 - mergeIntoShouldThrow: true,
362 - mergeDeepShouldThrow: true,
363 - mergeDeepIntoShouldThrow: true
364 - });
365 - testAllMerges({
366 - createOne: function() { return {hasDeepStructure: 'yes'}; },
367 - createTwo: function() { return []; },
368 - mergeShouldThrow: true,
369 - mergeIntoShouldThrow: true,
370 - mergeDeepShouldThrow: true,
371 - mergeDeepIntoShouldThrow: true
372 - });
373 - /*
374 - * mergeInto functions do not accept an empty first parameter. There would
375 - * be nothing to mutate!
376 - */
377 - testAllMerges({
378 - createOne: function() { return null; },
379 - createTwo: function() { return {field: 'yes'}; },
380 - mergeIntoShouldThrow: true,
381 - mergeDeepIntoShouldThrow: true,
382 - shallowResultShouldBe: {field: 'yes'},
383 - deepResultShouldBe: {field: 'yes'}
384 - });
385 - testAllMerges({
386 - createOne: function() { return undefined; },
387 - createTwo: function() { return {field: 'yes'}; },
388 - mergeIntoShouldThrow: true,
389 - mergeDeepIntoShouldThrow: true,
390 - shallowResultShouldBe: {field: 'yes'},
391 - deepResultShouldBe: {field: 'yes'}
392 - });
393 - // Every merge function accepts an empty second parameter.
394 - testAllMerges({
395 - createOne: function() { return {field: 'yes'}; },
396 - createTwo: function() { return null; },
397 - shallowResultShouldBe: {field: 'yes'},
398 - deepResultShouldBe: {field: 'yes'}
399 - });
400 - testAllMerges({
401 - createOne: function() { return null; },
402 - createTwo: function() { return null; },
403 - deepResultShouldBe: {},
404 - shallowResultShouldBe: {},
405 - mergeIntoShouldThrow: true,
406 - mergeDeepIntoShouldThrow: true
407 - });
408 - testAllMerges({
409 - createOne: function() { return {field: 'yes'}; },
410 - createTwo: function() { return undefined; },
411 - shallowResultShouldBe: {field: 'yes'},
412 - deepResultShouldBe: {field: 'yes'}
413 - });
414 - testAllMerges({
415 - createOne: function() { return 0; },
416 - createTwo: function() { return {field: 'yes'}; },
417 - mergeShouldThrow: true,
418 - mergeIntoShouldThrow: true,
419 - mergeDeepShouldThrow: true,
420 - mergeDeepIntoShouldThrow: true
421 - });
422 - /*
423 - * The fact that the second param is empty shouldn't change that we still
424 - * error on arrays.
425 - */
426 - testAllMerges({
427 - createOne: function() { return []; },
428 - createTwo: function() { return undefined; },
429 - mergeShouldThrow: true,
430 - mergeIntoShouldThrow: true,
431 - mergeDeepShouldThrow: true,
432 - mergeDeepIntoShouldThrow: true
433 - });
434 - testAllMerges({
435 - createOne: function() { return {field: 'yes'}; },
436 - createTwo: function() { return null; },
437 - shallowResultShouldBe: {field: 'yes'},
438 - deepResultShouldBe: {field: 'yes'}
439 - });
440 - });
441 -
442 -
443 - it('should merge very deep objects correctly.',
444 - function() {
445 - testAllMerges({
446 - createOne: function() {
447 - return {
448 - shouldBeClobberedByNull: {
449 - color: 'blue'
450 - },
451 - shouldGetTwoFieldsWhenDeepMerge: {
452 - firstField: { color: 'red' }
453 - },
454 - shouldRemainUndefined: undefined,
455 - shouldRemainFour: 4,
456 - shouldRemainZero: 0,
457 - shouldBeClobberedByObj: 0
458 - };
459 - },
460 - createTwo: function() {
461 - return {
462 - shouldBeClobberedByNull: null,
463 - shouldGetTwoFieldsWhenDeepMerge: {
464 - secondField: { color: 'black' }
465 - },
466 - shouldBeClobberedByObj: { thisObj: true }
467 - };
468 - },
469 - shallowResultShouldBe: {
470 - shouldBeClobberedByNull: null,
471 - // only gets one field when merged shallowly
472 - shouldGetTwoFieldsWhenDeepMerge: {
473 - secondField: { color: 'black' }
474 - },
475 - shouldRemainUndefined: undefined,
476 - shouldRemainFour: 4,
477 - shouldRemainZero: 0,
478 - shouldBeClobberedByObj: { thisObj: true }
479 - },
480 - deepResultShouldBe: {
481 - shouldRemainUndefined: undefined,
482 - shouldRemainFour: 4,
483 - shouldRemainZero: 0,
484 - shouldBeClobberedByNull: null,
485 - shouldGetTwoFieldsWhenDeepMerge: {
486 - firstField: { color: 'red' },
487 - secondField: { color: 'black' }
488 - },
489 - shouldBeClobberedByObj: { thisObj: true }
490 - }
491 - });
492 - }
493 - );
494 -
495 - it(
496 - 'should not throw with one non-colliding arrays with no arrayStrategy',
497 - function() {
498 - testAllMerges({
499 - createOne: function() { return { hasDeepStructure: 'yes' }; },
500 - createTwo: function() { return { hasArrayDeeper: [] }; },
501 - mergeShouldThrow: false,
502 - mergeIntoShouldThrow: false,
503 - mergeDeepShouldThrow: false,
504 - mergeDeepIntoShouldThrow: false,
505 - shallowResultShouldBe: {
506 - hasDeepStructure: 'yes',
507 - hasArrayDeeper: []
508 - },
509 - deepResultShouldBe: {
510 - hasDeepStructure: 'yes',
511 - hasArrayDeeper: []
512 - }
513 - });
514 - }
515 - );
516 -
517 - it(
518 - 'should not throw with two non-colliding arrays with no arrayStrategy',
519 - function() {
520 - testAllMerges({
521 - createOne: function() {
522 - return {
523 - arrayOne: [1]
524 - };
525 - },
526 - createTwo: function() {
527 - return {
528 - arrayTwo: [2]
529 - };
530 - },
531 - arrayStrategy: mergeHelpers.ArrayStrategies.Clobber,
532 - mergeShouldThrow: false,
533 - mergeIntoShouldThrow: false,
534 - mergeDeepShouldThrow: false,
535 - mergeDeepIntoShouldThrow: false
536 - });
537 - }
538 - ),
539 -
540 - it('should throw if no arrayStrategy supplied on Array merges', function() {
541 - testAllMerges({
542 - createOne: function() {
543 - return {
544 - arrayOne: [1]
545 - };
546 - },
547 - createTwo: function() {
548 - return {
549 - arrayOne: [9]
550 - };
551 - },
552 - shallowResultShouldBe: {
553 - arrayOne: [9]
554 - },
555 - // Doesn't throw on the shallow versions.
556 - mergeShouldThrow: false,
557 - mergeIntoShouldThrow: false,
558 - mergeDeepShouldThrow: true,
559 - mergeDeepIntoShouldThrow: true
560 - });
561 - }),
562 -
563 - it('should not throw if never forced to merge two Arrays', function() {
564 - testAllMerges({
565 - createOne: function() {
566 - return {
567 - arrayOne: [1]
568 - };
569 - },
570 - createTwo: function() {
571 - return {
572 - arrayTwo: [9] // <-- See, never forced to merge with arrayOne
573 - };
574 - },
575 - shallowResultShouldBe: {
576 - arrayOne: [1],
577 - arrayTwo: [9]
578 - },
579 - // Doesn't throw on the shallow versions.
580 - mergeShouldThrow: false,
581 - mergeIntoShouldThrow: false,
582 - mergeDeepShouldThrow: false,
583 - mergeDeepIntoShouldThrow: false
584 - });
585 - }),
586 -
587 - it(
588 - 'should correctly deep merge Arrays with arrayStrategy clobber',
589 - function() {
590 - testAllMerges({
591 - createOne: function() {
592 - return {
593 - deep: {
594 - array: [1],
595 - nonArray: {
596 - hi: 'hi'
597 - }
598 - }
599 - };
600 - },
601 - createTwo: function() {
602 - return {
603 - deep: {
604 - array: [10,11, {deepFieldInArray: 'yes'}],
605 - nonArray: {
606 - bye: 'bye'
607 - }
608 - }
609 - };
610 - },
611 - shallowResultShouldBe: {
612 - deep: {
613 - array: [10,11, {deepFieldInArray: 'yes'}],
614 - nonArray: {
615 - bye: 'bye'
616 - }
617 - }
618 - },
619 - deepResultShouldBe: {
620 - deep: {
621 - array: [10,11, {deepFieldInArray: 'yes'}],
622 - nonArray: {
623 - hi: 'hi',
624 - bye: 'bye'
625 - }
626 - }
627 - },
628 - arrayStrategy: mergeHelpers.ArrayStrategies.Clobber,
629 - mergeShouldThrow: false,
630 - mergeIntoShouldThrow: false,
631 - mergeDeepShouldThrow: false,
632 - mergeDeepIntoShouldThrow: false
633 - });
634 - }
635 - ),
636 -
637 -
638 - it(
639 - 'should correctly deep merge Arrays with arrayStrategy indexByIndex',
640 - function() {
641 - testAllMerges({
642 - createOne: function() {
643 - return {
644 - deep: {
645 - array: [{
646 - atIndexZero: {
647 - indexZeroGreeting: 'You Are At Index Zero',
648 - indexZeroStatus: false
649 - }
650 - }]
651 - }
652 - };
653 - },
654 - createTwo: function() {
655 - return {
656 - deep: {
657 - array: [{
658 - atIndexZero: {
659 - indexZeroStatus: true,
660 - newFieldAtIndexZero: 'newField'
661 - }
662 - },
663 - {
664 - atIndexOne: {
665 - nothingOriginallyAtThisIndex: true
666 - }
667 - }]
668 - }
669 - };
670 - },
671 - shallowResultShouldBe: {
672 - deep: {
673 - array: [{
674 - atIndexZero: {
675 - indexZeroStatus: true,
676 - newFieldAtIndexZero: 'newField'
677 - }
678 - },
679 - {
680 - atIndexOne: {
681 - nothingOriginallyAtThisIndex: true
682 - }
683 - }]
684 - }
685 - },
686 - deepResultShouldBe: {
687 - deep: {
688 - array: [{
689 - atIndexZero: {
690 - indexZeroGreeting: 'You Are At Index Zero', // <-- See!?
691 - indexZeroStatus: true,
692 - newFieldAtIndexZero: 'newField'
693 - }
694 - },
695 - {
696 - atIndexOne: {
697 - nothingOriginallyAtThisIndex: true
698 - }
699 - }]
700 - }
701 - },
702 - arrayStrategy: mergeHelpers.ArrayStrategies.IndexByIndex,
703 - mergeShouldThrow: false,
704 - mergeIntoShouldThrow: false,
705 - mergeDeepShouldThrow: false,
706 - mergeDeepIntoShouldThrow: false
707 - }
708 - );
709 - }),
710 -
711 - it('should merge deep data correctly', function() {
712 - var mergeData = {
713 - shallowObject: {
714 - deepObject: {
715 - c: 'c',
716 - d: 'd'
717 - },
718 - deepObject2: {
719 - three: 'three',
720 - four: 'four'
721 - }
722 - }
723 - };
724 - var deepResult = {
725 - shallowObject: {
726 - deepObject: {
727 - a: 'a',
728 - b: 'b',
729 - c: 'c',
730 - d: 'd'
731 - },
732 - deepObject2: {
733 - one: 'one',
734 - two: 'two',
735 - three: 'three',
736 - four: 'four'
737 - }
738 - }
739 - };
740 - var shallowResult = mergeData; // The same
741 -
742 - testAllMerges({
743 - createOne: function() { return twoLevelsDeep(); },
744 - createTwo: function() { return mergeData; },
745 - mergeShouldThrow: false,
746 - mergeIntoShouldThrow: false,
747 - mergeDeepShouldThrow: false,
748 - mergeDeepIntoShouldThrow: false,
749 - deepResultShouldBe: deepResult,
750 - shallowResultShouldBe: shallowResult
751 - });
752 - });
753 -
754 - it('should fill in complementing regions of data trees', function() {
755 - var mergeData = {
756 - shallowObject: {
757 - newDepth: {
758 - hello: 'goodbye'
759 - }
760 - }
761 - };
762 - var deepResult = {
763 - shallowObject: {
764 - deepObject: {
765 - a: 'a',
766 - b: 'b'
767 - },
768 - deepObject2: {
769 - one: 'one',
770 - two: 'two'
771 - },
772 - // This is added!
773 - newDepth: {
774 - hello: 'goodbye'
775 - }
776 - }
777 - };
778 - var shallowResult = mergeData; // The same
779 -
780 - testAllMerges({
781 - createOne: function() { return twoLevelsDeep(); },
782 - createTwo: function() { return mergeData; },
783 - // The deep merge of the two
784 - deepResultShouldBe: deepResult,
785 - shallowResultShouldBe: shallowResult
786 - });
787 - });
788 -
789 - /**
790 - * Arrays and Objects wipe out terminals (and vice versa).
791 - */
792 - it('should clobber terminals with objects/arrays vice versa', function() {
793 - var makeOne = function() {
794 - return {
795 - thisFieldDoesNothing: {},
796 - deep: {
797 - // Objects
798 - thisShouldSwitchFromObjectToTerminal: {hello: 'goodbye'},
799 - thisShouldSwitchFromTerminalToObject: 'yes',
800 -
801 - // Arrays
802 - thisShouldSwitchFromArrayToTerminal: ['goodbye'],
803 - thisShouldSwitchFromTerminalToArray: 'yes'
804 - }
805 - };
806 - };
807 - var makeTwo = function() {
808 - return {
809 - deep: {
810 - // Objects
811 - thisShouldSwitchFromObjectToTerminal: 'terminal',
812 - thisShouldSwitchFromTerminalToObject: {nowItsAnObject: 'yay'},
813 -
814 - // Arrays
815 - thisShouldSwitchFromArrayToTerminal: 'terminal',
816 - thisShouldSwitchFromTerminalToArray: ['yay']
817 - }
818 - };
819 - };
820 - var deepResult = {
821 - thisFieldDoesNothing: {
822 -
823 - },
824 - deep: {
825 - // Objects
826 - thisShouldSwitchFromObjectToTerminal: 'terminal',
827 - thisShouldSwitchFromTerminalToObject: {nowItsAnObject: 'yay'},
828 -
829 - // Arrays
830 - thisShouldSwitchFromArrayToTerminal: 'terminal',
831 - thisShouldSwitchFromTerminalToArray: ['yay']
832 - }
833 - };
834 -
835 - testAllMerges({
836 - createOne: makeOne,
837 - createTwo: makeTwo,
838 - deepResultShouldBe: deepResult, // The deep merge of the two
839 - shallowResultShouldBe: deepResult // The same this time.
840 - });
841 - });
842 -
843 - /**
844 - * Arrays wipe out Objects and Objects wipe out Arrays.
845 - */
846 - it('should clobber terminals with objects/arrays vice versa', function() {
847 - var makeOne = function() {
848 - return {
849 - thisFieldDoesNothing: {},
850 - deep: {
851 - thisShouldSwitchFromObjectToArray: {hello: 'goodbye'},
852 - thisShouldSwitchFromArrayToObject: [1,2,3]
853 - }
854 - };
855 - };
856 - var makeTwo = function() {
857 - return {
858 - deep: {
859 - thisShouldSwitchFromObjectToArray: [7,8,9],
860 - thisShouldSwitchFromArrayToObject: {iUsedToBeAnArray:true}
861 - }
862 - };
863 - };
864 - var deepResult = {
865 - thisFieldDoesNothing: {},
866 - deep: {
867 - thisShouldSwitchFromObjectToArray: [7,8,9],
868 - thisShouldSwitchFromArrayToObject: {iUsedToBeAnArray:true}
869 - }
870 - };
871 -
872 - testAllMerges({
873 - createOne: makeOne,
874 - createTwo: makeTwo,
875 - deepResultShouldBe: deepResult, // The deep merge of the two
876 - shallowResultShouldBe: deepResult // The same this time.
877 - });
878 - });
879 -
880 - it(
881 - 'should not clobber terminals/objects/arrays that are not over-written',
882 - function() {
883 - testAllMerges({
884 - createOne: function() {
885 - return {
886 - deep: {
887 - terminal: 'I am a terminal',
888 - object: {iAmAnObject: true},
889 - array: [1,2,3]
890 - }
891 - };
892 - },
893 - createTwo: function() {
894 - return {
895 - deep: {
896 - completelyDifferentField: 'no collision here!'
897 - }
898 - };
899 - },
900 - shallowResultShouldBe: {
901 - deep: {
902 - completelyDifferentField: 'no collision here!'
903 - }
904 - },
905 - deepResultShouldBe: {
906 - deep: {
907 - completelyDifferentField: 'no collision here!',
908 - terminal: 'I am a terminal',
909 - object: {iAmAnObject: true},
910 - array: [1,2,3]
911 - }
912 - },
913 - mergeShouldThrow: false,
914 - mergeIntoShouldThrow: false,
915 - mergeDeepShouldThrow: false,
916 - mergeDeepIntoShouldThrow: false
917 - }
918 - );
919 - }),
920 -
921 - it('should correctly clobber terminals with cloned arrays', function() {
922 - testAllMerges({
923 - createOne: function() {
924 - return {
925 - deep: {
926 - changesFromTermToArray: 'I am a terminal'
927 - }
928 - };
929 - },
930 - createTwo: function() {
931 - return {
932 - deep: {
933 - changesFromTermToArray: ['I', 'am', 'an', 'array']
934 - }
935 - };
936 - },
937 - shallowResultShouldBe: {
938 - deep: {
939 - changesFromTermToArray: ['I', 'am', 'an', 'array']
940 - }
941 - },
942 - deepResultShouldBe: {
943 - deep: {
944 - changesFromTermToArray: ['I', 'am', 'an', 'array']
945 - }
946 - },
947 - mergeShouldThrow: false,
948 - mergeIntoShouldThrow: false,
949 - mergeDeepShouldThrow: false,
950 - mergeDeepIntoShouldThrow: false
951 - });
952 - }),
953 -
954 - it('should detect terminals correctly in merge* functions', function() {
955 - var boolResFalse = isTerminal(false);
956 - expect(boolResFalse).toBe(true);
957 -
958 - var boolResTrue = isTerminal(true);
959 - expect(boolResTrue).toBe(true);
960 -
961 - var numRes = isTerminal(123);
962 - expect(numRes).toBe(true);
963 -
964 - var zeroRes = isTerminal(0);
965 - expect(zeroRes).toBe(true);
966 -
967 - var stringRes = isTerminal('i am a string');
968 - expect(stringRes).toBe(true);
969 -
970 - var objLiteralRes = isTerminal({somethingHere: true});
971 - expect(objLiteralRes).toBe(false);
972 -
973 - var undefRes = isTerminal(undefined);
974 - expect(undefRes).toBe(true);
975 -
976 - var nullRes = isTerminal(null);
977 - expect(nullRes).toBe(true);
978 -
979 - var arrRes = isTerminal([]);
980 - expect(arrRes).toBe(false);
981 - });
982 -});
983 -
src/vendor/core/merge.js renamed
src/vendor/core/mergeDeep.js renamed
src/vendor/core/mergeDeepInto.js renamed
src/vendor/core/mergeHelpers.js renamed
src/vendor/core/mergeInto.js renamed