Jump to content


Photo

General JavaScript


  • Please log in to reply
456 replies to this topic

#441 citypaul

citypaul

    Privileged

  • Privileged
  • PipPipPipPip
  • 847 posts
  • Gender:Male
  • Location:Manchester
  • Experience:Nothing
  • Area of Expertise:Web Developer

Posted 13 August 2017 - 09:38 AM

Look at using something like JsDom or mock out window references.

I went he mocking route.
Here I create some aliases to the window globals that I wish to mock
 

const addEListener = (e, cb) =>
  window.addEventListener(e, cb)

const removeEListener = (e, cb) =>
  window.removeEventListener(e, cb)

export {
  addEListener,
  removeEListener
}

I consume them in this component

 

import { Component, createElement as E } from 'react'
import T from 'prop-types'
import { classNames, addEListener, removeEListener } from '../../utils'

class NavbarDropdownToggle extends Component {
  constructor() {
    super()

    this.state = {
      isOpen: false
    }
  }

  componentDidMount() {
    addEListener('click', this.handleClickOutside)
  }

  componentWillUnmount() {
    removeEListener('click', this.handleClickOutside)
  }

  handleClick = () =>
    this.setState({
      isOpen: !this.state.isOpen
    })


  handleClickOutside = ({ target }) =>
    this.setState({
      isOpen: !this.button.contains(target)
        ? false
        : this.state.isOpen
    })

  render() {
    const { node, children, className, ...rest } = this.props
    return E(
      node || 'button',
      {
        onClick: this.handleClick,
        ref: (n) => { this.button = n },
        className: classNames(
          'c-navbar__dropdown-toggle', className,
          { 'is-open': this.state.isOpen }
        ),
        ...rest
      },
      children
    )
  }
}

NavbarDropdownToggle.propTypes = {
  className: T.string,
  node: T.string,
  children: T.node.isRequired
}

export default NavbarDropdownToggle

And tested here
 

import React from 'react'
import { shallow, mount } from 'enzyme'

import { Navbar } from '../'

import { addEListener, removeEListener } from '../../../utils/window'

jest.mock('../../../utils/window')

describe('<Navbar.Dropdown.Toggle />', () => {
  it('adds a click handler to the window when mounted and removes it when it unmounts', () => {
    const mockAddEventListener = jest.fn()
    const mockRemoveEventListener = jest.fn()
    addEListener.mockImplementation(mockAddEventListener)
    removeEListener.mockImplementation(mockRemoveEventListener)

    const $ = mount(<Navbar.Dropdown.Toggle>_</Navbar.Dropdown.Toggle>)
    const handleClickOutside = $.instance().handleClickOutside
    expect(mockAddEventListener).toHaveBeenCalledWith('click', handleClickOutside)
    $.unmount()
    expect(mockRemoveEventListener).toHaveBeenCalledWith('click', handleClickOutside)
  })
})


This is in Jest so the syntax will be different.  For Mocha you will need to bring in Sinon to mock.  These are reasons I use Jest because all this stuff just comes out of the box.

 

Are you testing that the DOM updates when the state of isOpen changes?

 

TBH I don't think I would explicitly check the event handler stuff at all there, at least not for setting the initial setup in componentDidMount. Isn't the point of that click handler to set the isOpen state when someone clicks? Can't you just test that entire thing by using Enzyme's simulate method with a click event, and prove that the `isOpen` state has the intended impact on the DOM? That way you are not tying your implementation detail to your test, and your test explicitly tests the thing you care about, which is that when someone clicks on an element, something happens in the DOM.

 

I much prefer the black box style of testing favoured by the people like Kent Beck and Martin Fowler who popularised TDD. 

 

The problem with the way the test currently is is that it's tightly coupled to the implementation, and it doesn't really test what appears to matter. Also, you've had to create an entirely new component under the hood just to allow the internals to be mocked, which again is a bit of a code/test smell to me. Sometimes these things may be necessary, but rarely in my experience.

 

If you focus your tests on behaviours that matter, you can refactor the internals on a whim with the confidence that comes from black box style testing. 



