com.facebook.react.bridge.JavaOnlyArray#of ( )源码实例Demo

下面列出了com.facebook.react.bridge.JavaOnlyArray#of ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。

@Test
public void testNodeValueListenerIfNotListening() {
  int nodeId = 1;

  createSimpleAnimatedViewWithOpacity(1000, 0d);
  JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.2d, 0.4d, 0.6d, 0.8d, 1d);

  Callback animationCallback = mock(Callback.class);
  AnimatedNodeValueListener valueListener = mock(AnimatedNodeValueListener.class);

  mNativeAnimatedNodesManager.startListeningToAnimatedNodeValue(nodeId, valueListener);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    nodeId,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1d),
    animationCallback);

  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(valueListener).onValueUpdate(eq(0d));

  mNativeAnimatedNodesManager.stopListeningToAnimatedNodeValue(nodeId);

  reset(valueListener);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(valueListener);
}
 
@Test
public void testFramesAnimation() {
  createSimpleAnimatedViewWithOpacity(1000, 0d);

  JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.2d, 0.4d, 0.6d, 0.8d, 1d);
  Callback animationCallback = mock(Callback.class);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    1,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1d),
    animationCallback);

  ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
      ArgumentCaptor.forClass(ReactStylesDiffMap.class);

  for (int i = 0; i < frames.size(); i++) {
    reset(mUIImplementationMock);
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
    verify(mUIImplementationMock)
        .synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
    assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN))
        .isEqualTo(frames.getDouble(i));
  }

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(mUIImplementationMock);
}
 
@Test
public void testFramesAnimationLoopsFiveTimes() {
  createSimpleAnimatedViewWithOpacity(1000, 0d);

  JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.2d, 0.4d, 0.6d, 0.8d, 1d);
  Callback animationCallback = mock(Callback.class);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    1,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1d, "iterations", 5),
    animationCallback);

  ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
      ArgumentCaptor.forClass(ReactStylesDiffMap.class);

  for (int iteration = 0; iteration < 5; iteration++) {
    for (int i = 0; i < frames.size(); i++) {
      reset(mUIImplementationMock);
      mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
      verify(mUIImplementationMock)
          .synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
      assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN))
          .isEqualTo(frames.getDouble(i));
    }
  }

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(mUIImplementationMock);
}
 
@Test
public void testNodeValueListenerIfListening() {
  int nodeId = 1;

  createSimpleAnimatedViewWithOpacity(1000, 0d);
  JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.2d, 0.4d, 0.6d, 0.8d, 1d);

  Callback animationCallback = mock(Callback.class);
  AnimatedNodeValueListener valueListener = mock(AnimatedNodeValueListener.class);

  mNativeAnimatedNodesManager.startListeningToAnimatedNodeValue(nodeId, valueListener);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    nodeId,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1d),
    animationCallback);

  for (int i = 0; i < frames.size(); i++) {
    reset(valueListener);
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
    verify(valueListener).onValueUpdate(eq(frames.getDouble(i)));
  }

  reset(valueListener);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(valueListener);
}
 
@Test
public void testAnimationCallbackFinish() {
  createSimpleAnimatedViewWithOpacity(1000, 0d);

  JavaOnlyArray frames = JavaOnlyArray.of(0d, 1d);
  Callback animationCallback = mock(Callback.class);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    1,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1d),
    animationCallback);

  ArgumentCaptor<ReadableMap> callbackResponseCaptor = ArgumentCaptor.forClass(ReadableMap.class);

  reset(animationCallback);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(animationCallback);

  reset(animationCallback);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(animationCallback).invoke(callbackResponseCaptor.capture());

  assertThat(callbackResponseCaptor.getValue().hasKey("finished")).isTrue();
  assertThat(callbackResponseCaptor.getValue().getBoolean("finished")).isTrue();

  reset(animationCallback);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(animationCallback);
}
 
@Test
public void testAdditionNode() {
  createAnimatedGraphWithAdditionNode(50, 100d, 1000d);

  Callback animationCallback = mock(Callback.class);
  JavaOnlyArray frames = JavaOnlyArray.of(0d, 1d);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    1,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 101d),
    animationCallback);

  mNativeAnimatedNodesManager.startAnimatingNode(
    2,
    2,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1010d),
    animationCallback);

  ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
    ArgumentCaptor.forClass(ReactStylesDiffMap.class);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1100d);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(mUIImplementationMock)
    .synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1111d);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(mUIImplementationMock);
}
 
/**
 * Verifies that {@link NativeAnimatedNodesManager#runUpdates} updates the view correctly in case
 * when one of the addition input nodes has started animating while the other one has not.
 *
 * We expect that the output of the addition node will take the starting value of the second input
 * node even though the node hasn't been connected to an active animation driver.
 */
