Skip to content

Commit db464a7

Browse files
authored
Merge pull request #5698 from lakshay776/issue-5135-arpeggio
Add unit tests for arpeggio widget (#5135)
2 parents 133f360 + 7991846 commit db464a7

File tree

2 files changed

+178
-0
lines changed

2 files changed

+178
-0
lines changed
Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/**
2+
* MusicBlocks v3.6.2
3+
*
4+
* @author Music Blocks Contributors
5+
*
6+
* @copyright 2026 Music Blocks Contributors
7+
*
8+
* @license
9+
* This program is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License as published by
11+
* the Free Software Foundation, either version 3 of the License, or
12+
* (at your option) any later version.
13+
*
14+
* This program is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+
* GNU Affero General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Affero General Public License
20+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
21+
*/
22+
23+
const Arpeggio = require("../arpeggio.js");
24+
25+
// --- Global Mocks ---
26+
global._ = msg => msg;
27+
global.platformColor = {
28+
labelColor: "#90c100",
29+
selectorBackground: "#f0f0f0",
30+
selectorBackgroundHOVER: "#e0e0e0"
31+
};
32+
global.docById = jest.fn(() => ({
33+
style: {},
34+
innerHTML: "",
35+
insertRow: jest.fn(() => ({
36+
insertCell: jest.fn(() => ({
37+
style: {},
38+
innerHTML: "",
39+
setAttribute: jest.fn(),
40+
addEventListener: jest.fn()
41+
})),
42+
setAttribute: jest.fn(),
43+
style: {}
44+
})),
45+
appendChild: jest.fn(),
46+
setAttribute: jest.fn()
47+
}));
48+
global.getNote = jest.fn(() => ["C", "", 4]);
49+
global.setCustomChord = jest.fn();
50+
global.keySignatureToMode = jest.fn(() => ["C", "major"]);
51+
global.getModeNumbers = jest.fn(() => "0 2 4 5 7 9 11");
52+
global.getTemperament = jest.fn(() => ({ pitchNumber: 12 }));
53+
54+
global.window = {
55+
innerWidth: 1200,
56+
widgetWindows: {
57+
windowFor: jest.fn().mockReturnValue({
58+
clear: jest.fn(),
59+
show: jest.fn(),
60+
addButton: jest.fn().mockReturnValue({ onclick: null }),
61+
getWidgetBody: jest.fn().mockReturnValue({
62+
append: jest.fn(),
63+
appendChild: jest.fn(),
64+
style: {}
65+
}),
66+
sendToCenter: jest.fn(),
67+
onclose: null,
68+
onmaximize: null,
69+
destroy: jest.fn()
70+
})
71+
}
72+
};
73+
74+
global.document = {
75+
createElement: jest.fn(() => ({
76+
style: {},
77+
innerHTML: "",
78+
appendChild: jest.fn(),
79+
append: jest.fn(),
80+
setAttribute: jest.fn(),
81+
addEventListener: jest.fn()
82+
}))
83+
};
84+
85+
describe("Arpeggio Widget", () => {
86+
let arpeggio;
87+
88+
beforeEach(() => {
89+
arpeggio = new Arpeggio();
90+
});
91+
92+
afterEach(() => {
93+
jest.clearAllMocks();
94+
});
95+
96+
// --- Constructor Tests ---
97+
describe("constructor", () => {
98+
test("should initialize with empty notesToPlay", () => {
99+
expect(arpeggio.notesToPlay).toEqual([]);
100+
});
101+
102+
test("should initialize with empty _blockMap", () => {
103+
expect(arpeggio._blockMap).toEqual([]);
104+
});
105+
106+
test("should initialize defaultCols to DEFAULTCOLS", () => {
107+
expect(arpeggio.defaultCols).toBe(Arpeggio.DEFAULTCOLS);
108+
expect(arpeggio.defaultCols).toBe(4);
109+
});
110+
});
111+
112+
// --- Static Constants Tests ---
113+
describe("static constants", () => {
114+
test("should have correct BUTTONDIVWIDTH", () => {
115+
expect(Arpeggio.BUTTONDIVWIDTH).toBe(295);
116+
});
117+
118+
test("should have correct CELLSIZE", () => {
119+
expect(Arpeggio.CELLSIZE).toBe(28);
120+
});
121+
122+
test("should have correct BUTTONSIZE", () => {
123+
expect(Arpeggio.BUTTONSIZE).toBe(53);
124+
});
125+
126+
test("should have correct ICONSIZE", () => {
127+
expect(Arpeggio.ICONSIZE).toBe(32);
128+
});
129+
130+
test("should have correct DEFAULTCOLS", () => {
131+
expect(Arpeggio.DEFAULTCOLS).toBe(4);
132+
});
133+
});
134+
135+
// --- Data Management Tests ---
136+
describe("data management", () => {
137+
test("should store notes to play", () => {
138+
arpeggio.notesToPlay.push(["C", 4]);
139+
arpeggio.notesToPlay.push(["E", 4]);
140+
arpeggio.notesToPlay.push(["G", 4]);
141+
expect(arpeggio.notesToPlay).toHaveLength(3);
142+
});
143+
144+
test("should store block map pairs", () => {
145+
arpeggio._blockMap.push([0, 1]);
146+
arpeggio._blockMap.push([3, 2]);
147+
expect(arpeggio._blockMap).toHaveLength(2);
148+
expect(arpeggio._blockMap[0]).toEqual([0, 1]);
149+
});
150+
151+
test("should allow clearing block map", () => {
152+
arpeggio._blockMap.push([0, 1]);
153+
arpeggio._blockMap = [];
154+
expect(arpeggio._blockMap).toHaveLength(0);
155+
});
156+
157+
test("should allow updating defaultCols", () => {
158+
arpeggio.defaultCols = 8;
159+
expect(arpeggio.defaultCols).toBe(8);
160+
});
161+
});
162+
163+
// --- Notes To Play Tests ---
164+
describe("notesToPlay", () => {
165+
test("should handle empty notesToPlay", () => {
166+
expect(arpeggio.notesToPlay).toEqual([]);
167+
expect(arpeggio.notesToPlay.length).toBe(0);
168+
});
169+
170+
test("should allow complex note entries", () => {
171+
arpeggio.notesToPlay.push(["sol", 4, "electronic synth"]);
172+
expect(arpeggio.notesToPlay[0]).toEqual(["sol", 4, "electronic synth"]);
173+
});
174+
});
175+
});

js/widgets/arpeggio.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -817,3 +817,6 @@ class Arpeggio {
817817
this._activity.blocks.loadNewBlocks(newStack);
818818
}
819819
}
820+
if (typeof module !== "undefined") {
821+
module.exports = Arpeggio;
822+
}

0 commit comments

Comments
 (0)