Skip to content

Commit

Permalink
Merge pull request #42642 from code-dot-org/ben/javalab/playground/pl…
Browse files Browse the repository at this point in the history
…ay-sound

Javalab: play sound in Playground when signal received
  • Loading branch information
bencodeorg committed Sep 24, 2021
2 parents bf833d4 + 9ed8c5c commit eacc919
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 18 deletions.
1 change: 1 addition & 0 deletions apps/src/javalab/Javalab.js
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ Javalab.prototype.onRun = function() {

// Called by the Javalab app when it wants to stop student code execution
Javalab.prototype.onStop = function() {
this.miniApp?.onStop?.();
this.javabuilderConnection.closeConnection();
};

Expand Down
1 change: 0 additions & 1 deletion apps/src/javalab/Neighborhood.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ export default class Neighborhood {
this.controller.reset(false, false);
}

// TODO: Call this function when we enable stopping a program
onStop() {
timeoutList.clearTimeouts();
this.resetSignalQueue();
Expand Down
45 changes: 37 additions & 8 deletions apps/src/javalab/Playground.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ export default class Playground {
);
}

onStop() {
this.endGame();
this.resetAudioElement();
}

onStarterAssetsReceived = result => {
const response = JSON.parse(result.response);
response.starter_assets.forEach(asset => {
Expand All @@ -49,8 +54,7 @@ export default class Playground {
this.isGameRunning = true;
break;
case PlaygroundSignalType.EXIT:
this.isGameRunning = false;
this.isGameOver = true;
this.endGame();
break;
case PlaygroundSignalType.ADD_CLICKABLE_ITEM:
this.addClickableItem(data.detail);
Expand Down Expand Up @@ -116,6 +120,8 @@ export default class Playground {
// can't play sound if game is over
return;
}

this.setMediaElement(this.getAudioElement(), soundData.filename);
}

setBackgroundImage(backgroundData) {
Expand All @@ -125,19 +131,23 @@ export default class Playground {
}

const filename = backgroundData.filename;

const backgroundElement = this.getBackgroundElement();
backgroundElement.onerror = () => {
this.setMediaElement(backgroundElement, filename);
backgroundElement.style.opacity = 1.0;
}

setMediaElement(element, filename) {
element.onerror = () => {
this.onFileLoadError(filename);
};
backgroundElement.src = this.getUrl(filename);
backgroundElement.style.opacity = 1.0;
element.src = this.getUrl(filename);
}

reset() {
this.isGameOver = false;
this.isGameRunning = false;
this.resetBackgroundElement();
this.resetAudioElement();
}

// TODO: Call this from click handler on new clickable items
Expand All @@ -162,10 +172,29 @@ export default class Playground {
return document.getElementById('playground-background');
}

getAudioElement() {
return document.getElementById('playground-audio');
}

resetAudioElement() {
const audioElement = this.getAudioElement();
audioElement.pause();
this.resetMediaElement(audioElement);
}

resetBackgroundElement() {
const backgroundElement = this.getBackgroundElement();
backgroundElement.onerror = undefined;
backgroundElement.src = undefined;
backgroundElement.style.opacity = 0.0;
this.resetMediaElement(backgroundElement);
}

resetMediaElement(element) {
element.onerror = undefined;
element.src = '';
}

endGame() {
this.isGameRunning = false;
this.isGameOver = true;
}
}
124 changes: 115 additions & 9 deletions apps/test/unit/javalab/PlaygroundTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('Playground', () => {
};

let backgroundElement,
audioElement,
onOutputMessage,
onNewlineMessage,
onJavabuilderMessage,
Expand Down Expand Up @@ -44,6 +45,8 @@ describe('Playground', () => {
}
};

audioElement = {};

playground = new Playground(
onOutputMessage,
onNewlineMessage,
Expand All @@ -54,6 +57,7 @@ describe('Playground', () => {
);

playground.getBackgroundElement = () => backgroundElement;
playground.getAudioElement = () => audioElement;
});

it('sets background image when receiving a SET_BACKGROUND_IMAGE message for a starter asset', () => {
Expand All @@ -64,9 +68,8 @@ describe('Playground', () => {
}
};

expect(backgroundElement.src).to.be.undefined;
verifyDefaultMediaElementState(backgroundElement);
expect(backgroundElement.style.opacity).to.equal(0);
expect(backgroundElement.onerror).to.be.undefined;

playground.handleSignal(data);

Expand All @@ -79,6 +82,26 @@ describe('Playground', () => {
verifyOnFileLoadError(starterAsset1);
});

it('sets audio when receiving a PLAY_SOUND message for a starter asset', () => {
const data = {
value: PlaygroundSignalType.PLAY_SOUND,
detail: {
filename: starterAsset1
}
};

verifyDefaultMediaElementState(audioElement);

playground.handleSignal(data);

expect(audioElement.src).to.equal(`${levelName}/${starterAsset1}`);
expect(audioElement.onerror).to.exist;

// Verify onerror callback
audioElement.onerror();
verifyOnFileLoadError(starterAsset1);
});

it('sets background image when receiving a SET_BACKGROUND_IMAGE message for an uploaded asset', () => {
const assetFile = 'assetFile';
const data = {
Expand All @@ -88,9 +111,8 @@ describe('Playground', () => {
}
};

expect(backgroundElement.src).to.be.undefined;
verifyDefaultMediaElementState(backgroundElement);
expect(backgroundElement.style.opacity).to.equal(0);
expect(backgroundElement.onerror).to.be.undefined;

playground.handleSignal(data);

Expand All @@ -103,6 +125,27 @@ describe('Playground', () => {
verifyOnFileLoadError(assetFile);
});

it('sets audio when receiving a PLAY_SOUND message for an uploaded asset', () => {
const assetFile = 'assetFile';
const data = {
value: PlaygroundSignalType.PLAY_SOUND,
detail: {
filename: assetFile
}
};

verifyDefaultMediaElementState(audioElement);

playground.handleSignal(data);

expect(audioElement.src).to.equal(`assets/${assetFile}`);
expect(audioElement.onerror).to.exist;

// Verify onerror callback
audioElement.onerror();
verifyOnFileLoadError(assetFile);
});

it("doesn't set background image if game is over", () => {
const exitMessage = {
value: PlaygroundSignalType.EXIT
Expand All @@ -117,16 +160,36 @@ describe('Playground', () => {

playground.handleSignal(exitMessage);

expect(backgroundElement.src).to.be.undefined;
verifyDefaultMediaElementState(backgroundElement);
expect(backgroundElement.style.opacity).to.equal(0);
expect(backgroundElement.onerror).to.be.undefined;

playground.handleSignal(data);

// Background should not update
expect(backgroundElement.src).to.be.undefined;
verifyDefaultMediaElementState(backgroundElement);
expect(backgroundElement.style.opacity).to.equal(0);
expect(backgroundElement.onerror).to.be.undefined;
});

it("doesn't play sound if game is over", () => {
const exitMessage = {
value: PlaygroundSignalType.EXIT
};

const data = {
value: PlaygroundSignalType.PLAY_SOUND,
detail: {
filename: 'filename'
}
};

playground.handleSignal(exitMessage);

verifyDefaultMediaElementState(audioElement);

playground.handleSignal(data);

// Audio element should not update
verifyDefaultMediaElementState(audioElement);
});

it('resets the background image on reset()', () => {
Expand All @@ -145,11 +208,49 @@ describe('Playground', () => {

playground.reset();

expect(backgroundElement.src).to.be.undefined;
expect(backgroundElement.src).to.equal('');
expect(backgroundElement.style.opacity).to.equal(0);
expect(backgroundElement.onerror).to.be.undefined;
});

it('resets sound element on reset()', () => {
const data = {
value: PlaygroundSignalType.PLAY_SOUND,
detail: {
filename: 'filename'
}
};

playground.handleSignal(data);

expect(audioElement.src).to.exist;
expect(audioElement.onerror).to.exist;

playground.reset();

expect(audioElement.src).to.equal('');
expect(audioElement.onerror).to.be.undefined;
});

it('resets sound element when stopped', () => {
const data = {
value: PlaygroundSignalType.PLAY_SOUND,
detail: {
filename: 'filename'
}
};

playground.handleSignal(data);

expect(audioElement.src).to.exist;
expect(audioElement.onerror).to.exist;

playground.onStop();

expect(audioElement.src).to.equal('');
expect(audioElement.onerror).to.be.undefined;
});

function verifyOnFileLoadError(filename) {
sinon.assert.calledOnce(onOutputMessage);
sinon.assert.calledWith(
Expand All @@ -158,4 +259,9 @@ describe('Playground', () => {
);
sinon.assert.calledOnce(onNewlineMessage);
}

function verifyDefaultMediaElementState(element) {
expect(element.src).to.be.undefined;
expect(element.onerror).to.be.undefined;
}
});

0 comments on commit eacc919

Please sign in to comment.