| <!DOCTYPE html> |
| <meta charset=utf-8> |
| <title>KeyframeEffect constructor</title> |
| <link rel="help" |
| href="https://drafts.csswg.org/web-animations/#dom-keyframeeffect-keyframeeffect"> |
| <link rel="help" |
| href="https://drafts.csswg.org/web-animations/#dom-keyframeeffectreadonly-keyframeeffectreadonly"> |
| <script src="/resources/testharness.js"></script> |
| <script src="/resources/testharnessreport.js"></script> |
| <script src="../../testcommon.js"></script> |
| <script src="../../resources/easing-tests.js"></script> |
| <script src="../../resources/keyframe-utils.js"></script> |
| <script src="../../resources/keyframe-tests.js"></script> |
| <body> |
| <div id="log"></div> |
| <div id="target"></div> |
| <script> |
| 'use strict'; |
| |
| const target = document.getElementById('target'); |
| |
| test(t => { |
| for (const frames of gEmptyKeyframeListTests) { |
| assert_equals(new KeyframeEffect(target, frames).getKeyframes().length, |
| 0, `number of frames for ${JSON.stringify(frames)}`); |
| } |
| }, 'A KeyframeEffect can be constructed with no frames'); |
| |
| test(t => { |
| for (const subtest of gEasingParsingTests) { |
| const easing = subtest[0]; |
| const expected = subtest[1]; |
| const effect = new KeyframeEffect(target, { |
| left: ['10px', '20px'] |
| }, { easing: easing }); |
| assert_equals(effect.getTiming().easing, expected, |
| `resulting easing for '${easing}'`); |
| } |
| }, 'easing values are parsed correctly when passed to the ' + |
| 'KeyframeEffect constructor in KeyframeEffectOptions'); |
| |
| test(t => { |
| for (const invalidEasing of gInvalidEasings) { |
| assert_throws_js(TypeError, () => { |
| new KeyframeEffect(target, null, { easing: invalidEasing }); |
| }, `TypeError is thrown for easing '${invalidEasing}'`); |
| } |
| }, 'Invalid easing values are correctly rejected when passed to the ' + |
| 'KeyframeEffect constructor in KeyframeEffectOptions'); |
| |
| test(t => { |
| const getKeyframe = |
| composite => ({ left: [ '10px', '20px' ], composite: composite }); |
| for (const composite of gGoodKeyframeCompositeValueTests) { |
| const effect = new KeyframeEffect(target, getKeyframe(composite)); |
| assert_equals(effect.getKeyframes()[0].composite, composite, |
| `resulting composite for '${composite}'`); |
| } |
| for (const composite of gBadKeyframeCompositeValueTests) { |
| assert_throws_js(TypeError, () => { |
| new KeyframeEffect(target, getKeyframe(composite)); |
| }); |
| } |
| }, 'composite values are parsed correctly when passed to the ' + |
| 'KeyframeEffect constructor in property-indexed keyframes'); |
| |
| test(t => { |
| const getKeyframes = composite => |
| [ |
| { offset: 0, left: '10px', composite: composite }, |
| { offset: 1, left: '20px' } |
| ]; |
| for (const composite of gGoodKeyframeCompositeValueTests) { |
| const effect = new KeyframeEffect(target, getKeyframes(composite)); |
| assert_equals(effect.getKeyframes()[0].composite, composite, |
| `resulting composite for '${composite}'`); |
| } |
| for (const composite of gBadKeyframeCompositeValueTests) { |
| assert_throws_js(TypeError, () => { |
| new KeyframeEffect(target, getKeyframes(composite)); |
| }); |
| } |
| }, 'composite values are parsed correctly when passed to the ' + |
| 'KeyframeEffect constructor in regular keyframes'); |
| |
| test(t => { |
| for (const composite of gGoodOptionsCompositeValueTests) { |
| const effect = new KeyframeEffect(target, { |
| left: ['10px', '20px'] |
| }, { composite }); |
| assert_equals(effect.getKeyframes()[0].composite, 'auto', |
| `resulting composite for '${composite}'`); |
| } |
| for (const composite of gBadOptionsCompositeValueTests) { |
| assert_throws_js(TypeError, () => { |
| new KeyframeEffect(target, { |
| left: ['10px', '20px'] |
| }, { composite: composite }); |
| }); |
| } |
| }, 'composite value is auto if the composite operation specified on the ' + |
| 'keyframe effect is being used'); |
| |
| for (const subtest of gKeyframesTests) { |
| test(t => { |
| const effect = new KeyframeEffect(target, subtest.input); |
| assert_frame_lists_equal(effect.getKeyframes(), subtest.output); |
| }, `A KeyframeEffect can be constructed with ${subtest.desc}`); |
| |
| test(t => { |
| const effect = new KeyframeEffect(target, subtest.input); |
| const secondEffect = new KeyframeEffect(target, effect.getKeyframes()); |
| assert_frame_lists_equal(secondEffect.getKeyframes(), |
| effect.getKeyframes()); |
| }, `A KeyframeEffect constructed with ${subtest.desc} roundtrips`); |
| } |
| |
| for (const subtest of gInvalidKeyframesTests) { |
| test(t => { |
| assert_throws_js(TypeError, () => { |
| new KeyframeEffect(target, subtest.input); |
| }); |
| }, `KeyframeEffect constructor throws with ${subtest.desc}`); |
| } |
| |
| test(t => { |
| const effect = new KeyframeEffect(target, { left: ['10px', '20px'] }); |
| |
| const timing = effect.getTiming(); |
| assert_equals(timing.delay, 0, 'default delay'); |
| assert_equals(timing.endDelay, 0, 'default endDelay'); |
| assert_equals(timing.fill, 'auto', 'default fill'); |
| assert_equals(timing.iterations, 1.0, 'default iterations'); |
| assert_equals(timing.iterationStart, 0.0, 'default iterationStart'); |
| assert_equals(timing.duration, 'auto', 'default duration'); |
| assert_equals(timing.direction, 'normal', 'default direction'); |
| assert_equals(timing.easing, 'linear', 'default easing'); |
| |
| assert_equals(effect.composite, 'replace', 'default composite'); |
| assert_equals(effect.iterationComposite, 'replace', |
| 'default iterationComposite'); |
| }, 'A KeyframeEffect constructed without any KeyframeEffectOptions object'); |
| |
| for (const subtest of gKeyframeEffectOptionTests) { |
| test(t => { |
| const effect = new KeyframeEffect(target, { left: ['10px', '20px'] }, |
| subtest.input); |
| |
| // Helper function to provide default expected values when the test does |
| // not supply them. |
| const expected = (field, defaultValue) => { |
| return field in subtest.expected ? subtest.expected[field] : defaultValue; |
| }; |
| |
| const timing = effect.getTiming(); |
| assert_equals(timing.delay, expected('delay', 0), |
| 'timing delay'); |
| assert_equals(timing.fill, expected('fill', 'auto'), |
| 'timing fill'); |
| assert_equals(timing.iterations, expected('iterations', 1), |
| 'timing iterations'); |
| assert_equals(timing.duration, expected('duration', 'auto'), |
| 'timing duration'); |
| assert_equals(timing.direction, expected('direction', 'normal'), |
| 'timing direction'); |
| |
| }, `A KeyframeEffect constructed by ${subtest.desc}`); |
| } |
| |
| for (const subtest of gInvalidKeyframeEffectOptionTests) { |
| test(t => { |
| assert_throws_js(TypeError, () => { |
| new KeyframeEffect(target, { left: ['10px', '20px'] }, subtest.input); |
| }); |
| }, `Invalid KeyframeEffect option by ${subtest.desc}`); |
| } |
| |
| test(t => { |
| const effect = new KeyframeEffect(null, { left: ['10px', '20px'] }, |
| { duration: 100 * MS_PER_SEC, |
| fill: 'forwards' }); |
| assert_equals(effect.target, null, |
| 'Effect created with null target has correct target'); |
| }, 'A KeyframeEffect constructed with null target'); |
| |
| test(t => { |
| const test_error = { name: 'test' }; |
| |
| assert_throws_exactly(test_error, () => { |
| new KeyframeEffect(target, { get left() { throw test_error }}) |
| }); |
| }, 'KeyframeEffect constructor propagates exceptions generated by accessing' |
| + ' the options object'); |
| </script> |
| </body> |