Compare commits

...
36 Commits
Author SHA1 Message Date
bendtherules e01fc3ab0b Release v0.0.6 2019-08-29 17:37:45 +05:30
bendtherules ba2e842338 Readme - Update example with proper working code and CSB link 2019-08-29 17:36:17 +05:30
bendtherules b16012f81e Release v0.0.5 2019-08-29 17:10:43 +05:30
bendtherules 113674df2f Update package-lock 2019-08-29 17:09:25 +05:30
bendtherules 88eb62298e Release v0.0.4 2019-08-29 17:01:43 +05:30
bendtherules 8e484fc074 TODO - findAllNodes added 2019-08-28 09:01:16 +05:30
bendtherules a88e0e0f07 Readme - Update with correct import statements 2019-08-28 09:00:57 +05:30
bendtherules a1204cfc1b lib - export utils from main export 2019-08-27 22:58:47 +05:30
bendtherules ceaa1eee32 TODO - Separate out major compatibility changes into diff item 2019-08-25 17:26:28 +05:30
bendtherules 3c5ea67992 Add testcase - traverseGenerator-skipped
Some simple cases covered. Needs more thought out cases with combination
2019-08-11 22:12:23 +05:30
bendtherules 395dc281ce test.traverseGenerator-ordered - Fix test group title 2019-08-11 20:19:13 +05:30
bendtherules 3e062ef911 Add testcase - getRootFiberNodeFromDOM - without any element as input 2019-08-10 17:20:20 +05:30
bendtherules e3750e9b50 Add TODO for getRootFiberNodeFromDOM changes needed (skip-ci) 2019-08-10 17:13:40 +05:30
bendtherules e1dec601e7 Add testcases - utils.getRootFiberNodeFromDOM 2019-08-10 17:08:32 +05:30
bendtherules d6d32fd847 Add testcase - utils.doesElementContainRootFiberNode 2019-08-10 16:17:01 +05:30
bendtherules 7aff88ecb8 package.json - Remove post-commit hook 2019-08-10 13:49:28 +05:30
bendtherules 400757182f Modify lint-staged - Run prettier and eslint only on changed files 2019-08-10 13:46:21 +05:30
bendtherules 6cd41bc4bd Modify 'lint' cmd - Run eslint for jsx.tsx also 2019-08-10 13:04:17 +05:30
bendtherules 5a33b3272e utils - export doesElementContainRootFiberNode 2019-08-10 13:01:07 +05:30
bendtherules e7e1415039 Add testcases - utils.isConstructorComponentClass 2019-08-10 12:59:40 +05:30
bendtherules 7e676b0edc Add testcases - utils.isConstructorFunctionComponent 2019-08-10 12:58:46 +05:30
bendtherules 9ebc534cc3 Modify isConstructorFunctionComponent - Check type is function 2019-08-10 12:54:30 +05:30
bendtherules d7276eff9e Modify utils.isConstructor* - Allow null type as input 2019-08-10 11:26:39 +05:30
bendtherules c5adcb3152 Add testcase - utils.isConstructorHtmlLike 2019-08-10 10:49:33 +05:30
bendtherules e64d51a3e6 Modify util.isConstructorHtmlLike - Allow null typeas input 2019-08-10 10:46:42 +05:30
bendtherules 887f4b95d1 Modify utils - doesElementContainRootFiberNode also checks for _internalRoot now 2019-08-10 10:37:12 +05:30
bendtherules 39080755ef Add testcase - isNodeComponentClass 2019-08-10 09:58:15 +05:30
bendtherules 69c1d2db86 Edit test title in utils.isNodeFunctionComponent 2019-08-10 01:49:40 +05:30
bendtherules 86216d3b05 Add testcase - utils.isNodeHtmlLike - Add negative test cases where it is false 2019-08-10 01:44:39 +05:30
bendtherules 933639e0ba Add testcase - utils.isNodeFunctionComponent 2019-08-10 01:43:14 +05:30
bendtherules 91ab867f19 Update package-lock 2019-08-09 23:02:15 +05:30
bendtherules 8f29fd8eea Fix prettier - mountInEnzyme
Was missed due to non-installation of husky in this machine
2019-08-09 23:00:08 +05:30
bendtherules c3ca45928a Add testcase - utils.isNodeHtmlLike
Tests isNodeHtmlLike and isNodeNotHtmlLike
2019-08-09 22:28:02 +05:30
bendtherules 2aa7a9bbf3 Test utils - mountInEnzyme - support loading intrinsic elements 2019-08-09 22:10:08 +05:30
bendtherules fb23260efe Add testcase - findNodeByComponentRef-negative
Check cases where ref is not found in fiber tree
2019-08-09 11:54:50 +05:30
bendtherules 6f5826d398 Add testcase - findNodeByComponent-negative
Test cases where component is not found
2019-08-09 10:05:24 +05:30
21 changed files with 1127 additions and 51 deletions
+15 -15
View File
@@ -1,18 +1,18 @@
{ {
"dist/react-fiber-traverse.cjs.development.js": { "dist/react-fiber-traverse.cjs.development.js": {
"bundled": 7398, "bundled": 9674,
"minified": 3036, "minified": 4533,
"gzipped": 967 "gzipped": 1335
}, },
"dist/react-fiber-traverse.cjs.production.js": { "dist/react-fiber-traverse.cjs.production.js": {
"bundled": 7398, "bundled": 9674,
"minified": 3036, "minified": 4533,
"gzipped": 967 "gzipped": 1335
}, },
"dist/react-fiber-traverse.esm.js": { "dist/react-fiber-traverse.esm.js": {
"bundled": 7048, "bundled": 9312,
"minified": 2736, "minified": 4229,
"gzipped": 869, "gzipped": 1245,
"treeshaked": { "treeshaked": {
"rollup": { "rollup": {
"code": 49, "code": 49,
@@ -24,13 +24,13 @@
} }
}, },
"dist/react-fiber-traverse.umd.development.js": { "dist/react-fiber-traverse.umd.development.js": {
"bundled": 32560, "bundled": 34862,
"minified": 8698, "minified": 9669,
"gzipped": 3085 "gzipped": 3394
}, },
"dist/react-fiber-traverse.umd.production.js": { "dist/react-fiber-traverse.umd.production.js": {
"bundled": 32560, "bundled": 34862,
"minified": 8698, "minified": 9669,
"gzipped": 3085 "gzipped": 3394
} }
} }
+26 -16
View File
@@ -10,29 +10,39 @@
## Basic Usage ## Basic Usage
```ts ```ts
import { render } from 'react-dom' import React from "react";
import { render } from "react-dom";
import { findNodeByComponentName } from 'react-fiber-traverse' import { findNodeByComponentName, Utils } from "react-fiber-traverse";
import { getRootFiberNodeFromDOM } from 'react-fiber-traverse/utils'
const rootElement = document.getElementById('root'); // Sample component
render(<App>, 'root'); // Say, if SomeComponentName looks like this -
function SomeComponentName() {
return <div>Some text</div>;
}
const rootFiberNode = getRootFiberNodeFromDOM(rootElement); // Render component
const rootElement = document.getElementById("root");
render(<SomeComponentName />, rootElement);
// Get root node
const rootFiberNode = Utils.getRootFiberNodeFromDOM(rootElement);
// Get component node
const someFiberNode = findNodeByComponentName(
rootFiberNode,
"SomeComponentName"
); // <- returns FiberNode for first usage of 'SomeComponentName'
console.log(someFiberNode.child.stateNode); // <- returns reference to the div
console.log(someFiberNode.child.stateNode.innerText); // <- returns 'Some text'
const someFiberNode = findNodeByComponentName(rootFiberNode, 'SomeComponentName'); // <- returns FiberNode for first usage of 'SomeComponentName'
// Say, if SomeComponentName renders (<div>Some text</div>)
someFiberNode.stateNode // <- returns reference to the div
someFiberNode.stateNode.innerText // <- returns 'Some text'
``` ```
## Live Examples ## Live Examples
- [Basic Usage](https://codesandbox.io/) - [Basic Usage](https://codesandbox.io/s/react-example-x82zk?expanddevtools=1&fontsize=14)
- [API Example](https://codesandbox.io/) <!--
- [UMD Build (Development)](https://codesandbox.io/)
- [UMD Build (Production)](https://codesandbox.io/)
## API ## API
**Props** **Props**
@@ -43,7 +53,7 @@ someFiberNode.stateNode.innerText // <- returns 'Some text'
**Example** **Example**
```jsx ```jsx
``` ``` -->
## Installation ## Installation
+12
View File
@@ -1,4 +1,16 @@
# TODOs
1. Write docs and examples 1. Write docs and examples
2. Reduce testcase boilerplate code for similar tests 2. Reduce testcase boilerplate code for similar tests
3. Publish to npm 3. Publish to npm
4. Build some demo 4. Build some demo
5. Add `findAllNodes` helpers which searches all usages of the components - by name or class
6. Fix getRootFiberNodeFromDOM and related helpers -
- .\_internalRoot is not present is 16.0.0 but present in 16.9.0
- actual node is available 2 levels nested inside this root node
- add testcase to run across all versions of React 16.0.0 (dev+prod) and above (because internals change)
7. Make it work with older React versions
8. Make it work with non React-DOM renderers
+10 -1
View File
@@ -1,3 +1,12 @@
module.exports = { module.exports = {
"*.{js,jsx,ts,tsx}": () => "run-s check:* lint" "*.{js,jsx,ts,tsx}": filenames => {
// Escape filenames and join by space
const joinedFilenames = filenames.map(str => `"${str}"`).join(" ");
return [
"run-s check:!(format)",
`npm run check:format -- ${joinedFilenames}`,
`npm run _lint -- ${joinedFilenames}`
];
// `run-s check:!(format) "check:format -- ${joinedFilenames}" "_lint -- ${joinedFilenames}"`;
}
}; };
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "react-fiber-traverse", "name": "react-fiber-traverse",
"version": "0.0.3", "version": "0.0.6",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
+4 -4
View File
@@ -1,6 +1,6 @@
{ {
"name": "react-fiber-traverse", "name": "react-fiber-traverse",
"version": "0.0.3", "version": "0.0.6",
"description": "Traverse and other utils on top of React fiber tree", "description": "Traverse and other utils on top of React fiber tree",
"main": "dist/index.js", "main": "dist/index.js",
"module": "dist/react-fiber-traverse.esm.js", "module": "dist/react-fiber-traverse.esm.js",
@@ -24,7 +24,8 @@
"compile": "tsc -p tsconfig.base.json", "compile": "tsc -p tsconfig.base.json",
"docs": "npx typedoc ./src/", "docs": "npx typedoc ./src/",
"format": "prettier --write \"**/*.{js,ts,tsx}\"", "format": "prettier --write \"**/*.{js,ts,tsx}\"",
"lint": "eslint . --ext .js,.ts", "_lint": "eslint --ext .js,.ts,.jsx,.tsx",
"lint": "npm run _lint -- .",
"postbundle": "del compiled && cp-cli ./index.js ./dist/index.js", "postbundle": "del compiled && cp-cli ./index.js ./dist/index.js",
"postversion": "git push && git push --tags && npm publish", "postversion": "git push && git push --tags && npm publish",
"release": "npm version -m 'Release v%s'", "release": "npm version -m 'Release v%s'",
@@ -41,8 +42,7 @@
}, },
"husky": { "husky": {
"hooks": { "hooks": {
"pre-commit": "lint-staged", "pre-commit": "lint-staged"
"post-commit": "npm run fix:format"
} }
}, },
"repository": { "repository": {
+3 -1
View File
@@ -4,11 +4,13 @@ import {
findNodeByComponentRef, findNodeByComponentRef,
findNodeByComponentName findNodeByComponentName
} from "./findNode"; } from "./findNode";
import * as Utils from "./utils";
export { export {
traverse, traverse,
traverseGenerator, traverseGenerator,
findNodeByComponent, findNodeByComponent,
findNodeByComponentRef, findNodeByComponentRef,
findNodeByComponentName findNodeByComponentName,
Utils
}; };
+13 -6
View File
@@ -7,6 +7,7 @@ import {
FiberNodeDOMContainer FiberNodeDOMContainer
} from "./mocked-types"; } from "./mocked-types";
// Support null in all of these
function isNodeHtmlLike(node: FiberNode): node is FiberNodeisHTMLLike { function isNodeHtmlLike(node: FiberNode): node is FiberNodeisHTMLLike {
return typeof node.type === "string" || node.type === null; return typeof node.type === "string" || node.type === null;
} }
@@ -30,8 +31,8 @@ function isNodeComponentClass(
} }
function isConstructorHtmlLike( function isConstructorHtmlLike(
ctr: React.ElementType ctr: React.ElementType | null
): ctr is Exclude<React.ElementType, React.ComponentType> { ): ctr is Exclude<React.ElementType | null, React.ComponentType> {
if (typeof ctr === "string" || ctr === null) { if (typeof ctr === "string" || ctr === null) {
return true; return true;
} }
@@ -39,7 +40,7 @@ function isConstructorHtmlLike(
} }
function isConstructorComponentClass( function isConstructorComponentClass(
ctr: React.ElementType ctr: React.ElementType | null
): ctr is React.ComponentClass { ): ctr is React.ComponentClass {
if (isConstructorHtmlLike(ctr)) { if (isConstructorHtmlLike(ctr)) {
return false; return false;
@@ -56,15 +57,20 @@ function isConstructorComponentClass(
} }
function isConstructorFunctionComponent( function isConstructorFunctionComponent(
ctr: React.ElementType ctr: React.ElementType | null
): ctr is React.FunctionComponent { ): ctr is React.FunctionComponent {
return !isConstructorComponentClass(ctr); return typeof ctr === "function" && !isConstructorComponentClass(ctr);
} }
function doesElementContainRootFiberNode( function doesElementContainRootFiberNode(
element: Element element: Element
): element is FiberNodeDOMContainer { ): element is FiberNodeDOMContainer {
return element.hasOwnProperty("_reactRootContainer"); return (
element.hasOwnProperty("_reactRootContainer") &&
(element as (Element & {
_reactRootContainer: any;
}))._reactRootContainer.hasOwnProperty("_internalRoot")
);
} }
/** /**
@@ -101,5 +107,6 @@ export {
isConstructorHtmlLike, isConstructorHtmlLike,
isConstructorComponentClass, isConstructorComponentClass,
isConstructorFunctionComponent, isConstructorFunctionComponent,
doesElementContainRootFiberNode,
getRootFiberNodeFromDOM getRootFiberNodeFromDOM
}; };
+177
View File
@@ -0,0 +1,177 @@
import * as React from "react";
// Import stuff from src
import { findNodeByComponent } from "../src";
import { mountAndGetRootNode } from "./utils/mountInEnzyme";
import getWrappedComponent from "./utils/getWrappedComponent";
// Import test helpers and sample components
import CDepth1 from "./sample-components/depth-1-simple";
import FnDepth1 from "./sample-components/depth-1-fn-simple";
import { createClassComponent } from "./utils/createComponent";
describe("findNodeByComponent", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
document.body.removeChild(container);
});
describe("missing", () => {
it("should not work for top-level class with same component name", () => {
const WrappedC = getWrappedComponent(CDepth1);
const rootNode = mountAndGetRootNode(WrappedC, container);
const FakeCDepth1 = createClassComponent(CDepth1.name);
const found = findNodeByComponent(rootNode, FakeCDepth1);
expect(found).toBeFalsy();
expect(found).toBe(null);
});
it("should not work for top-level class with different component name", () => {
const WrappedC = getWrappedComponent(CDepth1);
const rootNode = mountAndGetRootNode(WrappedC, container);
const FakeCDepth1 = createClassComponent("randomname");
const found = findNodeByComponent(rootNode, FakeCDepth1);
expect(found).toBeFalsy();
expect(found).toBe(null);
});
it("should not work for 2nd-level class with same component name", () => {
class C1 extends React.Component {
render() {
return <C2 />;
}
}
class C2 extends React.Component {
render() {
return <div>C2 here</div>;
}
}
const WrappedC = getWrappedComponent(C1);
const rootNode = mountAndGetRootNode(WrappedC, container);
const FakeC2 = createClassComponent(C2.name);
const found = findNodeByComponent(rootNode, FakeC2);
expect(found).toBeFalsy();
expect(found).toBe(null);
});
it("should not work for 2nd-level class with different component name", () => {
class C1 extends React.Component {
render() {
return <C2 />;
}
}
class C2 extends React.Component {
render() {
return <div>C2 here</div>;
}
}
const WrappedC = getWrappedComponent(C1);
const rootNode = mountAndGetRootNode(WrappedC, container);
const FakeC2 = createClassComponent("randomName");
const found = findNodeByComponent(rootNode, FakeC2);
expect(found).toBeFalsy();
expect(found).toBe(null);
});
it("should not work for top-level function with same function name", () => {
const WrappedC = getWrappedComponent(FnDepth1);
const rootNode = mountAndGetRootNode(WrappedC, container);
const FakeFnDepth1 = createClassComponent(FnDepth1.name);
const found = findNodeByComponent(rootNode, FakeFnDepth1);
expect(found).toBeFalsy();
expect(found).toBe(null);
});
it("should not work for top-level function with different function name", () => {
const WrappedC = getWrappedComponent(FnDepth1);
const rootNode = mountAndGetRootNode(WrappedC, container);
const FakeFnDepth1 = createClassComponent("randomName");
const found = findNodeByComponent(rootNode, FakeFnDepth1);
expect(found).toBeFalsy();
expect(found).toBe(null);
});
it("should not work for 2nd-level function with same function name", () => {
function Fn1() {
return <Fn2 />;
}
function Fn2() {
return <div>Fn2 here</div>;
}
const WrappedC = getWrappedComponent(Fn1);
const rootNode = mountAndGetRootNode(WrappedC, container);
const FakeFn2 = createClassComponent(Fn2.name);
const found = findNodeByComponent(rootNode, FakeFn2);
expect(found).toBeFalsy();
expect(found).toBe(null);
});
it("should not work for 2nd-level function with different function name", () => {
function Fn1() {
return <Fn2 />;
}
function Fn2() {
return <div>Fn2 here</div>;
}
const WrappedC = getWrappedComponent(Fn1);
const rootNode = mountAndGetRootNode(WrappedC, container);
const FakeFn2 = createClassComponent("randomName");
const found = findNodeByComponent(rootNode, FakeFn2);
expect(found).toBeFalsy();
expect(found).toBe(null);
});
it("should not work for 5th-level function with same or diff fn name", () => {
function Fn1() {
return (
<div>
<div>
<span></span>
<Fn2></Fn2>
</div>
</div>
);
}
function Fn2() {
return <a href="google.com">Fn2 here</a>;
}
const WrappedC = getWrappedComponent(Fn1);
const rootNode = mountAndGetRootNode(WrappedC, container);
const FakeFn2Same = createClassComponent(Fn2.name);
const FakeFn2Diff = createClassComponent("randomname");
const foundSame = findNodeByComponent(rootNode, FakeFn2Same);
const foundDiff = findNodeByComponent(rootNode, FakeFn2Diff);
expect(foundSame).toBeFalsy();
expect(foundSame).toBe(null);
expect(foundDiff).toBeFalsy();
expect(foundDiff).toBe(null);
});
});
});
@@ -0,0 +1,95 @@
import * as React from "react";
// Import stuff from src
import { findNodeByComponentRef } from "../src";
import { mountAndGetRootNode } from "./utils/mountInEnzyme";
import getWrappedComponent from "./utils/getWrappedComponent";
class RandomClass extends React.Component {
render() {
return <div></div>;
}
}
// Import test helpers and sample components
describe("findNodeByComponentRef", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
document.body.removeChild(container);
});
describe("negative", () => {
it("should not work for 2nd-level class", () => {
const C2Ref = React.createRef<C2>();
class C1 extends React.Component {
render() {
return <C2 ref={C2Ref} />;
}
}
class C2 extends React.Component {
render() {
return <div>C2 here</div>;
}
}
const WrappedC = getWrappedComponent(C1);
const rootNode = mountAndGetRootNode(WrappedC, container);
if (C2Ref.current === null) {
throw new Error("Ref is not yet set");
}
const foundSame = findNodeByComponentRef(rootNode, new C2({}));
const foundDiff = findNodeByComponentRef(rootNode, new RandomClass({}));
expect(foundSame).toBeFalsy();
expect(foundSame).toBe(null);
expect(foundDiff).toBeFalsy();
expect(foundDiff).toBe(null);
});
it("should work for 5th-level class", () => {
const C2Ref = React.createRef<C2>();
class C1 extends React.Component {
render() {
return (
<div>
<div>
<span></span>
<C2 ref={C2Ref}></C2>
</div>
</div>
);
}
}
class C2 extends React.Component {
render() {
return <a href="google.com">Fn2 here</a>;
}
}
const WrappedC = getWrappedComponent(C1);
const rootNode = mountAndGetRootNode(WrappedC, container);
if (C2Ref.current === null) {
throw new Error("Ref is not yet set");
}
const foundSame = findNodeByComponentRef(rootNode, new C2({}));
const foundDiff = findNodeByComponentRef(rootNode, new RandomClass({}));
expect(foundSame).toBeFalsy();
expect(foundSame).toBe(null);
expect(foundDiff).toBeFalsy();
expect(foundDiff).toBe(null);
});
});
});
+1 -1
View File
@@ -26,7 +26,7 @@ describe("traverseGenerator", () => {
document.body.removeChild(container); document.body.removeChild(container);
}); });
describe("basic", () => { describe("ordered", () => {
it("order doesn't matter for 1 node if self is present - case 1", () => { it("order doesn't matter for 1 node if self is present - case 1", () => {
const rootNode = mountAndGetRootNode(CDepth1, container); const rootNode = mountAndGetRootNode(CDepth1, container);
+143
View File
@@ -0,0 +1,143 @@
import * as React from "react";
// Import stuff from src
import { FiberNodeForComponentClass, FiberNode } from "../src/mocked-types";
import { traverseGenerator } from "../src";
// Import test helpers and sample components
import { mountAndGetRootNode } from "./utils/mountInEnzyme";
import {
createClassComponents
// createFunctionComponents
} from "./utils/createComponent";
describe("traverseGenerator", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
document.body.removeChild(container);
});
describe("skipped", () => {
it("should traverse just top-level with skipChild", () => {
const [C2, C3, C4, C5] = createClassComponents(["C2", "C3", "C4", "C5"]);
class C1 extends React.Component {
render() {
return (
<React.Fragment>
<C2>
<C3 />
</C2>
<C4>
<C5 />
</C4>
</React.Fragment>
);
}
}
const rootNode = mountAndGetRootNode(C1, container);
const nodeIterator = traverseGenerator(rootNode);
// Consume generator
let nodes: Array<FiberNode> = [];
const genArgs = { skipChild: true };
for (
let result = nodeIterator.next();
!result.done;
result = nodeIterator.next(genArgs)
) {
const currentNode = result.value;
nodes.push(currentNode);
}
expect(nodes.length).toBe(1);
expect(
nodes.map(tmpNode => (tmpNode as FiberNodeForComponentClass).type)
).toEqual([C1]);
});
it("should traverse just first 2 levels with inner skipChild", () => {
const [C2, C3, C4, C5] = createClassComponents(["C2", "C3", "C4", "C5"]);
class C1 extends React.Component {
render() {
return (
<React.Fragment>
<C2>
<C3 />
</C2>
<C4>
<C5 />
</C4>
</React.Fragment>
);
}
}
const rootNode = mountAndGetRootNode(C1, container);
const nodeIterator = traverseGenerator(rootNode);
// Consume generator
let nodes: Array<FiberNode> = [];
const genArgs = { skipChild: true };
for (
let result = nodeIterator.next();
!result.done;
result = nodeIterator.next(nodes.length > 1 ? genArgs : undefined)
) {
const currentNode = result.value;
nodes.push(currentNode);
}
expect(nodes.length).toBe(3);
expect(
nodes.map(tmpNode => (tmpNode as FiberNodeForComponentClass).type)
).toEqual([C1, C2, C4]);
});
it("should traverse one branch correctly with skipSibling", () => {
const [C2, C3, C4, C5] = createClassComponents(["C2", "C3", "C4", "C5"]);
class C1 extends React.Component {
render() {
return (
<React.Fragment>
<C2>
<C3 />
</C2>
<C4>
<C5 />
</C4>
</React.Fragment>
);
}
}
const rootNode = mountAndGetRootNode(C1, container);
const nodeIterator = traverseGenerator(rootNode);
// Consume generator
let nodes: Array<FiberNode> = [];
const genArgs = { skipSibling: true };
for (
let result = nodeIterator.next();
!result.done;
result = nodeIterator.next(genArgs)
) {
const currentNode = result.value;
nodes.push(currentNode);
}
expect(nodes.length).toBe(3);
expect(
nodes.map(tmpNode => (tmpNode as FiberNodeForComponentClass).type)
).toEqual([C1, C2, C3]);
});
});
});
@@ -0,0 +1,77 @@
// import * as React from "react";
// Import stuff from src
import { doesElementContainRootFiberNode } from "../src/utils";
// Import test helpers and sample components
import { mountAndGetRootNode } from "./utils/mountInEnzyme";
import { createFunctionComponent } from "./utils/createComponent";
describe("traverse", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
try {
document.body.removeChild(container);
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
}
});
describe("doesElementContainRootFiberNode", () => {
it("should work for direct element", () => {
const fn1 = createFunctionComponent("fn1");
const containerInner = container.appendChild(
document.createElement("span")
);
mountAndGetRootNode(fn1, containerInner);
const resultPositive = doesElementContainRootFiberNode(containerInner);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
});
it("should not work for parent element", () => {
const fn1 = createFunctionComponent("fn1");
const containerInner = container.appendChild(
document.createElement("span")
);
mountAndGetRootNode(fn1, containerInner);
const resultNegative = doesElementContainRootFiberNode(container);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for child element", () => {
const fn1 = createFunctionComponent("fn1");
const containerInner = container.appendChild(
document.createElement("span")
);
mountAndGetRootNode(fn1, container);
const resultNegative = doesElementContainRootFiberNode(containerInner);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should work for body", () => {
const fn1 = createFunctionComponent("fn1");
mountAndGetRootNode(fn1, document.body);
const resultPositive = doesElementContainRootFiberNode(document.body);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
});
});
});
@@ -0,0 +1,94 @@
// import * as React from "react";
// Import stuff from src
import { getRootFiberNodeFromDOM } from "../src/utils";
// Import test helpers and sample components
import { mountAndGetRootNode } from "./utils/mountInEnzyme";
import { createClassComponent } from "./utils/createComponent";
describe("traverse", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
try {
document.body.removeChild(container);
} catch (error) {
if (!(error instanceof DOMException)) {
throw error;
}
}
});
describe("getRootFiberNodeFromDOM", () => {
it("should work for direct element", () => {
const C1 = createClassComponent("C1");
const containerInner = container.appendChild(
document.createElement("span")
);
mountAndGetRootNode(C1, containerInner);
const resultPositive = getRootFiberNodeFromDOM(containerInner);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).not.toBe(null);
expect((resultPositive as any).child.child.type).toBe(C1);
});
it("should work for parent element", () => {
const C1 = createClassComponent("C1");
const containerInner = container.appendChild(
document.createElement("span")
);
mountAndGetRootNode(C1, containerInner);
const resultPositive = getRootFiberNodeFromDOM(container);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).not.toBe(null);
expect((resultPositive as any).child.child.type).toBe(C1);
});
it("should work without any element - default args", () => {
const C1 = createClassComponent("C1");
const containerInner = container.appendChild(
document.createElement("span")
);
mountAndGetRootNode(C1, containerInner);
const resultPositive = getRootFiberNodeFromDOM();
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).not.toBe(null);
expect((resultPositive as any).child.child.type).toBe(C1);
});
it("should not work for child element", () => {
const C1 = createClassComponent("C1");
const containerInner = container.appendChild(
document.createElement("span")
);
mountAndGetRootNode(C1, container);
const resultNegative = getRootFiberNodeFromDOM(containerInner);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(null);
});
it("should work for body", () => {
const C1 = createClassComponent("C1");
mountAndGetRootNode(C1, document.body);
const resultPositive = getRootFiberNodeFromDOM(document.body);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).not.toBe(null);
expect((resultPositive as any).child.child.type).toBe(C1);
});
});
});
@@ -0,0 +1,74 @@
// import * as React from "react";
// Import stuff from src
import { isConstructorComponentClass } from "../src/utils";
import {
createFunctionComponent,
createClassComponent
} from "./utils/createComponent";
// Import test helpers and sample components
describe("utils", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
document.body.removeChild(container);
});
describe("isConstructorComponentClass", () => {
it("should work for class components", () => {
const C1 = createClassComponent("C1");
const resultPositive = isConstructorComponentClass(C1);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
});
it("should not work for any class", () => {
const C1 = class {};
const resultNegative = isConstructorComponentClass(C1 as any);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for any function", () => {
const fn1 = function() {};
const resultNegative = isConstructorComponentClass(fn1 as any);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for function components", () => {
const fn1 = createFunctionComponent();
const resultNegative = isConstructorComponentClass(fn1);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for string", () => {
const resultNegative = isConstructorComponentClass("div");
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for null", () => {
const resultNegative = isConstructorComponentClass(null);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
});
});
@@ -0,0 +1,74 @@
// import * as React from "react";
// Import stuff from src
import { isConstructorFunctionComponent } from "../src/utils";
import {
createFunctionComponent,
createClassComponent
} from "./utils/createComponent";
// Import test helpers and sample components
describe("utils", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
document.body.removeChild(container);
});
describe("isConstructorFunctionComponent", () => {
it("should work for any function - current limitation", () => {
const fn1 = function() {};
const resultPositive = isConstructorFunctionComponent(fn1 as any);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
});
it("should work for function components", () => {
const fn1 = createFunctionComponent();
const resultPositive = isConstructorFunctionComponent(fn1);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
});
it("should not work for string", () => {
const resultNegative = isConstructorFunctionComponent("div");
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for null", () => {
const resultNegative = isConstructorFunctionComponent(null);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should work for any class - current limitation", () => {
const C1 = class {};
const resultPositive = isConstructorFunctionComponent(C1 as any);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
});
it("should not work for class components", () => {
const C1 = createClassComponent("C1");
const resultNegative = isConstructorFunctionComponent(C1);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
});
});
+74
View File
@@ -0,0 +1,74 @@
// import * as React from "react";
// Import stuff from src
import { isConstructorHtmlLike } from "../src/utils";
import {
createFunctionComponent,
createClassComponent
} from "./utils/createComponent";
// Import test helpers and sample components
describe("utils", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
document.body.removeChild(container);
});
describe("isConstructorHtmlLike", () => {
it("should work for string", () => {
const resultPositive = isConstructorHtmlLike("div");
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
});
it("should work for null", () => {
const resultPositive = isConstructorHtmlLike(null);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
});
it("should not work for any function", () => {
const fn1 = function() {};
const resultNegative = isConstructorHtmlLike(fn1 as any);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for function components", () => {
const fn1 = createFunctionComponent();
const resultNegative = isConstructorHtmlLike(fn1);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for any class", () => {
const C1 = class {};
const resultNegative = isConstructorHtmlLike(C1 as any);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for class components", () => {
const C1 = createClassComponent("C1");
const resultNegative = isConstructorHtmlLike(C1);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
});
});
+63
View File
@@ -0,0 +1,63 @@
// import * as React from "react";
// Import stuff from src
import { isNodeComponentClass } from "../src/utils";
import { mountAndGetRootNode } from "./utils/mountInEnzyme";
import {
createFunctionComponent,
createClassComponent
} from "./utils/createComponent";
// Import test helpers and sample components
describe("utils", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
document.body.removeChild(container);
});
describe("isNodeComponentClass", () => {
it("should work for class component", () => {
const C1 = createClassComponent("C1");
const rootNode = mountAndGetRootNode(C1, container);
const result = isNodeComponentClass(rootNode);
expect(result).not.toBeFalsy();
expect(result).toBe(true);
});
it("should not work for function component", () => {
const fn1 = createFunctionComponent("fn1");
const rootNode = mountAndGetRootNode(fn1, container);
const result = isNodeComponentClass(rootNode);
expect(result).toBeFalsy();
expect(result).toBe(false);
});
it("should not work for html like component - div", () => {
const rootNode = mountAndGetRootNode("div", container);
const result = isNodeComponentClass(rootNode);
expect(result).toBeFalsy();
expect(result).toBe(false);
});
it("should not work for html like component - svg", () => {
const rootNode = mountAndGetRootNode("svg", container);
const result = isNodeComponentClass(rootNode);
expect(result).toBeFalsy();
expect(result).toBe(false);
});
});
});
@@ -0,0 +1,63 @@
// import * as React from "react";
// Import stuff from src
import { isNodeFunctionComponent } from "../src/utils";
import { mountAndGetRootNode } from "./utils/mountInEnzyme";
import {
createFunctionComponent,
createClassComponent
} from "./utils/createComponent";
// Import test helpers and sample components
describe("utils", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
document.body.removeChild(container);
});
describe("isNodeFunctionComponent", () => {
it("should work for function component", () => {
const fn1 = createFunctionComponent("fn1");
const rootNode = mountAndGetRootNode(fn1, container);
const result = isNodeFunctionComponent(rootNode);
expect(result).not.toBeFalsy();
expect(result).toBe(true);
});
it("should not work for class component", () => {
const C1 = createClassComponent("C1");
const rootNode = mountAndGetRootNode(C1, container);
const result = isNodeFunctionComponent(rootNode);
expect(result).toBeFalsy();
expect(result).toBe(false);
});
it("should not work for html like component - div", () => {
const rootNode = mountAndGetRootNode("div", container);
const result = isNodeFunctionComponent(rootNode);
expect(result).toBeFalsy();
expect(result).toBe(false);
});
it("should work for html like component - svg", () => {
const rootNode = mountAndGetRootNode("svg", container);
const result = isNodeFunctionComponent(rootNode);
expect(result).toBeFalsy();
expect(result).toBe(false);
});
});
});
+102
View File
@@ -0,0 +1,102 @@
import * as React from "react";
// Import stuff from src
import { isNodeHtmlLike, isNodeNotHtmlLike } from "../src/utils";
import { mountAndGetRootNode } from "./utils/mountInEnzyme";
import { FiberNodeForInstrinsicElement } from "../src/mocked-types";
import {
createFunctionComponent,
createClassComponent
} from "./utils/createComponent";
// Import test helpers and sample components
describe("utils", () => {
let container: HTMLDivElement;
beforeEach(() => {
container = document.body.appendChild(document.createElement("div"));
});
afterEach(() => {
document.body.removeChild(container);
});
describe("isNodeHtmlLike", () => {
it("should work for div", () => {
const rootNode = mountAndGetRootNode("div", container);
const resultPositive = isNodeHtmlLike(rootNode);
const resultNegative = isNodeNotHtmlLike(rootNode);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should work for svg root element", () => {
const rootNode = mountAndGetRootNode("svg", container);
const resultPositive = isNodeHtmlLike(rootNode);
const resultNegative = isNodeNotHtmlLike(rootNode);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should work for svg inner elements", () => {
function SomeSVG() {
return (
<svg>
<circle cx={5} />
</svg>
);
}
const rootNode = mountAndGetRootNode(SomeSVG, container);
expect(isNodeHtmlLike(rootNode)).toBeFalsy();
const circleNode = (rootNode.child as FiberNodeForInstrinsicElement)
.child as FiberNodeForInstrinsicElement;
expect(circleNode).not.toBe(null);
expect(circleNode.type).toBe("circle");
const resultPositive = isNodeHtmlLike(circleNode);
const resultNegative = isNodeNotHtmlLike(circleNode);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for function component", () => {
const fn1 = createFunctionComponent("fn1");
const rootNode = mountAndGetRootNode(fn1, container);
const resultPositive = isNodeNotHtmlLike(rootNode);
const resultNegative = isNodeHtmlLike(rootNode);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
it("should not work for class component", () => {
const C1 = createClassComponent("C1");
const rootNode = mountAndGetRootNode(C1, container);
const resultPositive = isNodeNotHtmlLike(rootNode);
const resultNegative = isNodeHtmlLike(rootNode);
expect(resultPositive).not.toBeFalsy();
expect(resultPositive).toBe(true);
expect(resultNegative).toBeFalsy();
expect(resultNegative).toBe(false);
});
});
});
+5 -5
View File
@@ -1,7 +1,7 @@
import * as React from "react"; import * as React from "react";
import { mount } from "enzyme"; import { mount } from "enzyme";
import { FiberNode, FiberNodeForComponentClass } from "../../src/mocked-types"; import { FiberNode, FiberNodeForComponentClass } from "../../src/mocked-types";
import { isConstructorFunctionComponent } from "../../src/utils"; import { isConstructorComponentClass } from "../../src/utils";
import getWrappedComponent from "./getWrappedComponent"; import getWrappedComponent from "./getWrappedComponent";
export class RootNodeNotFoundError extends Error { export class RootNodeNotFoundError extends Error {
@@ -13,14 +13,14 @@ export class RootNodeNotFoundError extends Error {
} }
export function mountAndGetRootNode( export function mountAndGetRootNode(
SomeComponent: React.ComponentType, SomeComponent: React.ElementType,
container: HTMLElement container: HTMLElement
): FiberNode { ): FiberNode {
const rootRef = React.createRef<React.Component>(); const rootRef = React.createRef<React.Component>();
let ToMount = undefined; let ToMount: React.ComponentClass | undefined = undefined;
// Wrap function components in ComponentClass to be able to set ref // Wrap function components in ComponentClass to be able to set ref
if (isConstructorFunctionComponent(SomeComponent)) { if (!isConstructorComponentClass(SomeComponent)) {
ToMount = getWrappedComponent(SomeComponent); ToMount = getWrappedComponent(SomeComponent);
} else { } else {
ToMount = SomeComponent; ToMount = SomeComponent;
@@ -41,7 +41,7 @@ export function mountAndGetRootNode(
} }
// Unwrap function components to return expected root node // Unwrap function components to return expected root node
if (isConstructorFunctionComponent(SomeComponent)) { if (!isConstructorComponentClass(SomeComponent)) {
return (rootNode as FiberNodeForComponentClass).child as FiberNode; return (rootNode as FiberNodeForComponentClass).child as FiberNode;
} else { } else {
return rootNode; return rootNode;