1cb93a386Sopenharmony_cidescribe('Skottie behavior', () => {
2cb93a386Sopenharmony_ci    let container;
3cb93a386Sopenharmony_ci
4cb93a386Sopenharmony_ci    beforeEach(async () => {
5cb93a386Sopenharmony_ci        await LoadCanvasKit;
6cb93a386Sopenharmony_ci        container = document.createElement('div');
7cb93a386Sopenharmony_ci        container.innerHTML = `
8cb93a386Sopenharmony_ci            <canvas width=600 height=600 id=test></canvas>
9cb93a386Sopenharmony_ci            <canvas width=600 height=600 id=report></canvas>`;
10cb93a386Sopenharmony_ci        document.body.appendChild(container);
11cb93a386Sopenharmony_ci    });
12cb93a386Sopenharmony_ci
13cb93a386Sopenharmony_ci    afterEach(() => {
14cb93a386Sopenharmony_ci        document.body.removeChild(container);
15cb93a386Sopenharmony_ci    });
16cb93a386Sopenharmony_ci
17cb93a386Sopenharmony_ci    const expectArrayCloseTo = (a, b, precision) => {
18cb93a386Sopenharmony_ci        precision = precision || 14; // digits of precision in base 10
19cb93a386Sopenharmony_ci        expect(a.length).toEqual(b.length);
20cb93a386Sopenharmony_ci        for (let i=0; i<a.length; i++) {
21cb93a386Sopenharmony_ci          expect(a[i]).toBeCloseTo(b[i], precision);
22cb93a386Sopenharmony_ci        }
23cb93a386Sopenharmony_ci    };
24cb93a386Sopenharmony_ci
25cb93a386Sopenharmony_ci    const imgPromise = fetch('/assets/flightAnim.gif')
26cb93a386Sopenharmony_ci        .then((response) => response.arrayBuffer());
27cb93a386Sopenharmony_ci    const jsonPromise = fetch('/assets/animated_gif.json')
28cb93a386Sopenharmony_ci        .then((response) => response.text());
29cb93a386Sopenharmony_ci    const washPromise = fetch('/assets/map-shield.json')
30cb93a386Sopenharmony_ci        .then((response) => response.text());
31cb93a386Sopenharmony_ci
32cb93a386Sopenharmony_ci    gm('skottie_animgif', (canvas, promises) => {
33cb93a386Sopenharmony_ci        if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
34cb93a386Sopenharmony_ci            console.warn('Skipping test because not compiled with skottie');
35cb93a386Sopenharmony_ci            return;
36cb93a386Sopenharmony_ci        }
37cb93a386Sopenharmony_ci        expect(promises[1]).not.toBe('NOT FOUND');
38cb93a386Sopenharmony_ci        const animation = CanvasKit.MakeManagedAnimation(promises[1], {
39cb93a386Sopenharmony_ci            'flightAnim.gif': promises[0],
40cb93a386Sopenharmony_ci        });
41cb93a386Sopenharmony_ci        expect(animation).toBeTruthy();
42cb93a386Sopenharmony_ci        const bounds = CanvasKit.LTRBRect(0, 0, 500, 500);
43cb93a386Sopenharmony_ci
44cb93a386Sopenharmony_ci        const size = animation.size();
45cb93a386Sopenharmony_ci        expectArrayCloseTo(size, Float32Array.of(800, 600), 4);
46cb93a386Sopenharmony_ci
47cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
48cb93a386Sopenharmony_ci        animation.render(canvas, bounds);
49cb93a386Sopenharmony_ci
50cb93a386Sopenharmony_ci        // We intentionally make the length of this array 5 and add a sentinel value
51cb93a386Sopenharmony_ci        // of 999 so we can make sure the bounds are copied into this rect and a new
52cb93a386Sopenharmony_ci        // one is not allocated.
53cb93a386Sopenharmony_ci        const damageRect = Float32Array.of(0, 0, 0, 0, 999);
54cb93a386Sopenharmony_ci
55cb93a386Sopenharmony_ci        // There was a bug, fixed in https://skia-review.googlesource.com/c/skia/+/241757
56cb93a386Sopenharmony_ci        // that seeking again and drawing again revealed.
57cb93a386Sopenharmony_ci        animation.seek(0.5, damageRect);
58cb93a386Sopenharmony_ci        expectArrayCloseTo(damageRect, Float32Array.of(0, 0, 800, 600, 999), 4);
59cb93a386Sopenharmony_ci
60cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
61cb93a386Sopenharmony_ci        animation.render(canvas, bounds);
62cb93a386Sopenharmony_ci        animation.delete();
63cb93a386Sopenharmony_ci    }, imgPromise, jsonPromise);
64cb93a386Sopenharmony_ci
65cb93a386Sopenharmony_ci    gm('skottie_setcolor', (canvas, promises) => {
66cb93a386Sopenharmony_ci        if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
67cb93a386Sopenharmony_ci            console.warn('Skipping test because not compiled with skottie');
68cb93a386Sopenharmony_ci            return;
69cb93a386Sopenharmony_ci        }
70cb93a386Sopenharmony_ci        expect(promises[0]).not.toBe('NOT FOUND');
71cb93a386Sopenharmony_ci        const bounds = CanvasKit.LTRBRect(0, 0, 500, 500);
72cb93a386Sopenharmony_ci        canvas.clear(CanvasKit.WHITE);
73cb93a386Sopenharmony_ci
74cb93a386Sopenharmony_ci        const animation = CanvasKit.MakeManagedAnimation(promises[0]);
75cb93a386Sopenharmony_ci        expect(animation).toBeTruthy();
76cb93a386Sopenharmony_ci        animation.setColor('$Icon Fill', CanvasKit.RED);
77cb93a386Sopenharmony_ci        animation.seek(0.5);
78cb93a386Sopenharmony_ci        animation.render(canvas, bounds);
79cb93a386Sopenharmony_ci        animation.delete();
80cb93a386Sopenharmony_ci    }, washPromise);
81cb93a386Sopenharmony_ci
82cb93a386Sopenharmony_ci    it('can load audio assets', (done) => {
83cb93a386Sopenharmony_ci        if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
84cb93a386Sopenharmony_ci            console.warn('Skipping test because not compiled with skottie');
85cb93a386Sopenharmony_ci            return;
86cb93a386Sopenharmony_ci        }
87cb93a386Sopenharmony_ci        const mockSoundMap = {
88cb93a386Sopenharmony_ci            map : new Map(),
89cb93a386Sopenharmony_ci            getPlayer : function(name) {return this.map.get(name)},
90cb93a386Sopenharmony_ci            setPlayer : function(name, player) {this.map.set(name, player)},
91cb93a386Sopenharmony_ci        };
92cb93a386Sopenharmony_ci        function mockPlayer(name) {
93cb93a386Sopenharmony_ci            this.name = name;
94cb93a386Sopenharmony_ci            this.wasPlayed = false,
95cb93a386Sopenharmony_ci            this.seek = function(t) {
96cb93a386Sopenharmony_ci                this.wasPlayed = true;
97cb93a386Sopenharmony_ci            }
98cb93a386Sopenharmony_ci        }
99cb93a386Sopenharmony_ci        for (let i = 0; i < 20; i++) {
100cb93a386Sopenharmony_ci            var name = 'audio_' + i;
101cb93a386Sopenharmony_ci            mockSoundMap.setPlayer(name, new mockPlayer(name));
102cb93a386Sopenharmony_ci        }
103cb93a386Sopenharmony_ci        fetch('/assets/audio_external.json')
104cb93a386Sopenharmony_ci        .then((response) => response.text())
105cb93a386Sopenharmony_ci        .then((lottie) => {
106cb93a386Sopenharmony_ci            const animation = CanvasKit.MakeManagedAnimation(lottie, null, null, mockSoundMap);
107cb93a386Sopenharmony_ci            expect(animation).toBeTruthy();
108cb93a386Sopenharmony_ci            // 190 frames in sample lottie
109cb93a386Sopenharmony_ci            for (let t = 0; t < 190; t++) {
110cb93a386Sopenharmony_ci                animation.seekFrame(t);
111cb93a386Sopenharmony_ci            }
112cb93a386Sopenharmony_ci            animation.delete();
113cb93a386Sopenharmony_ci            for(const player of mockSoundMap.map.values()) {
114cb93a386Sopenharmony_ci                expect(player.wasPlayed).toBeTrue(player.name + " was not played");
115cb93a386Sopenharmony_ci            }
116cb93a386Sopenharmony_ci            done();
117cb93a386Sopenharmony_ci        });
118cb93a386Sopenharmony_ci    });
119cb93a386Sopenharmony_ci
120cb93a386Sopenharmony_ci    it('can get logs', (done) => {
121cb93a386Sopenharmony_ci        if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
122cb93a386Sopenharmony_ci            console.warn('Skipping test because not compiled with skottie');
123cb93a386Sopenharmony_ci            return;
124cb93a386Sopenharmony_ci        }
125cb93a386Sopenharmony_ci
126cb93a386Sopenharmony_ci        const logger = {
127cb93a386Sopenharmony_ci           errors:   [],
128cb93a386Sopenharmony_ci           warnings: [],
129cb93a386Sopenharmony_ci
130cb93a386Sopenharmony_ci           reset: function() { this.errors = []; this.warnings = []; },
131cb93a386Sopenharmony_ci
132cb93a386Sopenharmony_ci           // Logger API
133cb93a386Sopenharmony_ci           onError:   function(err) { this.errors.push(err)   },
134cb93a386Sopenharmony_ci           onWarning: function(wrn) { this.warnings.push(wrn) }
135cb93a386Sopenharmony_ci        };
136cb93a386Sopenharmony_ci
137cb93a386Sopenharmony_ci        {
138cb93a386Sopenharmony_ci            const json = `{
139cb93a386Sopenharmony_ci                "v": "5.2.1",
140cb93a386Sopenharmony_ci                "w": 100,
141cb93a386Sopenharmony_ci                "h": 100,
142cb93a386Sopenharmony_ci                "fr": 10,
143cb93a386Sopenharmony_ci                "ip": 0,
144cb93a386Sopenharmony_ci                "op": 100,
145cb93a386Sopenharmony_ci                "layers": [{
146cb93a386Sopenharmony_ci                    "ty": 3,
147cb93a386Sopenharmony_ci                    "nm": "null",
148cb93a386Sopenharmony_ci                    "ind": 0,
149cb93a386Sopenharmony_ci                    "ip": 0
150cb93a386Sopenharmony_ci                }]
151cb93a386Sopenharmony_ci            }`;
152cb93a386Sopenharmony_ci            const animation = CanvasKit.MakeManagedAnimation(json, null, null, null, logger);
153cb93a386Sopenharmony_ci            expect(animation).toBeTruthy();
154cb93a386Sopenharmony_ci            expect(logger.errors.length).toEqual(0);
155cb93a386Sopenharmony_ci            expect(logger.warnings.length).toEqual(0);
156cb93a386Sopenharmony_ci        }
157cb93a386Sopenharmony_ci
158cb93a386Sopenharmony_ci        {
159cb93a386Sopenharmony_ci            const json = `{
160cb93a386Sopenharmony_ci                "v": "5.2.1",
161cb93a386Sopenharmony_ci                "w": 100,
162cb93a386Sopenharmony_ci                "h": 100,
163cb93a386Sopenharmony_ci                "fr": 10,
164cb93a386Sopenharmony_ci                "ip": 0,
165cb93a386Sopenharmony_ci                "op": 100,
166cb93a386Sopenharmony_ci                "layers": [{
167cb93a386Sopenharmony_ci                    "ty": 2,
168cb93a386Sopenharmony_ci                    "nm": "image",
169cb93a386Sopenharmony_ci                    "ind": 0,
170cb93a386Sopenharmony_ci                    "ip": 0
171cb93a386Sopenharmony_ci                }]
172cb93a386Sopenharmony_ci            }`;
173cb93a386Sopenharmony_ci            const animation = CanvasKit.MakeManagedAnimation(json, null, null, null, logger);
174cb93a386Sopenharmony_ci            expect(animation).toBeTruthy();
175cb93a386Sopenharmony_ci            expect(logger.errors.length).toEqual(1);
176cb93a386Sopenharmony_ci            expect(logger.warnings.length).toEqual(0);
177cb93a386Sopenharmony_ci
178cb93a386Sopenharmony_ci            // Image layer missing refID
179cb93a386Sopenharmony_ci            expect(logger.errors[0].includes('missing ref'));
180cb93a386Sopenharmony_ci            logger.reset();
181cb93a386Sopenharmony_ci        }
182cb93a386Sopenharmony_ci
183cb93a386Sopenharmony_ci        {
184cb93a386Sopenharmony_ci            const json = `{
185cb93a386Sopenharmony_ci                "v": "5.2.1",
186cb93a386Sopenharmony_ci                "w": 100,
187cb93a386Sopenharmony_ci                "h": 100,
188cb93a386Sopenharmony_ci                "fr": 10,
189cb93a386Sopenharmony_ci                "ip": 0,
190cb93a386Sopenharmony_ci                "op": 100,
191cb93a386Sopenharmony_ci                "layers": [{
192cb93a386Sopenharmony_ci                    "ty": 1,
193cb93a386Sopenharmony_ci                    "nm": "solid",
194cb93a386Sopenharmony_ci                    "sw": 100,
195cb93a386Sopenharmony_ci                    "sh": 100,
196cb93a386Sopenharmony_ci                    "sc": "#aabbcc",
197cb93a386Sopenharmony_ci                    "ind": 0,
198cb93a386Sopenharmony_ci                    "ip": 0,
199cb93a386Sopenharmony_ci                    "ef": [{
200cb93a386Sopenharmony_ci                      "mn": "FOO"
201cb93a386Sopenharmony_ci                    }]
202cb93a386Sopenharmony_ci                }]
203cb93a386Sopenharmony_ci            }`;
204cb93a386Sopenharmony_ci            const animation = CanvasKit.MakeManagedAnimation(json, null, null, null, logger);
205cb93a386Sopenharmony_ci            expect(animation).toBeTruthy();
206cb93a386Sopenharmony_ci            expect(logger.errors.length).toEqual(0);
207cb93a386Sopenharmony_ci            expect(logger.warnings.length).toEqual(1);
208cb93a386Sopenharmony_ci
209cb93a386Sopenharmony_ci            // Unsupported effect FOO
210cb93a386Sopenharmony_ci            expect(logger.warnings[0].includes('FOO'));
211cb93a386Sopenharmony_ci            logger.reset();
212cb93a386Sopenharmony_ci        }
213cb93a386Sopenharmony_ci
214cb93a386Sopenharmony_ci        done();
215cb93a386Sopenharmony_ci    });
216cb93a386Sopenharmony_ci
217cb93a386Sopenharmony_ci    it('can access dynamic props', () => {
218cb93a386Sopenharmony_ci        if (!CanvasKit.skottie || !CanvasKit.managed_skottie) {
219cb93a386Sopenharmony_ci            console.warn('Skipping test because not compiled with skottie');
220cb93a386Sopenharmony_ci            return;
221cb93a386Sopenharmony_ci        }
222cb93a386Sopenharmony_ci
223cb93a386Sopenharmony_ci        const json = `{
224cb93a386Sopenharmony_ci            "v": "5.2.1",
225cb93a386Sopenharmony_ci            "w": 100,
226cb93a386Sopenharmony_ci            "h": 100,
227cb93a386Sopenharmony_ci            "fr": 10,
228cb93a386Sopenharmony_ci            "ip": 0,
229cb93a386Sopenharmony_ci            "op": 100,
230cb93a386Sopenharmony_ci            "fonts": {
231cb93a386Sopenharmony_ci              "list": [{
232cb93a386Sopenharmony_ci                "fName": "test_font",
233cb93a386Sopenharmony_ci                "fFamily": "test-family",
234cb93a386Sopenharmony_ci                "fStyle": "TestFontStyle"
235cb93a386Sopenharmony_ci              }]
236cb93a386Sopenharmony_ci            },
237cb93a386Sopenharmony_ci            "layers": [
238cb93a386Sopenharmony_ci              {
239cb93a386Sopenharmony_ci                "ty": 4,
240cb93a386Sopenharmony_ci                "nm": "__shape_layer",
241cb93a386Sopenharmony_ci                "ind": 0,
242cb93a386Sopenharmony_ci                "ip": 0,
243cb93a386Sopenharmony_ci                "shapes": [
244cb93a386Sopenharmony_ci                  {
245cb93a386Sopenharmony_ci                    "ty": "el",
246cb93a386Sopenharmony_ci                    "p": { "a": 0, "k": [ 50, 50 ] },
247cb93a386Sopenharmony_ci                    "s": { "a": 0, "k": [ 50, 50 ] }
248cb93a386Sopenharmony_ci                  },{
249cb93a386Sopenharmony_ci                    "ty": "fl",
250cb93a386Sopenharmony_ci                    "nm": "__shape_fill",
251cb93a386Sopenharmony_ci                    "c": { "a": 0, "k": [ 1, 0, 0] }
252cb93a386Sopenharmony_ci                  },{
253cb93a386Sopenharmony_ci                    "ty": "tr",
254cb93a386Sopenharmony_ci                    "nm": "__shape_opacity",
255cb93a386Sopenharmony_ci                    "o": { "a": 0, "k": 50 }
256cb93a386Sopenharmony_ci                  }
257cb93a386Sopenharmony_ci                ]
258cb93a386Sopenharmony_ci              },{
259cb93a386Sopenharmony_ci                "ty": 5,
260cb93a386Sopenharmony_ci                "nm": "__text_layer",
261cb93a386Sopenharmony_ci                "ip": 0,
262cb93a386Sopenharmony_ci                "t": {
263cb93a386Sopenharmony_ci                  "d": {
264cb93a386Sopenharmony_ci                    "k": [{
265cb93a386Sopenharmony_ci                      "t": 0,
266cb93a386Sopenharmony_ci                      "s": {
267cb93a386Sopenharmony_ci                        "f": "test_font",
268cb93a386Sopenharmony_ci                        "s": 100,
269cb93a386Sopenharmony_ci                        "t": "Foo Bar Baz",
270cb93a386Sopenharmony_ci                        "lh": 120,
271cb93a386Sopenharmony_ci                        "ls": 12
272cb93a386Sopenharmony_ci                      }
273cb93a386Sopenharmony_ci                    }]
274cb93a386Sopenharmony_ci                  }
275cb93a386Sopenharmony_ci                }
276cb93a386Sopenharmony_ci              }
277cb93a386Sopenharmony_ci            ]
278cb93a386Sopenharmony_ci        }`;
279cb93a386Sopenharmony_ci
280cb93a386Sopenharmony_ci        const animation = CanvasKit.MakeManagedAnimation(json, null, '__');
281cb93a386Sopenharmony_ci        expect(animation).toBeTruthy();
282cb93a386Sopenharmony_ci
283cb93a386Sopenharmony_ci        {
284cb93a386Sopenharmony_ci            const colors = animation.getColorProps();
285cb93a386Sopenharmony_ci            expect(colors.length).toEqual(1);
286cb93a386Sopenharmony_ci            expect(colors[0].key).toEqual('__shape_fill');
287cb93a386Sopenharmony_ci            expect(colors[0].value).toEqual(CanvasKit.ColorAsInt(255,0,0,255));
288cb93a386Sopenharmony_ci
289cb93a386Sopenharmony_ci            const opacities = animation.getOpacityProps();
290cb93a386Sopenharmony_ci            expect(opacities.length).toEqual(1);
291cb93a386Sopenharmony_ci            expect(opacities[0].key).toEqual('__shape_opacity');
292cb93a386Sopenharmony_ci            expect(opacities[0].value).toEqual(50);
293cb93a386Sopenharmony_ci
294cb93a386Sopenharmony_ci            const texts = animation.getTextProps();
295cb93a386Sopenharmony_ci            expect(texts.length).toEqual(1);
296cb93a386Sopenharmony_ci            expect(texts[0].key).toEqual('__text_layer');
297cb93a386Sopenharmony_ci            expect(texts[0].value.text).toEqual('Foo Bar Baz');
298cb93a386Sopenharmony_ci            expect(texts[0].value.size).toEqual(100);
299cb93a386Sopenharmony_ci        }
300cb93a386Sopenharmony_ci
301cb93a386Sopenharmony_ci        expect(animation.setColor('__shape_fill', [0,1,0,1])).toEqual(true);
302cb93a386Sopenharmony_ci        expect(animation.setOpacity('__shape_opacity', 100)).toEqual(true);
303cb93a386Sopenharmony_ci        expect(animation.setText('__text_layer', 'baz bar foo', 10)).toEqual(true);
304cb93a386Sopenharmony_ci
305cb93a386Sopenharmony_ci        {
306cb93a386Sopenharmony_ci            const colors = animation.getColorProps();
307cb93a386Sopenharmony_ci            expect(colors.length).toEqual(1);
308cb93a386Sopenharmony_ci            expect(colors[0].key).toEqual('__shape_fill');
309cb93a386Sopenharmony_ci            expect(colors[0].value).toEqual(CanvasKit.ColorAsInt(0,255,0,255));
310cb93a386Sopenharmony_ci
311cb93a386Sopenharmony_ci            const opacities = animation.getOpacityProps();
312cb93a386Sopenharmony_ci            expect(opacities.length).toEqual(1);
313cb93a386Sopenharmony_ci            expect(opacities[0].key).toEqual('__shape_opacity');
314cb93a386Sopenharmony_ci            expect(opacities[0].value).toEqual(100);
315cb93a386Sopenharmony_ci
316cb93a386Sopenharmony_ci            const texts = animation.getTextProps();
317cb93a386Sopenharmony_ci            expect(texts.length).toEqual(1);
318cb93a386Sopenharmony_ci            expect(texts[0].key).toEqual('__text_layer');
319cb93a386Sopenharmony_ci            expect(texts[0].value.text).toEqual('baz bar foo');
320cb93a386Sopenharmony_ci            expect(texts[0].value.size).toEqual(10);
321cb93a386Sopenharmony_ci        }
322cb93a386Sopenharmony_ci
323cb93a386Sopenharmony_ci        expect(animation.setColor('INVALID_KEY', [0,1,0,1])).toEqual(false);
324cb93a386Sopenharmony_ci        expect(animation.setOpacity('INVALID_KEY', 100)).toEqual(false);
325cb93a386Sopenharmony_ci        expect(animation.setText('INVALID KEY', '', 10)).toEqual(false);
326cb93a386Sopenharmony_ci    });
327cb93a386Sopenharmony_ci});
328