@Test
public void testViewReceiveUpdatesIfOneOfAnimationHasntStarted() {
  createAnimatedGraphWithAdditionNode(50, 100d, 1000d);

  // Start animating only the first addition input node
  Callback animationCallback = mock(Callback.class);
  JavaOnlyArray frames = JavaOnlyArray.of(0d, 1d);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    1,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 101d),
    animationCallback);

  ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
    ArgumentCaptor.forClass(ReactStylesDiffMap.class);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1100d);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(mUIImplementationMock)
    .synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1101d);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(mUIImplementationMock);
}
 
/**
 * Verifies that {@link NativeAnimatedNodesManager#runUpdates} updates the view correctly in case
 * when one of the addition input nodes animation finishes before the other.
 *
 * We expect that the output of the addition node after one of the animation has finished will
 * take the last value of the animated node and the view will receive updates up until the second
 * animation is over.
 */
@Test
public void testViewReceiveUpdatesWhenOneOfAnimationHasFinished() {
  createAnimatedGraphWithAdditionNode(50, 100d, 1000d);

  Callback animationCallback = mock(Callback.class);

  // Start animating for the first addition input node, will have 2 frames only
  JavaOnlyArray firstFrames = JavaOnlyArray.of(0d, 1d);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    1,
    JavaOnlyMap.of("type", "frames", "frames", firstFrames, "toValue", 200d),
    animationCallback);

  // Start animating for the first addition input node, will have 6 frames
  JavaOnlyArray secondFrames = JavaOnlyArray.of(0d, 0.2d, 0.4d, 0.6d, 0.8d, 1d);
  mNativeAnimatedNodesManager.startAnimatingNode(
    2,
    2,
    JavaOnlyMap.of("type", "frames", "frames", secondFrames, "toValue", 1010d),
    animationCallback);

  ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
    ArgumentCaptor.forClass(ReactStylesDiffMap.class);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(1100d);

  for (int i = 1; i < secondFrames.size(); i++) {
    reset(mUIImplementationMock);
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
    verify(mUIImplementationMock)
      .synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
    assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN))
      .isEqualTo(1200d + secondFrames.getDouble(i) * 10d);
  }

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(mUIImplementationMock);
}
 
@Test
public void testMultiplicationNode() {
  mNativeAnimatedNodesManager.createAnimatedNode(
    1,
    JavaOnlyMap.of("type", "value", "value", 1d, "offset", 0d));
  mNativeAnimatedNodesManager.createAnimatedNode(
    2,
    JavaOnlyMap.of("type", "value", "value", 5d, "offset", 0d));

  mNativeAnimatedNodesManager.createAnimatedNode(
    3,
    JavaOnlyMap.of("type", "multiplication", "input", JavaOnlyArray.of(1, 2)));

  mNativeAnimatedNodesManager.createAnimatedNode(
    4,
    JavaOnlyMap.of("type", "style", "style", JavaOnlyMap.of("translateX", 3)));
  mNativeAnimatedNodesManager.createAnimatedNode(
    5,
    JavaOnlyMap.of("type", "props", "props", JavaOnlyMap.of("style", 4)));
  mNativeAnimatedNodesManager.connectAnimatedNodes(1, 3);
  mNativeAnimatedNodesManager.connectAnimatedNodes(2, 3);
  mNativeAnimatedNodesManager.connectAnimatedNodes(3, 4);
  mNativeAnimatedNodesManager.connectAnimatedNodes(4, 5);
  mNativeAnimatedNodesManager.connectAnimatedNodeToView(5, 50);

  Callback animationCallback = mock(Callback.class);
  JavaOnlyArray frames = JavaOnlyArray.of(0d, 1d);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    1,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 2d),
    animationCallback);

  mNativeAnimatedNodesManager.startAnimatingNode(
    2,
    2,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 10d),
    animationCallback);

  ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
    ArgumentCaptor.forClass(ReactStylesDiffMap.class);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(5d);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(20d);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(mUIImplementationMock);
}
 
/**
 * This test verifies that when {@link NativeAnimatedModule#stopAnimation} is called the animation
 * will no longer be updating the nodes it has been previously attached to and that the animation
 * callback will be triggered with {@code {finished: false}}
 */
