@flighthq/physics3d 0.4.0-edge.1908.751021c
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/dist/broadphase.d.ts +4 -0
- package/dist/broadphase.d.ts.map +1 -0
- package/dist/broadphase.js +158 -0
- package/dist/broadphase.js.map +1 -0
- package/dist/colliderCollision.d.ts +5 -0
- package/dist/colliderCollision.d.ts.map +1 -0
- package/dist/colliderCollision.js +55 -0
- package/dist/colliderCollision.js.map +1 -0
- package/dist/colliderTransform.d.ts +5 -0
- package/dist/colliderTransform.d.ts.map +1 -0
- package/dist/colliderTransform.js +424 -0
- package/dist/colliderTransform.js.map +1 -0
- package/dist/contactIntake.d.ts +5 -0
- package/dist/contactIntake.d.ts.map +1 -0
- package/dist/contactIntake.js +237 -0
- package/dist/contactIntake.js.map +1 -0
- package/dist/contacts.d.ts +4 -0
- package/dist/contacts.d.ts.map +1 -0
- package/dist/contacts.js +64 -0
- package/dist/contacts.js.map +1 -0
- package/dist/continuous.d.ts +5 -0
- package/dist/continuous.d.ts.map +1 -0
- package/dist/continuous.js +829 -0
- package/dist/continuous.js.map +1 -0
- package/dist/contract.d.ts +28 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +28 -0
- package/dist/contract.js.map +1 -0
- package/dist/debugGeometry.d.ts +4 -0
- package/dist/debugGeometry.d.ts.map +1 -0
- package/dist/debugGeometry.js +333 -0
- package/dist/debugGeometry.js.map +1 -0
- package/dist/enablePhysics3DGuards.d.ts +4 -0
- package/dist/enablePhysics3DGuards.d.ts.map +1 -0
- package/dist/enablePhysics3DGuards.js +155 -0
- package/dist/enablePhysics3DGuards.js.map +1 -0
- package/dist/explainPhysics3DCollision.d.ts +3 -0
- package/dist/explainPhysics3DCollision.d.ts.map +1 -0
- package/dist/explainPhysics3DCollision.js +30 -0
- package/dist/explainPhysics3DCollision.js.map +1 -0
- package/dist/explainPhysics3DJoints.d.ts +3 -0
- package/dist/explainPhysics3DJoints.d.ts.map +1 -0
- package/dist/explainPhysics3DJoints.js +32 -0
- package/dist/explainPhysics3DJoints.js.map +1 -0
- package/dist/explainPhysics3DStep.d.ts +3 -0
- package/dist/explainPhysics3DStep.d.ts.map +1 -0
- package/dist/explainPhysics3DStep.js +51 -0
- package/dist/explainPhysics3DStep.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/integrate.d.ts +6 -0
- package/dist/integrate.d.ts.map +1 -0
- package/dist/integrate.js +184 -0
- package/dist/integrate.js.map +1 -0
- package/dist/islands.d.ts +6 -0
- package/dist/islands.d.ts.map +1 -0
- package/dist/islands.js +333 -0
- package/dist/islands.js.map +1 -0
- package/dist/jointBreakage.d.ts +5 -0
- package/dist/jointBreakage.d.ts.map +1 -0
- package/dist/jointBreakage.js +44 -0
- package/dist/jointBreakage.js.map +1 -0
- package/dist/jointCollisionSuppression.d.ts +4 -0
- package/dist/jointCollisionSuppression.d.ts.map +1 -0
- package/dist/jointCollisionSuppression.js +40 -0
- package/dist/jointCollisionSuppression.js.map +1 -0
- package/dist/jointFactories.d.ts +9 -0
- package/dist/jointFactories.d.ts.map +1 -0
- package/dist/jointFactories.js +159 -0
- package/dist/jointFactories.js.map +1 -0
- package/dist/jointMath.d.ts +17 -0
- package/dist/jointMath.d.ts.map +1 -0
- package/dist/jointMath.js +361 -0
- package/dist/jointMath.js.map +1 -0
- package/dist/jointReaction.d.ts +8 -0
- package/dist/jointReaction.d.ts.map +1 -0
- package/dist/jointReaction.js +65 -0
- package/dist/jointReaction.js.map +1 -0
- package/dist/jointRegistry.d.ts +8 -0
- package/dist/jointRegistry.d.ts.map +1 -0
- package/dist/jointRegistry.js +144 -0
- package/dist/jointRegistry.js.map +1 -0
- package/dist/jointRows.d.ts +33 -0
- package/dist/jointRows.d.ts.map +1 -0
- package/dist/jointRows.js +352 -0
- package/dist/jointRows.js.map +1 -0
- package/dist/joints.d.ts +16 -0
- package/dist/joints.d.ts.map +1 -0
- package/dist/joints.js +1060 -0
- package/dist/joints.js.map +1 -0
- package/dist/massProperties.d.ts +13 -0
- package/dist/massProperties.d.ts.map +1 -0
- package/dist/massProperties.js +462 -0
- package/dist/massProperties.js.map +1 -0
- package/dist/material.d.ts +3 -0
- package/dist/material.d.ts.map +1 -0
- package/dist/material.js +13 -0
- package/dist/material.js.map +1 -0
- package/dist/nodeSync.d.ts +3 -0
- package/dist/nodeSync.d.ts.map +1 -0
- package/dist/nodeSync.js +16 -0
- package/dist/nodeSync.js.map +1 -0
- package/dist/ownership.d.ts +8 -0
- package/dist/ownership.d.ts.map +1 -0
- package/dist/ownership.js +26 -0
- package/dist/ownership.js.map +1 -0
- package/dist/physics3DBroadphasePublication.d.ts +5 -0
- package/dist/physics3DBroadphasePublication.d.ts.map +1 -0
- package/dist/physics3DBroadphasePublication.js +49 -0
- package/dist/physics3DBroadphasePublication.js.map +1 -0
- package/dist/physics3DSpatialIndexingGuards.d.ts +4 -0
- package/dist/physics3DSpatialIndexingGuards.d.ts.map +1 -0
- package/dist/physics3DSpatialIndexingGuards.js +11 -0
- package/dist/physics3DSpatialIndexingGuards.js.map +1 -0
- package/dist/registerBuiltInPhysics3DJointSolvers.d.ts +3 -0
- package/dist/registerBuiltInPhysics3DJointSolvers.d.ts.map +1 -0
- package/dist/registerBuiltInPhysics3DJointSolvers.js +15 -0
- package/dist/registerBuiltInPhysics3DJointSolvers.js.map +1 -0
- package/dist/solver.d.ts +8 -0
- package/dist/solver.d.ts.map +1 -0
- package/dist/solver.js +460 -0
- package/dist/solver.js.map +1 -0
- package/dist/step.d.ts +5 -0
- package/dist/step.d.ts.map +1 -0
- package/dist/step.js +302 -0
- package/dist/step.js.map +1 -0
- package/dist/stepValidation.d.ts +13 -0
- package/dist/stepValidation.d.ts.map +1 -0
- package/dist/stepValidation.js +284 -0
- package/dist/stepValidation.js.map +1 -0
- package/dist/symmetricTensor.d.ts +11 -0
- package/dist/symmetricTensor.d.ts.map +1 -0
- package/dist/symmetricTensor.js +150 -0
- package/dist/symmetricTensor.js.map +1 -0
- package/dist/world.d.ts +27 -0
- package/dist/world.d.ts.map +1 -0
- package/dist/world.js +762 -0
- package/dist/world.js.map +1 -0
- package/dist/worldQueries.d.ts +11 -0
- package/dist/worldQueries.d.ts.map +1 -0
- package/dist/worldQueries.js +409 -0
- package/dist/worldQueries.js.map +1 -0
- package/package.json +55 -0
- package/src/broadphase.test.ts +265 -0
- package/src/colliderCollision.test.ts +57 -0
- package/src/colliderTransform.test.ts +221 -0
- package/src/contactIntake.test.ts +370 -0
- package/src/contacts.test.ts +64 -0
- package/src/continuous.test.ts +679 -0
- package/src/debugGeometry.test.ts +288 -0
- package/src/enablePhysics3DGuards.test.ts +206 -0
- package/src/explainPhysics3DCollision.test.ts +78 -0
- package/src/explainPhysics3DJoints.test.ts +101 -0
- package/src/explainPhysics3DStep.test.ts +142 -0
- package/src/integrate.test.ts +279 -0
- package/src/islands.test.ts +384 -0
- package/src/jointBreakage.test.ts +143 -0
- package/src/jointCollisionSuppression.test.ts +94 -0
- package/src/jointFactories.test.ts +244 -0
- package/src/jointMath.test.ts +646 -0
- package/src/jointReaction.test.ts +241 -0
- package/src/jointRegistry.test.ts +229 -0
- package/src/jointRows.test.ts +548 -0
- package/src/joints.test.ts +1066 -0
- package/src/massProperties.test.ts +611 -0
- package/src/material.test.ts +40 -0
- package/src/nodeSync.test.ts +50 -0
- package/src/ownership.test.ts +61 -0
- package/src/physics3DBroadphasePublication.test.ts +63 -0
- package/src/physics3DSpatialIndexingGuards.test.ts +43 -0
- package/src/registerBuiltInPhysics3DJointSolvers.test.ts +51 -0
- package/src/solver.test.ts +567 -0
- package/src/step.test.ts +490 -0
- package/src/stepValidation.test.ts +346 -0
- package/src/stress.test.ts +526 -0
- package/src/symmetricTensor.test.ts +166 -0
- package/src/world.test.ts +1127 -0
- package/src/worldQueries.test.ts +565 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createPhysics3DBallAndSocketJoint,
|
|
5
|
+
createPhysics3DConeTwistJoint,
|
|
6
|
+
createPhysics3DDistanceJoint,
|
|
7
|
+
createPhysics3DFixedJoint,
|
|
8
|
+
createPhysics3DGeneric6DofJoint,
|
|
9
|
+
createPhysics3DHingeJoint,
|
|
10
|
+
createPhysics3DSliderJoint,
|
|
11
|
+
} from './jointFactories';
|
|
12
|
+
import {
|
|
13
|
+
Physics3DBallAndSocketJointKind,
|
|
14
|
+
Physics3DConeTwistJointKind,
|
|
15
|
+
Physics3DDistanceJointKind,
|
|
16
|
+
Physics3DFixedJointKind,
|
|
17
|
+
Physics3DGeneric6DofJointKind,
|
|
18
|
+
Physics3DHingeJointKind,
|
|
19
|
+
Physics3DSliderJointKind,
|
|
20
|
+
} from './joints';
|
|
21
|
+
|
|
22
|
+
describe('createPhysics3DBallAndSocketJoint', () => {
|
|
23
|
+
it('stamps its kind and zeroes every accumulator', () => {
|
|
24
|
+
const joint = createPhysics3DBallAndSocketJoint({ bodyA: 3, bodyB: 7 });
|
|
25
|
+
|
|
26
|
+
expect(joint.kind).toBe(Physics3DBallAndSocketJointKind);
|
|
27
|
+
expect(joint.bodyA).toBe(3);
|
|
28
|
+
expect(joint.bodyB).toBe(7);
|
|
29
|
+
expect([joint.impulse0, joint.impulse1, joint.impulse2, joint.impulse3, joint.impulse4, joint.impulse5]).toEqual([
|
|
30
|
+
0, 0, 0, 0, 0, 0,
|
|
31
|
+
]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('defaults both anchors to the body origin and the pair to non-colliding', () => {
|
|
35
|
+
const joint = createPhysics3DBallAndSocketJoint({ bodyA: 0, bodyB: 1 });
|
|
36
|
+
|
|
37
|
+
expect([joint.localAnchorAX, joint.localAnchorAY, joint.localAnchorAZ]).toEqual([0, 0, 0]);
|
|
38
|
+
expect(joint.collideConnected).toBe(false);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('carries the anchors it was given', () => {
|
|
42
|
+
const joint = createPhysics3DBallAndSocketJoint({
|
|
43
|
+
bodyA: 0,
|
|
44
|
+
bodyB: 1,
|
|
45
|
+
localAnchorAX: 1,
|
|
46
|
+
localAnchorAY: 2,
|
|
47
|
+
localAnchorAZ: 3,
|
|
48
|
+
localAnchorBZ: -4,
|
|
49
|
+
collideConnected: true,
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
expect([joint.localAnchorAX, joint.localAnchorAY, joint.localAnchorAZ]).toEqual([1, 2, 3]);
|
|
53
|
+
expect(joint.localAnchorBZ).toBe(-4);
|
|
54
|
+
expect(joint.collideConnected).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe('createPhysics3DConeTwistJoint', () => {
|
|
59
|
+
it('defaults to a 45 degree cone with the twist unbounded', () => {
|
|
60
|
+
const joint = createPhysics3DConeTwistJoint({ bodyA: 0, bodyB: 1 });
|
|
61
|
+
|
|
62
|
+
expect(joint.kind).toBe(Physics3DConeTwistJointKind);
|
|
63
|
+
expect(joint.enableSwingLimit).toBe(true);
|
|
64
|
+
expect(joint.swingLimitY).toBeCloseTo(Math.PI / 4, 12);
|
|
65
|
+
expect(joint.swingLimitZ).toBeCloseTo(Math.PI / 4, 12);
|
|
66
|
+
expect(joint.enableTwistLimit).toBe(false);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('carries an elliptical cone and a twist interval', () => {
|
|
70
|
+
const joint = createPhysics3DConeTwistJoint({
|
|
71
|
+
bodyA: 0,
|
|
72
|
+
bodyB: 1,
|
|
73
|
+
swingLimitY: 0.3,
|
|
74
|
+
swingLimitZ: 1.1,
|
|
75
|
+
enableTwistLimit: true,
|
|
76
|
+
lowerTwistAngle: -0.2,
|
|
77
|
+
upperTwistAngle: 0.9,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(joint.swingLimitY).toBe(0.3);
|
|
81
|
+
expect(joint.swingLimitZ).toBe(1.1);
|
|
82
|
+
expect(joint.lowerTwistAngle).toBe(-0.2);
|
|
83
|
+
expect(joint.upperTwistAngle).toBe(0.9);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('createPhysics3DDistanceJoint', () => {
|
|
88
|
+
it('defaults to a RIGID strut with no spring and no limit', () => {
|
|
89
|
+
const joint = createPhysics3DDistanceJoint({ bodyA: 0, bodyB: 1, length: 3 });
|
|
90
|
+
|
|
91
|
+
expect(joint.kind).toBe(Physics3DDistanceJointKind);
|
|
92
|
+
expect(joint.length).toBe(3);
|
|
93
|
+
expect(joint.enableSpring).toBe(false);
|
|
94
|
+
expect(joint.enableLimit).toBe(false);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
it('defaults its interval to UNBOUNDED above, not to zero', () => {
|
|
98
|
+
// A caller who switches the limit on without naming a maximum means a rope with no stated bound. An
|
|
99
|
+
// interval defaulting to [0, 0] would instead pin the two anchors together, which is the opposite of a
|
|
100
|
+
// rope and would look like the joint holding rather than the default being wrong.
|
|
101
|
+
const joint = createPhysics3DDistanceJoint({ bodyA: 0, bodyB: 1 });
|
|
102
|
+
|
|
103
|
+
expect(joint.minLength).toBe(0);
|
|
104
|
+
expect(joint.maxLength).toBe(Number.POSITIVE_INFINITY);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('carries the spring and limit it was given', () => {
|
|
108
|
+
const joint = createPhysics3DDistanceJoint({
|
|
109
|
+
bodyA: 0,
|
|
110
|
+
bodyB: 1,
|
|
111
|
+
length: 2,
|
|
112
|
+
enableSpring: true,
|
|
113
|
+
frequencyHz: 4,
|
|
114
|
+
dampingRatio: 0.7,
|
|
115
|
+
enableLimit: true,
|
|
116
|
+
minLength: 1,
|
|
117
|
+
maxLength: 5,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(joint.frequencyHz).toBe(4);
|
|
121
|
+
expect(joint.dampingRatio).toBe(0.7);
|
|
122
|
+
expect(joint.minLength).toBe(1);
|
|
123
|
+
expect(joint.maxLength).toBe(5);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('starts its limit accumulators cold', () => {
|
|
127
|
+
const joint = createPhysics3DDistanceJoint({ bodyA: 0, bodyB: 1 });
|
|
128
|
+
|
|
129
|
+
expect(joint.lowerLimitImpulse).toBe(0);
|
|
130
|
+
expect(joint.upperLimitImpulse).toBe(0);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe('createPhysics3DFixedJoint', () => {
|
|
135
|
+
it('defaults both frames to the identity', () => {
|
|
136
|
+
const joint = createPhysics3DFixedJoint({ bodyA: 0, bodyB: 1 });
|
|
137
|
+
|
|
138
|
+
expect(joint.kind).toBe(Physics3DFixedJointKind);
|
|
139
|
+
expect([joint.localRotationAX, joint.localRotationAY, joint.localRotationAZ, joint.localRotationAW]).toEqual([
|
|
140
|
+
0, 0, 0, 1,
|
|
141
|
+
]);
|
|
142
|
+
expect([joint.localRotationBX, joint.localRotationBY, joint.localRotationBZ, joint.localRotationBW]).toEqual([
|
|
143
|
+
0, 0, 0, 1,
|
|
144
|
+
]);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('carries the frames it was given', () => {
|
|
148
|
+
const half = Math.SQRT1_2;
|
|
149
|
+
const joint = createPhysics3DFixedJoint({ bodyA: 0, bodyB: 1, localRotationAZ: half, localRotationAW: half });
|
|
150
|
+
|
|
151
|
+
expect(joint.localRotationAZ).toBeCloseTo(half, 12);
|
|
152
|
+
expect(joint.localRotationAW).toBeCloseTo(half, 12);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe('createPhysics3DGeneric6DofJoint', () => {
|
|
157
|
+
it('defaults every axis to free', () => {
|
|
158
|
+
const joint = createPhysics3DGeneric6DofJoint({ bodyA: 0, bodyB: 1 });
|
|
159
|
+
|
|
160
|
+
expect(joint.kind).toBe(Physics3DGeneric6DofJointKind);
|
|
161
|
+
// `lower > upper` is the encoding for free, so a joint built with no bounds constrains nothing. Defaulting
|
|
162
|
+
// to locked would make a partially configured joint silently rigid, which is the harder failure to see.
|
|
163
|
+
expect(joint.lowerLinearX).toBeGreaterThan(joint.upperLinearX);
|
|
164
|
+
expect(joint.lowerLinearY).toBeGreaterThan(joint.upperLinearY);
|
|
165
|
+
expect(joint.lowerLinearZ).toBeGreaterThan(joint.upperLinearZ);
|
|
166
|
+
expect(joint.lowerAngularX).toBeGreaterThan(joint.upperAngularX);
|
|
167
|
+
expect(joint.lowerAngularY).toBeGreaterThan(joint.upperAngularY);
|
|
168
|
+
expect(joint.lowerAngularZ).toBeGreaterThan(joint.upperAngularZ);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('carries the bounds it was given', () => {
|
|
172
|
+
const joint = createPhysics3DGeneric6DofJoint({
|
|
173
|
+
bodyA: 0,
|
|
174
|
+
bodyB: 1,
|
|
175
|
+
lowerLinearY: -2,
|
|
176
|
+
upperLinearY: 5,
|
|
177
|
+
lowerAngularZ: 0,
|
|
178
|
+
upperAngularZ: 0,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
expect(joint.lowerLinearY).toBe(-2);
|
|
182
|
+
expect(joint.upperLinearY).toBe(5);
|
|
183
|
+
expect(joint.lowerAngularZ).toBe(0);
|
|
184
|
+
expect(joint.upperAngularZ).toBe(0);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('createPhysics3DHingeJoint', () => {
|
|
189
|
+
it('defaults to a free hinge with no motor', () => {
|
|
190
|
+
const joint = createPhysics3DHingeJoint({ bodyA: 0, bodyB: 1 });
|
|
191
|
+
|
|
192
|
+
expect(joint.kind).toBe(Physics3DHingeJointKind);
|
|
193
|
+
expect(joint.enableLimit).toBe(false);
|
|
194
|
+
expect(joint.enableMotor).toBe(false);
|
|
195
|
+
expect(joint.motorImpulse).toBe(0);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('carries its limits and motor settings', () => {
|
|
199
|
+
const joint = createPhysics3DHingeJoint({
|
|
200
|
+
bodyA: 0,
|
|
201
|
+
bodyB: 1,
|
|
202
|
+
enableLimit: true,
|
|
203
|
+
lowerAngle: -1,
|
|
204
|
+
upperAngle: 2,
|
|
205
|
+
enableMotor: true,
|
|
206
|
+
motorSpeed: 3,
|
|
207
|
+
maxMotorTorque: 40,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(joint.lowerAngle).toBe(-1);
|
|
211
|
+
expect(joint.upperAngle).toBe(2);
|
|
212
|
+
expect(joint.motorSpeed).toBe(3);
|
|
213
|
+
expect(joint.maxMotorTorque).toBe(40);
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
describe('createPhysics3DSliderJoint', () => {
|
|
218
|
+
it('defaults to free travel with no motor', () => {
|
|
219
|
+
const joint = createPhysics3DSliderJoint({ bodyA: 0, bodyB: 1 });
|
|
220
|
+
|
|
221
|
+
expect(joint.kind).toBe(Physics3DSliderJointKind);
|
|
222
|
+
expect(joint.enableLimit).toBe(false);
|
|
223
|
+
expect(joint.enableMotor).toBe(false);
|
|
224
|
+
expect(joint.motorImpulse).toBe(0);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
it('carries its travel interval and motor settings', () => {
|
|
228
|
+
const joint = createPhysics3DSliderJoint({
|
|
229
|
+
bodyA: 0,
|
|
230
|
+
bodyB: 1,
|
|
231
|
+
enableLimit: true,
|
|
232
|
+
lowerTranslation: -3,
|
|
233
|
+
upperTranslation: 3,
|
|
234
|
+
enableMotor: true,
|
|
235
|
+
motorSpeed: 2,
|
|
236
|
+
maxMotorForce: 15,
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
expect(joint.lowerTranslation).toBe(-3);
|
|
240
|
+
expect(joint.upperTranslation).toBe(3);
|
|
241
|
+
expect(joint.motorSpeed).toBe(2);
|
|
242
|
+
expect(joint.maxMotorForce).toBe(15);
|
|
243
|
+
});
|
|
244
|
+
});
|