#442 citypaul

citypaul

    Privileged

  • Privileged
  • PipPipPipPip
  • 847 posts
  • Gender:Male
  • Location:Manchester
  • Experience:Nothing
  • Area of Expertise:Web Developer

Posted 13 August 2017 - 09:41 AM

There's an example in the Jest docs that does something similar here. This uses jQuery to change the DOM, but the principle is the same - don't test the explicit setting of the click handler, but instead test the resulting output of triggering the event in terms of the effect it has on the DOM. 

 

https://facebook.git...ial-jquery.html


Edited by citypaul, 13 August 2017 - 09:42 AM.


#443 rbrtsmith

rbrtsmith

    ReferenceError

  • Privileged
  • PipPipPipPipPip
  • 4,052 posts
  • Gender:Male
  • Location:Manchester, UK
  • Experience:Nothing
  • Area of Expertise:Web Developer

Posted 14 August 2017 - 07:41 AM

AFAIK Enzyme's simulate method only works on React nodes and not on real DOM nodes, as I am testing here for click events  on the actual document itself itself.  The expected behaviour is that if you click anywhere on the document that is not this component or a descendant of it, the state is updated to set isOpen to false.
I am not sure how else to test the behaviour.

Whilst mocking might not be the best solution it does enable me to test that the component behaves as expected.  I'd be happy to see a better way of doing this :)


Edited by rbrtsmith, 14 August 2017 - 07:42 AM.


#444 Jack

Jack

    NaN

  • Moderators
  • PipPipPipPipPip
  • 3,218 posts
  • Gender:Male
  • Location:Jersey Channel Islands
  • Experience:Advanced
  • Area of Expertise:Web Designer

Posted 14 August 2017 - 08:01 AM

I've not done much of this kinda stuff using Jest yet (started using Jest for some pure JS stuff, but not had to build anything new in React since using it, so not quite as familiar with JSDOM). However, I've never had to mock out the window object like this before and it doesn't sound like a great idea to me.

 

I've tended to use Karma and Mocha/Jasmine in the past, and that gives you a real DOM to work with if you're combining it with something like PhantomJS. 

 

I'd be surprised if you have to mock out the window object in this way. I'd prefer to use a real DOM, or a DOM like representation that's so close to reality it might as well be the real thing.

 

If you're using Karma with Phantom then you have a real window object because you have a real DOM, and the problem goes away.

 

Good point, I could probably use Chrome's new headless browser for this. Do you find any performance decreases testing with a headless browser rather than just Node?



#445 Jack

Jack

    NaN

  • Moderators
  • PipPipPipPipPip
  • 3,218 posts
  • Gender:Male
  • Location:Jersey Channel Islands
  • Experience:Advanced
  • Area of Expertise:Web Designer

Posted 31 August 2017 - 11:24 AM

GraphQL client for CraftCMS https://github.com/markhuot/craftql. The stuff people write for this CMS is just a cut above.



#446 Jack

Jack

    NaN

  • Moderators
  • PipPipPipPipPip
  • 3,218 posts
  • Gender:Male
  • Location:Jersey Channel Islands
  • Experience:Advanced
  • Area of Expertise:Web Designer

Posted 19 September 2017 - 09:38 AM

Just filled out the FEM 2018 course picker, they've had some incredible courses on this year. Also, it's the last day of the sale, you can save $178, and if you're lucky, Marc will save that price in your account permanently.



#447 Jack

Jack

    NaN

  • Moderators
  • PipPipPipPipPip
  • 3,218 posts
  • Gender:Male
  • Location:Jersey Channel Islands
  • Experience:Advanced
  • Area of Expertise:Web Designer

Posted 29 September 2017 - 09:37 AM

A couple links I've been looking at recently.
 
https://date-fns.org - like MomentJS, but modular, so you can include the specific things you want or compose things together.
 
Never Write Another HoC



#448 rbrtsmith

rbrtsmith

    ReferenceError

  • Privileged
  • PipPipPipPipPip
  • 4,052 posts
  • Gender:Male
  • Location:Manchester, UK
  • Experience:Nothing
  • Area of Expertise:Web Developer

