<!DOCTYPE html>
<meta charset=utf-8>
<title>Test basic functionality of scroll linked animation.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<script src="testcommon.js"></script>
<style>
  .scroller {
    overflow: auto;
    height: 100px;
    width: 100px;
    will-change: transform;
  }

  .contents {
    height: 1000px;
    width: 100%;
  }
</style>
<div id="log"></div>
<script>
'use strict';

promise_test(async t => {
    const animation = createScrollLinkedAnimation(t);
    const scroller = animation.timeline.source;

    // Ensure we have a valid animation frame time before continuing the test.
    // This is so that we can properly determine frame advancement after the
    // style change.
    await waitForNextFrame();

    // Make the scroll timeline inactive.
    scroller.style.overflow = 'visible';
    // Wait for new animation frame which allows the timeline to compute new
    // current time.
    await waitForNextFrame();
    assert_equals(animation.timeline.currentTime, null,
                  'Sanity check the timeline is inactive.');
    // Play the animation when the timeline is inactive.
    animation.play();
    assert_equals(animation.currentTime, null,
                  'The current time is null when the timeline is inactive.');
    assert_equals(animation.startTime, null,
                  'The start time is unresolved while play-pending.');
    await waitForNextFrame();
    assert_true(animation.pending,
                'Animation has play pending task while timeline is inactive.');
    assert_equals(animation.playState, 'running',
                  `State is 'running' in Pending state.`);
}, `Play pending task doesn't run when the timeline is inactive.`);

promise_test(async t => {
    const animation = createScrollLinkedAnimation(t);
    const scroller = animation.timeline.source;

    await waitForNextFrame();

    // Make the scroll timeline inactive.
    scroller.style.overflow = 'visible';
    // Wait for new animation frame which allows the timeline to compute new
    // current time.
    await waitForNextFrame();
    assert_equals(animation.timeline.currentTime, null,
                  'Sanity check the timeline is inactive.');
    // Play the animation when the timeline is inactive.
    animation.play();

    // Make the scroll timeline active.
    scroller.style.overflow = 'auto';
    await animation.ready;
    // Ready promise is resolved as a result of the timeline becoming active.
    assert_percents_equal(animation.currentTime, 0,
        'Animation current time is resolved when the animation is ready.');
    assert_percents_equal(animation.startTime, 0,
        'Animation start time is resolved when the animation is ready.');
}, 'Animation start and current times are correct if scroll timeline is ' +
   'activated after animation.play call.');

promise_test(async t => {
    const animation = createScrollLinkedAnimation(t);
    const scroller = animation.timeline.source;
    const target = animation.effect.target;

    await waitForNextFrame();

    // Make the scroll timeline inactive.
    scroller.style.overflow = 'visible';
    scroller.scrollTop;
    // Wait for new animation frame which allows the timeline to compute new
    // current time.
    await waitForNextFrame();
    assert_equals(animation.timeline.currentTime, null,
                  'Sanity check the timeline is inactive.');
    // Set start time when the timeline is inactive.
    animation.startTime = CSSNumericValue.parse("0%");
    assert_equals(animation.currentTime, null,
                  'Sanity check current time is unresolved when the timeline ' +
                  'is inactive.');

    // Make the scroll timeline active.
    scroller.style.overflow = 'auto';
    // Wait for new animation frame which allows the timeline to compute new
    // current time.
    await waitForNextFrame();

    assert_percents_equal(animation.currentTime, 0,
        'Animation current time is resolved when the timeline is active.');
    assert_percents_equal(animation.startTime, 0,
                          'Animation start time is resolved.');
    assert_percents_equal(animation.effect.getComputedTiming().localTime, 0,
        'Effect local time is resolved when the timeline is active.');
    assert_equals(Number(getComputedStyle(target).opacity), 0,
        'Animation has an effect when the timeline is active.');
}, 'Animation start and current times are correct if scroll timeline is ' +
   'activated after setting start time.');

promise_test(async t => {
    const animation = createScrollLinkedAnimation(t);
    const scroller = animation.timeline.source;
    const maxScroll = scroller.scrollHeight - scroller.clientHeight;
    const target = animation.effect.target;

    await waitForNextFrame();

    // Advance the scroller.
    scroller.scrollTop = 0.2 * maxScroll;

    // Wait for new animation frame which allows the timeline to compute new
    // current time.
    await waitForNextFrame();
    // Play the animation when the timeline is active.
    animation.play();
    await animation.ready;

    // Make the scroll timeline inactive.
    scroller.style.overflow = 'visible';
    scroller.scrollTop;
    await waitForNextFrame();
    assert_equals(animation.timeline.currentTime, null,
                  'Sanity check the timeline is inactive.');
    assert_equals(animation.playState, 'running',
                  `State is 'running' when the timeline is inactive.`);
    assert_equals(animation.currentTime, null,
                  'Current time is unresolved when the timeline is inactive.');
    assert_percents_equal(animation.startTime, 0,
                          'Start time is zero when the timeline is inactive.');
    assert_equals(animation.effect.getComputedTiming().localTime, null,
                  'Effect local time is null when the timeline is inactive.');
    assert_equals(Number(getComputedStyle(target).opacity), 1,
        'Animation does not have an effect when the timeline is inactive.');

    // Make the scroll timeline active.
    scroller.style.overflow = 'auto';
    await waitForNextFrame();

    assert_equals(animation.playState, 'running',
                  `State is 'running' when the timeline is active.`);
    assert_percents_equal(animation.currentTime, 20,
        'Current time is resolved when the timeline is active.');
    assert_percents_equal(animation.startTime, 0,
                          'Start time is zero when the timeline is active.');
    assert_percents_equal(animation.effect.getComputedTiming().localTime, 20,
        'Effect local time is resolved when the timeline is active.');
    assert_equals(Number(getComputedStyle(target).opacity), 0.2,
        'Animation has an effect when the timeline is active.');
}, 'Animation current time is correct when the timeline becomes newly ' +
   'inactive and then active again.');
</script>
