DevTools: Add Jest snapshot serializer for number formatting (#23139)
Numbers in JavaScript can have precision issues due to how they are encoded. This shows up in snapshot tests sometimes with values like 0.0009999999999999992, which makes the tests hard to read and visually diff. This PR adds a new snapshot serializers which clamps numbers at 3 decimal points (e.g. the above number 0.0009999999999999992 is serialized as 0.001). This new serializer does not impact non-numeric values, integers, and special numbers like NaN and Infinity.
Brian Vaughn committed
Jan 20, 2022 at 15:52 UTC
a63708432082d480d422b0c93de5403611d9ce38
10 files changed
+57
-30
packages/react-devtools-shared/src/__tests__/__serializers__/dehydratedValueSerializer.js
renamed
packages/react-devtools-shared/src/__tests__/__serializers__/hookSerializer.js
renamed
packages/react-devtools-shared/src/__tests__/__serializers__/inspectedElementSerializer.js
renamed
packages/react-devtools-shared/src/__tests__/__serializers__/numberToFixedSerializer.js
new
+24
@@ -0,0 +1,24 @@
1
+const MAX_DECIMAL_PLACES = 3;
2
+
3
+// test() is part of Jest's serializer API
4
+export function test(maybeNumber) {
5
+ return (
6
+ typeof maybeNumber === 'number' &&
7
+ Number.isFinite(maybeNumber) &&
8
+ !Number.isInteger(maybeNumber) &&
9
+ !Number.isNaN(maybeNumber)
10
+ );
11
+}
12
+
13
+// print() is part of Jest's serializer API
14
+export function print(number, serialize, indent) {
15
+ const string = number.toString();
16
+ const pieces = string.split('.');
17
+ if (pieces.length === 2) {
18
+ if (pieces[1].length > MAX_DECIMAL_PLACES) {
19
+ return number.toFixed(MAX_DECIMAL_PLACES);
20
+ }
21
+ }
22
+
23
+ return string;
24
+}
packages/react-devtools-shared/src/__tests__/__serializers__/storeSerializer.js
renamed
packages/react-devtools-shared/src/__tests__/__serializers__/treeContextStateSerializer.js
renamed
packages/react-devtools-shared/src/__tests__/preprocessData-test.js
+23
-23
@@ -439,7 +439,7 @@ describe('Timeline profiler', () => {
439
Object {
440
"batchUID": 0,
441
"depth": 0,
442
- "duration": 0.004999999999999999,
442
+ "duration": 0.005,
443
"lanes": Array [
444
9,
445
],
@@ -459,7 +459,7 @@ describe('Timeline profiler', () => {
459
Object {
460
"batchUID": 0,
461
"depth": 0,
462
- "duration": 0.002999999999999999,
462
+ "duration": 0.003,
463
"lanes": Array [
464
9,
465
],
@@ -469,7 +469,7 @@ describe('Timeline profiler', () => {
469
Object {
470
"batchUID": 0,
471
"depth": 1,
472
- "duration": 0.0010000000000000009,
472
+ "duration": 0.001,
473
"lanes": Array [
474
9,
475
],
@@ -529,7 +529,7 @@ describe('Timeline profiler', () => {
529
Object {
530
"batchUID": 0,
531
"depth": 0,
532
- "duration": 0.004999999999999999,
532
+ "duration": 0.005,
533
"lanes": Array [
534
9,
535
],
@@ -549,7 +549,7 @@ describe('Timeline profiler', () => {
549
Object {
550
"batchUID": 0,
551
"depth": 0,
552
- "duration": 0.002999999999999999,
552
+ "duration": 0.003,
553
"lanes": Array [
554
9,
555
],
@@ -559,7 +559,7 @@ describe('Timeline profiler', () => {
559
Object {
560
"batchUID": 0,
561
"depth": 1,
562
- "duration": 0.0010000000000000009,
562
+ "duration": 0.001,
563
"lanes": Array [
564
9,
565
],
@@ -656,7 +656,7 @@ describe('Timeline profiler', () => {
656
Object {
657
"batchUID": 0,
658
"depth": 1,
659
- "duration": 0.0009999999999999992,
659
+ "duration": 0.001,
660
"lanes": Array [
661
0,
662
],
@@ -748,7 +748,7 @@ describe('Timeline profiler', () => {
748
Object {
749
"batchUID": 0,
750
"depth": 1,
751
- "duration": 0.0009999999999999992,
751
+ "duration": 0.001,
752
"lanes": Array [
753
0,
754
],
@@ -865,7 +865,7 @@ describe('Timeline profiler', () => {
865
Object {
866
"batchUID": 0,
867
"depth": 1,
868
- "duration": 0.0009999999999999992,
868
+ "duration": 0.001,
869
"lanes": Array [
870
4,
871
],
@@ -887,7 +887,7 @@ describe('Timeline profiler', () => {
887
Object {
888
"batchUID": 1,
889
"depth": 0,
890
- "duration": 0.012000000000000004,
890
+ "duration": 0.012,
891
"lanes": Array [
892
4,
893
],
@@ -897,7 +897,7 @@ describe('Timeline profiler', () => {
897
Object {
898
"batchUID": 1,
899
"depth": 0,
900
- "duration": 0.0030000000000000027,
900
+ "duration": 0.003,
901
"lanes": Array [
902
4,
903
],
@@ -907,7 +907,7 @@ describe('Timeline profiler', () => {
907
Object {
908
"batchUID": 1,
909
"depth": 0,
910
- "duration": 0.008000000000000004,
910
+ "duration": 0.008,
911
"lanes": Array [
912
4,
913
],
@@ -917,7 +917,7 @@ describe('Timeline profiler', () => {
917
Object {
918
"batchUID": 1,
919
"depth": 1,
920
- "duration": 0.0010000000000000009,
920
+ "duration": 0.001,
921
"lanes": Array [
922
4,
923
],
@@ -927,7 +927,7 @@ describe('Timeline profiler', () => {
927
Object {
928
"batchUID": 1,
929
"depth": 0,
930
- "duration": 0.0029999999999999957,
930
+ "duration": 0.003,
931
"lanes": Array [
932
4,
933
],
@@ -946,21 +946,21 @@ describe('Timeline profiler', () => {
946
},
947
Object {
948
"componentName": "App",
949
- "duration": 0.0020000000000000018,
949
+ "duration": 0.002,
950
"timestamp": 0.018,
951
"type": "passive-effect-mount",
952
"warning": null,
953
},
954
Object {
955
"componentName": "App",
956
- "duration": 0.0010000000000000009,
956
+ "duration": 0.001,
957
"timestamp": 0.023,
958
"type": "render",
959
"warning": null,
960
},
961
Object {
962
"componentName": "App",
963
- "duration": 0.0010000000000000009,
963
+ "duration": 0.001,
964
"timestamp": 0.036,
965
"type": "passive-effect-mount",
966
"warning": null,
@@ -1052,7 +1052,7 @@ describe('Timeline profiler', () => {
1052
Object {
1053
"batchUID": 0,
1054
"depth": 1,
1055
- "duration": 0.0009999999999999992,
1055
+ "duration": 0.001,
1056
"lanes": Array [
1057
4,
1058
],
@@ -1072,7 +1072,7 @@ describe('Timeline profiler', () => {
1072
Object {
1073
"batchUID": 1,
1074
"depth": 0,
1075
- "duration": 0.012000000000000004,
1075
+ "duration": 0.012,
1076
"lanes": Array [
1077
4,
1078
],
@@ -1082,7 +1082,7 @@ describe('Timeline profiler', () => {
1082
Object {
1083
"batchUID": 1,
1084
"depth": 0,
1085
- "duration": 0.0030000000000000027,
1085
+ "duration": 0.003,
1086
"lanes": Array [
1087
4,
1088
],
@@ -1092,7 +1092,7 @@ describe('Timeline profiler', () => {
1092
Object {
1093
"batchUID": 1,
1094
"depth": 0,
1095
- "duration": 0.008000000000000004,
1095
+ "duration": 0.008,
1096
"lanes": Array [
1097
4,
1098
],
@@ -1102,7 +1102,7 @@ describe('Timeline profiler', () => {
1102
Object {
1103
"batchUID": 1,
1104
"depth": 1,
1105
- "duration": 0.0010000000000000009,
1105
+ "duration": 0.001,
1106
"lanes": Array [
1107
4,
1108
],
@@ -1112,7 +1112,7 @@ describe('Timeline profiler', () => {
1112
Object {
1113
"batchUID": 1,
1114
"depth": 0,
1115
- "duration": 0.0029999999999999957,
1115
+ "duration": 0.003,
1116
"lanes": Array [
1117
4,
1118
],
packages/react-devtools-shared/src/__tests__/storeStressSync-test.js
+1
-1
@@ -28,7 +28,7 @@ describe('StoreStress (Legacy Mode)', () => {
28
act = utils.act;
29
legacyRender = utils.legacyRender;
30
31
- print = require('./storeSerializer').print;
31
+ print = require('./__serializers__/storeSerializer').print;
32
});
33
34
// This is a stress test for the tree mount/update/unmount traversal.
packages/react-devtools-shared/src/__tests__/storeStressTestConcurrent-test.js
+1
-1
@@ -28,7 +28,7 @@ describe('StoreStressConcurrent', () => {
28
// this helper with the real thing.
29
actAsync = require('./utils').actAsync;
30
31
- print = require('./storeSerializer').print;
31
+ print = require('./__serializers__/storeSerializer').print;
32
});
33
34
// TODO: Remove this in favor of @gate pragma
scripts/jest/config.build-devtools.js
+8
-5
@@ -62,19 +62,22 @@ module.exports = Object.assign({}, baseConfig, {
62
testRegex: 'packages/react-devtools-shared/.+/__tests__/[^]+.test.js$',
63
snapshotSerializers: [
64
require.resolve(
65
- '../../packages/react-devtools-shared/src/__tests__/dehydratedValueSerializer.js'
65
+ '../../packages/react-devtools-shared/src/__tests__/__serializers__/dehydratedValueSerializer.js'
66
),
67
require.resolve(
68
- '../../packages/react-devtools-shared/src/__tests__/hookSerializer.js'
68
+ '../../packages/react-devtools-shared/src/__tests__/__serializers__/hookSerializer.js'
69
),
70
require.resolve(
71
- '../../packages/react-devtools-shared/src/__tests__/inspectedElementSerializer.js'
71
+ '../../packages/react-devtools-shared/src/__tests__/__serializers__/inspectedElementSerializer.js'
72
),
73
require.resolve(
74
- '../../packages/react-devtools-shared/src/__tests__/storeSerializer.js'
74
+ '../../packages/react-devtools-shared/src/__tests__/__serializers__/storeSerializer.js'
75
),
76
require.resolve(
77
- '../../packages/react-devtools-shared/src/__tests__/treeContextStateSerializer.js'
77
+ '../../packages/react-devtools-shared/src/__tests__/__serializers__/treeContextStateSerializer.js'
78
+ ),
79
+ require.resolve(
80
+ '../../packages/react-devtools-shared/src/__tests__/__serializers__/numberToFixedSerializer.js'
81
),
82
],
83
setupFiles: [