From b82fb936958a2b1c6bef7d84bc18da2269d93877 Mon Sep 17 00:00:00 2001 From: Jason Staten Date: Fri, 1 Nov 2019 16:03:48 -0600 Subject: [PATCH] feat: first passing render tests --- jest.config.js | 6 ++ package.json | 4 +- src/__tests__/__snapshots__/render.js.snap | 23 +++-- src/__tests__/render.js | 92 ++++-------------- src/pure.js | 105 ++++----------------- 5 files changed, 61 insertions(+), 169 deletions(-) create mode 100644 jest.config.js diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..57494f4 --- /dev/null +++ b/jest.config.js @@ -0,0 +1,6 @@ +const config = require('kcd-scripts/jest') + +module.exports = { + ...config, + testEnvironment: 'jsdom', +} diff --git a/package.json b/package.json index 58af3eb..eb044c0 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "devDependencies": { "@testing-library/jest-dom": "^4.1.0", "angular": "^1.7.8", - "angular-mock": "^1.0.0", + "angular-mocks": "^1.7.8", "cross-env": "^6.0.0", "kcd-scripts": "^1.7.0", "npm-run-all": "^4.1.5", @@ -59,7 +59,7 @@ }, "peerDependencies": { "angular": "*", - "angular-mock": "*" + "angular-mocks": "*" }, "eslintConfig": { "extends": "./node_modules/kcd-scripts/eslint.js", diff --git a/src/__tests__/__snapshots__/render.js.snap b/src/__tests__/__snapshots__/render.js.snap index eaf4144..2c6dc42 100644 --- a/src/__tests__/__snapshots__/render.js.snap +++ b/src/__tests__/__snapshots__/render.js.snap @@ -2,11 +2,22 @@ exports[`supports fragments 1`] = ` -
- - DocumentFragment - - is pretty cool! -
+ + + +
+ + + + DocumentFragment + + is pretty cool! + +
+ + +
`; diff --git a/src/__tests__/render.js b/src/__tests__/render.js index 28d7f9e..b338c7c 100644 --- a/src/__tests__/render.js +++ b/src/__tests__/render.js @@ -1,89 +1,31 @@ -import React from 'react' -import ReactDOM from 'react-dom' +import angular from 'angular' +import 'angular-mocks' import {render} from '../' -test('renders div into document', () => { - const ref = React.createRef() - const {container} = render(
) - expect(container.firstChild).toBe(ref.current) +beforeEach(() => { + angular.module('atl', []) + angular.mock.module('atl') }) -test('works great with react portals', () => { - class MyPortal extends React.Component { - constructor(...args) { - super(...args) - this.portalNode = document.createElement('div') - this.portalNode.dataset.testid = 'my-portal' - } - componentDidMount() { - document.body.appendChild(this.portalNode) - } - componentWillUnmount() { - this.portalNode.parentNode.removeChild(this.portalNode) - } - render() { - return ReactDOM.createPortal( - , - this.portalNode, - ) - } - } - - function Greet({greeting, subject}) { - return ( -
- - {greeting} {subject} - -
- ) - } - - const {unmount, getByTestId, getByText} = render() - expect(getByText('Hello World')).toBeInTheDocument() - const portalNode = getByTestId('my-portal') - expect(portalNode).toBeInTheDocument() - unmount() - expect(portalNode).not.toBeInTheDocument() +test('renders div into document', () => { + const {container} = render(`
`) + expect(container.firstChild.id).toBe('child') }) test('returns baseElement which defaults to document.body', () => { - const {baseElement} = render(
) + const {baseElement} = render(`
`) expect(baseElement).toBe(document.body) }) test('supports fragments', () => { - class Test extends React.Component { - render() { - return ( -
- DocumentFragment is pretty cool! -
- ) - } - } - - const {asFragment} = render() - expect(asFragment()).toMatchSnapshot() -}) - -test('renders options.wrapper around node', () => { - const WrapperComponent = ({children}) => ( -
{children}
- ) - - const {container, getByTestId} = render(
, { - wrapper: WrapperComponent, + angular.module('atl').component('atlFragment', { + template: ` +
+ DocumentFragment is pretty cool! +
+ `, }) - expect(getByTestId('wrapper')).toBeInTheDocument() - expect(container.firstChild).toMatchInlineSnapshot(` -
-
-
-`) + const {asFragment} = render(``) + expect(asFragment()).toMatchSnapshot() }) diff --git a/src/pure.js b/src/pure.js index 1b1838b..589e20c 100644 --- a/src/pure.js +++ b/src/pure.js @@ -1,35 +1,14 @@ -import React from 'react' -import ReactDOM from 'react-dom' +import angular from 'angular' +import 'angular-mocks' import { getQueriesForElement, prettyDOM, fireEvent as dtlFireEvent, - configure as configureDTL, } from '@testing-library/dom' -import act, {asyncAct} from './act-compat' - -configureDTL({ - asyncWrapper: async cb => { - let result - await asyncAct(async () => { - result = await cb() - }) - return result - }, -}) const mountedContainers = new Set() -function render( - ui, - { - container, - baseElement = container, - queries, - hydrate = false, - wrapper: WrapperComponent, - } = {}, -) { +function render(ui, {container, baseElement = container, queries} = {}) { if (!baseElement) { // default to document.body instead of documentElement to avoid output of potentially-large // head elements (such as JSS style blocks) in debug output @@ -44,18 +23,19 @@ function render( // they're passing us a custom container or not. mountedContainers.add(container) - const wrapUiIfNeeded = innerElement => - WrapperComponent - ? React.createElement(WrapperComponent, null, innerElement) - : innerElement + let $scope + angular.mock.inject([ + '$compile', + '$rootScope', + ($compile, $rootScope) => { + $scope = $rootScope.$new() - act(() => { - if (hydrate) { - ReactDOM.hydrate(wrapUiIfNeeded(ui), container) - } else { - ReactDOM.render(wrapUiIfNeeded(ui), container) - } - }) + const element = $compile(ui)($scope)[0] + container.appendChild(element) + + $scope.$digest() + }, + ]) return { container, @@ -66,12 +46,6 @@ function render( el.forEach(e => console.log(prettyDOM(e))) : // eslint-disable-next-line no-console, console.log(prettyDOM(el)), - unmount: () => ReactDOM.unmountComponentAtNode(container), - rerender: rerenderUi => { - render(wrapUiIfNeeded(rerenderUi), {container, baseElement}) - // Intentionally do not return anything to avoid unnecessarily complicating the API. - // folks can use all the same utilities we return in the first place that are bound to the container - }, asFragment: () => { /* istanbul ignore if (jsdom limitation) */ if (typeof document.createRange === 'function') { @@ -84,6 +58,7 @@ function render( template.innerHTML = container.innerHTML return template.content }, + $scope, ...getQueriesForElement(baseElement, queries), } } @@ -95,63 +70,21 @@ function cleanup() { // maybe one day we'll expose this (perhaps even as a utility returned by render). // but let's wait until someone asks for it. function cleanupAtContainer(container) { - ReactDOM.unmountComponentAtNode(container) if (container.parentNode === document.body) { document.body.removeChild(container) } mountedContainers.delete(container) } -// react-testing-library's version of fireEvent will call -// dom-testing-library's version of fireEvent wrapped inside -// an "act" call so that after all event callbacks have been -// been called, the resulting useEffect callbacks will also -// be called. function fireEvent(...args) { - let returnValue - act(() => { - returnValue = dtlFireEvent(...args) - }) - return returnValue + return dtlFireEvent(...args) } Object.keys(dtlFireEvent).forEach(key => { fireEvent[key] = (...args) => { - let returnValue - act(() => { - returnValue = dtlFireEvent[key](...args) - }) - return returnValue + return dtlFireEvent[key](...args) } }) -// React event system tracks native mouseOver/mouseOut events for -// running onMouseEnter/onMouseLeave handlers -// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/EnterLeaveEventPlugin.js#L24-L31 -fireEvent.mouseEnter = fireEvent.mouseOver -fireEvent.mouseLeave = fireEvent.mouseOut - -fireEvent.select = (node, init) => { - // React tracks this event only on focused inputs - node.focus() - - // React creates this event when one of the following native events happens - // - contextMenu - // - mouseUp - // - dragEnd - // - keyUp - // - keyDown - // so we can use any here - // @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/SelectEventPlugin.js#L203-L224 - fireEvent.keyUp(node, init) -} - -// just re-export everything from dom-testing-library export * from '@testing-library/dom' -export {render, cleanup, fireEvent, act} - -// NOTE: we're not going to export asyncAct because that's our own compatibility -// thing for people using react-dom@16.8.0. Anyone else doesn't need it and -// people should just upgrade anyway. - -/* eslint func-name-matching:0 */ +export {render, cleanup, fireEvent}