├── .DS_Store ├── .gitignore ├── LICENSE ├── README.md ├── my-app ├── .eslintrc.json ├── .gitignore ├── forge.config.ts ├── icons │ ├── icon.icns │ ├── icon.ico │ └── icon.png ├── logo.png ├── package-lock.json ├── package.json ├── postcss.config.js ├── src │ ├── Routes │ │ ├── Home.tsx │ │ └── MainPage.tsx │ ├── UserTests │ │ ├── TestBlock.cy.js │ │ └── UserTestFile.cy.js │ ├── components │ │ ├── App.tsx │ │ ├── ButtonComponent.tsx │ │ ├── DescribePage.tsx │ │ ├── DropdownButton.tsx │ │ ├── DynamicModal.tsx │ │ ├── Flow Components │ │ │ ├── CustomNode.tsx │ │ │ ├── Flow.tsx │ │ │ ├── HtmlCustomNode.tsx │ │ │ └── HtmlFlow.tsx │ │ ├── FormWrapper.tsx │ │ ├── GetFile.tsx │ │ ├── ItBlockPage.tsx │ │ ├── PreviewPopup.tsx │ │ ├── SelectAction.tsx │ │ ├── SmallerPreviewPopup.tsx │ │ ├── StatementPage.tsx │ │ ├── TestGenContainer.tsx │ │ ├── TestingUi.tsx │ │ ├── Webview.tsx │ │ └── useMultiStepForm.ts │ ├── emptyFile.cy.js │ ├── getNonce.ts │ ├── index.css │ ├── index.html │ ├── index.ts │ ├── main.tsx │ ├── options │ │ ├── actionOptions.ts │ │ ├── assertionOptions.ts │ │ ├── optionVariables.ts │ │ └── otherCommandOptions.ts │ ├── parser.ts │ ├── preload.ts │ ├── renderer.ts │ └── types │ │ ├── ImportObj.ts │ │ └── Tree.ts ├── tailwind.config.js ├── testing │ └── test2.js ├── tsconfig.json ├── webpack.main.config.ts ├── webpack.plugins.ts ├── webpack.renderer.config.ts └── webpack.rules.ts ├── package-lock.json └── package.json /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Cydekick/efd8edf339389abdc3687c350bf4aa6b704fa6a7/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Preston Mounivong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | image 3 |

4 | 5 |

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |

20 | 21 | 22 |

23 | Cydekick.com 24 |

25 | 26 | Cydekick is a Cypress test code generator designed for React applications. It enables you to visualize the component hierarchy of your React application, interact with your application in real-time, and seamlessly generate test code. 27 | 28 | ## Table of Contents 29 | - [Interface & Features](#interface--features) 30 | - [Prerequisites](#prerequisites) 31 | - [Installation](#installation) 32 | - [Usage](#usage) 33 | - [Contributing](#contributing) 34 | - [Support](#support) 35 | - [For Future Iterations](#future-iterations) 36 | - [License](#license-information) 37 | - [The Cydekick Team](#the-cydekick-team) 38 | 39 | ## Interface & Features 40 | 41 | Overview: 42 | Upon launching Cydekick, users are presented with an intuitive interface designed to simplify Cypress testing for React applications. The main features include: 43 | 44 | 1. Visual Component Hierarchy: 45 | - Navigate through the component hierarchy using React Flow, providing a visual representation of your React application's structure. 46 | 47 | 2. Highlight Components: 48 | - Click on a component to reveal its HTML structure, allowing users to inspect and interact with individual components. 49 | 50 | 3. Generate Test Code: 51 | - Seamlessly generate Cypress test code by providing text for "describe" and "it" blocks, along with selecting Cypress commands for testing. 52 | 53 | 4. Preview and Save: 54 | - Preview and save the generated test code in a separate file, giving users the flexibility to edit and download the code at their convenience. 55 | 56 | ## Prerequisites 57 | 58 | To use Cydekick, you will need to: 59 | 60 | - Install Cypress in your application. 61 | - Add your own "data-cy" IDs to elements you wish to test on your app. 62 | - Run your application concurrently. 63 | 64 | ## Installation 65 | 66 | Follow these steps to install Cydekick: 67 | 68 | 1. Download the latest version of Cydekick from [HERE](https://cydekick.dev/#home). 69 | 2. Choose the version that matches your operating system: Mac, Windows, or Linux. 70 | 3. Unzip the file onto your Computer and proceed to the next steps below. 71 | 72 | ## Usage 73 | 74 | To use Cydekick effectively, follow these steps: 75 | 76 | 1. Launch Cydekick. 77 | 78 | 2. Input the files of your React application and the URL where it's hosted. 79 | 80 |

