From 71a55ce18d76aba06efc428f1193fb22ca817dd8 Mon Sep 17 00:00:00 2001 From: yuche Date: Tue, 16 Jan 2018 20:55:28 +0800 Subject: [PATCH] test: add some nerv-test-utils test case --- browsers/karma.js | 1 + .../__tests__/__snapshots__/test.spec.js.snap | 42 +++ .../nerv-test-utils/__tests__/test.spec.js | 262 ++++++++++++++++++ packages/nerv-test-utils/package.json | 3 +- packages/nerv-test-utils/rollup.config.js | 100 ++----- packages/nerv-test-utils/src/index.ts | 60 ++-- packages/nerv/src/render.ts | 2 +- 7 files changed, 354 insertions(+), 116 deletions(-) create mode 100644 packages/nerv-test-utils/__tests__/__snapshots__/test.spec.js.snap create mode 100644 packages/nerv-test-utils/__tests__/test.spec.js diff --git a/browsers/karma.js b/browsers/karma.js index 0432cade..b269313b 100644 --- a/browsers/karma.js +++ b/browsers/karma.js @@ -1,5 +1,6 @@ /* eslint-disable */ window.it.skip = xit; +window.it.skipKarma = xit; window.describe.skip = xdescribe; // disable the test suites in IE8 window.describe.ie = document.all && !document.addEventListener diff --git a/packages/nerv-test-utils/__tests__/__snapshots__/test.spec.js.snap b/packages/nerv-test-utils/__tests__/__snapshots__/test.spec.js.snap new file mode 100644 index 00000000..b94883d7 --- /dev/null +++ b/packages/nerv-test-utils/__tests__/__snapshots__/test.spec.js.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`ReactTestUtils Simulate should have locally attached media events 1`] = ` +Array [ + "animationEnd", + "animationIteration", + "animationStart", + "blur", + "change", + "click", + "contextMenu", + "doubleClick", + "drag", + "dragEnd", + "dragEnter", + "dragExit", + "dragLeave", + "dragOver", + "dragStart", + "drop", + "error", + "focus", + "input", + "keyDown", + "keyPress", + "keyUp", + "load", + "mouseDown", + "mouseEnter", + "mouseLeave", + "mouseMove", + "mouseOut", + "mouseOver", + "mouseUp", + "submit", + "touchCancel", + "touchEnd", + "touchMove", + "touchStart", + "transitionEnd", +] +`; diff --git a/packages/nerv-test-utils/__tests__/test.spec.js b/packages/nerv-test-utils/__tests__/test.spec.js new file mode 100644 index 00000000..e7aebb08 --- /dev/null +++ b/packages/nerv-test-utils/__tests__/test.spec.js @@ -0,0 +1,262 @@ +import React from 'nervjs' +import { renderToString } from 'nerv-server' +import ReactTestUtils from '../src' +// eslint-disable-next-line +const { createElement } = React + +function getTestDocument (markup) { + const doc = document.implementation.createHTMLDocument('') + doc.open() + doc.write( + markup || 'test doc' + ) + doc.close() + return doc +} + +const isNode = !!( + typeof process !== 'undefined' && + process.versions && + process.versions.node +) + +if (isNode) it.skipKarma = it + +describe('ReactTestUtils', () => { + it.skipKarma('Simulate should have locally attached media events', () => { + expect(Object.keys(ReactTestUtils.Simulate).sort()).toMatchSnapshot() + }) + + it.skipKarma( + 'gives Jest mocks a passthrough implementation with mockComponent()', + () => { + class MockedComponent extends React.Component { + render () { + throw new Error('Should not get here.') + } + } + // This is close enough to what a Jest mock would give us. + MockedComponent.prototype.render = jest.fn() + + // Patch it up so it returns its children. + ReactTestUtils.mockComponent(MockedComponent) + + const container = document.createElement('div') + React.render(Hello, container) + expect(container.textContent).toBe('Hello') + } + ) + + it('can scryRenderedComponentsWithType', () => { + class Child extends React.Component { + render () { + return null + } + } + class Wrapper extends React.Component { + render () { + return ( +
+ +
+ ) + } + } + + const renderedComponent = ReactTestUtils.renderIntoDocument() + const scryResults = ReactTestUtils.scryRenderedComponentsWithType( + renderedComponent, + Child + ) + expect(scryResults.length).toBe(1) + }) + + it('can scryRenderedDOMComponentsWithClass with TextComponent', () => { + class Wrapper extends React.Component { + render () { + return ( +
+ Hello Jim +
+ ) + } + } + + const renderedComponent = ReactTestUtils.renderIntoDocument() + // debugger + const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass( + renderedComponent, + 'NonExistentClass' + ) + expect(scryResults.length).toBe(0) + }) + + it('can scryRenderedDOMComponentsWithClass with className contains \\n', () => { + class Wrapper extends React.Component { + render () { + return ( +
+ Hello Jim +
+ ) + } + } + + const renderedComponent = ReactTestUtils.renderIntoDocument() + const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass( + renderedComponent, + 'x' + ) + expect(scryResults.length).toBe(1) + }) + + it('can scryRenderedDOMComponentsWithClass with multiple classes', () => { + class Wrapper extends React.Component { + render () { + return ( +
+ Hello Jim +
+ ) + } + } + + const renderedComponent = ReactTestUtils.renderIntoDocument() + const scryResults1 = ReactTestUtils.scryRenderedDOMComponentsWithClass( + renderedComponent, + 'x y' + ) + expect(scryResults1.length).toBe(1) + + const scryResults2 = ReactTestUtils.scryRenderedDOMComponentsWithClass( + renderedComponent, + 'x z' + ) + expect(scryResults2.length).toBe(1) + + const scryResults3 = ReactTestUtils.scryRenderedDOMComponentsWithClass( + renderedComponent, + ['x', 'y'] + ) + expect(scryResults3.length).toBe(1) + + expect(scryResults1[0]).toBe(scryResults2[0]) + expect(scryResults1[0]).toBe(scryResults3[0]) + + const scryResults4 = ReactTestUtils.scryRenderedDOMComponentsWithClass( + renderedComponent, + ['x', 'a'] + ) + expect(scryResults4.length).toBe(0) + + const scryResults5 = ReactTestUtils.scryRenderedDOMComponentsWithClass( + renderedComponent, + ['x a'] + ) + expect(scryResults5.length).toBe(0) + }) + + it.skip('traverses children in the correct order', () => { + class Wrapper extends React.Component { + render () { + return
{this.props.children}
+ } + } + + const container = document.createElement('div') + React.render( + + {null} +
purple
+
, + container + ) + const tree = React.render( + +
orange
+
purple
+
, + container + ) + + const log = [] + ReactTestUtils.findAllInRenderedTree(tree, function (child) { + if (ReactTestUtils.isDOMComponent(child)) { + log.push(React.findDOMNode(child).textContent) + } + }) + + // Should be document order, not mount order (which would be purple, orange) + expect(log).toEqual(['orangepurple', 'orange', 'purple']) + }) + + it('should support injected wrapper components as DOM components', () => { + const injectedDOMComponents = [ + 'button', + 'form', + 'iframe', + 'img', + 'input', + 'option', + 'select', + 'textarea' + ] + + injectedDOMComponents.forEach(function (type) { + const testComponent = ReactTestUtils.renderIntoDocument( + React.createElement(type) + ) + expect(testComponent.tagName).toBe(type.toUpperCase()) + expect(ReactTestUtils.isDOMComponent(testComponent)).toBe(true) + }) + + // Full-page components (html, head, body) can't be rendered into a div + // directly... + class Root extends React.Component { + render () { + return ( + + + hello + + hello, world + + ) + } + } + + const markup = renderToString() + const testDocument = getTestDocument(markup) + const component = React.hydrate(, testDocument) + + expect(component.refs.html.tagName).toBe('HTML') + expect(component.refs.head.tagName).toBe('HEAD') + expect(component.refs.body.tagName).toBe('BODY') + expect(ReactTestUtils.isDOMComponent(component.refs.html)).toBe(true) + expect(ReactTestUtils.isDOMComponent(component.refs.head)).toBe(true) + expect(ReactTestUtils.isDOMComponent(component.refs.body)).toBe(true) + }) + + it('can scry with stateless components involved', () => { + const Stateless = () => ( +
+
+
+ ) + + class SomeComponent extends React.Component { + render () { + return ( +
+ +
+
+ ) + } + } + + const inst = ReactTestUtils.renderIntoDocument() + const hrs = ReactTestUtils.scryRenderedDOMComponentsWithTag(inst, 'hr') + expect(hrs.length).toBe(2) + }) +}) diff --git a/packages/nerv-test-utils/package.json b/packages/nerv-test-utils/package.json index a5fc0088..5f12e3a3 100644 --- a/packages/nerv-test-utils/package.json +++ b/packages/nerv-test-utils/package.json @@ -10,7 +10,8 @@ "nervjs": "1.2.4", "simulate-event": "^1.4.0", "nerv-shared": "1.2.3", - "nerv-utils": "1.2.4-beta.1" + "nerv-utils": "1.2.4-beta.1", + "nerv-server": "1.2.3" }, "repository": { "type": "git", diff --git a/packages/nerv-test-utils/rollup.config.js b/packages/nerv-test-utils/rollup.config.js index bcd2e269..39d215b7 100644 --- a/packages/nerv-test-utils/rollup.config.js +++ b/packages/nerv-test-utils/rollup.config.js @@ -1,96 +1,36 @@ const typescript = require('rollup-plugin-typescript2') -const resolve = require('rollup-plugin-node-resolve') -const buble = require('rollup-plugin-buble') -const uglify = require('rollup-plugin-uglify') -const optimizeJs = require('optimize-js') -const babel = require('rollup-plugin-babel') -const es3 = require('rollup-plugin-es3') +const alias = require('rollup-plugin-alias') const { join } = require('path') -const cwd = __dirname - -const optJSPlugin = { - name: 'optimizeJs', - transformBundle (code) { - return optimizeJs(code, { - sourceMap: false, - sourceType: 'module' - }) - } +function resolver (path) { + return join(__dirname, path) } -const babelPlugin = babel({ - babelrc: false, - presets: ['es3'] -}) -const uglifyPlugin = uglify({ - compress: { - // compress options - booleans: true, - dead_code: true, - drop_debugger: true, - unused: true - }, - ie8: true, - parse: { - // parse options - html5_comments: false, - shebang: false - }, - sourceMap: false, - toplevel: false, - warnings: false -}) -const baseConfig = { - input: join(cwd, 'src/index.ts'), +const cwd = process.cwd() +module.exports = { + input: resolver('src/index.ts'), output: [ { - file: join(cwd, 'dist/index.js'), + sourcemap: true, format: 'cjs', - sourcemap: true + file: resolver('dist/index.js') }, { - file: join(cwd, 'dist/nerv-test-utils.js'), - format: 'umd', - name: 'NervTestUtils', - sourcemap: true + sourcemap: true, + format: 'es', + file: resolver('dist/index.esm.js') } ], external: ['nervjs'], + globals: { + nervjs: 'Nerv' + }, plugins: [ - resolve(), + alias({ + 'nervjs': join(cwd, '../nerv/dist/index'), + 'nerv-shared': join(cwd, '../nerv-shared/dist/index'), + 'nerv-utils': join(cwd, '../nerv-utils/dist/index') + }), typescript({ typescript: require('typescript') - }), - buble(), - babelPlugin, - es3(['defineProperty', 'freeze']) + }) ] } -const esmConfig = Object.assign({}, baseConfig, { - output: Object.assign({}, baseConfig.output, { - sourcemap: true, - format: 'es', - file: join(cwd, 'dist/index.esm.js') - }) -}) -const productionConfig = Object.assign({}, baseConfig, { - output: { - format: 'umd', - file: join(cwd, 'dist/nerv-test-utils.js'), - name: 'NervTestUtils', - sourcemap: false - }, - plugins: baseConfig.plugins.concat([uglifyPlugin, optJSPlugin]) -}) - -function rollup () { - const target = process.env.TARGET - - if (target === 'umd') { - return baseConfig - } else if (target === 'esm') { - return esmConfig - } else { - return [baseConfig, esmConfig, productionConfig] - } -} -module.exports = rollup() diff --git a/packages/nerv-test-utils/src/index.ts b/packages/nerv-test-utils/src/index.ts index f03af0de..a2916e30 100644 --- a/packages/nerv-test-utils/src/index.ts +++ b/packages/nerv-test-utils/src/index.ts @@ -1,10 +1,11 @@ -import Nerv from 'nervjs' +import React from 'nervjs' import { isValidElement, isComposite, VirtualNode, isWidget, - isVNode + isVNode, + isComponent } from 'nerv-shared' import { isString, isArray } from 'nerv-utils' // tslint:disable-next-line:no-var-requires @@ -13,7 +14,7 @@ const simulateEvents = require('simulate-event') function renderIntoDocument (instance) { const dom = document.createElement('div') document.body.appendChild(dom) - return Nerv.render(instance, dom) + return React.render(instance, dom) } // tslint:disable-next-line:max-line-length @@ -66,15 +67,15 @@ function isElement (instance) { } function isElementOfType (instance, convenienceConstructor) { - return isElement(instance) && convenienceConstructor + return isElement(instance) && convenienceConstructor === instance.type } -function isDOMComponent (instance) { - return isVNode(instance) +function isDOMComponent (inst) { + return !!(inst && inst.nodeType === 1 && inst.tagName) } -function isDOMElementOfType (instance: any, type: string): boolean { - return isDOMComponent(instance) && isString(type) && instance.type === type +function isDOMComponentOfType (instance: any, tagName: string): boolean { + return isDOMComponent(instance) && isString(tagName) && instance.tagName === tagName.toUpperCase() } function isCompositeComponent (instance) { @@ -92,10 +93,10 @@ function findAllInRenderedTree ( tree: VirtualNode, test: (vnode: VirtualNode) => boolean ) { - if (isValidElement(tree)) { + if (isValidElement(tree) || isComponent(tree)) { let result = test(tree) ? [tree] : [] let children - if (isWidget(tree)) { + if (isWidget(tree) || isComponent(tree)) { children = tree._rendered } else if (isVNode(tree)) { children = tree.children @@ -110,6 +111,8 @@ function findAllInRenderedTree ( children.forEach((child) => { result = result.concat(findAllInRenderedTree(child, test) as any) }) + } else if (isDOMComponent(children)) { + console.log(children) } return result } else { @@ -133,11 +136,10 @@ function scryRenderedDOMComponentsWithClass ( ) { return findAllInRenderedTree(tree, (instance) => { if (isVNode(instance)) { - const theClass = instance.props.className - const classList = - theClass && isString(theClass) - ? theClass.trim().split(/\s+/) - : Object.keys(theClass as object).filter(Boolean) + const theClass = instance.props.className || '' + const classList = isString(theClass) + ? theClass.trim().split(/\s+/) + : Object.keys(theClass as object).filter(Boolean) return parseClass(classNames).every( (className) => classList.indexOf(className) !== -1 ) @@ -159,7 +161,10 @@ function findRenderedDOMComponentWithClass ( function scryRenderedDOMComponentsWithTag (tree, tag: string) { return findAllInRenderedTree(tree, (instance) => { - return isDOMElementOfType(instance, tag) + if (isVNode(instance)) { + return isDOMComponentOfType(instance.dom, tag) + } + return false }) } @@ -188,19 +193,10 @@ function findRenderedComponentWithType (tree, type: string) { function mockComponent (module, mockTagName) { mockTagName = mockTagName || module.mockTagName || 'div' module.prototype.render.mockImplementation(function () { - return Nerv.createElement(mockTagName, null, this.props.children) + return React.createElement(mockTagName, null, this.props.children) }) } -function createRenderer () { - // tslint:disable-next-line:no-empty - return () => {} -} - -function batchedUpdates (cb) { - cb() -} - export { Simulate, renderIntoDocument, @@ -208,7 +204,7 @@ export { isElement, isElementOfType, isDOMComponent, - isDOMElementOfType, + isDOMComponentOfType, isCompositeComponent, isCompositeComponentWithType, findAllInRenderedTree, @@ -217,9 +213,7 @@ export { scryRenderedDOMComponentsWithTag, findRenderedComponentWithType, findRenderedDOMComponentWithClass, - findRenderedDOMComponentWithTag, - batchedUpdates, - createRenderer + findRenderedDOMComponentWithTag } export default { @@ -229,7 +223,7 @@ export default { isElement, isElementOfType, isDOMComponent, - isDOMElementOfType, + isDOMComponentOfType, isCompositeComponent, isCompositeComponentWithType, findAllInRenderedTree, @@ -238,7 +232,5 @@ export default { scryRenderedDOMComponentsWithTag, findRenderedComponentWithType, findRenderedDOMComponentWithClass, - findRenderedDOMComponentWithTag, - batchedUpdates, - createRenderer + findRenderedDOMComponentWithTag } diff --git a/packages/nerv/src/render.ts b/packages/nerv/src/render.ts index 0060f501..39c8ed9e 100644 --- a/packages/nerv/src/render.ts +++ b/packages/nerv/src/render.ts @@ -17,7 +17,7 @@ export function render ( return null } /* istanbul ignore if */ - if (!container || container.nodeType !== 1) { + if (!container) { throw new Error(`${container} should be a DOM Element`) } const lastVnode = (container as any)._component