@@ -5,6 +5,10 @@ import { RiveFileFactory } from '@rive-app/react-native';
55
66const DATABINDING = require ( '../assets/rive/databinding.riv' ) ;
77
8+ function isExperimental ( ) {
9+ return RiveFileFactory . getBackend ( ) === 'experimental' ;
10+ }
11+
812function expectDefined < T > ( value : T ) : asserts value is NonNullable < T > {
913 expect ( value ) . toBeDefined ( ) ;
1014}
@@ -250,3 +254,182 @@ describe('Property Listeners', () => {
250254 expect ( ( ) => cleanup2 ( ) ) . not . toThrow ( ) ;
251255 } ) ;
252256} ) ;
257+
258+ const delay = ( ms : number ) => new Promise ( ( r ) => setTimeout ( r , ms ) ) ;
259+
260+ describe ( 'Listener callback invocation (experimental only)' , ( ) => {
261+ // The experimental backend emits the current value on addListener;
262+ // legacy only fires on subsequent changes — these tests would hang.
263+ it ( 'numberProperty listener emits current value' , async ( ) => {
264+ if ( ! isExperimental ( ) ) return ;
265+ const instance = await createGordonInstance ( ) ;
266+ const prop = instance . numberProperty ( 'age' ) ;
267+ expectDefined ( prop ) ;
268+ const value = await new Promise < number > ( ( resolve ) => {
269+ const cleanup = prop . addListener ( ( v ) => {
270+ cleanup ( ) ;
271+ resolve ( v ) ;
272+ } ) ;
273+ } ) ;
274+ expect ( value ) . toBe ( 30 ) ;
275+ } ) ;
276+
277+ it ( 'stringProperty listener emits current value' , async ( ) => {
278+ if ( ! isExperimental ( ) ) return ;
279+ const instance = await createGordonInstance ( ) ;
280+ const prop = instance . stringProperty ( 'name' ) ;
281+ expectDefined ( prop ) ;
282+ const value = await new Promise < string > ( ( resolve ) => {
283+ const cleanup = prop . addListener ( ( v ) => {
284+ cleanup ( ) ;
285+ resolve ( v ) ;
286+ } ) ;
287+ } ) ;
288+ expect ( value ) . toBe ( 'Gordon' ) ;
289+ } ) ;
290+
291+ it ( 'booleanProperty listener emits current value' , async ( ) => {
292+ if ( ! isExperimental ( ) ) return ;
293+ const instance = await createGordonInstance ( ) ;
294+ const prop = instance . booleanProperty ( 'likes_popcorn' ) ;
295+ expectDefined ( prop ) ;
296+ const value = await new Promise < boolean > ( ( resolve ) => {
297+ const cleanup = prop . addListener ( ( v ) => {
298+ cleanup ( ) ;
299+ resolve ( v ) ;
300+ } ) ;
301+ } ) ;
302+ expect ( value ) . toBe ( false ) ;
303+ } ) ;
304+
305+ it ( 'colorProperty listener emits current value' , async ( ) => {
306+ if ( ! isExperimental ( ) ) return ;
307+ const instance = await createGordonInstance ( ) ;
308+ const prop = instance . colorProperty ( 'favourite_color' ) ;
309+ expectDefined ( prop ) ;
310+ const value = await new Promise < number > ( ( resolve ) => {
311+ const cleanup = prop . addListener ( ( v ) => {
312+ cleanup ( ) ;
313+ resolve ( v ) ;
314+ } ) ;
315+ } ) ;
316+ const rgb = getRGB ( value ) ;
317+ expect ( rgb ) . toEqual ( { r : 255 , g : 0 , b : 0 } ) ;
318+ } ) ;
319+
320+ it ( 'enumProperty listener emits current value' , async ( ) => {
321+ if ( ! isExperimental ( ) ) return ;
322+ const instance = await createGordonInstance ( ) ;
323+ const prop = instance . enumProperty ( 'favourite_pet' ) ;
324+ expectDefined ( prop ) ;
325+ const value = await new Promise < string > ( ( resolve ) => {
326+ const cleanup = prop . addListener ( ( v ) => {
327+ cleanup ( ) ;
328+ resolve ( v ) ;
329+ } ) ;
330+ } ) ;
331+ expect ( value ) . toBe ( 'dog' ) ;
332+ } ) ;
333+ } ) ;
334+
335+ describe ( 'set() method works for all property types' , ( ) => {
336+ it ( 'numberProperty set() updates value' , async ( ) => {
337+ const instance = await createGordonInstance ( ) ;
338+ const prop = instance . numberProperty ( 'age' ) ;
339+ expectDefined ( prop ) ;
340+ prop . set ( 99 ) ;
341+ await delay ( 100 ) ;
342+ expect ( await prop . getValueAsync ( ) ) . toBe ( 99 ) ;
343+ } ) ;
344+
345+ it ( 'stringProperty set() updates value' , async ( ) => {
346+ const instance = await createGordonInstance ( ) ;
347+ const prop = instance . stringProperty ( 'name' ) ;
348+ expectDefined ( prop ) ;
349+ prop . set ( 'Alice' ) ;
350+ await delay ( 100 ) ;
351+ expect ( await prop . getValueAsync ( ) ) . toBe ( 'Alice' ) ;
352+ } ) ;
353+
354+ it ( 'booleanProperty set() updates value' , async ( ) => {
355+ const instance = await createGordonInstance ( ) ;
356+ const prop = instance . booleanProperty ( 'likes_popcorn' ) ;
357+ expectDefined ( prop ) ;
358+ prop . set ( true ) ;
359+ await delay ( 100 ) ;
360+ expect ( await prop . getValueAsync ( ) ) . toBe ( true ) ;
361+ } ) ;
362+
363+ it ( 'colorProperty set() updates value' , async ( ) => {
364+ const instance = await createGordonInstance ( ) ;
365+ const prop = instance . colorProperty ( 'favourite_color' ) ;
366+ expectDefined ( prop ) ;
367+ prop . set ( 0xff00ff00 ) ;
368+ await delay ( 100 ) ;
369+ const rgb = getRGB ( await prop . getValueAsync ( ) ) ;
370+ expect ( rgb ) . toEqual ( { r : 0 , g : 255 , b : 0 } ) ;
371+ } ) ;
372+
373+ it ( 'enumProperty set() updates value' , async ( ) => {
374+ const instance = await createGordonInstance ( ) ;
375+ const prop = instance . enumProperty ( 'favourite_pet' ) ;
376+ expectDefined ( prop ) ;
377+ prop . set ( 'cat' ) ;
378+ await delay ( 100 ) ;
379+ expect ( await prop . getValueAsync ( ) ) . toBe ( 'cat' ) ;
380+ } ) ;
381+
382+ it ( 'triggerProperty trigger() does not throw' , async ( ) => {
383+ const instance = await createGordonInstance ( ) ;
384+ const prop = instance . triggerProperty ( 'jump' ) ;
385+ expectDefined ( prop ) ;
386+ prop . trigger ( ) ;
387+ await delay ( 100 ) ;
388+ } ) ;
389+ } ) ;
390+
391+ describe ( 'set() + getValueAsync() round-trip' , ( ) => {
392+ it ( 'booleanProperty set + getValueAsync' , async ( ) => {
393+ const instance = await createGordonInstance ( ) ;
394+ const prop = instance . booleanProperty ( 'likes_popcorn' ) ;
395+ expectDefined ( prop ) ;
396+ prop . set ( true ) ;
397+ await delay ( 100 ) ;
398+ expect ( await prop . getValueAsync ( ) ) . toBe ( true ) ;
399+ } ) ;
400+
401+ it ( 'colorProperty set + getValueAsync' , async ( ) => {
402+ const instance = await createGordonInstance ( ) ;
403+ const prop = instance . colorProperty ( 'favourite_color' ) ;
404+ expectDefined ( prop ) ;
405+ prop . set ( 0xff0000ff ) ;
406+ await delay ( 100 ) ;
407+ const rgb = getRGB ( await prop . getValueAsync ( ) ) ;
408+ expect ( rgb ) . toEqual ( { r : 0 , g : 0 , b : 255 } ) ;
409+ } ) ;
410+
411+ it ( 'enumProperty set + getValueAsync' , async ( ) => {
412+ const instance = await createGordonInstance ( ) ;
413+ const prop = instance . enumProperty ( 'favourite_pet' ) ;
414+ expectDefined ( prop ) ;
415+ prop . set ( 'cat' ) ;
416+ await delay ( 100 ) ;
417+ expect ( await prop . getValueAsync ( ) ) . toBe ( 'cat' ) ;
418+ } ) ;
419+ } ) ;
420+
421+ describe ( 'removeListeners stops callbacks' , ( ) => {
422+ it ( 'no callbacks fire after removeListeners' , async ( ) => {
423+ const instance = await createGordonInstance ( ) ;
424+ const prop = instance . numberProperty ( 'age' ) ;
425+ expectDefined ( prop ) ;
426+ const values : number [ ] = [ ] ;
427+ prop . addListener ( ( v ) => values . push ( v ) ) ;
428+ await delay ( 200 ) ;
429+ const countBefore = values . length ;
430+ prop . removeListeners ( ) ;
431+ prop . set ( 999 ) ;
432+ await delay ( 300 ) ;
433+ expect ( values . length ) . toBe ( countBefore ) ;
434+ } ) ;
435+ } ) ;
0 commit comments