Drum Machine: Not passing last test

This is the final test I need to pass:

When I press the trigger key associated with each .drum-pad, the audio clip contained in its child <audio> element should be triggered (e.g. pressing the Q key should trigger the drum pad which contains the string “Q”, pressing the W key should trigger the drum pad which contains the string “W”, etc.).

I don’t see why my code won’t pass the last test, even though it does exactly what it’s supposed to do. Can anyone help me out? Thank you.

code: https://codesandbox.io/s/github/anthonyzamarro/fcc-drum-machine

EDIT 2:

I’ve updated my code, but still no luck

components/drums.js

import React, { Component } from "react";
import { connect } from "react-redux";
import { drumClicked } from "../actions/drumsAction";
import { drumPressed } from "../actions/drumsAction";

class Drums extends Component {
  constructor(props) {
    super(props);
    this.state = {
      drum: this.props.drumData.drumData,
      current: 'active'
    };
    this.handleClick = this.handleClick.bind(this);
    this.handleKeyPress = this.handleKeyPress.bind(this);
  }
  componentDidMount() {
    // document.addEventListener("keydown", e => this.handleKeyPress(e));
  }
  handleClick(e, d) {
    e.target.children[0].play();
    e.persist()
    e.target.classList.add(this.state.current);
    setTimeout(() => {
      e.target.classList.remove(this.state.current);
    }, 100);
    this.props.itemClicked(d);
  }
  handleKeyPress(e) {
    this.state.drum.forEach(d => {
      if (e.key === d.keyCode) {
        let currentDrum = document.querySelector(`#key-${d.key}`);
        // console.log(currentDrum.children[0]);
        currentDrum.children[0].play();
        currentDrum.classList.add(this.state.current);
        setTimeout(() => {
          currentDrum.classList.remove(this.state.current);
        }, 100);
        this.props.itemPressed(d);
      }
    });
  }
  render() {
    let displayDrums = this.props.drumData.drumData.map(drum => {
      return (
        <li
          key={drum.id}
          onClick={e => this.handleClick(e, drum)}
          onKeyPress={e => this.handleKeyPress(e)}
          id={`key-${drum.key}`}
          className={`drum-pad`}
          tabIndex="0"
          ref={li => li && li.focus()}
        >
          {drum.key}
          <audio src={drum.audio} className="clip" id={drum.key} />
        </li>
      );
    });
    return (
      <div className="display-drums-container">
        <ul>{displayDrums}</ul>
      </div>
    );
  }
}

function mapStateToProps(drum_data) {
  return {
    drumData: drum_data
  };
}

function mapDispatchToProps(dispatch) {
  return {
    itemClicked: data => {
      dispatch(drumClicked(data));
    },
    itemPressed: data => {
      dispatch(drumPressed(data));
    }
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Drums);

Sorry, I can’t seem to get it to work. I can’t get the test script work. It comes up for a few second and then disappears after a second. I’ve tried adding the MDN in the Dependency section but it is fighting me so I’ve given up. I just don’t know that ide.

If it helps, here is the test from the test suite:

      it(`When I press the trigger key associated with each
      .drum-pad, the audio clip contained in its child <audio> element should be
      triggered (e.g. pressing the Q key should trigger the drum pad which
      contains the string "Q", pressing the W key should trigger the drum pad
      which contains the string "W", etc.).`,
      function() {
        const keyCodes = [ 81, 87, 69, 65, 83, 68, 90, 88, 67 ];
        assert.isAtLeast(
          audioElements.length,
          9,
          'Audio elements do not exist '
        );

        audioElements.forEach((el, i) => {
          el.pause();
          __triggerEvent(
            el.parentElement,
            'keydown',
            keyCodes[i]
          );
          assert.isFalse(
            el.paused,
            'No audio plays when the ' + el.id + ' key is pressed '
          );
          el.pause();
        });
      });
1 Like

And here is how it is grabbing those elements:

    const drumPads = document.querySelectorAll('.drum-pad');
    const audioElements = document.querySelectorAll('.drum-pad .clip');

@kevinSmith Thank you for taking the time to help me out. I updated my question to include the relevant code. After viewing the test you posted, I’m starting to think that I’ve added the keydown event on the wrong element. I’m still debugging my issue, but thanks again for your help.

@kevinSmith

I figured out why my test was failing. I was looking for the key rather than the key code for keyboard press events. Just another case of not paying close enough attention. All the tests pass now, which is great.

However, I am getting an error in the console that looks like this:

21%20AM

I’m not sure if it’s an issue on my side (I’m developing locally, not in the browser) or if there is a problem in the test suite. I just want to bring this to someone’s attention. Thanks again for you help.

You are developing locally? Can you create a repo? Then I can run it and figure it out.

Looking here and here, it looks like pause is being called before play is done loading.

Is this happening when you run the app in general or when the test suite is running?

Here’s the repo: https://github.com/anthonyzamarro/fcc-drum-machine

The error only occurs when the test suite is running. I noticed in the test suite, there is a call to pause the element, so maybe this has something to do with it? I noticed it in the code you posted above.

I’m hosting the app on Github Pages as well. When you run the tests the promise error occurs. You can check it out here: https://anthonyzamarro.github.io/fcc-drum-machine/

OK, I’ll try to find some time for it tonight. Unless someone else wants to beat me to it.

Yeah, I downloaded your repo and ran it and didn’t get those errors. I ran my old drum machine and noticed some errors for the test too, just different ones.

I think your code is fine and it’s the test that is a problem. These lines:

          __triggerEvent(/* ... */);
          assert.isFalse(/* ... */);
          el.pause();

This is the trigger code:

    function __triggerEvent(el, eventType, keyCode) {
      const eventObj = document.createEventObject
        ? document.createEventObject()
        : document.createEvent('Events');
      if (eventObj.initEvent) {
        eventObj.initEvent(eventType, true, true);
      }
      if (keyCode) {
        eventObj.keyCode = keyCode;
        eventObj.which = keyCode;
        eventObj.key = String.fromCharCode(keyCode);
      }
      /* eslint no-unused-expressions: ["error", { "allowTernary": true }] */
      el.dispatchEvent
        ? el.dispatchEvent(eventObj)
        : el.fireEvent('on' + eventType, eventObj);
    }

Notice that there is nothing to handle async anywhere - no callbacks, no promises, not async/await. They’re triggering the event and praying that it gets set up before the pause gets called. I’m not an expert here, but that seems like bad writing to me. Anytime you are dealing with anything outside the immediate browser, you are dealing with async, whether it’s getting json data from the other side of the planet or firing off the sound card. Ideally something to handle the async is needed, or even a small delay would probably work for testing purposes here.

Don’t sweat it - move one. Good job.

1 Like