Posted 29 September 2017 - 04:49 PM

I've watch the never write another HOC before, render prop callbacks are a really cool and powerful pattern.  HOC can be a bit confusing how props passed in can be obfuscated from consumer, whereas render props are explicit.  Bit like context, although when building a framework context is really, really useful :)

I've been waiting ages for a modular Date library as good as Moment.js is, it's huge!



#449 citypaul

citypaul

    Privileged

  • Privileged
  • PipPipPipPip
  • 847 posts
  • Gender:Male
  • Location:Manchester
  • Experience:Nothing
  • Area of Expertise:Web Developer

Posted 30 September 2017 - 07:25 PM

I've watch the never write another HOC before, render prop callbacks are a really cool and powerful pattern.  HOC can be a bit confusing how props passed in can be obfuscated from consumer, whereas render props are explicit.  Bit like context, although when building a framework context is really, really useful :)
I've been waiting ages for a modular Date library as good as Moment.js is, it's huge!

I heard about date-fns on a Wes Bos podcast a week or so ago: https://syntax.fm/sh...ility-libraries

Had a need for it a day or two later and tried it out. Very cool indeed. I needed to parse a partial iso8601 date format into a js date, then format it as a nice string. There's a parse function that does just that, and a format function that works just as you'd expect. Instead of using moment I just used two functions that were just a few k each. Much smaller than momentjs, and far easier than doing it natively in js.

Edited by citypaul, 30 September 2017 - 07:26 PM.


#450 citypaul

citypaul

    Privileged

  • Privileged
  • PipPipPipPip
  • 847 posts
  • Gender:Male
  • Location:Manchester
  • Experience:Nothing
  • Area of Expertise:Web Developer

Posted 30 September 2017 - 07:29 PM

Just filled out the FEM 2018 course picker, they've had some incredible courses on this year. Also, it's the last day of the sale, you can save $178, and if you're lucky, Marc will save that price in your account permanently.


I finally got work to shell out for 15 licenses :-)


#451 Jack

Jack

    NaN

  • Moderators
  • PipPipPipPipPip
  • 3,218 posts
  • Gender:Male
  • Location:Jersey Channel Islands
  • Experience:Advanced
  • Area of Expertise:Web Designer

Posted 02 October 2017 - 10:59 AM

I heard about date-fns on a Wes Bos podcast a week or so ago: https://syntax.fm/sh...ility-libraries

Had a need for it a day or two later and tried it out. Very cool indeed. I needed to parse a partial iso8601 date format into a js date, then format it as a nice string. There's a parse function that does just that, and a format function that works just as you'd expect. Instead of using moment I just used two functions that were just a few k each. Much smaller than momentjs, and far easier than doing it natively in js.

 
Yeah, they seem to have a crazy amount of tests as well, which I guess you really need on a date library.
 

Each build CI checks more than 650 000 examples in about 400 time zones.

 

*faints*



#452 rbrtsmith

rbrtsmith

    ReferenceError

  • Privileged
  • PipPipPipPipPip
  • 4,052 posts
  • Gender:Male
  • Location:Manchester, UK
  • Experience:Nothing
  • Area of Expertise:Web Developer

Posted 04 October 2017 - 08:55 AM

 
Yeah, they seem to have a crazy amount of tests as well, which I guess you really need on a date library.
 

 

*faints*

I can't remember the name of the library, but there's one been worked on at the moment by *I think* Lee Byron over at Facebook that generates loads of edge case data for tests and loops over them.  I would assume they are doing something like this rather than having such a high number of static tests.

I first heard about this at a talk I went to given by Martin Fowler a few months ago, it certainly sounded interesting.



#453 Jack

Jack

    NaN

  • Moderators
  • PipPipPipPipPip
  • 3,218 posts
  • Gender:Male
  • Location:Jersey Channel Islands
  • Experience:Advanced
  • Area of Expertise:Web Designer

Posted 23 October 2017 - 03:07 PM

