Skip to content

Commit 0ecb4e3

Browse files
Merge pull request #7 from oslabs-beta/testing
Testing
2 parents 5d047d1 + 518d2ad commit 0ecb4e3

File tree

8 files changed

+9511
-9412
lines changed

8 files changed

+9511
-9412
lines changed

src/app/components/StateRoute/ComponentMap/ComponentMap.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,8 @@ export default function ComponentMap({
216216

217217
const formatContext = (data) => {
218218
const contextFormat = [];
219-
// const nestedObj = [];
220219
for (const key in data) {
220+
// Suggestion: update the front end to display as a list if we have object
221221
contextFormat.push(<p className='statecontext'>{`${key}: ${data[key]}`}</p>);
222222
}
223223
return contextFormat;

src/backend/__tests__/tree.test.ts

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,56 @@
11
import Tree from '../tree';
2+
import { serializeState, scrubUnserializableMembers } from '../tree';
23

3-
/**
4-
* Created new tree under sibling and copy and clean tree describe block --
5-
* Reason is because other tests are adding properties to tree and affecting the child block,
6-
* so this was a quick way to test the trees getting reset to initial state
7-
*
8-
* Possible fix if more time allowed: Making use of beforeEach or afterEach --
9-
*/
4+
describe('Serialize state unit test', () => {
5+
const dummyState = {
6+
counter: 1,
7+
playerOne: 'X',
8+
board: [
9+
['', 'O', 'X'],
10+
['', 'O', 'X'],
11+
['O', 'X', ''],
12+
],
13+
};
1014

11-
describe('Tree unit test', () => {
15+
const circularState: { [key: string]: any } = {};
16+
circularState.circ = circularState;
17+
18+
const serializedState = serializeState(dummyState);
19+
const serializedCircularState = serializeState(circularState);
20+
21+
it('should create a deep copy of state', () => {
22+
expect(dummyState).toEqual(serializedState);
23+
expect(dummyState).not.toBe(serializedState);
24+
});
25+
26+
it('should detect circular state', () => {
27+
expect(serializedCircularState).toEqual('circularState');
28+
});
29+
});
30+
31+
describe('Scrub unserialized members unit test', () => {
32+
const dummyState = {
33+
counter: 1,
34+
playerOne: 'X',
35+
board: [
36+
['', 'O', 'X'],
37+
['', 'O', 'X'],
38+
['O', 'X', ''],
39+
],
40+
increment: function () {
41+
this.counter++;
42+
},
43+
};
44+
const newTree = new Tree(dummyState);
45+
const scrubbedTree = scrubUnserializableMembers(newTree);
46+
// make sure return type is tree
47+
it('should be instance of tree', () => {
48+
expect(newTree).toBeInstanceOf(Tree);
49+
});
50+
// make sure function is scrubbed
51+
});
52+
53+
xdescribe('Tree unit test', () => {
1254
const newTree = new Tree({});
1355
describe('Constructor', () => {
1456
it('should be able to create a newTree', () => {
@@ -30,20 +72,6 @@ describe('Tree unit test', () => {
3072
});
3173
});
3274

33-
/**
34-
*
35-
* making sure to adhere to ts practices when goign through tests
36-
*
37-
* ^^
38-
* the tree should have initial values of state,
39-
* name, etc to be default as per newly created tree
40-
* update the add child and add sibling tests
41-
*
42-
* update the clean tree copy test to make it test for deep equaltiy? (note:
43-
* this test may always fail if we make it so because there is no way to have deep equalituy
44-
* with some shit that isn't allowed)
45-
*/
46-
4775
describe('Adding children', () => {
4876
const returnChild = newTree.addChild('stateful', 'child', {}, null);
4977

@@ -79,6 +107,8 @@ describe('Tree unit test', () => {
79107
});
80108
});
81109

110+
// TO DO- add serializing state tests
111+
describe('Serializing state unit test', () => {});
82112
// review this test
83113
// returnSibling not used?
84114
// Check Test

src/backend/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@
1111
import 'regenerator-runtime/runtime';
1212
import linkFiberStart from './linkFiber';
1313
import timeJumpStart from './timeJump';
14-
import { Snapshot, Mode, MsgData } from './types/backendTypes';
14+
import { Snapshot, Status, MsgData } from './types/backendTypes';
1515

1616
// * State snapshot object initialized here
1717
const snapShot: Snapshot = {
1818
tree: null,
1919
unfilteredTree: null,
2020
};
2121

22-
const mode: Mode = {
22+
const mode: Status = {
2323
jumping: false,
2424
paused: false,
2525
};

src/backend/linkFiber.ts

Lines changed: 103 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,42 @@ import {
1616
// tree
1717
Snapshot,
1818
// jump, pause
19-
Mode,
19+
Status,
2020
// array of state and component
2121
HookStates,
2222
// object with tree structure
2323
Fiber,
2424
} from './types/backendTypes';
25+
import {
26+
FunctionComponent,
27+
ClassComponent,
28+
IndeterminateComponent, // Before we know whether it is function or class
29+
HostRoot, // Root of a host tree. Could be nested inside another node.
30+
HostPortal, // A subtree. Could be an entry point to a different renderer.
31+
/**
32+
* Host Component: a type of component that represents a native DOM element in the browser environment, such as div, span, input, h1 etc.
33+
*/
34+
HostComponent, // has stateNode of html elements
35+
HostText,
36+
Fragment,
37+
Mode,
38+
ContextConsumer,
39+
ContextProvider,
40+
ForwardRef,
41+
Profiler,
42+
SuspenseComponent,
43+
MemoComponent,
44+
SimpleMemoComponent, // A higher order component where if the component renders the same result given the same props, react skips rendering the component and uses last rendered result. Has memoizedProps/memoizedState but no stateNode
45+
LazyComponent,
46+
IncompleteClassComponent,
47+
DehydratedFragment,
48+
SuspenseListComponent,
49+
FundamentalComponent,
50+
ScopeComponent,
51+
Block,
52+
OffscreenComponent,
53+
LegacyHiddenComponent,
54+
} from './types/backendTypes';
2555
// import function that creates a tree
2656
import Tree from './tree';
2757
// passes the data down to its components
@@ -54,7 +84,7 @@ let rtid = null;
5484
*
5585
* Middleware: Gets a copy of the current snap.tree and posts a recordSnap message to the window
5686
*/
57-
function sendSnapshot(snap: Snapshot, mode: Mode): void {
87+
function sendSnapshot(snap: Snapshot, mode: Status): void {
5888
// Don't send messages while jumping or while paused
5989
if (mode.jumping || mode.paused) return;
6090
// If there is no current tree creates a new one
@@ -87,7 +117,7 @@ function sendSnapshot(snap: Snapshot, mode: Mode): void {
87117
* Middleware: Updates snap object with latest snapshot, using @sendSnapshot
88118
*/
89119
// updating tree depending on current mode on the panel (pause, etc)
90-
function updateSnapShotTree(snap: Snapshot, mode: Mode): void {
120+
function updateSnapShotTree(snap: Snapshot, mode: Status): void {
91121
// this is the currently active root fiber(the mutable root of the tree)
92122
if (fiberRoot) {
93123
const { current } = fiberRoot;
@@ -138,6 +168,7 @@ function traverseHooks(memoizedState: any): HookStates {
138168
// This runs after every Fiber commit. It creates a new snapshot
139169
const exclude = new Set([
140170
'alternate',
171+
'basename',
141172
'baseQueue',
142173
'baseState',
143174
'child',
@@ -149,20 +180,25 @@ const exclude = new Set([
149180
'deps',
150181
'dependencies',
151182
'destroy',
183+
'dispatch',
184+
'location',
152185
'effects',
153186
'element',
154187
'elementType',
155188
'firstBaseUpdate',
156189
'firstEffect',
157190
'flags',
158191
'get key',
192+
'getState',
159193
'key',
160194
'lanes',
161195
'lastBaseUpdate',
162196
'lastEffect',
197+
'liftedStore',
163198
'navigator',
164199
'memoizedState',
165200
'mode',
201+
'navigationType',
166202
'next',
167203
'nextEffect',
168204
'pending',
@@ -172,12 +208,15 @@ const exclude = new Set([
172208
'Provider',
173209
'updateQueue',
174210
'ref',
211+
'replaceReducer',
175212
'responders',
176213
'return',
177214
'route',
178215
'routeContext',
179216
'shared',
180217
'sibling',
218+
'subscribe',
219+
'subscription',
181220
'stateNode',
182221
'tag',
183222
'type',
@@ -193,39 +232,57 @@ const exclude = new Set([
193232
'_store',
194233
'_threadCount',
195234
'$$typeof',
235+
'@@observable',
196236
]);
197-
// -------------------CONVERT PROPT DATA TO STRING------------------------------
198-
// This recursive function is used to grab the state of children components
199-
// and push them into the parent componenent
200-
// react elements throw errors on client side of application - convert react/functions into string
201-
function convertDataToString(newObj, newPropData = {}, depth = 0) {
202-
// const newPropData = oldObj;
203-
for (const key in newObj) {
204-
// Skip keys that are in exclude list OR if there is no value at key
205-
if (exclude.has(key) || !newObj[key]) {
237+
// -----------------TRIMMING PASSED IN FIBER ROOT DATA--------------------------
238+
/**
239+
* This recursive function is used to grab the state of children components
240+
and push them into the parent componenent react elements throw errors on client side of application - convert react/functions into string
241+
*
242+
* @param reactDevData - The data object obtained from React Devtool. Ex: memoizedProps, memoizedState
243+
* @param reactimeData - The updated data object to send to front end of Reactime.
244+
* @param depth - reactDevData is nested object. The value in reactDevData can be another object. Depth is use to keep track the depth during the unraveling of nested object
245+
* @returns reactimeData - the updated data object to send to front end of ReactTime
246+
*/
247+
function convertDataToString(reactDevData, reactimeData = {}, excludeSet?: any) {
248+
if (!excludeSet) excludeSet = exclude;
249+
for (const key in reactDevData) {
250+
// Skip keys that are in exclude set OR if there is no value at key
251+
// Falsy values such as 0, false, null are still valid value
252+
if (excludeSet.has(key) || reactDevData[key] === undefined) {
206253
continue;
207-
// newPropData[key] = 'reactFiber';
208-
// return newPropData;
209-
}
210-
// If value at key is a function, assign key with value 'function' to newPropData object
211-
else if (typeof newObj[key] === 'function') {
212-
newPropData[key] = 'function';
213254
}
214-
// If value at key is an object, recusive call convertDataToString to traverse through all keys and append to newPropData object accodingly
215-
else if (typeof newObj[key] === 'object') {
216-
// newPropData[key] =
217-
depth > 10
218-
? 'convertDataToString reached max depth'
219-
: convertDataToString(newObj[key], newPropData, depth + 1);
255+
// If value at key is a function, assign key with value 'function' to reactimeData object
256+
else if (typeof reactDevData[key] === 'function') {
257+
reactimeData[key] = 'function';
220258
} else {
221-
newPropData[key] = newObj[key];
259+
reactimeData[key] = reactDevData[key];
222260
}
223261
}
224-
return newPropData;
262+
return reactimeData;
263+
}
264+
// ------------------------TRIMMING CONTEXT DATA--------------------------------
265+
function trimContextData(memoizedState, reactimeContextData = {}) {
266+
const exclude = new Set(['children', 'store', 'subscription']);
267+
while (memoizedState) {
268+
//Trim the current level of memoizedState data:
269+
const updateMemoizedState = convertDataToString(memoizedState?.memoizedState[0], {}, exclude);
270+
//Update Reactime data:
271+
Object.assign(reactimeContextData, updateMemoizedState);
272+
//Move on to the next level:
273+
memoizedState = memoizedState?.next;
274+
}
275+
276+
return reactimeContextData;
225277
}
226278
// -------------------------CREATE TREE TO SEND TO FRONT END--------------------
227279
/**
228280
* Every time a state change is made in the accompanying app, the extension creates a Tree “snapshot” of the current state, and adds it to the current “cache” of snapshots in the extension
281+
*
282+
* @param currentFiber
283+
* @param tree
284+
* @param fromSibling
285+
* @returns
229286
*/
230287
function createTree(
231288
currentFiber: Fiber,
@@ -262,10 +319,12 @@ function createTree(
262319
elementType,
263320
memoizedProps,
264321
memoizedState,
322+
dependencies,
323+
_debugHookTypes,
265324
});
266325

267-
// Tag === 5 signify this is a React Fragment. Each JSX component return a React fragment => The parent of a React Fragment could be a JSX component
268-
if (tag === 5) {
326+
// TODO: Understand this if statement
327+
if (tag === HostComponent) {
269328
try {
270329
// Ensure parent component has memoizedProps property
271330
if (
@@ -302,15 +361,24 @@ function createTree(
302361
} = {};
303362
let componentFound = false;
304363

364+
/**
365+
* In addition to React Component JSX from user input, there are other components that user would be using under the hood without needing to see it. For example, if Redux is used, a ContextProvider, called ReactRedux.Provider is created under the hood to manage the context store.
366+
* @variable filteredComponents is boolean value, used to filter out 'under-the-hood' components
367+
*/
368+
// const filteredComponents = tag != ContextProvider;
369+
const filteredComponents = true;
305370
// check to see if the parent component has any state/props
306-
if (memoizedProps && Object.keys(memoizedProps).length) {
307-
componentData.props = convertDataToString(memoizedProps, {});
371+
if (filteredComponents && memoizedProps && Object.keys(memoizedProps).length) {
372+
componentData.props = convertDataToString(memoizedProps);
308373
}
309-
310374
// if the component uses the useContext hook, we want to grab the context object and add it to the componentData object for that fiber
311-
if (tag === 0 && _debugHookTypes && dependencies?.firstContext?.memoizedValue) {
312-
componentData.context = convertDataToString(dependencies.firstContext.memoizedValue, null);
375+
// if (tag === 0 && _debugHookTypes && dependencies?.firstContext?.memoizedValue) {
376+
// componentData.context = convertDataToString(dependencies.firstContext.memoizedValue);
377+
// }
378+
if ((tag === FunctionComponent || tag === ClassComponent) && memoizedState?.memoizedState) {
379+
componentData.context = trimContextData(memoizedState);
313380
}
381+
314382
// Check if node is a stateful class component
315383
if (stateNode && stateNode.state && (tag === 0 || tag === 1 || tag === 2)) {
316384
// Save component's state and setState() function to our record for future
@@ -360,6 +428,7 @@ function createTree(
360428
treeBaseDuration,
361429
};
362430
console.log('props', componentData.props);
431+
console.log('context', componentData.context);
363432
let newNode = null;
364433

365434
// We want to add this fiber node to the snapshot
@@ -449,7 +518,7 @@ interface DevTools {
449518
* @return a function to be invoked by index.js that initiates snapshot monitoring
450519
* linkFiber contains core module functionality, exported as an anonymous function.
451520
*/
452-
export default (snap: Snapshot, mode: Mode): (() => void) => {
521+
export default (snap: Snapshot, mode: Status): (() => void) => {
453522
// Checks for visiblity of document
454523
function onVisibilityChange() {
455524
// Hidden property = background tab/minimized window

0 commit comments

Comments
 (0)