@Test
public void testHandleStoppingAnimation() {
  createSimpleAnimatedViewWithOpacity(1000, 0d);

  JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.2d, 0.4d, 0.6d, 0.8d, 1.0d);
  Callback animationCallback = mock(Callback.class);
  mNativeAnimatedNodesManager.startAnimatingNode(
    404,
    1,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 1d),
    animationCallback);

  ArgumentCaptor<ReadableMap> callbackResponseCaptor = ArgumentCaptor.forClass(ReadableMap.class);

  reset(animationCallback);
  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(mUIImplementationMock, times(2))
    .synchronouslyUpdateViewOnUIThread(anyInt(), any(ReactStylesDiffMap.class));
  verifyNoMoreInteractions(animationCallback);

  reset(animationCallback);
  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.stopAnimation(404);
  verify(animationCallback).invoke(callbackResponseCaptor.capture());
  verifyNoMoreInteractions(animationCallback);
  verifyNoMoreInteractions(mUIImplementationMock);

  assertThat(callbackResponseCaptor.getValue().hasKey("finished")).isTrue();
  assertThat(callbackResponseCaptor.getValue().getBoolean("finished")).isFalse();

  reset(animationCallback);
  reset(mUIImplementationMock);
  // Run "update" loop a few more times -> we expect no further updates nor callback calls to be
  // triggered
  for (int i = 0; i < 5; i++) {
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  }

  verifyNoMoreInteractions(mUIImplementationMock);
  verifyNoMoreInteractions(animationCallback);
}
 
@Test
public void testInterpolationNode() {
  mNativeAnimatedNodesManager.createAnimatedNode(
    1,
    JavaOnlyMap.of("type", "value", "value", 10d, "offset", 0d));

  mNativeAnimatedNodesManager.createAnimatedNode(
    2,
    JavaOnlyMap.of(
      "type",
      "interpolation",
      "inputRange",
      JavaOnlyArray.of(10d, 20d),
      "outputRange",
      JavaOnlyArray.of(0d, 1d),
      "extrapolateLeft",
      "extend",
      "extrapolateRight",
      "extend"));

  mNativeAnimatedNodesManager.createAnimatedNode(
    3,
    JavaOnlyMap.of("type", "style", "style", JavaOnlyMap.of("opacity", 2)));
  mNativeAnimatedNodesManager.createAnimatedNode(
    4,
    JavaOnlyMap.of("type", "props", "props", JavaOnlyMap.of("style", 3)));
  mNativeAnimatedNodesManager.connectAnimatedNodes(1, 2);
  mNativeAnimatedNodesManager.connectAnimatedNodes(2, 3);
  mNativeAnimatedNodesManager.connectAnimatedNodes(3, 4);
  mNativeAnimatedNodesManager.connectAnimatedNodeToView(4, 50);

  Callback animationCallback = mock(Callback.class);
  JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.2d, 0.4d, 0.6d, 0.8d, 1d);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    1,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 20d),
    animationCallback);

  ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
    ArgumentCaptor.forClass(ReactStylesDiffMap.class);

  for (int i = 0; i < frames.size(); i++) {
    reset(mUIImplementationMock);
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
    verify(mUIImplementationMock)
      .synchronouslyUpdateViewOnUIThread(eq(50), stylesCaptor.capture());
    assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN))
      .isEqualTo(frames.getDouble(i));
  }

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(mUIImplementationMock);
}
 
@Test
public void testRestoreDefaultProps() {
  int viewTag = 1000;
  int propsNodeTag = 3;
  mNativeAnimatedNodesManager.createAnimatedNode(
    1,
    JavaOnlyMap.of("type", "value", "value", 1d, "offset", 0d));
  mNativeAnimatedNodesManager.createAnimatedNode(
    2,
    JavaOnlyMap.of("type", "style", "style", JavaOnlyMap.of("opacity", 1)));
  mNativeAnimatedNodesManager.createAnimatedNode(
    propsNodeTag,
    JavaOnlyMap.of("type", "props", "props", JavaOnlyMap.of("style", 2)));
  mNativeAnimatedNodesManager.connectAnimatedNodes(1, 2);
  mNativeAnimatedNodesManager.connectAnimatedNodes(2, propsNodeTag);
  mNativeAnimatedNodesManager.connectAnimatedNodeToView(propsNodeTag, viewTag);

  JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.5d, 1d);
  Callback animationCallback = mock(Callback.class);
  mNativeAnimatedNodesManager.startAnimatingNode(
    1,
    1,
    JavaOnlyMap.of("type", "frames", "frames", frames, "toValue", 0d),
    animationCallback);

  ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
    ArgumentCaptor.forClass(ReactStylesDiffMap.class);

  for (int i = 0; i < frames.size(); i++) {
    reset(mUIImplementationMock);
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  }

  verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().getDouble("opacity", Double.NaN)).isEqualTo(0);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.restoreDefaultValues(propsNodeTag, viewTag);
  verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(viewTag), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().isNull("opacity"));
}
 