I've wondered for a while why most modern Node tutorials use the commonJS syntax for modules. Apparently, Node doesn't support ES modules yet, without Webpack (which is probably why I've never noticed), anyway, it's super annoying. Luckily the guy behind Lodash has created this https://github.com/standard-things/esm.



#454 rbrtsmith

rbrtsmith

    ReferenceError

  • Privileged
  • PipPipPipPipPip
  • 4,052 posts
  • Gender:Male
  • Location:Manchester, UK
  • Experience:Nothing
  • Area of Expertise:Web Developer

Posted 24 October 2017 - 11:22 AM

I've wondered for a while why most modern Node tutorials use the commonJS syntax for modules. Apparently, Node doesn't support ES modules yet, without Webpack (which is probably why I've never noticed), anyway, it's super annoying. Luckily the guy behind Lodash has created this https://github.com/standard-things/esm.

Shouldn't really use Webpack on the server as it's intended for the client, you can transpile just using Babel.

Babel can to transpile your ES6+ code and dump it all in a lib folder where you server will read from, although Node has implemented the vast majority of ES features now so I prefer to just use commonJS imports.

To be honest I don't find using them to be much of a pain.

Not a Node project, but an example of using Babel in isolation to transpile some code and dump it in a lib folder
https://github.com/N...ackage.json#L11


Edited by rbrtsmith, 24 October 2017 - 11:24 AM.


#455 Jack

Jack

    NaN

  • Moderators
  • PipPipPipPipPip
  • 3,218 posts
  • Gender:Male
  • Location:Jersey Channel Islands
  • Experience:Advanced
  • Area of Expertise:Web Designer

Posted 24 October 2017 - 11:49 AM

Shouldn't really use Webpack on the server as it's intended for the client, you can transpile just using Babel.

Babel can to transpile your ES6+ code and dump it all in a lib folder where you server will read from, although Node has implemented the vast majority of ES features now so I prefer to just use commonJS imports.

To be honest I don't find using them to be much of a pain.

Not a Node project, but an example of using Babel in isolation to transpile some code and dump it in a lib folder
https://github.com/N...ackage.json#L11

 

Yeah I use Webpack and Babel a lot client side, but I'm building something just in Node at the moment, so it was confusing initially to not be able to use the same import syntax. I love the ESM syntax, it ditches messy module.exports, temporary variables, and it's super easy to grab individual methods without having to assign them to anything. All in all it's a lot cleaner, and @std/esm doesn't touch Babel, so you can use the same syntax for modules everywhere. This is an inevitable upgrade from April 2018 anyway if people want to use Node 10.0.



#456 rbrtsmith

rbrtsmith

    ReferenceError

  • Privileged
  • PipPipPipPipPip
  • 4,052 posts
  • Gender:Male
  • Location:Manchester, UK
  • Experience:Nothing
  • Area of Expertise:Web Developer

Posted 24 October 2017 - 12:01 PM

I'm not convinced another type of module is the best approach though, it's something that will likely end up legacy and when new people come to onboard it will be a point of confusion.

I've honestly never had a problem with CommonJS syntax, ES6 imports being a minor improvement.  Generally with Node I don't use Babel as the compile step is some extra complexity with not enough gain to warrant it.

The Node team are working on implementing ES6 import/export but it's more tricky than initially thought but I believe they're getting quite close now so it shouldn't be a problem for long :)



#457 Jack

Jack

    NaN

  • Moderators
  • PipPipPipPipPip
  • 3,218 posts
  • Gender:Male
  • Location:Jersey Channel Islands
  • Experience:Advanced
  • Area of Expertise:Web Designer

Posted 24 October 2017 - 12:57 PM

Std/esm is standards compliant with what the Node team are working on, it's not a new module system or anything. April 2018 is supposed to be when Node 10.0 lands with the feature flag for ESM removed, at that stage you should be able to transition over. I expect there to be gains with static analysis and performance over time bringing this into Node, from what I've read, require is synchronous, and doesn't allow for much optimisation.
 
At the end of the day, though, I just like the syntactic sugar ¯\_(ツ)_/¯






2 user(s) are reading this topic

1 members, 1 guests, 0 anonymous users