|
2 | 2 |
|
3 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
4 | 4 |
|
5 |
| - |
6 | 5 | ## How Was It Made?
|
| 6 | + |
7 | 7 | ### Project Creation
|
| 8 | + |
8 | 9 | 1. `create-react-app basic && cd basic`
|
9 | 10 | 1. Add some useful tools: `yarn add lodash` or `npm i --save lodash`
|
10 | 11 |
|
11 | 12 | ### Add Firebase
|
| 13 | + |
12 | 14 | 1. Install Firebase library: `yarn add firebase` or `npm i --save firebase`
|
13 | 15 | 1. Add Firebase config to `src/config.js`:
|
14 |
| - ```js |
15 |
| - export const firebase = { |
16 |
| - apiKey: "AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots", |
17 |
| - authDomain: "redux-firebasev3.firebaseapp.com", |
18 |
| - databaseURL: "https://redux-firebasev3.firebaseio.com", |
19 |
| - projectId: "redux-firebasev3", |
20 |
| - storageBucket: "redux-firebasev3.appspot.com", |
21 |
| - messagingSenderId: "823357791673" |
22 |
| - } |
23 |
| - ``` |
| 16 | + |
| 17 | +```js |
| 18 | +export const firebase = { |
| 19 | + apiKey: "AIzaSyCTUERDM-Pchn_UDTsfhVPiwM4TtNIxots", |
| 20 | + authDomain: "redux-firebasev3.firebaseapp.com", |
| 21 | + databaseURL: "https://redux-firebasev3.firebaseio.com", |
| 22 | + projectId: "redux-firebasev3", |
| 23 | + storageBucket: "redux-firebasev3.appspot.com", |
| 24 | + messagingSenderId: "823357791673", |
| 25 | +}; |
| 26 | +``` |
| 27 | + |
24 | 28 | 1. Add `src/initFirebase.js` - a util to import Firebase and initialize it (supports already initialized Firebase instance on window for testing):
|
25 | 29 |
|
26 |
| - ```js |
27 |
| - import firebase from 'firebase/app' |
28 |
| - import 'firebase/auth' |
29 |
| - import 'firebase/database' |
30 |
| - import 'firebase/firestore' // make sure you add this for firestore |
31 |
| - import { firebase as fbConfig } from './config' |
32 |
| - |
33 |
| - let firebaseInstance |
34 |
| - |
35 |
| - export default function initFirebase(initialState, history) { |
36 |
| - if (firebaseInstance) { |
37 |
| - return firebaseInstance |
38 |
| - } |
39 |
| - // Handle initializeing firebase app if not already on window (when running tests) |
40 |
| - if (window.fbInstance) { |
41 |
| - firebaseInstance = window.fbInstance |
42 |
| - } |
43 |
| - // Init Firebase if an instance doesn't already exist |
44 |
| - if (!firebaseInstance) { |
45 |
| - firebase.initializeApp(fbConfig) |
46 |
| - firebaseInstance = firebase |
47 |
| - } |
48 |
| - // Return Firebase instance |
49 |
| - return firebaseInstance |
| 30 | +```js |
| 31 | +import firebase from "firebase/app"; |
| 32 | +import "firebase/auth"; |
| 33 | +import "firebase/database"; |
| 34 | +import "firebase/firestore"; // make sure you add this for firestore |
| 35 | +import { firebase as fbConfig } from "./config"; |
| 36 | + |
| 37 | +let firebaseInstance; |
| 38 | + |
| 39 | +export default function initFirebase(initialState, history) { |
| 40 | + if (firebaseInstance) { |
| 41 | + return firebaseInstance; |
50 | 42 | }
|
51 |
| - ``` |
| 43 | + // Handle initializeing firebase app if not already on window (when running tests) |
| 44 | + if (window.fbInstance) { |
| 45 | + firebaseInstance = window.fbInstance; |
| 46 | + } |
| 47 | + // Init Firebase if an instance doesn't already exist |
| 48 | + if (!firebaseInstance) { |
| 49 | + firebase.initializeApp(fbConfig); |
| 50 | + firebaseInstance = firebase; |
| 51 | + } |
| 52 | + // Return Firebase instance |
| 53 | + return firebaseInstance; |
| 54 | +} |
| 55 | +``` |
52 | 56 |
|
53 | 57 | 1. Load Firebase data in the home component:
|
54 |
| - |
55 |
| - ```jsx |
56 |
| - import React, { Component } from 'react'; |
57 |
| - import { invoke, map } from 'lodash'; |
58 |
| - import logo from './logo.svg'; |
59 |
| - import initFirebase from './initFirebase' |
60 |
| - import Project from './Project' |
61 |
| - import './App.css'; |
62 |
| - |
63 |
| - const fbInstance = initFirebase() |
64 |
| - |
65 |
| - class App extends Component { |
66 |
| - constructor() { |
67 |
| - super() |
68 |
| - this.state = { loading: false } |
69 |
| - } |
70 |
| - |
71 |
| - componentDidMount() { |
72 |
| - this.setState({ loading: true }) |
73 |
| - fbInstance.database() |
74 |
| - .ref('projects') |
75 |
| - .limitToFirst(10) |
76 |
| - .on('value', (snap) => { |
| 58 | + |
| 59 | +```jsx |
| 60 | +import React, { Component } from "react"; |
| 61 | +import { invoke, map } from "lodash"; |
| 62 | +import logo from "./logo.svg"; |
| 63 | +import initFirebase from "./initFirebase"; |
| 64 | +import Project from "./Project"; |
| 65 | +import "./App.css"; |
| 66 | + |
| 67 | +const fbInstance = initFirebase(); |
| 68 | + |
| 69 | +class App extends Component { |
| 70 | + constructor() { |
| 71 | + super(); |
| 72 | + this.state = { loading: false }; |
| 73 | + } |
| 74 | + |
| 75 | + componentDidMount() { |
| 76 | + this.setState({ loading: true }); |
| 77 | + fbInstance |
| 78 | + .database() |
| 79 | + .ref("projects") |
| 80 | + .limitToFirst(10) |
| 81 | + .on( |
| 82 | + "value", |
| 83 | + (snap) => { |
77 | 84 | this.setState({
|
78 | 85 | projects: snap.val(),
|
79 |
| - loading: false |
80 |
| - }) |
81 |
| - }, (err) => { |
| 86 | + loading: false, |
| 87 | + }); |
| 88 | + }, |
| 89 | + (err) => { |
82 | 90 | this.setState({
|
83 | 91 | loading: false,
|
84 |
| - error: invoke(err, 'toString') || err |
85 |
| - }) |
86 |
| - }) |
87 |
| - } |
88 |
| - |
89 |
| - render() { |
90 |
| - const { loading, projects } = this.state |
91 |
| - return ( |
92 |
| - <div className="App"> |
93 |
| - <header className="App-header"> |
94 |
| - <img src={logo} className="App-logo" alt="logo" /> |
95 |
| - <p> |
96 |
| - Edit <code>src/App.js</code> and save to reload. |
97 |
| - </p> |
98 |
| - { |
99 |
| - loading |
100 |
| - ? <div>Loading...</div> |
101 |
| - : !projects |
102 |
| - ? <div>Projects not found</div> |
103 |
| - : map(projects, (project, projectKey) => |
104 |
| - <Project |
105 |
| - key={`Project-${projectKey}`} |
106 |
| - project={project} |
107 |
| - projectId={projectKey} |
108 |
| - /> |
109 |
| - ) |
110 |
| - } |
111 |
| - </header> |
112 |
| - </div> |
| 92 | + error: invoke(err, "toString") || err, |
| 93 | + }); |
| 94 | + } |
113 | 95 | );
|
114 |
| - } |
115 | 96 | }
|
116 | 97 |
|
117 |
| - export default App; |
118 |
| - ``` |
| 98 | + render() { |
| 99 | + const { loading, projects } = this.state; |
| 100 | + return ( |
| 101 | + <div className="App"> |
| 102 | + <header className="App-header"> |
| 103 | + <img src={logo} className="App-logo" alt="logo" /> |
| 104 | + <p> |
| 105 | + Edit <code>src/App.js</code> and save to reload. |
| 106 | + </p> |
| 107 | + {loading ? ( |
| 108 | + <div>Loading...</div> |
| 109 | + ) : !projects ? ( |
| 110 | + <div>Projects not found</div> |
| 111 | + ) : ( |
| 112 | + map(projects, (project, projectKey) => ( |
| 113 | + <Project |
| 114 | + key={`Project-${projectKey}`} |
| 115 | + project={project} |
| 116 | + projectId={projectKey} |
| 117 | + /> |
| 118 | + )) |
| 119 | + )} |
| 120 | + </header> |
| 121 | + </div> |
| 122 | + ); |
| 123 | + } |
| 124 | +} |
| 125 | + |
| 126 | +export default App; |
| 127 | +``` |
119 | 128 |
|
120 | 129 | ### Add Cypress Testing
|
| 130 | + |
121 | 131 | 1. Add `.env` that looks like so (to skip warnings due to Cypress deps being out of date):
|
122 |
| - ``` |
123 |
| - SKIP_PREFLIGHT_CHECK=true |
124 |
| - ``` |
125 |
| -1. Install Cypress: `yarn add --dev cypress` or `npm i --save-dev cypress` |
126 |
| -1. Install deps for testing: `yarn add --dev cypress-firebase firebase-tools-extra cross-env` or `npm i --save-dev cypress-firebase firebase-tools-extra cross-env` |
127 |
| -1. Run `cypress open` to scaffold out Cypress project and open Cypress UI |
128 |
| -1. Close the Cypress UI and remove the `cypress/examples` folder |
129 |
| -1. Add npm scripts: |
130 |
| - ```json |
131 |
| - "build:testConfig": "cypress-firebase createTestEnvFile", |
132 |
| - "test": "npm run build:testConfig && cross-env CYPRESS_baseUrl=http://localhost:3000 cypress run", |
133 |
| - "test:ui": "npm run build:testConfig && cross-env CYPRESS_baseUrl=http://localhost:3000 cypress open", |
134 |
| - ``` |
135 |
| -1. Add custom commands and Firebase initialization to `cypress/support/commands.js`: |
136 |
| - ```js |
137 |
| - import firebase from 'firebase/app'; |
138 |
| - import 'firebase/auth'; |
139 |
| - import 'firebase/database'; |
140 |
| - import 'firebase/firestore'; |
141 |
| - import { attachCustomCommands } from 'cypress-firebase'; |
142 |
| - const fbConfig = { |
143 |
| - // your firebase config |
144 |
| - } |
145 |
| - window.fbInstance = firebase.initializeApp(fbConfig); |
146 |
| - // add cy.login, cy.logout, cy.callRtdb, and cy.callFirestore |
147 |
| - attachCustomCommands({ Cypress, cy, firebase }) |
148 |
| - ``` |
| 132 | + |
| 133 | +``` |
| 134 | +SKIP_PREFLIGHT_CHECK=true |
| 135 | +``` |
| 136 | + |
| 137 | +1. Install `cypress-firebase` and [`firebase-admin`](https://www.npmjs.org/package/firebase-admin) both: `npm i --save-dev cypress-firebase firebase-admin` or `yarn add -D cypress-firebase firebase-admin` |
| 138 | +1. Go to project setting on firebase console and generate new private key. See how to do so [in the Google Docs](https://sites.google.com/site/scriptsexamples/new-connectors-to-google-services/firebase/tutorials/authenticate-with-a-service-account). |
| 139 | +1. Add `serviceAccount.json` to your `.gitignore` (THIS IS VERY IMPORTANT TO KEEPING YOUR INFORMATION SECURE!) |
| 140 | +1. Save the downloaded file as `serviceAccount.json` in the root of your project (make sure that it is .gitignored) - needed for `firebase-admin` to have read/write access to your DB from within your tests |
| 141 | +1. Add the following your custom commands file (`cypress/support/commands.js`): |
| 142 | + |
| 143 | + ```js |
| 144 | + import firebase from "firebase/app"; |
| 145 | + import "firebase/auth"; |
| 146 | + import "firebase/database"; |
| 147 | + import "firebase/firestore"; |
| 148 | + import { attachCustomCommands } from "cypress-firebase"; |
| 149 | + |
| 150 | + const fbConfig = { |
| 151 | + // Your config from Firebase Console |
| 152 | + }; |
| 153 | + |
| 154 | + firebase.initializeApp(fbConfig); |
| 155 | + |
| 156 | + attachCustomCommands({ Cypress, cy, firebase }); |
| 157 | + ``` |
| 158 | + |
| 159 | +1. Make sure that you load the custom commands file in an `cypress/support/index.js` like so: |
| 160 | + |
| 161 | + ```js |
| 162 | + import "./commands"; |
| 163 | + ``` |
| 164 | + |
| 165 | + **NOTE**: This is a pattern which is setup by default by Cypress, so this file may already exist |
| 166 | + |
| 167 | +1. Setup plugin adding following your plugins file (`cypress/plugins/index.js`): |
| 168 | + |
| 169 | + ```js |
| 170 | + const admin = require("firebase-admin"); |
| 171 | + const cypressFirebasePlugin = require("cypress-firebase").plugin; |
| 172 | + |
| 173 | + module.exports = (on, config) => { |
| 174 | + const extendedConfig = cypressFirebasePlugin(on, config, admin); |
| 175 | + |
| 176 | + // Add other plugins/tasks such as code coverage here |
| 177 | + |
| 178 | + return extendedConfig; |
| 179 | + }; |
| 180 | + ``` |
0 commit comments