Skip to content

Commit

Permalink
Added unit tests for injectHookVariableNames
Browse files Browse the repository at this point in the history
  • Loading branch information
saphal1998 committed Dec 14, 2020
1 parent a06f8d9 commit ff7b6c3
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/**
* @flow
*/

import {parse} from '@babel/parser';
import {
getHookVariableName,
getPotentialHookDeclarationsFromAST,
checkNodeLocation,
isConfirmedHookDeclaration,
getFilteredHookASTNodes,
} from 'react-devtools-shared/src/utils';

describe('injectHookVariableNamesFunction', () => {
it('should identify variable names in destructed syntax', async done => {
const jsxCode = `
const Example = () => {
const [count, setCount] = React.useState(1);
return count;
};
`;

const ast = parse(jsxCode, {
sourceType: 'unambiguous',
plugins: ['jsx', 'typescript'],
});
const hookAstNodes = getPotentialHookDeclarationsFromAST(ast);

// Only one hook node is present in the source code
expect(hookAstNodes).toHaveLength(1);

const hookName = getHookVariableName(hookAstNodes[0]);
expect(hookName).toBe('count');
done();
});

it('should identify variable names in direct assignment', async done => {
const jsxCode = `
const Example = () => {
const count = React.useState(1);
return count;
};
`;

const ast = parse(jsxCode, {
sourceType: 'unambiguous',
plugins: ['jsx', 'typescript'],
});
const hookAstNodes = getPotentialHookDeclarationsFromAST(ast);

// Only one hook node is present in the source code
expect(hookAstNodes).toHaveLength(1);

const hookName = getHookVariableName(hookAstNodes[0]);
expect(hookName).toBe('count');
done();
});

it('should identify variable names in case of destructured assignment', async done => {
const jsxCode = `
const Example = () => {
const count = React.useState(1);
const [x, setX] = count;
return count;
};
`;

const ast = parse(jsxCode, {
sourceType: 'unambiguous',
plugins: ['jsx', 'typescript'],
});
const hookAstNodes = getPotentialHookDeclarationsFromAST(ast);
// This line number corresponds to where the hook is present
const lineNumber = 3;

// Isolate the Hook AST Node
const potentialReactHookASTNode = hookAstNodes.find(
node =>
checkNodeLocation(node, lineNumber) && isConfirmedHookDeclaration(node),
);
// Find the nodes that are associated with the React Hook found - in this case we obtain the [x, setX] line
const nodesAssociatedWithReactHookASTNode = getFilteredHookASTNodes(
potentialReactHookASTNode,
hookAstNodes,
'example-app',
new Map(),
);

// Only one node should be found here
expect(nodesAssociatedWithReactHookASTNode).toHaveLength(1);
const relatedNode = nodesAssociatedWithReactHookASTNode[0];

// The [x,setX] destructuring is on line 4
expect(relatedNode.node.loc.start.line).toBe(4);

const hookName = getHookVariableName(relatedNode);
expect(hookName).toBe('x');
done();
});

it('should identify variable names for multiple hooks in one app', async done => {
const jsxCode = `
const Example = () => {
const count = React.useState(1);
const [x, setX] = count;
const [count1, setCount1] = useState(0);
return count;
};
`;

done();
});
});
12 changes: 6 additions & 6 deletions packages/react-devtools-shared/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -975,7 +975,7 @@ function modifyHooksToAddVariableNames(hookLog: HooksTree, sourceMaps: Downloade
return hook;
}
// nodesAssociatedWithReactHookASTNode could directly be a used to obtain the hook variable name
// nodesAssociatedWithReactHookASTNode could directly be used to obtain the hook variable name
// depending on the type of potentialReactHookASTNode
try {
const nodesAssociatedWithReactHookASTNode: NodePath[] = getFilteredHookASTNodes(potentialReactHookASTNode, potentialHooksFound, source, potentialHooksOfFile);
Expand Down Expand Up @@ -1065,7 +1065,7 @@ function getASTFromSourceFileContents(fileContents: string) {
* @param {NodePath} path
* @return {boolean}
*/
function isConfirmedHookDeclaration(path: NodePath): boolean {
export function isConfirmedHookDeclaration(path: NodePath): boolean {
const node = path.node.init;
if (node.type !== AST_NODE_TYPES.CALL_EXPRESSION) {
return false;
Expand All @@ -1080,7 +1080,7 @@ function isConfirmedHookDeclaration(path: NodePath): boolean {
* @param {NodePath} path AST NodePath
* @param {number} line The line number provided by source maps
*/
function checkNodeLocation(path: NodePath, line: number): boolean {
export function checkNodeLocation(path: NodePath, line: number): boolean {
const locationOfNode = path.node.loc;
return (line === locationOfNode.start.line);
}
Expand All @@ -1101,7 +1101,7 @@ function isStateOrReducerHook(path: NodePath): boolean {
* @param {File} sourceAST
* @return {NodePath[]}
*/
function getPotentialHookDeclarationsFromAST(sourceAST: File): NodePath[] {
export function getPotentialHookDeclarationsFromAST(sourceAST: File): NodePath[] {
const potentialHooksFound: NodePath[] = [];
traverse(sourceAST, {
enter(path) {
Expand Down Expand Up @@ -1205,7 +1205,7 @@ function nodeContainsHookVariableName(hookNode: NodePath): boolean {
* @param {Map<string, Array<NodePath>>} potentialHooksOfFile
* @return {NodePath[]} nodesAssociatedWithReactHookASTNode
*/
function getFilteredHookASTNodes(potentialReactHookASTNode: NodePath, potentialHooksFound: NodePath[], source: string, potentialHooksOfFile: Map<string, Array<NodePath>>): NodePath[] {
export function getFilteredHookASTNodes(potentialReactHookASTNode: NodePath, potentialHooksFound: NodePath[], source: string, potentialHooksOfFile: Map<string, Array<NodePath>>): NodePath[] {
// Remove targetHook from potentialHooks array since its purpose is served.
// Also to clean the potentialHooks array for further filtering member nodes down the line.
const hookIdx = potentialHooksFound.indexOf(potentialReactHookASTNode);
Expand Down Expand Up @@ -1355,7 +1355,7 @@ function filterMemberWithHookVariableName(hook: NodePath): boolean {
* @param {NodePath} hook The AST Node Path for the concerned hook
* @return {string} The variable name to be injected
*/
function getHookVariableName(hook: NodePath, isCustomHook: boolean = false): string {
export function getHookVariableName(hook: NodePath, isCustomHook: boolean = false): string {
const nodeType = hook.node.id.type;
switch (nodeType) {
case AST_NODE_TYPES.ARRAY_PATTERN:
Expand Down

0 comments on commit ff7b6c3

Please sign in to comment.