|
1 | 1 | const helpers = require("./helpers/global-setup"); |
2 | 2 |
|
| 3 | +// Validate Animate.css integration for compliments module using class toggling. |
| 4 | +// We intentionally ignore computed animation styles (jsdom doesn't simulate real animations). |
3 | 5 | describe("AnimateCSS integration Test", () => { |
4 | | - // define config file for testing |
5 | | - let testConfigFile = "tests/configs/modules/compliments/compliments_animateCSS.js"; |
6 | | - // define config file to fallback to default: wrong animation name (must return no animation) |
7 | | - let testConfigFileFallbackToDefault = "tests/configs/modules/compliments/compliments_animateCSS_fallbackToDefault.js"; |
8 | | - // define config file with an inverted name animation : in for out and vice versa (must return no animation) |
9 | | - let testConfigFileInvertedAnimationName = "tests/configs/modules/compliments/compliments_animateCSS_invertedAnimationName.js"; |
10 | | - // define config file with no animation defined |
11 | | - let testConfigByDefault = "tests/configs/modules/compliments/compliments_anytime.js"; |
| 6 | + // Config variants under test |
| 7 | + const TEST_CONFIG_ANIM = "tests/configs/modules/compliments/compliments_animateCSS.js"; |
| 8 | + const TEST_CONFIG_FALLBACK = "tests/configs/modules/compliments/compliments_animateCSS_fallbackToDefault.js"; // invalid animation names |
| 9 | + const TEST_CONFIG_INVERTED = "tests/configs/modules/compliments/compliments_animateCSS_invertedAnimationName.js"; // in/out swapped |
| 10 | + const TEST_CONFIG_NONE = "tests/configs/modules/compliments/compliments_anytime.js"; // no animations defined |
12 | 11 |
|
13 | 12 | /** |
14 | | - * move similar tests in function doTest |
15 | | - * @param {string} [animationIn] animation in name of AnimateCSS to test. |
16 | | - * @param {string} [animationOut] animation out name of AnimateCSS to test. |
17 | | - * @returns {boolean} result |
| 13 | + * Get the compliments container element (waits until available). |
| 14 | + * @returns {Promise<HTMLElement>} compliments root element |
18 | 15 | */ |
19 | | - const doTest = async (animationIn, animationOut) => { |
| 16 | + async function getComplimentsElement () { |
20 | 17 | await helpers.getDocument(); |
21 | | - let elem = await helpers.waitForElement(".compliments"); |
22 | | - expect(elem).not.toBeNull(); |
23 | | - let styles = window.getComputedStyle(elem); |
| 18 | + const el = await helpers.waitForElement(".compliments"); |
| 19 | + expect(el).not.toBeNull(); |
| 20 | + return el; |
| 21 | + } |
24 | 22 |
|
25 | | - if (animationIn && animationIn !== "") { |
26 | | - expect(styles._values.get("animation-name")).toBe(animationIn); |
27 | | - } else { |
28 | | - expect(styles._values.get("animation-name")).toBeUndefined(); |
| 23 | + /** |
| 24 | + * Wait for an Animate.css class to appear and persist briefly. |
| 25 | + * @param {string} cls Animation class name without leading dot (e.g. animate__flipInX) |
| 26 | + * @param {{timeout?: number}} [options] Poll timeout in ms (default 6000) |
| 27 | + * @returns {Promise<boolean>} true if class detected in time |
| 28 | + */ |
| 29 | + async function waitForAnimationClass (cls, { timeout = 6000 } = {}) { |
| 30 | + const start = Date.now(); |
| 31 | + while (Date.now() - start < timeout) { |
| 32 | + if (document.querySelector(`.compliments.animate__animated.${cls}`)) { |
| 33 | + // small stability wait |
| 34 | + await new Promise((r) => setTimeout(r, 50)); |
| 35 | + if (document.querySelector(`.compliments.animate__animated.${cls}`)) return true; |
| 36 | + } |
| 37 | + await new Promise((r) => setTimeout(r, 100)); |
29 | 38 | } |
| 39 | + throw new Error(`Timeout waiting for class ${cls}`); |
| 40 | + } |
30 | 41 |
|
31 | | - if (animationOut && animationOut !== "") { |
32 | | - elem = await helpers.waitForElement(`.compliments.animate__animated.animate__${animationOut}`); |
33 | | - expect(elem).not.toBeNull(); |
34 | | - styles = window.getComputedStyle(elem); |
35 | | - expect(styles._values.get("animation-name")).toBe(animationOut); |
36 | | - } else { |
37 | | - expect(styles._values.get("animation-name")).toBeUndefined(); |
| 42 | + /** |
| 43 | + * Assert that no Animate.css animation class is applied within a time window. |
| 44 | + * @param {number} [ms] Observation period in ms (default 2000) |
| 45 | + * @returns {Promise<void>} |
| 46 | + */ |
| 47 | + async function assertNoAnimationWithin (ms = 2000) { |
| 48 | + const start = Date.now(); |
| 49 | + while (Date.now() - start < ms) { |
| 50 | + if (document.querySelector(".compliments.animate__animated")) { |
| 51 | + throw new Error("Unexpected animate__animated class present in non-animation scenario"); |
| 52 | + } |
| 53 | + await new Promise((r) => setTimeout(r, 100)); |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * Run one animation test scenario. |
| 59 | + * @param {string} [animationIn] Expected animate-in name |
| 60 | + * @param {string} [animationOut] Expected animate-out name |
| 61 | + * @returns {Promise<boolean>} true when scenario assertions pass |
| 62 | + */ |
| 63 | + async function runAnimationTest (animationIn, animationOut) { |
| 64 | + await getComplimentsElement(); |
| 65 | + if (!animationIn && !animationOut) { |
| 66 | + await assertNoAnimationWithin(2000); |
| 67 | + return true; |
| 68 | + } |
| 69 | + if (animationIn) await waitForAnimationClass(`animate__${animationIn}`); |
| 70 | + if (animationOut) { |
| 71 | + // Wait just beyond one update cycle (updateInterval=2000ms) before expecting animateOut. |
| 72 | + await new Promise((r) => setTimeout(r, 2100)); |
| 73 | + await waitForAnimationClass(`animate__${animationOut}`); |
38 | 74 | } |
39 | 75 | return true; |
40 | | - }; |
| 76 | + } |
41 | 77 |
|
42 | 78 | afterEach(async () => { |
43 | 79 | await helpers.stopApplication(); |
44 | 80 | }); |
45 | 81 |
|
46 | 82 | describe("animateIn and animateOut Test", () => { |
47 | 83 | it("with flipInX and flipOutX animation", async () => { |
48 | | - await helpers.startApplication(testConfigFile); |
49 | | - await expect(doTest("flipInX", "flipOutX")).resolves.toBe(true); |
| 84 | + await helpers.startApplication(TEST_CONFIG_ANIM); |
| 85 | + await expect(runAnimationTest("flipInX", "flipOutX")).resolves.toBe(true); |
50 | 86 | }); |
51 | 87 | }); |
52 | 88 |
|
53 | 89 | describe("use animateOut name for animateIn (vice versa) Test", () => { |
54 | | - it("without animation", async () => { |
55 | | - await helpers.startApplication(testConfigFileInvertedAnimationName); |
56 | | - await expect(doTest()).resolves.toBe(true); |
| 90 | + it("without animation (inverted names)", async () => { |
| 91 | + await helpers.startApplication(TEST_CONFIG_INVERTED); |
| 92 | + await expect(runAnimationTest()).resolves.toBe(true); |
57 | 93 | }); |
58 | 94 | }); |
59 | 95 |
|
60 | 96 | describe("false Animation name test", () => { |
61 | | - it("without animation", async () => { |
62 | | - await helpers.startApplication(testConfigFileFallbackToDefault); |
63 | | - await expect(doTest()).resolves.toBe(true); |
| 97 | + it("without animation (invalid names)", async () => { |
| 98 | + await helpers.startApplication(TEST_CONFIG_FALLBACK); |
| 99 | + await expect(runAnimationTest()).resolves.toBe(true); |
64 | 100 | }); |
65 | 101 | }); |
66 | 102 |
|
67 | 103 | describe("no Animation defined test", () => { |
68 | | - it("without animation", async () => { |
69 | | - await helpers.startApplication(testConfigByDefault); |
70 | | - await expect(doTest()).resolves.toBe(true); |
| 104 | + it("without animation (no config)", async () => { |
| 105 | + await helpers.startApplication(TEST_CONFIG_NONE); |
| 106 | + await expect(runAnimationTest()).resolves.toBe(true); |
71 | 107 | }); |
72 | 108 | }); |
73 | 109 | }); |
0 commit comments