Search code examples
javascriptunit-testingmootools

Can't trigger mouseenter programmatically in MooTools


I'm writing unit tests and I need to trigger a mouseenter to make sure the handler is attached to a certain element and it does the right things in the callback. However, I spent a bunch of time on it and realized it seems just to be mouse events... I think. http://jsbin.com/EZIriqA/1/edit

It should fire two alerts. One for "double click" and one for "mouse enter". It only fires one. The code is identical (I made sure by copying and pasting it and just changing the event names).

Here's the code:

$('foo').addEvent('mouseenter:relay(li[data-type])', function () {
  alert('Mouse Enter');
});

$('foo').fireEvent('mouseenter', {
  target: $$('li[data-type]')[0]
});

$('foo').addEvent('dblclick:relay(li[data-type])', function () {
  alert('Double Click');
});

$('foo').fireEvent('dblclick', {
  target: $$('li[data-type]')[0]
});

How would I go about testing code in mouseenters if I can't get it to trigger? I can move all the code around to being methods on the class assign them here, but I didn't write this code and reason I'm writing unit tests is to safely refactor all of it.


Solution

  • Disclaimer: drunk. but: this is because mootools kind of pioneered mouseenter (along with prototype) before it was a native event. mouseenter is actually extending mouseover in the polyfill. delegated mouseenter is actually not a good practice - it has been buggy and is considered a better idea to use mouseover:relay() instead.

    This was added recently-ish (1.4.4/5) - https://github.com/mootools/mootools-core/blob/master/Source/Element/Element.Event.js#L156-L162 - Element.NativeEvents.mouseenter = 2; bit. Not sure it has worked on the delegator level yet.

    Anyway - because mouseenter has mouseover as base, you can parentel.fireEvent('mouseover', { target: el }) and it will be fine.

    I know this is pseudo code but you still don't seem to be using mootools as per best practices.

    document.id('the-sandbox').set('html', '<ul id="foo"><li data-type="foo">Foo</li><li data-type="bar">Bar</li></ul>');
    var t = document.getElement('li[data-type]'),
        foo = document.id('foo');
    
    foo.addEvents({
      'mouseenter:relay(li[data-type])': function () {
        console.log('Mouse Enter'); 
      },
      'dblclick:relay(li[data-type])': function () { 
        console.log('Double Click');
      }  
    });
    
    foo.fireEvent('mouseover', { 
      target: t
    }).fireEvent('dblclick', {
      target: t
    });
    

    differences: even though $ returns an el, use the element api. new Elements / prime selectors won't return an element, will be wrapped. selectors should be cached. $$ should be cached but is better to use document.getElement -> querySelector vs $$ -> querySelectorAll()[0].

    Also, if you want to do tests of these, I suggest something like Syn (synthetic event library, on github) - it's really like a mini web-driver that you can tell to mouseover, click, type etc. very handy if you don't mind crossing testing boundaries between unit and end2end / integration testing.