Here’s how it gets done, without breaking anything in the tests.
The as-of-yet incomplete set of tests are at https://jsfiddle.net/36qhy49g/1/
The init tests all seem to be complete there though, so we can happily play about with code relating to the initialization without hopefully risking damaging anything else.
The addEvents() function is not to your liking.
function addEvents(handlers = {}) {
eventHandlers.afterPlayerReady = handlers.afterPlayerReady;
events.afterPlayerReady = new Event("afterPlayerReady");
}
You want to use just a single handler instead, so we assign a handler variable inside of the code.
function addEvents(handlers = {}) {
const handler = handlers.afterPlayerReady;
eventHandlers.afterPlayerReady = handlers.afterPlayerReady;
events.afterPlayerReady = new Event("afterPlayerReady");
}
The tests still pass.
We can then update the eventHandlers.afterPlayerReady line to use that handler variable.
function addEvents(handlers = {}) {
const handler = handlers.afterPlayerReady;
// eventHandlers.afterPlayerReady = handlers.afterPlayerReady;
eventHandlers.afterPlayerReady = handler;
events.afterPlayerReady = new Event("afterPlayerReady");
}
The tests still pass.
That handler variable now isn’t really needed inside of the function. It’s not appropriate yet to just replace the existing function parameter yet, because that can break all sorts of other things, so we’ll just perch the new handler parameter on the end of the function parameters.
// function addEvents(handlers = {}) {
function addEvents(handlers = {}, handler) {
const handler = handlers.afterPlayerReady;
eventHandlers.afterPlayerReady = handler;
events.afterPlayerReady = new Event("afterPlayerReady");
}
That causes the code to break because the const
is attempting to define a variable that already exists. We can remove const
from that line, while leaving everything else on that line unchanged.
function addEvents(handlers = {}, handler) {
// const handler = handlers.afterPlayerReady;
handler = handlers.afterPlayerReady;
eventHandlers.afterPlayerReady = handler;
events.afterPlayerReady = new Event("afterPlayerReady");
}
And the tests still pass.
The next stage is to get something useful from that handler parameter. That means passing in a second function parameter to everywhere that addEvents is being called.
Fortunately, that happens only in one place. We can get a handler from that initEventHandlers function:
function init(initEventHandlers) {
const afterPlayerReadyHandler = initEventHandlers.afterPlayerReady;
addEvents(initEventHandlers);
loadIframeScript();
window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
}
And before we can even pass that handler into addEvents, that breaks one of the tests.
videoPlayer tests > init > loads iframe script
TypeError: Cannot read properties of undefined (reading 'afterPlayerReady')
We have a test that calls the init() method with no function variable parameters, so there is no object in which afterPlayerReady can be found.
Is it appropriate to call the init() method with no function parameters? Yes it can be indeed, so we need a way to get a potential handler without breaking the code. One of those ways is to have the init() method parameter default to being an object, but then we are going to be setting up an event with an undefined handler, which isn’t any good. Another way is to check if the initEventHandlers variable exists, before doing anything with potential handlers in there.
function init(initEventHandlers) {
if (initEventHandlers) {
const afterPlayerReadyHandler = initEventHandlers.afterPlayerReady;
addEvents(initEventHandlers);
}
loadIframeScript();
window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
}
The tests go back to passing, and we can continue.
While making the update here, I also notice that it makes better sense to setup the handlers after loading the iframe script and setting up the apiready parts.
function init(initEventHandlers) {
// if (initEventHandlers) {
// const afterPlayerReadyHandler = initEventHandlers.afterPlayerReady;
// addEvents(initEventHandlers);
// }
loadIframeScript();
window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
if (initEventHandlers) {
const afterPlayerReadyHandler = initEventHandlers.afterPlayerReady;
addEvents(initEventHandlers);
}
}
With this new ordering of things, we can instead check if there are no event handlers to init and return out of the function, before the handler gets done.
function init(initEventHandlers) {
loadIframeScript();
window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
if (!initEventHandlers) {
return;
}
// if (initEventHandlers) {
const afterPlayerReadyHandler = initEventHandlers.afterPlayerReady;
addEvents(initEventHandlers);
// }
}
The tests still pass.
We can now send that afterPlayerReadyHandler variable to the addEvents function.
function init(initEventHandlers) {
loadIframeScript();
window.onYouTubeIframeAPIReady = onYouTubeIframeAPIReady;
if (!initEventHandlers) {
return;
}
const afterPlayerReadyHandler = initEventHandlers.afterPlayerReady;
// addEvents(initEventHandlers);
addEvents(initEventHandlers, afterPlayerReadyHandler);
}
Now that all things that use the addEvents() function have both function parameters, we can switch around the order of those function parameters so that the handler comes first.
// function addEvents(handlers = {}, handler) {
function addEvents(handler, handlers = {}) {
handler = handlers.afterPlayerReady;
eventHandlers.afterPlayerReady = handler;
events.afterPlayerReady = new Event("afterPlayerReady");
}
...
const afterPlayerReadyHandler = initEventHandlers.afterPlayerReady;
// addEvents(initEventHandlers, afterPlayerReadyHandler);
addEvents(afterPlayerReadyHandler, initEventHandlers);
The tests still pass, so we can now update addEvents() so that it doesn’t use the handlers function parameter.
function addEvents(handler, handlers = {}) {
// handler = handlers.afterPlayerReady;
eventHandlers.afterPlayerReady = handler;
events.afterPlayerReady = new Event("afterPlayerReady");
}
The tests still pass.
The handlers function parameter is no longer being used, so that can now be removed.
// function addEvents(handler, handlers = {}) {
function addEvents(handler) {
eventHandlers.afterPlayerReady = handler;
events.afterPlayerReady = new Event("afterPlayerReady");
}
The tests still pass.
This is when I notice that the addEvents() function is now poorly named, because it only deals with the afterPlayerReady event, but doesn’t yet do anything to inform us about that in the function name. We will get to that after removing the second function parameter.
The second function parameter can now be removed from everywhere else that it was used.
const afterPlayerReadyHandler = initEventHandlers.afterPlayerReady;
// addEvents(afterPlayerReadyHandler, initEventHandlers);
addEvents(afterPlayerReadyHandler);
The tests still pass.
This is now a good time to rename the addEvents function, to something more suitable such as initAfterPlayerReady.
// function addEvents(handler) {
function initAfterPlayerReady(handler) {
eventHandlers.afterPlayerReady = handler;
events.afterPlayerReady = new Event("afterPlayerReady");
}
...
const afterPlayerReadyHandler = initEventHandlers.afterPlayerReady;
// initHandlers(afterPlayerReadyHandler);
initAfterPlayerReady(afterPlayerReadyHandler);
And the tests still pass.
That also looks to be all of the work that needs to be done in that section to achieve the desired outcome, of having the function be singular in nature instead of plural.
Hopefully this helps to demonstrate the usefulness and benefits of having those tests in place, as well as that it’s not normally as easy as making a simple change.