81 | DemoGif1 82 |

83 | 84 | 3. Explore the component hierarchy using React Flow and select the component of your application that you want to test. 85 | 86 |

87 | DemoGif2 88 |

89 | 90 | 4. Generate test code by providing text for "describe" and "it" blocks, and selecting Cypress commands. 91 | 92 |

93 | DemoGif3 94 |

95 | 96 | 5. Ensure you add the statement to the editor by clicking the "end statement" button. 97 | 98 | 6. Complete the "describe" block and click "preview" at the top right to view your test file. 99 | 100 | 7. Congratulations on your first test code; You're free to edit, preview, or save the generated code. 101 | 102 |

103 | DemoGif4 104 |

105 | 106 | ## Contributing 107 | 108 | 109 | We've launched Cydekick as a valuable tool to streamline Cypress testing for users. We plan to introduce more features, extensions, and enhancements to make Cypress testing even more efficient. We appreciate any contributions from the community and encourage you to give Cydekick a try. Feel free to suggest improvements or report any issues you encounter using the application. Your interest and involvement are highly valued! 110 | 111 | ## For Future Iterations 112 | 113 | - Currently, the Cypress commands are not compatible with anonymous functions. 114 | - The commands do not support an "options" object parameter. 115 | - The commands only support strings and numbers. 116 | - There is a limited number of command options. 117 | 118 | ## Support 119 | 120 | Encounter issues or have suggestions? Please feel free to message the creators down below. 121 | 122 | ## License Information 123 | 124 | Cydekick is licensed under the MIT License. See the [LICENSE](https://github.com/oslabs-beta/Cydekick/blob/main/LICENSE) file for details. 125 | 126 | ## The Cydekick Team 127 | 128 | Developed by: 129 | 130 | - **Preston Mounivong** 131 | - LinkedIn: [Preston Mounivong](https://www.linkedin.com/in/prestonmounivong/) 132 | - GitHub: [prrrrreston](https://github.com/prrrrreston) 133 | 134 | - **Sid Saxena** 135 | - LinkedIn: [Sid Saxena](https://www.linkedin.com/in/siddhantsaxena27/) 136 | - GitHub: [sidsaxena27](https://github.com/sidsaxena27) 137 | 138 | - **Jacob Sasser** 139 | - LinkedIn: [Jacob Sasser](https://www.linkedin.com/in/jacob-sasser-11a424112/) 140 | - GitHub: [jacobsasser](https://github.com/jacobsasser) 141 | 142 | - **Quinn Craddock** 143 | - LinkedIn: [Quinn Craddock](https://www.linkedin.com/in/quinn-craddock4/) 144 | - GitHub: [quinnCraddock4](https://github.com/quinnCraddock4) 145 | 146 | -------------------------------------------------------------------------------- /my-app/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": [ 8 | "eslint:recommended", 9 | "plugin:@typescript-eslint/eslint-recommended", 10 | "plugin:@typescript-eslint/recommended", 11 | "plugin:import/recommended", 12 | "plugin:import/electron", 13 | "plugin:import/typescript" 14 | ], 15 | "parser": "@typescript-eslint/parser" 16 | } 17 | -------------------------------------------------------------------------------- /my-app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | .DS_Store 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # TypeScript cache 43 | *.tsbuildinfo 44 | 45 | # Optional npm cache directory 46 | .npm 47 | 48 | # Optional eslint cache 49 | .eslintcache 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | # Output of 'npm pack' 55 | *.tgz 56 | 57 | # Yarn Integrity file 58 | .yarn-integrity 59 | 60 | # dotenv environment variables file 61 | .env 62 | .env.test 63 | 64 | # parcel-bundler cache (https://parceljs.org/) 65 | .cache 66 | 67 | # next.js build output 68 | .next 69 | 70 | # nuxt.js build output 71 | .nuxt 72 | 73 | # vuepress build output 74 | .vuepress/dist 75 | 76 | # Serverless directories 77 | .serverless/ 78 | 79 | # FuseBox cache 80 | .fusebox/ 81 | 82 | # DynamoDB Local files 83 | .dynamodb/ 84 | 85 | # Webpack 86 | .webpack/ 87 | 88 | # Vite 89 | .vite/ 90 | 91 | # Electron-Forge 92 | out/ 93 | -------------------------------------------------------------------------------- /my-app/forge.config.ts: -------------------------------------------------------------------------------- 1 | import type { ForgeConfig } from '@electron-forge/shared-types'; 2 | import { MakerSquirrel } from '@electron-forge/maker-squirrel'; 3 | import { MakerZIP } from '@electron-forge/maker-zip'; 4 | import { MakerDeb } from '@electron-forge/maker-deb'; 5 | import { MakerRpm } from '@electron-forge/maker-rpm'; 6 | import { AutoUnpackNativesPlugin } from '@electron-forge/plugin-auto-unpack-natives'; 7 | import { WebpackPlugin } from '@electron-forge/plugin-webpack'; 8 | 9 | import { mainConfig } from './webpack.main.config'; 10 | import { rendererConfig } from './webpack.renderer.config'; 11 | 12 | const config: ForgeConfig = { 13 | packagerConfig: { 14 | asar: { 15 | unpackDir: "src/UserTests" 16 | }, 17 | icon: './icons/icon', // based on platform, will "add" .png for linux, .ico for windows, and .icns for mac 18 | }, 19 | rebuildConfig: {}, 20 | makers: [new MakerSquirrel({}), new MakerZIP({}, ['darwin']), new MakerRpm({}), new MakerDeb({})], 21 | plugins: [ 22 | new AutoUnpackNativesPlugin({}), 23 | new WebpackPlugin({ 24 | mainConfig, 25 | renderer: { 26 | config: rendererConfig, 27 | entryPoints: [ 28 | { 29 | html: './src/index.html', 30 | js: './src/renderer.ts', 31 | name: 'main_window', 32 | preload: { 33 | js: './src/preload.ts', 34 | }, 35 | }, 36 | ], 37 | }, 38 | }), 39 | ], 40 | }; 41 | 42 | export default config; 43 | -------------------------------------------------------------------------------- /my-app/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Cydekick/efd8edf339389abdc3687c350bf4aa6b704fa6a7/my-app/icons/icon.icns -------------------------------------------------------------------------------- /my-app/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Cydekick/efd8edf339389abdc3687c350bf4aa6b704fa6a7/my-app/icons/icon.ico -------------------------------------------------------------------------------- /my-app/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Cydekick/efd8edf339389abdc3687c350bf4aa6b704fa6a7/my-app/icons/icon.png -------------------------------------------------------------------------------- /my-app/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Cydekick/efd8edf339389abdc3687c350bf4aa6b704fa6a7/my-app/logo.png -------------------------------------------------------------------------------- /my-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Cydekick", 3 | "productName": "Cydekick", 4 | "version": "1.0.0", 5 | "description": "My Electron application description", 6 | "main": ".webpack/main", 7 | "scripts": { 8 | "start": "electron-forge start", 9 | "package": "electron-forge package", 10 | "make": "electron-forge make", 11 | "make:linux": "electron-forge make --platform=linux", 12 | "make:win": "electron-forge make --platform=win32", 13 | "make:mac": "electron-forge make --platform=darwin", 14 | "publish": "electron-forge publish", 15 | "lint": "eslint --ext .ts,.tsx ." 16 | }, 17 | "keywords": [], 18 | "author": { 19 | "name": "Siddhant Saxena", 20 | "email": "sidsaxena27@gmail.com" 21 | }, 22 | "license": "MIT", 23 | "devDependencies": { 24 | "@babel/core": "^7.22.20", 25 | "@babel/parser": "^7.22.16", 26 | "@babel/preset-react": "^7.22.15", 27 | "@electron-forge/cli": "^6.4.2", 28 | "@electron-forge/maker-deb": "^6.4.2", 29 | "@electron-forge/maker-rpm": "^6.4.2", 30 | "@electron-forge/maker-squirrel": "^6.4.2", 31 | "@electron-forge/maker-zip": "^6.4.2", 32 | "@electron-forge/plugin-auto-unpack-natives": "^6.4.2", 33 | "@electron-forge/plugin-webpack": "^6.4.2", 34 | "@types/react": "^18.2.21", 35 | "@types/react-dom": "^18.2.7", 36 | "@typescript-eslint/eslint-plugin": "^5.62.0", 37 | "@typescript-eslint/parser": "^5.62.0", 38 | "@vercel/webpack-asset-relocator-loader": "^1.7.3", 39 | "babel-loader": "^9.1.3", 40 | "css-loader": "^6.8.1", 41 | "electron": "26.2.1", 42 | "eslint": "^8.49.0", 43 | "eslint-plugin-import": "^2.28.1", 44 | "fork-ts-checker-webpack-plugin": "^7.3.0", 45 | "node-loader": "^2.0.0", 46 | "postcss-loader": "^7.3.3", 47 | "postcss-nesting": "^12.0.1", 48 | "style-loader": "^3.3.3", 49 | "tailwindcss": "^3.3.3", 50 | "ts-loader": "^9.4.4", 51 | "ts-node": "^10.9.1", 52 | "typescript": "~4.5.4" 53 | }, 54 | "dependencies": { 55 | "@dagrejs/dagre": "^1.0.4", 56 | "@electron/remote": "^2.0.11", 57 | "@types/node": "^20.7.0", 58 | "babel-plugin-react-css-modules": "^5.2.6", 59 | "electron-squirrel-startup": "^1.0.0", 60 | "fs": "^0.0.1-security", 61 | "monaco-editor": "^0.39.0", 62 | "monaco-editor-webpack-plugin": "^7.1.0", 63 | "path": "^0.12.7", 64 | "react": "^18.2.0", 65 | "react-dom": "^18.2.0", 66 | "react-flow-renderer": "^10.3.17", 67 | "react-monaco-editor": "^0.54.0", 68 | "react-router": "^6.16.0", 69 | "react-router-dom": "^6.16.0", 70 | "util": "^0.12.5" 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /my-app/postcss.config.js: -------------------------------------------------------------------------------- 1 | /* eslint @typescript-eslint/no-var-requires: "off" */ 2 | const tailwindcss = require('tailwindcss'); 3 | 4 | module.exports = { 5 | plugins: [ 6 | tailwindcss('./tailwind.config.js')], 7 | }; 8 | -------------------------------------------------------------------------------- /my-app/src/Routes/Home.tsx: -------------------------------------------------------------------------------- 1 | import GetFile from "../components/GetFile"; 2 | import React from "react"; 3 | import { Tree } from "../types/Tree"; 4 | 5 | /* 6 | mint greenish: #1DF28F 7 | darker green: #048C7F 8 | background color: #1B1E26 9 | 10 | */ 11 | type HomePageProps = { 12 | setUrl: React.Dispatch>; 13 | url: string; 14 | fileTree:Tree; 15 | setPageState: React.Dispatch>; 16 | setFileTree: React.Dispatch>; 17 | }; 18 | 19 | const Home = (props: HomePageProps) => { 20 | const { fileTree, setUrl, setFileTree, url, setPageState } = props; 21 | const [buttonSlide, setButtonSlide] = React.useState(false); 22 | // Button Handler to switch Routes 23 | const handleSubmission = () => { 24 | setButtonSlide(true) 25 | setTimeout(()=>{ 26 | setPageState("MainPage"); 27 | setButtonSlide(false) 28 | }, 300); 29 | } 30 | 31 | const handleChange = () => { 32 | const urlInputElement = document.getElementById( 33 | "url_form_id" 34 | ) as HTMLInputElement; 35 | if (urlInputElement) { 36 | setUrl(urlInputElement.value); 37 | } 38 | } 39 | 40 | return ( 41 |
42 |
43 |
44 | 45 | 46 | 47 |
48 | 55 | 68 |
69 | 70 |
71 |
72 | ); 73 | }; 74 | 75 | export default Home; 76 | -------------------------------------------------------------------------------- /my-app/src/Routes/MainPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Webview from '../components/Webview'; 3 | import Flow from '../components/Flow Components/Flow'; 4 | import ButtonComponent from '../components/ButtonComponent'; 5 | import TestGenContainer from '../components/TestGenContainer'; 6 | import { Tree as TreeType } from '../types/Tree'; 7 | import HtmlFlow from '../components/Flow Components/HtmlFlow'; 8 | 9 | type MainPageProps = { 10 | url: string; 11 | fileTree: TreeType; 12 | setPageState: React.Dispatch>; 13 | }; 14 | 15 | const MainPage = (props: MainPageProps) => { 16 | const { url, fileTree, setPageState } = props; 17 | const [currentComponent, setCurrentComponent] = React.useState({ 18 | id: '', 19 | name: '', 20 | fileName: '', 21 | filePath: '', 22 | importPath: '', 23 | expanded: false, 24 | depth: 0, 25 | count: 0, 26 | thirdParty: false, 27 | reactRouter: false, 28 | reduxConnect: false, 29 | children: [], 30 | htmlChildrenTestIds: {}, 31 | parentList: [], 32 | props: {}, 33 | error: '', 34 | }); 35 | const [currentHTML, setCurrentHTML] = React.useState(''); 36 | const [currentTestId, setCurrentTestId] = React.useState(''); 37 | const [data, setData] = React.useState(''); 38 | const [onComponentFlow, setOnComponentFlow] = React.useState(true); 39 | 40 | React.useEffect(() => console.log(currentTestId), [currentTestId]); 41 | // Route Handling between pages 42 | const handleBack = () => { 43 | setPageState('Home'); 44 | }; 45 | const flowToggle = () => { 46 | if (data) setOnComponentFlow(!onComponentFlow); 47 | }; 48 | const handleReload = () => { 49 | const webview = document.getElementById( 50 | "webview" 51 | ) as Electron.WebviewTag | null; 52 | webview.loadURL(url); 53 | }; 54 | 55 | return ( 56 |
57 | 75 |
76 | 83 | 92 | 98 |
99 |
100 | 105 |
106 |
107 | ); 108 | }; 109 | 110 | export default MainPage; 111 | -------------------------------------------------------------------------------- /my-app/src/UserTests/TestBlock.cy.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/Cydekick/efd8edf339389abdc3687c350bf4aa6b704fa6a7/my-app/src/UserTests/TestBlock.cy.js -------------------------------------------------------------------------------- /my-app/src/UserTests/UserTestFile.cy.js: -------------------------------------------------------------------------------- 1 | describe('fd', () => { 2 | it('sd', () => { 3 | .clear()}) 4 | it('sdf', () => { 5 | }) 6 | }) -------------------------------------------------------------------------------- /my-app/src/components/App.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Home from "../Routes/Home"; 3 | import MainPage from "../Routes/MainPage"; 4 | import {Tree} from "../types/Tree" 5 | 6 | const App = () => { 7 | const [url, setUrl] = React.useState('http://localhost:8080/'); 8 | const [fileTree, setFileTree] = React.useState({ 9 | id: '', 10 | name: '', 11 | fileName: '', 12 | filePath: '', 13 | importPath: '', 14 | expanded: false, 15 | depth: 0, 16 | count: 0, 17 | thirdParty: false, 18 | reactRouter: false, 19 | reduxConnect: false, 20 | children: [], 21 | htmlChildrenTestIds: {}, 22 | parentList: [], 23 | props: {}, 24 | error: '', 25 | }); 26 | const [pageState, setPageState] = React.useState("Home"); 27 | 28 | return pageState === "Home" ? ( 29 | 36 | ) : ( 37 | 38 | ); 39 | }; 40 | 41 | export default App; 42 | -------------------------------------------------------------------------------- /my-app/src/components/ButtonComponent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PreviewPopup from './PreviewPopup'; 3 | import path from 'path'; 4 | import os from 'os'; 5 | import fs from 'fs'; 6 | 7 | const { ipcRenderer } = window.require('electron'); 8 | 9 | 10 | const ButtonComponent = () => { 11 | const [open, setOpen] = React.useState(false); 12 | 13 | const handleOpen = () => { 14 | setOpen(true); 15 | }; 16 | 17 | const handleClose = () => { 18 | setOpen(false); 19 | }; 20 | 21 | // const handleSaveFile = async () => { 22 | // const tempDirPath = path.join(os.tmpdir(), 'UserTests', 'UserTestFile.cy.js'); 23 | // const content = fs.readFileSync(tempDirPath, 'utf-8'); 24 | 25 | // // const { dialog } = window.require('@electron/remote'); 26 | // const filePath = await dialog.showSaveDialog({ 27 | // title: 'Save your file', 28 | // defaultPath: 'UserTestFile.cy.js', 29 | // filters: [{ name: 'JavaScript', extensions: ['js'] }], 30 | // }); 31 | 32 | // if (filePath) { 33 | // fs.writeFileSync(filePath, content); 34 | // } 35 | // }; 36 | const handleSaveFile = () => { 37 | const tempDirPath = path.join(os.tmpdir(), 'UserTests', 'UserTestFile.cy.js'); 38 | const content = fs.readFileSync(tempDirPath, 'utf-8'); 39 | 40 | ipcRenderer.invoke('save-file', content); 41 | }; 42 | 43 | 44 | return ( 45 |
46 | 51 | 52 | 55 | {open && } 56 |
57 | ); 58 | }; 59 | 60 | export default ButtonComponent; -------------------------------------------------------------------------------- /my-app/src/components/DescribePage.tsx: -------------------------------------------------------------------------------- 1 | import SmallerPreviewPopup from './SmallerPreviewPopup' 2 | import React from 'react'; 3 | const fs = window.require('fs'); 4 | const os = window.require('os'); 5 | 6 | const path = window.require('path') 7 | 8 | type DescribePageProps = { 9 | setCurrentPageNum: React.Dispatch> 10 | } 11 | 12 | function DescribePage({ setCurrentPageNum }: DescribePageProps) { 13 | 14 | //States 15 | const [code, setCode] = React.useState(''); // initial string that is displayed on the editor 16 | 17 | //text that is rendered whenever the describe page is mounted 18 | React.useEffect(() => { 19 | const fileContent = `'Welcome to Cydekick!' \n'Enter text for your describe block!'` 20 | setCode(fileContent); 21 | }, []); 22 | 23 | //Describe block function that appends our describe block to the monaco editor 24 | function createDescribeBlock(): void { 25 | const describeText = (document.getElementById('describeText') as HTMLInputElement).value 26 | const testFileContent = describeBlock(describeText); 27 | // const filePath = path.join(__dirname, 'src', 'UserTests', 'TestBlock.cy.js'); 28 | const filePath = path.join(os.tmpdir(), 'UserTests', 'TestBlock.cy.js'); 29 | 30 | 31 | fs.writeFileSync(filePath, testFileContent); 32 | setCurrentPageNum(1) 33 | } 34 | 35 | //describe block that returns a describe string 36 | function describeBlock(string: string): string { 37 | return `describe('${string}', () => {`; 38 | } 39 | 40 | 41 | return ( 42 |
43 |
46 |

Name for describe block:

47 | 52 | 57 |
58 |
59 | 60 |
61 |
62 | ); 63 | } 64 | 65 | export default DescribePage 66 | 67 | -------------------------------------------------------------------------------- /my-app/src/components/DropdownButton.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import DynamicModal from './DynamicModal'; 3 | 4 | interface Modal { 5 | type: string; 6 | labelText?: string; 7 | inputType?: string; 8 | options?: string[] 9 | } 10 | 11 | interface OptionDetails { 12 | option: string; 13 | code: string; 14 | tooltip: string; 15 | modal?: Modal[]; 16 | modalCreateCode?: (text:string[]) => string; 17 | } 18 | 19 | interface Props { 20 | label: string; 21 | options: Record; 22 | onClickOption: (code: string, details: OptionDetails) => void; 23 | dropDown: string; 24 | setDropDown: (param:string)=>void 25 | } 26 | 27 | const DropdownButton: React.FC = ({ dropDown, setDropDown, label, onClickOption, options }) => { 28 | const [isOpen, setIsOpen] = useState(false); 29 | 30 | // array to keep track of state 31 | const [modalOpenStates, setModalOpenStates] = useState( 32 | Object.keys(options).map(() => false), 33 | ); 34 | 35 | function onButtonClick(index: number, optionDetails: OptionDetails) { 36 | if (!optionDetails.modal) { 37 | onClickOption(optionDetails.code, optionDetails); 38 | } else { 39 | // Open the modal for the selected option by updating its state 40 | const updatedModalStates = [...modalOpenStates]; 41 | updatedModalStates[index] = true; 42 | setModalOpenStates(updatedModalStates); 43 | } 44 | } 45 | 46 | function closeModal(index: number) { 47 | // Close the modal for the specified option by updating its state 48 | const updatedModalStates = [...modalOpenStates]; 49 | updatedModalStates[index] = false; 50 | setModalOpenStates(updatedModalStates); 51 | } 52 | React.useEffect(() =>{ 53 | if (label !== dropDown){ 54 | setIsOpen(false) 55 | } 56 | }, [dropDown]) 57 | return ( 58 |
59 |
60 | {' '} 61 | {/* Adjust spacing between parent buttons */} 62 | 69 | {/* Add other parent buttons here */} 70 |
71 | {isOpen && ( 72 |
73 | {Object.values(options).map((optionDetails, index) => ( 74 |
75 | 83 | closeModal(index)} 87 | onClickOption={onClickOption} 88 | /> 89 |
90 | ))} 91 |
92 | )} 93 |
94 | ); 95 | }; 96 | 97 | export default DropdownButton; 98 | -------------------------------------------------------------------------------- /my-app/src/components/DynamicModal.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface Modal { 4 | type: string; 5 | labelText?: string; 6 | inputType?: string; 7 | options?: string[] 8 | } 9 | 10 | 11 | interface OptionDetails { 12 | option: string; 13 | code: string; 14 | tooltip: string; 15 | modal?: Modal[]; 16 | modalCreateCode?: (text:string[]) => string; 17 | } 18 | 19 | type DynamicModalType = { 20 | infoObj:OptionDetails; 21 | isOpen:boolean; 22 | setIsOpen:React.Dispatch>; 23 | onClickOption:(code: string, details: OptionDetails) => void; 24 | } 25 | 26 | function DynamicModal({ infoObj, isOpen, setIsOpen, onClickOption }:DynamicModalType) { 27 | function createCode(): void { 28 | const modalForm = document.getElementById('modalForm') as HTMLFormElement | null; 29 | if (modalForm){ 30 | 31 | const modalElements = modalForm.elements as HTMLFormControlsCollection; 32 | const arrayOfEleVal: any[] = []; 33 | for (const ele of modalElements) { 34 | if (ele instanceof HTMLTextAreaElement || ele instanceof HTMLSelectElement) arrayOfEleVal.push(ele.value); 35 | } 36 | onClickOption(infoObj.modalCreateCode(arrayOfEleVal), infoObj); 37 | setIsOpen(false); 38 | } 39 | } 40 | 41 | function createLabel(labelText: string) { 42 | return ; 43 | } 44 | 45 | function createInput(inputType:string) { 46 | return