A src/__tests__/digest.js => src/__tests__/digest.js +39 -0
@@ 0,0 1,39 @@
+import angular from 'angular'
+import 'angular-mocks'
+import {render, fireEvent} from '../'
+
+beforeEach(() => {
+ angular.module('atl', [])
+ angular.mock.module('atl')
+})
+
+test('`fireEvent` triggers a digest', () => {
+ angular.module('atl').component('atlDigest', {
+ template: `
+ <button ng-ref="$ctrl.btn">
+ Click Me
+ </button>
+ <div ng-if="$ctrl.wasClicked">
+ Clicked!
+ </div>
+ `,
+ controller: class {
+ wasClicked = false
+ btn = null
+ $postLink() {
+ this.btn.on('click', this.handleClick)
+ }
+
+ handleClick = () => {
+ this.wasClicked = true
+ }
+ },
+ })
+
+ const {getByRole, queryByText} = render(`<atl-digest></atl-digest>`)
+
+ const button = getByRole('button')
+ expect(queryByText('Clicked!')).toBeNull()
+ fireEvent.click(button)
+ expect(queryByText('Clicked!')).not.toBeNull()
+})
M src/__tests__/events.js => src/__tests__/events.js +7 -11
@@ 135,10 135,8 @@ eventTypes.forEach(({type, events, elementType, init}) => {
it(`triggers ${eventName}`, () => {
const spy = jest.fn()
- const {getByTestId} = render(
- `
- <${elementType}
- data-testid="target"
+ const {container} = render(
+ `<${elementType}
ng-on-${propName}="spy()"
></${elementType}>`,
{
@@ 148,8 146,7 @@ eventTypes.forEach(({type, events, elementType, init}) => {
},
)
- const target = getByTestId('target')
- fireEvent[eventName](target, init)
+ fireEvent[eventName](container.firstChild, init)
expect(spy).toHaveBeenCalledTimes(1)
})
})
@@ 160,11 157,10 @@ test('calling `fireEvent` directly works too', () => {
const spy = jest.fn()
const {getByTestId} = render(
- `
- <button
- data-testid="target"
- ng-click="spy()"
- ></button>`,
+ `<button
+ data-testid="target"
+ ng-click="spy()"
+ ></button>`,
{
scope: {
spy,
D src/act-compat.js => src/act-compat.js +0 -135
@@ 1,135 0,0 @@
-import React from 'react'
-import ReactDOM from 'react-dom'
-import * as testUtils from 'react-dom/test-utils'
-
-const reactAct = testUtils.act
-const actSupported = reactAct !== undefined
-
-// act is supported react-dom@16.8.0
-// so for versions that don't have act from test utils
-// we do this little polyfill. No warnings, but it's
-// better than nothing.
-function actPolyfill(cb) {
- ReactDOM.unstable_batchedUpdates(cb)
- ReactDOM.render(<div />, document.createElement('div'))
-}
-
-const act = reactAct || actPolyfill
-
-let youHaveBeenWarned = false
-let isAsyncActSupported = null
-
-function asyncAct(cb) {
- if (actSupported === true) {
- if (isAsyncActSupported === null) {
- return new Promise((resolve, reject) => {
- // patch console.error here
- const originalConsoleError = console.error
- console.error = function error(...args) {
- /* if console.error fired *with that specific message* */
- /* istanbul ignore next */
- const firstArgIsString = typeof args[0] === 'string'
- if (
- firstArgIsString &&
- args[0].indexOf(
- 'Warning: Do not await the result of calling ReactTestUtils.act',
- ) === 0
- ) {
- // v16.8.6
- isAsyncActSupported = false
- } else if (
- firstArgIsString &&
- args[0].indexOf(
- 'Warning: The callback passed to ReactTestUtils.act(...) function must not return anything',
- ) === 0
- ) {
- // no-op
- } else {
- originalConsoleError.apply(console, args)
- }
- }
- let cbReturn, result
- try {
- result = reactAct(() => {
- cbReturn = cb()
- return cbReturn
- })
- } catch (err) {
- console.error = originalConsoleError
- reject(err)
- return
- }
-
- result.then(
- () => {
- console.error = originalConsoleError
- // if it got here, it means async act is supported
- isAsyncActSupported = true
- resolve()
- },
- err => {
- console.error = originalConsoleError
- isAsyncActSupported = true
- reject(err)
- },
- )
-
- // 16.8.6's act().then() doesn't call a resolve handler, so we need to manually flush here, sigh
-
- if (isAsyncActSupported === false) {
- console.error = originalConsoleError
- /* istanbul ignore next */
- if (!youHaveBeenWarned) {
- // if act is supported and async act isn't and they're trying to use async
- // act, then they need to upgrade from 16.8 to 16.9.
- // This is a seemless upgrade, so we'll add a warning
- console.error(
- `It looks like you're using a version of react-dom that supports the "act" function, but not an awaitable version of "act" which you will need. Please upgrade to at least react-dom@16.9.0 to remove this warning.`,
- )
- youHaveBeenWarned = true
- }
-
- cbReturn.then(() => {
- // a faux-version.
- // todo - copy https://github.com/facebook/react/blob/master/packages/shared/enqueueTask.js
- Promise.resolve().then(() => {
- // use sync act to flush effects
- act(() => {})
- resolve()
- })
- }, reject)
- }
- })
- } else if (isAsyncActSupported === false) {
- // use the polyfill directly
- let result
- act(() => {
- result = cb()
- })
- return result.then(() => {
- return Promise.resolve().then(() => {
- // use sync act to flush effects
- act(() => {})
- })
- })
- }
- // all good! regular act
- return act(cb)
- }
- // use the polyfill
- let result
- act(() => {
- result = cb()
- })
- return result.then(() => {
- return Promise.resolve().then(() => {
- // use sync act to flush effects
- act(() => {})
- })
- })
-}
-
-export default act
-export {asyncAct}
-
-/* eslint no-console:0 */
M src/pure.js => src/pure.js +22 -9
@@ 84,23 84,36 @@ function cleanupScope(scope) {
scope.$destroy()
}
+function getRootScope() {
+ let $rootScope
+ angular.mock.inject([
+ '$rootScope',
+ rootScope => {
+ $rootScope = rootScope
+ },
+ ])
+ return $rootScope
+}
+
function fireEvent(...args) {
- return dtlFireEvent(...args)
+ const $rootScope = getRootScope()
+ const result = dtlFireEvent(...args)
+ $rootScope.$digest()
+ return result
}
Object.keys(dtlFireEvent).forEach(key => {
fireEvent[key] = (...args) => {
- return dtlFireEvent[key](...args)
+ const $rootScope = getRootScope()
+ const result = dtlFireEvent[key](...args)
+ $rootScope.$digest()
+ return result
}
})
-fireEvent.mouseEnter = (...args) => {
- return dtlFireEvent.mouseOver(...args)
-}
-
-fireEvent.mouseLeave = (...args) => {
- return dtlFireEvent.mouseOut(...args)
-}
+// AngularJS maps `mouseEnter` to `mouseOver` and `mouseLeave` to `mouseOut`
+fireEvent.mouseEnter = fireEvent.mouseOver
+fireEvent.mouseLeave = fireEvent.mouseOut
export * from '@testing-library/dom'
export {render, cleanup, fireEvent}