Skip to content

Commit b9494a5

Browse files
alokdevxalokdangre
andauthored
added test for ornamentblocks (#4555)
Co-authored-by: Studycode001 <[email protected]>
1 parent fb53e60 commit b9494a5

File tree

2 files changed

+316
-0
lines changed

2 files changed

+316
-0
lines changed

js/blocks/OrnamentBlocks.js

+4
Original file line numberDiff line numberDiff line change
@@ -744,3 +744,7 @@ function setupOrnamentBlocks(activity) {
744744
new NewSlurBlock().setup(activity);
745745
new NewStaccatoBlock().setup(activity);
746746
}
747+
748+
if (typeof module !== "undefined" && module.exports) {
749+
module.exports = { setupOrnamentBlocks };
750+
}
+312
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
/**
2+
* MusicBlocks v3.6.2
3+
*
4+
* @author Alok Dangre
5+
*
6+
* @copyright 2025 Alok Dangre
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 { setupOrnamentBlocks } = require("../OrnamentBlocks");
24+
25+
describe("setupOrnamentBlocks", () => {
26+
let activity, logo, createdBlocks;
27+
28+
class DummyValueBlock {
29+
constructor(name, displayName) {
30+
this.name = name;
31+
this.displayName = displayName;
32+
createdBlocks[name] = this;
33+
}
34+
35+
setPalette(palette, activity) {
36+
this.palette = palette;
37+
return this;
38+
}
39+
40+
beginnerBlock(flag) {
41+
this.isBeginner = flag;
42+
return this;
43+
}
44+
45+
setHelpString(helpArray) {
46+
this.help = helpArray;
47+
return this;
48+
}
49+
50+
formBlock(params) {
51+
this.blockParams = params;
52+
return this;
53+
}
54+
55+
makeMacro(macroFunc) {
56+
this.macro = macroFunc;
57+
return this;
58+
}
59+
60+
setup(activity) {
61+
return this;
62+
}
63+
}
64+
65+
class DummyFlowClampBlock extends DummyValueBlock {
66+
}
67+
68+
beforeEach(() => {
69+
createdBlocks = {};
70+
71+
global._ = jest.fn((str) => str);
72+
global.ValueBlock = DummyValueBlock;
73+
global.FlowClampBlock = DummyFlowClampBlock;
74+
global.NOINPUTERRORMSG = "No input provided";
75+
global.NANERRORMSG = "Not a number";
76+
global.last = (arr) => arr[arr.length - 1];
77+
78+
global.Singer = {
79+
OrnamentActions: {
80+
doNeighbor: jest.fn(),
81+
setSlur: jest.fn(),
82+
setStaccato: jest.fn()
83+
},
84+
noteCounter: jest.fn((logo, turtle, val) => val)
85+
};
86+
87+
activity = {
88+
errorMsg: jest.fn(),
89+
textMsg: jest.fn(),
90+
blocks: { blockList: {} }
91+
};
92+
93+
activity.turtles = {
94+
turtleObjs: {},
95+
ithTurtle(turtle) {
96+
if (!this.turtleObjs[turtle]) {
97+
this.turtleObjs[turtle] = {
98+
singer: { staccato: [], glide: [], justCounting: [] }
99+
};
100+
}
101+
return this.turtleObjs[turtle];
102+
}
103+
};
104+
105+
logo = {
106+
inStatusMatrix: false,
107+
statusFields: [],
108+
stopTurtle: false,
109+
setDispatchBlock: jest.fn(),
110+
setTurtleListener: jest.fn(),
111+
notation: {
112+
notationBeginSlur: jest.fn(),
113+
notationEndSlur: jest.fn()
114+
}
115+
};
116+
117+
setupOrnamentBlocks(activity);
118+
});
119+
120+
describe("StaccatoFactorBlock", () => {
121+
it("should push \"staccato\" status when in status matrix", () => {
122+
const blk = "blkSF1";
123+
activity.blocks.blockList[blk] = { connections: [10] };
124+
activity.blocks.blockList[10] = { name: "print" };
125+
logo.inStatusMatrix = true;
126+
const staccatoFactorBlock = createdBlocks["staccatofactor"];
127+
staccatoFactorBlock.arg(logo, 0, blk);
128+
expect(logo.statusFields).toContainEqual([blk, "staccato"]);
129+
});
130+
131+
it("should return the last staccato factor if available", () => {
132+
logo.inStatusMatrix = false;
133+
const turtleObj = activity.turtles.ithTurtle(0);
134+
turtleObj.singer.staccato = [5, 10, 15];
135+
const staccatoFactorBlock = createdBlocks["staccatofactor"];
136+
const result = staccatoFactorBlock.arg(logo, 0, "blkSF2");
137+
expect(result).toEqual(15);
138+
});
139+
140+
it("should return 0 if no staccato factors exist", () => {
141+
logo.inStatusMatrix = false;
142+
const turtleObj = activity.turtles.ithTurtle(0);
143+
turtleObj.singer.staccato = [];
144+
const staccatoFactorBlock = createdBlocks["staccatofactor"];
145+
const result = staccatoFactorBlock.arg(logo, 0, "blkSF3");
146+
expect(result).toEqual(0);
147+
});
148+
149+
it("updateParameter should return the block value from activity", () => {
150+
const blk = "blkSF4";
151+
activity.blocks.blockList[blk] = { value: 42 };
152+
const staccatoFactorBlock = createdBlocks["staccatofactor"];
153+
const result = staccatoFactorBlock.updateParameter(logo, 0, blk);
154+
expect(result).toEqual(42);
155+
});
156+
});
157+
158+
describe("SlurFactorBlock", () => {
159+
it("should push \"slur\" status when in status matrix", () => {
160+
const blk = "blkSLF1";
161+
activity.blocks.blockList[blk] = { connections: [20] };
162+
activity.blocks.blockList[20] = { name: "print" };
163+
logo.inStatusMatrix = true;
164+
const slurFactorBlock = createdBlocks["slurfactor"];
165+
slurFactorBlock.arg(logo, 0, blk);
166+
expect(logo.statusFields).toContainEqual([blk, "slur"]);
167+
});
168+
169+
it("should return negative of the last staccato factor if available", () => {
170+
logo.inStatusMatrix = false;
171+
const turtleObj = activity.turtles.ithTurtle(0);
172+
turtleObj.singer.staccato = [3, 6, 9];
173+
const slurFactorBlock = createdBlocks["slurfactor"];
174+
const result = slurFactorBlock.arg(logo, 0, "blkSLF2");
175+
expect(result).toEqual(-9);
176+
});
177+
178+
it("should return 0 if no staccato factors exist", () => {
179+
logo.inStatusMatrix = false;
180+
const turtleObj = activity.turtles.ithTurtle(0);
181+
turtleObj.singer.staccato = [];
182+
const slurFactorBlock = createdBlocks["slurfactor"];
183+
const result = slurFactorBlock.arg(logo, 0, "blkSLF3");
184+
expect(result).toEqual(0);
185+
});
186+
187+
it("updateParameter should return the block value from activity", () => {
188+
const blk = "blkSLF4";
189+
activity.blocks.blockList[blk] = { value: 55 };
190+
const slurFactorBlock = createdBlocks["slurfactor"];
191+
const result = slurFactorBlock.updateParameter(logo, 0, blk);
192+
expect(result).toEqual(55);
193+
});
194+
});
195+
196+
describe("NeighborBlock", () => {
197+
it("should call doNeighbor and return expected array", () => {
198+
const neighborBlock = createdBlocks["neighbor"];
199+
const result = neighborBlock.flow([2, 0.5, 99], logo, 0, "blkNeighbor");
200+
expect(Singer.OrnamentActions.doNeighbor).toHaveBeenCalledWith(2, 0.5, 0, "blkNeighbor");
201+
expect(result).toEqual([99, 1]);
202+
});
203+
204+
it("should call errorMsg if arguments are not numbers", () => {
205+
const neighborBlock = createdBlocks["neighbor"];
206+
neighborBlock.flow(["a", 0.5, 99], logo, 0, "blkNeighbor");
207+
expect(activity.errorMsg).toHaveBeenCalledWith(NANERRORMSG, "blkNeighbor");
208+
expect(logo.stopTurtle).toBe(true);
209+
});
210+
});
211+
212+
describe("Neighbor2Block", () => {
213+
it("should inherit flow behavior from NeighborBlock", () => {
214+
const neighbor2Block = createdBlocks["neighbor2"];
215+
const result = neighbor2Block.flow([3, 0.25, 88], logo, 0, "blkNeighbor2");
216+
expect(Singer.OrnamentActions.doNeighbor).toHaveBeenCalledWith(3, 0.25, 0, "blkNeighbor2");
217+
expect(result).toEqual([88, 1]);
218+
});
219+
});
220+
221+
describe("GlideBlock", () => {
222+
it("should return undefined if second argument is undefined", () => {
223+
const glideBlock = createdBlocks["glide"];
224+
const result = glideBlock.flow([1 / 16], logo, 0, "blkGlide");
225+
expect(result).toBeUndefined();
226+
});
227+
228+
it("should process glide correctly with valid arguments", () => {
229+
const glideBlock = createdBlocks["glide"];
230+
const turtleIndex = 0;
231+
const turtleObj = activity.turtles.ithTurtle(turtleIndex);
232+
turtleObj.singer.glide = [];
233+
const result = glideBlock.flow([0.1, 8], logo, turtleIndex, "blkGlide");
234+
expect(turtleObj.singer.glide).toContain(0.1);
235+
expect(Singer.noteCounter).toHaveBeenCalledWith(logo, turtleIndex, 8);
236+
expect(logo.notation.notationBeginSlur).toHaveBeenCalledWith(turtleIndex);
237+
expect(logo.setDispatchBlock).toHaveBeenCalledWith(
238+
"blkGlide",
239+
turtleIndex,
240+
expect.stringMatching(/^_glide_/)
241+
);
242+
expect(logo.setTurtleListener).toHaveBeenCalled();
243+
expect(result).toEqual([8, 1]);
244+
});
245+
246+
it("should default glide argument and call errorMsg if first argument is invalid", () => {
247+
const glideBlock = createdBlocks["glide"];
248+
const turtleIndex = 0;
249+
const result = glideBlock.flow([null, 8], logo, turtleIndex, "blkGlide");
250+
expect(activity.errorMsg).toHaveBeenCalledWith(NOINPUTERRORMSG, "blkGlide");
251+
expect(result).toEqual([8, 1]);
252+
});
253+
});
254+
255+
describe("SlurBlock", () => {
256+
it("should return undefined if second argument is undefined", () => {
257+
const slurBlock = createdBlocks["slur"];
258+
const result = slurBlock.flow([1 / 16], logo, 0, "blkSlur");
259+
expect(result).toBeUndefined();
260+
});
261+
262+
it("should call setSlur with correct arguments", () => {
263+
const slurBlock = createdBlocks["slur"];
264+
slurBlock.flow([0.2, 7], logo, 0, "blkSlur");
265+
expect(Singer.OrnamentActions.setSlur).toHaveBeenCalledWith(0.2, 0, "blkSlur");
266+
});
267+
268+
it("should default slur argument and call errorMsg if first argument is invalid", () => {
269+
const slurBlock = createdBlocks["slur"];
270+
slurBlock.flow([null, 7], logo, 0, "blkSlur");
271+
expect(activity.errorMsg).toHaveBeenCalledWith(NOINPUTERRORMSG, "blkSlur");
272+
});
273+
});
274+
275+
describe("StaccatoBlock", () => {
276+
it("should return undefined if second argument is undefined", () => {
277+
const staccatoBlock = createdBlocks["staccato"];
278+
const result = staccatoBlock.flow([1 / 32], logo, 0, "blkStaccato");
279+
expect(result).toBeUndefined();
280+
});
281+
282+
it("should call setStaccato with correct arguments", () => {
283+
const staccatoBlock = createdBlocks["staccato"];
284+
staccatoBlock.flow([0.05, 5], logo, 0, "blkStaccato");
285+
expect(Singer.OrnamentActions.setStaccato).toHaveBeenCalledWith(0.05, 0, "blkStaccato");
286+
});
287+
288+
it("should default staccato argument and call errorMsg if first argument is invalid", () => {
289+
const staccatoBlock = createdBlocks["staccato"];
290+
staccatoBlock.flow([null, 5], logo, 0, "blkStaccato");
291+
expect(activity.errorMsg).toHaveBeenCalledWith(NOINPUTERRORMSG, "blkStaccato");
292+
});
293+
});
294+
295+
describe("NewSlurBlock", () => {
296+
it("should not be hidden and behave like SlurBlock", () => {
297+
const newSlurBlock = createdBlocks["newslur"];
298+
expect(newSlurBlock.hidden).toBe(false);
299+
newSlurBlock.flow([0.15, 10], logo, 0, "blkNewSlur");
300+
expect(Singer.OrnamentActions.setSlur).toHaveBeenCalledWith(0.15, 0, "blkNewSlur");
301+
});
302+
});
303+
304+
describe("NewStaccatoBlock", () => {
305+
it("should not be hidden and behave like StaccatoBlock", () => {
306+
const newStaccatoBlock = createdBlocks["newstaccato"];
307+
expect(newStaccatoBlock.hidden).toBe(false);
308+
newStaccatoBlock.flow([0.08, 12], logo, 0, "blkNewStaccato");
309+
expect(Singer.OrnamentActions.setStaccato).toHaveBeenCalledWith(0.08, 0, "blkNewStaccato");
310+
});
311+
});
312+
});

0 commit comments

Comments
 (0)