Skip to content

Commit 6ceda66

Browse files
testing progress
1 parent 0a972c3 commit 6ceda66

File tree

7 files changed

+86
-60
lines changed

7 files changed

+86
-60
lines changed

src/backend/__tests__/linkFiber.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function App(): JSX.Element {
3535
return <div>{fooState}</div>;
3636
}
3737

38-
xdescribe('unit test for linkFiber', () => {
38+
describe('unit test for linkFiber', () => {
3939
beforeAll(async () => {
4040
await SERVER;
4141
const args = puppeteer

src/backend/__tests__/masterTree.test.ts renamed to src/backend/__tests__/masterTree.test.tsx

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Fiber } from '../types/backendTypes';
33
import componentActionsRecord from '../models/masterState';
44
import createTree from '../controllers/createTree/createTree';
55
import Tree from '../models/tree';
6+
import React, { useState } from 'react';
67
import {
78
allowedComponentTypes,
89
nextJSDefaultComponent,
@@ -14,6 +15,17 @@ describe('master tree tests', () => {
1415
let mockFiberNode: Fiber;
1516
let mockSiblingNode: Fiber;
1617
let mockChildNode: Fiber;
18+
function MockFunctionalComponent() {
19+
const [count, setCount] = useState(0);
20+
return (
21+
<div>
22+
<button className='increment' onClick={() => setCount(count + 1)}>
23+
You clicked me {count} times.
24+
</button>
25+
</div>
26+
);
27+
}
28+
1729
beforeEach(() => {
1830
// create a mock Fiber node with relevant properties
1931
mockFiberNode = {
@@ -31,23 +43,26 @@ describe('master tree tests', () => {
3143
_debugHookTypes: [],
3244
};
3345

46+
// create a mock child Fiber node with relevant properties for class component
3447
mockChildNode = {
3548
...mockFiberNode,
3649
tag: 1,
3750
elementType: { name: 'child' },
3851
stateNode: { state: { counter: 0 }, props: { start: 0 } },
3952
};
53+
54+
// create a mock sibling Fiber node with relevant properties for class component
4055
mockSiblingNode = {
4156
...mockFiberNode,
4257
tag: 0,
43-
elementType: { name: 'sibling' },
58+
elementType: MockFunctionalComponent,
4459
memoizedState: { memoizedState: 1, queue: [{}, { state: { value: 'test' } }], next: null },
4560
};
4661
// clear the saved component actions record
4762
componentActionsRecord.clear();
4863
});
4964
describe('create tree tests', () => {
50-
it('should return a Tree if we pass in a empty fiber node', () => {
65+
xit('should return a Tree if we pass in a empty fiber node', () => {
5166
const tree = createTree(mockFiberNode);
5267
const children = tree.children;
5368

@@ -58,7 +73,7 @@ describe('master tree tests', () => {
5873
expect(children[0].state).toEqual('stateless');
5974
});
6075

61-
it('should filter out NextJS default components with no children or siblings', () => {
76+
xit('should filter out NextJS default components with no children or siblings', () => {
6277
for (let name of nextJSDefaultComponent) {
6378
mockFiberNode.elementType.name = name;
6479
const tree = createTree(mockFiberNode);
@@ -76,19 +91,22 @@ describe('master tree tests', () => {
7691
const children = tree.children;
7792
const firstChild = children[0];
7893
const secondChild = children[1];
94+
console.log('First Child', firstChild);
95+
console.log('Second Child', secondChild);
7996
expect(children.length).toEqual(2);
80-
// expect(firstChild.componentData?.state).toEqual(2);
97+
expect(firstChild.componentData.state).toEqual({ counter: 0 });
98+
expect(secondChild.componentData.hooksState);
8199
}
82100
});
83101

84-
it('should filter out remix default components with no children or siblings', () => {
102+
xit('should filter out remix default components with no children or siblings', () => {
85103
for (let name of remixDefaultComponents) {
86104
mockFiberNode.elementType.name = name;
87105
const tree = createTree(mockFiberNode);
88106
}
89107
});
90108

91-
it('should only traverse allowed components', () => {
109+
xit('should only traverse allowed components', () => {
92110
for (let tag of allowedComponentTypes) {
93111
mockFiberNode.elementType.tag = tag;
94112
const tree = createTree(mockFiberNode);
@@ -102,11 +120,11 @@ describe('master tree tests', () => {
102120
});
103121
});
104122

105-
describe('add sibling', () => {});
123+
xdescribe('add sibling', () => {});
106124

107-
describe('add children', () => {});
125+
xdescribe('add children', () => {});
108126

109-
describe('createComponentActionsRecord', () => {
127+
xdescribe('createComponentActionsRecord', () => {
110128
it('should save a new component action record if the Fiber node is a stateful class component', () => {
111129
mockFiberNode.tag = 1; // ClassComponent
112130
mockFiberNode.stateNode = {

src/backend/controllers/createTree/createTree.ts

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import {
33
// object with tree structure
44
Fiber,
5+
ComponentData,
56
} from '../../types/backendTypes';
67
import {
78
FunctionComponent,
@@ -118,24 +119,17 @@ export default function createTree(currentFiberNode: Fiber): Tree {
118119

119120
// --------------INITIALIZE OBJECT TO CONTAIN COMPONENT DATA---------------
120121
let newState: 'stateless' | object = 'stateless';
121-
let componentData: {
122-
actualDuration?: number;
123-
actualStartTime?: number;
124-
selfBaseDuration?: number;
125-
treeBaseDuration?: number;
126-
props: {};
127-
context: {};
128-
state?: {};
129-
hooksState?: {};
130-
hooksIndex?: number[];
131-
index?: number;
132-
} = {
122+
let componentData: ComponentData = {
133123
actualDuration,
134124
actualStartTime,
135125
selfBaseDuration,
136126
treeBaseDuration,
137127
props: {},
138128
context: {},
129+
state: null,
130+
index: null,
131+
hooksState: null,
132+
hooksIndex: null,
139133
};
140134

141135
// ---------------APPEND PROP DATA FROM REACT DEV TOOL----------------------
@@ -227,7 +221,10 @@ export default function createTree(currentFiberNode: Fiber): Tree {
227221
// We then store them along with the corresponding memoizedState.queue,
228222
// which includes the dispatch() function we use to change their state.
229223
const hooksStates = getHooksStateAndUpdateMethod(memoizedState);
224+
// console.log('hooksState');
225+
console.log(componentName, elementType);
230226
const hooksNames = getHooksNames(elementType.toString());
227+
console.log('TESThooksNames', hooksNames);
231228
// Intialize state & index:
232229
// newState.hooksState = [];
233230
componentData.hooksState = {};

src/backend/controllers/createTree/statePropExtractors.ts

Lines changed: 39 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -213,46 +213,53 @@ export function getHooksStateAndUpdateMethod(memoizedState: Fiber['memoizedState
213213
*/
214214
export function getHooksNames(elementType: string): { hookName: string; varName: string }[] {
215215
// Initialize empty object to store the setters and getter
216+
// Abstract Syntax Tree
216217
let AST: any;
217218
try {
218-
AST = JSXParser.parse(elementType);
219+
AST = JSXParser.parse(elementType).body;
219220
} catch (e) {
220221
throw Error('Error occurs at helpers getHooksName.ts Cannot parse functional component.');
221222
}
223+
console.log('parsed JSX', AST);
222224
// Begin search for hook names, only if ast has a body property.
223-
AST = AST.body;
224-
225-
// Statements get all the names of the hooks. For example: useCount, useWildcard, ...
226-
const statements: { hookName: string; varName: string }[] = [];
227-
/** All module exports always start off as a single 'FunctionDeclaration' type
228-
* Other types: "BlockStatement" / "ExpressionStatement" / "ReturnStatement"
229-
* Iterate through AST of every function declaration
230-
* Check within each function declaration if there are hook declarations & variable name declaration */
231-
AST.forEach((functionDec: any) => {
232-
let declarationBody: any;
233-
if (functionDec.expression?.body) declarationBody = functionDec.expression.body.body;
234-
// check if functionDec.expression.body exists, then set declarationBody to functionDec's body
235-
else declarationBody = functionDec.body?.body ?? [];
236-
// Traverse through the function's funcDecs and Expression Statements
237-
declarationBody.forEach((elem: any) => {
238-
// Hooks will always be contained in a variable declaration
239-
if (elem.type === 'VariableDeclaration') {
240-
// Obtain the declarations array from elem.
241-
const { declarations } = elem;
242-
// Obtain the reactHook:
243-
const reactHook: string = declarations[0]?.init?.callee?.expressions[1]?.property?.name;
244-
if (reactHook === 'useState') {
245-
// Obtain the variable being set:
246-
let varName: string = declarations[1]?.id?.name;
247-
// Obtain the setState method:
248-
let hookName: string = declarations[2]?.id?.name;
249-
// Push reactHook & varName to statements array
250-
statements.push({ hookName, varName });
225+
try {
226+
// Statements get all the names of the hooks. For example: useCount, useWildcard, ...
227+
const statements: { hookName: string; varName: string }[] = [];
228+
/** All module exports always start off as a single 'FunctionDeclaration' type
229+
* Other types: "BlockStatement" / "ExpressionStatement" / "ReturnStatement"
230+
* Iterate through AST of every function declaration
231+
* Check within each function declaration if there are hook declarations & variable name declaration */
232+
AST.forEach((functionDec: any) => {
233+
let declarationBody: any;
234+
if (functionDec.expression?.body) declarationBody = functionDec.expression.body.body;
235+
// check if functionDec.expression.body exists, then set declarationBody to functionDec's body
236+
else declarationBody = functionDec.body?.body ?? [];
237+
console.log('declaration body', declarationBody);
238+
// Traverse through the function's funcDecs and Expression Statements
239+
declarationBody.forEach((elem: any) => {
240+
// Hooks will always be contained in a variable declaration
241+
if (elem.type === 'VariableDeclaration') {
242+
// Obtain the declarations array from elem.
243+
const { declarations } = elem;
244+
// Obtain the reactHook:
245+
console.log('Callee', declarations[0].init.callee);
246+
const reactHook: string = declarations[0]?.init?.callee?.expressions[1]?.property?.name;
247+
if (reactHook === 'useState') {
248+
// Obtain the variable being set:
249+
let varName: string = declarations[1]?.id?.name;
250+
// Obtain the setState method:
251+
let hookName: string = declarations[2]?.id?.name;
252+
// Push reactHook & varName to statements array
253+
statements.push({ hookName, varName });
254+
}
251255
}
252-
}
256+
});
253257
});
254-
});
255-
return statements;
258+
return statements;
259+
} catch (err) {
260+
console.log(err);
261+
throw new Error();
262+
}
256263
}
257264

258265
// DEPERACATED: After React DEV Tool Update. This function no longer works. Keep for history record

src/backend/controllers/timeJump.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ async function updateReactFiberTree(
6868
// ------------------------STATEFUL CLASS COMPONENT-------------------------
6969
// Check if it is a stateful class component
7070
// Index can be zero => falsy value => DO NOT REMOVE UNDEFINED
71-
if (index !== undefined) {
71+
if (index !== null) {
7272
// Obtain the BOUND update method at the given index
7373
const classComponent = componentActionsRecord.getComponentByIndex(index);
7474
// Update component state
@@ -86,7 +86,7 @@ async function updateReactFiberTree(
8686
// if yes, grab all relevant components for this snapshot by its index
8787
// call dispatch on each component passing in the corresponding currState value
8888
//index can be zero => falsy value => DO NOT REMOVE UNDEFINED
89-
if (hooksIndex !== undefined) {
89+
if (hooksIndex !== null) {
9090
// Obtain the array of BOUND update methods at the given indexes.
9191
// NOTE: each useState will be a separate update method. So if a component have 3 useState, we will obtain an array of 3 update methods.
9292
const functionalComponent = componentActionsRecord.getComponentByIndexHooks(hooksIndex);

src/backend/models/tree.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
/* eslint-disable no-console */
77
/* eslint-disable no-param-reassign */
88
import { Route } from './routes';
9+
import { ComponentData } from '../types/backendTypes';
910

1011
// ComponentNames is used to store a mapping between a component's unique identifier and its name. This mapping is used to reconstruct the component instances during deserialization.
1112
let componentNames = {};
@@ -52,7 +53,7 @@ class Tree {
5253

5354
name: string;
5455

55-
componentData: {};
56+
componentData: ComponentData;
5657

5758
children: Tree[];
5859

src/backend/types/backendTypes.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,16 @@ export interface MsgData {
5858
* @member props -
5959
*/
6060
export interface ComponentData {
61-
index?: number;
62-
hooksIndex?: number;
6361
actualDuration?: number;
6462
actualStartTime?: number;
6563
selfBaseDuration?: number;
6664
treeBaseDuration?: number;
67-
props?: any;
65+
props: {};
66+
context: {};
67+
state: {} | null;
68+
hooksState: {} | null;
69+
hooksIndex: number[] | null;
70+
index: number | null;
6871
}
6972

7073
/**

0 commit comments

Comments
 (0)