mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
* Remove redundant initial of isArray (#21163) * Reapply prettier * Type the isArray function with refinement support This ensures that an argument gets refined just like it does if isArray is used directly. I'm not sure how to express with just a direct reference so I added a function wrapper and confirmed that this does get inlined properly by closure compiler. * A few more * Rename unit test to internal This is not testing a bundle. Co-authored-by: Behnam Mohammadi <itten@live.com>
275 lines
6.9 KiB
JavaScript
275 lines
6.9 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @jest-environment node
|
|
*/
|
|
'use strict';
|
|
|
|
const ReactNativeAttributePayload = require('../ReactNativeAttributePayload');
|
|
|
|
const diff = ReactNativeAttributePayload.diff;
|
|
|
|
describe('ReactNativeAttributePayload', () => {
|
|
it('should work with simple example', () => {
|
|
expect(diff({a: 1, c: 3}, {b: 2, c: 3}, {a: true, b: true})).toEqual({
|
|
a: null,
|
|
b: 2,
|
|
});
|
|
});
|
|
|
|
it('should skip fields that are equal', () => {
|
|
expect(
|
|
diff(
|
|
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
|
|
{a: 1, b: 'two', c: true, d: false, e: undefined, f: 0},
|
|
{a: true, b: true, c: true, d: true, e: true, f: true},
|
|
),
|
|
).toEqual(null);
|
|
});
|
|
|
|
it('should remove fields', () => {
|
|
expect(diff({a: 1}, {}, {a: true})).toEqual({a: null});
|
|
});
|
|
|
|
it('should remove fields that are set to undefined', () => {
|
|
expect(diff({a: 1}, {a: undefined}, {a: true})).toEqual({a: null});
|
|
});
|
|
|
|
it('should ignore invalid fields', () => {
|
|
expect(diff({a: 1}, {b: 2}, {})).toEqual(null);
|
|
});
|
|
|
|
it('should use the diff attribute', () => {
|
|
const diffA = jest.fn((a, b) => true);
|
|
const diffB = jest.fn((a, b) => false);
|
|
expect(
|
|
diff(
|
|
{a: [1], b: [3]},
|
|
{a: [2], b: [4]},
|
|
{a: {diff: diffA}, b: {diff: diffB}},
|
|
),
|
|
).toEqual({a: [2]});
|
|
expect(diffA).toBeCalledWith([1], [2]);
|
|
expect(diffB).toBeCalledWith([3], [4]);
|
|
});
|
|
|
|
it('should not use the diff attribute on addition/removal', () => {
|
|
const diffA = jest.fn();
|
|
const diffB = jest.fn();
|
|
expect(
|
|
diff({a: [1]}, {b: [2]}, {a: {diff: diffA}, b: {diff: diffB}}),
|
|
).toEqual({a: null, b: [2]});
|
|
expect(diffA).not.toBeCalled();
|
|
expect(diffB).not.toBeCalled();
|
|
});
|
|
|
|
it('should do deep diffs of Objects by default', () => {
|
|
expect(
|
|
diff(
|
|
{a: [1], b: {k: [3, 4]}, c: {k: [4, 4]}},
|
|
{a: [2], b: {k: [3, 4]}, c: {k: [4, 5]}},
|
|
{a: true, b: true, c: true},
|
|
),
|
|
).toEqual({a: [2], c: {k: [4, 5]}});
|
|
});
|
|
|
|
it('should work with undefined styles', () => {
|
|
expect(
|
|
diff(
|
|
{style: {a: '#ffffff', b: 1}},
|
|
{style: undefined},
|
|
{style: {b: true}},
|
|
),
|
|
).toEqual({b: null});
|
|
expect(
|
|
diff(
|
|
{style: undefined},
|
|
{style: {a: '#ffffff', b: 1}},
|
|
{style: {b: true}},
|
|
),
|
|
).toEqual({b: 1});
|
|
expect(
|
|
diff({style: undefined}, {style: undefined}, {style: {b: true}}),
|
|
).toEqual(null);
|
|
});
|
|
|
|
it('should work with empty styles', () => {
|
|
expect(diff({a: 1, c: 3}, {}, {a: true, b: true})).toEqual({a: null});
|
|
expect(diff({}, {a: 1, c: 3}, {a: true, b: true})).toEqual({a: 1});
|
|
expect(diff({}, {}, {a: true, b: true})).toEqual(null);
|
|
});
|
|
|
|
it('should flatten nested styles and predefined styles', () => {
|
|
const validStyleAttribute = {someStyle: {foo: true, bar: true}};
|
|
|
|
expect(
|
|
diff({}, {someStyle: [{foo: 1}, {bar: 2}]}, validStyleAttribute),
|
|
).toEqual({foo: 1, bar: 2});
|
|
|
|
expect(
|
|
diff({someStyle: [{foo: 1}, {bar: 2}]}, {}, validStyleAttribute),
|
|
).toEqual({foo: null, bar: null});
|
|
|
|
const barStyle = {
|
|
bar: 3,
|
|
};
|
|
|
|
expect(
|
|
diff(
|
|
{},
|
|
{someStyle: [[{foo: 1}, {foo: 2}], barStyle]},
|
|
validStyleAttribute,
|
|
),
|
|
).toEqual({foo: 2, bar: 3});
|
|
});
|
|
|
|
it('should reset a value to a previous if it is removed', () => {
|
|
const validStyleAttribute = {someStyle: {foo: true, bar: true}};
|
|
|
|
expect(
|
|
diff(
|
|
{someStyle: [{foo: 1}, {foo: 3}]},
|
|
{someStyle: [{foo: 1}, {bar: 2}]},
|
|
validStyleAttribute,
|
|
),
|
|
).toEqual({foo: 1, bar: 2});
|
|
});
|
|
|
|
it('should not clear removed props if they are still in another slot', () => {
|
|
const validStyleAttribute = {someStyle: {foo: true, bar: true}};
|
|
|
|
expect(
|
|
diff(
|
|
{someStyle: [{}, {foo: 3, bar: 2}]},
|
|
{someStyle: [{foo: 3}, {bar: 2}]},
|
|
validStyleAttribute,
|
|
),
|
|
).toEqual({foo: 3}); // this should ideally be null. heuristic tradeoff.
|
|
|
|
expect(
|
|
diff(
|
|
{someStyle: [{}, {foo: 3, bar: 2}]},
|
|
{someStyle: [{foo: 1, bar: 1}, {bar: 2}]},
|
|
validStyleAttribute,
|
|
),
|
|
).toEqual({bar: 2, foo: 1});
|
|
});
|
|
|
|
it('should clear a prop if a later style is explicit null/undefined', () => {
|
|
const validStyleAttribute = {someStyle: {foo: true, bar: true}};
|
|
expect(
|
|
diff(
|
|
{someStyle: [{}, {foo: 3, bar: 2}]},
|
|
{someStyle: [{foo: 1}, {bar: 2, foo: null}]},
|
|
validStyleAttribute,
|
|
),
|
|
).toEqual({foo: null});
|
|
|
|
expect(
|
|
diff(
|
|
{someStyle: [{foo: 3}, {foo: null, bar: 2}]},
|
|
{someStyle: [{foo: null}, {bar: 2}]},
|
|
validStyleAttribute,
|
|
),
|
|
).toEqual({foo: null});
|
|
|
|
expect(
|
|
diff(
|
|
{someStyle: [{foo: 1}, {foo: null}]},
|
|
{someStyle: [{foo: 2}, {foo: null}]},
|
|
validStyleAttribute,
|
|
),
|
|
).toEqual({foo: null}); // this should ideally be null. heuristic.
|
|
|
|
// Test the same case with object equality because an early bailout doesn't
|
|
// work in this case.
|
|
const fooObj = {foo: 3};
|
|
expect(
|
|
diff(
|
|
{someStyle: [{foo: 1}, fooObj]},
|
|
{someStyle: [{foo: 2}, fooObj]},
|
|
validStyleAttribute,
|
|
),
|
|
).toEqual({foo: 3}); // this should ideally be null. heuristic.
|
|
|
|
expect(
|
|
diff(
|
|
{someStyle: [{foo: 1}, {foo: 3}]},
|
|
{someStyle: [{foo: 2}, {foo: undefined}]},
|
|
validStyleAttribute,
|
|
),
|
|
).toEqual({foo: null}); // this should ideally be null. heuristic.
|
|
});
|
|
|
|
// Function properties are just markers to native that events should be sent.
|
|
it('should convert functions to booleans', () => {
|
|
// Note that if the property changes from one function to another, we don't
|
|
// need to send an update.
|
|
expect(
|
|
diff(
|
|
{
|
|
a: function() {
|
|
return 1;
|
|
},
|
|
b: function() {
|
|
return 2;
|
|
},
|
|
c: 3,
|
|
},
|
|
{
|
|
b: function() {
|
|
return 9;
|
|
},
|
|
c: function() {
|
|
return 3;
|
|
},
|
|
},
|
|
{a: true, b: true, c: true},
|
|
),
|
|
).toEqual({a: null, c: true});
|
|
});
|
|
|
|
it('should skip changed functions', () => {
|
|
expect(
|
|
diff(
|
|
{
|
|
a: function() {
|
|
return 1;
|
|
},
|
|
},
|
|
{
|
|
a: function() {
|
|
return 9;
|
|
},
|
|
},
|
|
{a: true},
|
|
),
|
|
).toEqual(null);
|
|
});
|
|
|
|
it('should skip deeply-nested changed functions', () => {
|
|
expect(
|
|
diff(
|
|
{
|
|
wrapper: {
|
|
a: function() {
|
|
return 1;
|
|
},
|
|
},
|
|
},
|
|
{
|
|
wrapper: {
|
|
a: function() {
|
|
return 9;
|
|
},
|
|
},
|
|
},
|
|
{wrapper: true},
|
|
),
|
|
).toEqual(null);
|
|
});
|
|
});
|