/**
 * In this test we verify that when value is being tracked we can update destination value in the
 * middle of ongoing animation and the animation will update and animate to the new spot. This is
 * tested using simple 5 frame backed timing animation.
 */
@Test
public void testTracking() {
  JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.25d, 0.5d, 0.75d, 1d);
  JavaOnlyMap animationConfig = JavaOnlyMap.of("type", "frames", "frames", frames);

  createAnimatedGraphWithTrackingNode(1000, 0d, animationConfig);

  ArgumentCaptor<ReactStylesDiffMap> stylesCaptor =
          ArgumentCaptor.forClass(ReactStylesDiffMap.class);

  reset(mUIImplementationMock);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verify(mUIImplementationMock).synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
  assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN)).isEqualTo(0d);

  // update "toValue" to 100, we expect tracking animation to animate now from 0 to 100 in 5 steps
  mNativeAnimatedNodesManager.setAnimatedNodeValue(1, 100d);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // kick off the animation

  for (int i = 0; i < frames.size(); i++) {
    reset(mUIImplementationMock);
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
    verify(mUIImplementationMock)
            .synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
    assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN))
            .isEqualTo(frames.getDouble(i) * 100d);
  }

  // update "toValue" to 0 but run only two frames from the animation,
  // we expect tracking animation to animate now from 100 to 75
  mNativeAnimatedNodesManager.setAnimatedNodeValue(1, 0d);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // kick off the animation

  for (int i = 0; i < 2; i++) {
    reset(mUIImplementationMock);
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
    verify(mUIImplementationMock)
            .synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
    assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN))
            .isEqualTo(100d * (1d - frames.getDouble(i)));
  }

  // at this point we expect tracking value to be at 75
  assertThat(((ValueAnimatedNode) mNativeAnimatedNodesManager.getNodeById(3)).getValue())
          .isEqualTo(75d);

  // we update "toValue" again to 100 and expect the animation to restart from the current place
  mNativeAnimatedNodesManager.setAnimatedNodeValue(1, 100d);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // kick off the animation

  for (int i = 0; i < frames.size(); i++) {
    reset(mUIImplementationMock);
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
    verify(mUIImplementationMock)
            .synchronouslyUpdateViewOnUIThread(eq(1000), stylesCaptor.capture());
    assertThat(stylesCaptor.getValue().getDouble("translateX", Double.NaN))
            .isEqualTo(50d + 50d * frames.getDouble(i));
  }
}
 
/**
 * In this test we verify that when tracking is set up for a given animated node and when the
 * animation settles it will not be registered as an active animation and therefore will not
 * consume resources on running the animation that has already completed. Then we verify that when
 * the value updates the animation will resume as expected and the complete again when reaches the
 * end.
 */
@Test
public void testTrackingPausesWhenEndValueIsReached() {
  JavaOnlyArray frames = JavaOnlyArray.of(0d, 0.5d, 1d);
  JavaOnlyMap animationConfig = JavaOnlyMap.of("type", "frames", "frames", frames);

  createAnimatedGraphWithTrackingNode(1000, 0d, animationConfig);
  mNativeAnimatedNodesManager.setAnimatedNodeValue(1, 100d);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // make sure animation starts

  reset(mUIImplementationMock);
  for (int i = 0; i < frames.size(); i++) {
    assertThat(mNativeAnimatedNodesManager.hasActiveAnimations()).isTrue();
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  }
  verify(mUIImplementationMock, times(frames.size()))
          .synchronouslyUpdateViewOnUIThread(eq(1000), any(ReactStylesDiffMap.class));

  // the animation has completed, we expect no updates to be done
  reset(mUIImplementationMock);
  assertThat(mNativeAnimatedNodesManager.hasActiveAnimations()).isFalse();
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(mUIImplementationMock);


  // we update end value and expect the animation to restart
  mNativeAnimatedNodesManager.setAnimatedNodeValue(1, 200d);
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime()); // make sure animation starts

  reset(mUIImplementationMock);
  for (int i = 0; i < frames.size(); i++) {
    assertThat(mNativeAnimatedNodesManager.hasActiveAnimations()).isTrue();
    mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  }
  verify(mUIImplementationMock, times(frames.size()))
          .synchronouslyUpdateViewOnUIThread(eq(1000), any(ReactStylesDiffMap.class));

  // the animation has completed, we expect no updates to be done
  reset(mUIImplementationMock);
  assertThat(mNativeAnimatedNodesManager.hasActiveAnimations()).isFalse();
  mNativeAnimatedNodesManager.runUpdates(nextFrameTime());
  verifyNoMoreInteractions(mUIImplementationMock);
}