~statianzo/angularjs-testing-library

d7c50a87d11b922ea44ade4dfc4ddc650bd42087 — Jason Staten 4 years ago 1706665
feat: flush intervals by 50 ms default
2 files changed, 74 insertions(+), 50 deletions(-)

M src/__tests__/stopwatch.js
M src/pure.js
M src/__tests__/stopwatch.js => src/__tests__/stopwatch.js +61 -43
@@ 1,56 1,74 @@
import React from 'react'
import {render, fireEvent} from '../'
import angular from 'angular'
import 'angular-mocks'
import {render, fireEvent, wait} from '../'

class StopWatch {
  lapse = 0
  running = false

  constructor($interval) {
    this.$interval = $interval
  }

class StopWatch extends React.Component {
  state = {lapse: 0, running: false}
  handleRunClick = () => {
    this.setState(state => {
      if (state.running) {
        clearInterval(this.timer)
      } else {
        const startTime = Date.now() - this.state.lapse
        this.timer = setInterval(() => {
          this.setState({lapse: Date.now() - startTime})
        })
      }
      return {running: !state.running}
    })
    if (this.running) {
      clearInterval(this.timer)
    } else {
      const startTime = Date.now() - this.lapse
      this.timer = this.$interval(() => {
        this.lapse = Date.now() - startTime
      })
    }
    this.running = !this.running
  }

  handleClearClick = () => {
    clearInterval(this.timer)
    this.setState({lapse: 0, running: false})
    this.$interval.cancel(this.timer)
    this.lapse = 0
    this.running = false
  }
  componentWillUnmount() {
    clearInterval(this.timer)
  }
  render() {
    const {lapse, running} = this.state
    return (
      <div>
        <span>{lapse}ms</span>
        <button onClick={this.handleRunClick}>
          {running ? 'Stop' : 'Start'}
        </button>
        <button onClick={this.handleClearClick}>Clear</button>
      </div>
    )

  $onDestroy() {
    this.$interval.cancel(this.timer)
  }
}

const wait = time => new Promise(resolve => setTimeout(resolve, time))
const template = `
  <div>
    <span data-testid="elapsed">{{$ctrl.lapse}}ms</span>
    <button ng-click="$ctrl.handleRunClick()">
      {{$ctrl.running ? 'Stop' : 'Start'}}
    </button>
    <button ng-click="$ctrl.handleClearClick()">Clear</button>
  </div>
`

beforeEach(() => {
  angular.module('atl', [])
  angular.mock.module('atl')
  angular.module('atl').component('atlStopwatch', {
    template,
    controller: StopWatch,
  })
})

test('unmounts a component', async () => {
  jest.spyOn(console, 'error').mockImplementation(() => {})
  const {unmount, getByText, container} = render(<StopWatch />)
  const {$scope, unmount, getByText, getByTestId} = render(
    `<atl-stopwatch></atl-stopwatch>`,
  )
  const elapsedTime = getByTestId('elapsed')

  expect(elapsedTime).toHaveTextContent('0ms')

  fireEvent.click(getByText('Start'))
  // Ensure it starts
  getByText('Stop')

  await wait()

  expect(elapsedTime.textContent).not.toEqual('0ms')

  unmount()
  // hey there reader! You don't need to have an assertion like this one
  // this is just me making sure that the unmount function works.
  // You don't need to do this in your apps. Just rely on the fact that this works.
  expect(container.innerHTML).toBe('')
  // just wait to see if the interval is cleared or not
  // if it's not, then we'll call setState on an unmounted component
  // and get an error.
  // eslint-disable-next-line no-console
  await wait(() => expect(console.error).not.toHaveBeenCalled())

  expect($scope.$$destroyed).toBe(true)
})

M src/pure.js => src/pure.js +13 -7
@@ 59,6 59,9 @@ function render(ui, {container, baseElement = container, queries, scope} = {}) {
      return template.content
    },
    $scope,
    unmount: () => {
      $scope.$destroy()
    },
    ...getQueriesForElement(baseElement, queries),
  }
}


@@ 79,6 82,7 @@ function cleanupAtContainer(container) {

function cleanupScope(scope) {
  scope.$destroy()
  mountedScopes.delete(scope)
}

function getAngularService(name) {


@@ 112,16 116,18 @@ Object.keys(dtlFireEvent).forEach(key => {
fireEvent.mouseEnter = fireEvent.mouseOver
fireEvent.mouseLeave = fireEvent.mouseOut

function flush() {
function flush(millis = 50) {
  const $browser = getAngularService('$browser')
  if ($browser.deferredFns.length) {
    $browser.defer.flush()
  }
  const $rootScope = getAngularService('$rootScope')
  const $interval = getAngularService('$interval')
  $interval.flush(millis)
  $browser.defer.flush(millis)
  $rootScope.$digest()
}

function wait(callback, options) {
  dtlWait(() => {
    flush()
function wait(callback, options = {}) {
  return dtlWait(() => {
    flush(options.interval)
    if (callback) {
      callback()
    }