diff --git a/app/electron/main.js b/app/electron/main.js index c6efa1eba..88cf5e2b4 100644 --- a/app/electron/main.js +++ b/app/electron/main.js @@ -204,6 +204,7 @@ app.on('ready', createWindow); // Quit when all windows are closed. app.on('window-all-closed', () => { + win.webContents.executeJavaScript('window.localStorage.clear();'); app.quit(); }); @@ -256,8 +257,8 @@ app.on('web-contents-created', (event, contents) => { 'https://www.facebook.com', 'https://www.smashingmagazine.com', 'https://www.html5rocks.com', + 'app://rse/' ]; - // Log and prevent the app from redirecting to a new page if ( !validOrigins.includes(parsedUrl.origin) @@ -360,153 +361,152 @@ if (isDev) { } // // for github oauth login in production, since cookies are not accessible through document.cookie on local filesystem, we need electron to grab the cookie that is set from oauth, this listens for an set cookie event from the renderer process then sends back the cookie -// ipcMain.on('set_cookie', event => { -// session.defaultSession.cookies -// .get({ url: serverUrl }) -// .then(cookie => { -// // this if statement is necessary or the setInterval on main app will constantly run and will emit this event.reply, causing a memory leak -// // checking for a cookie inside array will only emit reply when a cookie exists -// if (cookie[0]) { -// //console.log(cookie); -// event.reply('give_cookie', cookie); -// } -// }) -// .catch(error => { -// console.log('Error giving cookies in set_cookie:', error); -// }); -// }); - -// // again for production, document.cookie is not accessible so we need this listener on main to delete the cookie on logout -// ipcMain.on('delete_cookie', event => { -// session.defaultSession.cookies -// .remove(serverUrl, 'ssid') -// // .then(removed => { -// // console.log('Cookies deleted', removed); -// // }) -// .catch(err => console.log('Error deleting cookie:', err)); -// }); - -// // opens new window for github oauth when button on sign in page is clicked -// ipcMain.on('github', event => { -// // your applications credentials -// const githubUrl = 'https://github.com/login/oauth/authorize?'; -// const options = { -// client_id: process.env.GITHUB_ID, -// client_secret: process.env.GITHUB_SECRET, -// scopes: ['user:email', 'notifications'] -// }; -// // create new browser window object with size, title, security options -// const github = new BrowserWindow({ -// width: 800, -// height: 600, -// title: 'Github Oauth', -// webPreferences: { -// nodeIntegration: false, -// nodeIntegrationInWorker: false, -// nodeIntegrationInSubFrames: false, -// contextIsolation: true, -// enableRemoteModule: false, -// zoomFactor: 1.0 -// } -// }); -// const authUrl = `${githubUrl}client_id=${process.env.GITHUB_ID}`; -// github.loadURL(authUrl); -// github.show(); -// const handleCallback = url => { -// const raw_code = /code=([^&]\*)/.exec(url) || null; -// const code = raw_code && raw_code.length > 1 ? raw_code[1] : null; -// const error = /\?error=(.+)\$/.exec(url); - -// if (code || error) { -// // Close the browser if code found or error -// authWindow.destroy(); -// } - -// // If there is a code, proceed to get token from github -// if (code) { -// self.requestGithubToken(options, code); -// } else if (error) { -// alert( -// "Oops! Something went wrong and we couldn't" + -// 'log you in using Github. Please try again.' -// ); -// } -// }; - -// github.webContents.on('will-navigate', (e, url) => handleCallback(url)); - -// github.webContents.on('did-finish-load', (e, url, a, b) => { -// github.webContents.selectAll(); -// }); - -// github.webContents.on('did-get-redirect-request', (e, oldUrl, newUrl) => -// handleCallback(newUrl) -// ); - -// // Reset the authWindow on close -// github.on('close', () => (authWindow = null), false); - -// // if final callback is reached and we get a redirect from server back to our app, close oauth window -// github.webContents.on('will-redirect', (e, callbackUrl) => { -// const matches = callbackUrl.match(/(?<=\?=).*/); -// const ssid = matches ? matches[0] : ''; -// callbackUrl = callbackUrl.replace(/\?=.*/, ''); -// let redirectUrl = 'app://rse/'; -// if (isDev) { -// redirectUrl = 'http://localhost:8080/'; -// } -// if (callbackUrl === redirectUrl) { -// dialog.showMessageBox({ -// type: 'info', -// title: 'ReacType', -// icon: resolve('app/src/public/icons/png/256x256.png'), -// message: 'Github Oauth Successful!' -// }); -// github.close(); -// win.webContents -// .executeJavaScript(`window.localStorage.setItem('ssid', '${ssid}')`) -// .then(result => win.loadURL(selfHost)) -// .catch(err => console.log(err)); -// } -// }); -// }); - -// ipcMain.on('tutorial', event => { -// // create new browser window object with size, title, security options -// const tutorial = new BrowserWindow({ -// width: 800, -// height: 600, -// minWidth: 661, -// title: 'Tutorial', -// webPreferences: { -// nodeIntegration: false, -// nodeIntegrationInWorker: false, -// nodeIntegrationInSubFrames: false, -// contextIsolation: true, -// enableRemoteModule: false, -// zoomFactor: 1.0 -// } -// }); -// // redirects to relevant server endpoint -// //github.loadURL(`${serverUrl}/github`); -// // show window -// tutorial.show(); -// // if final callback is reached and we get a redirect from server back to our app, close oauth window -// // github.webContents.on('will-redirect', (e, callbackUrl) => { -// // let redirectUrl = 'app://rse/'; -// // if (isDev) { -// // redirectUrl = 'http://localhost:8080/'; -// // } -// // if (callbackUrl === redirectUrl) { -// // dialog.showMessageBox({ -// // type: 'info', -// // title: 'ReacType', -// // icon: resolve('app/src/public/icons/png/256x256.png'), -// // message: 'Github Oauth Successful!' -// // }); -// // github.close(); -// // } -// // }); -// }); +ipcMain.on('set_cookie', event => { + session.defaultSession.cookies + .get({ url: serverUrl }) + .then(cookie => { + // this if statement is necessary or the setInterval on main app will constantly run and will emit this event.reply, causing a memory leak + // checking for a cookie inside array will only emit reply when a cookie exists + if (cookie[0]) { + event.reply('give_cookie', cookie); + } + }) + .catch(error => { + console.log('Error giving cookies in set_cookie:', error); + }); +}); + +// again for production, document.cookie is not accessible so we need this listener on main to delete the cookie on logout +ipcMain.on('delete_cookie', event => { + session.defaultSession.cookies + .remove(serverUrl, 'ssid') + // .then(removed => { + // console.log('Cookies deleted', removed); + // }) + .catch(err => console.log('Error deleting cookie:', err)); +}); + +// opens new window for github oauth when button on sign in page is clicked +ipcMain.on('github', event => { + // your applications credentials + const githubUrl = 'https://github.com/login/oauth/authorize?'; + const options = { + client_id: process.env.GITHUB_ID, + client_secret: process.env.GITHUB_SECRET, + scopes: ['user:email', 'notifications'] + }; + // create new browser window object with size, title, security options + const github = new BrowserWindow({ + width: 800, + height: 600, + title: 'Github Oauth', + webPreferences: { + nodeIntegration: false, + nodeIntegrationInWorker: false, + nodeIntegrationInSubFrames: false, + contextIsolation: true, + enableRemoteModule: false, + zoomFactor: 1.0 + } + }); + const authUrl = `${githubUrl}client_id=${process.env.GITHUB_ID}`; + github.loadURL(authUrl); + github.show(); + const handleCallback = url => { + const raw_code = /code=([^&]\*)/.exec(url) || null; + const code = raw_code && raw_code.length > 1 ? raw_code[1] : null; + const error = /\?error=(.+)\$/.exec(url); + + if (code || error) { + // Close the browser if code found or error + authWindow.destroy(); + } + + // If there is a code, proceed to get token from github + if (code) { + self.requestGithubToken(options, code); + } else if (error) { + alert( + "Oops! Something went wrong and we couldn't" + + 'log you in using Github. Please try again.' + ); + } + }; + + github.webContents.on('will-navigate', (e, url) => handleCallback(url)); + + github.webContents.on('did-finish-load', (e, url, a, b) => { + github.webContents.selectAll(); + }); + + github.webContents.on('did-get-redirect-request', (e, oldUrl, newUrl) => + handleCallback(newUrl) + ); + + // Reset the authWindow on close + github.on('close', () => (authWindow = null), false); + + // if final callback is reached and we get a redirect from server back to our app, close oauth window + github.webContents.on('will-redirect', (e, callbackUrl) => { + const matches = callbackUrl.match(/(?<=\?=).*/); + const ssid = matches ? matches[0] : ''; + callbackUrl = callbackUrl.replace(/\?=.*/, ''); + let redirectUrl = 'app://rse/'; + if (isDev) { + redirectUrl = 'http://localhost:8080/'; + } + if (callbackUrl === redirectUrl) { + dialog.showMessageBox({ + type: 'info', + title: 'ReacType', + icon: resolve('app/src/public/icons/png/256x256.png'), + message: 'Github Oauth Successful!' + }); + github.close(); + win.webContents + .executeJavaScript(`window.localStorage.setItem('ssid', '${ssid}')`) + .then(result => win.loadURL(`${redirectUrl}`)) + .catch(err => console.log(err)); + } + }); +}); + +ipcMain.on('tutorial', event => { + // create new browser window object with size, title, security options + const tutorial = new BrowserWindow({ + width: 800, + height: 600, + minWidth: 661, + title: 'Tutorial', + webPreferences: { + nodeIntegration: false, + nodeIntegrationInWorker: false, + nodeIntegrationInSubFrames: false, + contextIsolation: true, + enableRemoteModule: false, + zoomFactor: 1.0 + } + }); + // redirects to relevant server endpoint + github.loadURL(`${serverUrl}/github`); + // show window + tutorial.show(); + // if final callback is reached and we get a redirect from server back to our app, close oauth window + github.webContents.on('will-redirect', (e, callbackUrl) => { + let redirectUrl = 'app://rse/'; + if (isDev) { + redirectUrl = 'http://localhost:8080/'; + } + if (callbackUrl === redirectUrl) { + dialog.showMessageBox({ + type: 'info', + title: 'ReacType', + icon: resolve('app/src/public/icons/png/256x256.png'), + message: 'Github Oauth Successful!' + }); + github.close(); + } + }); +}); module.exports = dialog; diff --git a/app/src/Dashboard/ProjectContainer.tsx b/app/src/Dashboard/ProjectContainer.tsx index 9579c433e..73b230c4b 100644 --- a/app/src/Dashboard/ProjectContainer.tsx +++ b/app/src/Dashboard/ProjectContainer.tsx @@ -2,7 +2,7 @@ import React, { useState, useContext, useEffect, createContext, } from 'react'; import { - createMuiTheme, MuiThemeProvider, makeStyles, Theme, useTheme, + createTheme, MuiThemeProvider, makeStyles, Theme, useTheme, } from '@material-ui/core/styles'; import { useQuery } from '@apollo/client'; diff --git a/app/src/components/bottom/BottomTabs.tsx b/app/src/components/bottom/BottomTabs.tsx index 92465184c..2d0f6057c 100644 --- a/app/src/components/bottom/BottomTabs.tsx +++ b/app/src/components/bottom/BottomTabs.tsx @@ -46,7 +46,7 @@ const BottomTabs = (props): JSX.Element => { Arrow.renderArrow(state.canvasFocus.childId); return ( -
+
{ const useStyles = makeStyles(theme => ({ root: { flexGrow: 1, - backgroundColor: '#003366', height: '100%', color: '#E8E8E8', boxShadow: '0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23)', }, + rootLight: { + backgroundColor: '#003366' + }, bottomHeader: { flex: 1, flexDirection: 'row', diff --git a/app/src/components/bottom/CreationPanel.tsx b/app/src/components/bottom/CreationPanel.tsx index 4c398fdff..6b5c55bd8 100644 --- a/app/src/components/bottom/CreationPanel.tsx +++ b/app/src/components/bottom/CreationPanel.tsx @@ -2,12 +2,14 @@ import React, { useContext } from 'react'; import ComponentPanel from '../right/ComponentPanel' import StatePropsPanel from '../right/StatePropsPanel' import HTMLPanel from '../left/HTMLPanel' +import { styleContext } from '../../containers/AppContainer'; // Creation panel holds all of the creation functionality of the application. ComponentPanel, HTMLPanel, and StatePropsPanel are all hanged here. // This allows users to create all aspects of this application in one place. const CreationPanel = (props): JSX.Element => { + const {style} = useContext(styleContext); return ( -
+
diff --git a/app/src/components/left/HTMLPanel.tsx b/app/src/components/left/HTMLPanel.tsx index a2cc809f2..34f849f60 100644 --- a/app/src/components/left/HTMLPanel.tsx +++ b/app/src/components/left/HTMLPanel.tsx @@ -2,6 +2,8 @@ import React, { useState, useCallback, useContext, useEffect } from 'react'; import Grid from '@material-ui/core/Grid'; import StateContext from '../../context/context'; import HTMLItem from './HTMLItem'; +// import { styleContext } from './AppContainer'; + import { makeStyles, styled } from '@material-ui/core/styles'; import { Button, @@ -32,6 +34,7 @@ const HTMLPanel = (props): JSX.Element => { const [errorStatus, setErrorStatus] = useState(false); const [state, dispatch] = useContext(StateContext); const {isThemeLight} = props; + // const { style } = useContext(styleContext); let startingID = 0; state.HTMLTypes.forEach(element => { if (element.id >= startingID) startingID = element.id; @@ -156,12 +159,12 @@ const HTMLPanel = (props): JSX.Element => { }, []); return ( -
+
-

New HTML Tag:

+

New HTML Tag:

Tag: @@ -268,10 +271,16 @@ const useStyles = makeStyles({ borderRadius: '4px', }, lightThemeFontColor: { - color: '#155084' + color: '#155084', + '& .MuiInputBase-root': { + color: 'rgba (0, 0, 0, 0.54)', + } }, darkThemeFontColor: { - color: '#ffffff' + color: '#ffffff', + '& .MuiInputBase-root': { + color: '#fff', + } }, errorMessage: { fontSize:"11px", diff --git a/app/src/components/login/SignIn.tsx b/app/src/components/login/SignIn.tsx index 39f94e4ad..dfe7cc72b 100644 --- a/app/src/components/login/SignIn.tsx +++ b/app/src/components/login/SignIn.tsx @@ -73,7 +73,7 @@ const SignIn: React.FC = props => { // Commented because window api is broken. Work in progress - /* useEffect(() => { + useEffect(() => { const githubCookie = setInterval(() => { window.api.setCookie(); window.api.getCookie(cookie => { @@ -88,7 +88,7 @@ const SignIn: React.FC = props => { } }); }, 2000); - }, []);*/ + }, []); const handleChange = (e: React.ChangeEvent) => { let inputVal = e.target.value; @@ -158,7 +158,14 @@ const SignIn: React.FC = props => { window.localStorage.setItem('ssid', 'guest'); props.history.push('/'); }; - + + const handleGithubLogin = ( + e: React.MouseEvent + ) => { + e.preventDefault(); + window.api.github(); + props.history.push('/'); + } const responseFacebook = response => { if (response.accessToken) { newUserIsCreated(response.email, response.email, randomPassword()).then( @@ -235,7 +242,16 @@ const SignIn: React.FC = props => { > Sign In - + + + diff --git a/app/src/components/right/ComponentDrag.tsx b/app/src/components/right/ComponentDrag.tsx index 777c68849..60fda99da 100644 --- a/app/src/components/right/ComponentDrag.tsx +++ b/app/src/components/right/ComponentDrag.tsx @@ -34,6 +34,7 @@ const ComponentDrag = ({isThemeLight}): JSX.Element => { name={comp.name} id={comp.id} root={true} + isThemeLight={isThemeLight} /> ); diff --git a/app/src/components/right/ComponentPanel.tsx b/app/src/components/right/ComponentPanel.tsx index 82f0d87cb..e30beb2d5 100644 --- a/app/src/components/right/ComponentPanel.tsx +++ b/app/src/components/right/ComponentPanel.tsx @@ -125,7 +125,7 @@ const ComponentPanel = ({isThemeLight}): JSX.Element => { }, []); return ( -
+
{/* Add a new component*/}

= ({ name, id, root, isFocus }) => { + isThemeLight: boolean; +}> = ({ name, id, root, isFocus, isThemeLight }) => { const classes = useStyles(); const [state, dispatch] = useContext(StateContext); @@ -73,8 +74,7 @@ const ComponentPanelItem: React.FC<{ xs={8} style={{ display: 'grid', - color: '#262626', - display: 'grid', + color: isThemeLight ? 'black' : 'white', backgroundColor: 'transparent', border: root ? '2px dotted #186BB4' @@ -86,7 +86,7 @@ const ComponentPanelItem: React.FC<{ > {isFocus &&
}
-

{name}

+

{name}

); @@ -102,6 +102,12 @@ const useStyles = makeStyles({ width: '12px', height: '12px', borderRadius: '25px', + }, + lightTheme: { + color: 'rgba (0, 0, 0, 0.54)' + }, + darkTheme: { + color: '#fff' } }); diff --git a/app/src/components/right/ExportButton.tsx b/app/src/components/right/ExportButton.tsx index 8c08ed54e..ebedab976 100644 --- a/app/src/components/right/ExportButton.tsx +++ b/app/src/components/right/ExportButton.tsx @@ -60,15 +60,22 @@ export default function ExportButton() { ))} + + + + ); + let testchecked = 0; // helper function called by showGenerateAppModal // this function will prompt the user to choose an app directory once they've chosen their export option const chooseGenOptions = (genOpt: number) => { // set export option: 0 --> export only components, 1 --> export full project + genOption = genOpt; window.api.chooseAppDir(); + testchecked = document.getElementById('tests').checked; closeModal(); }; @@ -86,6 +93,7 @@ export default function ExportButton() { ? state.name : 'New_ReacType_Project_' + Math.ceil(Math.random() * 99).toString(), genOption, + testchecked, state.projectType, state.components, state.rootComponents diff --git a/app/src/components/right/StatePropsPanel.tsx b/app/src/components/right/StatePropsPanel.tsx index d13dfb9ab..bfcf20d82 100644 --- a/app/src/components/right/StatePropsPanel.tsx +++ b/app/src/components/right/StatePropsPanel.tsx @@ -5,6 +5,9 @@ import { makeStyles, styled, Theme, + createTheme, + ThemeProvider, + withStyles } from "@material-ui/core/styles"; import Button from "@material-ui/core/Button"; import { @@ -24,6 +27,7 @@ import { import StateContext from "../../context/context"; import TableStateProps from "./TableStateProps"; + const StatePropsPanel = ({ isThemeLight }): JSX.Element => { const classes = useStyles(); const [state] = useContext(StateContext); @@ -119,27 +123,33 @@ const StatePropsPanel = ({ isThemeLight }): JSX.Element => {
-

Create New State

+

Create New State

setInputKey(e.target.value)} - /> + className={isThemeLight ? `${classes.rootLight} ${classes.inputTextLight}` : `${classes.rootDark} ${classes.inputTextDark}`} + /> setInputValue(e.target.value)} - /> - - Type + className={isThemeLight ? `${classes.rootLight} ${classes.inputTextLight}` : `${classes.rootDark} ${classes.inputTextDark}`} + /> + + + Type + - Required + + Required +

{


-

Current State Name: {state.components[state.canvasFocus.componentId - 1].name}

- +

+ Current State Name: {state.components[state.canvasFocus.componentId - 1].name} +

+
); }; const useStyles = makeStyles((theme: Theme) => - createStyles({ + ({ inputField: { marginTop: "10px", borderRadius: "5px", @@ -287,6 +302,9 @@ const useStyles = makeStyles((theme: Theme) => darkThemeFontColor: { color: "#fff", }, + greyThemeFontColor: { + color: 'rgba(0,0,0,0.54)', + }, formControl: { margin: theme.spacing(1), minWidth: 120, @@ -294,6 +312,51 @@ const useStyles = makeStyles((theme: Theme) => selectEmpty: { marginTop: theme.spacing(2), }, + color: { + color: '#fff', + }, + rootLight: { + '& .MuiFormLabel-root': { + color: 'rgba(0,0,0,0.54)' + } + }, + rootDark: { + '& .MuiFormLabel-root': { + color: '#fff' + }, + '& .MuiOutlinedInput-notchedOutline': { + borderColor: '#fff' + } + }, + underlineDark: { + borderBottom: '1px solid white' + }, + rootUnderlineDark: { + '& .MuiSelect-icon': { + color: '#fff', + }, + '&::before': { + borderBottom: '1px solid #fff' + } + }, + rootUnderlineLight: { + '& .MuiSelect-icon': { + color: 'rgba(0,0,0,0.54)', + }, + '&::before': { + borderBottom: '1px solid rgba(0,0,0,0.54)' + } + }, + inputTextDark: { + '& .MuiInputBase-input': { + color: 'white' + } + }, + inputTextLight: { + '& .MuiInputBase-input': { + color: 'rgba(0,0,0,0.54)' + } + } }) ); @@ -308,4 +371,5 @@ const MyButton = styled(Button)({ padding: "0 30px", }); + export default StatePropsPanel; diff --git a/app/src/components/right/TableStateProps.tsx b/app/src/components/right/TableStateProps.tsx index efe5386c5..746a84bd3 100644 --- a/app/src/components/right/TableStateProps.tsx +++ b/app/src/components/right/TableStateProps.tsx @@ -7,6 +7,7 @@ import { import Button from '@material-ui/core/Button'; import ClearIcon from '@material-ui/icons/Clear'; import StateContext from '../../context/context'; +import { makeStyles } from '@material-ui/core/styles'; import { StatePropsPanelProps } from '../../interfaces/Interfaces'; @@ -50,9 +51,9 @@ const getColumns = (props) => { }; return ( ); @@ -62,14 +63,19 @@ const getColumns = (props) => { }; const TableStateProps = (props) => { + const classes = useStyles(); const [state] = useContext(StateContext); const [editRowsModel] = useState ({}); const [gridColumns, setGridColumns] = useState([]); + + useEffect(() => { + setGridColumns(getColumns(props)); + }, [props.isThemeLight]) // get currentComponent by using currently focused component's id const currentId = state.canvasFocus.componentId; const currentComponent = state.components[currentId - 1]; - + const rows = currentComponent.stateProps.slice(); const { selectHandler } : StatePropsPanelProps = props; @@ -87,9 +93,35 @@ const TableStateProps = (props) => { pageSize={5} editRowsModel={editRowsModel} onRowClick = {selectHandler} + className={props.isThemeLight ? classes.themeLight : classes.themeDark} />

); }; + +const useStyles = makeStyles({ + themeLight: { + color: 'rgba(0,0,0,0.54)', + '& .MuiTablePagination-root': { + color: 'rbga(0,0,0,0.54)' + }, + }, + themeDark: { + color: 'white', + '& .MuiTablePagination-root': { + color: 'white' + }, + '& .MuiIconButton-root': { + color: 'white' + }, + '& .MuiSvgIcon-root': { + color: 'white' + }, + '& .MuiDataGrid-window': { + backgroundColor: 'rgba(0,0,0,0.54)' + } + } +}); + export default TableStateProps; diff --git a/app/src/containers/AppContainer.tsx b/app/src/containers/AppContainer.tsx index f72d12928..56faf60fa 100644 --- a/app/src/containers/AppContainer.tsx +++ b/app/src/containers/AppContainer.tsx @@ -1,5 +1,5 @@ import React, { useState, useContext, createContext } from 'react'; -import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles'; +import { createTheme, MuiThemeProvider } from '@material-ui/core/styles'; // import Button from '@material-ui/core/Button'; import NavBar from '../components/top/NavBar'; import LeftContainer from './LeftContainer'; diff --git a/app/src/helperFunctions/generateCode.ts b/app/src/helperFunctions/generateCode.ts index 6433bf568..e1c7fe951 100644 --- a/app/src/helperFunctions/generateCode.ts +++ b/app/src/helperFunctions/generateCode.ts @@ -188,7 +188,7 @@ const generateUnformattedCode = ( .join('\n') : imports .map((comp: string) => { - return `import ${comp} from './${comp}.tsx'`; + return `import ${comp} from './${comp}'`; }) .join('\n'); @@ -206,7 +206,7 @@ const generateUnformattedCode = ( ${ classBased ? `class ${currentComponent.name} extends Component {` - : `const ${currentComponent.name} = (props): JSX.Element => {` + : `const ${currentComponent.name} = (props: any): JSX.Element => {` } ${ @@ -274,7 +274,7 @@ const generateUnformattedCode = ( ${links ? `import { Link } from 'gatsby'` : ``} - const ${currentComponent.name} = (props): JSX.Element => { + const ${currentComponent.name} = (props: any): JSX.Element => { const [value, setValue] = useState("INITIAL VALUE"); diff --git a/app/src/helperFunctions/gitHubOauth.ts b/app/src/helperFunctions/gitHubOauth.ts index be972ee88..5eeaa0fe1 100644 --- a/app/src/helperFunctions/gitHubOauth.ts +++ b/app/src/helperFunctions/gitHubOauth.ts @@ -1,8 +1,7 @@ const handleGitHubOauth = () => { - console.log('git hub'); const gitHubUrl = 'https://github.com/login/oauth/authorize?'; const authUrl = - `${gitHubUrl}client_id=${process.env.GITHUB_ID}`; + `${gitHubUrl}client_id=`; window.open(authUrl); } diff --git a/app/src/public/styles/style.css b/app/src/public/styles/style.css index 9f231e81c..78b0fee83 100644 --- a/app/src/public/styles/style.css +++ b/app/src/public/styles/style.css @@ -31,6 +31,7 @@ h4 { display: flex; flex-direction: row; height: 100%; + background-color: #fff; } .customization-section { @@ -45,7 +46,6 @@ h4 { display: flex; flex-direction: row; justify-content: space-evenly; - background-color: #F9F9F9; } .state-prop-grid { @@ -56,7 +56,6 @@ h4 { .HTMLItemCreate { flex-grow: 1; - background-color: #F9F9F9; justify-content: center; } /* diff --git a/app/src/public/styles/theme.ts b/app/src/public/styles/theme.ts index 7afd8de30..bb4440b92 100644 --- a/app/src/public/styles/theme.ts +++ b/app/src/public/styles/theme.ts @@ -1,6 +1,6 @@ -import { createMuiTheme } from '@material-ui/core/styles'; +import { createTheme } from '@material-ui/core/styles'; -export const theme1 = createMuiTheme({ +export const theme1 = createTheme({ palette: { @@ -14,7 +14,7 @@ export const theme1 = createMuiTheme({ }); -export const theme2 = createMuiTheme({ +export const theme2 = createTheme({ palette: { secondary: { main: '#304D6D', // dark mode color diff --git a/app/src/utils/createApplication.util.ts b/app/src/utils/createApplication.util.ts index df9efda7c..8a70addbe 100644 --- a/app/src/utils/createApplication.util.ts +++ b/app/src/utils/createApplication.util.ts @@ -3,6 +3,8 @@ import createFiles from './createFiles.util'; import { Component} from '../interfaces/Interfaces'; +import createTestSuiteClassic from './createTestSuiteClassic.util' + const camelToKebab= (camel:string) => { return camel.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase(); }; @@ -108,8 +110,19 @@ export const createDefaultCSS = (path, appName, components) => { }); }; -export const createPackage = (path, appName) => { +export const createPackage = (path, appName, test) => { const filePath = `${path}/${appName}/package.json`; + let tsjest = `, + "@types/enzyme": "^3.10.9", + "@types/jest": "^27.0.1", + "babel-jest": "^27.2.0", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.6", + "jest": "^27.2.0", + "@types/enzyme-adapter-react-16": "^1.0.6", + "ts-jest": "^27.0.5", + "enzyme-to-json": "^3.6.2"`; + const data = ` { "name": "reactype", @@ -119,7 +132,9 @@ export const createPackage = (path, appName) => { "scripts": { "start": "node server/server.js", "build": "cross-env NODE_ENV=production webpack", - "dev": "cross-env NODE_ENV=development webpack-dev-server" + "dev": "cross-env NODE_ENV=development webpack-dev-server"${ + test ? `, + "test": "jest"`: '' } }, "nodemonConfig": { "ignore": [ @@ -160,7 +175,8 @@ export const createPackage = (path, appName) => { "typescript": "^3.8.3", "webpack": "^4.29.6", "webpack-cli": "^3.3.0", - "webpack-dev-server": "^3.2.1" + "webpack-dev-server": "^3.2.1"${ + test ? tsjest : '' } } } `; @@ -252,18 +268,20 @@ export const createBabel = (path, appName) => { export const createTsConfig = (path, appName) => { const filePath = `${path}/${appName}/tsconfig.json`; const data = ` -{ - "compilerOptions": { - "outDir": "./dist/", - "sourceMap": true, - "noImplicitAny": true, - "module": "commonjs", - "target": "es6", - "jsx": "react", - "allowSyntheticDefaultImports": true - }, - "include": ["./src/**/*"] -} + { + "compilerOptions": { + "outDir": "./dist/", + "sourceMap": true, + "noImplicitAny": true, + "module": "commonjs", + "target": "es6", + "jsx": "react", + "lib": ["dom", "es6"], + "moduleResolution": "node", + "esModuleInterop": true + }, + "include": ["./src/**/*"] + } `; window.api.writeFile(filePath, data, err => { if (err) { @@ -340,23 +358,26 @@ app.listen(8080, () => { async function createApplicationUtil({ path, appName, - components + components, + testchecked, }: { path: string; appName: string; components: Component[]; + testchecked: boolean; }) { - console.log('in the createApplication util'); - await createIndexHtml(path, appName); await createIndexTsx(path, appName); await createDefaultCSS(path, appName, components); - await createPackage(path, appName); + await createPackage(path, appName, testchecked); await createWebpack(path, appName); await createBabel(path, appName); await createTsConfig(path, appName); await createTsLint(path, appName); await createServer(path, appName); + if (testchecked) { + await createTestSuiteClassic({path, appName, components, testchecked}); + } await createFiles(components, path, appName, true); } export default createApplicationUtil; diff --git a/app/src/utils/createGatsbyApp.util.ts b/app/src/utils/createGatsbyApp.util.ts index 32d54df28..bab21770a 100644 --- a/app/src/utils/createGatsbyApp.util.ts +++ b/app/src/utils/createGatsbyApp.util.ts @@ -1,6 +1,7 @@ // Create all files necessary to run a gatsby.js application import createGatsbyFiles from './createGatsbyFiles.util'; +import createTestSuite from './createTestSuite.util'; import { Component } from '../interfaces/Interfaces'; const camelToKebab= (camel:string) => { @@ -24,30 +25,48 @@ const compToCSS = (component: Component) => { } //createPackage -export const createPackage = (path, appName) => { +export const createPackage = (path, appName, test) => { const filePath = `${path}/${appName}/package.json`; + + let tsjest = `, + "@types/enzyme": "^3.10.9", + "@types/jest": "^27.0.1", + "babel-jest": "^27.2.0", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.6", + "jest": "^27.2.0", + "@types/react-dom": "^17.0.9", + "@types/enzyme-adapter-react-16": "^1.0.6", + "@types/react-test-renderer": "^17.0.1", + "babel-preset-gatsby": "^1.13.0", + "identity-obj-proxy": "^3.0.0", + "ts-jest": "^27.0.5"`; + const data = ` -{ - "name": "reactype-gatsby", - "version": "1.0.0", - "description": "", - "scripts": { - "dev": "gatsby develop", - "build": "gatsby build", - "start": "npm run dev" - }, - "dependencies": { - "gatsby": "^2.26.1", - "react": "16.13.1", - "react-dom": "16.13.1" - }, - "devDependencies": { - "@types/node": "^14.0.20", - "@types/react": "^16.9.41", - "typescript": "^3.9.6" - } -} - `; + { + "name": "reactype-gatsby", + "version": "1.0.0", + "description": "", + "scripts": { + "dev": "gatsby develop", + "build": "gatsby build", + "start": "npm run dev"${ + test ? `, + "test": "jest"`: '' } + }, + "dependencies": { + "gatsby": "^2.26.1", + "react": "16.13.1", + "react-dom": "16.13.1" + }, + "devDependencies": { + "@types/node": "^14.0.20", + "@types/react": "^16.9.41", + "typescript": "^3.9.6"${ + test ? tsjest : '' } + } + } + `; window.api.writeFile(filePath, data, err => { if (err) { console.log('package.json error:', err.message); @@ -58,9 +77,24 @@ export const createPackage = (path, appName) => { }; //createTSConfig (empty) export const createTsConfig = (path, appName) => { - const filePath = `${path}/${appName}/tsconfig.json`; + const filePath:string = `${path}/${appName}/tsconfig.json`; //running 'gatsby dev' will autopopulate this with default values - window.api.writeFile(filePath, '', err => { + const data:string = `{ + "compilerOptions": { + "outDir": "./dist/", + "sourceMap": true, + "noImplicitAny": true, + "module": "commonjs", + "target": "esnext", + "jsx": "react", + "lib": ["dom", "esnext"], + "moduleResolution": "node", + "esModuleInterop": true + }, + "include": ["./src/**/*"] +} +`; + window.api.writeFile(filePath, data, err => { if (err) { console.log('TSConfig error:', err.message); } else { @@ -110,7 +144,7 @@ export const initFolders = (path:string, appName: string) => { }; //createBaseTsx -export const createBaseTsx = (path, appName) => { +export const createBaseTsx = (path: string, appName: string) => { const filePath:string = `${path}/${appName}/src/pages/_app.tsx`; const data:string = ` @@ -140,21 +174,23 @@ async function createGatsbyAppUtil({ path, appName, components, - rootComponents + rootComponents, + testchecked, }: { path: string; appName: string; components: Component[]; rootComponents: number[]; + testchecked: boolean; }) { - console.log('in the createGatsbyApplication util'); - await initFolders(path, appName); await createBaseTsx(path, appName); await createDefaultCSS(path, appName, components); - await createPackage(path, appName); + await createPackage(path, appName, testchecked); await createTsConfig(path, appName); + if (testchecked) { + await createTestSuite({path, appName, components, rootComponents, testchecked}); + } await createGatsbyFiles(components, path, appName, rootComponents); - } export default createGatsbyAppUtil; diff --git a/app/src/utils/createNextApp.util.ts b/app/src/utils/createNextApp.util.ts index 1f06edfd4..dd3e8b36f 100644 --- a/app/src/utils/createNextApp.util.ts +++ b/app/src/utils/createNextApp.util.ts @@ -3,6 +3,8 @@ import createNextFiles from './createNextFiles.util'; import { Component } from '../interfaces/Interfaces'; +import createTestSuiteNext from './createTestSuiteNext.util'; + const camelToKebab= (camel:string) => { return camel.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase(); }; @@ -24,8 +26,20 @@ const compToCSS = (component: Component) => { } //createPackage -export const createPackage = (path, appName) => { +export const createPackage = (path, appName, test) => { const filePath = `${path}/${appName}/package.json`; + + let testpackages = `, + "@types/enzyme": "^3.10.9", + "@types/jest": "^27.0.1", + "babel-jest": "^27.2.0", + "enzyme": "^3.11.0", + "enzyme-adapter-react-16": "^1.15.6", + "jest": "^27.2.0", + "@types/enzyme-adapter-react-16": "^1.0.6", + "identity-obj-proxy": "^3.0.0", + "ts-jest": "^27.0.5"`; + const data = ` { "name": "reactype-next", @@ -34,7 +48,9 @@ export const createPackage = (path, appName) => { "scripts": { "dev": "next dev", "build": "next build", - "start": "next start" + "start": "next start"${ + test ? `, + "test": "jest"`: '' } }, "dependencies": { "next": "9.3.5", @@ -44,15 +60,18 @@ export const createPackage = (path, appName) => { "devDependencies": { "@types/node": "^14.0.20", "@types/react": "^16.9.41", - "typescript": "^3.9.6" + "@types/react-dom": "^17.0.9", + "typescript": "^3.9.6"${ + test ? testpackages : '' + } } } - `; +`; window.api.writeFile(filePath, data, err => { if (err) { - console.log('package.json error:', err.message); + console.log('createNextApp.util package.json error:', err.message); } else { - console.log('package.json written successfully'); + console.log('createNextApp.util package.json written successfully'); } }); }; @@ -60,7 +79,46 @@ export const createPackage = (path, appName) => { export const createTsConfig = (path, appName) => { const filePath = `${path}/${appName}/tsconfig.json`; //running 'next dev' will autopopulate this with default values - window.api.writeFile(filePath, '', err => { + const data:string = `{ + "compileOnSave": false, + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "jsx": "preserve", + "allowJs": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "removeComments": false, + "preserveConstEnums": true, + "sourceMap": true, + "skipLibCheck": true, + "baseUrl": ".", + "lib": [ + "dom", + "es2016" + ], + "strict": false, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "resolveJsonModule": true, + "isolatedModules": true + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} +`; + + + window.api.writeFile(filePath, data, err => { if (err) { console.log('TSConfig error:', err.message); } else { @@ -139,21 +197,23 @@ async function createNextAppUtil({ path, appName, components, - rootComponents + rootComponents, + testchecked, }: { path: string; appName: string; components: Component[]; rootComponents: number[]; + testchecked: boolean; }) { - console.log('in the createNextApplication util'); - await initFolders(path, appName); await createBaseTsx(path, appName); await createDefaultCSS(path, appName, components); - await createPackage(path, appName); + await createPackage(path, appName, testchecked); await createTsConfig(path, appName); + if (testchecked) { + await createTestSuiteNext({path, appName, components, rootComponents, testchecked}); + } await createNextFiles(components, path, appName, rootComponents); - } export default createNextAppUtil; diff --git a/app/src/utils/createTestSuite.util.ts b/app/src/utils/createTestSuite.util.ts new file mode 100644 index 000000000..45b0b09dd --- /dev/null +++ b/app/src/utils/createTestSuite.util.ts @@ -0,0 +1,200 @@ + +// create config files +// add webpack dependencies +// create tests for components + +const initFolders = (path: string, appName: string) => { + let dir = path; + dir = `${dir}/${appName}`; + if (!window.api.existsSync(`${dir}/__mocks__`)) { + window.api.mkdirSync(`${dir}/__mocks__`); + } + if (!window.api.existsSync(`${dir}/__tests__`)) { + window.api.mkdirSync(`${dir}/__tests__`); + } +} + +const createMocksFiles = (path: string, appName: string) => { + const filePath:string = `${path}/${appName}/__mocks__/file-mock.js`; + let data = `module.exports = "test-file-stub";` + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuite.util createMocksFiles error', err.message); + } else { + console.log('createTestSuite.util createMocksFiles written successfully'); + } + }); + +} + + + +const createTestsFiles = (path: string, appName: string) => { + const filePath:string = `${path}/${appName}/__mocks__/gatspy.js`; + let data = ` + const React = require("react") + const gatsby = jest.requireActual("gatsby") + + module.exports = { + ...gatsby, + Link: jest.fn().mockImplementation( + ({ + activeClassName, + activeStyle, + getProps, + innerRef, + partiallyActive, + ref, + replace, + to, + ...rest + }) => + React.createElement("a", { + ...rest, + href: to, + }) + ), + StaticQuery: jest.fn(), + useStaticQuery: jest.fn(), + } +`; + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuite.util createTestsFiles error', err.message); + } else { + console.log('createTestSuite.util createTestsFiles written successfully'); + } + }); +} + +async function createJestConfigFile(path: String, appName: String){ + const filePath:string = `${path}/${appName}/jest.config.js`; + const data:string = ` + module.exports = { + transform: { + "^.+\\.tsx?$": "ts-jest", + "^.+\\.jsx?$": "/jest-preprocess.js", + }, + testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.([tj]sx?)$", + moduleNameMapper: { + ".+\\.(css|styl|less|sass|scss)$": "identity-obj-proxy", + ".+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/file-mock.js", + }, + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + testPathIgnorePatterns: ["node_modules", ".cache"], + transformIgnorePatterns: ["node_modules/(?!(gatsby)/)"], + globals: { + __PATH_PREFIX__: '', + } + } + ` + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuite.util createJestConfigFile error:', err.message); + } else { + console.log('createTestSuit.util createJestConfigFile written successfully'); + } + }); +}; + +async function createJestPreprocessFile(path: string, appName: string){ + const filePath: string = `${path}/${appName}/jest-preprocess.js`; + const data:string = ` + const babelOptions = { + presets: ["babel-preset-gatsby"], + } + + module.exports = require("babel-jest").default.createTransformer(babelOptions)`; + + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuite.util createJestPreprocessFile error:', err.message); + } else { + console.log('createTestSuit.util createJestPreprocessFile written successfully'); + } + }); +} + +async function createComponentTests(path: string, appName: string, components: Component[]) { + const filePath: string = `${path}/${appName}/__tests__/test.tsx`; + + let data:string = ` + import React from "react" + import Enzyme, { shallow } from 'enzyme'; + + + import Adapter from 'enzyme-adapter-react-16'; + Enzyme.configure({ adapter: new Adapter() }); + `; + + components.forEach(page => { + + let importString = '' + if (page.isPage) { + + importString = ` + import ${capitalize(page.name)} from "../src/pages/${page.name}";`; + data = data + importString; + } else { + importString = ` + import ${capitalize(page.name)} from "../src/components/${page.name}";`; + data = data + importString; + } + }) + + //let describe = `describe("${page.name}", () => {` + components.forEach(page => { + data = data + ` + + describe("${capitalize(page.name)}", () => {` + + + data = data + ` + it("renders correctly", () => { + const tree = shallow(<${capitalize(page.name)} />); + expect(tree).toMatchSnapshot(); + })` + + + data = data + ` + });` + }) + + + + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuite.util createComponentTests error:', err.message); + } else { + console.log('createTestSuit.util createComponentTests written successfully'); + } + }); +} + +const capitalize = (string: string) => { + return string.charAt(0).toUpperCase() + string.slice(1); +} + + +async function createTestSuite({ + path, + appName, + components, + rootComponents, + testchecked, +}: { + path: string; + appName: string; + components: Component[]; + rootComponents: number[]; + testchecked: boolean; +}) { + await initFolders(path, appName); + await createMocksFiles(path, appName); + await createTestsFiles(path, appName); + await createJestConfigFile(path, appName); + await createJestPreprocessFile(path, appName); + await createComponentTests(path, appName, components); +} + +export default createTestSuite; \ No newline at end of file diff --git a/app/src/utils/createTestSuiteClassic.util.ts b/app/src/utils/createTestSuiteClassic.util.ts new file mode 100644 index 000000000..326a97baa --- /dev/null +++ b/app/src/utils/createTestSuiteClassic.util.ts @@ -0,0 +1,152 @@ + + + +const initFolders = (path: string, appName: string) => { + let dir = path; + dir = `${dir}/${appName}`; + if (!window.api.existsSync(`${dir}/__tests__`)) { + window.api.mkdirSync(`${dir}/__tests__`); + } +} + +const createJestConfigFile = (path: string, appName: string) => { +const filePath:string = `${path}/${appName}/jest.config.js`; +const data:String= ` +module.exports = { + snapshotSerializers: ["enzyme-to-json/serializer"], + transform: { + "^.+\\.tsx?$": "ts-jest", + "^.+\\.jsx?$": "/jest-preprocess.js", + }, + testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.([tj]sx?)$", + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + testPathIgnorePatterns: ["node_moules", ".cache"], + globals: { + __PATH_PREFIX__: "", + } +} ` + + +window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuiteClassic.util createJestConfigFile error:', err.message); + } else { + console.log('createTestSuiteClassic.util createJestConfigFile written successfully'); + } + }); +}; + +const createJestPreprocessFile = (path: string, appName: string) => { + const filePath: string = `${path}/${appName}/jest-preprocess.js`; + const data: string = ` + module.exports = require("babel-jest")`; + + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuite.util createJestPreprocessFile error:', err.message); + } else { + console.log('createTestSuit.util createJestPreprocessFile written successfully'); + } + }); +} + +async function createComponentTests(path: string, appName: string, components: Component[]) { + const filePath: string = `${path}/${appName}/__tests__/test.tsx`; + + let data:string = ` + import { shallow } from 'enzyme' + import React from 'react'; + + import * as Enzyme from 'enzyme' + import Adapter from 'enzyme-adapter-react-16' + + Enzyme.configure({ + adapter: new Adapter(), +}) + `; + + components.forEach(page => { + + let importString = ` + import ${capitalize(page.name)} from "../src/components/${page.name}";`; + data = data + importString; + }) + + //let describe = `describe("${page.name}", () => {` + components.forEach(page => { + data = data + ` + + describe("${capitalize(page.name)}", () => {` + + + data = data + ` + it('renders snapshots, too', () => { + const wrapper = shallow(< ${capitalize(page.name)} />) + expect(wrapper).toMatchSnapshot() + })` + + + data = data + ` + });` + }) + + + + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuite.util createComponentTests error:', err.message); + } else { + console.log('createTestSuit.util createComponentTests written successfully'); + } + }); + } + + const capitalize = (string: string) => { + return string.charAt(0).toUpperCase() + string.slice(1); + } + +// ////////////////////////// +// // DELETE BELOW AFTERWARDS +// import { shallow } from 'enzyme' +// import React from 'react'; + +// import * as Enzyme from 'enzyme' +// import Adapter from 'enzyme-adapter-react-16' + +// Enzyme.configure({ +// adapter: new Adapter(), +// }) + +// describe('Hello, Enzyme!', () => { +// it('renders', () => { +// const wrapper = shallow(
+//

Hello, Enzyme!

+//
) +// expect(wrapper.find('h1').html()).toMatch(/Hello, Enzyme/) +// }) + +// it('renders snapshots, too', () => { +// const wrapper = shallow(< />) +// expect(wrapper).toMatchSnapshot() +// }) +// }) +// // DELETE ABOVE AFTERWARDS +// ////////////////////////// +async function createTestSuiteClassic({ + path, + appName, + components, + testchecked, +}: { + path: string; + appName: string; + components: Component[]; + testchecked: boolean; +}) { + await initFolders(path, appName); + await createJestConfigFile(path, appName); + await createJestPreprocessFile(path, appName); + await createComponentTests(path, appName, components); +} + +export default createTestSuiteClassic; \ No newline at end of file diff --git a/app/src/utils/createTestSuiteNext.util.ts b/app/src/utils/createTestSuiteNext.util.ts new file mode 100644 index 000000000..6342aaa7f --- /dev/null +++ b/app/src/utils/createTestSuiteNext.util.ts @@ -0,0 +1,208 @@ + + +const initFolders = (path: string, appName: string) => { + let dir = path; + dir = `${dir}/${appName}`; + if (!window.api.existsSync(`${dir}/__mocks__`)) { + window.api.mkdirSync(`${dir}/__mocks__`); + } + if (!window.api.existsSync(`${dir}/__tests__`)) { + window.api.mkdirSync(`${dir}/__tests__`); + } +} + +async function createJestConfigFile(path: String, appName: String){ + const filePath:string = `${path}/${appName}/jest.config.js`; + const data:string = ` +module.exports = { + moduleFileExtensions: [ + "ts", + "tsx", + "js" + ], + transform: { + "^.+\\.tsx?$": "ts-jest", + "^.+\\.jsx?$": "/jest-preprocess.js", + + }, + testRegex: "(/__tests__/.*|(\\.|/)(test|spec))\\.([tj]sx?)$", + globals: { + "ts-jest": { + babelConfig: true, + tsconfig: "jest.tsconfig.json" + } + }, + coveragePathIgnorePatterns: [ + "/node_modules/", + "enzyme.js" + ], + setupFilesAfterEnv: ["/enzyme.js"], + coverageReporters: [ + "json", + "lcov", + "text", + "text-summary" + ], + moduleNameMapper: { + "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "/__mocks__/file-mock.js", + "\\.(css|less|scss)$": "identity-obj-proxy" + } +} +`; + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuiteNext.util createJestConfigFile error:', err.message); + } else { + console.log('createTestSuitNext.util createJestConfigFile written successfully'); + } + }); +}; + +async function createJestTsconfigJsonFile(path: String, appName: String){ + const filePath:string = `${path}/${appName}/jest.tsconfig.json`; + const data:string = ` +{ + "compilerOptions": { + "module": "commonjs", + "target": "esnext", + "jsx": "react", + "sourceMap": false, + "experimentalDecorators": true, + "noImplicitUseStrict": true, + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "lib": [ + "es2017", + "dom" + ], + "typeRoots": [ + "node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "out", + ".next" + ] +} +`; + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuiteNext.util createJestTsconfigJsonFile error:', err.message); + } else { + console.log('createTestSuitNext.util createJestTsconfigJsonFile written successfully'); + } + }); +}; + + +async function createJestPreprocessFile(path: string, appName: string){ + const filePath: string = `${path}/${appName}/jest-preprocess.js`; + const data:string = ` +const babelOptions = { + presets: ["next/babel"], +} + +module.exports = require("babel-jest").default.createTransformer(babelOptions)`; + + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuite.util createJestPreprocessFile error:', err.message); + } else { + console.log('createTestSuit.util createJestPreprocessFile written successfully'); + } + }); +} + +async function createEnzymeFile(path: string, appName: string){ + const filePath: string = `${path}/${appName}/enzyme.js`; + const data:string = `const Adapter = require('enzyme-adapter-react-16'); +require('enzyme').configure({adapter: new Adapter()});`; + + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuite.util createEnzymeFile error:', err.message); + } else { + console.log('createTestSuit.util createEnzymeFile written successfully'); + } + }); +} + +async function createComponentTests(path: string, appName: string, components: Component[]) { + const filePath: string = `${path}/${appName}/__tests__/test.tsx`; + + let data:string = ` + import { shallow } from 'enzyme' + import React from 'react'; + `; + + components.forEach(page => { + let importString = ''; + if (page.isPage) { + importString = ` +import ${capitalize(page.name)} from "../pages/${page.name}";`; + data = data + importString; + } else { + importString = ` +import ${capitalize(page.name)} from "../components/${page.name}";`; + data = data + importString; + } + }) + + //let describe = `describe("${page.name}", () => {` + components.forEach(page => { + data = data + ` + + describe("${capitalize(page.name)}", () => {` + + + data = data + ` + it('renders snapshots, too', () => { + const wrapper = shallow(< ${capitalize(page.name)} />) + expect(wrapper).toMatchSnapshot() + })` + + + data = data + ` + });` + }) + + + + window.api.writeFile(filePath, data, err => { + if (err) { + console.log('createTestSuiteNext.util createComponentTests error:', err.message); + } else { + console.log('createTestSuitNext.util createComponentTests written successfully'); + } + }); +} + +const capitalize = (string: string) => { + return string.charAt(0).toUpperCase() + string.slice(1); +} + + +async function createTestSuite({ + path, + appName, + components, + rootComponents, + testchecked, +}: { + path: string; + appName: string; + components: Component[]; + rootComponents: number[]; + testchecked: boolean; +}) { + await initFolders(path, appName); + await createJestConfigFile(path, appName); + await createJestTsconfigJsonFile(path, appName); + await createJestPreprocessFile(path, appName); + await createEnzymeFile(path, appName); + await createComponentTests(path, appName, components); +} + +export default createTestSuite; \ No newline at end of file diff --git a/app/src/utils/exportProject.util.ts b/app/src/utils/exportProject.util.ts index 5f65afe2d..e14522336 100644 --- a/app/src/utils/exportProject.util.ts +++ b/app/src/utils/exportProject.util.ts @@ -8,13 +8,14 @@ const exportProject = ( path: string, appName: string, genOption: number, + tests: boolean, projectType: string, components: any, rootComponents: number[] ) => { // Create fully functional classic react application if (genOption === 1 && projectType === 'Classic React') { - createApplicationUtil({ path, appName, components }).catch(err => + createApplicationUtil({ path, appName, components, testchecked: tests }).catch(err => console.log(err) ); } // export all component files, but don't create all application files @@ -22,13 +23,14 @@ const exportProject = ( createFiles(components, path, appName, false); } // Create fully functional Next.js and Gatsby.js application files else if (genOption === 1 && projectType === 'Next.js') { - createNextApp({ path, appName, components, rootComponents }).catch(err => + createNextApp({ path, appName, components, rootComponents, testchecked: tests }).catch(err => console.log(err) ); } else if (genOption === 1 && projectType === 'Gatsby.js') { - createGatsbyApp({ path, appName, components, rootComponents }).catch(err => + createGatsbyApp({ path, appName, components, rootComponents, testchecked: tests }).catch(err => console.log(err)); } + }; export default exportProject; diff --git a/package.json b/package.json index 7a9ae9eb6..449fa41f1 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,8 @@ "dev-server": "cross-env NODE_ENV=development webpack-dev-server --config ./webpack.development.js", "dev": "concurrently --success first \"cross-env NODE_ENV=development webpack-dev-server --config ./webpack.development.js\" \"cross-env NODE_ENV=development electron .\" \"npm run server\" -k", "p": "concurrently --success first \"npm run dev-server\" \"cross-env NODE_ENV=production electron .\" \"nodemon server/server.js\" -k", - "prod-build": "cross-env NODE_ENV=production npx webpack --mode=production --config ./webpack.production.js", - "prod": "npm run prod-build && electron . --no-sandbox", + "prod-build": "cross-env NODE_ENV=production npx webpack --mode=production --config ./webpack.production.js", + "prod": "npm run prod-build && electron . --no-sandbox", "pack": "electron-builder --dir", "dist": "npm run prod-build && electron-builder", "dist-mac": "npm run prod-build && electron-builder --mac", @@ -63,7 +63,7 @@ "url": "git+https://github.com/open-source-labs/ReacType.git" }, "keywords": [ - "electron", + "electron", "prototying", "react", "next.js" diff --git a/server/controllers/sessionController.js b/server/controllers/sessionController.js index 49de717f1..9cd71679b 100644 --- a/server/controllers/sessionController.js +++ b/server/controllers/sessionController.js @@ -1,3 +1,5 @@ +const fetch = require ('node-fetch'); + require('dotenv').config(); const { Sessions } = require('../models/reactypeModels'); @@ -38,7 +40,7 @@ sessionController.startSession = (req, res, next) => { log: `Error in sessionController.startSession find session: ${err}`, message: { err: - 'Error in sessionController.startSession find session, check server logs for details' + 'Error in sessionController.startSession find session, check server logs for details' } }); // if session doesn't exist, create a session @@ -66,97 +68,97 @@ sessionController.startSession = (req, res, next) => { }); }; -// sessionController.gitHubResponse = (req, res, next) => { -// // console.log(req) -// const { code } = req.query; -// // console.log('code =>', code); -// if (!code) -// return next({ -// log: 'Undefined or no code received from github.com', -// message: 'Undefined or no code received from github.com', -// status: 400 -// }); -// fetch(`https://github.com/login/oauth/access_token`, { -// method: 'POST', -// headers: { -// Accept: 'application/json', -// 'Content-Type': 'application/json' -// }, -// body: JSON.stringify({ -// client_id: process.env.GITHUB_ID, -// client_secret: process.env.GITHUB_SECRET, -// code -// }) -// }) -// .then(res => res.json()) -// .then(token => { -// res.locals.token = token['access_token']; -// return next(); -// }) -// .catch(err => -// res.status(500).json({ message: `${err.message} in gitHubResponse` }) -// ); -// }; +sessionController.gitHubResponse = (req, res, next) => { + const { code } = req.query; + if (!code) + return next({ + log: 'Undefined or no code received from github.com', + message: 'Undefined or no code received from github.com', + status: 400 + }); + fetch(`https://github.com/login/oauth/access_token?client_id=${process.env.GITHUB_ID}&client_secret=${process.env.GITHUB_SECRET}&code=${code}`, { + method: 'POST', + headers: { + accept: 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + client_id: process.env.GITHUB_ID, + client_secret: process.env.GITHUB_SECRET, + code + }) + }) + .then(res => res.json()) + .then(token => { + res.locals.token = token['access_token']; + return next(); + }) + .catch(err => { + res.status(500).json({ message: `${err.message} in gitHubResponse` }) + } + ); + +}; -// sessionController.gitHubSendToken = (req, res, next) => { -// const { token } = res.locals; -// fetch(`https://api.github.com/user/emails`, { -// method: 'GET', -// headers: { -// Accept: 'application/vnd.github.v3+json', -// Authorization: `token ${token}` -// } -// }) -// .then(res => res.json()) -// .then(data => { -// res.locals.githubEmail = data[0]['email']; -// res.locals.signUpType = 'oauth'; -// return next(); -// }) -// .catch(err => { -// if (err.message === `Cannot read property 'email' of undefined`) { -// return res -// .status(400) -// .json({ message: `${err.message} in gitHubSendToken` }); -// } else { -// return res -// .status(500) -// .json({ message: `${err.message} in gitHubSendToken` }); -// } -// }); -// }; +sessionController.gitHubSendToken = (req, res, next) => { + const { token } = res.locals; + fetch(`https://api.github.com/user/public_emails`, { + method: 'GET', + headers: { + Accept: 'application/vnd.github.v3+json', + Authorization: `token ${token}` + } + }) + .then(res => res.json()) + .then(data => { + res.locals.githubEmail = data[0]['email']; + res.locals.signUpType = 'oauth'; + return next(); + }) + .catch(err => { + if (err.message === `Cannot read property 'email' of undefined`) { + return res + .status(400) + .json({ message: `${err.message} in gitHubSendToken` }); + } else { + return res + .status(500) + .json({ message: `${err.message} in gitHubSendToken` }); + } + }); +}; -// // creates a session when logging in with github -// sessionController.githubSession = (req, res, next) => { -// // req.user is passed in from passport js -> serializeuser/deserializeuser -// const cookieId = req.user._id; -// Sessions.findOne({ cookieId }, (err, session) => { -// if (err) { -// return next({ -// log: `Error in sessionController.githubSession find session: ${err}`, -// message: { -// err: `Error in sessionController.githubSession find session, check server logs for details` -// } -// }); -// } else if (!session) { -// Sessions.create({ cookieId }, (err, session) => { -// if (err) { -// return next({ -// log: `Error in sessionController.githubSession create session: ${err}`, -// message: { -// err: `Error in sessionController.githubSession create session, check server logs for details` -// } -// }); -// } else { -// res.locals.id = session.cookieId; -// return next(); -// } -// }); -// } else { -// res.locals.id = session.cookieId; -// return next(); -// } -// }); -// }; +// creates a session when logging in with github +sessionController.githubSession = (req, res, next) => { + // req.user is passed in from passport js -> serializeuser/deserializeuser + const cookieId = req.user._id; + Sessions.findOne({ cookieId }, (err, session) => { + if (err) { + return next({ + log: `Error in sessionController.githubSession find session: ${err}`, + message: { + err: `Error in sessionController.githubSession find session, check server logs for details` + } + }); + } else if (!session) { + Sessions.create({ cookieId }, (err, session) => { + if (err) { + return next({ + log: `Error in sessionController.githubSession create session: ${err}`, + message: { + err: `Error in sessionController.githubSession create session, check server logs for details` + } + }); + } else { + res.locals.id = session.cookieId; + return next(); + } + }); + } else { + res.locals.id = session.cookieId; + return next(); + } + }); +}; module.exports = sessionController; diff --git a/server/controllers/userController.js b/server/controllers/userController.js index fbc8d4dfa..812a1f8ad 100644 --- a/server/controllers/userController.js +++ b/server/controllers/userController.js @@ -5,36 +5,38 @@ const userController = {}; const bcrypt = require('bcryptjs'); // random password is subtituted when user uses Oauth and no new password is provided -// const randomPassword = () => { -// function getRandomSpecialChar() { -// const code = Math.round(Math.random() * (38 - 37) + 37); -// return String.fromCharCode(code); -// } -// function getRandomDigit() { -// const code = Math.round(Math.random() * (57 - 48) + 48); -// return String.fromCharCode(code); -// } -// function getRandomLetter() { -// const code = Math.round(Math.random() * (90 - 65) + 65); -// return String.fromCharCode(code); -// } -// let password = ''; -// for (let i = 0; i < 6; i += 1) { -// password += getRandomLetter() + getRandomDigit() + getRandomSpecialChar(); -// } -// return password; -// } +const randomPassword = () => { + function getRandomSpecialChar() { + const code = Math.round(Math.random() * (38 - 37) + 37); + return String.fromCharCode(code); + } + function getRandomDigit() { + const code = Math.round(Math.random() * (57 - 48) + 48); + return String.fromCharCode(code); + } + function getRandomLetter() { + const code = Math.round(Math.random() * (90 - 65) + 65); + return String.fromCharCode(code); + } + let password = ''; + for (let i = 0; i < 6; i += 1) { + password += getRandomLetter() + getRandomDigit() + getRandomSpecialChar(); + } + return password; +} userController.createUser = (req, res, next) => { - const { email, username, password } = req.body; + let email, username, password; // use this condition for Oauth login - // if (res.locals.signUpType === 'oauth') { - // email = res.locals.githubEmail; - // username = email; - // password = randomPassword(); - // } - // error handling if username or email or password is missing + if (res.locals.signUpType === 'oauth') { + email = res.locals.githubEmail; + username = email; + password = randomPassword(); + res.locals.githubPassword = password; + } else { + ({ email, username, password } = req.body); + } if (!username) { return res.status(400).json('No username input'); @@ -83,10 +85,10 @@ userController.createUser = (req, res, next) => { userController.verifyUser = (req, res, next) => { let { username, password, isFbOauth } = req.body; // handle Oauth - // if (res.locals.signUpType === 'oauth') { - // username = res.locals.githubEmail; - // password = res.locals.githubPassword; - // } + if (res.locals.signUpType === 'oauth') { + username = res.locals.githubEmail; + password = res.locals.githubPassword; + } if (!username) { return res.status(400).json('No Username Input'); } diff --git a/server/graphQL/resolvers/query.js b/server/graphQL/resolvers/query.js index a2041c209..3d7f6450a 100644 --- a/server/graphQL/resolvers/query.js +++ b/server/graphQL/resolvers/query.js @@ -7,7 +7,6 @@ const { Projects, Comments } = require('../../models/reactypeModels'); const Project = { getProject: async (parent, { projId }) => { const resp = await Projects.findOne({ _id: projId }); - // console.log('getProject return >>> ', resp); if (resp) { return ({ name: resp.name, @@ -29,7 +28,6 @@ const Project = { getAllProjects: async (parent, { userId }) => { let resp = await Projects.find({}); - // console.log('getAllProjects resp >>> ', resp); if (userId) { // use loosely equal for the callback because there are some discrepancy between the type of userId from the db vs from the mutation query resp = resp.filter(proj => proj.userId == userId); diff --git a/server/models/reactypeModels.js b/server/models/reactypeModels.js index 68f10fce2..b040ae431 100644 --- a/server/models/reactypeModels.js +++ b/server/models/reactypeModels.js @@ -10,7 +10,8 @@ */ const mongoose = require('mongoose'); const bcrypt = require('bcryptjs'); -const URI = process.env.NODE_ENV === 'production' ? process.env.URI : require('../../config').URI; +const mongoURI = 'mongodb+srv://Daniel:codesmith@cluster0.mm1df.mongodb.net/Cluster0?retryWrites=true&w=majority'; +const URI = process.env.NODE_ENV === 'production' ? mongoURI : 'mongodb+srv://Daniel:codesmith@cluster0.mm1df.mongodb.net/Cluster0?retryWrites=true&w=majority'; const SALT_WORK_FACTOR = 14; // connect to mongo db diff --git a/server/package.json b/server/package.json index e575dd3b3..ddd04d5a1 100644 --- a/server/package.json +++ b/server/package.json @@ -6,21 +6,18 @@ "engines": { "node": "12.17.0" }, - "scripts": { "test": "jest --verbose ", "test:watch": "jest --watch", "start": "cross-env NODE_ENV=production node server.js", - "prod": "cross-env NODE_ENV=production server.js", + "prod": "cross-env NODE_ENV=production nodemon server.js", "dev": "cross-env NODE_ENV=development nodemon server.js" }, - - "keywords": [], "author": "", "license": "ISC", "dependencies": { - "apollo-server-express": "^2.24.0", + "apollo-server-express": "^2.24.0", "bcryptjs": "^2.4.3", "body-parser": "^1.19.0", "cookie-parser": "^1.4.5", @@ -28,7 +25,7 @@ "cross-env": "^7.0.2", "dotenv": "^8.2.0", "express": "^4.17.1", - "graphql": "^15.5.0", + "graphql": "^15.5.0", "mongoose": "^5.9.22", "nodemon": "^2.0.4", "passport": "^0.4.1", diff --git a/server/server.js b/server/server.js index 73401e35e..d0d645895 100644 --- a/server/server.js +++ b/server/server.js @@ -1,6 +1,7 @@ const { ApolloServer } = require('apollo-server-express'); const express = require('express'); const cookieParser = require('cookie-parser'); +const passport = require('passport'); const path = require('path'); const cors = require('cors'); @@ -38,26 +39,26 @@ app.use( // subsequent logins seem to be working fine, however // initializes passport and passport sessions -// app.use(passport.initialize()); -// app.use(passport.session()); - -// // for Oauth which is currently not working -// app.get( -// '/github/callback', -// sessionController.gitHubResponse, -// sessionController.gitHubSendToken, -// userController.createUser, -// userController.verifyUser, -// cookieController.setSSIDCookie, -// sessionController.startSession, -// (req, res) => { -// if (isDev) { -// return res.status(200).redirect(`http://localhost:8080?=${res.locals.ssid}`); -// } else { -// return res.status(200).redirect('app://rse'); -// } -// } -// ); +app.use(passport.initialize()); +app.use(passport.session()); + +// for Oauth which is currently not working +app.get( + '/github/callback', + sessionController.gitHubResponse, + sessionController.gitHubSendToken, + userController.createUser, + userController.verifyUser, + cookieController.setSSIDCookie, + sessionController.startSession, + (req, res) => { + if (isDev) { + return res.status(200).redirect(`http://localhost:8080?=${res.locals.ssid}`); + } else { + return res.status(200).redirect(`app://rse?=${res.locals.ssid}`); + } + } +); /*