1- const Tempo = require ( "./tempo.js" ) ;
2-
3- // --- 1. Global Mocks (Fake the Browser Environment) ---
4- global . _ = ( msg ) => msg ; // Mock translation function
5- global . getDrumSynthName = jest . fn ( ) ;
6-
7- // Mock the Window Manager
8- global . window = {
9- widgetWindows : {
10- windowFor : jest . fn ( ) . mockReturnValue ( {
11- clear : jest . fn ( ) ,
12- show : jest . fn ( ) ,
13- addButton : jest . fn ( ) . mockReturnValue ( { onclick : ( ) => { } } ) ,
14- addInputButton : jest . fn ( ) . mockImplementation ( ( val ) => ( {
15- value : val ,
16- addEventListener : jest . fn ( )
17- } ) ) ,
18- getWidgetBody : jest . fn ( ) . mockReturnValue ( {
19- appendChild : jest . fn ( ) ,
20- insertRow : jest . fn ( ) . mockReturnValue ( {
21- insertCell : jest . fn ( ) . mockReturnValue ( {
22- appendChild : jest . fn ( ) ,
23- setAttribute : jest . fn ( )
24- } )
25- } )
26- } ) ,
27- sendToCenter : jest . fn ( )
28- } )
29- }
30- } ;
31-
32- // Mock Document (for creating the canvas)
33- global . document = {
34- createElement : jest . fn ( ) . mockReturnValue ( {
35- style : { } ,
36- getContext : jest . fn ( ) . mockReturnValue ( {
37- clearRect : jest . fn ( ) ,
38- beginPath : jest . fn ( ) ,
39- fillStyle : "" ,
40- ellipse : jest . fn ( ) ,
41- fill : jest . fn ( ) ,
42- closePath : jest . fn ( )
43- } )
44- } )
45- } ;
46-
47- describe ( "Tempo Widget" , ( ) => {
48- let tempoWidget ;
49- let mockActivity ;
50-
51- beforeEach ( ( ) => {
52- tempoWidget = new Tempo ( ) ;
53-
54- // Mock the Music Blocks Activity object
55- mockActivity = {
56- logo : {
57- synth : { loadSynth : jest . fn ( ) } ,
58- firstNoteTime : 1000
59- } ,
60- blocks : {
61- blockList : { } , // Empty block list for now
62- loadNewBlocks : jest . fn ( )
63- } ,
64- refreshCanvas : jest . fn ( ) ,
65- saveLocally : jest . fn ( ) ,
66- textMsg : jest . fn ( ) ,
67- errorMsg : jest . fn ( ) // This is what the code calls!
68- } ;
69-
70- // --- FIX 1: Set the Global 'activity' variable ---
71- // The widget code calls 'activity.errorMsg', relying on it being global.
72- global . activity = mockActivity ;
73-
74- // Manually setup initial state usually handled by init()
75- tempoWidget . activity = mockActivity ;
76- tempoWidget . BPMs = [ 100 ] ; // Start at 100 BPM
77- tempoWidget . BPMInputs = [ { value : 100 } ] ; // Fake input element
78- tempoWidget . _intervals = [ 600 ] ;
79- tempoWidget . BPMBlocks = [ null ] ;
80-
81- // --- FIX 2: Initialize 'isMoving' ---
82- tempoWidget . isMoving = true ;
83- } ) ;
84-
85- test ( "should initialize with default values" , ( ) => {
86- expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 100 ) ;
87- expect ( tempoWidget . isMoving ) . toBe ( true ) ;
88- } ) ;
89-
90- test ( "speedUp() should increase BPM by 10%" , ( ) => {
91- // 100 + 10% = 110
92- tempoWidget . speedUp ( 0 ) ;
93- expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 110 ) ;
94- expect ( tempoWidget . BPMInputs [ 0 ] . value ) . toBe ( 110 ) ;
95- } ) ;
96-
97- test ( "slowDown() should decrease BPM by 10%" , ( ) => {
98- // 100 - 10% = 90
99- tempoWidget . slowDown ( 0 ) ;
100- expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 90 ) ;
101- expect ( tempoWidget . BPMInputs [ 0 ] . value ) . toBe ( 90 ) ;
102- } ) ;
103-
104- test ( "should not exceed maximum BPM of 1000" , ( ) => {
105- tempoWidget . BPMs [ 0 ] = 950 ;
106- // 950 + 95 = 1045 -> Should clamp to 1000
107- tempoWidget . speedUp ( 0 ) ;
108-
109- expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 1000 ) ;
110- expect ( mockActivity . errorMsg ) . toHaveBeenCalled ( ) ;
111- } ) ;
112-
113- test ( "should not go below minimum BPM of 30" , ( ) => {
114- tempoWidget . BPMs [ 0 ] = 32 ;
115- // 32 - 3.2 = 28.8 -> Should clamp to 30
116- tempoWidget . slowDown ( 0 ) ;
117-
118- expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 30 ) ;
119- expect ( mockActivity . errorMsg ) . toHaveBeenCalled ( ) ;
120- } ) ;
121- } ) ;
1+ const Tempo = require ( "./tempo.js" ) ;
2+
3+ // --- 1. Global Mocks (Fake the Browser Environment) ---
4+ global . _ = msg => msg ; // Mock translation function
5+ global . getDrumSynthName = jest . fn ( ) ;
6+
7+ // Mock the Window Manager
8+ global . window = {
9+ widgetWindows : {
10+ windowFor : jest . fn ( ) . mockReturnValue ( {
11+ clear : jest . fn ( ) ,
12+ show : jest . fn ( ) ,
13+ addButton : jest . fn ( ) . mockReturnValue ( { onclick : ( ) => { } } ) ,
14+ addInputButton : jest . fn ( ) . mockImplementation ( val => ( {
15+ value : val ,
16+ addEventListener : jest . fn ( )
17+ } ) ) ,
18+ getWidgetBody : jest . fn ( ) . mockReturnValue ( {
19+ appendChild : jest . fn ( ) ,
20+ insertRow : jest . fn ( ) . mockReturnValue ( {
21+ insertCell : jest . fn ( ) . mockReturnValue ( {
22+ appendChild : jest . fn ( ) ,
23+ setAttribute : jest . fn ( )
24+ } )
25+ } )
26+ } ) ,
27+ sendToCenter : jest . fn ( )
28+ } )
29+ }
30+ } ;
31+
32+ // Mock Document (for creating the canvas)
33+ global . document = {
34+ createElement : jest . fn ( ) . mockReturnValue ( {
35+ style : { } ,
36+ getContext : jest . fn ( ) . mockReturnValue ( {
37+ clearRect : jest . fn ( ) ,
38+ beginPath : jest . fn ( ) ,
39+ fillStyle : "" ,
40+ ellipse : jest . fn ( ) ,
41+ fill : jest . fn ( ) ,
42+ closePath : jest . fn ( )
43+ } )
44+ } )
45+ } ;
46+
47+ describe ( "Tempo Widget" , ( ) => {
48+ let tempoWidget ;
49+ let mockActivity ;
50+
51+ beforeEach ( ( ) => {
52+ tempoWidget = new Tempo ( ) ;
53+
54+ // Mock the Music Blocks Activity object
55+ mockActivity = {
56+ logo : {
57+ synth : { loadSynth : jest . fn ( ) } ,
58+ firstNoteTime : 1000
59+ } ,
60+ blocks : {
61+ blockList : { } , // Empty block list for now
62+ loadNewBlocks : jest . fn ( )
63+ } ,
64+ refreshCanvas : jest . fn ( ) ,
65+ saveLocally : jest . fn ( ) ,
66+ textMsg : jest . fn ( ) ,
67+ errorMsg : jest . fn ( ) // This is what the code calls!
68+ } ;
69+
70+ // --- FIX 1: Set the Global 'activity' variable ---
71+ // The widget code calls 'activity.errorMsg', relying on it being global.
72+ global . activity = mockActivity ;
73+
74+ // Manually setup initial state usually handled by init()
75+ tempoWidget . activity = mockActivity ;
76+ tempoWidget . BPMs = [ 100 ] ; // Start at 100 BPM
77+ tempoWidget . BPMInputs = [ { value : 100 } ] ; // Fake input element
78+ tempoWidget . _intervals = [ 600 ] ;
79+ tempoWidget . BPMBlocks = [ null ] ;
80+
81+ // --- FIX 2: Initialize 'isMoving' ---
82+ tempoWidget . isMoving = true ;
83+ } ) ;
84+
85+ test ( "should initialize with default values" , ( ) => {
86+ expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 100 ) ;
87+ expect ( tempoWidget . isMoving ) . toBe ( true ) ;
88+ } ) ;
89+
90+ test ( "speedUp() should increase BPM by 10%" , ( ) => {
91+ // 100 + 10% = 110
92+ tempoWidget . speedUp ( 0 ) ;
93+ expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 110 ) ;
94+ expect ( tempoWidget . BPMInputs [ 0 ] . value ) . toBe ( 110 ) ;
95+ } ) ;
96+
97+ test ( "slowDown() should decrease BPM by 10%" , ( ) => {
98+ // 100 - 10% = 90
99+ tempoWidget . slowDown ( 0 ) ;
100+ expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 90 ) ;
101+ expect ( tempoWidget . BPMInputs [ 0 ] . value ) . toBe ( 90 ) ;
102+ } ) ;
103+
104+ test ( "should not exceed maximum BPM of 1000" , ( ) => {
105+ tempoWidget . BPMs [ 0 ] = 950 ;
106+ // 950 + 95 = 1045 -> Should clamp to 1000
107+ tempoWidget . speedUp ( 0 ) ;
108+
109+ expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 1000 ) ;
110+ expect ( mockActivity . errorMsg ) . toHaveBeenCalled ( ) ;
111+ } ) ;
112+
113+ test ( "should not go below minimum BPM of 30" , ( ) => {
114+ tempoWidget . BPMs [ 0 ] = 32 ;
115+ // 32 - 3.2 = 28.8 -> Should clamp to 30
116+ tempoWidget . slowDown ( 0 ) ;
117+
118+ expect ( tempoWidget . BPMs [ 0 ] ) . toBe ( 30 ) ;
119+ expect ( mockActivity . errorMsg ) . toHaveBeenCalled ( ) ;
120+ } ) ;
121+ } ) ;
0 commit comments