├── .gitignore
├── LICENSE
├── README.md
├── bin
├── cli.js
└── extension.js
├── dist
├── devpanel.tmpl.html
├── devtools.html
├── img
│ └── logo
│ │ ├── 128x128.png
│ │ ├── 16x16.png
│ │ └── 48x48.png
├── js
│ └── devtools.js
└── manifest.json
├── example
├── .babelrc
├── .eslintrc
├── .gitignore
├── README.md
├── app
│ ├── actions
│ │ └── counter.js
│ ├── api
│ │ └── .gitkeep
│ ├── app.global.css
│ ├── app.html
│ ├── app.icns
│ ├── components
│ │ ├── Counter.css
│ │ ├── Counter.js
│ │ ├── Home.css
│ │ └── Home.js
│ ├── containers
│ │ ├── App.js
│ │ ├── CounterPage.js
│ │ └── HomePage.js
│ ├── index.js
│ ├── reducers
│ │ ├── counter.js
│ │ └── index.js
│ ├── routes.js
│ ├── store
│ │ ├── configureStore.development.js
│ │ ├── configureStore.js
│ │ └── configureStore.production.js
│ └── utils
│ │ └── .gitkeep
├── main.js
├── package.js
├── package.json
├── server.js
├── test
│ ├── .eslintrc
│ ├── actions
│ │ └── counter.spec.js
│ ├── components
│ │ └── Counter.spec.js
│ ├── containers
│ │ └── CounterPage.spec.js
│ ├── e2e.js
│ ├── example.js
│ ├── reducers
│ │ └── counter.spec.js
│ └── setup.js
├── webpack.config.base.js
├── webpack.config.development.js
├── webpack.config.node.js
└── webpack.config.production.js
├── package.json
├── src
└── devpanel.js
└── webpack.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | .DS_Store
4 | dist/js/*.bundle.js
5 | dist/devpanel.html
6 | *.zip
7 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | The MIT License (MIT)
3 |
4 | Copyright (c) 2016 Jhen-Jie Hong
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RemoteDev Extension [](https://www.npmjs.com/package/remotedev-extension) [](https://david-dm.org/jhen0409/remotedev-extension) [](https://david-dm.org/jhen0409/remotedev-extension#info=devDependencies)
2 |
3 | > Use Redux DevTools in the Browser/Electron DevTools
4 |
5 | 
6 |
7 | The demo is used [electron-react-boilerplate](https://github.com/chentsulin/electron-react-boilerplate).
8 |
9 | ## Why?
10 |
11 | [Redux DevTools Extension](https://github.com/zalmoxisus/redux-devtools-extension) is awesome, but it [cannot running on Electron](https://github.com/zalmoxisus/redux-devtools-extension/issues/13). This project as a major support for the DevTools Extension of [Electron](https://github.com/atom/electron), it means that it doesn't use __background script__.
12 |
13 | This extension is included [remotedev-app](https://github.com/zalmoxisus/remotedev-app), can be used with [remotedev](https://github.com/zalmoxisus/remotedev) / [remote-redux-devtools](https://github.com/zalmoxisus/remote-redux-devtools).
14 |
15 | ## Installation
16 |
17 | ```bash
18 | $ npm i --save-dev remotedev-extension
19 | ```
20 |
21 | ## Usage (Browser)
22 |
23 | [Download Chrome Extension](https://chrome.google.com/webstore/detail/remotedev-devtools/npmkpkaejamnfodceoimeeioacfcijop)
24 | [Download Opera Extension](https://addons.opera.com/extensions/details/remotedev-devtools)
25 |
26 | ## Usage (Electron)
27 |
28 | ```js
29 | const electron = require('electron');
30 | const app = electron.app;
31 | const BrowserWindow = electron.BrowserWindow;
32 |
33 | app.on('ready', () => {
34 | // Add DevTools Extension,
35 | // if you want remove it,
36 | // Use: BrowserWindow.removeDevToolsExtension('RemoteDev DevTools');
37 | BrowserWindow.addDevToolsExtension('node_modules/remotedev-extension/dist');
38 |
39 | // ...
40 | });
41 | ```
42 |
43 | __*NOTE:*__ The Electron v0.37 have a [separate window problem](https://github.com/atom/electron/issues/4958#issuecomment-205121647).
44 |
45 | ## CLI and node
46 |
47 | ```bash
48 | $ remotedev-extension [options]
49 | ```
50 |
51 | ```js
52 | require('remotedev-extension')(options);
53 | ```
54 |
55 | #### Options
56 |
57 | * --hostname: the `remotedev-server` hostname, will apply `node_modules/remotedev-extension/dist` settings.
58 | (default: `localhost` if `port` is set)
59 | * --port: the `remotedev-server` port, will apply `node_modules/remotedev-extension/dist` settings.
60 | (default: `8000` if `runserver` or `hostname` is set)
61 | * --runserver: start the `remotedev-server` with options on local.
62 | * --ui-no-buttonbar: Set `noButtonBar` prop for [remotedev-app](https://github.com/zalmoxisus/remotedev-app/blob/master/src/app/index.js#L19).
63 |
64 | ## Example of Electron
65 |
66 | You can refer to [example folder](example).
67 |
68 | ## License
69 |
70 | [MIT](LICENSE)
71 |
--------------------------------------------------------------------------------
/bin/cli.js:
--------------------------------------------------------------------------------
1 | #! /usr/bin/env node
2 |
3 | const argv = require('minimist')(process.argv.slice(2), {
4 | boolean: ['runserver', 'ui-no-buttonbar']
5 | });
6 | require('./extension')(argv);
7 |
--------------------------------------------------------------------------------
/bin/extension.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const filePath = path.join(__dirname, '../dist/devpanel.tmpl.html');
4 | const distPath = path.join(__dirname, '../dist/devpanel.html');
5 | const startRemoteDev = require('remotedev-server');
6 |
7 | const html = fs.readFileSync(filePath, 'utf-8');
8 |
9 | module.exports = argv => {
10 | if (argv.hostname || argv.port) {
11 | fs.writeFileSync(
12 | distPath,
13 | html.replace(
14 | '// __remotedevOptionsSet__',
15 | 'window.remotedevOptions = ' + JSON.stringify({
16 | hostname: argv.hostname || 'localhost',
17 | port: argv.port || 8000,
18 | autoReconnect: true,
19 | noButtonBar: argv['ui-no-buttonbar']
20 | })
21 | )
22 | );
23 | } else {
24 | fs.writeFileSync(distPath, html);
25 | }
26 | if (argv.runserver) {
27 | argv.port = argv.port || 8000;
28 | return startRemoteDev(argv);
29 | }
30 | return { on: (status, cb) => cb() };
31 | };
32 |
--------------------------------------------------------------------------------
/dist/devpanel.tmpl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/dist/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | RemoteDev
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/dist/img/logo/128x128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhen0409/remotedev-extension/2f95aef4b8d1276d93b9126d90d1f6540bc0e679/dist/img/logo/128x128.png
--------------------------------------------------------------------------------
/dist/img/logo/16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhen0409/remotedev-extension/2f95aef4b8d1276d93b9126d90d1f6540bc0e679/dist/img/logo/16x16.png
--------------------------------------------------------------------------------
/dist/img/logo/48x48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhen0409/remotedev-extension/2f95aef4b8d1276d93b9126d90d1f6540bc0e679/dist/img/logo/48x48.png
--------------------------------------------------------------------------------
/dist/js/devtools.js:
--------------------------------------------------------------------------------
1 | chrome.devtools.panels.create(
2 | 'RemoteDev', null, 'devpanel.html', function(panel) {}
3 | );
--------------------------------------------------------------------------------
/dist/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.0.10",
3 | "name": "RemoteDev DevTools",
4 | "manifest_version": 2,
5 | "description": "Use Redux DevTools in the DevTools",
6 | "icons": {
7 | "16": "img/logo/16x16.png",
8 | "48": "img/logo/48x48.png",
9 | "128": "img/logo/128x128.png"
10 | },
11 | "devtools_page": "devtools.html",
12 | "permissions": [ "storage" ],
13 | "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'; style-src * 'unsafe-inline'; img-src 'self' data:;"
14 | }
15 |
--------------------------------------------------------------------------------
/example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["es2015", "stage-0", "react"],
3 | "plugins": ["add-module-exports"],
4 | "env": {
5 | "development": {
6 | "presets": ["react-hmre"]
7 | },
8 | "test": {
9 | "plugins": [
10 | ["webpack-loaders", { "config": "webpack.config.node.js", "verbose": false }]
11 | ]
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/example/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "parser": "babel-eslint",
3 | "extends": "airbnb",
4 | "env": {
5 | "browser": true,
6 | "mocha": true,
7 | "node": true
8 | },
9 | "rules": {
10 | "react/jsx-no-bind": 0,
11 | "react/prefer-stateless-function": 0,
12 | "comma-dangle": 0,
13 | "no-use-before-define": 0,
14 | "consistent-return": 0
15 | },
16 | "plugins": [
17 | "react"
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/example/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
29 | # OSX
30 | .DS_Store
31 |
32 | # App packaged
33 | dist
34 | release
35 |
--------------------------------------------------------------------------------
/example/README.md:
--------------------------------------------------------------------------------
1 | # RemoteDev Extension example of Electron
2 |
3 | This example is used [Electron React Boilerplate](https://github.com/chentsulin/electron-react-boilerplate).
4 |
5 | ## Different
6 |
7 | * Changed [package.json](package.json)
8 | * Changed [main.js](main.js)
9 | * Changed [server.js](server.js)
10 | * Changed [app/store/configureStore.development.js](app/store/configureStore.development.js)
11 | * Changed [app/containers/App.js](app/containers/App.js)
12 | * Removed [app/containers/DevTools.js](app/containers/DevTools.js)
13 |
14 | ## Usage
15 |
16 | ```bash
17 | # clone this repo
18 | $ git clone https://github.com/jhen0409/remotedev-extension.git
19 |
20 | # Install dependencies
21 | $ cd example && npm i
22 |
23 | # Run
24 | $ npm run dev
25 | ```
26 |
--------------------------------------------------------------------------------
/example/app/actions/counter.js:
--------------------------------------------------------------------------------
1 | export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
2 | export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
3 |
4 | export function increment() {
5 | return {
6 | type: INCREMENT_COUNTER
7 | };
8 | }
9 |
10 | export function decrement() {
11 | return {
12 | type: DECREMENT_COUNTER
13 | };
14 | }
15 |
16 | export function incrementIfOdd() {
17 | return (dispatch, getState) => {
18 | const { counter } = getState();
19 |
20 | if (counter % 2 === 0) {
21 | return;
22 | }
23 |
24 | dispatch(increment());
25 | };
26 | }
27 |
28 | export function incrementAsync(delay = 1000) {
29 | return dispatch => {
30 | setTimeout(() => {
31 | dispatch(increment());
32 | }, delay);
33 | };
34 | }
35 |
--------------------------------------------------------------------------------
/example/app/api/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhen0409/remotedev-extension/2f95aef4b8d1276d93b9126d90d1f6540bc0e679/example/app/api/.gitkeep
--------------------------------------------------------------------------------
/example/app/app.global.css:
--------------------------------------------------------------------------------
1 | body {
2 | position: relative;
3 | color: white;
4 | height: 100vh;
5 | background-color: #232C39;
6 | background-image: linear-gradient(45deg, rgba(0, 216, 255, .5) 10%, rgba(0, 1, 127, .7));
7 | font-family: Arial, Helvetica, Helvetica Neue;
8 | overflow-y: hidden;
9 | }
10 |
11 | h2 {
12 | margin: 0;
13 | font-size: 2.25rem;
14 | font-weight: bold;
15 | letter-spacing: -.025em;
16 | color: #fff;
17 | }
18 |
19 | p {
20 | font-size: 24px;
21 | }
22 |
23 | li {
24 | list-style: none;
25 | }
26 |
27 | a {
28 | color: white;
29 | opacity: .75;
30 | text-decoration: none;
31 | }
32 |
33 | a:hover {
34 | opacity: 1;
35 | text-decoration: none;
36 | cursor: pointer;
37 | }
38 |
--------------------------------------------------------------------------------
/example/app/app.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Hello Electron React!
6 |
7 |
17 |
18 |
19 |
20 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/example/app/app.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhen0409/remotedev-extension/2f95aef4b8d1276d93b9126d90d1f6540bc0e679/example/app/app.icns
--------------------------------------------------------------------------------
/example/app/components/Counter.css:
--------------------------------------------------------------------------------
1 | .backButton {
2 | position: absolute;
3 | }
4 |
5 | .counter {
6 | position: absolute;
7 | top: 30%;
8 | left: 45%;
9 | font-size: 10rem;
10 | font-weight: bold;
11 | letter-spacing: -.025em;
12 | }
13 |
14 | .btnGroup {
15 | position: relative;
16 | top: 500px;
17 | width: 480px;
18 | margin: 0 auto;
19 | }
20 |
21 | .btn {
22 | font-size: 1.6rem;
23 | font-weight: bold;
24 | background-color: #fff;
25 | border-radius: 50%;
26 | margin: 10px;
27 | width: 100px;
28 | height: 100px;
29 | opacity: .7;
30 | cursor: pointer;
31 | font-family: Arial, Helvetica, Helvetica Neue;
32 | }
33 |
34 | .btn:hover {
35 | color: white;
36 | background-color: rgba(0, 0, 0, 0.5);
37 | }
38 |
--------------------------------------------------------------------------------
/example/app/components/Counter.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 | import { Link } from 'react-router';
3 | import styles from './Counter.css';
4 |
5 | class Counter extends Component {
6 | static propTypes = {
7 | increment: PropTypes.func.isRequired,
8 | incrementIfOdd: PropTypes.func.isRequired,
9 | incrementAsync: PropTypes.func.isRequired,
10 | decrement: PropTypes.func.isRequired,
11 | counter: PropTypes.number.isRequired
12 | };
13 |
14 | render() {
15 | const { increment, incrementIfOdd, incrementAsync, decrement, counter } = this.props;
16 | return (
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | {counter}
25 |
26 |
27 |
30 |
33 |
34 |
35 |
36 |
37 | );
38 | }
39 | }
40 |
41 | export default Counter;
42 |
--------------------------------------------------------------------------------
/example/app/components/Home.css:
--------------------------------------------------------------------------------
1 | .container {
2 | position: absolute;
3 | top: 30%;
4 | left: 10px;
5 | text-align: center;
6 | }
7 |
8 | .container h2 {
9 | font-size: 5rem;
10 | }
11 |
12 | .container a {
13 | font-size: 1.4rem;
14 | }
15 |
--------------------------------------------------------------------------------
/example/app/components/Home.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import { Link } from 'react-router';
3 | import styles from './Home.css';
4 |
5 |
6 | export default class Home extends Component {
7 | render() {
8 | return (
9 |
10 |
11 |
Home
12 | to Counter
13 |
14 |
15 | );
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example/app/containers/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component, PropTypes } from 'react';
2 |
3 | export default class App extends Component {
4 | static propTypes = {
5 | children: PropTypes.element.isRequired
6 | };
7 |
8 | render() {
9 | return (
10 | {this.props.children}
11 | );
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/example/app/containers/CounterPage.js:
--------------------------------------------------------------------------------
1 | import { bindActionCreators } from 'redux';
2 | import { connect } from 'react-redux';
3 | import Counter from '../components/Counter';
4 | import * as CounterActions from '../actions/counter';
5 |
6 | function mapStateToProps(state) {
7 | return {
8 | counter: state.counter
9 | };
10 | }
11 |
12 | function mapDispatchToProps(dispatch) {
13 | return bindActionCreators(CounterActions, dispatch);
14 | }
15 |
16 | export default connect(mapStateToProps, mapDispatchToProps)(Counter);
17 |
--------------------------------------------------------------------------------
/example/app/containers/HomePage.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Home from '../components/Home';
3 |
4 | export default class HomePage extends Component {
5 | render() {
6 | return (
7 |
8 | );
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/example/app/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render } from 'react-dom';
3 | import { Provider } from 'react-redux';
4 | import { Router, hashHistory } from 'react-router';
5 | import { syncHistoryWithStore } from 'react-router-redux';
6 | import routes from './routes';
7 | import configureStore from './store/configureStore';
8 | import './app.global.css';
9 |
10 | const store = configureStore();
11 | const history = syncHistoryWithStore(hashHistory, store);
12 |
13 | render(
14 |
15 |
16 | ,
17 | document.getElementById('root')
18 | );
19 |
--------------------------------------------------------------------------------
/example/app/reducers/counter.js:
--------------------------------------------------------------------------------
1 | import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter';
2 |
3 | export default function counter(state = 0, action) {
4 | switch (action.type) {
5 | case INCREMENT_COUNTER:
6 | return state + 1;
7 | case DECREMENT_COUNTER:
8 | return state - 1;
9 | default:
10 | return state;
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/example/app/reducers/index.js:
--------------------------------------------------------------------------------
1 | import { combineReducers } from 'redux';
2 | import { routerReducer as routing } from 'react-router-redux';
3 | import counter from './counter';
4 |
5 | const rootReducer = combineReducers({
6 | counter,
7 | routing
8 | });
9 |
10 | export default rootReducer;
11 |
--------------------------------------------------------------------------------
/example/app/routes.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Route, IndexRoute } from 'react-router';
3 | import App from './containers/App';
4 | import HomePage from './containers/HomePage';
5 | import CounterPage from './containers/CounterPage';
6 |
7 |
8 | export default (
9 |
10 |
11 |
12 |
13 | );
14 |
--------------------------------------------------------------------------------
/example/app/store/configureStore.development.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware, compose } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import createLogger from 'redux-logger';
4 | import { hashHistory } from 'react-router';
5 | import { routerMiddleware } from 'react-router-redux';
6 | import rootReducer from '../reducers';
7 | import devTools from 'remote-redux-devtools';
8 |
9 | const logger = createLogger({
10 | level: 'info',
11 | collapsed: true,
12 | });
13 |
14 | const router = routerMiddleware(hashHistory);
15 |
16 | const enhancer = compose(
17 | applyMiddleware(thunk, router, logger),
18 | devTools({
19 | name: 'Electron',
20 | hostname: 'localhost',
21 | port: 8000
22 | })
23 | );
24 |
25 | export default function configureStore(initialState) {
26 | const store = createStore(rootReducer, initialState, enhancer);
27 |
28 | if (module.hot) {
29 | module.hot.accept('../reducers', () =>
30 | store.replaceReducer(require('../reducers'))
31 | );
32 | }
33 |
34 | return store;
35 | }
36 |
--------------------------------------------------------------------------------
/example/app/store/configureStore.js:
--------------------------------------------------------------------------------
1 | if (process.env.NODE_ENV === 'production') {
2 | module.exports = require('./configureStore.production');
3 | } else {
4 | module.exports = require('./configureStore.development');
5 | }
6 |
--------------------------------------------------------------------------------
/example/app/store/configureStore.production.js:
--------------------------------------------------------------------------------
1 | import { createStore, applyMiddleware } from 'redux';
2 | import thunk from 'redux-thunk';
3 | import { hashHistory } from 'react-router';
4 | import { routerMiddleware } from 'react-router-redux';
5 | import rootReducer from '../reducers';
6 |
7 | const router = routerMiddleware(hashHistory);
8 |
9 | const enhancer = applyMiddleware(thunk, router);
10 |
11 | export default function configureStore(initialState) {
12 | return createStore(rootReducer, initialState, enhancer);
13 | }
14 |
--------------------------------------------------------------------------------
/example/app/utils/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhen0409/remotedev-extension/2f95aef4b8d1276d93b9126d90d1f6540bc0e679/example/app/utils/.gitkeep
--------------------------------------------------------------------------------
/example/main.js:
--------------------------------------------------------------------------------
1 | /* eslint strict: 0 */
2 | 'use strict';
3 |
4 | process.env.NODE_ENV = process.env.NODE_ENV || 'production';
5 |
6 | const electron = require('electron');
7 | const app = electron.app;
8 | const BrowserWindow = electron.BrowserWindow;
9 | const Menu = electron.Menu;
10 | const crashReporter = electron.crashReporter;
11 | const shell = electron.shell;
12 | let menu;
13 | let template;
14 | let mainWindow = null;
15 |
16 | crashReporter.start();
17 |
18 | app.on('window-all-closed', () => {
19 | if (process.platform !== 'darwin') app.quit();
20 | });
21 |
22 | app.on('ready', () => {
23 | mainWindow = new BrowserWindow({ width: 1024, height: 728 });
24 |
25 | mainWindow.loadURL(`file://${__dirname}/app/app.html`);
26 |
27 | mainWindow.on('closed', () => {
28 | mainWindow = null;
29 | });
30 |
31 | if (process.env.NODE_ENV === 'development') {
32 | // Add DevTools Extension
33 | BrowserWindow.removeDevToolsExtension('RemoteDev DevTools');
34 | BrowserWindow.addDevToolsExtension('node_modules/remotedev-extension/dist');
35 | mainWindow.openDevTools();
36 | }
37 |
38 | if (process.platform === 'darwin') {
39 | template = [{
40 | label: 'Electron',
41 | submenu: [{
42 | label: 'About ElectronReact',
43 | selector: 'orderFrontStandardAboutPanel:'
44 | }, {
45 | type: 'separator'
46 | }, {
47 | label: 'Services',
48 | submenu: []
49 | }, {
50 | type: 'separator'
51 | }, {
52 | label: 'Hide ElectronReact',
53 | accelerator: 'Command+H',
54 | selector: 'hide:'
55 | }, {
56 | label: 'Hide Others',
57 | accelerator: 'Command+Shift+H',
58 | selector: 'hideOtherApplications:'
59 | }, {
60 | label: 'Show All',
61 | selector: 'unhideAllApplications:'
62 | }, {
63 | type: 'separator'
64 | }, {
65 | label: 'Quit',
66 | accelerator: 'Command+Q',
67 | click() {
68 | app.quit();
69 | }
70 | }]
71 | }, {
72 | label: 'Edit',
73 | submenu: [{
74 | label: 'Undo',
75 | accelerator: 'Command+Z',
76 | selector: 'undo:'
77 | }, {
78 | label: 'Redo',
79 | accelerator: 'Shift+Command+Z',
80 | selector: 'redo:'
81 | }, {
82 | type: 'separator'
83 | }, {
84 | label: 'Cut',
85 | accelerator: 'Command+X',
86 | selector: 'cut:'
87 | }, {
88 | label: 'Copy',
89 | accelerator: 'Command+C',
90 | selector: 'copy:'
91 | }, {
92 | label: 'Paste',
93 | accelerator: 'Command+V',
94 | selector: 'paste:'
95 | }, {
96 | label: 'Select All',
97 | accelerator: 'Command+A',
98 | selector: 'selectAll:'
99 | }]
100 | }, {
101 | label: 'View',
102 | submenu: (process.env.NODE_ENV === 'development') ? [{
103 | label: 'Reload',
104 | accelerator: 'Command+R',
105 | click() {
106 | mainWindow.restart();
107 | }
108 | }, {
109 | label: 'Toggle Full Screen',
110 | accelerator: 'Ctrl+Command+F',
111 | click() {
112 | mainWindow.setFullScreen(!mainWindow.isFullScreen());
113 | }
114 | }, {
115 | label: 'Toggle Developer Tools',
116 | accelerator: 'Alt+Command+I',
117 | click() {
118 | mainWindow.toggleDevTools();
119 | }
120 | }] : [{
121 | label: 'Toggle Full Screen',
122 | accelerator: 'Ctrl+Command+F',
123 | click() {
124 | mainWindow.setFullScreen(!mainWindow.isFullScreen());
125 | }
126 | }]
127 | }, {
128 | label: 'Window',
129 | submenu: [{
130 | label: 'Minimize',
131 | accelerator: 'Command+M',
132 | selector: 'performMiniaturize:'
133 | }, {
134 | label: 'Close',
135 | accelerator: 'Command+W',
136 | selector: 'performClose:'
137 | }, {
138 | type: 'separator'
139 | }, {
140 | label: 'Bring All to Front',
141 | selector: 'arrangeInFront:'
142 | }]
143 | }, {
144 | label: 'Help',
145 | submenu: [{
146 | label: 'Learn More',
147 | click() {
148 | shell.openExternal('http://electron.atom.io');
149 | }
150 | }, {
151 | label: 'Documentation',
152 | click() {
153 | shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme');
154 | }
155 | }, {
156 | label: 'Community Discussions',
157 | click() {
158 | shell.openExternal('https://discuss.atom.io/c/electron');
159 | }
160 | }, {
161 | label: 'Search Issues',
162 | click() {
163 | shell.openExternal('https://github.com/atom/electron/issues');
164 | }
165 | }]
166 | }];
167 |
168 | menu = Menu.buildFromTemplate(template);
169 | Menu.setApplicationMenu(menu);
170 | } else {
171 | template = [{
172 | label: '&File',
173 | submenu: [{
174 | label: '&Open',
175 | accelerator: 'Ctrl+O'
176 | }, {
177 | label: '&Close',
178 | accelerator: 'Ctrl+W',
179 | click() {
180 | mainWindow.close();
181 | }
182 | }]
183 | }, {
184 | label: '&View',
185 | submenu: (process.env.NODE_ENV === 'development') ? [{
186 | label: '&Reload',
187 | accelerator: 'Ctrl+R',
188 | click() {
189 | mainWindow.restart();
190 | }
191 | }, {
192 | label: 'Toggle &Full Screen',
193 | accelerator: 'F11',
194 | click() {
195 | mainWindow.setFullScreen(!mainWindow.isFullScreen());
196 | }
197 | }, {
198 | label: 'Toggle &Developer Tools',
199 | accelerator: 'Alt+Ctrl+I',
200 | click() {
201 | mainWindow.toggleDevTools();
202 | }
203 | }] : [{
204 | label: 'Toggle &Full Screen',
205 | accelerator: 'F11',
206 | click() {
207 | mainWindow.setFullScreen(!mainWindow.isFullScreen());
208 | }
209 | }]
210 | }, {
211 | label: 'Help',
212 | submenu: [{
213 | label: 'Learn More',
214 | click() {
215 | shell.openExternal('http://electron.atom.io');
216 | }
217 | }, {
218 | label: 'Documentation',
219 | click() {
220 | shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme');
221 | }
222 | }, {
223 | label: 'Community Discussions',
224 | click() {
225 | shell.openExternal('https://discuss.atom.io/c/electron');
226 | }
227 | }, {
228 | label: 'Search Issues',
229 | click() {
230 | shell.openExternal('https://github.com/atom/electron/issues');
231 | }
232 | }]
233 | }];
234 | menu = Menu.buildFromTemplate(template);
235 | mainWindow.setMenu(menu);
236 | }
237 | });
238 |
--------------------------------------------------------------------------------
/example/package.js:
--------------------------------------------------------------------------------
1 | /* eslint strict: 0, no-shadow: 0, no-unused-vars: 0, no-console: 0 */
2 | 'use strict';
3 |
4 | const os = require('os');
5 | const webpack = require('webpack');
6 | const cfg = require('./webpack.config.production.js');
7 | const packager = require('electron-packager');
8 | const del = require('del');
9 | const exec = require('child_process').exec;
10 | const argv = require('minimist')(process.argv.slice(2));
11 | const pkg = require('./package.json');
12 | const devDeps = Object.keys(pkg.devDependencies);
13 |
14 | const appName = argv.name || argv.n || pkg.productName;
15 | const shouldUseAsar = argv.asar || argv.a || false;
16 | const shouldBuildAll = argv.all || false;
17 |
18 |
19 | const DEFAULT_OPTS = {
20 | dir: './',
21 | name: appName,
22 | asar: shouldUseAsar,
23 | ignore: [
24 | '/test($|/)',
25 | '/tools($|/)',
26 | '/release($|/)'
27 | ].concat(devDeps.map(name => `/node_modules/${name}($|/)`))
28 | };
29 |
30 | const icon = argv.icon || argv.i || 'app/app';
31 |
32 | if (icon) {
33 | DEFAULT_OPTS.icon = icon;
34 | }
35 |
36 | const version = argv.version || argv.v;
37 |
38 | if (version) {
39 | DEFAULT_OPTS.version = version;
40 | startPack();
41 | } else {
42 | // use the same version as the currently-installed electron-prebuilt
43 | exec('npm list electron-prebuilt --dev', (err, stdout) => {
44 | if (err) {
45 | DEFAULT_OPTS.version = '0.37.2';
46 | } else {
47 | DEFAULT_OPTS.version = stdout.split('electron-prebuilt@')[1].replace(/\s/g, '');
48 | }
49 |
50 | startPack();
51 | });
52 | }
53 |
54 |
55 | function startPack() {
56 | console.log('start pack...');
57 | webpack(cfg, (err, stats) => {
58 | if (err) return console.error(err);
59 | del('release')
60 | .then(paths => {
61 | if (shouldBuildAll) {
62 | // build for all platforms
63 | const archs = ['ia32', 'x64'];
64 | const platforms = ['linux', 'win32', 'darwin'];
65 |
66 | platforms.forEach(plat => {
67 | archs.forEach(arch => {
68 | pack(plat, arch, log(plat, arch));
69 | });
70 | });
71 | } else {
72 | // build for current platform only
73 | pack(os.platform(), os.arch(), log(os.platform(), os.arch()));
74 | }
75 | })
76 | .catch(err => {
77 | console.error(err);
78 | });
79 | });
80 | }
81 |
82 | function pack(plat, arch, cb) {
83 | // there is no darwin ia32 electron
84 | if (plat === 'darwin' && arch === 'ia32') return;
85 |
86 | const iconObj = {
87 | icon: DEFAULT_OPTS.icon + (() => {
88 | let extension = '.png';
89 | if (plat === 'darwin') {
90 | extension = '.icns';
91 | } else if (plat === 'win32') {
92 | extension = '.ico';
93 | }
94 | return extension;
95 | })()
96 | };
97 |
98 | const opts = Object.assign({}, DEFAULT_OPTS, iconObj, {
99 | platform: plat,
100 | arch,
101 | prune: true,
102 | 'app-version': pkg.version || DEFAULT_OPTS.version,
103 | out: `release/${plat}-${arch}`
104 | });
105 |
106 | packager(opts, cb);
107 | }
108 |
109 |
110 | function log(plat, arch) {
111 | return (err, filepath) => {
112 | if (err) return console.error(err);
113 | console.log(`${plat}-${arch} finished!`);
114 | };
115 | }
116 |
--------------------------------------------------------------------------------
/example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "electron-react-boilerplate",
3 | "productName": "ElectronReact",
4 | "version": "0.9.0",
5 | "description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development",
6 | "main": "main.js",
7 | "scripts": {
8 | "test": "cross-env NODE_ENV=test mocha --compilers js:babel-core/register --recursive --require ./test/setup.js test/**/*.spec.js",
9 | "test-watch": "npm test -- --watch",
10 | "test-e2e": "cross-env NODE_ENV=test mocha --compilers js:babel-core/register --require ./test/setup.js --require co-mocha ./test/e2e.js",
11 | "lint": "eslint app test *.js",
12 | "hot-server": "node server.js",
13 | "build": "cross-env NODE_ENV=production webpack --config webpack.config.production.js --progress --profile --colors",
14 | "start": "cross-env NODE_ENV=production electron ./",
15 | "start-hot": "cross-env HOT=1 NODE_ENV=development electron ./",
16 | "package": "cross-env NODE_ENV=production node package.js",
17 | "package-all": "npm run package -- --all",
18 | "postinstall": "node node_modules/fbjs-scripts/node/check-dev-engines.js package.json",
19 | "dev": "concurrently --kill-others \"npm run hot-server\" \"npm run start-hot\""
20 | },
21 | "bin": {
22 | "electron": "./node_modules/.bin/electron"
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/chentsulin/electron-react-boilerplate.git"
27 | },
28 | "author": {
29 | "name": "C. T. Lin",
30 | "email": "chentsulin@gmail.com",
31 | "url": "https://github.com/chentsulin"
32 | },
33 | "license": "MIT",
34 | "bugs": {
35 | "url": "https://github.com/chentsulin/electron-react-boilerplate/issues"
36 | },
37 | "keywords": [
38 | "electron",
39 | "boilerplate",
40 | "react",
41 | "react-router",
42 | "flux",
43 | "webpack",
44 | "react-hot"
45 | ],
46 | "homepage": "https://github.com/chentsulin/electron-react-boilerplate#readme",
47 | "devDependencies": {
48 | "asar": "^0.10.0",
49 | "babel-core": "^6.7.4",
50 | "babel-eslint": "^6.0.0",
51 | "babel-loader": "^6.2.4",
52 | "babel-plugin-add-module-exports": "^0.1.2",
53 | "babel-plugin-webpack-loaders": "^0.4.0",
54 | "babel-polyfill": "^6.7.4",
55 | "babel-preset-es2015": "^6.6.0",
56 | "babel-preset-react": "^6.5.0",
57 | "babel-preset-react-hmre": "^1.1.1",
58 | "babel-preset-stage-0": "^6.5.0",
59 | "chai": "^3.5.0",
60 | "chromedriver": "^2.21.2",
61 | "co-mocha": "^1.1.2",
62 | "concurrently": "^2.0.0",
63 | "cross-env": "^1.0.7",
64 | "css-loader": "^0.23.1",
65 | "del": "^2.2.0",
66 | "electron-packager": "^6.0.0",
67 | "electron-prebuilt": "^0.37.2",
68 | "electron-rebuild": "^1.1.3",
69 | "eslint": "^2.5.3",
70 | "eslint-config-airbnb": "^6.2.0",
71 | "eslint-plugin-react": "^4.2.3",
72 | "express": "^4.13.4",
73 | "extract-text-webpack-plugin": "^1.0.1",
74 | "fbjs-scripts": "^0.5.0",
75 | "jsdom": "^8.2.0",
76 | "json-loader": "^0.5.4",
77 | "minimist": "^1.2.0",
78 | "mocha": "^2.4.5",
79 | "node-libs-browser": "^1.0.0",
80 | "react-addons-test-utils": "^0.14.7",
81 | "redux-logger": "^2.6.1",
82 | "remote-redux-devtools": "^0.1.5",
83 | "remotedev-extension": "0.0.9",
84 | "selenium-webdriver": "^2.53.1",
85 | "sinon": "^1.17.3",
86 | "style-loader": "^0.13.1",
87 | "webpack": "^1.12.14",
88 | "webpack-dev-middleware": "^1.6.1",
89 | "webpack-hot-middleware": "^2.10.0",
90 | "webpack-target-electron-renderer": "^0.4.0"
91 | },
92 | "dependencies": {
93 | "css-modules-require-hook": "^4.0.0",
94 | "electron-debug": "^0.5.2",
95 | "font-awesome": "^4.5.0",
96 | "postcss": "^5.0.19",
97 | "react": "^0.14.7",
98 | "react-dom": "^0.14.7",
99 | "react-redux": "^4.4.1",
100 | "react-router": "^2.0.1",
101 | "react-router-redux": "^4.0.0",
102 | "redux": "^3.3.1",
103 | "redux-thunk": "^2.0.1"
104 | },
105 | "devEngines": {
106 | "node": "4.x || 5.x",
107 | "npm": "2.x || 3.x"
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/example/server.js:
--------------------------------------------------------------------------------
1 | /* eslint strict: 0, no-console: 0 */
2 | 'use strict';
3 |
4 | const express = require('express');
5 | const webpack = require('webpack');
6 | const config = require('./webpack.config.development');
7 |
8 | const app = express();
9 | const compiler = webpack(config);
10 |
11 | const PORT = 3000;
12 |
13 | app.use(require('webpack-dev-middleware')(compiler, {
14 | publicPath: config.output.publicPath,
15 | stats: {
16 | colors: true
17 | }
18 | }));
19 |
20 | app.use(require('webpack-hot-middleware')(compiler));
21 |
22 | app.listen(PORT, 'localhost', err => {
23 | if (err) {
24 | console.log(err);
25 | return;
26 | }
27 |
28 | console.log(`Listening at http://localhost:${PORT}`);
29 | });
30 |
31 | const remotedev = require('remotedev-extension');
32 | remotedev({
33 | hostname: 'localhost',
34 | port: 8000,
35 | runserver: true,
36 | 'ui-no-buttonbar': true
37 | });
38 |
--------------------------------------------------------------------------------
/example/test/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "mocha": true
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/example/test/actions/counter.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint no-unused-expressions: 0 */
2 | import { expect } from 'chai';
3 | import { spy } from 'sinon';
4 | import * as actions from '../../app/actions/counter';
5 |
6 |
7 | describe('actions', () => {
8 | it('increment should create increment action', () => {
9 | expect(actions.increment()).to.deep.equal({ type: actions.INCREMENT_COUNTER });
10 | });
11 |
12 | it('decrement should create decrement action', () => {
13 | expect(actions.decrement()).to.deep.equal({ type: actions.DECREMENT_COUNTER });
14 | });
15 |
16 | it('incrementIfOdd should create increment action', () => {
17 | const fn = actions.incrementIfOdd();
18 | expect(fn).to.be.a('function');
19 | const dispatch = spy();
20 | const getState = () => ({ counter: 1 });
21 | fn(dispatch, getState);
22 | expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).to.be.true;
23 | });
24 |
25 | it('incrementIfOdd shouldnt create increment action if counter is even', () => {
26 | const fn = actions.incrementIfOdd();
27 | const dispatch = spy();
28 | const getState = () => ({ counter: 2 });
29 | fn(dispatch, getState);
30 | expect(dispatch.called).to.be.false;
31 | });
32 |
33 | // There's no nice way to test this at the moment...
34 | it('incrementAsync', (done) => {
35 | const fn = actions.incrementAsync(1);
36 | expect(fn).to.be.a('function');
37 | const dispatch = spy();
38 | fn(dispatch);
39 | setTimeout(() => {
40 | expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).to.be.true;
41 | done();
42 | }, 5);
43 | });
44 | });
45 |
--------------------------------------------------------------------------------
/example/test/components/Counter.spec.js:
--------------------------------------------------------------------------------
1 | /* eslint no-unused-expressions: 0 */
2 | import { expect } from 'chai';
3 | import { spy } from 'sinon';
4 | import React from 'react';
5 | import {
6 | renderIntoDocument,
7 | scryRenderedDOMComponentsWithTag,
8 | findRenderedDOMComponentWithClass,
9 | Simulate
10 | } from 'react-addons-test-utils';
11 | import Counter from '../../app/components/Counter';
12 |
13 |
14 | function setup() {
15 | const actions = {
16 | increment: spy(),
17 | incrementIfOdd: spy(),
18 | incrementAsync: spy(),
19 | decrement: spy()
20 | };
21 | const component = renderIntoDocument();
22 | return {
23 | component,
24 | actions,
25 | buttons: scryRenderedDOMComponentsWithTag(component, 'button').map(button => button),
26 | p: findRenderedDOMComponentWithClass(component, 'counter')
27 | };
28 | }
29 |
30 |
31 | describe('Counter component', () => {
32 | it('should display count', () => {
33 | const { p } = setup();
34 | expect(p.textContent).to.match(/^1$/);
35 | });
36 |
37 | it('first button should call increment', () => {
38 | const { buttons, actions } = setup();
39 | Simulate.click(buttons[0]);
40 | expect(actions.increment.called).to.be.true;
41 | });
42 |
43 | it('second button should call decrement', () => {
44 | const { buttons, actions } = setup();
45 | Simulate.click(buttons[1]);
46 | expect(actions.decrement.called).to.be.true;
47 | });
48 |
49 | it('third button should call incrementIfOdd', () => {
50 | const { buttons, actions } = setup();
51 | Simulate.click(buttons[2]);
52 | expect(actions.incrementIfOdd.called).to.be.true;
53 | });
54 |
55 | it('fourth button should call incrementAsync', () => {
56 | const { buttons, actions } = setup();
57 | Simulate.click(buttons[3]);
58 | expect(actions.incrementAsync.called).to.be.true;
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/example/test/containers/CounterPage.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import React from 'react';
3 | import {
4 | renderIntoDocument,
5 | scryRenderedDOMComponentsWithTag,
6 | findRenderedDOMComponentWithClass,
7 | Simulate
8 | } from 'react-addons-test-utils';
9 | import { Provider } from 'react-redux';
10 | import CounterPage from '../../app/containers/CounterPage';
11 | import configureStore from '../../app/store/configureStore';
12 |
13 |
14 | function setup(initialState) {
15 | const store = configureStore(initialState);
16 | const app = renderIntoDocument(
17 |
18 |
19 |
20 | );
21 | return {
22 | app,
23 | buttons: scryRenderedDOMComponentsWithTag(app, 'button').map(button => button),
24 | p: findRenderedDOMComponentWithClass(app, 'counter')
25 | };
26 | }
27 |
28 |
29 | describe('containers', () => {
30 | describe('App', () => {
31 | it('should display initial count', () => {
32 | const { p } = setup();
33 | expect(p.textContent).to.match(/^0$/);
34 | });
35 |
36 | it('should display updated count after increment button click', () => {
37 | const { buttons, p } = setup();
38 | Simulate.click(buttons[0]);
39 | expect(p.textContent).to.match(/^1$/);
40 | });
41 |
42 | it('should display updated count after descrement button click', () => {
43 | const { buttons, p } = setup();
44 | Simulate.click(buttons[1]);
45 | expect(p.textContent).to.match(/^-1$/);
46 | });
47 |
48 | it('shouldnt change if even and if odd button clicked', () => {
49 | const { buttons, p } = setup();
50 | Simulate.click(buttons[2]);
51 | expect(p.textContent).to.match(/^0$/);
52 | });
53 |
54 | it('should change if odd and if odd button clicked', () => {
55 | const { buttons, p } = setup({ counter: 1 });
56 | Simulate.click(buttons[2]);
57 | expect(p.textContent).to.match(/^2$/);
58 | });
59 | });
60 | });
61 |
--------------------------------------------------------------------------------
/example/test/e2e.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 | import chromedriver from 'chromedriver';
3 | import webdriver from 'selenium-webdriver';
4 | import { expect } from 'chai';
5 | import electronPath from 'electron-prebuilt';
6 | import homeStyles from '../app/components/Home.css';
7 | import counterStyles from '../app/components/Counter.css';
8 |
9 | chromedriver.start(); // on port 9515
10 | process.on('exit', chromedriver.stop);
11 |
12 | const delay = time => new Promise(resolve => setTimeout(resolve, time));
13 |
14 | describe('main window', function spec() {
15 | this.timeout(5000);
16 |
17 | before(async () => {
18 | await delay(1000); // wait chromedriver start time
19 | this.driver = new webdriver.Builder()
20 | .usingServer('http://localhost:9515')
21 | .withCapabilities({
22 | chromeOptions: {
23 | binary: electronPath,
24 | args: [`app=${path.resolve()}`]
25 | }
26 | })
27 | .forBrowser('electron')
28 | .build();
29 | });
30 |
31 | after(async () => {
32 | await this.driver.quit();
33 | });
34 |
35 | const findCounter = () => this.driver.findElement(webdriver.By.className(counterStyles.counter));
36 |
37 | const findButtons = () => this.driver.findElements(webdriver.By.className(counterStyles.btn));
38 |
39 | it('should open window', async () => {
40 | const title = await this.driver.getTitle();
41 | expect(title).to.equal('Hello Electron React!');
42 | });
43 |
44 | it('should to Counter with click "to Counter" link', async () => {
45 | const link = await this.driver.findElement(webdriver.By.css(`.${homeStyles.container} > a`));
46 | link.click();
47 |
48 | const counter = await findCounter();
49 | expect(await counter.getText()).to.equal('0');
50 | });
51 |
52 | it('should display updated count after increment button click', async () => {
53 | const buttons = await findButtons();
54 | buttons[0].click();
55 |
56 | const counter = await findCounter();
57 | expect(await counter.getText()).to.equal('1');
58 | });
59 |
60 | it('should display updated count after descrement button click', async () => {
61 | const buttons = await findButtons();
62 | const counter = await findCounter();
63 |
64 | buttons[1].click(); // -
65 |
66 | expect(await counter.getText()).to.equal('0');
67 | });
68 |
69 | it('shouldnt change if even and if odd button clicked', async () => {
70 | const buttons = await findButtons();
71 | const counter = await findCounter();
72 | buttons[2].click(); // odd
73 |
74 | expect(await counter.getText()).to.equal('0');
75 | });
76 |
77 | it('should change if odd and if odd button clicked', async () => {
78 | const buttons = await findButtons();
79 | const counter = await findCounter();
80 |
81 | buttons[0].click(); // +
82 | buttons[2].click(); // odd
83 |
84 | expect(await counter.getText()).to.equal('2');
85 | });
86 |
87 | it('should change if async button clicked and a second later', async () => {
88 | const buttons = await findButtons();
89 | const counter = await findCounter();
90 | buttons[3].click(); // async
91 |
92 | expect(await counter.getText()).to.equal('2');
93 |
94 | await this.driver.wait(() =>
95 | counter.getText().then(text => text === '3')
96 | , 1000, 'count not as expected');
97 | });
98 |
99 | it('should back to home if back button clicked', async () => {
100 | const link = await this.driver.findElement(
101 | webdriver.By.css(`.${counterStyles.backButton} > a`)
102 | );
103 | link.click();
104 |
105 | await this.driver.findElement(webdriver.By.className(homeStyles.container));
106 | });
107 | });
108 |
--------------------------------------------------------------------------------
/example/test/example.js:
--------------------------------------------------------------------------------
1 | /* eslint func-names: 0 */
2 | import { expect } from 'chai';
3 |
4 |
5 | describe('description', () => {
6 | it('description', () => {
7 | expect(1 + 2).to.equal(3);
8 | });
9 | });
10 |
--------------------------------------------------------------------------------
/example/test/reducers/counter.spec.js:
--------------------------------------------------------------------------------
1 | import { expect } from 'chai';
2 | import counter from '../../app/reducers/counter';
3 | import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../app/actions/counter';
4 |
5 |
6 | describe('reducers', () => {
7 | describe('counter', () => {
8 | it('should handle initial state', () => {
9 | expect(counter(undefined, {})).to.equal(0);
10 | });
11 |
12 | it('should handle INCREMENT_COUNTER', () => {
13 | expect(counter(1, { type: INCREMENT_COUNTER })).to.equal(2);
14 | });
15 |
16 | it('should handle DECREMENT_COUNTER', () => {
17 | expect(counter(1, { type: DECREMENT_COUNTER })).to.equal(0);
18 | });
19 |
20 | it('should handle unknown action type', () => {
21 | expect(counter(1, { type: 'unknown' })).to.equal(1);
22 | });
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/example/test/setup.js:
--------------------------------------------------------------------------------
1 | import 'babel-polyfill';
2 | import { jsdom } from 'jsdom';
3 |
4 | global.document = jsdom('');
5 | global.window = document.defaultView;
6 | global.navigator = global.window.navigator;
7 | window.localStorage = window.sessionStorage = {
8 | getItem(key) {
9 | return this[key];
10 | },
11 | setItem(key, value) {
12 | this[key] = value;
13 | },
14 | removeItem(key) {
15 | this[key] = undefined;
16 | },
17 | };
18 |
--------------------------------------------------------------------------------
/example/webpack.config.base.js:
--------------------------------------------------------------------------------
1 | /* eslint strict: 0 */
2 | 'use strict';
3 |
4 | const path = require('path');
5 |
6 | module.exports = {
7 | module: {
8 | loaders: [{
9 | test: /\.jsx?$/,
10 | loaders: ['babel-loader'],
11 | exclude: /node_modules/
12 | }, {
13 | test: /\.json$/,
14 | loader: 'json-loader'
15 | }]
16 | },
17 | output: {
18 | path: path.join(__dirname, 'dist'),
19 | filename: 'bundle.js',
20 | libraryTarget: 'commonjs2'
21 | },
22 | resolve: {
23 | extensions: ['', '.js', '.jsx'],
24 | packageMains: ['webpack', 'browser', 'web', 'browserify', ['jam', 'main'], 'main']
25 | },
26 | plugins: [
27 |
28 | ],
29 | externals: [
30 | // put your node 3rd party libraries which can't be built with webpack here
31 | // (mysql, mongodb, and so on..)
32 | ]
33 | };
34 |
--------------------------------------------------------------------------------
/example/webpack.config.development.js:
--------------------------------------------------------------------------------
1 | /* eslint strict: 0 */
2 | 'use strict';
3 |
4 | const webpack = require('webpack');
5 | const webpackTargetElectronRenderer = require('webpack-target-electron-renderer');
6 | const baseConfig = require('./webpack.config.base');
7 |
8 |
9 | const config = Object.create(baseConfig);
10 |
11 | config.debug = true;
12 |
13 | config.devtool = 'cheap-module-eval-source-map';
14 |
15 | config.entry = [
16 | 'webpack-hot-middleware/client?path=http://localhost:3000/__webpack_hmr',
17 | './app/index'
18 | ];
19 |
20 | config.output.publicPath = 'http://localhost:3000/dist/';
21 |
22 | config.module.loaders.push({
23 | test: /\.global\.css$/,
24 | loaders: [
25 | 'style-loader',
26 | 'css-loader?sourceMap'
27 | ]
28 | }, {
29 | test: /^((?!\.global).)*\.css$/,
30 | loaders: [
31 | 'style-loader',
32 | 'css-loader?modules&sourceMap&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
33 | ]
34 | });
35 |
36 |
37 | config.plugins.push(
38 | new webpack.HotModuleReplacementPlugin(),
39 | new webpack.NoErrorsPlugin(),
40 | new webpack.DefinePlugin({
41 | __DEV__: true,
42 | 'process.env': {
43 | NODE_ENV: JSON.stringify('development')
44 | }
45 | })
46 | );
47 |
48 | config.target = webpackTargetElectronRenderer(config);
49 |
50 | module.exports = config;
51 |
--------------------------------------------------------------------------------
/example/webpack.config.node.js:
--------------------------------------------------------------------------------
1 | // for babel-plugin-webpack-loaders
2 | const devConfigs = require('./webpack.config.development');
3 |
4 | module.exports = {
5 | output: {
6 | libraryTarget: 'commonjs2'
7 | },
8 | module: {
9 | loaders: devConfigs.module.loaders.slice(1) // remove babel-loader
10 | }
11 | };
12 |
--------------------------------------------------------------------------------
/example/webpack.config.production.js:
--------------------------------------------------------------------------------
1 | /* eslint strict: 0 */
2 | 'use strict';
3 |
4 | const webpack = require('webpack');
5 | const ExtractTextPlugin = require('extract-text-webpack-plugin');
6 | const webpackTargetElectronRenderer = require('webpack-target-electron-renderer');
7 | const baseConfig = require('./webpack.config.base');
8 |
9 |
10 | const config = Object.create(baseConfig);
11 |
12 | config.devtool = 'source-map';
13 |
14 | config.entry = './app/index';
15 |
16 | config.output.publicPath = '../dist/';
17 |
18 | config.module.loaders.push({
19 | test: /\.global\.css$/,
20 | loader: ExtractTextPlugin.extract(
21 | 'style-loader',
22 | 'css-loader'
23 | )
24 | }, {
25 | test: /^((?!\.global).)*\.css$/,
26 | loader: ExtractTextPlugin.extract(
27 | 'style-loader',
28 | 'css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]'
29 | )
30 | });
31 |
32 | config.plugins.push(
33 | new webpack.optimize.OccurenceOrderPlugin(),
34 | new webpack.DefinePlugin({
35 | __DEV__: false,
36 | 'process.env': {
37 | NODE_ENV: JSON.stringify('production')
38 | }
39 | }),
40 | new webpack.optimize.UglifyJsPlugin({
41 | compressor: {
42 | screw_ie8: true,
43 | warnings: false
44 | }
45 | }),
46 | new ExtractTextPlugin('style.css', { allChunks: true })
47 | );
48 |
49 | config.target = webpackTargetElectronRenderer(config);
50 |
51 | module.exports = config;
52 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "remotedev-extension",
3 | "version": "0.0.10",
4 | "description": "Use Redux DevTools in the Browser/Electron DevTools",
5 | "repository": {
6 | "type": "git",
7 | "url": "git+https://github.com/jhen0409/remotedev-extension.git"
8 | },
9 | "keywords": [
10 | "remote",
11 | "redux",
12 | "devtools",
13 | "remotedev",
14 | "react",
15 | "chrome",
16 | "opera",
17 | "electron",
18 | "extension"
19 | ],
20 | "files": [
21 | "bin",
22 | "dist"
23 | ],
24 | "main": "bin/extension.js",
25 | "bin": {
26 | "remotedev-extension": "bin/cli.js"
27 | },
28 | "scripts": {
29 | "compress": "node bin/cli.js && nodezip -c dist.zip dist",
30 | "build": "webpack --stats --progress",
31 | "prepublish": "node bin/cli.js && npm run build"
32 | },
33 | "author": "Jhen ",
34 | "license": "MIT",
35 | "devDependencies": {
36 | "node-zip": "^1.1.1",
37 | "react": "^0.14.7",
38 | "react-dom": "^0.14.7",
39 | "remotedev-app": "^0.3.4",
40 | "webpack": "^1.12.12"
41 | },
42 | "dependencies": {
43 | "minimist": "^1.2.0",
44 | "remotedev-server": "0.0.7"
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/devpanel.js:
--------------------------------------------------------------------------------
1 | const React = require('react');
2 | const ReactDOM = require('react-dom');
3 | const DevTools = require('remotedev-app');
4 |
5 | const remotedevOptions = window.remotedevOptions;
6 | const options = {
7 | socketOptions: remotedevOptions,
8 | noButtonBar: false
9 | };
10 |
11 | // prevent setting from previous UI setting
12 | if (remotedevOptions && remotedevOptions.noButtonBar) {
13 | options.noButtonBar = true;
14 | localStorage.removeItem('s:hostname');
15 | localStorage.removeItem('s:port');
16 | }
17 |
18 | ReactDOM.render(
19 | React.createElement(DevTools, options),
20 | document.querySelector('#root')
21 | );
22 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const webpack = require('webpack');
3 | const port = 3030;
4 |
5 | module.exports = {
6 | entry: './src/devpanel',
7 | output: {
8 | path: path.join(__dirname, 'dist/js'),
9 | filename: 'devpanel.bundle.js',
10 | publicPath: `/js/`
11 | },
12 | plugins: [
13 | new webpack.optimize.DedupePlugin(),
14 | new webpack.optimize.UglifyJsPlugin({
15 | comments: false,
16 | compressor: {
17 | warnings: false
18 | }
19 | }),
20 | new webpack.DefinePlugin({
21 | 'process.env': {
22 | 'NODE_ENV': JSON.stringify('production')
23 | }
24 | })
25 | ],
26 | resolve: {
27 | extensions: ['', '.js']
28 | }
29 | };
30 |
--------------------------------------------------------------------------------