Skip to content

Commit e187eaa

Browse files
committed
test: add unit tests for tempo.js
1 parent e4219ba commit e187eaa

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

js/widgets/tempo.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,3 +438,6 @@ class Tempo {
438438
return this._save_lock;
439439
}
440440
}
441+
if (typeof module !== "undefined") {
442+
module.exports = Tempo;
443+
}

js/widgets/tempo.test.js

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,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

Comments
 (0)