@@ -8,12 +8,12 @@ import type { DomEditSelection } from "./domEditingTypes";
88( globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT : boolean } ) . IS_REACT_ACT_ENVIRONMENT = true ;
99
1010function bind ( dataAttributes : Record < string , string > ) {
11- const onSetAttribute = vi . fn ( ) ;
11+ const onSetAttributeQuiet = vi . fn ( ) ;
1212 const captured : { current : VolumeAutomationBinding | null } = { current : null } ;
1313 function Probe ( ) {
1414 captured . current = useVolumeAutomation (
1515 { dataAttributes } as unknown as DomEditSelection ,
16- onSetAttribute ,
16+ onSetAttributeQuiet ,
1717 ) ;
1818 return null ;
1919 }
@@ -23,7 +23,7 @@ function bind(dataAttributes: Record<string, string>) {
2323 createRoot ( host ) . render ( < Probe /> ) ;
2424 } ) ;
2525 if ( ! captured . current ) throw new Error ( "hook never ran" ) ;
26- return { binding : captured . current , onSetAttribute } ;
26+ return { binding : captured . current , onSetAttributeQuiet } ;
2727}
2828
2929const volumeLane = ( v : number ) =>
@@ -50,29 +50,29 @@ describe("useVolumeAutomation", () => {
5050
5151 it ( "seeds a new lane at the level the slider already shows" , ( ) => {
5252 // Automating a track must not change how loud it is.
53- const { binding, onSetAttribute } = bind ( { volume : "0.55" } ) ;
53+ const { binding, onSetAttributeQuiet } = bind ( { volume : "0.55" } ) ;
5454 act ( ( ) => binding . onAutomateVolume ( ) ) ;
55- expect ( onSetAttribute ) . toHaveBeenCalledWith (
55+ expect ( onSetAttributeQuiet ) . toHaveBeenCalledWith (
5656 "data-automation" ,
5757 JSON . stringify ( { version : 1 , lanes : [ { target : "volume" , points : [ { t : 0 , v : 0.55 } ] } ] } ) ,
5858 ) ;
5959 } ) ;
6060
6161 it ( "treats a missing data-volume as unity" , ( ) => {
62- const { binding, onSetAttribute } = bind ( { } ) ;
62+ const { binding, onSetAttributeQuiet } = bind ( { } ) ;
6363 act ( ( ) => binding . onAutomateVolume ( ) ) ;
64- expect ( JSON . parse ( String ( onSetAttribute . mock . calls [ 0 ] [ 1 ] ) ) . lanes [ 0 ] . points [ 0 ] . v ) . toBe ( 1 ) ;
64+ expect ( JSON . parse ( String ( onSetAttributeQuiet . mock . calls [ 0 ] [ 1 ] ) ) . lanes [ 0 ] . points [ 0 ] . v ) . toBe ( 1 ) ;
6565 } ) ;
6666
6767 it ( "keeps FX lanes when adding the volume one" , ( ) => {
6868 const automation = JSON . stringify ( {
6969 version : 1 ,
7070 lanes : [ { target : "fx.n1.frequency" , points : [ { t : 0 , v : 400 } ] } ] ,
7171 } ) ;
72- const { binding, onSetAttribute } = bind ( { volume : "0.4" , automation } ) ;
72+ const { binding, onSetAttributeQuiet } = bind ( { volume : "0.4" , automation } ) ;
7373 act ( ( ) => binding . onAutomateVolume ( ) ) ;
7474 expect (
75- JSON . parse ( String ( onSetAttribute . mock . calls [ 0 ] [ 1 ] ) ) . lanes . map (
75+ JSON . parse ( String ( onSetAttributeQuiet . mock . calls [ 0 ] [ 1 ] ) ) . lanes . map (
7676 ( l : { target : string } ) => l . target ,
7777 ) ,
7878 ) . toEqual ( [ "fx.n1.frequency" , "volume" ] ) ;
@@ -86,22 +86,37 @@ describe("useVolumeAutomation", () => {
8686 { target : "fx.n1.frequency" , points : [ { t : 0 , v : 400 } ] } ,
8787 ] ,
8888 } ) ;
89- const { binding, onSetAttribute } = bind ( { volume : "0.4" , automation } ) ;
89+ const { binding, onSetAttributeQuiet } = bind ( { volume : "0.4" , automation } ) ;
9090 act ( ( ) => binding . onRemoveVolumeAutomation ( ) ) ;
9191 expect (
92- JSON . parse ( String ( onSetAttribute . mock . calls [ 0 ] [ 1 ] ) ) . lanes . map (
92+ JSON . parse ( String ( onSetAttributeQuiet . mock . calls [ 0 ] [ 1 ] ) ) . lanes . map (
9393 ( l : { target : string } ) => l . target ,
9494 ) ,
9595 ) . toEqual ( [ "fx.n1.frequency" ] ) ;
9696 } ) ;
9797
9898 it ( "clears the attribute when the volume lane was the only one" , ( ) => {
99- const { binding, onSetAttribute } = bind ( { volume : "0.4" , automation : volumeLane ( 0.2 ) } ) ;
99+ const { binding, onSetAttributeQuiet } = bind ( { volume : "0.4" , automation : volumeLane ( 0.2 ) } ) ;
100100 act ( ( ) => binding . onRemoveVolumeAutomation ( ) ) ;
101- expect ( onSetAttribute ) . toHaveBeenCalledWith ( "data-automation" , "" ) ;
101+ // Null, not "": the quiet path removes an attribute it is given null for.
102+ expect ( onSetAttributeQuiet ) . toHaveBeenCalledWith ( "data-automation" , null ) ;
102103 } ) ;
103104
104105 it ( "reads an unreadable attribute as no automation" , ( ) => {
105106 expect ( bind ( { volume : "0.55" , automation : "{not json" } ) . binding . volumeAutomated ) . toBe ( false ) ;
106107 } ) ;
108+
109+ it ( "seeds at unity when data-volume is present but empty" , ( ) => {
110+ // Number("") is 0, so `?? "1"` alone seeded the lane at silence while the
111+ // engine read the same empty attribute as unity.
112+ const { binding, onSetAttributeQuiet } = bind ( { volume : "" } ) ;
113+ act ( ( ) => binding . onAutomateVolume ( ) ) ;
114+ expect ( JSON . parse ( String ( onSetAttributeQuiet . mock . calls [ 0 ] [ 1 ] ) ) . lanes [ 0 ] . points [ 0 ] . v ) . toBe ( 1 ) ;
115+ } ) ;
116+
117+ it ( "seeds at unity when data-volume is not a number" , ( ) => {
118+ const { binding, onSetAttributeQuiet } = bind ( { volume : "loud" } ) ;
119+ act ( ( ) => binding . onAutomateVolume ( ) ) ;
120+ expect ( JSON . parse ( String ( onSetAttributeQuiet . mock . calls [ 0 ] [ 1 ] ) ) . lanes [ 0 ] . points [ 0 ] . v ) . toBe ( 1 ) ;
121+ } ) ;
107122} ) ;
0 commit comments