Browse Source

first commit

renovate/lint-staged-8.x
Jack Mallers 7 years ago
commit
85485bb5eb
  1. 27
      .babelrc
  2. 20
      .editorconfig
  3. 53
      .eslintignore
  4. 50
      .eslintrc
  5. 26
      .flowconfig
  6. 4
      .gitattributes
  7. 52
      .gitignore
  8. 3
      .stylelintrc
  9. 42
      .travis.yml
  10. 19
      .vscode/settings.json
  11. 384
      CHANGELOG.md
  12. 22
      LICENSE
  13. 334
      README.md
  14. 22
      app/.eslintrc
  15. 41
      app/actions/counter.js
  16. 39
      app/app.global.css
  17. 46
      app/app.html
  18. BIN
      app/app.icns
  19. 37
      app/components/Counter.css
  20. 42
      app/components/Counter.js
  21. 14
      app/components/Home.css
  22. 17
      app/components/Home.js
  23. 17
      app/containers/App.js
  24. 16
      app/containers/CounterPage.js
  25. 11
      app/containers/HomePage.js
  26. 20
      app/containers/Root.js
  27. 27
      app/index.js
  28. 86
      app/main.dev.js
  29. 186
      app/menu.js
  30. 18
      app/package.json
  31. 21
      app/reducers/counter.js
  32. 11
      app/reducers/index.js
  33. 15
      app/routes.js
  34. 62
      app/store/configureStore.dev.js
  35. 6
      app/store/configureStore.js
  36. 17
      app/store/configureStore.prod.js
  37. 0
      app/utils/.gitkeep
  38. 4
      app/yarn.lock
  39. 35
      appveyor.yml
  40. 3
      flow-typed/module_vx.x.x.js
  41. 3
      internals/flow/CSSModule.js.flow
  42. 2
      internals/flow/WebpackAsset.js.flow
  43. BIN
      internals/img/eslint-padded-90.png
  44. BIN
      internals/img/eslint-padded.png
  45. BIN
      internals/img/eslint.png
  46. BIN
      internals/img/flow-padded-90.png
  47. BIN
      internals/img/flow-padded.png
  48. BIN
      internals/img/flow.png
  49. BIN
      internals/img/jest-padded-90.png
  50. BIN
      internals/img/jest-padded.png
  51. BIN
      internals/img/jest.png
  52. BIN
      internals/img/js-padded.png
  53. BIN
      internals/img/js.png
  54. BIN
      internals/img/npm.png
  55. BIN
      internals/img/react-padded-90.png
  56. BIN
      internals/img/react-padded.png
  57. BIN
      internals/img/react-router-padded-90.png
  58. BIN
      internals/img/react-router-padded.png
  59. BIN
      internals/img/react-router.png
  60. BIN
      internals/img/react.png
  61. BIN
      internals/img/redux-padded-90.png
  62. BIN
      internals/img/redux-padded.png
  63. BIN
      internals/img/redux.png
  64. BIN
      internals/img/webpack-padded-90.png
  65. BIN
      internals/img/webpack-padded.png
  66. BIN
      internals/img/webpack.png
  67. BIN
      internals/img/yarn-padded-90.png
  68. BIN
      internals/img/yarn-padded.png
  69. BIN
      internals/img/yarn.png
  70. 1
      internals/mocks/fileMock.js
  71. 24
      internals/scripts/CheckBuiltsExist.js
  72. 15
      internals/scripts/CheckNodeEnv.js
  73. 16382
      package-lock.json
  74. 210
      package.json
  75. BIN
      resources/icon.icns
  76. BIN
      resources/icon.ico
  77. BIN
      resources/icon.png
  78. BIN
      resources/icons/1024x1024.png
  79. BIN
      resources/icons/128x128.png
  80. BIN
      resources/icons/16x16.png
  81. BIN
      resources/icons/24x24.png
  82. BIN
      resources/icons/256x256.png
  83. BIN
      resources/icons/32x32.png
  84. BIN
      resources/icons/48x48.png
  85. BIN
      resources/icons/512x512.png
  86. BIN
      resources/icons/64x64.png
  87. BIN
      resources/icons/96x96.png
  88. 51
      resources/zap_1.svg
  89. 51
      resources/zap_2.svg
  90. 51
      resources/zap_3.svg
  91. 13
      test/.eslintrc
  92. 13
      test/actions/__snapshots__/counter.spec.js.snap
  93. 41
      test/actions/counter.spec.js
  94. 68
      test/components/Counter.spec.js
  95. 63
      test/components/__snapshots__/Counter.spec.js.snap
  96. 57
      test/containers/CounterPage.spec.js
  97. 113
      test/e2e/e2e.spec.js
  98. 5
      test/example.js
  99. 9
      test/reducers/__snapshots__/counter.spec.js.snap
  100. 22
      test/reducers/counter.spec.js

27
.babelrc

@ -0,0 +1,27 @@
{
"presets": [
["env", {
"targets": { "node": 7 },
"useBuiltIns": true
}],
"stage-0",
"react"
],
"plugins": ["add-module-exports", "dynamic-import-webpack"],
"env": {
"production": {
"presets": ["react-optimize"],
"plugins": ["babel-plugin-dev-expression"]
},
"development": {
"plugins": [
"transform-class-properties",
"transform-es2015-classes",
["flow-runtime", {
"assert": true,
"annotate": true
}]
]
}
}
}

20
.editorconfig

@ -0,0 +1,20 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{json,js,jsx,html,css,yml}]
indent_style = space
indent_size = 2
[.eslintrc]
indent_style = space
indent_size = 2
[*.md]
trim_trailing_whitespace = false

53
.eslintignore

@ -0,0 +1,53 @@
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
.eslintcache
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
app/node_modules
# OSX
.DS_Store
# flow-typed
flow-typed/npm/*
!flow-typed/npm/module_vx.x.x.js
# App packaged
release
app/main.prod.js
app/main.prod.js.map
app/renderer.prod.js
app/renderer.prod.js.map
app/style.css
app/style.css.map
dist
dll
main.js
main.js.map
.idea
npm-debug.log.*
__snapshots__

50
.eslintrc

@ -0,0 +1,50 @@
{
"parser": "babel-eslint",
"parserOptions": {
"sourceType": "module",
"allowImportExportEverywhere": true
},
"extends": "airbnb",
"env": {
"browser": true,
"node": true
},
"rules": {
"arrow-parens": ["off"],
"compat/compat": "error",
"consistent-return": "off",
"comma-dangle": "off",
"flowtype-errors/show-errors": "error",
"generator-star-spacing": "off",
"import/no-unresolved": "error",
"import/no-extraneous-dependencies": "off",
"no-console": "off",
"no-use-before-define": "off",
"no-multi-assign": "off",
"promise/param-names": "error",
"promise/always-return": "error",
"promise/catch-or-return": "error",
"promise/no-native": "off",
"react/sort-comp": ["error", {
"order": ["type-annotations", "static-methods", "lifecycle", "everything-else", "render"]
}],
"react/jsx-no-bind": "off",
"react/jsx-filename-extension": ["error", { "extensions": [".js", ".jsx"] }],
"react/prefer-stateless-function": "off"
},
"plugins": [
"flowtype",
"flowtype-errors",
"import",
"promise",
"compat",
"react"
],
"settings": {
"import/resolver": {
"webpack": {
"config": "webpack.config.eslint.js"
}
}
}
}

26
.flowconfig

@ -0,0 +1,26 @@
[ignore]
<PROJECT_ROOT>/node_modules/*
<PROJECT_ROOT>/app/main.prod.js
<PROJECT_ROOT>/app/main.prod.js.map
<PROJECT_ROOT>/app/dist/.*
<PROJECT_ROOT>/resources/.*
<PROJECT_ROOT>/release/.*
<PROJECT_ROOT>/dll/.*
<PROJECT_ROOT>/release/.*
<PROJECT_ROOT>/git/.*
[include]
[libs]
[options]
esproposal.class_static_fields=enable
esproposal.class_instance_fields=enable
esproposal.export_star_as=enable
module.name_mapper.extension='css' -> '<PROJECT_ROOT>/internals/flow/CSSModule.js.flow'
module.name_mapper.extension='styl' -> '<PROJECT_ROOT>/internals/flow/CSSModule.js.flow'
module.name_mapper.extension='scss' -> '<PROJECT_ROOT>/internals/flow/CSSModule.js.flow'
module.name_mapper.extension='png' -> '<PROJECT_ROOT>/internals/flow/WebpackAsset.js.flow'
module.name_mapper.extension='jpg' -> '<PROJECT_ROOT>/internals/flow/WebpackAsset.js.flow'
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue

4
.gitattributes

@ -0,0 +1,4 @@
* text eol=lf
*.png binary
*.ico binary
*.icns binary

52
.gitignore

@ -0,0 +1,52 @@
# Logs
logs
*.log
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
.eslintcache
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
app/node_modules
# OSX
.DS_Store
# flow-typed
flow-typed/npm/*
!flow-typed/npm/module_vx.x.x.js
# App packaged
release
app/main.prod.js
app/main.prod.js.map
app/renderer.prod.js
app/renderer.prod.js.map
app/style.css
app/style.css.map
dist
dll
main.js
main.js.map
.idea
npm-debug.log.*

3
.stylelintrc

@ -0,0 +1,3 @@
{
"extends": "stylelint-config-standard"
}

42
.travis.yml

@ -0,0 +1,42 @@
sudo: false
language: node_js
node_js:
- 8
- 7
cache:
yarn: true
directories:
- node_modules
- app/node_modules
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
- icnsutils
- graphicsmagick
- xz-utils
- xorriso
install:
- export CXX="g++-4.8"
- yarn
- cd app && yarn && cd ..
- "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
before_script:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start &
- sleep 3
script:
- node --version
- yarn lint
- yarn package
- yarn test
- yarn test-e2e

19
.vscode/settings.json

@ -0,0 +1,19 @@
{
"javascript.validate.enable": false,
"flow.useNPMPackagedFlow": true,
"search.exclude": {
".git": true,
".eslintcache": true,
"app/dist": true,
"app/main.prod.js": true,
"app/main.prod.js.map": true,
"bower_components": true,
"dll": true,
"flow-typed": true,
"release": true,
"node_modules": true,
"npm-debug.log.*": true,
"test/**/__snapshots__": true,
"yarn.lock": true
}
}

384
CHANGELOG.md

