diff --git a/js/js-export/API/DictBlocksAPI.js b/js/js-export/API/DictBlocksAPI.js index 760d2cddff..899c83b098 100644 --- a/js/js-export/API/DictBlocksAPI.js +++ b/js/js-export/API/DictBlocksAPI.js @@ -51,3 +51,5 @@ class DictBlocksAPI { return this.runCommand("getValue", [args[1], args[0], this.turIndex]); } } + +module.exports = DictBlocksAPI; \ No newline at end of file diff --git a/js/js-export/API/IntervalsBlocksAPI.js b/js/js-export/API/IntervalsBlocksAPI.js index 8956023b5e..9ad3d719da 100644 --- a/js/js-export/API/IntervalsBlocksAPI.js +++ b/js/js-export/API/IntervalsBlocksAPI.js @@ -58,3 +58,4 @@ class IntervalsBlocksAPI { return this.runCommand("setTemperament", [args[0], args[1], args[2]]); } } +module.exports = IntervalsBlocksAPI; diff --git a/js/js-export/API/MeterBlocksAPI.js b/js/js-export/API/MeterBlocksAPI.js index 3a74993147..66ce847982 100644 --- a/js/js-export/API/MeterBlocksAPI.js +++ b/js/js-export/API/MeterBlocksAPI.js @@ -74,3 +74,6 @@ class MeterBlocksAPI { return Singer.MeterActions.getNotesPlayed(args[0], this.turIndex); } } + + +module.exports = MeterBlocksAPI; diff --git a/js/js-export/API/PenBlocksAPI.js b/js/js-export/API/PenBlocksAPI.js index 29685c75fb..b46cc8676f 100644 --- a/js/js-export/API/PenBlocksAPI.js +++ b/js/js-export/API/PenBlocksAPI.js @@ -99,3 +99,4 @@ class PenBlocksAPI { return this.runCommand("doSetFont", [args[0]]); } } +module.exports = PenBlocksAPI; diff --git a/js/js-export/API/PitchBlocksAPI.js b/js/js-export/API/PitchBlocksAPI.js index 93112bc2b8..ea786e2da4 100644 --- a/js/js-export/API/PitchBlocksAPI.js +++ b/js/js-export/API/PitchBlocksAPI.js @@ -179,3 +179,4 @@ class PitchBlocksAPI { return Singer.PitchActions.numToPitch(args[0], "octave", this.turIndex); } } +module.exports = PitchBlocksAPI; diff --git a/js/js-export/API/ToneBlocksAPI.js b/js/js-export/API/ToneBlocksAPI.js index 43b70417f0..f7fe4d420a 100644 --- a/js/js-export/API/ToneBlocksAPI.js +++ b/js/js-export/API/ToneBlocksAPI.js @@ -129,3 +129,4 @@ class ToneBlocksAPI { return this.ENDFLOWCOMMAND; } } +module.exports = ToneBlocksAPI; \ No newline at end of file diff --git a/js/js-export/API/__tests__/DictBlocksAPI.test.js b/js/js-export/API/__tests__/DictBlocksAPI.test.js new file mode 100644 index 0000000000..3daf3aee62 --- /dev/null +++ b/js/js-export/API/__tests__/DictBlocksAPI.test.js @@ -0,0 +1,100 @@ +const JSInterface = { + validateArgs: jest.fn(), +}; +global.JSInterface = JSInterface; + +global.globalActivity = { + turtles: { + ithTurtle: jest.fn(() => ({ name: "defaultDict" })), + }, +}; + +const DictBlocksAPI = require("../DictBlocksAPI"); + +describe("DictBlocksAPI", () => { + let dictBlocksAPI; + + beforeEach(() => { + dictBlocksAPI = new DictBlocksAPI(); + dictBlocksAPI.turIndex = 0; + dictBlocksAPI.runCommand = jest.fn(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + test("getDict calls runCommand with correct arguments", () => { + JSInterface.validateArgs.mockReturnValue(["mockDict"]); + + dictBlocksAPI.getDict("testDict"); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith("getDict", ["testDict"]); + expect(dictBlocksAPI.runCommand).toHaveBeenCalledWith("getDict", ["mockDict", 0]); + }); + + test("getDict uses default turIndex when no dict is provided", () => { + JSInterface.validateArgs.mockReturnValue(["defaultDict"]); + + dictBlocksAPI.getDict(); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith("getDict", [0]); + expect(dictBlocksAPI.runCommand).toHaveBeenCalledWith("getDict", ["defaultDict", 0]); + }); + + test("showDict calls runCommand with correct arguments", () => { + JSInterface.validateArgs.mockReturnValue(["mockDict"]); + + dictBlocksAPI.showDict("testDict"); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith("showDict", ["testDict"]); + expect(dictBlocksAPI.runCommand).toHaveBeenCalledWith("showDict", ["mockDict", 0]); + }); + + test("showDict uses default turIndex when no dict is provided", () => { + JSInterface.validateArgs.mockReturnValue(["defaultDict"]); + + dictBlocksAPI.showDict(); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith("showDict", [0]); + expect(dictBlocksAPI.runCommand).toHaveBeenCalledWith("showDict", ["defaultDict", 0]); + }); + + test("setValue calls runCommand with correct arguments", () => { + JSInterface.validateArgs.mockReturnValue(["key", "value", "testDict"]); + + dictBlocksAPI.setValue("key", "value", "testDict"); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith("setValue", ["key", "value", "testDict"]); + expect(dictBlocksAPI.runCommand).toHaveBeenCalledWith("setValue", ["testDict", "key", "value", 0]); + }); + + test("setValue uses default dict when no dict is provided", () => { + JSInterface.validateArgs.mockReturnValue(["key", "value", "defaultDict"]); + global.globalActivity.turtles.ithTurtle.mockReturnValue({ name: "defaultDict" }); + + dictBlocksAPI.setValue("key", "value"); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith("setValue", ["key", "value", "defaultDict"]); + expect(dictBlocksAPI.runCommand).toHaveBeenCalledWith("setValue", ["defaultDict", "key", "value", 0]); + }); + + test("getValue calls runCommand with correct arguments", () => { + JSInterface.validateArgs.mockReturnValue(["key", "testDict"]); + + dictBlocksAPI.getValue("key", "testDict"); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith("getValue", ["key", "testDict"]); + expect(dictBlocksAPI.runCommand).toHaveBeenCalledWith("getValue", ["testDict", "key", 0]); + }); + + test("getValue uses default dict when no dict is provided", () => { + JSInterface.validateArgs.mockReturnValue(["key", "defaultDict"]); + global.globalActivity.turtles.ithTurtle.mockReturnValue({ name: "defaultDict" }); + + dictBlocksAPI.getValue("key"); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith("getValue", ["key", "defaultDict"]); + expect(dictBlocksAPI.runCommand).toHaveBeenCalledWith("getValue", ["defaultDict", "key", 0]); + }); +}); diff --git a/js/js-export/API/__tests__/IntervalsBlocksAPI.test.js b/js/js-export/API/__tests__/IntervalsBlocksAPI.test.js new file mode 100644 index 0000000000..deafc1c899 --- /dev/null +++ b/js/js-export/API/__tests__/IntervalsBlocksAPI.test.js @@ -0,0 +1,60 @@ +const JSInterface = { + validateArgs: jest.fn(), +}; +global.JSInterface = JSInterface; +const IntervalsBlocksAPI = require('../IntervalsBlocksAPI'); + +const MusicBlocks = { BLK: 'mockedBlock' }; +global.MusicBlocks = MusicBlocks; + +describe('IntervalsBlocksAPI', () => { + let intervalsBlocksAPI; + + beforeEach(() => { + intervalsBlocksAPI = new IntervalsBlocksAPI(); + intervalsBlocksAPI.turIndex = 1; + intervalsBlocksAPI.ENDFLOWCOMMAND = 'end'; + intervalsBlocksAPI.runCommand = jest.fn(); + }); + + test('setKey calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue(['C', 'major']); + intervalsBlocksAPI.setKey('C', 'major'); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setKey', ['C', 'major']); + expect(intervalsBlocksAPI.runCommand).toHaveBeenCalledWith('setKey', ['C', 'major', 1]); + }); + + test('defineMode calls runCommand and awaits flow', async () => { + const flow = jest.fn(); + JSInterface.validateArgs.mockReturnValue(['dorian', flow]); + await intervalsBlocksAPI.defineMode('dorian', flow); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('defineMode', ['dorian', flow]); + expect(intervalsBlocksAPI.runCommand).toHaveBeenCalledWith('defineMode', ['dorian', 1, 'mockedBlock']); + expect(flow).toHaveBeenCalled(); + }); + + test('setScalarInterval calls runCommand and awaits flow', async () => { + const flow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([5, flow]); + await intervalsBlocksAPI.setScalarInterval(5, flow); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setScalarInterval', [5, flow]); + expect(intervalsBlocksAPI.runCommand).toHaveBeenCalledWith('setScalarInterval', [5, 1]); + expect(flow).toHaveBeenCalled(); + }); + + test('setSemitoneInterval calls runCommand and awaits flow', async () => { + const flow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([7, flow]); + await intervalsBlocksAPI.setSemitoneInterval(7, flow); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setSemitoneInterval', [7, flow]); + expect(intervalsBlocksAPI.runCommand).toHaveBeenCalledWith('setSemitoneInterval', [7, 1]); + expect(flow).toHaveBeenCalled(); + }); + + test('setTemperament calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue(['equal', 440, 4]); + intervalsBlocksAPI.setTemperament('equal', 440, 4); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setTemperament', ['equal', 440, 4]); + expect(intervalsBlocksAPI.runCommand).toHaveBeenCalledWith('setTemperament', ['equal', 440, 4]); + }); +}); diff --git a/js/js-export/API/__tests__/MeterBlocksAPI.test.js b/js/js-export/API/__tests__/MeterBlocksAPI.test.js new file mode 100644 index 0000000000..fa309abc77 --- /dev/null +++ b/js/js-export/API/__tests__/MeterBlocksAPI.test.js @@ -0,0 +1,88 @@ + +const JSInterface = { + validateArgs: jest.fn(), +}; +global.JSInterface = JSInterface; +const MeterBlocksAPI = require('../MeterBlocksAPI'); +const Singer = { MeterActions: { getNotesPlayed: jest.fn() } }; +global.Singer = Singer; + +describe('MeterBlocksAPI', () => { + let meterBlocksAPI; + + beforeEach(() => { + meterBlocksAPI = new MeterBlocksAPI(); + meterBlocksAPI.turIndex = 1; + meterBlocksAPI.ENDFLOWCOMMAND = 'end'; + meterBlocksAPI.runCommand = jest.fn(); + }); + + test('setMeter calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([4, 4]); + meterBlocksAPI.setMeter(4, 4); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setMeter', [4, 4]); + expect(meterBlocksAPI.runCommand).toHaveBeenCalledWith('setMeter', [4, 4, 1]); + }); + + test('setBPM calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([120, 4]); + meterBlocksAPI.setBPM(120, 4); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setBPM', [120, 4]); + expect(meterBlocksAPI.runCommand).toHaveBeenCalledWith('setBPM', [120, 4, 1]); + }); + + test('setMasterBPM calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([100, 4]); + meterBlocksAPI.setMasterBPM(100, 4); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setMasterBPM', [100, 4]); + expect(meterBlocksAPI.runCommand).toHaveBeenCalledWith('setMasterBPM', [100, 4]); + }); + + test('onEveryNoteDo calls runCommand with validated arguments', () => { + const action = jest.fn(); + JSInterface.validateArgs.mockReturnValue([action]); + meterBlocksAPI.onEveryNoteDo(action); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('onEveryNoteDo', [action]); + expect(meterBlocksAPI.runCommand).toHaveBeenCalledWith('onEveryNoteDo', [action, null, null, 1]); + }); + + test('onEveryBeatDo calls runCommand with validated arguments', () => { + const action = jest.fn(); + JSInterface.validateArgs.mockReturnValue([action]); + meterBlocksAPI.onEveryBeatDo(action); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('onEveryBeatDo', [action]); + expect(meterBlocksAPI.runCommand).toHaveBeenCalledWith('onEveryBeatDo', [action, null, null, 1]); + }); + + test('onStrongBeatDo calls runCommand with validated arguments', () => { + const action = jest.fn(); + JSInterface.validateArgs.mockReturnValue([2, action]); + meterBlocksAPI.onStrongBeatDo(2, action); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('onStrongBeatDo', [2, action]); + expect(meterBlocksAPI.runCommand).toHaveBeenCalledWith('onStrongBeatDo', [2, action, null, null, 1]); + }); + + test('onWeakBeatDo calls runCommand with validated arguments', () => { + const action = jest.fn(); + JSInterface.validateArgs.mockReturnValue([action]); + meterBlocksAPI.onWeakBeatDo(action); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('onWeakBeatDo', [action]); + expect(meterBlocksAPI.runCommand).toHaveBeenCalledWith('onWeakBeatDo', [action, null, null, 1]); + }); + + test('setNoClock calls runCommand and awaits flow', async () => { + const flow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([flow]); + await meterBlocksAPI.setNoClock(flow); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setNoClock', [flow]); + expect(meterBlocksAPI.runCommand).toHaveBeenCalledWith('setNoClock', [1]); + expect(flow).toHaveBeenCalled(); + }); + + test('getNotesPlayed calls Singer.MeterActions.getNotesPlayed with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([4]); + meterBlocksAPI.getNotesPlayed(4); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('getNotesPlayed', [4]); + expect(Singer.MeterActions.getNotesPlayed).toHaveBeenCalledWith(4, 1); + }); +}); diff --git a/js/js-export/API/__tests__/PenBlocksAPI.test.js b/js/js-export/API/__tests__/PenBlocksAPI.test.js new file mode 100644 index 0000000000..9fca7a9f21 --- /dev/null +++ b/js/js-export/API/__tests__/PenBlocksAPI.test.js @@ -0,0 +1,111 @@ +const JSInterface = { + validateArgs: jest.fn(), +}; +global.JSInterface = JSInterface; +const PenBlocksAPI = require('../PenBlocksAPI') +global.globalActivity = { + turtles: { + refreshCanvas: jest.fn(), + }, + logo: { + turtles: { + setBackgroundColor: jest.fn(), + }, + }, +}; + +describe('PenBlocksAPI', () => { + let penBlocksAPI; + + beforeEach(() => { + penBlocksAPI = new PenBlocksAPI(); + penBlocksAPI.turIndex = 1; + penBlocksAPI.ENDFLOW = 'end'; + penBlocksAPI.runCommand = jest.fn(); + }); + + test('setColor calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([50]); + penBlocksAPI.setColor(50); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setColor', [50]); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doSetColor', [50]); + }); + + test('setGrey calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([30]); + penBlocksAPI.setGrey(30); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setGrey', [30]); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doSetChroma', [30]); + }); + + test('setShade calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([70]); + penBlocksAPI.setShade(70); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setShade', [70]); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doSetValue', [70]); + }); + + test('setHue calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([120]); + penBlocksAPI.setHue(120); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setHue', [120]); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doSetHue', [120]); + }); + + test('setTranslucency calculates and calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([80]); + penBlocksAPI.setTranslucency(80); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setTranslucency', [80]); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doSetPenAlpha', [1.0 - 80 / 100]); + }); + + test('setPensize calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([10]); + penBlocksAPI.setPensize(10); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setPensize', [10]); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doSetPensize', [10]); + }); + + test('penUp calls runCommand', () => { + penBlocksAPI.penUp(); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doPenUp'); + }); + + test('penDown calls runCommand', () => { + penBlocksAPI.penDown(); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doPenDown'); + }); + + test('fillShape calls commands and refreshCanvas', async () => { + const flow = jest.fn(); + await penBlocksAPI.fillShape(flow); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doStartFill'); + expect(flow).toHaveBeenCalled(); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doEndFill'); + expect(global.globalActivity.turtles.refreshCanvas).toHaveBeenCalled(); + }); + + test('hollowLine calls commands and refreshCanvas', async () => { + const flow = jest.fn(); + await penBlocksAPI.hollowLine(flow); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doStartHollowLine'); + expect(flow).toHaveBeenCalled(); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doEndHollowLine'); + expect(global.globalActivity.turtles.refreshCanvas).toHaveBeenCalled(); + }); + + test('fillBackground calls setBackgroundColor', () => { + penBlocksAPI.fillBackground(); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('_anonymous', expect.any(Function)); + const setBackgroundFunc = penBlocksAPI.runCommand.mock.calls[0][1]; + setBackgroundFunc(); + expect(global.globalActivity.logo.turtles.setBackgroundColor).toHaveBeenCalledWith(1); + }); + + test('setFont calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue(['Arial']); + penBlocksAPI.setFont('Arial'); + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setFont', ['Arial']); + expect(penBlocksAPI.runCommand).toHaveBeenCalledWith('doSetFont', ['Arial']); + }); +}); diff --git a/js/js-export/API/__tests__/PitchBlocksAPI.test.js b/js/js-export/API/__tests__/PitchBlocksAPI.test.js new file mode 100644 index 0000000000..89d413ca12 --- /dev/null +++ b/js/js-export/API/__tests__/PitchBlocksAPI.test.js @@ -0,0 +1,156 @@ +const JSInterface = { + validateArgs: jest.fn(), +}; +global.JSInterface = JSInterface; +const PitchBlocksAPI = require('../PitchBlocksAPI'); + +global.MusicBlocks = { BLK: 'mockBlock' }; +global.Singer = { + PitchActions: { + numToPitch: jest.fn() + } +}; + +describe('PitchBlocksAPI', () => { + let pitchBlocksAPI; + + beforeEach(() => { + pitchBlocksAPI = new PitchBlocksAPI(); + pitchBlocksAPI.turIndex = 1; + pitchBlocksAPI.ENDFLOWCOMMAND = 'end'; + pitchBlocksAPI.runCommand = jest.fn(); + }); + + test('playPitch calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue(['C', 4]); + + pitchBlocksAPI.playPitch('C', 4); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('playPitch', ['C', 4]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('playPitch', ['C', 4, 0, 1, MusicBlocks.BLK]); + }); + + test('stepPitch calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([2]); + + pitchBlocksAPI.stepPitch(2); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('stepPitch', [2]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('stepPitch', [2, 1]); + }); + + test('playNthModalPitch calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([3, 5]); + + pitchBlocksAPI.playNthModalPitch(3, 5); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('playNthModalPitch', [3, 5]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('playNthModalPitch', [3, 5, 1, MusicBlocks.BLK]); + }); + + test('playPitchNumber calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([7]); + + pitchBlocksAPI.playPitchNumber(7); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('playPitchNumber', [7]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('playPitchNumber', [7, 1, MusicBlocks.BLK]); + }); + + test('playHertz calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([440]); + + pitchBlocksAPI.playHertz(440); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('playHertz', [440]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('playHertz', [440, 1]); + }); + + test('setAccidental calls runCommand with validated arguments and executes flow', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue(['sharp', mockFlow]); + + const result = await pitchBlocksAPI.setAccidental('sharp', mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setAccidental', ['sharp', mockFlow]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('setAccidental', ['sharp', 1, MusicBlocks.BLK]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(pitchBlocksAPI.ENDFLOWCOMMAND); + }); + + test('setScalarTranspose calls runCommand with validated arguments and executes flow', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([2, mockFlow]); + + const result = await pitchBlocksAPI.setScalarTranspose(2, mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setScalarTranspose', [2, mockFlow]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('setScalarTranspose', [2, 1]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(pitchBlocksAPI.ENDFLOWCOMMAND); + }); + + test('setSemitoneTranspose calls runCommand with validated arguments and executes flow', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([1, mockFlow]); + + const result = await pitchBlocksAPI.setSemitoneTranspose(1, mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setSemitoneTranspose', [1, mockFlow]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('setSemitoneTranspose', [1, 1]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(pitchBlocksAPI.ENDFLOWCOMMAND); + }); + + test('setRegister calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([3]); + + pitchBlocksAPI.setRegister(3); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setRegister', [3]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('setRegister', [3, 1]); + }); + + test('invert calls runCommand with validated arguments and executes flow', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue(['inversionName', 4, 'mode', mockFlow]); + + const result = await pitchBlocksAPI.invert('inversionName', 4, 'mode', mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('invert', ['inversionName', 4, 'mode', mockFlow]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('invert', ['inversionName', 4, 'mode', 1]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(pitchBlocksAPI.ENDFLOWCOMMAND); + }); + + test('setPitchNumberOffset calls runCommand with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([7, 3]); + + pitchBlocksAPI.setPitchNumberOffset(7, 3); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setPitchNumberOffset', [7, 3]); + expect(pitchBlocksAPI.runCommand).toHaveBeenCalledWith('setPitchNumberOffset', [7, 3, 1]); + }); + + test('numToPitch calls numToPitch in Singer.PitchActions with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([5]); + Singer.PitchActions.numToPitch.mockReturnValue('C'); + + const result = pitchBlocksAPI.numToPitch(5); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('numToPitch', [5]); + expect(Singer.PitchActions.numToPitch).toHaveBeenCalledWith(5, 'pitch', 1); + expect(result).toBe('C'); + }); + + test('numToOctave calls numToPitch in Singer.PitchActions with validated arguments', () => { + JSInterface.validateArgs.mockReturnValue([5]); + Singer.PitchActions.numToPitch.mockReturnValue(4); + + const result = pitchBlocksAPI.numToOctave(5); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('numToOctave', [5]); + expect(Singer.PitchActions.numToPitch).toHaveBeenCalledWith(5, 'octave', 1); + expect(result).toBe(4); + }); +}); diff --git a/js/js-export/API/__tests__/ToneBlocksAPI.test.js b/js/js-export/API/__tests__/ToneBlocksAPI.test.js new file mode 100644 index 0000000000..ca6564aca4 --- /dev/null +++ b/js/js-export/API/__tests__/ToneBlocksAPI.test.js @@ -0,0 +1,101 @@ +const JSInterface = { + validateArgs: jest.fn(), +}; +global.JSInterface = JSInterface; +const ToneBlocksAPI = require('../ToneBlocksAPI'); +global.MusicBlocks = { BLK: 'mockBlock' }; + +describe('ToneBlocksAPI', () => { + let toneBlocksAPI; + + beforeEach(() => { + toneBlocksAPI = new ToneBlocksAPI(); + toneBlocksAPI.turIndex = 1; + toneBlocksAPI.ENDFLOWCOMMAND = 'end'; + toneBlocksAPI.runCommand = jest.fn(); + }); + + test('setInstrument calls runCommand with validated arguments', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue(['piano', mockFlow]); + + const result = await toneBlocksAPI.setInstrument('piano', mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('setInstrument', ['piano', mockFlow]); + expect(toneBlocksAPI.runCommand).toHaveBeenCalledWith('setTimbre', ['piano', toneBlocksAPI.turIndex]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(toneBlocksAPI.ENDFLOWCOMMAND); + }); + + test('doVibrato calls runCommand with validated arguments', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([0.5, 2, mockFlow]); + + const result = await toneBlocksAPI.doVibrato(0.5, 2, mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('doVibrato', [0.5, 2, mockFlow]); + expect(toneBlocksAPI.runCommand).toHaveBeenCalledWith('doVibrato', [0.5, 2, toneBlocksAPI.turIndex]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(toneBlocksAPI.ENDFLOWCOMMAND); + }); + + test('doChorus calls runCommand with validated arguments', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([1.2, 0.3, 0.7, mockFlow]); + + const result = await toneBlocksAPI.doChorus(1.2, 0.3, 0.7, mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('doChorus', [1.2, 0.3, 0.7, mockFlow]); + expect(toneBlocksAPI.runCommand).toHaveBeenCalledWith('doChorus', [1.2, 0.3, 0.7, toneBlocksAPI.turIndex]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(toneBlocksAPI.ENDFLOWCOMMAND); + }); + + test('doPhaser calls runCommand with validated arguments', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([0.8, 2, 440, mockFlow]); + + const result = await toneBlocksAPI.doPhaser(0.8, 2, 440, mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('doPhaser', [0.8, 2, 440, mockFlow]); + expect(toneBlocksAPI.runCommand).toHaveBeenCalledWith('doPhaser', [0.8, 2, 440, toneBlocksAPI.turIndex]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(toneBlocksAPI.ENDFLOWCOMMAND); + }); + + test('doTremolo calls runCommand with validated arguments', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([4, 0.5, mockFlow]); + + const result = await toneBlocksAPI.doTremolo(4, 0.5, mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('doTremolo', [4, 0.5, mockFlow]); + expect(toneBlocksAPI.runCommand).toHaveBeenCalledWith('doTremolo', [4, 0.5, toneBlocksAPI.turIndex]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(toneBlocksAPI.ENDFLOWCOMMAND); + }); + + test('doDistortion calls runCommand with validated arguments', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([0.8, mockFlow]); + + const result = await toneBlocksAPI.doDistortion(0.8, mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('doDistortion', [0.8, mockFlow]); + expect(toneBlocksAPI.runCommand).toHaveBeenCalledWith('doDistortion', [0.8, toneBlocksAPI.turIndex]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(toneBlocksAPI.ENDFLOWCOMMAND); + }); + + test('doHarmonic calls runCommand with validated arguments', async () => { + const mockFlow = jest.fn(); + JSInterface.validateArgs.mockReturnValue([3, mockFlow]); + + const result = await toneBlocksAPI.doHarmonic(3, mockFlow); + + expect(JSInterface.validateArgs).toHaveBeenCalledWith('doHarmonic', [3, mockFlow]); + expect(toneBlocksAPI.runCommand).toHaveBeenCalledWith('doHarmonic', [3, toneBlocksAPI.turIndex, MusicBlocks.BLK]); + expect(mockFlow).toHaveBeenCalled(); + expect(result).toBe(toneBlocksAPI.ENDFLOWCOMMAND); + }); +});