Skip to content

Commit e3f6481

Browse files
committed
Merge branch 'master' into map
2 parents b76c137 + 8699203 commit e3f6481

File tree

18 files changed

+375
-241
lines changed

18 files changed

+375
-241
lines changed

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[submodule "tests/manual-tests/recoilTest"]
2+
path = tests/manual-tests/recoilTest
3+
url = https://github.com/kevinfey/recoilTest

.travis.yml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,14 @@
1-
services:
2-
- docker
1+
language: node_js
2+
node_js:
3+
- 12
4+
dist: trusty
5+
cache: npm
6+
git:
7+
submodules: false
8+
# branches:
9+
# only:
10+
# - implementtravisci
11+
install:
12+
- npm install
313
script:
4-
- docker-compose up --abort-on-container-exit
14+
- npm run test

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"scripts": {
1616
"build": "webpack --mode production",
1717
"dev": "webpack --mode development --watch",
18-
"test": "jest --verbose --coverage --watchAll --runInBand --detectOpenHandles --forceExit",
18+
"test": "jest --verbose --coverage --watchAll --forceExit",
1919
"docker-test-lint": "eslint --ext .js --ext .jsx src",
2020
"docs": "typedoc --json docs --inputFiles src/app --inputFiles src/backend --readme docs/readme.md"
2121
},

readme_2.md

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,98 @@
1-
This documentation explains the architecture of Reactime v4.
1+
# Reactime v4 Architecture
22

3+
## Brief
4+
Our mission at Reactime is to maintain and iterate constantly, but never at the expense of future developers.<br />We know how hard it is to quickly get up to speed and onboard in a new codebase.<br />So, here are some helpful pointers to help you hit the ground running. 🏃🏾💨
35

4-
![demo](./AppStructureDiagram.png)
6+
### Main Structure
7+
8+
In the *src* folder, there are three directories we care about: *app*, *backend*, and *extension*.
9+
```
10+
src/
11+
├── app/ # Frontend code
12+
│ │ #
13+
│   ├── __tests__/ #
14+
│   ├── actions/ # Redux action creators
15+
│   ├── components/ # React components
16+
│   ├── constants/ #
17+
│   ├── containers/ # More React components
18+
│   ├── reducers/ # Redux mechanism for updating state
19+
│   ├── styles/ #
20+
│   ├── index.tsx # App component
21+
│   ├── module.d.ts #
22+
│   └── store.tsx #
23+
24+
├── backend/ # "Backend" code
25+
│ │ #
26+
│   ├── __tests__/ #
27+
│   ├── types/ # Typescript interfaces
28+
│   ├── helpers.js #
29+
│   ├── index.ts # Starting point for backend functionality
30+
│   ├── index.d.ts #
31+
│   ├── linkFiber.ts #
32+
│   ├── masterState.js # Component action record interface
33+
│   ├── module.d.ts #
34+
│   ├── package.json #
35+
│   ├── puppeteerServer.js #
36+
│   ├── readme.md #
37+
│   ├── timeJump.ts # Rerenders DOM based on snapshot from background
38+
│   └── tree.ts # Custom structure to send to background
39+
40+
├── extension/ # Chrome Extension code
41+
│ │ #
42+
│   ├── build/ # Destination for bundles
43+
│   │ # and manifest.json (Chrome config file)
44+
│ │ #
45+
│   ├── background.js #
46+
│   └── contentScript.ts #
47+
└──
48+
```
549

6-
In the src folder, there are three directories: app, backend, and extension.
50+
1. The *app* folder is responsible for the Single Page Application that you see when you open the chrome dev tools under the Reactime tab.
751

52+
2. The *backend* folder contains the set of all scripts that we inject into our "target" application via `background.js`
53+
- In Reactime, its main role is to generate data and handle time-jump requests from the background script in our *extension* folder.
854