@ -0,0 +1,384 @@
# 0.12.0 (2017.7.8)
#### Misc
- Removed `babel-polyfill`
- Renamed and alphabetized npm scripts
#### Breaking
- Changed node dev `__dirname` and `__filename` to node built in fn's (https://github.com/chentsulin/electron-react-boilerplate/pull/1035)
- Renamed `app/bundle.js` to `app/renderer.prod.js` for consistency
- Renamed `dll/vendor.js` to `dll/renderer.dev.dll.js` for consistency
#### Additions
- Enable node_modules cache on CI
# 0.11.2 (2017.5.1)
Yay! Another patch release. This release mostly includes refactorings and router bug fixes. Huge thanks to @anthonyraymond!
⚠️ Windows electron builds are failing because of [this issue](https://github.com/electron/electron/issues/9321). This is not an issue with the boilerplate ⚠️
#### Breaking
- **Renamed `./app/main.development.js` => `./app/main.{dev,prod}.js`:** [#963](https://github.com/chentsulin/electron-react-boilerplate/pull/963)
#### Fixes
- **Fixed reloading when not on `/` path:** [#958](https://github.com/chentsulin/electron-react-boilerplate/pull/958) [#949](https://github.com/chentsulin/electron-react-boilerplate/pull/949)
#### Additions
- **Added support for stylefmt:** [#960](https://github.com/chentsulin/electron-react-boilerplate/pull/960)
# 0.11.1 (2017.4.23)
You can now debug the production build with devtools like so:
```
DEBUG_PROD=true npm run package
```
🎉🎉🎉
#### Additions
- **Added support for debugging production build:** [#fab245a](https://github.com/chentsulin/electron-react-boilerplate/pull/941/commits/fab245a077d02a09630f74270806c0c534a4ff95)
#### Bug Fixes
- **Fixed bug related to importing native dependencies:** [#933](https://github.com/chentsulin/electron-react-boilerplate/pull/933)
#### Improvements
- **Updated all deps to latest semver**
# 0.11.0 (2017.4.19)
Here's the most notable changes since `v0.10.0`. Its been about a year since a release has been pushed. Expect a new release to be published every 3-4 weeks.
#### Breaking Changes
- **Dropped support for node < 6**
- **Refactored webpack config files**
- **Migrate to two-package.json project structure**
- **Updated all devDeps to latest semver**
- **Migrated to Jest:** [#768](https://github.com/chentsulin/electron-react-boilerplate/pull/768)
- **Migrated to `react-router@4`**
- **Migrated to `electron-builder@4`**
- **Migrated to `webpack@2`**
- **Migrated to `react-hot-loader@3`**
- **Changed default live reload server PORT to `1212` from `3000`**
#### Additions
- **Added support for Yarn:** [#451](https://github.com/chentsulin/electron-react-boilerplate/pull/451)
- **Added support for Flow:** [#425](https://github.com/chentsulin/electron-react-boilerplate/pull/425)
- **Added support for stylelint:** [#911](https://github.com/chentsulin/electron-react-boilerplate/pull/911)
- **Added support for electron-builder:** [#876](https://github.com/chentsulin/electron-react-boilerplate/pull/876)
- **Added optional support for SASS:** [#880](https://github.com/chentsulin/electron-react-boilerplate/pull/880)
- **Added support for eslint-plugin-flowtype:** [#911](https://github.com/chentsulin/electron-react-boilerplate/pull/911)
- **Added support for appveyor:** [#280](https://github.com/chentsulin/electron-react-boilerplate/pull/280)
- **Added support for webpack dlls:** [#860](https://github.com/chentsulin/electron-react-boilerplate/pull/860)
- **Route based code splitting:** [#884](https://github.com/chentsulin/electron-react-boilerplate/pull/884)
- **Added support for Webpack Bundle Analyzer:** [#922](https://github.com/chentsulin/electron-react-boilerplate/pull/922)
#### Improvements
- **Parallelize renderer and main build processes when running `npm run build`**
- **Dynamically generate electron app menu**
- **Improved vscode integration:** [#856](https://github.com/chentsulin/electron-react-boilerplate/pull/856)
#### Bug Fixes
- **Fixed hot module replacement race condition bug:** [#917](https://github.com/chentsulin/electron-react-boilerplate/pull/917) [#920](https://github.com/chentsulin/electron-react-boilerplate/pull/920)
# 0.10.0 (2016.4.18)
#### Improvements
- **Use Babel in main process with Webpack build:** [#201](https://github.com/chentsulin/electron-react-boilerplate/pull/201)
- **Change targets to built-in support by webpack:** [#197](https://github.com/chentsulin/electron-react-boilerplate/pull/197)
- **use es2015 syntax for webpack configs:** [#195](https://github.com/chentsulin/electron-react-boilerplate/pull/195)
- **Open application when webcontent is loaded:** [#192](https://github.com/chentsulin/electron-react-boilerplate/pull/192)
- **Upgraded dependencies**
#### Bug fixed
- **Fix `npm list electron-prebuilt` in package.js:** [#188](https://github.com/chentsulin/electron-react-boilerplate/pull/188)
# 0.9.0 (2016.3.23)
#### Improvements
- **Added [redux-logger](https://github.com/fcomb/redux-logger)**
- **Upgraded [react-router-redux](https://github.com/reactjs/react-router-redux) to v4**
- **Upgraded dependencies**
- **Added `npm run dev` command:** [#162](https://github.com/chentsulin/electron-react-boilerplate/pull/162)
- **electron to v0.37.2**
#### Breaking Changes
- **css module as default:** [#154](https://github.com/chentsulin/electron-react-boilerplate/pull/154).
- **set default NODE_ENV to production:** [#140](https://github.com/chentsulin/electron-react-boilerplate/issues/140)
# 0.8.0 (2016.2.17)
#### Bug fixed
- **Fix lint errors**
- **Fix Webpack publicPath for production builds**: [#119](https://github.com/chentsulin/electron-react-boilerplate/issues/119).
- **package script now chooses correct OS icon extension**
#### Improvements
- **babel 6**
- **Upgrade Dependencies**
- **Enable CSS source maps**
- **Add json-loader**: [#128](https://github.com/chentsulin/electron-react-boilerplate/issues/128).
- **react-router 2.0 and react-router-redux 3.0**
# 0.7.1 (2015.12.27)
#### Bug fixed
- **Fixed npm script on windows 10:** [#103](https://github.com/chentsulin/electron-react-boilerplate/issues/103).
- **history and react-router version bump**: [#109](https://github.com/chentsulin/electron-react-boilerplate/issues/109), [#110](https://github.com/chentsulin/electron-react-boilerplate/pull/110).
#### Improvements
- **electron 0.36**
# 0.7.0 (2015.12.16)
#### Bug fixed
- **Fixed process.env.NODE_ENV variable in webpack:** [#74](https://github.com/chentsulin/electron-react-boilerplate/pull/74).
- **add missing object-assign**: [#76](https://github.com/chentsulin/electron-react-boilerplate/pull/76).
- **packaging in npm@3:** [#77](https://github.com/chentsulin/electron-react-boilerplate/pull/77).
- **compatibility in windows:** [#100](https://github.com/chentsulin/electron-react-boilerplate/pull/100).
- **disable chrome debugger in production env:** [#102](https://github.com/chentsulin/electron-react-boilerplate/pull/102).
#### Improvements
- **redux**
- **css-modules**
- **upgrade to react-router 1.x**
- **unit tests**
- **e2e tests**
- **travis-ci**
- **upgrade to electron 0.35.x**
- **use es2015**
- **check dev engine for node and npm**
# 0.6.5 (2015.11.7)
#### Improvements
- **Bump style-loader to 0.13**
- **Bump css-loader to 0.22**
# 0.6.4 (2015.10.27)
#### Improvements
- **Bump electron-debug to 0.3**
# 0.6.3 (2015.10.26)
#### Improvements
- **Initialize ExtractTextPlugin once:** [#64](https://github.com/chentsulin/electron-react-boilerplate/issues/64).
# 0.6.2 (2015.10.18)
#### Bug fixed
- **Babel plugins production env not be set properly:** [#57](https://github.com/chentsulin/electron-react-boilerplate/issues/57).
# 0.6.1 (2015.10.17)
#### Improvements
- **Bump electron to v0.34.0**
# 0.6.0 (2015.10.16)
#### Breaking Changes
- **From react-hot-loader to react-transform**
# 0.5.2 (2015.10.15)
#### Improvements
- **Run tests with babel-register:** [#29](https://github.com/chentsulin/electron-react-boilerplate/issues/29).
# 0.5.1 (2015.10.12)
#### Bug fixed
- **Fix #51:** use `path.join(__dirname` instead of `./`.
# 0.5.0 (2015.10.11)
#### Improvements
- **Simplify webpack config** see [#50](https://github.com/chentsulin/electron-react-boilerplate/pull/50).
#### Breaking Changes
- **webpack configs**
- **port changed:** changed default port from 2992 to 3000.
- **npm scripts:** remove `start-dev` and `dev-server`. rename `hot-dev-server` to `hot-server`.
# 0.4.3 (2015.9.22)
#### Bug fixed
- **Fix #45 zeromq crash:** bump version of `electron-prebuilt`.
# 0.4.2 (2015.9.15)
#### Bug fixed
- **run start-hot breaks chrome refresh(CTRL+R) (#42)**: bump `electron-debug` to `0.2.1`
# 0.4.1 (2015.9.11)
#### Improvements
- **use electron-prebuilt version for packaging (#33)**
# 0.4.0 (2015.9.5)
#### Improvements
- **update dependencies**
# 0.3.0 (2015.8.31)
#### Improvements
- **eslint-config-airbnb**
# 0.2.10 (2015.8.27)
#### Features
- **custom placeholder icon**
#### Improvements
- **electron-renderer as target:** via [webpack-target-electron-renderer](https://github.com/chentsulin/webpack-target-electron-renderer)
# 0.2.9 (2015.8.18)
#### Bug fixed
- **Fix hot-reload**
# 0.2.8 (2015.8.13)
#### Improvements
- **bump electron-debug**
- **babelrc**
- **organize webpack scripts**
# 0.2.7 (2015.7.9)
#### Bug fixed
- **defaultProps:** fix typos.
# 0.2.6 (2015.7.3)
#### Features
- **menu**
#### Bug fixed
- **package.js:** include webpack build.
# 0.2.5 (2015.7.1)
#### Features
- **NPM Script:** support multi-platform
- **package:** `--all` option
# 0.2.4 (2015.6.9)
#### Bug fixed
- **Eslint:** typo, [#17](https://github.com/chentsulin/electron-react-boilerplate/issues/17) and improve `.eslintrc`
# 0.2.3 (2015.6.3)
#### Features
- **Package Version:** use latest release electron version as default
- **Ignore Large peerDependencies**
#### Bug fixed
- **Npm Script:** typo, [#6](https://github.com/chentsulin/electron-react-boilerplate/pull/6)
- **Missing css:** [#7](https://github.com/chentsulin/electron-react-boilerplate/pull/7)
# 0.2.2 (2015.6.2)
#### Features
- **electron-debug**
#### Bug fixed
- **Webpack:** add `.json` and `.node` to extensions for imitating node require.
- **Webpack:** set `node_modules` to externals for native module support.
# 0.2.1 (2015.5.30)
#### Bug fixed
- **Webpack:** #1, change build target to `atom`.
# 0.2.0 (2015.5.30)
#### Features
- **Ignore:** `test`, `tools`, `release` folder and devDependencies in `package.json`.
- **Support asar**
- **Support icon**
# 0.1.0 (2015.5.27)
#### Features
- **Webpack:** babel, react-hot, ...
- **Flux:** actions, api, components, containers, stores..
- **Package:** darwin (osx), linux and win32 (windows) platform.

22
LICENSE

@ -0,0 +1,22 @@
The MIT License (MIT)
Copyright (c) 2015-present C. T. Lin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

334
README.md

@ -0,0 +1,334 @@
# electron-react-boilerplate
### A Boilerplate for Scalable Cross-Platform Desktop Apps
<br/>
[![Build Status][travis-image]][travis-url]
[![Appveyor Build Status][appveyor-image]][appveyor-url]
[![Dependency Status][david_img]][david_site]
[![Github Tag][github-tag-image]][github-tag-url]
[![Join the chat at https://gitter.im/electron-react-boilerplate/Lobby](https://badges.gitter.im/electron-react-boilerplate/Lobby.svg)](https://gitter.im/electron-react-boilerplate/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![OpenCollective](https://opencollective.com/electron-react-boilerplate/backers/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/electron-react-boilerplate/sponsors/badge.svg)](#sponsors)
[![React](/internals/img/react-padded-90.png)](https://facebook.github.io/react/)
[![Webpack](/internals/img/webpack-padded-90.png)](https://webpack.github.io/)
[![Redux](/internals/img/redux-padded-90.png)](http://redux.js.org/)
[![React Router](/internals/img/react-router-padded-90.png)](https://github.com/ReactTraining/react-router)
[![Flow](/internals/img/flow-padded-90.png)](https://flowtype.org/)
[![ESLint](/internals/img/eslint-padded-90.png)](http://eslint.org/)
[![Jest](/internals/img/jest-padded-90.png)](https://facebook.github.io/jest/)
[![Yarn](/internals/img/yarn-padded-90.png)](https://yarnpkg.com/)
[Electron](http://electron.atom.io/) application boilerplate based on [React](https://facebook.github.io/react/), [Redux](https://github.com/reactjs/redux), [React Router](https://github.com/reactjs/react-router), [Webpack](http://webpack.github.io/docs/), [React Transform HMR](https://github.com/gaearon/react-transform-hmr) for rapid application development.
## Screenshot
![Electron Boilerplate Demo](https://cloud.githubusercontent.com/assets/3382565/10557547/b1f07a4e-74e3-11e5-8d27-79ab6947d429.gif)
## Install
* **Note: requires a node version >= 7 and an npm version >= 4.**
* **If you have installation or compilation issues with this project, please see [our debugging guide](https://github.com/chentsulin/electron-react-boilerplate/issues/400)**
First, clone the repo via git:
```bash
git clone --depth=1 https://github.com/chentsulin/electron-react-boilerplate.git your-project-name
```
And then install dependencies with yarn.
```bash
$ cd your-project-name
$ yarn
```
**Note**: If you can't use [yarn](https://github.com/yarnpkg/yarn) for some reason, try `npm install`.
## Run
Start the app in the `dev` environment. This starts the renderer process in [**hot-module-replacement**](https://webpack.js.org/guides/hmr-react/) mode and starts a server that sends hot updates to the renderer process:
```bash
$ npm run dev
```
You Run these two commands __simultaneously__ in different console tabs:
```bash
$ npm run start-renderer-dev
$ npm run start-main-dev
```
## Editor Configuration
**Atom**
```bash
apm install editorconfig es6-javascript atom-ternjs javascript-snippets linter linter-eslint language-babel autocomplete-modules file-icons
```
**VSCode**
* [Editorconfig](https://github.com/editorconfig/editorconfig-vscode)
* [ESLint](https://github.com/Microsoft/vscode-eslint)
* [Flow](https://github.com/flowtype/flow-for-vscode)
* [Babel](https://github.com/dzannotti/vscode-babel)
* [Jest](https://github.com/orta/vscode-jest)
* [ES6 Snippets](https://marketplace.visualstudio.com/items?itemName=xabikos.JavaScriptSnippets)
* [React Snippets](https://marketplace.visualstudio.com/items?itemName=xabikos.ReactSnippets)
:bulb: *If you are using the `flow-for-vscode` plugin, make sure to disable the `flowtype-errors/show-errors` eslint rule in the `.eslintrc` by setting it to `0`*
**Sublime**
* [Editorconfig Integration](https://github.com/sindresorhus/editorconfig-sublime#readme)
* [Linting](https://github.com/SublimeLinter/SublimeLinter3)
* [ESLint Integration](https://github.com/roadhump/SublimeLinter-eslint)
* [Syntax Highlighting](https://github.com/babel/babel-sublime)
* [Autocompletion](https://github.com/ternjs/tern_for_sublime)
* [Node Snippets](https://packagecontrol.io/packages/JavaScript%20%26%20NodeJS%20Snippets)
* [ES6 Snippets](https://packagecontrol.io/packages/ES6-Toolkit)
**Others**
* [Editorconfig](http://editorconfig.org/#download)
* [ESLint](http://eslint.org/docs/user-guide/integrations#editors)
* Babel Syntax Plugin
## DevTools
#### Toggle Chrome DevTools
- OS X: <kbd>Cmd</kbd> <kbd>Alt</kbd> <kbd>I</kbd> or <kbd>F12</kbd>
- Linux: <kbd>Ctrl</kbd> <kbd>Shift</kbd> <kbd>I</kbd> or <kbd>F12</kbd>
- Windows: <kbd>Ctrl</kbd> <kbd>Shift</kbd> <kbd>I</kbd> or <kbd>F12</kbd>
*See [electron-debug](https://github.com/sindresorhus/electron-debug) for more information.*
#### DevTools extension
This boilerplate includes the following DevTools extensions:
* [Devtron](https://github.com/electron/devtron) - Install via [electron-debug](https://github.com/sindresorhus/electron-debug).
* [React Developer Tools](https://github.com/facebook/react-devtools) - Install via [electron-devtools-installer](https://github.com/GPMDP/electron-devtools-installer).
* [Redux DevTools](https://github.com/zalmoxisus/redux-devtools-extension) - Install via [electron-devtools-installer](https://github.com/GPMDP/electron-devtools-installer).
You can find the tabs on Chrome DevTools.
If you want to update extensions version, please set `UPGRADE_EXTENSIONS` env, just run:
```bash
$ UPGRADE_EXTENSIONS=1 npm run dev
# For Windows
$ set UPGRADE_EXTENSIONS=1 && npm run dev
```
:bulb: You can debug your production build with devtools by simply setting the `DEBUG_PROD` env variable:
```
DEBUG_PROD=true npm run package
```
## CSS Modules
This boilerplate is configured to use [css-modules](https://github.com/css-modules/css-modules) out of the box.
All `.css` file extensions will use css-modules unless it has `.global.css`.
If you need global styles, stylesheets with `.global.css` will not go through the
css-modules loader. e.g. `app.global.css`
If you want to import global css libraries (like `bootstrap`), you can just write the following code in `.global.css`:
```css
@import "~bootstrap/dist/css/bootstrap.css";
```
## Sass support
If you want to use Sass in your app, you only need to import `.sass` files instead of `.css` once:
```js
import './app.global.scss';
```
## Packaging
To package apps for the local platform:
```bash
$ npm run package
```
To package apps for all platforms:
First, refer to [Multi Platform Build](https://github.com/electron-userland/electron-builder/wiki/Multi-Platform-Build) for dependencies.
Then,
```bash
$ npm run package-all
```
To package apps with options:
```bash
$ npm run package -- --[option]
```
## Further commands
To run the application without packaging run
```bash
$ npm run build
$ npm start
```
To run End-to-End Test
```bash
$ npm run build
$ npm run test-e2e
```
#### Options
See [electron-builder CLI Usage](https://github.com/electron-userland/electron-builder#cli-usage)
## How to add modules to the project
You will need to add other modules to this boilerplate, depending on the requirements of your project. For example, you may want to add [node-postgres](https://github.com/brianc/node-postgres) to communicate with PostgreSQL database, or
[material-ui](http://www.material-ui.com/) to reuse react UI components.
⚠️ Please read following section before installing any dependencies ⚠️
### Module Structure
This boilerplate uses a [two package.json structure](https://github.com/electron-userland/electron-builder/wiki/Two-package.json-Structure). This means, you will have two `package.json` files.
1. `./package.json` in the root of your project
1. `./app/package.json` inside `app` folder
### Which `package.json` file to use
**Rule of thumb** is: all modules go into `./package.json` except native modules. Native modules go into `./app/package.json`.
1. If the module is native to a platform (like node-postgres) or otherwise should be included with the published package (i.e. bcrypt, openbci), it should be listed under `dependencies` in `./app/package.json`.
2. If a module is `import`ed by another module, include it in `dependencies` in `./package.json`. See [this ESLint rule](https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-extraneous-dependencies.md). Examples of such modules are `material-ui`, `redux-form`, and `moment`.
3. Otherwise, modules used for building, testing and debugging should be included in `devDependencies` in `./package.json`.
### Further Readings
See the wiki page, [Module Structure — Two package.json Structure](https://github.com/chentsulin/electron-react-boilerplate/wiki/Module-Structure----Two-package.json-Structure) to understand what is native module, the rationale behind two package.json structure and more.
For an example app that uses this boilerplate and packages native dependencies, see [erb-sqlite-example](https://github.com/amilajack/erb-sqlite-example).
## Static Type Checking
This project comes with Flow support out of the box! You can annotate your code with types, [get Flow errors as ESLint errors](https://github.com/amilajack/eslint-plugin-flowtype-errors), and get [type errors during runtime](https://github.com/codemix/flow-runtime) during development. Types are completely optional.
## Native-like UI
If you want to have native-like User Interface (OS X El Capitan and Windows 10), [react-desktop](https://github.com/gabrielbull/react-desktop) may perfect suit for you.
## Dispatching redux actions from main process
see discusses in [#118](https://github.com/chentsulin/electron-react-boilerplate/issues/118) and [#108](https://github.com/chentsulin/electron-react-boilerplate/issues/108)
## How to keep the boilerplate updated
If your application is a fork from this repo, you can add this repo to another git remote:
```sh
git remote add upstream https://github.com/chentsulin/electron-react-boilerplate.git
```
Then, use git to merge some latest commits:
```sh
git pull upstream master
```
## Maintainers
- [C. T. Lin](https://github.com/chentsulin)
- [Jhen-Jie Hong](https://github.com/jhen0409)
- [Amila Welihinda](https://github.com/amilajack)
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/electron-react-boilerplate#backer)]
<a href="https://opencollective.com/electron-react-boilerplate/backer/0/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/1/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/2/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/3/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/4/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/5/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/6/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/7/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/8/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/9/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/10/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/11/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/12/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/13/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/14/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/15/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/16/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/17/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/18/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/19/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/20/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/21/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/22/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/23/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/24/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/25/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/26/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/27/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/28/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/backer/29/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/backer/29/avatar.svg"></a>
## Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/electron-react-boilerplate#sponsor)]
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/0/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/1/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/2/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/3/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/4/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/5/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/6/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/7/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/8/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/9/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/10/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/11/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/12/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/13/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/14/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/15/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/16/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/17/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/18/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/19/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/20/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/21/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/22/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/23/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/24/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/25/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/26/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/27/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/28/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/electron-react-boilerplate/sponsor/29/website" target="_blank"><img src="https://opencollective.com/electron-react-boilerplate/sponsor/29/avatar.svg"></a>
## License
MIT © [C. T. Lin](https://github.com/chentsulin)
[npm-image]: https://img.shields.io/npm/v/electron-react-boilerplate.svg?style=flat-square
[github-tag-image]: https://img.shields.io/github/tag/chentsulin/electron-react-boilerplate.svg
[github-tag-url]: https://github.com/chentsulin/electron-react-boilerplate/releases/latest
[travis-image]: https://travis-ci.org/chentsulin/electron-react-boilerplate.svg?branch=master
[travis-url]: https://travis-ci.org/chentsulin/electron-react-boilerplate
[appveyor-image]: https://ci.appveyor.com/api/projects/status/github/chentsulin/electron-react-boilerplate?svg=true
[appveyor-url]: https://ci.appveyor.com/project/chentsulin/electron-react-boilerplate/branch/master
[david_img]: https://img.shields.io/david/chentsulin/electron-react-boilerplate.svg
[david_site]: https://david-dm.org/chentsulin/electron-react-boilerplate

22
app/.eslintrc

@ -0,0 +1,22 @@
{
"rules": {
"flowtype/boolean-style": [2, "boolean"],
"flowtype/define-flow-type": 1,
"flowtype/delimiter-dangle": [2, "never"],
"flowtype/generic-spacing": [2, "never"],
"flowtype/no-primitive-constructor-types": 2,
"flowtype/no-weak-types": 1,
"flowtype/object-type-delimiter": [2, "comma"],
"flowtype/require-parameter-type": 0,
"flowtype/require-return-type": 0,
"flowtype/require-valid-file-annotation": 0,
"flowtype/semi": [2, "always"],
"flowtype/space-after-type-colon": [2, "always"],
"flowtype/space-before-generic-bracket": [2, "never"],
"flowtype/space-before-type-colon": [2, "never"],
"flowtype/union-intersection-spacing": [2, "always"],
"flowtype/use-flow-type": 2,
"flowtype/valid-syntax": 2,
"flowtype-errors/show-errors": 2
}
}

41
app/actions/counter.js

@ -0,0 +1,41 @@
// @flow
import type { counterStateType } from '../reducers/counter';
type actionType = {
+type: string
};
export const INCREMENT_COUNTER = 'INCREMENT_COUNTER';
export const DECREMENT_COUNTER = 'DECREMENT_COUNTER';
export function increment() {
return {
type: INCREMENT_COUNTER
};
}
export function decrement() {
return {
type: DECREMENT_COUNTER
};
}
export function incrementIfOdd() {
return (dispatch: (action: actionType) => void, getState: () => counterStateType) => {
const { counter } = getState();
if (counter % 2 === 0) {
return;
}
dispatch(increment());
};
}
export function incrementAsync(delay: number = 1000) {
return (dispatch: (action: actionType) => void) => {
setTimeout(() => {
dispatch(increment());
}, delay);
};
}

39
app/app.global.css

@ -0,0 +1,39 @@
@import "~font-awesome/css/font-awesome.css";
body {
position: relative;
color: white;
height: 100vh;
background-color: #000;
background-image: linear-gradient(45deg, rgba(29, 29, 29, 0.5) 10%, rgba(235, 184, 100, 0.7));
font-family: Arial, Helvetica, Helvetica Neue, serif;
overflow-y: hidden;
}
h2 {
margin: 0;
font-size: 2.25rem;
font-weight: bold;
letter-spacing: -0.025em;
color: #fff;
}
p {
font-size: 24px;
}
li {
list-style: none;
}
a {
color: white;
opacity: 0.75;
text-decoration: none;
}
a:hover {
opacity: 1;
text-decoration: none;
cursor: pointer;
}

46
app/app.html

@ -0,0 +1,46 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello Electron React!</title>
<script>
(function() {
if (!process.env.HOT) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = './dist/style.css';
// HACK: Writing the script path should be done with webpack
document.getElementsByTagName('head')[0].appendChild(link);
}
}());
</script>
</head>
<body>
<div id="root"></div>
<script>
{
const scripts = [];
// Dynamically insert the DLL script in development env in the
// renderer process
if (process.env.NODE_ENV === 'development') {
scripts.push('../dll/renderer.dev.dll.js');
}
// Dynamically insert the bundled app script in the renderer process
const port = process.env.PORT || 1212;
scripts.push(
(process.env.HOT)
? 'http://localhost:' + port + '/dist/renderer.dev.js'
: './dist/renderer.prod.js'
);
document.write(
scripts
.map(script => '<script defer src="' + script + '"><\/script>')
.join('')
);
}
</script>
</body>
</html>

BIN
app/app.icns

Binary file not shown.

37
app/components/Counter.css

@ -0,0 +1,37 @@
.backButton {
position: absolute;
}
.counter {
position: absolute;
top: 30%;
left: 45%;
font-size: 10rem;
font-weight: bold;
letter-spacing: -0.025em;
}
.btnGroup {
position: relative;
top: 500px;
width: 480px;
margin: 0 auto;
}
.btn {
font-size: 1.6rem;
font-weight: bold;
background-color: #fff;
border-radius: 50%;
margin: 10px;
width: 100px;
height: 100px;
opacity: 0.7;
cursor: pointer;
font-family: Arial, Helvetica, Helvetica Neue;
}
.btn:hover {
color: white;
background-color: rgba(0, 0, 0, 0.5);
}

42
app/components/Counter.js

@ -0,0 +1,42 @@
// @flow
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import styles from './Counter.css';
class Counter extends Component {
props: {
increment: () => void,
incrementIfOdd: () => void,
incrementAsync: () => void,
decrement: () => void,
counter: number
};
render() {
const { increment, incrementIfOdd, incrementAsync, decrement, counter } = this.props;
return (
<div>
<div className={styles.backButton} data-tid="backButton">
<Link to="/">
<i className="fa fa-arrow-left fa-3x" />
</Link>
</div>
<div className={`counter ${styles.counter}`} data-tid="counter">
{counter}
</div>
<div className={styles.btnGroup}>
<button className={styles.btn} onClick={increment} data-tclass="btn">
<i className="fa fa-plus" />
</button>
<button className={styles.btn} onClick={decrement} data-tclass="btn">
<i className="fa fa-minus" />
</button>
<button className={styles.btn} onClick={incrementIfOdd} data-tclass="btn">odd</button>
<button className={styles.btn} onClick={() => incrementAsync()} data-tclass="btn">async</button>
</div>
</div>
);
}
}
export default Counter;

14
app/components/Home.css

@ -0,0 +1,14 @@
.container {
position: absolute;
top: 30%;
left: 10px;
text-align: center;
}
.container h2 {
font-size: 5rem;
}
.container a {
font-size: 1.4rem;
}

17
app/components/Home.js

@ -0,0 +1,17 @@
// @flow
import React, { Component } from 'react'
import { Link } from 'react-router-dom'
import InlineSVG from 'react-inline-svg'
import styles from './Home.css'
export default class Home extends Component {
render() {
return (
<div>
<div className={styles.container} data-tid="container">
</div>
</div>
);
}
}

17
app/containers/App.js

@ -0,0 +1,17 @@
// @flow
import React, { Component } from 'react';
import type { Children } from 'react';
export default class App extends Component {
props: {
children: Children
};
render() {
return (
<div>
{this.props.children}
</div>
);
}
}

16
app/containers/CounterPage.js

@ -0,0 +1,16 @@
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Counter from '../components/Counter';
import * as CounterActions from '../actions/counter';
function mapStateToProps(state) {
return {
counter: state.counter
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(CounterActions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(Counter);

11
app/containers/HomePage.js

@ -0,0 +1,11 @@
// @flow
import React, { Component } from 'react';
import Home from '../components/Home';
export default class HomePage extends Component {
render() {
return (
<Home />
);
}
}

20
app/containers/Root.js

@ -0,0 +1,20 @@
// @flow
import React from 'react';
import { Provider } from 'react-redux';
import { ConnectedRouter } from 'react-router-redux';
import Routes from '../routes';
type RootType = {
store: {},
history: {}
};
export default function Root({ store, history }: RootType) {
return (
<Provider store={store}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</Provider>
);
}

27
app/index.js

@ -0,0 +1,27 @@
import React from 'react';
import { render } from 'react-dom';
import { AppContainer } from 'react-hot-loader';
import Root from './containers/Root';
import { configureStore, history } from './store/configureStore';
import './app.global.css';
const store = configureStore();
render(
<AppContainer>
<Root store={store} history={history} />
</AppContainer>,
document.getElementById('root')
);
if (module.hot) {
module.hot.accept('./containers/Root', () => {
const NextRoot = require('./containers/Root'); // eslint-disable-line global-require
render(
<AppContainer>
<NextRoot store={store} history={history} />
</AppContainer>,
document.getElementById('root')
);
});
}

86
app/main.dev.js

@ -0,0 +1,86 @@
/* eslint global-require: 1, flowtype-errors/show-errors: 0 */
/**
* This module executes inside of electron's main process. You can start
* electron renderer process from here and communicate with the other processes
* through IPC.
*
* When running `npm run build` or `npm run build-main`, this file is compiled to
* `./app/main.prod.js` using webpack. This gives us some performance wins.
*
* @flow
*/
import { app, BrowserWindow } from 'electron';
import MenuBuilder from './menu';
let mainWindow = null;
if (process.env.NODE_ENV === 'production') {
const sourceMapSupport = require('source-map-support');
sourceMapSupport.install();
}
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
require('electron-debug')();
const path = require('path');
const p = path.join(__dirname, '..', 'app', 'node_modules');
require('module').globalPaths.push(p);
}
const installExtensions = async () => {
const installer = require('electron-devtools-installer');
const forceDownload = !!process.env.UPGRADE_EXTENSIONS;
const extensions = [
'REACT_DEVELOPER_TOOLS',
'REDUX_DEVTOOLS'
];
return Promise
.all(extensions.map(name => installer.default(installer[name], forceDownload)))
.catch(console.log);
};
/**
* Add event listeners...
*/
app.on('window-all-closed', () => {
// Respect the OSX convention of having the application in memory even
// after all windows have been closed
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('ready', async () => {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
await installExtensions();
}
mainWindow = new BrowserWindow({
show: false,
width: 1024,
height: 728
});
mainWindow.loadURL(`file://${__dirname}/app.html`);
// @TODO: Use 'ready-to-show' event
// https://github.com/electron/electron/blob/master/docs/api/browser-window.md#using-ready-to-show-event
mainWindow.webContents.on('did-finish-load', () => {
if (!mainWindow) {
throw new Error('"mainWindow" is not defined');
}
mainWindow.show();
mainWindow.focus();
});
mainWindow.on('closed', () => {
mainWindow = null;
});
const menuBuilder = new MenuBuilder(mainWindow);
menuBuilder.buildMenu();
});

186
app/menu.js

@ -0,0 +1,186 @@
// @flow
import { app, Menu, shell, BrowserWindow } from 'electron';
export default class MenuBuilder {
mainWindow: BrowserWindow;
constructor(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow;
}
buildMenu() {
if (process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true') {
this.setupDevelopmentEnvironment();
}
let template;
if (process.platform === 'darwin') {
template = this.buildDarwinTemplate();
} else {
template = this.buildDefaultTemplate();
}
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
return menu;
}
setupDevelopmentEnvironment() {
this.mainWindow.openDevTools();
this.mainWindow.webContents.on('context-menu', (e, props) => {
const { x, y } = props;
Menu
.buildFromTemplate([{
label: 'Inspect element',
click: () => {
this.mainWindow.inspectElement(x, y);
}
}])
.popup(this.mainWindow);
});
}
buildDarwinTemplate() {
const subMenuAbout = {
label: 'Electron',
submenu: [
{ label: 'About ElectronReact', selector: 'orderFrontStandardAboutPanel:' },
{ type: 'separator' },
{ label: 'Services', submenu: [] },
{ type: 'separator' },
{ label: 'Hide ElectronReact', accelerator: 'Command+H', selector: 'hide:' },
{ label: 'Hide Others', accelerator: 'Command+Shift+H', selector: 'hideOtherApplications:' },
{ label: 'Show All', selector: 'unhideAllApplications:' },
{ type: 'separator' },
{ label: 'Quit', accelerator: 'Command+Q', click: () => { app.quit(); } }
]
};
const subMenuEdit = {
label: 'Edit',
submenu: [
{ label: 'Undo', accelerator: 'Command+Z', selector: 'undo:' },
{ label: 'Redo', accelerator: 'Shift+Command+Z', selector: 'redo:' },
{ type: 'separator' },
{ label: 'Cut', accelerator: 'Command+X', selector: 'cut:' },
{ label: 'Copy', accelerator: 'Command+C', selector: 'copy:' },
{ label: 'Paste', accelerator: 'Command+V', selector: 'paste:' },
{ label: 'Select All', accelerator: 'Command+A', selector: 'selectAll:' }
]
};
const subMenuViewDev = {
label: 'View',
submenu: [
{ label: 'Reload', accelerator: 'Command+R', click: () => { this.mainWindow.webContents.reload(); } },
{ label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } },
{ label: 'Toggle Developer Tools', accelerator: 'Alt+Command+I', click: () => { this.mainWindow.toggleDevTools(); } }
]
};
const subMenuViewProd = {
label: 'View',
submenu: [
{ label: 'Toggle Full Screen', accelerator: 'Ctrl+Command+F', click: () => { this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen()); } }
]
};
const subMenuWindow = {
label: 'Window',
submenu: [
{ label: 'Minimize', accelerator: 'Command+M', selector: 'performMiniaturize:' },
{ label: 'Close', accelerator: 'Command+W', selector: 'performClose:' },
{ type: 'separator' },
{ label: 'Bring All to Front', selector: 'arrangeInFront:' }
]
};
const subMenuHelp = {
label: 'Help',
submenu: [
{ label: 'Learn More', click() { shell.openExternal('http://electron.atom.io'); } },
{ label: 'Documentation', click() { shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme'); } },
{ label: 'Community Discussions', click() { shell.openExternal('https://discuss.atom.io/c/electron'); } },
{ label: 'Search Issues', click() { shell.openExternal('https://github.com/atom/electron/issues'); } }
]
};
const subMenuView = process.env.NODE_ENV === 'development'
? subMenuViewDev
: subMenuViewProd;
return [
subMenuAbout,
subMenuEdit,
subMenuView,
subMenuWindow,
subMenuHelp
];
}
buildDefaultTemplate() {
const templateDefault = [{
label: '&File',
submenu: [{
label: '&Open',
accelerator: 'Ctrl+O'
}, {
label: '&Close',
accelerator: 'Ctrl+W',
click: () => {
this.mainWindow.close();
}
}]
}, {
label: '&View',
submenu: (process.env.NODE_ENV === 'development') ? [{
label: '&Reload',
accelerator: 'Ctrl+R',
click: () => {
this.mainWindow.webContents.reload();
}
}, {
label: 'Toggle &Full Screen',
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
}
}, {
label: 'Toggle &Developer Tools',
accelerator: 'Alt+Ctrl+I',
click: () => {
this.mainWindow.toggleDevTools();
}
}] : [{
label: 'Toggle &Full Screen',
accelerator: 'F11',
click: () => {
this.mainWindow.setFullScreen(!this.mainWindow.isFullScreen());
}
}]
}, {
label: 'Help',
submenu: [{
label: 'Learn More',
click() {
shell.openExternal('http://electron.atom.io');
}
}, {
label: 'Documentation',
click() {
shell.openExternal('https://github.com/atom/electron/tree/master/docs#readme');
}
}, {
label: 'Community Discussions',
click() {
shell.openExternal('https://discuss.atom.io/c/electron');
}
}, {
label: 'Search Issues',
click() {
shell.openExternal('https://github.com/atom/electron/issues');
}
}]
}];
return templateDefault;
}
}

18
app/package.json

@ -0,0 +1,18 @@
{
"name": "electron-react-boilerplate",
"productName": "electron-react-boilerplate",
"version": "1.0.0",
"description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development",
"main": "./main.prod.js",
"author": {
"name": "C. T. Lin",
"email": "chentsulin@gmail.com",
"url": "https://github.com/chentsulin"
},
"scripts": {
"postinstall": "npm rebuild --runtime=electron --target=1.6.6 --disturl=https://atom.io/download/atom-shell --build-from-source"
},
"license": "MIT",
"dependencies": {
}
}

21
app/reducers/counter.js

@ -0,0 +1,21 @@
// @flow
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../actions/counter';
export type counterStateType = {
+counter: number
};
type actionType = {
+type: string
};
export default function counter(state: number = 0, action: actionType) {
switch (action.type) {
case INCREMENT_COUNTER:
return state + 1;
case DECREMENT_COUNTER:
return state - 1;
default:
return state;
}
}

11
app/reducers/index.js

@ -0,0 +1,11 @@
// @flow
import { combineReducers } from 'redux';
import { routerReducer as router } from 'react-router-redux';
import counter from './counter';
const rootReducer = combineReducers({
counter,
router,
});
export default rootReducer;

15
app/routes.js

@ -0,0 +1,15 @@
/* eslint flowtype-errors/show-errors: 0 */
import React from 'react';
import { Switch, Route } from 'react-router';
import App from './containers/App';
import HomePage from './containers/HomePage';
import CounterPage from './containers/CounterPage';
export default () => (
<App>
<Switch>
<Route path="/counter" component={CounterPage} />
<Route path="/" component={HomePage} />
</Switch>
</App>
);

62
app/store/configureStore.dev.js

@ -0,0 +1,62 @@
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import { createHashHistory } from 'history';
import { routerMiddleware, routerActions } from 'react-router-redux';
import { createLogger } from 'redux-logger';
import rootReducer from '../reducers';
import * as counterActions from '../actions/counter';
import type { counterStateType } from '../reducers/counter';
const history = createHashHistory();
const configureStore = (initialState?: counterStateType) => {
// Redux Configuration
const middleware = [];
const enhancers = [];
// Thunk Middleware
middleware.push(thunk);
// Logging Middleware
const logger = createLogger({
level: 'info',
collapsed: true
});
middleware.push(logger);
// Router Middleware
const router = routerMiddleware(history);
middleware.push(router);
// Redux DevTools Configuration
const actionCreators = {
...counterActions,
...routerActions,
};
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
/* eslint-disable no-underscore-dangle */
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// Options: http://zalmoxisus.github.io/redux-devtools-extension/API/Arguments.html
actionCreators,
})
: compose;
/* eslint-enable no-underscore-dangle */
// Apply Middleware & Compose Enhancers
enhancers.push(applyMiddleware(...middleware));
const enhancer = composeEnhancers(...enhancers);
// Create Store
const store = createStore(rootReducer, initialState, enhancer);
if (module.hot) {
module.hot.accept('../reducers', () =>
store.replaceReducer(require('../reducers')) // eslint-disable-line global-require
);
}
return store;
};
export default { configureStore, history };

6
app/store/configureStore.js

@ -0,0 +1,6 @@
// @flow
if (process.env.NODE_ENV === 'production') {
module.exports = require('./configureStore.prod'); // eslint-disable-line global-require
} else {
module.exports = require('./configureStore.dev'); // eslint-disable-line global-require
}

17
app/store/configureStore.prod.js

@ -0,0 +1,17 @@
// @flow
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { createBrowserHistory } from 'history';
import { routerMiddleware } from 'react-router-redux';
import rootReducer from '../reducers';
import type { counterStateType } from '../reducers/counter';
const history = createBrowserHistory();
const router = routerMiddleware(history);
const enhancer = applyMiddleware(thunk, router);
function configureStore(initialState?: counterStateType) {
return createStore(rootReducer, initialState, enhancer);
}
export default { configureStore, history };

0
app/utils/.gitkeep

4
app/yarn.lock

@ -0,0 +1,4 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1

35
appveyor.yml

@ -0,0 +1,35 @@
os: unstable
environment:
matrix:
- nodejs_version: 8
- nodejs_version: 7
cache:
- "%LOCALAPPDATA%/Yarn"
- node_modules -> package.json
- app/node_modules -> app/package.json
matrix:
fast_finish: true
build: off
version: '{build}'
shallow_clone: true
clone_depth: 1
install:
- ps: Install-Product node $env:nodejs_version
- set CI=true
- yarn
- cd app && yarn
test_script:
- node --version
- yarn lint
- yarn package
- yarn test
- yarn test-e2e

3
flow-typed/module_vx.x.x.js

@ -0,0 +1,3 @@
declare module 'module' {
declare module.exports: any;
}

3
internals/flow/CSSModule.js.flow

@ -0,0 +1,3 @@
// @flow
declare export default { [key: string]: string }

2
internals/flow/WebpackAsset.js.flow

@ -0,0 +1,2 @@
// @flow
declare export default string

BIN
internals/img/eslint-padded-90.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
internals/img/eslint-padded.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

BIN
internals/img/eslint.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

BIN
internals/img/flow-padded-90.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
internals/img/flow-padded.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

BIN
internals/img/flow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
internals/img/jest-padded-90.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
internals/img/jest-padded.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
internals/img/jest.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
internals/img/js-padded.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

BIN
internals/img/js.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
internals/img/npm.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
internals/img/react-padded-90.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
internals/img/react-padded.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
internals/img/react-router-padded-90.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

BIN
internals/img/react-router-padded.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
internals/img/react-router.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

BIN
internals/img/react.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

BIN
internals/img/redux-padded-90.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
internals/img/redux-padded.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
internals/img/redux.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
internals/img/webpack-padded-90.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
internals/img/webpack-padded.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

BIN
internals/img/webpack.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
internals/img/yarn-padded-90.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.1 KiB

BIN
internals/img/yarn-padded.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

BIN
internals/img/yarn.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

1
internals/mocks/fileMock.js

@ -0,0 +1 @@
export default 'test-file-stub';

24
internals/scripts/CheckBuiltsExist.js

@ -0,0 +1,24 @@
// @flow
// Check if the renderer and main bundles are built
import path from 'path';
import chalk from 'chalk';
import fs from 'fs';
function CheckBuildsExist() {
const mainPath = path.join(__dirname, '..', '..', 'app', 'main.prod.js');
const rendererPath = path.join(__dirname, '..', '..', 'app', 'dist', 'renderer.prod.js');
if (!fs.existsSync(mainPath)) {
throw new Error(chalk.whiteBright.bgRed.bold(
'The main process is not built yet. Build it by running "npm run build-main"'
));
}
if (!fs.existsSync(rendererPath)) {
throw new Error(chalk.whiteBright.bgRed.bold(
'The renderer process is not built yet. Build it by running "npm run build-renderer"'
));
}
}
CheckBuildsExist();

15
internals/scripts/CheckNodeEnv.js

@ -0,0 +1,15 @@
// @flow
import chalk from 'chalk';
export default function CheckNodeEnv(expectedEnv: string) {
if (!expectedEnv) {
throw new Error('"expectedEnv" not set');
}
if (process.env.NODE_ENV !== expectedEnv) {
console.log(chalk.whiteBright.bgRed.bold(
`"process.env.NODE_ENV" must be "${expectedEnv}" to use this webpack config`
));
process.exit(2);
}
}

16382
package-lock.json

File diff suppressed because it is too large

210
package.json

@ -0,0 +1,210 @@
{
"name": "bolt-desktop",
"productName": "ElectronReact",
"version": "0.0.1",
"description": "Electron application boilerplate based on React, React Router, Webpack, React Hot Loader for rapid application development",
"scripts": {
"build": "concurrently \"npm run build-main\" \"npm run build-renderer\"",
"build-dll": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.dev.dll.js --colors",
"build-main": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.main.prod.js --colors",
"build-renderer": "cross-env NODE_ENV=production node --trace-warnings -r babel-register ./node_modules/webpack/bin/webpack --config webpack.config.renderer.prod.js --colors",
"dev": "cross-env START_HOT=1 npm run start-renderer-dev",
"flow": "flow",
"flow-typed": "rimraf flow-typed/npm && flow-typed install --overwrite || true",
"lint": "eslint --cache --format=node_modules/eslint-formatter-pretty .",
"lint-fix": "npm run lint -- --fix",
"lint-styles": "stylelint app/*.css app/components/*.css --syntax scss",
"lint-styles-fix": "stylefmt -r app/*.css app/components/*.css",
"package": "npm run build && build --publish never",
"package-all": "npm run build && build -mwl",
"package-linux": "npm run build && build --linux",
"package-win": "npm run build && build --win --x64",
"postinstall": "concurrently \"npm run flow-typed\" \"npm run build-dll\" \"electron-builder install-app-deps\" \"node node_modules/fbjs-scripts/node/check-dev-engines.js package.json\"",
"prestart": "npm run build",
"start": "cross-env NODE_ENV=production electron ./app/",
"start-main-dev": "cross-env HOT=1 NODE_ENV=development electron -r babel-register ./app/main.dev",
"start-renderer-dev": "cross-env NODE_ENV=development node --trace-warnings -r babel-register ./node_modules/webpack-dev-server/bin/webpack-dev-server --config webpack.config.renderer.dev.js",
"test": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings ./test/runTests.js",
"test-all": "npm run lint && npm run flow && npm run build && npm run test && npm run test-e2e",
"test-e2e": "cross-env NODE_ENV=test BABEL_DISABLE_CACHE=1 node --trace-warnings ./test/runTests.js e2e",
"test-watch": "npm test -- --watch"
},
"browserslist": "electron 1.6",
"build": {
"productName": "ElectronReact",
"appId": "org.develar.ElectronReact",
"files": [
"dist/",
"node_modules/",
"app.html",
"main.prod.js",
"main.prod.js.map",
"package.json"
],
"dmg": {
"contents": [
{
"x": 130,
"y": 220
},
{
"x": 410,
"y": 220,
"type": "link",
"path": "/Applications"
}
]
},
"win": {
"target": [
"nsis"
]
},
"linux": {
"target": [
"deb",
"AppImage"
]
},
"directories": {
"buildResources": "resources",
"output": "release"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/chentsulin/electron-react-boilerplate.git"
},
"author": "C. T. Lin <chentsulin@gmail.com> (https://github.com/chentsulin)",
"license": "MIT",
"bugs": {
"url": "https://github.com/chentsulin/electron-react-boilerplate/issues"
},
"keywords": [
"electron",
"boilerplate",
"react",
"redux",
"flow",
"sass",
"webpack",
"hot",
"reload"
],
"homepage": "https://github.com/chentsulin/electron-react-boilerplate#readme",
"jest": {
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/internals/mocks/fileMock.js",
"\\.(css|less|sass|scss)$": "identity-obj-proxy"
},
"moduleFileExtensions": [
"js"
],
"moduleDirectories": [
"node_modules",
"app/node_modules"
],
"transform": {
"^.+\\.js$": "babel-jest"
},
"setupFiles": [
"./internals/scripts/CheckBuiltsExist.js"
]
},
"devDependencies": {
"babel-core": "^6.24.1",
"babel-eslint": "^7.2.3",
"babel-jest": "^20.0.3",
"babel-loader": "^7.1.0",
"babel-plugin-add-module-exports": "^0.2.1",
"babel-plugin-dev-expression": "^0.2.1",
"babel-plugin-dynamic-import-webpack": "^1.0.1",
"babel-plugin-flow-runtime": "^0.11.1",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-es2015-classes": "^6.24.1",
"babel-preset-env": "^1.5.1",
"babel-preset-react": "^6.24.1",
"babel-preset-react-hmre": "^1.1.1",
"babel-preset-react-optimize": "^1.0.1",
"babel-preset-stage-0": "^6.24.1",
"babel-register": "^6.24.1",
"babili-webpack-plugin": "^0.1.2",
"chalk": "^2.0.1",
"concurrently": "^3.5.0",
"cross-env": "^5.0.0",
"cross-spawn": "^5.1.0",
"css-loader": "^0.28.3",
"electron": "^1.6.10",
"electron-builder": "^19.8.0",
"electron-devtools-installer": "^2.2.0",
"enzyme": "^2.9.1",
"enzyme-to-json": "^1.5.1",
"eslint": "^3.19.0",
"eslint-config-airbnb": "^15.0.1",
"eslint-formatter-pretty": "^1.1.0",
"eslint-import-resolver-webpack": "^0.8.3",
"eslint-plugin-compat": "^1.0.4",
"eslint-plugin-flowtype": "^2.33.0",
"eslint-plugin-flowtype-errors": "^3.3.0",
"eslint-plugin-import": "^2.6.0",
"eslint-plugin-jest": "^20.0.3",
"eslint-plugin-jsx-a11y": "5.0.3",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-react": "^7.1.0",
"express": "^4.15.3",
"extract-text-webpack-plugin": "^2.1.0",
"fbjs-scripts": "^0.8.0",
"file-loader": "^0.11.1",
"flow-bin": "^0.48.0",
"flow-runtime": "^0.13.0",
"flow-typed": "^2.1.2",
"html-webpack-plugin": "^2.29.0",
"identity-obj-proxy": "^3.0.0",
"jest": "^20.0.4",
"jsdom": "^11.0.0",
"minimist": "^1.2.0",
"node-sass": "^4.5.3",
"react-addons-test-utils": "^15.6.0",
"react-test-renderer": "^15.6.1",
"redux-logger": "^3.0.6",
"rimraf": "^2.6.1",
"sass-loader": "^6.0.6",
"sinon": "^2.3.5",
"spectron": "^3.7.0",
"style-loader": "^0.18.1",
"stylefmt": "^6.0.0",
"stylelint": "^7.12.0",
"stylelint-config-standard": "^16.0.0",
"url-loader": "^0.5.8",
"webpack": "^3.0.0",
"webpack-bundle-analyzer": "^2.8.2",
"webpack-dev-server": "^2.5.0",
"webpack-merge": "^4.1.0"
},
"dependencies": {
"devtron": "^1.4.0",
"electron-debug": "^1.2.0",
"font-awesome": "^4.7.0",
"history": "^4.6.3",
"react": "^15.6.1",
"react-dom": "^15.6.1",
"react-hot-loader": "3.0.0-beta.6",
"react-inline-svg": "^1.1.0",
"react-redux": "^5.0.5",
"react-router": "^4.1.1",
"react-router-dom": "^4.1.1",
"react-router-redux": "^5.0.0-alpha.6",
"redux": "^3.7.1",
"redux-thunk": "^2.2.0",
"source-map-support": "^0.4.15"
},
"devEngines": {
"node": ">=7.x",
"npm": ">=4.x",
"yarn": ">=0.21.3"
},
"main": "webpack.config.base.js",
"directories": {
"test": "test"
}
}

BIN
resources/icon.icns

Binary file not shown.

BIN
resources/icon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

BIN
resources/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
resources/icons/1024x1024.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

BIN
resources/icons/128x128.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
resources/icons/16x16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 954 B

BIN
resources/icons/24x24.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
resources/icons/256x256.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
resources/icons/32x32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
resources/icons/48x48.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
resources/icons/512x512.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
resources/icons/64x64.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
resources/icons/96x96.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

51
resources/zap_1.svg

@ -0,0 +1,51 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="128" height="128" viewBox="0 0 100 100">
<defs>
<g id="background">
<path fill="url(#background-gradient)" stroke="none" d=" M 90.75 23.05 Q 89.5 23.05 88.85 24.15 88.15 25.25 88.85 26.35 97.25 40.2 95.15 56.15 93.05 71.65 81.75 82.6 70.35 93.7 54.9 95.25 32.95 97.55 17.6 81.85 2.2 66.2 4.85 44.2 6.6 30.2 16.15 19.7 25.65 9.1 39.25 5.8 50.55 3 61.7 5.95 62.85 6.3 63.7 5.65 64.6 5 64.6 3.85 64.6 2.25 62.95 1.7 52.55 -1.1 41.6 0.65 25.55 3.3 14.4 14.85 3.1 26.25 0.65 42.4 -2.95 66.9 14 84.5 30.85 102.1 55.2 99.7 72.6 97.9 85.2 85.4 97.95 72.8 99.7 55.35 101.45 38.55 92.75 24.05 92.05 23.05 90.75 23.05 Z"/>
</g><mask id="mask">
<path fill="#FFFFFF" stroke="none" d=" M 82.2 82.15 Q 95.5 68.85 95.5 49.95 95.5 31.1 82.2 17.8 68.85 4.5 50 4.5 31.15 4.5 17.8 17.8 4.5 31.1 4.5 49.95 4.5 68.85 17.8 82.15 31.15 95.45 50 95.45 68.85 95.45 82.2 82.15 Z"/>
</mask>
<linearGradient id="background-gradient" x1="0" y1="0" y2="1" x2="0" >
<stop stop-color="#ebb864" offset="0%"/>
<stop stop-color="#1d1d1d" offset="100%"/>
</linearGradient>
<g transform="scale(2.0833333333333335)" id="picture"><path d="M24 0C10.7 0 0 10.7 0 24s10.7 24 24 24 24-10.7 24-24S37.3 0 24 0zm-7.3 42.7h-.2l6.4-15.9h-8.2L31.9 5.3l-6.7 16.6h8.1L16.7 42.7z"/></g>
<linearGradient id="picture-gradient" x1="0" y1="0" y2="1" x2="0" >
<stop stop-color="#ebb864" offset="0%"/>
<stop stop-color="#1d1d1d" offset="100%"/>
</linearGradient>
</defs>
<use xlink:href="#background" fill="url(#background-gradient)" />
<g mask="url(#mask)">
<g transform="
translate(
50 50
)
translate(0 0) scale(0.5)
translate(
-50 -50
)">
<use xlink:href="#picture" fill="url(#picture-gradient)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

51
resources/zap_2.svg

@ -0,0 +1,51 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="128" height="128" viewBox="0 0 100 100">
<defs>
<g id="background">
<path fill="url(#background-gradient)" stroke="none" d=" M 90.75 23.05 Q 89.5 23.05 88.85 24.15 88.15 25.25 88.85 26.35 97.25 40.2 95.15 56.15 93.05 71.65 81.75 82.6 70.35 93.7 54.9 95.25 32.95 97.55 17.6 81.85 2.2 66.2 4.85 44.2 6.6 30.2 16.15 19.7 25.65 9.1 39.25 5.8 50.55 3 61.7 5.95 62.85 6.3 63.7 5.65 64.6 5 64.6 3.85 64.6 2.25 62.95 1.7 52.55 -1.1 41.6 0.65 25.55 3.3 14.4 14.85 3.1 26.25 0.65 42.4 -2.95 66.9 14 84.5 30.85 102.1 55.2 99.7 72.6 97.9 85.2 85.4 97.95 72.8 99.7 55.35 101.45 38.55 92.75 24.05 92.05 23.05 90.75 23.05 Z"/>
</g><mask id="mask">
<path fill="#FFFFFF" stroke="none" d=" M 82.2 82.15 Q 95.5 68.85 95.5 49.95 95.5 31.1 82.2 17.8 68.85 4.5 50 4.5 31.15 4.5 17.8 17.8 4.5 31.1 4.5 49.95 4.5 68.85 17.8 82.15 31.15 95.45 50 95.45 68.85 95.45 82.2 82.15 Z"/>
</mask>
<linearGradient id="background-gradient" x1="0" y1="0" y2="1" x2="0" >
<stop stop-color="#1d1d1d" offset="0%"/>
<stop stop-color="#ebb864" offset="100%"/>
</linearGradient>
<g transform="scale(2.0833333333333335)" id="picture"><path d="M38 8c-.8 0-1.5.1-2.2.3C33.6 3.4 28.7 0 23 0 15.9 0 10.1 5.3 9.1 12.1 8.8 12 8.4 12 8 12c-4.4 0-8 3.6-8 8s3.6 8 8 8h30c5.5 0 10-4.5 10-10S43.5 8 38 8zm0 18H8c-3.3 0-6-2.7-6-6s2.7-6 6-6c.3 0 .5 0 .9.1l2 .3.3-2C11.9 6.5 17 2 23 2c4.7 0 9 2.8 10.9 7.1l.7 1.5 1.6-.4c.6-.1 1.2-.2 1.8-.2 4.4 0 8 3.6 8 8s-3.6 8-8 8zm-10.7 8l1.9-4H18.7L16 35.5c-.1.2 0 .5.3.5h4.5L16 47.8c0 .1 0 .2.1.2 0 0 .1 0 .2-.1l15.6-13.6c.2-.2.2-.3-.1-.3h-4.5zm-3.9 3.8l.3-.6.1-.1h.6l-1 .7z"/></g>
<linearGradient id="picture-gradient" x1="0" y1="0" y2="1" x2="0" >
<stop stop-color="#1d1d1d" offset="0%"/>
<stop stop-color="#ebb864" offset="100%"/>
</linearGradient>
</defs>
<use xlink:href="#background" fill="url(#background-gradient)" />
<g mask="url(#mask)">
<g transform="
translate(
50 50
)
translate(0 0) scale(0.5)
translate(
-50 -50
)">
<use xlink:href="#picture" fill="url(#picture-gradient)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

51
resources/zap_3.svg

@ -0,0 +1,51 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="128" height="128" viewBox="0 0 100 100">
<defs>
<g id="background">
<path fill="url(#background-gradient)" stroke="none" d=" M 90.75 23.05 Q 89.5 23.05 88.85 24.15 88.15 25.25 88.85 26.35 97.25 40.2 95.15 56.15 93.05 71.65 81.75 82.6 70.35 93.7 54.9 95.25 32.95 97.55 17.6 81.85 2.2 66.2 4.85 44.2 6.6 30.2 16.15 19.7 25.65 9.1 39.25 5.8 50.55 3 61.7 5.95 62.85 6.3 63.7 5.65 64.6 5 64.6 3.85 64.6 2.25 62.95 1.7 52.55 -1.1 41.6 0.65 25.55 3.3 14.4 14.85 3.1 26.25 0.65 42.4 -2.95 66.9 14 84.5 30.85 102.1 55.2 99.7 72.6 97.9 85.2 85.4 97.95 72.8 99.7 55.35 101.45 38.55 92.75 24.05 92.05 23.05 90.75 23.05 Z"/>
</g><mask id="mask">
<path fill="#FFFFFF" stroke="none" d=" M 82.2 82.15 Q 95.5 68.85 95.5 49.95 95.5 31.1 82.2 17.8 68.85 4.5 50 4.5 31.15 4.5 17.8 17.8 4.5 31.1 4.5 49.95 4.5 68.85 17.8 82.15 31.15 95.45 50 95.45 68.85 95.45 82.2 82.15 Z"/>
</mask>
<linearGradient id="background-gradient" x1="0" y1="0" y2="1" x2="0" >
<stop stop-color="#ebb864" offset="0%"/>
<stop stop-color="#1d1d1d" offset="100%"/>
</linearGradient>
<g transform="scale(2.0833333333333335)" id="picture"><path d="M24 0C10.7 0 0 10.7 0 24s10.7 24 24 24 24-10.7 24-24S37.3 0 24 0zM4 24C4 13 13 4 24 4c2.5 0 4.8.5 7 1.3L13.5 27.2h9.3l-6.2 15.3C9.2 39.6 4 32.4 4 24zm20 20c-2.3 0-4.5-.4-6.5-1.1l17-21.2h-9.1l6.5-16C39 8.7 44 15.8 44 24c0 11-9 20-20 20z"/></g>
<linearGradient id="picture-gradient" x1="0" y1="0" y2="1" x2="0" >
<stop stop-color="#ebb864" offset="0%"/>
<stop stop-color="#1d1d1d" offset="100%"/>
</linearGradient>
</defs>
<use xlink:href="#background" fill="url(#background-gradient)" />
<g mask="url(#mask)">
<g transform="
translate(
50 50
)
translate(0 0) scale(0.55)
translate(
-50 -50
)">
<use xlink:href="#picture" fill="url(#picture-gradient)" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

13
test/.eslintrc

@ -0,0 +1,13 @@
{
"env": {
"jest/globals": true
},
"plugins": [
"jest"
],
"rules": {
"jest/no-disabled-tests": "warn",
"jest/no-focused-tests": "error",
"jest/no-identical-title": "error"
}
}

13
test/actions/__snapshots__/counter.spec.js.snap

@ -0,0 +1,13 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`actions should decrement should create decrement action 1`] = `
Object {
"type": "DECREMENT_COUNTER",
}
`;
exports[`actions should increment should create increment action 1`] = `
Object {
"type": "INCREMENT_COUNTER",
}
`;

41
test/actions/counter.spec.js

@ -0,0 +1,41 @@
import { spy } from 'sinon';
import * as actions from '../../app/actions/counter';
describe('actions', () => {
it('should increment should create increment action', () => {
expect(actions.increment()).toMatchSnapshot();
});
it('should decrement should create decrement action', () => {
expect(actions.decrement()).toMatchSnapshot();
});
it('should incrementIfOdd should create increment action', () => {
const fn = actions.incrementIfOdd();
expect(fn).toBeInstanceOf(Function);
const dispatch = spy();
const getState = () => ({ counter: 1 });
fn(dispatch, getState);
expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true);
});
it('should incrementIfOdd shouldnt create increment action if counter is even', () => {
const fn = actions.incrementIfOdd();
const dispatch = spy();
const getState = () => ({ counter: 2 });
fn(dispatch, getState);
expect(dispatch.called).toBe(false);
});
// There's no nice way to test this at the moment...
it('should incrementAsync', done => {
const fn = actions.incrementAsync(1);
expect(fn).toBeInstanceOf(Function);
const dispatch = spy();
fn(dispatch);
setTimeout(() => {
expect(dispatch.calledWith({ type: actions.INCREMENT_COUNTER })).toBe(true);
done();
}, 5);
});
});

68
test/components/Counter.spec.js

@ -0,0 +1,68 @@
import { spy } from 'sinon';
import React from 'react';
import { shallow } from 'enzyme';
import { BrowserRouter as Router } from 'react-router-dom';
import renderer from 'react-test-renderer';
import Counter from '../../app/components/Counter';
function setup() {
const actions = {
increment: spy(),
incrementIfOdd: spy(),
incrementAsync: spy(),
decrement: spy()
};
const component = shallow(<Counter counter={1} {...actions} />);
return {
component,
actions,
buttons: component.find('button'),
p: component.find('.counter')
};
}
describe('Counter component', () => {
it('should should display count', () => {
const { p } = setup();
expect(p.text()).toMatch(/^1$/);
});
it('should first button should call increment', () => {
const { buttons, actions } = setup();
buttons.at(0).simulate('click');
expect(actions.increment.called).toBe(true);
});
it('should match exact snapshot', () => {
const { actions } = setup();
const tree = renderer
.create(
<div>
<Router>
<Counter counter={1} {...actions} />
</Router>
</div>
)
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should second button should call decrement', () => {
const { buttons, actions } = setup();
buttons.at(1).simulate('click');
expect(actions.decrement.called).toBe(true);
});
it('should third button should call incrementIfOdd', () => {
const { buttons, actions } = setup();
buttons.at(2).simulate('click');
expect(actions.incrementIfOdd.called).toBe(true);
});
it('should fourth button should call incrementAsync', () => {
const { buttons, actions } = setup();
buttons.at(3).simulate('click');
expect(actions.incrementAsync.called).toBe(true);
});
});

63
test/components/__snapshots__/Counter.spec.js.snap

@ -0,0 +1,63 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Counter component should match exact snapshot 1`] = `
<div>
<div>
<div
className="backButton"
data-tid="backButton"
>
<a
href="/"
onClick={[Function]}
>
<i
className="fa fa-arrow-left fa-3x"
/>
</a>
</div>
<div
className="counter counter"
data-tid="counter"
>
1
</div>
<div
className="btnGroup"
>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
<i
className="fa fa-plus"
/>
</button>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
<i
className="fa fa-minus"
/>
</button>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
odd
</button>
<button
className="btn"
data-tclass="btn"
onClick={[Function]}
>
async
</button>
</div>
</div>
</div>
`;

57
test/containers/CounterPage.spec.js

@ -0,0 +1,57 @@
import React from 'react';
import { mount } from 'enzyme';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { ConnectedRouter } from 'react-router-redux';
import CounterPage from '../../app/containers/CounterPage';
import { configureStore } from '../../app/store/configureStore';
function setup(initialState) {
const store = configureStore(initialState);
const history = createBrowserHistory();
const app = mount(
<Provider store={store}>
<ConnectedRouter history={history}>
<CounterPage />
</ConnectedRouter>
</Provider>
);
return {
app,
buttons: app.find('button'),
p: app.find('.counter')
};
}
describe('containers', () => {
describe('App', () => {
it('should display initial count', () => {
const { p } = setup();
expect(p.text()).toMatch(/^0$/);
});
it('should display updated count after increment button click', () => {
const { buttons, p } = setup();
buttons.at(0).simulate('click');
expect(p.text()).toMatch(/^1$/);
});
it('should display updated count after descrement button click', () => {
const { buttons, p } = setup();
buttons.at(1).simulate('click');
expect(p.text()).toMatch(/^-1$/);
});
it('shouldnt change if even and if odd button clicked', () => {
const { buttons, p } = setup();
buttons.at(2).simulate('click');
expect(p.text()).toMatch(/^0$/);
});
it('should change if odd and if odd button clicked', () => {
const { buttons, p } = setup({ counter: 1 });
buttons.at(2).simulate('click');
expect(p.text()).toMatch(/^2$/);
});
});
});

113
test/e2e/e2e.spec.js

@ -0,0 +1,113 @@
import { Application } from 'spectron';
import electronPath from 'electron';
import path from 'path';
jasmine.DEFAULT_TIMEOUT_INTERVAL = 15000;
const delay = time => new Promise(resolve => setTimeout(resolve, time));
describe('main window', function spec() {
beforeAll(async () => {
this.app = new Application({
path: electronPath,
args: [path.join(__dirname, '..', '..', 'app')],
});
return this.app.start();
});
afterAll(() => {
if (this.app && this.app.isRunning()) {
return this.app.stop();
}
});
const findCounter = () => this.app.client.element('[data-tid="counter"]');
const findButtons = async () => {
const { value } = await this.app.client.elements('[data-tclass="btn"]');
return value.map(btn => btn.ELEMENT);
};
it('should open window', async () => {
const { client, browserWindow } = this.app;
await client.waitUntilWindowLoaded();
await delay(500);
const title = await browserWindow.getTitle();
expect(title).toBe('Hello Electron React!');
});
it('should haven\'t any logs in console of main window', async () => {
const { client } = this.app;
const logs = await client.getRenderProcessLogs();
// Print renderer process logs
logs.forEach(log => {
console.log(log.message);
console.log(log.source);
console.log(log.level);
});
expect(logs).toHaveLength(0);
});
it('should to Counter with click "to Counter" link', async () => {
const { client } = this.app;
await client.click('[data-tid=container] > a');
expect(await findCounter().getText()).toBe('0');
});
it('should display updated count after increment button click', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[0]); // +
expect(await findCounter().getText()).toBe('1');
});
it('should display updated count after descrement button click', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[1]); // -
expect(await findCounter().getText()).toBe('0');
});
it('shouldnt change if even and if odd button clicked', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[2]); // odd
expect(await findCounter().getText()).toBe('0');
});
it('should change if odd and if odd button clicked', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[0]); // +
await client.elementIdClick(buttons[2]); // odd
expect(await findCounter().getText()).toBe('2');
});
it('should change if async button clicked and a second later', async () => {
const { client } = this.app;
const buttons = await findButtons();
await client.elementIdClick(buttons[3]); // async
expect(await findCounter().getText()).toBe('2');
await delay(1500);
expect(await findCounter().getText()).toBe('3');
});
it('should back to home if back button clicked', async () => {
const { client } = this.app;
await client.element(
'[data-tid="backButton"] > a'
).click();
expect(
await client.isExisting('[data-tid="container"]')
).toBe(true);
});
});

5
test/example.js

@ -0,0 +1,5 @@
describe('description', () => {
it('should have description', () => {
expect(1 + 2).toBe(3);
});
});

9
test/reducers/__snapshots__/counter.spec.js.snap

@ -0,0 +1,9 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`reducers counter should handle DECREMENT_COUNTER 1`] = `0`;
exports[`reducers counter should handle INCREMENT_COUNTER 1`] = `2`;
exports[`reducers counter should handle initial state 1`] = `0`;
exports[`reducers counter should handle unknown action type 1`] = `1`;

22
test/reducers/counter.spec.js

@ -0,0 +1,22 @@
import counter from '../../app/reducers/counter';
import { INCREMENT_COUNTER, DECREMENT_COUNTER } from '../../app/actions/counter';
describe('reducers', () => {
describe('counter', () => {
it('should handle initial state', () => {
expect(counter(undefined, {})).toMatchSnapshot();
});
it('should handle INCREMENT_COUNTER', () => {
expect(counter(1, { type: INCREMENT_COUNTER })).toMatchSnapshot();
});
it('should handle DECREMENT_COUNTER', () => {
expect(counter(1, { type: DECREMENT_COUNTER })).toMatchSnapshot();
});
it('should handle unknown action type', () => {
expect(counter(1, { type: 'unknown' })).toMatchSnapshot();
});
});
});

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save