9-
The app folder is responsible a SPA that you see when you open the chrome dev tools under the Reactime tab.
55+
3. The *extension* folder is where the `contentScript.js` and `background.js` are located.
56+
- Like regular web apps, Chrome Extensions are event-based. The background script is where one typically monitors for browser triggers (e.g. events like closing a tab, for example). The content script is what allows us to read or write to our target web application, usually as a result of [messages passed](https://developer.chrome.com/extensions/messaging) from the background script.
57+
- These two files help us handle requests both from the web browser and from the Reactime extension itself
1058

11-
The backend folder is responsible for generating data and handle time-jump request from the background.js scripts in extension.
59+
Still unsure about what contents scripts and background scripts do for Reactime, or for a chrome extensions in general?
60+
- The implementation details [can be found](./src/extension/background.js) [in the files](./src/extension/contentScript.ts) themselves.
61+
- We also encourage you to dive into [the official Chrome Developer Docs](https://developer.chrome.com/home). Some relevant sections are reproduced below:
1262

13-
The extension folder is where the contentscript.js and background.js located. These two files belongs to Chrome internal to help us handle requests both from the web browser and from the chrome dev tools. Unsure what contentscripts and backgroundscripts are? The details implementation are documented in the files themselves.
63+
> Content scripts are files that run in the context of web pages.
64+
>
65+
> By using the standard Document Object Model (DOM), they are able to **read** details of the web pages the browser visits, **make changes** to them and **pass information back** to their parent extension. ([Source](https://developer.chrome.com/extensions/content_scripts))
1466
15-
> Content scripts are files that run in the context of web pages. By using the standard Document Object Model (DOM), they are able to read details of the web pages the browser visits, make changes to them and pass information to their parent extension. Source: https://developer.chrome.com/extensions/content_scripts
67+
- One helpful way to remember a content script's role in the Chrome ecosystem is to think: a *content* script is used to read and modify a target web page's rendered *content*.
1668

17-
>A background page is loaded when it is needed, and unloaded when it goes idle. Some examples of events include:
69+
>A background page is loaded when it is needed, and unloaded when it goes idle.
70+
>
71+
> Some examples of events include:
1872
>The extension is first installed or updated to a new version.
1973
>The background page was listening for an event, and the event is dispatched.
2074
>A content script or other extension sends a message.
21-
>Another view in the extension, such as a popup, calls runtime.getBackgroundPage.
22-
>Once it has been loaded, a background page will stay running as long as it is performing an action, such as calling a Chrome API or issuing a network request. Additionally, the background page will not unload until all visible views and all message ports are closed. Note that opening a view does not cause the event page to load, but only prevents it from closing once loaded. Source: https://developer.chrome.com/extensions/background_pages
75+
>Another view in the extension, such as a popup, calls `runtime.getBackgroundPage`.
76+
>
77+
>Once it has been loaded, a background page will stay running as long as it is performing an action, such as calling a Chrome API or issuing a network request.
78+
>
79+
> Additionally, the background page will not unload until all visible views and all message ports are closed. Note that opening a view does not cause the event page to load, but only prevents it from closing once loaded. ([Source](https://developer.chrome.com/extensions/background_pages))
80+
81+
- You can think of background scripts serving a purpose analogous to that of a **server** in the client/server paradigm. Much like a server, our `background.js` listens constantly for messages (i.e. requests) from two main places:
82+
1. The content script
83+
2. The chrome extension "front-end" **(*NOT* the interface of the browser, this is an important distinction.)**
84+
- In other words, a background script works as a sort of middleman, directly maintaining connection with its parent extension, and acting as a proxy enabling communication between it and the content script.
2385

24-
Just to reiterate, contentscript is use to read and modify information that is rendered on the webpage, and a host of other objects. Background is very similar to client/server concept in which background is behaving like a server, listening to request from the contentscipt and **the request from the "front-end" of the chrome dev tools in the reactime tab (not the interface of the browser, this is an important distinction.)** In other words, background script works directly with the React Dev Tools, whereas contentscript works with the interface of the browser.
86+
87+
### Data Flow
2588

2689
The general flow of data is described in the following steps:
2790

28-
1. When the background bundle is loaded from the browser, it injects a script into the dom. This script uses a technique called [throttle](https://medium.com/@bitupon.211/debounce-and-throttle-160affa5457b) to get the data of the state of the app to send to the contentscript every specified miliseconds (in our case, it's 70ms).
91+
![demo](./AppStructureDiagram.png)
2992

93+
1. When the background bundle is loaded by the browser, it executes a script injection into the dom. (see section on *backend*). This script uses a technique called [throttle](https://medium.com/@bitupon.211/debounce-and-throttle-160affa5457b) to send state data from the app to the content script every specified milliseconds (in our case, this interval is 70ms).
3094

31-
2. This contentscript always listens to the messages being sent from the interface of the browser. The recieved data will immediately be sent to the background script which then update an object that persist in background script called **tabsObj**. Each time tabsObj is updated, the most recent version will be sent to the interface of reactime dev tools written the app folder.
95+
2. The content script always listens for messages being passed from the extension's target application. Upon receiving data from the target app, the content script will immediately forward this data to the background script which then updates an object called `tabsObj`. Each time `tabsObj` is updated, its latest version will be passed to Reactime, where it is processed for displaying to the user by the *app* folder scripts.
3296

33-
3. Likewise, when there is an action from Reactime dev tools - a jump request for example, a request will be made to the background script which is proxied to the content script. This content script will talk to the browser interface to request the *state* that the user wants to jump to. One important thing to note here is that the jump action will be excecuted in the backend script because it has direct access to the DOM.
97+
3. Likewise, when Reactime emits an action due to user interaction -- a "jump" request for example -- a message will be passed from Reactime via the background script to the content script. Then, the content script will pass a message to the target application containing a payload that represents the state the user wants the DOM to reflect or "jump" to.
98+
- One important thing to note here is that this jump action must be dispatched in the target application (i.e. *backend* land), because only there do we have direct access to the DOM.

src/app/__tests__/Chart.test.tsx renamed to src/app/__tests__/Chart_deprecated_.test.tsx

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* eslint-disable @typescript-eslint/no-explicit-any */
1+
/* eslint-disable @typescript-eslint/no-explicxit-any */
22
/* eslint-disable @typescript-eslint/no-var-requires */
33
/* eslint-disable react/jsx-props-no-spreading */
44
/* eslint-disable import/no-extraneous-dependencies */
@@ -7,45 +7,77 @@
77
import React from 'react';
88
import { configure, mount } from 'enzyme';
99
import Adapter from 'enzyme-adapter-react-16';
10+
import History from '../components/History';
1011

11-
const Chart = require('../components/Chart').default;
1212

13-
// Unit test cases for d3 functionality
13+
// Unxit test cases for d3 functionalxity
1414
configure({ adapter: new (Adapter as any)() });
1515

16-
// Test the life cycle methods in Chart
17-
describe('Life cycle methods in Chart', () => {
16+
// Test the life cycle methods in History
17+
describe('Life cycle methods in History', () => {
1818
let wrapper;
1919
const props = {
20-
hierarchy: 0,
20+
hierarchy: {
21+
branch: 0,
22+
children : [
23+
{
24+
index:1,
25+
name:2,
26+
branch:0,
27+
stateSnapshot:{},
28+
children: []
29+
}
30+
],
31+
index : 0,
32+
name : 1,
33+
stateSnapshot : {
34+
children:[
35+
{
36+
children: [],
37+
componentData: {
38+
actualDuration : 1.0,
39+
actualStartTime : 1.0,
40+
selfBaseDuration : 1.0,
41+
treeBaseDuration : 1.0,
42+
},
43+
name: 'root',
44+
state: 'stateless'
45+
}
46+
],
47+
componentData: {},
48+
name: "root",
49+
state: "root"
50+
}
51+
},
2152
};
2253
// Set up wrapper
2354
beforeEach(() => {
24-
wrapper = mount(<Chart {...props} />);
55+
console.log(props)
56+
wrapper = mount(<History {...props} />);
2557
});
2658
// test componentDidMount
27-
it('should call componentDidMount once', () => {
59+
xit('should call componentDidMount once', () => {
2860
const instance = wrapper.instance();
2961
jest.spyOn(instance, 'componentDidMount');
3062
instance.componentDidMount();
3163
expect(instance.componentDidMount).toHaveBeenCalledTimes(1);
3264
});
33-
// test maked3Tree within componentDidMount
34-
it('should call maked3Tree upon mounting', () => {
65+
// test maked3Tree wxithin componentDidMount
66+
xit('should call maked3Tree upon mounting', () => {
3567
const instance = wrapper.instance();
3668
jest.spyOn(instance, 'maked3Tree');
3769
instance.componentDidMount();
3870
expect(instance.maked3Tree).toHaveBeenCalledTimes(1);
3971
});
4072
// test componentDidUpdate
41-
it('should call componentDidUpdate once', () => {
73+
xit('should call componentDidUpdate once', () => {
4274
const instance = wrapper.instance();
4375
jest.spyOn(instance, 'componentDidUpdate');
4476
instance.componentDidUpdate();
4577
expect(instance.componentDidUpdate).toHaveBeenCalledTimes(1);
4678
});
47-
// test maked3Tree within componentDidUpdate
48-
it('should call maked3Tree once upon updating', () => {
79+
// test maked3Tree wxithin componentDidUpdate
80+
xit('should call maked3Tree once upon updating', () => {
4981
const instance = wrapper.instance();
5082
jest.spyOn(instance, 'maked3Tree');
5183
instance.componentDidUpdate();
@@ -65,11 +97,11 @@ describe('Root object', () => {
6597
};
6698
// Set up wrapper
6799
beforeEach(() => {
68-
wrapper = mount(<Chart {...props} />);
100+
wrapper = mount(<History {...props} />);
69101
});
70102

71103
// eslint-disable-next-line jest/no-disabled-tests
72-
it('should be a deep clone of the hierarchy', () => {
104+
xit('should be a deep clone of the hierarchy', () => {
73105
const instance = wrapper.instance();
74106
instance.componentDidMount();
75107
expect(typeof root).toBe(typeof props.hierarchy);
@@ -85,10 +117,10 @@ describe('maked3Tree method', () => {
85117
};
86118
// Set up wrapper
87119
beforeEach(() => {
88-
wrapper = mount(<Chart {...props} />);
120+
wrapper = mount(<History {...props} />);
89121
});
90-
// Test the invocation of removed3Tree within maked3Tree
91-
it('should call removed3Tree once', () => {
122+
// Test the invocation of removed3Tree wxithin maked3Tree
123+
xit('should call removed3Tree once', () => {
92124
const instance = wrapper.instance();
93125
jest.spyOn(instance, 'removed3Tree');
94126
instance.maked3Tree();
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* eslint:disable */
2+
3+
import * as d3 from 'd3'
4+
5+
describe('D3Canvas Testing', ()=> {
6+
const getCanvas = () => {
7+
return d3.select('#canvas')
8+
}
9+
10+
it ('should exist', ()=>{
11+
expect(getCanvas()).not.toBeNull();
12+
})
13+
14+
})
15+
16+
describe('D3 Node Testing', ()=> {
17+
const getNodes = () => {
18+
return d3.select('g')
19+
}
20+
21+
it ('should exist', () => {
22+
expect(getNodes()).not.toBeNull();
23+
})
24+
25+
26+
})

src/app/__tests__/History.test.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* eslint:disable */
2+
3+
import * as d3 from 'd3'
4+
5+
describe('D3 Canvas Testing', ()=> {
6+
const getCanvas = () => {
7+
return d3.select('#canvas')
8+
}
9+
10+
it ('should render', ()=>{
11+
expect(getCanvas()).not.toBeNull();
12+
})
13+
14+
})
15+
16+
describe('D3 Node Testing', ()=> {
17+
const getNodes = () => {
18+
return d3.select('g')
19+
}
20+
21+
it ('should render', () => {
22+
expect(getNodes()).not.toBeNull();
23+
})
24+
25+
26+
})

0 commit comments

Comments
 (0)