├── .DS_Store ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .vscode └── settings.json ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── assets ├── .DS_Store ├── img │ ├── eeql.png │ ├── eeql_logo.png │ └── eeql_logo_copy.png └── style │ ├── colors.scss │ ├── fonts.scss │ └── global.scss ├── babel.config.js ├── dist ├── main.js └── main.js.map ├── package-lock.json ├── package.json ├── public └── electron.js ├── src ├── App.tsx ├── components │ ├── CodeEditor │ │ ├── CodeEditor.scss │ │ ├── CodeEditor.tsx │ │ ├── options.ts │ │ └── start.js │ ├── FileTree │ │ ├── FileTree.scss │ │ └── FileTree.tsx │ ├── FileUpdate │ │ ├── FileUpdate.scss │ │ └── FileUpdate.tsx │ ├── FileUpload │ │ ├── FileUpload.scss │ │ └── FileUpload.tsx │ ├── NavBar │ │ ├── NavBar.scss │ │ └── NavBar.tsx │ ├── PortUpdate │ │ ├── PortUpdate.scss │ │ └── PortUpdate.tsx │ └── TestSuite │ │ ├── GraphQLEndpoint │ │ ├── GraphQLEndpoint.scss │ │ ├── GraphQLEndpoint.tsx │ │ └── MockSchema.js │ │ ├── RestEndpoint │ │ ├── RestEndpoint.scss │ │ └── RestEndpoint.tsx │ │ ├── Supertest.js │ │ ├── TestBuilder │ │ ├── TestBuilder.scss │ │ └── TestBuilder.tsx │ │ ├── TestGeneration │ │ ├── RestTestCreation.js │ │ └── graphQLTestCreation.js │ │ └── server │ │ ├── db │ │ ├── markets.dev.json │ │ ├── markets.js │ │ └── markets.test.json │ │ └── index.js ├── index.html ├── index.js ├── pages │ ├── Home │ │ ├── Home.scss │ │ └── Home.tsx │ ├── Landing │ │ ├── Landing.scss │ │ └── Landing.tsx │ └── Visualize │ │ ├── Visualize.scss │ │ └── Visualize.tsx └── provider │ ├── DataProvider.tsx │ ├── StateProvider.tsx │ └── TestProvider.tsx ├── tsconfig.json ├── webpack.electron.config.js └── webpack.react.config.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/eeQL/efa2296b48eff63e7b0258658ec1a42fb5a7ee36/.DS_Store -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib" 3 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | contact@eeql.io. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 OSLabs Beta 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 | # eeQL 2 | eeql_github copy 3 | 4 | _"EEQL is a standalone test creation suite that constructs reliable endpoint tests for both GraphQL and RESTful API's."_. 5 | 6 |
7 | 8 | ## Installation 9 | 10 | Please dive into [eeQL.io](https://www.eeql.io/) to install this application. 11 | 12 | Refer to our [Builds/Resources](https://github.com/oslabs-beta/eeQL/releases) for more information on compatability and previous releases. 13 | 14 |
15 | 16 | ## Usage 17 | ### GETTING STARTED 18 | 19 |
20 | 21 |
22 | 23 | - Input desired port for proper integration into your project 24 | - Upload a project directory (note: a testing folder will be created if none exists). 25 | - eeQL will automatically create, display and locate testing files within your uploaded project to facilitate reliable test creation. 26 | 27 |
28 | 29 | ### TEST CREATION 30 | 31 | REST 32 |
33 | 34 |
35 | - Select the REST button 36 | - Select the request method that you plan on testing 37 | - Upload the server file of your project 38 | - Fill out the requested fields within the _Test Builder Module_ 39 | - Press the save button to automatically create a Jest-and-Supertest-based REST test ! 40 | 41 | 42 | GRAPHQL 43 |
44 | 45 |
46 | - Select the GraphQL button 47 | - Upload the schema file of your project 48 | - Upload the resolvers file of your project 49 | - Provide a description of the test 50 | - Determine if the operation is a valid or invalid one 51 | - Provide the operation (query/mutation) text 52 | - Determine if the operation is a query or a mutation 53 | - Optionally, provide a mutation object if the operation is a mutation instead of a query 54 | - Press the save button to generate an easyGraphQL-based operation test! 55 | 56 | 57 |
58 | 59 | ## Contributing 60 | Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. 61 | Please make sure to update tests as appropriate. 62 | 63 |
64 | 65 | ## Contact and Inquiries 66 | [Contact us](contact@eeql.io) for all other issues/inquiries. If you'd like to follow up on updates with eeQL, please check us out [here](https://www.linkedin.com/company/eeql/about/)! 67 | 68 |
69 | 70 | ## Developers 71 | [Abu Fofanah](https://www.linkedin.com/in/abu-fofanah/) | 72 | [Jason Speare](https://www.linkedin.com/in/jason-speare/) | 73 | [Kimberley Spicer]( https://www.linkedin.com/in/kimberleyspicer/) | 74 | [Ramtin Khoee](https://www.linkedin.com/in/ramtinkhoee/) | 75 |
76 | 77 | ## License 78 | This project is licensed under the [MIT license](https://choosealicense.com/licenses/mit/). 79 | 80 | 81 | -------------------------------------------------------------------------------- /assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/eeQL/efa2296b48eff63e7b0258658ec1a42fb5a7ee36/assets/.DS_Store -------------------------------------------------------------------------------- /assets/img/eeql.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/eeQL/efa2296b48eff63e7b0258658ec1a42fb5a7ee36/assets/img/eeql.png -------------------------------------------------------------------------------- /assets/img/eeql_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/eeQL/efa2296b48eff63e7b0258658ec1a42fb5a7ee36/assets/img/eeql_logo.png -------------------------------------------------------------------------------- /assets/img/eeql_logo_copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oslabs-beta/eeQL/efa2296b48eff63e7b0258658ec1a42fb5a7ee36/assets/img/eeql_logo_copy.png -------------------------------------------------------------------------------- /assets/style/colors.scss: -------------------------------------------------------------------------------- 1 | $eeql-gray: #c2c2c2; 2 | $eeql-white: #fcfbf6; 3 | 4 | -------------------------------------------------------------------------------- /assets/style/fonts.scss: -------------------------------------------------------------------------------- 1 | @import url(https://fonts.googleapis.com/css2?family=Lora:wght@600&family=Raleway:wght@500&family=Montserrat&display=swap); 2 | 3 | $lora: 'Lora', serif; 4 | $raleway: 'Raleway', sans-serif; 5 | $montserrat: 'Montserrat', sans-serif; 6 | 7 | $eeql-font: Helvetica, sans-serif; -------------------------------------------------------------------------------- /assets/style/global.scss: -------------------------------------------------------------------------------- 1 | #global { 2 | background-color: #1e1e21; 3 | } 4 | 5 | body { 6 | background-color: #1e1e21; 7 | } 8 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // extending babel - plugins for transpiling code 3 | presets: [ 4 | "@babel/preset-env", 5 | "@babel/preset-react", 6 | "@babel/preset-typescript", 7 | ], 8 | }; 9 | -------------------------------------------------------------------------------- /dist/main.js: -------------------------------------------------------------------------------- 1 | /******/ (function(modules) { // webpackBootstrap 2 | /******/ // The module cache 3 | /******/ var installedModules = {}; 4 | /******/ 5 | /******/ // The require function 6 | /******/ function __webpack_require__(moduleId) { 7 | /******/ 8 | /******/ // Check if module is in cache 9 | /******/ if(installedModules[moduleId]) { 10 | /******/ return installedModules[moduleId].exports; 11 | /******/ } 12 | /******/ // Create a new module (and put it into the cache) 13 | /******/ var module = installedModules[moduleId] = { 14 | /******/ i: moduleId, 15 | /******/ l: false, 16 | /******/ exports: {} 17 | /******/ }; 18 | /******/ 19 | /******/ // Execute the module function 20 | /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 21 | /******/ 22 | /******/ // Flag the module as loaded 23 | /******/ module.l = true; 24 | /******/ 25 | /******/ // Return the exports of the module 26 | /******/ return module.exports; 27 | /******/ } 28 | /******/ 29 | /******/ 30 | /******/ // expose the modules object (__webpack_modules__) 31 | /******/ __webpack_require__.m = modules; 32 | /******/ 33 | /******/ // expose the module cache 34 | /******/ __webpack_require__.c = installedModules; 35 | /******/ 36 | /******/ // define getter function for harmony exports 37 | /******/ __webpack_require__.d = function(exports, name, getter) { 38 | /******/ if(!__webpack_require__.o(exports, name)) { 39 | /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 40 | /******/ } 41 | /******/ }; 42 | /******/ 43 | /******/ // define __esModule on exports 44 | /******/ __webpack_require__.r = function(exports) { 45 | /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 46 | /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 47 | /******/ } 48 | /******/ Object.defineProperty(exports, '__esModule', { value: true }); 49 | /******/ }; 50 | /******/ 51 | /******/ // create a fake namespace object 52 | /******/ // mode & 1: value is a module id, require it 53 | /******/ // mode & 2: merge all properties of value into the ns 54 | /******/ // mode & 4: return value when already ns object 55 | /******/ // mode & 8|1: behave like require 56 | /******/ __webpack_require__.t = function(value, mode) { 57 | /******/ if(mode & 1) value = __webpack_require__(value); 58 | /******/ if(mode & 8) return value; 59 | /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 60 | /******/ var ns = Object.create(null); 61 | /******/ __webpack_require__.r(ns); 62 | /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 63 | /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 64 | /******/ return ns; 65 | /******/ }; 66 | /******/ 67 | /******/ // getDefaultExport function for compatibility with non-harmony modules 68 | /******/ __webpack_require__.n = function(module) { 69 | /******/ var getter = module && module.__esModule ? 70 | /******/ function getDefault() { return module['default']; } : 71 | /******/ function getModuleExports() { return module; }; 72 | /******/ __webpack_require__.d(getter, 'a', getter); 73 | /******/ return getter; 74 | /******/ }; 75 | /******/ 76 | /******/ // Object.prototype.hasOwnProperty.call 77 | /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 78 | /******/ 79 | /******/ // __webpack_public_path__ 80 | /******/ __webpack_require__.p = ""; 81 | /******/ 82 | /******/ 83 | /******/ // Load entry module and return exports 84 | /******/ return __webpack_require__(__webpack_require__.s = "./public/electron.js"); 85 | /******/ }) 86 | /************************************************************************/ 87 | /******/ ({ 88 | 89 | /***/ "./public/electron.js": 90 | /*!****************************!*\ 91 | !*** ./public/electron.js ***! 92 | \****************************/ 93 | /*! no exports provided */ 94 | /***/ (function(module, __webpack_exports__, __webpack_require__) { 95 | 96 | "use strict"; 97 | __webpack_require__.r(__webpack_exports__); 98 | /* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! electron */ "electron"); 99 | /* harmony import */ var electron__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(electron__WEBPACK_IMPORTED_MODULE_0__); 100 | /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! path */ "path"); 101 | /* harmony import */ var path__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_1__); 102 | /* harmony import */ var url__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! url */ "url"); 103 | /* harmony import */ var url__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(url__WEBPACK_IMPORTED_MODULE_2__); 104 | 105 | 106 | // currentWindow represents our current rendered page in electron app 107 | 108 | var currentWindow; // create a function that opens a create a new window 109 | 110 | function newWindow() { 111 | currentWindow = new electron__WEBPACK_IMPORTED_MODULE_0__["BrowserWindow"]({ 112 | title: "eeQL", 113 | // to be determined 114 | icon: null, 115 | // eeql-blue 116 | backgroundColor: "##1c90f5", 117 | // starting dimensions of new window 118 | height: 800, 119 | width: 800, 120 | // allow integration of node modules in build. 121 | webPreferences: { 122 | nodeIntegration: true 123 | } 124 | }); // check if dist folder exists by running a test on the node environmnet 125 | // if in development mode, serve the local server to electron 126 | 127 | if (true) { 128 | currentWindow.loadURL("http://localhost:4000"); 129 | currentWindow.webContents.openDevTools(); 130 | } // otherwise, serve the compile dist folder for render 131 | else {} // set window to null after app is closed. 132 | // create IPC event listener on browserRouter 133 | 134 | 135 | currentWindow.on("closed", function () { 136 | currentWindow = null; 137 | }); 138 | } // after IPC event listener is triggered "ready" 139 | // invoke our create new window by invoking "newWindow()" 140 | 141 | 142 | electron__WEBPACK_IMPORTED_MODULE_0__["app"].on("ready", newWindow); // electron, by default, refreshes every navigation 143 | // since we're using node modules outside of our dist folder, we have to disable auto refresh 144 | 145 | electron__WEBPACK_IMPORTED_MODULE_0__["app"].allowRendererProcessReuse = true; 146 | 147 | /***/ }), 148 | 149 | /***/ "electron": 150 | /*!***************************!*\ 151 | !*** external "electron" ***! 152 | \***************************/ 153 | /*! no static exports found */ 154 | /***/ (function(module, exports) { 155 | 156 | module.exports = require("electron"); 157 | 158 | /***/ }), 159 | 160 | /***/ "path": 161 | /*!***********************!*\ 162 | !*** external "path" ***! 163 | \***********************/ 164 | /*! no static exports found */ 165 | /***/ (function(module, exports) { 166 | 167 | module.exports = require("path"); 168 | 169 | /***/ }), 170 | 171 | /***/ "url": 172 | /*!**********************!*\ 173 | !*** external "url" ***! 174 | \**********************/ 175 | /*! no static exports found */ 176 | /***/ (function(module, exports) { 177 | 178 | module.exports = require("url"); 179 | 180 | /***/ }) 181 | 182 | /******/ }); 183 | //# sourceMappingURL=main.js.map -------------------------------------------------------------------------------- /dist/main.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["webpack:///webpack/bootstrap","webpack:///./public/electron.js","webpack:///external \"electron\"","webpack:///external \"path\"","webpack:///external \"url\""],"names":["currentWindow","newWindow","BrowserWindow","title","icon","backgroundColor","height","width","webPreferences","nodeIntegration","process","loadURL","webContents","openDevTools","on","app","allowRendererProcessReuse"],"mappings":";QAAA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;;QAEA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;;;QAGA;QACA;;QAEA;QACA;;QAEA;QACA;QACA;QACA,0CAA0C,gCAAgC;QAC1E;QACA;;QAEA;QACA;QACA;QACA,wDAAwD,kBAAkB;QAC1E;QACA,iDAAiD,cAAc;QAC/D;;QAEA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA,yCAAyC,iCAAiC;QAC1E,gHAAgH,mBAAmB,EAAE;QACrI;QACA;;QAEA;QACA;QACA;QACA,2BAA2B,0BAA0B,EAAE;QACvD,iCAAiC,eAAe;QAChD;QACA;QACA;;QAEA;QACA,sDAAsD,+DAA+D;;QAErH;QACA;;;QAGA;QACA;;;;;;;;;;;;;AClFA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;CAGA;;AACA,IAAIA,aAAJ,C,CAEA;;AACA,SAASC,SAAT,GAAqB;AACnBD,eAAa,GAAG,IAAIE,sDAAJ,CAAkB;AAChCC,SAAK,EAAE,MADyB;AAEhC;AACAC,QAAI,EAAE,IAH0B;AAIhC;AACAC,mBAAe,EAAE,UALe;AAMhC;AACAC,UAAM,EAAE,GAPwB;AAQhCC,SAAK,EAAE,GARyB;AAShC;AACAC,kBAAc,EAAE;AAAEC,qBAAe,EAAE;AAAnB;AAVgB,GAAlB,CAAhB,CADmB,CAcnB;AACA;;AACA,MAAIC,IAAJ,EAA4C;AAC1CV,iBAAa,CAACW,OAAd,CAAsB,uBAAtB;AACAX,iBAAa,CAACY,WAAd,CAA0BC,YAA1B;AACD,GAHD,CAIA;AAJA,OAKK,EArBc,CAmCnB;AACA;;;AACAb,eAAa,CAACc,EAAd,CAAiB,QAAjB,EAA2B,YAAM;AAC/Bd,iBAAa,GAAG,IAAhB;AACD,GAFD;AAGD,C,CACD;AACA;;;AACAe,4CAAG,CAACD,EAAJ,CAAO,OAAP,EAAgBb,SAAhB,E,CACA;AACA;;AACAc,4CAAG,CAACC,yBAAJ,GAAgC,IAAhC,C;;;;;;;;;;;ACtDA,qC;;;;;;;;;;;ACAA,iC;;;;;;;;;;;ACAA,gC","file":"main.js","sourcesContent":[" \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId]) {\n \t\t\treturn installedModules[moduleId].exports;\n \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.l = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./public/electron.js\");\n","import { app, BrowserWindow } from \"electron\";\nimport * as path from \"path\";\nimport * as url from \"url\";\n\n// currentWindow represents our current rendered page in electron app\nlet currentWindow;\n\n// create a function that opens a create a new window\nfunction newWindow() {\n currentWindow = new BrowserWindow({\n title: \"eeQL\",\n // to be determined\n icon: null,\n // eeql-blue\n backgroundColor: \"##1c90f5\",\n // starting dimensions of new window\n height: 800,\n width: 800,\n // allow integration of node modules in build.\n webPreferences: { nodeIntegration: true },\n });\n\n // check if dist folder exists by running a test on the node environmnet\n // if in development mode, serve the local server to electron\n if (process.env.NODE_ENV === \"development\") {\n currentWindow.loadURL(\"http://localhost:4000\");\n currentWindow.webContents.openDevTools();\n }\n // otherwise, serve the compile dist folder for render\n else {\n currentWindow.loadURL(\n // tried implementing WhatWG URL, it's current non-functional, see dicussion reference for more details.\n // https://github.com/nodejs/node/issues/25099\n url.format({\n // allow electron to render a file (html), in our dist folder\n pathname: path.resolve(__dirname, \"../dist/index.html\"),\n // set type\n protocol: \"file:\",\n // allow for propper formating of directory name.\n slashes: true,\n })\n );\n }\n // set window to null after app is closed.\n // create IPC event listener on browserRouter\n currentWindow.on(\"closed\", () => {\n currentWindow = null;\n });\n}\n// after IPC event listener is triggered \"ready\"\n// invoke our create new window by invoking \"newWindow()\"\napp.on(\"ready\", newWindow);\n// electron, by default, refreshes every navigation\n// since we're using node modules outside of our dist folder, we have to disable auto refresh\napp.allowRendererProcessReuse = true;\n","module.exports = require(\"electron\");","module.exports = require(\"path\");","module.exports = require(\"url\");"],"sourceRoot":""} -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eeql", 3 | "version": "1.0.0", 4 | "description": "A tool to help test, visualize and optimize your data.", 5 | "main": "dist/main.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/oslabs-beta/eeQL.git" 9 | }, 10 | "keywords": [ 11 | "testing", 12 | "electron", 13 | "graphQL" 14 | ], 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/oslabs-beta/eeQL/issues" 18 | }, 19 | "homepage": "https://github.com/oslabs-beta/eeQL#readme", 20 | "author": "eeQL", 21 | "contributors": [ 22 | "Ramtin Khoee", 23 | "Kimberley Spicer", 24 | "Abu Fofanah", 25 | "Jason Speare" 26 | ], 27 | "scripts": { 28 | "prod": "npm run p-react && npm run p-electron", 29 | "p-react": "cross-env NODE_ENV=production webpack --config webpack.react.config.js --mode production", 30 | "p-electron": "cross-env NODE_ENV=production webpack --config webpack.electron.config.js --mode production && electron .", 31 | "dev": " concurrently \"npm run d-electron\" \"npm run d-react\"", 32 | "d-react": "cross-env NODE_ENV=development webpack-dev-server --config webpack.react.config.js --mode development", 33 | "d-electron": "cross-env NODE_ENV=development concurrently \"webpack --config webpack.electron.config.js --mode development\" \"electron .\"", 34 | "e-build": "electron-builder", 35 | "test": "jest --verbose" 36 | }, 37 | "jest": { 38 | "globalSetup": "./jest-setup.js", 39 | "globalTeardown": "./jest-teardown.js" 40 | }, 41 | "build": { 42 | "directories": { 43 | "output": "build", 44 | "buildResources": "assets" 45 | }, 46 | "extends": null, 47 | "files": [ 48 | "dist/**/*", 49 | "node_modules/**/*" 50 | ], 51 | "productName": "eeQL", 52 | "appId": "com.electron.pinocchio", 53 | "mac": { 54 | "category": "public.app-category.developer-tools" 55 | }, 56 | "dmg": { 57 | "icon": null, 58 | "contents": [ 59 | { 60 | "x": 100, 61 | "y": 150 62 | }, 63 | { 64 | "x": 300, 65 | "y": 150, 66 | "type": "link", 67 | "path": "/Applications" 68 | } 69 | ] 70 | }, 71 | "win": { 72 | "target": [ 73 | "nsis" 74 | ], 75 | "icon": null 76 | }, 77 | "nsis": { 78 | "oneClick": false, 79 | "allowToChangeInstallationDirectory": true 80 | }, 81 | "linux": { 82 | "target": [ 83 | "AppImage", 84 | "snap" 85 | ] 86 | } 87 | }, 88 | "devDependencies": { 89 | "@babel/core": "^7.9.0", 90 | "@babel/plugin-proposal-decorators": "^7.12.12", 91 | "@babel/preset-env": "^7.9.5", 92 | "@babel/preset-react": "^7.9.4", 93 | "@babel/preset-typescript": "^7.9.0", 94 | "@types/jest": "^26.0.20", 95 | "@types/react": "^16.9.34", 96 | "@types/react-dom": "^16.9.6", 97 | "@types/react-router-dom": "^5.1.7", 98 | "@typescript-eslint/eslint-plugin": "^4.13.0", 99 | "@typescript-eslint/parser": "^4.13.0", 100 | "babel-loader": "^8.1.0", 101 | "css-loader": "^5.0.1", 102 | "electron": "^8.2.2", 103 | "electron-builder": "^22.7.0", 104 | "electron-devtools-installer": "^3.1.1", 105 | "electron-is-dev": "^1.2.0", 106 | "electron-webpack": "^2.8.2", 107 | "eslint": "^7.17.0", 108 | "eslint-config-airbnb": "^18.2.1", 109 | "eslint-plugin-import": "^2.22.1", 110 | "eslint-plugin-jsx-a11y": "^6.4.1", 111 | "eslint-plugin-react": "^7.22.0", 112 | "eslint-plugin-react-hooks": "^4.2.0", 113 | "file-loader": "^6.2.0", 114 | "html-loader": "^1.3.2", 115 | "html-webpack-plugin": "^4.2.0", 116 | "node-sass": "^5.0.0", 117 | "sass": "^1.32.5", 118 | "sass-loader": "^10.1.1", 119 | "style-loader": "^2.0.0", 120 | "svg-url-loader": "^7.1.1", 121 | "ts-jest": "^26.5.3", 122 | "ts-loader": "^8.0.14", 123 | "typescript": "^3.7.2", 124 | "url-loader": "^4.1.1", 125 | "webpack": "^4.46.0", 126 | "webpack-cli": "^3.3.11", 127 | "webpack-dev-server": "^3.10.3" 128 | }, 129 | "dependencies": { 130 | "@apollo/client": "^3.3.11", 131 | "@devexpress/dx-react-core": "2.7.5", 132 | "@devexpress/dx-react-grid": "2.7.5", 133 | "@devexpress/dx-react-grid-material-ui": "2.7.5", 134 | "@material-ui/core": "^4.11.3", 135 | "@material-ui/icons": "^4.11.2", 136 | "bluebird": "^3.7.2", 137 | "bootstrap": "^5.0.0-beta2", 138 | "concurrently": "^5.3.0", 139 | "cross-env": "^7.0.3", 140 | "d3": "^5.16.0", 141 | "d3-scale": "^3.2.3", 142 | "graphiql-plus": "^1.0.1", 143 | "graphql": "^15.5.0", 144 | "graphql-matcha": "0.0.6", 145 | "graphql-tools": "^7.0.4", 146 | "monaco-editor": "^0.21.2", "monaco-editor-electron": "^1.0.6", 147 | "monaco-editor-webpack-plugin": "^2.1.0", 148 | "react": "^16.13.1", 149 | "react-awesome-button": "^6.5.1", 150 | "react-d3-graph": "^2.6.0", 151 | "react-dom": "^16.13.1", 152 | "react-grid-layout": "^1.2.2", 153 | "react-monaco-editor": "^0.41.2", 154 | "react-resizable": "^1.11.1", 155 | "react-router-dom": "^5.2.0", 156 | "styled-components": "^5.2.1" 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /public/electron.js: -------------------------------------------------------------------------------- 1 | import { app, BrowserWindow } from "electron"; 2 | import * as path from "path"; 3 | import * as url from "url"; 4 | 5 | // currentWindow represents our current rendered page in electron app 6 | let currentWindow; 7 | 8 | // create a function that opens a create a new window 9 | function newWindow() { 10 | currentWindow = new BrowserWindow({ 11 | title: "eeQL", 12 | // to be determined 13 | icon: null, 14 | // eeql-blue 15 | backgroundColor: "##1c90f5", 16 | // starting dimensions of new window 17 | height: 800, 18 | width: 800, 19 | // allow integration of node modules in build. 20 | webPreferences: { nodeIntegration: true }, 21 | }); 22 | 23 | // check if dist folder exists by running a test on the node environmnet 24 | // if in development mode, serve the local server to electron 25 | if (process.env.NODE_ENV === "development") { 26 | currentWindow.loadURL("http://localhost:4000"); 27 | currentWindow.webContents.openDevTools(); 28 | } 29 | // otherwise, serve the compile dist folder for render 30 | else { 31 | currentWindow.loadURL( 32 | // tried implementing WhatWG URL, it's current non-functional, see dicussion reference for more details. 33 | // https://github.com/nodejs/node/issues/25099 34 | url.format({ 35 | // allow electron to render a file (html), in our dist folder 36 | pathname: path.resolve(__dirname, "../dist/index.html"), 37 | // set type 38 | protocol: "file:", 39 | // allow for propper formating of directory name. 40 | slashes: true, 41 | }) 42 | ); 43 | } 44 | // set window to null after app is closed. 45 | // create IPC event listener on browserRouter 46 | currentWindow.on("closed", () => { 47 | currentWindow = null; 48 | }); 49 | } 50 | // after IPC event listener is triggered "ready" 51 | // invoke our create new window by invoking "newWindow()" 52 | app.on("ready", newWindow); 53 | // electron, by default, refreshes every navigation 54 | // since we're using node modules outside of our dist folder, we have to disable auto refresh 55 | app.allowRendererProcessReuse = true; 56 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState, useEffect } from "react"; 2 | import { StateContext } from "./provider/StateProvider"; 3 | import { Switch, Route } from "react-router-dom"; 4 | import Home from "./pages/Home/Home"; 5 | import Landing from "./pages/Landing/Landing"; 6 | import Visualize from "./pages/Visualize/Visualize"; 7 | import "../assets/style/global.scss"; 8 | 9 | const App = () => { 10 | return ( 11 |
12 | 13 | 14 | 15 | 16 | 17 |
18 | ); 19 | }; 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /src/components/CodeEditor/CodeEditor.scss: -------------------------------------------------------------------------------- 1 | .file-tree-item { 2 | font-size: 16px; 3 | background-color: transparent; 4 | color: rgb(255, 214, 163); 5 | border: none; 6 | } 7 | 8 | #file-tree-head { 9 | list-style-type: none !important; 10 | } 11 | 12 | #code-editor-head { 13 | font-family: Arial, Helvetica, sans-serif; 14 | border: solid 2px rgb(255, 214, 163); 15 | padding: 30px 20px 30px 20px; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/components/CodeEditor/CodeEditor.tsx: -------------------------------------------------------------------------------- 1 | import "./CodeEditor.scss"; 2 | import React, { useContext, useEffect, useState } from "react"; 3 | import { StateContext } from "../../provider/StateProvider"; 4 | import MonacoEditor from "react-monaco-editor"; 5 | import options from "./options"; 6 | const { remote } = window.require("electron"); 7 | const electronFs = remote.require("fs"); 8 | 9 | const CodeEditor = () => { 10 | //from our glabal state 11 | const { activeFile, fileTree }: any = useContext(StateContext); 12 | //local state 13 | const [getContents, setContents] = useState(""); 14 | const getFileContents = (path: String) => { 15 | if (path.length > 0) { 16 | setContents(electronFs.readFileSync(path, "utf8")); 17 | } 18 | }; 19 | useEffect(() => { 20 | getFileContents(activeFile); 21 | [activeFile, fileTree]; 22 | }); 23 | 24 | return ( 25 |
26 |
27 | 35 |
36 | ); 37 | }; 38 | 39 | export default CodeEditor; 40 | -------------------------------------------------------------------------------- /src/components/CodeEditor/options.ts: -------------------------------------------------------------------------------- 1 | // MONACO OPTIONS PAGE: 2 | 3 | const options: any = { 4 | selectOnLineNumbers: true, 5 | autoIndent: true, 6 | colorDecorators: true, 7 | wrappingIndent: "indent", 8 | wordWrap: "bounded", 9 | automaticLayout: true, 10 | }; 11 | 12 | export default options; 13 | -------------------------------------------------------------------------------- /src/components/CodeEditor/start.js: -------------------------------------------------------------------------------- 1 | 2 | /* __...__ 3 | ..-' '. 4 | .-' | 5 | _..' EEQL | 6 | ...--"" . 7 | ..-" __.' 8 | .' ___...--' 9 | : ____....---' 10 | : .' 11 | : : _____ 12 | : : _..--""" """--..__ 13 | : : ." ""i--. 14 | : '.: : '. 15 | : '--...___i---""""--..___.' : 16 | : ""---...---"" : 17 | '. : 18 | '-. : 19 | '--... .' 20 | : ""---....._____.....---"" 21 | '. '. 22 | '-.. '. 23 | '. : 24 | : .' 25 | / : 26 | .' : 27 | .' .--' 28 | '--' */ -------------------------------------------------------------------------------- /src/components/FileTree/FileTree.scss: -------------------------------------------------------------------------------- 1 | * { 2 | list-style-type: none; 3 | } 4 | .file-tree-item { 5 | font-size: 16px; 6 | background-color: transparent; 7 | color: rgb(255, 214, 163); 8 | border: none; 9 | } 10 | 11 | #file-tree-head { 12 | list-style-type: none !important; 13 | } 14 | 15 | #file-tree-head { 16 | font-family: Arial, Helvetica, sans-serif; 17 | border: solid 2px rgb(255, 214, 163); 18 | padding: 20px 120px 50px 30px; 19 | padding-right: 40px; 20 | font-size: 20px; 21 | } 22 | 23 | #file-tree-head-empty { 24 | font-family: Arial, Helvetica, sans-serif; 25 | border: solid 2px rgb(255, 214, 163); 26 | padding: 20px; 27 | padding-right: 20px; 28 | text-align: center; 29 | font-size: 14px; 30 | } -------------------------------------------------------------------------------- /src/components/FileTree/FileTree.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useState } from "react"; 2 | import { StateContext } from "../../provider/StateProvider"; 3 | import "./FileTree.scss"; 4 | 5 | // create the file tree and allow a expand/minimize functionality. 6 | // if no files included, 7 | const FileTree = () => { 8 | const [folderView, setFolderView] = useState({}); 9 | const { userPath, fileTree, activeFileHandler }: any = useContext( 10 | StateContext 11 | ); 12 | const projectName = nameSetter(userPath.split("/")); 13 | const viewValue: { 14 | [key: string]: Boolean; 15 | } = {}; 16 | 17 | function nameSetter(name) { 18 | return name[name.length - 1]; 19 | } 20 | 21 | const viewHandler = (fileName: string): void => { 22 | folderView[fileName] 23 | ? setFolderView({ ...folderView, [fileName]: false }) 24 | : setFolderView({ ...folderView, [fileName]: true }); 25 | }; 26 | 27 | useEffect(() => { 28 | setFolderView(viewValue); 29 | }, []); 30 | 31 | const createTree = (tree) => 32 | tree.map((file) => { 33 | let ext = [file.name.split(".")[1], file.name.split(".")[2]]; 34 | if (file.files.length || ext[0] === undefined) { 35 | viewValue[file.name] = false; 36 | return ( 37 | 49 | ); 50 | } else if (ext[0] == "js" || ext[1] == "js") { 51 | return ( 52 | 65 | ); 66 | } else if (ext[0] == "html") { 67 | return ( 68 | 81 | ); 82 | } else if (ext[0] == "css") { 83 | return ( 84 | 97 | ); 98 | } else if (ext[0] == "test") { 99 | return ( 100 | 113 | ); 114 | } else if (ext[0] == "scss") { 115 | return ( 116 | 129 | ); 130 | } else if (ext[0] == "md") { 131 | return ( 132 | 145 | ); 146 | } else if (ext[1] == "config") { 147 | return ( 148 | 161 | ); 162 | } else if (ext[0] == "json") { 163 | return ( 164 | 177 | ); 178 | } else if (ext[0] == "tsx") { 179 | return ( 180 | 193 | ); 194 | } else if ( 195 | ext[0] == "png" || 196 | ext[0] == "jpeg" || 197 | ext[0] == "jpg" || 198 | ext[0] == "svc" || 199 | ext[0] == "icn" 200 | ) { 201 | return ( 202 | 215 | ); 216 | } else { 217 | return ( 218 | 231 | ); 232 | } 233 | }); 234 | if (userPath.length > 1) { 235 | return ( 236 |
237 |
238 | 239 | {" " + projectName} 240 |
241 |

{userPath}

242 | {createTree(fileTree)} 243 |
244 | ); 245 | } else 246 | return ( 247 |
248 |
249 | 250 | {" EMPTY"} 251 |
252 |
253 | ); 254 | }; 255 | 256 | export default FileTree; 257 | -------------------------------------------------------------------------------- /src/components/FileUpdate/FileUpdate.scss: -------------------------------------------------------------------------------- 1 | #file-upload-head { 2 | display: block; 3 | margin-left: auto; 4 | margin-right: auto; 5 | } 6 | 7 | .MuiButton-label { 8 | border-bottom: none !important; 9 | 10 | } 11 | 12 | .MuiButton-containedPrimary { 13 | margin-left: 12px !important; 14 | min-width: 20px; 15 | 16 | text-decoration: none !important; 17 | } 18 | .MuiSvgIcon-root { 19 | padding: 4px; 20 | margin-left: 4px; 21 | } 22 | 23 | .MuiButton-containedPrimary { 24 | background-color: #32c2b5 !important; 25 | // color: rgb(52, 52, 52) !important; 26 | display: inline flex !important; 27 | margin: 12px !important ; 28 | // margin: 2px; 29 | } 30 | .MuiInputBase-input { 31 | // margin-right: -90px !important; 32 | box-shadow: none; 33 | color: white !important; 34 | // text-align: right; 35 | } 36 | .MuiInput-underline:after { 37 | border-bottom: 2px solid #ffad3f !important; 38 | 39 | } 40 | .MuiInput-root { 41 | border-bottom: 2px solid #32c2b5 !important; 42 | // padding: 6px 6px !important; 43 | // margin: 8px; 44 | } 45 | .MuiTouchRipple-root { 46 | color: #32c2b5; 47 | } 48 | .aws-btn { 49 | font-family: Arial, Helvetica, sans-serif; 50 | --button-default-height: 50px; 51 | --button-default-font-size: 14px; 52 | --button-default-border-radius: 7px; 53 | --button-horizontal-padding: 25px; 54 | --button-raise-level: 7px; 55 | --button-hover-pressure: 3; 56 | --transform-speed: 0.175s; 57 | --button-primary-color: #32c2b5; 58 | --button-primary-color-dark: #167970; 59 | --button-primary-color-light: #000000; 60 | --button-primary-color-hover: #b1ece6; 61 | --button-primary-border: 0px solid #ffffff; 62 | --button-secondary-color: #f8ac40; 63 | --button-secondary-color-dark: #7e5011; 64 | --button-secondary-color-light: #000000; 65 | --button-secondary-color-hover: #ffd9a3; 66 | --button-secondary-border: none; 67 | --button-anchor-color: #b30000; 68 | --button-anchor-color-dark: #791616; 69 | --button-anchor-color-light: #000000; 70 | --button-anchor-color-hover: #ff0000; 71 | --button-anchor-border: 1px solid #922a2a; 72 | } -------------------------------------------------------------------------------- /src/components/FileUpdate/FileUpdate.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | /* eslint-disable import/no-unresolved */ // ! Be careful 3 | 4 | import React, { useContext, useState, useEffect } from "react"; 5 | import { StateContext } from "../../provider/StateProvider"; 6 | import { Link } from "react-router-dom"; 7 | // @ts-ignore 8 | import "react-awesome-button/dist/styles.scss"; 9 | import "./FileUpdate.scss"; 10 | // @ts-ignore 11 | import { AwesomeButton, AwesomeButtonProgress } from "react-awesome-button"; 12 | 13 | const { remote } = window.require("electron"); 14 | const fs = remote.require("fs"); 15 | let current: string = ""; 16 | let directoryArray; 17 | let fileArray; 18 | let cancelled = false; 19 | 20 | const FileUpdate = () => { 21 | function nameSetter(name) { 22 | return name[name.length - 1]; 23 | } 24 | const { 25 | userPath, 26 | fileTreeHandler, 27 | pathHandler, 28 | fileTree, 29 | updateTree, 30 | }: any = useContext(StateContext); 31 | const { activePortHandler }: any = useContext(StateContext); 32 | const projectName = nameSetter(userPath.split("/")); 33 | 34 | const setPort = (e) => { 35 | e.preventDefault(); 36 | console.log(e.target.value); 37 | activePortHandler(e.target.value); 38 | }; 39 | const getPath = () => { 40 | remote.dialog 41 | .showOpenDialog({ 42 | properties: ["openDirectory"], 43 | message: "Please choose your project folder", 44 | }) 45 | .then((files: any) => { 46 | if (files.cancelled) { 47 | cancelled = true; 48 | } else !files.cancelled; 49 | current = files.filePaths[0]; 50 | exportTestFile(current); 51 | pathHandler(files.filePaths[0]); 52 | generateFileTree(current); 53 | }); 54 | }; 55 | const exportTestFile = (dir) => { 56 | if (!fs.existsSync(`${dir}/__tests__`)) fs.mkdirSync(`${dir}/__tests__`); 57 | }; 58 | const generateFileTree = (dir) => { 59 | let getDirectory = (current) => { 60 | directoryArray = fs 61 | .readdirSync(current) 62 | .filter((file) => file !== "node_modules" && file[0] !== "."); 63 | if (directoryArray.filter((file, i) => !file.includes("")) == []) { 64 | return directoryArray; 65 | } 66 | }; 67 | getDirectory(dir); 68 | 69 | fileArray = directoryArray.map((name: string) => { 70 | let path = dir; 71 | path = `${path}/${name}`; 72 | const file: any = { path, name, files: [] }; 73 | 74 | if (!file.name.includes(".")) { 75 | file.files = generateFileTree(file.path); 76 | } 77 | return file; 78 | }); 79 | fileTreeHandler(fileArray); 80 | return fileArray; 81 | }; 82 | 83 | return ( 84 | 102 | ); 103 | }; 104 | 105 | export default FileUpdate; 106 | -------------------------------------------------------------------------------- /src/components/FileUpload/FileUpload.scss: -------------------------------------------------------------------------------- 1 | #file-upload-head { 2 | display: block; 3 | margin-left: auto; 4 | margin-right: auto; 5 | } 6 | 7 | .MuiButton-label { 8 | border-bottom: none !important; 9 | 10 | } 11 | 12 | .MuiButton-containedPrimary { 13 | margin-left: 12px !important; 14 | min-width: 20px; 15 | 16 | text-decoration: none !important; 17 | } 18 | .MuiSvgIcon-root { 19 | padding: 4px; 20 | margin-left: 4px; 21 | } 22 | 23 | .MuiButton-containedPrimary { 24 | background-color: #32c2b5 !important; 25 | // color: rgb(52, 52, 52) !important; 26 | display: inline flex !important; 27 | margin: 12px !important ; 28 | // margin: 2px; 29 | } 30 | .MuiInputBase-input { 31 | // margin-right: -90px !important; 32 | box-shadow: none; 33 | color: white !important; 34 | text-align: center; 35 | } 36 | .MuiInput-underline:after { 37 | border-bottom: 2px solid #ffad3f !important; 38 | 39 | } 40 | .MuiInput-root { 41 | border-bottom: 2px solid #32c2b5 !important; 42 | // padding: 6px 6px !important; 43 | // margin: 8px; 44 | } 45 | .MuiTouchRipple-root { 46 | color: #32c2b5; 47 | } 48 | .aws-btn { 49 | font-family: Arial, Helvetica, sans-serif; 50 | --button-default-height: 50px; 51 | --button-default-font-size: 14px; 52 | --button-default-border-radius: 7px; 53 | --button-horizontal-padding: 25px; 54 | --button-raise-level: 7px; 55 | --button-hover-pressure: 3; 56 | --transform-speed: 0.175s; 57 | --button-primary-color: #32c2b5; 58 | --button-primary-color-dark: #167970; 59 | --button-primary-color-light: #000000; 60 | --button-primary-color-hover: #b1ece6; 61 | --button-primary-border: 0px solid #ffffff; 62 | --button-secondary-color: #f8ac40; 63 | --button-secondary-color-dark: #7e5011; 64 | --button-secondary-color-light: #000000; 65 | --button-secondary-color-hover: #ffd9a3; 66 | --button-secondary-border: none; 67 | --button-anchor-color: #b30000; 68 | --button-anchor-color-dark: #791616; 69 | --button-anchor-color-light: #000000; 70 | --button-anchor-color-hover: #ff0000; 71 | --button-anchor-border: 1px solid #922a2a; 72 | } -------------------------------------------------------------------------------- /src/components/FileUpload/FileUpload.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { StateContext } from "../../provider/StateProvider"; 3 | import Input from "../../../node_modules/@material-ui/core/Input"; 4 | import { Link } from "react-router-dom"; 5 | // @ts-ignore 6 | import "react-awesome-button/dist/styles.scss"; 7 | import "./FileUpload.scss"; 8 | // @ts-ignore 9 | import { AwesomeButton, AwesomeButtonProgress } from "react-awesome-button"; 10 | 11 | const { remote } = window.require("electron"); 12 | const fs = remote.require("fs"); 13 | let current: string = ""; 14 | let directoryArray; 15 | let fileArray; 16 | let cancelled = false; 17 | 18 | const FileUpload = () => { 19 | function nameSetter(name) { 20 | return name[name.length - 1]; 21 | } 22 | const { userPath, fileTreeHandler, pathHandler }: any = useContext( 23 | StateContext 24 | ); 25 | const { activePortHandler }: any = useContext(StateContext); 26 | const projectName = nameSetter(userPath.split("/")); 27 | 28 | const exportTestFile = (dir) => { 29 | if (!fs.existsSync(`${dir}/__tests__`)) fs.mkdirSync(`${dir}/__tests__`); 30 | }; 31 | 32 | const setPort = (e) => { 33 | e.preventDefault(); 34 | activePortHandler(e.target.value); 35 | }; 36 | 37 | const getPath = () => { 38 | remote.dialog 39 | .showOpenDialog({ 40 | properties: ["openDirectory"], 41 | message: "Please choose your project folder", 42 | }) 43 | .then((files: any) => { 44 | if (files.cancelled) { 45 | cancelled = true; 46 | } else !files.cancelled; 47 | current = files.filePaths[0]; 48 | exportTestFile(current); 49 | pathHandler(files.filePaths[0]); 50 | generateFileTree(current); 51 | }); 52 | }; 53 | 54 | const generateFileTree = (dir) => { 55 | let getDirectory = (current) => { 56 | directoryArray = fs 57 | .readdirSync(current) 58 | .filter((file) => file !== "node_modules" && file[0] !== "."); 59 | if (directoryArray.filter((file, i) => !file.includes("")) == []) { 60 | return directoryArray; 61 | } 62 | }; 63 | getDirectory(dir); 64 | 65 | fileArray = directoryArray.map((name: string) => { 66 | let path = dir; 67 | path = `${path}/${name}`; 68 | const file: any = { path, name, files: [] }; 69 | 70 | if (!file.name.includes(".")) { 71 | file.files = generateFileTree(file.path); 72 | } 73 | return file; 74 | }); 75 | fileTreeHandler(fileArray); 76 | return fileArray; 77 | }; 78 | 79 | if (fileArray) { 80 | return ( 81 | 111 | ); 112 | } else 113 | return ( 114 | 143 | ); 144 | }; 145 | 146 | export default FileUpload; 147 | -------------------------------------------------------------------------------- /src/components/NavBar/NavBar.scss: -------------------------------------------------------------------------------- 1 | /* Dropdown Button */ 2 | .dropbtn { 3 | color: #1e1e21; 4 | font-size: 12px; 5 | border: none; 6 | 7 | background-color: #1e1e21; 8 | } 9 | #nav-text { 10 | margin-top: 10px; 11 | } 12 | .port { 13 | text-align: right; 14 | color: #ffffff; 15 | 16 | 17 | } 18 | 19 | /* The container
- needed to position the dropdown content */ 20 | .dropdown { 21 | display: flex; 22 | flex-direction: row; 23 | align-items: center; 24 | justify-content: flex-start; 25 | margin-bottom: 20px; 26 | padding-bottom: 10px; 27 | // border-bottom: solid 4px rgb(75, 162, 155) !important; 28 | } 29 | -------------------------------------------------------------------------------- /src/components/NavBar/NavBar.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import "./NavBar.scss"; 4 | import PortUpdate from "../../components/PortUpdate/PortUpdate"; 5 | import FileUpdate from "../../components/FileUpdate/FileUpdate"; 6 | // @ts-ignore 7 | import logo from "../../../assets/img/eeQl_logo.png"; 8 | const NavBar = () => { 9 | return ( 10 |
11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | ); 22 | }; 23 | 24 | export default NavBar; 25 | -------------------------------------------------------------------------------- /src/components/PortUpdate/PortUpdate.scss: -------------------------------------------------------------------------------- 1 | 2 | .aws-btn { 3 | font-family: Arial, Helvetica, sans-serif; 4 | --button-default-height: 50px; 5 | --button-default-font-size: 14px; 6 | --button-default-border-radius: 7px; 7 | --button-horizontal-padding: 25px; 8 | --button-raise-level: 7px; 9 | --button-hover-pressure: 3; 10 | --transform-speed: 0.175s; 11 | --button-primary-color: #32c2b5; 12 | --button-primary-color-dark: #167970; 13 | --button-primary-color-light: #000000; 14 | --button-primary-color-hover: #b1ece6; 15 | --button-primary-border: 0px solid #ffffff; 16 | --button-secondary-color: #f8ac40; 17 | --button-secondary-color-dark: #7e5011; 18 | --button-secondary-color-light: #000000; 19 | --button-secondary-color-hover: #ffd9a3; 20 | --button-secondary-border: none; 21 | --button-anchor-color: #b30000; 22 | --button-anchor-color-dark: #791616; 23 | --button-anchor-color-light: #000000; 24 | --button-anchor-color-hover: #ff0000; 25 | --button-anchor-border: 1px solid #922a2a; 26 | } -------------------------------------------------------------------------------- /src/components/PortUpdate/PortUpdate.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | /* eslint-disable import/no-unresolved */ // ! Be careful 3 | 4 | import React, { useContext, useState } from "react"; 5 | import { StateContext } from "../../provider/StateProvider"; 6 | import "./PortUpdate.scss"; 7 | import { Link } from "react-router-dom"; 8 | import Input from "../../../node_modules/@material-ui/core/Input"; 9 | // @ts-ignore 10 | import "react-awesome-button/dist/styles.scss"; 11 | // @ts-ignore 12 | import { AwesomeButton } from "react-awesome-button"; 13 | 14 | const { remote } = window.require("electron"); 15 | const fs = remote.require("fs"); 16 | 17 | const PortUpdate = () => { 18 | const { activePortHandler, activePort }: any = useContext(StateContext); 19 | const [newPort, newPortHandler] = useState(false); 20 | 21 | let submitPort = () => { 22 | newPortHandler(false); 23 | }; 24 | 25 | let updatePort = (e) => { 26 | if (newPort === false) { 27 | newPortHandler(true); 28 | } else { 29 | activePortHandler(e.target.value); 30 | } 31 | }; 32 | 33 | if (newPort === false) 34 | return ( 35 | 42 | ); 43 | else 44 | return ( 45 | 59 | ); 60 | }; 61 | 62 | export default PortUpdate; 63 | -------------------------------------------------------------------------------- /src/components/TestSuite/GraphQLEndpoint/GraphQLEndpoint.scss: -------------------------------------------------------------------------------- 1 | // h4 { 2 | // font-size: 14px; 3 | // } 4 | 5 | .gql-endpoint { 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: space-evenly; 9 | color: rgb(216, 216, 216); 10 | 11 | } 12 | 13 | #test-title { 14 | padding: 8px; 15 | } 16 | 17 | #server-button { 18 | padding-top: 10px; 19 | padding-bottom: 10px; 20 | } 21 | 22 | // #operationIsMutation { 23 | // color: green; 24 | // background-color: yellow; 25 | // } 26 | 27 | // .file { 28 | // align-self: flex-start; 29 | // } 30 | 31 | // .test { 32 | // align-self: center; 33 | // } 34 | 35 | // .code { 36 | // align-self: flex-end; 37 | // } -------------------------------------------------------------------------------- /src/components/TestSuite/GraphQLEndpoint/GraphQLEndpoint.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from "react"; 2 | // import GraphQL from "graphql"; 3 | // @ts-ignore 4 | import { TestContext } from "../../../provider/TestProvider"; 5 | import { StateContext } from "../../../provider/StateProvider"; 6 | import Input from "../../../../node_modules/@material-ui/core/Input"; 7 | import "./GraphQLEndpoint.scss"; 8 | // @ts-ignore 9 | import { AwesomeButtonProgress } from "react-awesome-button"; 10 | 11 | import Switch, { SwitchClassKey, SwitchProps } from '@material-ui/core/Switch'; 12 | import Grid from '@material-ui/core/Grid'; 13 | import { green } from '@material-ui/core/colors' 14 | import { withStyles } from '@material-ui/core/styles' 15 | 16 | const { remote } = window.require("electron"); 17 | const GraphQl = () => { 18 | const { test, testHandler }: any = useContext(TestContext); 19 | const { activePort, activeFile, setActiveFile }: any = useContext( 20 | StateContext 21 | ); 22 | const [operationIsMutation, setOperationIsMutation] = useState(false); 23 | const [operationIsValid, setOperationIsValid] = useState(false); 24 | 25 | // variable denoting current file in file operations 26 | let current; 27 | 28 | 29 | // handler for adding schema path to state 30 | const getSchemaPath = () => { 31 | remote.dialog 32 | .showOpenDialog({ 33 | properties: ["openFile"], 34 | message: "Please choose your schema file", 35 | }) 36 | .then((files: any) => { 37 | if (!files.cancelled) { 38 | current = files.filePaths[0]; 39 | testHandler("schemaFile", current); 40 | } 41 | }); 42 | }; 43 | 44 | // handler for adding resolver path to state 45 | const getResolverPath = () => { 46 | remote.dialog 47 | .showOpenDialog({ 48 | properties: ["openFile"], 49 | message: "Please choose your resolver file", 50 | }) 51 | .then((files: any) => { 52 | if (!files.cancelled) { 53 | current = files.filePaths[0]; 54 | testHandler("resolverFile", current); 55 | } 56 | }); 57 | }; 58 | 59 | // handler for adding data from input fields to state 60 | const inputHandler = (e: React.ChangeEvent): void => { 61 | if (e == null) { 62 | console.log({ schemaFile: current }); 63 | testHandler("schemaFile", current); 64 | } else if (e == undefined) { 65 | console.log({ resolverFile: current }); 66 | testHandler("resolverFile", current); 67 | } else testHandler(e.target.id, (e.target as HTMLInputElement).value); 68 | }; 69 | 70 | // handler for query/mutation switch 71 | const gqlOperationHandler = (): void => { 72 | setOperationIsMutation(!operationIsMutation) 73 | testHandler("operationIsMutation", operationIsMutation); 74 | } 75 | 76 | // handler for valid/invalid query switch 77 | const validOperationHandler = (): void => { 78 | setOperationIsValid(!operationIsValid) 79 | testHandler("operationIsValid", operationIsValid); 80 | } 81 | 82 | //color change for switch 83 | const GreenSwitch = withStyles({ 84 | switchBase: { 85 | color: green[300], 86 | '&$checked': { 87 | color: green[500], 88 | }, 89 | '&$checked + $track': { 90 | backgroundColor: green[500], 91 | }, 92 | }, 93 | checked: {}, 94 | track: {}, 95 | } 96 | )(Switch) 97 | 98 | 99 | 100 | return ( 101 |
102 | 103 |
GraphQL Test Builder
104 |
105 |
106 | {/* button to upload schemas */} 107 | { 111 | getSchemaPath(); 112 | setTimeout(() => { 113 | next(); 114 | }, 1000); 115 | return; 116 | }} 117 | loadingLabel="connecting" 118 | resultLabel="connected" 119 | > 120 | UPLOAD SCHEMAS 121 | 122 | {/* button to upload resolvers */} 123 | { 127 | getResolverPath(); 128 | setTimeout(() => { 129 | next(); 130 | }, 1000); 131 | return; 132 | }} 133 | loadingLabel="connecting" 134 | resultLabel="connected" 135 | > 136 | UPLOAD RESOLVERS 137 | 138 |
139 |
140 | {/* input for describe/it description */} 141 | 148 | 149 | {/* switch for valid/invalid operation */} 150 | 151 | Valid Operation 152 | 153 | 158 | 159 | Invalid Operation 160 | 161 | 162 | {/* input for query/mutation text */} 163 | 172 | 173 | {/* switch for query/mutation */} 174 | 175 | Query 176 | 177 | 182 | 183 | Mutation 184 | 185 | 186 | {/* conditionally render input where user enters data to be fed to the mutation. */} 187 | {(operationIsMutation) ? 188 | 198 | : 199 | null 200 | } 201 |

202 |
203 |
204 | ); 205 | }; 206 | 207 | export default GraphQl; -------------------------------------------------------------------------------- /src/components/TestSuite/GraphQLEndpoint/MockSchema.js: -------------------------------------------------------------------------------- 1 | import { gql } from "@apollo/client"; 2 | //gql function to parse the query string into a query document. 3 | 4 | export const dummyQuery = gql` 5 | query starwars { 6 | people { 7 | id 8 | name 9 | birth_year 10 | films { 11 | title 12 | } 13 | } 14 | } 15 | `; 16 | 17 | export function StarWars({ name }) { 18 | const { loading, error, data } = useQuery(dummyQuery, { 19 | variables: { name }, 20 | }); 21 | if (loading) return

Loading!

; 22 | if (error) return

Error ;3

; 23 | 24 | return ( 25 |

26 | {data.people.name} was born in {data.people.year} 27 |

28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/components/TestSuite/RestEndpoint/RestEndpoint.scss: -------------------------------------------------------------------------------- 1 | // h4 { 2 | // font-size: 14px; 3 | // } 4 | 5 | .rest-endpoint { 6 | display: flex; 7 | flex-direction: column; 8 | justify-content: space-evenly; 9 | color: rgb(216, 216, 216); 10 | 11 | } 12 | 13 | #test-title { 14 | padding: 8px; 15 | } 16 | #server-button { 17 | padding-top: 10px; 18 | padding-bottom: 10px; 19 | } 20 | 21 | // .file { 22 | // align-self: flex-start; 23 | // } 24 | 25 | // .test { 26 | // align-self: center; 27 | // } 28 | 29 | // .code { 30 | // align-self: flex-end; 31 | // } 32 | 33 | -------------------------------------------------------------------------------- /src/components/TestSuite/RestEndpoint/RestEndpoint.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState } from "react"; 2 | import { TestContext } from "../../../provider/TestProvider"; 3 | import { StateContext } from "../../../provider/StateProvider"; 4 | import "./RestEndpoint.scss"; 5 | import Input from "../../../../node_modules/@material-ui/core/Input"; 6 | // @ts-ignore 7 | import { AwesomeButtonProgress } from "react-awesome-button"; 8 | const { remote } = window.require("electron"); 9 | 10 | const RestEndpoint = () => { 11 | const { test, testHandler }: any = useContext(TestContext); 12 | const { activePort, activeFile, setActiveFile }: any = useContext( 13 | StateContext 14 | ); 15 | const [local, localhandler] = useState({}); 16 | let current; 17 | 18 | //Add methods to methodOptions array, and create options to be rendered in select/dropdown element 19 | const methods = ["GET", "POST", "PUT", "DELETE"]; 20 | const methodOptions = []; 21 | for (let a = 0; a < methods.length; a++) { 22 | methodOptions.push( 23 | 26 | ); 27 | } 28 | const getPath = () => { 29 | remote.dialog 30 | .showOpenDialog({ 31 | properties: ["openFile"], 32 | message: "Please choose your server file", 33 | }) 34 | .then((files: any) => { 35 | if (!files.cancelled) { 36 | current = files.filePaths[0]; 37 | inputHandler(null); 38 | } 39 | }); 40 | }; 41 | 42 | //handler for state to reflect entires in a select tag 43 | const selectHandler = (e: any): void => { 44 | const dropdownId = e.target.id; 45 | const dropdown = document.getElementById(dropdownId) as HTMLSelectElement; 46 | const selection = dropdown.options[dropdown.selectedIndex].text; 47 | testHandler(dropdownId, selection); 48 | }; 49 | 50 | //handler for state to reflect entries in an input tag 51 | const inputHandler = (e: React.ChangeEvent): void => { 52 | if (e == null) { 53 | console.log({ serverApp: current }); 54 | testHandler("serverApp", current); 55 | } else testHandler(e.target.id, (e.target as HTMLInputElement).value); 56 | }; 57 | 58 | return ( 59 |
60 | 61 |
REST Test Builder
62 |
63 | {/*

Server file name:

*/} 64 | {/*

HTTP method:

*/} 65 | 72 | {/* */} 73 |
74 | { 78 | getPath(); 79 | setTimeout(() => { 80 | next(); 81 | }, 1000); 82 | return; 83 | }} 84 | loadingLabel="connecting" 85 | resultLabel="connected" 86 | > 87 | UPLOAD SERVER FILE 88 | 89 |
90 | 97 | 105 | 113 | 121 | 129 | 130 |

131 |
132 |
133 | ); 134 | }; 135 | 136 | export default RestEndpoint; 137 | -------------------------------------------------------------------------------- /src/components/TestSuite/Supertest.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | const request = require("supertest"); 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | // const app = require('app'); 6 | import { TestContext } from "../../provider/TestProvider"; 7 | import { StateContext } from "../../provider/StateProvider"; 8 | 9 | const { test, monacoPoster } = useContext(TestContext); 10 | const { activePort } = useContext(StateContext); 11 | const db = "./db/markets.js"; 12 | 13 | // if element selected is strictly equal to POST, the post request test should run 14 | if (test.methodSelect === "POST") { 15 | //POST request test 16 | //describe syntax 17 | describe(`${db}`, () => { 18 | //it syntax 19 | it(`${test.expectedRes}`, (done) => { 20 | //request 21 | request(activePort) 22 | //post method 23 | .post(test.serverApp) 24 | //send method 25 | .send(`value is equivalent to ${outputDropdown.selectedIndex.text}`) 26 | //expect method 27 | .expect(res.status) 28 | .to.be.equal(200) 29 | //done method 30 | .done(); 31 | }); 32 | }); 33 | } 34 | 35 | const str = `${test.expectedRes} `; 36 | monacoPoster(str); 37 | // console.log(str) 38 | 39 | // if element selected is strictly equal to GET, the get request test should run 40 | if (document.getElementById("methodSelect") === "GET") { 41 | //GET request test 42 | //describe syntax 43 | describe(`${db}`, () => { 44 | //it syntax 45 | it(`${test.expectedRes}`, (done) => { 46 | request(activePort) 47 | //get method 48 | .get(test.serverApp) 49 | //set method 50 | .set({ 51 | Accept: "application/json", 52 | }) 53 | //expect method 54 | .expect({ 55 | "Content-Type": "json", 56 | }) 57 | //done method 58 | .done(); 59 | }); 60 | }); 61 | } 62 | 63 | // if element selected is strictly equal to PUT, the put request test should run 64 | if (document.getElementById("methodSelect") === "PUT") { 65 | //PUT request test 66 | //describe syntax 67 | describe(`${db}`, () => { 68 | //it syntax 69 | it(`${test.expectedRes}`, (done) => { 70 | request(activePort) 71 | //get method 72 | .put(test.serverApp) 73 | //set method 74 | .set({ 75 | Accept: "application/json", 76 | }) 77 | //expect method 78 | .expect({ 79 | "Content-Type": "json", 80 | }) 81 | //done method 82 | .done(); 83 | }); 84 | }); 85 | } 86 | 87 | // if element selected is strictly equal to DELETE, the delete request test should run 88 | if (document.getElementById("methodSelect") === "DELETE") { 89 | //DELETE request test 90 | describe(`${db}`, () => { 91 | //it syntax 92 | it(`${test.expectedRes}`, (done) => { 93 | request(activePort) 94 | //get method 95 | .delete(test.serverApp) 96 | //set method 97 | .set({ 98 | Accept: "application/json", 99 | }) 100 | //expect method 101 | .expect({ 102 | "Content-Type": "json", 103 | }) 104 | //done method 105 | .done(); 106 | }); 107 | }); 108 | } 109 | 110 | export default TestSuite; 111 | -------------------------------------------------------------------------------- /src/components/TestSuite/TestBuilder/TestBuilder.scss: -------------------------------------------------------------------------------- 1 | // h3 { 2 | // font-size: 14px; 3 | // } 4 | 5 | // .test-builder { 6 | // display: grid; 7 | // justify-content: center; 8 | // color: rgb(216, 216, 216); 9 | // width: 15vw; 10 | // } 11 | 12 | // .file { 13 | // align-self: flex-start; 14 | // } 15 | 16 | // .test { 17 | // align-self: center; 18 | // } 19 | 20 | // .code { 21 | // align-self: flex-end; 22 | // } 23 | .test-builder { 24 | font-family: Arial, Helvetica, sans-serif; 25 | border: solid 2px rgb(255, 214, 163); 26 | padding: 40px 40px 40px 40px; 27 | text-align: center; 28 | } 29 | 30 | 31 | .aws-btn { 32 | font-family: Arial, Helvetica, sans-serif; 33 | padding: 2px; 34 | margin: 5px; 35 | --button-default-height: 50px; 36 | --button-default-font-size: 14px; 37 | --button-default-border-radius: 7px; 38 | --button-horizontal-padding: 25px; 39 | --button-raise-level: 7px; 40 | --button-hover-pressure: 3; 41 | --transform-speed: 0.175s; 42 | --button-primary-color: #32c2b5; 43 | --button-primary-color-dark: #167970; 44 | --button-primary-color-light: #000000; 45 | --button-primary-color-hover: #b1ece6; 46 | --button-primary-border: 0px solid #ffffff; 47 | --button-secondary-color: #f8ac40; 48 | --button-secondary-color-dark: #7e5011; 49 | --button-secondary-color-light: #000000; 50 | --button-secondary-color-hover: #ffd9a3; 51 | --button-secondary-border: none; 52 | --button-anchor-color: #b30000; 53 | --button-anchor-color-dark: #791616; 54 | --button-anchor-color-light: #000000; 55 | --button-anchor-color-hover: #ff0000; 56 | --button-anchor-border: 1px solid #922a2a; 57 | } 58 | -------------------------------------------------------------------------------- /src/components/TestSuite/TestBuilder/TestBuilder.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useEffect, useState } from "react"; 2 | import { TestContext } from "../../../provider/TestProvider"; 3 | import { StateContext } from "../../../provider/StateProvider"; 4 | import "./TestBuilder.scss"; 5 | import RestEndpoint from "../RestEndpoint/RestEndpoint"; 6 | import GraphQl from "../GraphQLEndpoint/GraphQLEndpoint"; 7 | import RestTestCreation from "../TestGeneration/RestTestCreation"; 8 | import graphQLTestCreation from "../TestGeneration/graphQLTestCreation" 9 | import Input from "../../../../node_modules/@material-ui/core/Input"; 10 | import FileUpdate from "../../FileUpdate/FileUpdate" 11 | 12 | // import {ReactDOM, render} from 'react-dom' 13 | // @ts-ignore 14 | import { AwesomeButton, AwesomeButtonProgress } from "react-awesome-button"; 15 | 16 | 17 | const { remote } = window.require("electron"); 18 | const fs = remote.require("fs"); 19 | let directoryArray; 20 | let fileArray; 21 | 22 | const TestBuilder = () => { 23 | const { test, testHandler, resetHandler }: any = useContext(TestContext); 24 | const { 25 | userPath, 26 | activeFile, 27 | activeFileHandler, 28 | updateTreeHandler, 29 | fileTreeHandler, 30 | pathHandler, 31 | fileTree, 32 | updateTree, 33 | }: any = useContext(StateContext); 34 | const [local, localhandler] = useState(true); 35 | const [testName, testNameHandler] = useState(''); 36 | 37 | const generateFileTree = (dir) => { 38 | let getDirectory = (current) => { 39 | directoryArray = fs 40 | .readdirSync(current) 41 | .filter((file) => file !== "node_modules" && file[0] !== "."); 42 | if (directoryArray.filter((file, i) => !file.includes("")) == []) { 43 | return directoryArray; 44 | } 45 | }; 46 | getDirectory(dir); 47 | 48 | fileArray = directoryArray.map((name: string) => { 49 | let path = dir; 50 | path = `${path}/${name}`; 51 | const file: any = { path, name, files: [] }; 52 | 53 | if (!file.name.includes(".")) { 54 | file.files = generateFileTree(file.path); 55 | } 56 | return file; 57 | }); 58 | fileTreeHandler(fileArray); 59 | return fileArray; 60 | }; 61 | 62 | const clicker = () => { 63 | // gather current value from all fields. 64 | // serverApp, expectedRes, methodSelect, desiredEndpoint, inputData, outputData, headerInfo 65 | let selectFieldsArray = ["methodSelect"]; 66 | 67 | let inputFieldsArray = [ 68 | "serverApp", 69 | "expectedRes", 70 | "desiredEndpoint", 71 | "inputData", 72 | "outputData", 73 | "headerInfo", 74 | ]; 75 | // iterate through all dropdowns (select is the formal name) to extract current selected value, and store that selection in our state 76 | for (let i = 0; i < selectFieldsArray.length; i++) { 77 | const dropDown = document.getElementById( 78 | selectFieldsArray[i] 79 | ) as HTMLSelectElement; 80 | const selection = dropDown.options[dropDown.selectedIndex].text; 81 | testHandler(selectFieldsArray[i], selection); 82 | } 83 | console.log("state after select fields loop", test); 84 | 85 | for (let j = 0; j < inputFieldsArray.length; j++) { 86 | const inputField = document.getElementById(inputFieldsArray[j]); 87 | const inputValue = inputField.innerText ? inputField.innerText : "qwerty"; 88 | testHandler(inputFieldsArray[j], inputValue); 89 | } 90 | 91 | console.log(test); 92 | console.log("monaco", activeFile); 93 | }; 94 | 95 | const inputHandler = (e: React.ChangeEvent): void => { 96 | // if (e == null) { 97 | // console.log({ serverApp: current }); 98 | // testHandler("serverApp", current); 99 | // } else if (e == undefined) { 100 | // console.log({ schemaApp: current }); 101 | // testHandler("schemaApp", current); 102 | // } else 103 | testHandler(e.target.id, (e.target as HTMLInputElement).value); 104 | }; 105 | 106 | const handleSaveTest = () => { 107 | 108 | const createTest = (local) ? RestTestCreation(test) : graphQLTestCreation(test); 109 | console.log(createTest); 110 | fs.writeFileSync( 111 | `${userPath}/__tests__/${test.testFileName}.js`, 112 | createTest, 113 | { 114 | encoding: "utf8", 115 | } 116 | ); 117 | }; 118 | 119 | // if (local) { 120 | return ( 121 |
122 | 131 | localhandler(true)} 136 | > 137 | REST 138 | 139 | localhandler(false)} 144 | > 145 | GRAPHQL 146 | 147 | 148 | {/* conditionally render the form for building Rest or GraphQL tests */} 149 | { 150 | (local) ? : 151 | } 152 |

153 | {/* save button */} 154 | { 161 | console.log("Saving Test..."); 162 | handleSaveTest(); 163 | generateFileTree(userPath); 164 | setTimeout(() => { 165 | next(); 166 | }, 1000); 167 | }} 168 | > 169 | SAVE 170 | 171 |
172 | ); 173 | }; 174 | 175 | export default TestBuilder; 176 | -------------------------------------------------------------------------------- /src/components/TestSuite/TestGeneration/RestTestCreation.js: -------------------------------------------------------------------------------- 1 | const RestTestCreation = (state) => { 2 | // Sample Representation of Test Output: 3 | ` 4 | const supertest = require('supertest'); 5 | const app = require('../server/server.js'); 6 | const request = supertest(app); 7 | jest.useFakeTimers(); 8 | 9 | test('Gets html from root page', async (done) => { 10 | const response = await request 11 | .get('/') 12 | .set({}); 13 | expect(response.status).toBe(200); 14 | return done(); 15 | });`; 16 | 17 | const serverApp = state.serverApp; 18 | const expectedRes = state.expectedRes; 19 | const methodSelect = state.methodSelect; 20 | const desiredEndpoint = state.desiredEndpoint; 21 | const inputData = 22 | state.methodSelect === "POST" || state.methodSelect === "PUT" 23 | ? `.send(${state.inputData});` 24 | : ""; 25 | const headerInfo = state.headerInfo ? `.set(${headerInfo})` : ""; 26 | const outputData = state.outputData; 27 | 28 | const test = ` 29 | const supertest = require('supertest'); 30 | const app = require('${serverApp}'); 31 | const request = supertest(app); 32 | jest.useFakeTimers(); 33 | 34 | test('${expectedRes}', async (done) => { 35 | const response = await request 36 | .${methodSelect}('${desiredEndpoint}') 37 | ${inputData} 38 | ${headerInfo} 39 | expect(${inputData}).toBe(${outputData}); 40 | return done(); 41 | }); 42 | `; 43 | 44 | return test; 45 | }; 46 | 47 | export default RestTestCreation; 48 | -------------------------------------------------------------------------------- /src/components/TestSuite/TestGeneration/graphQLTestCreation.js: -------------------------------------------------------------------------------- 1 | const graphQLTestCreation = (state) => { 2 | 3 | 4 | const addMutationObject = (state.operationIsMutation !== false) ? "" : `, ${state.mutationObject}`; 5 | 6 | const egqlBoilerplate = (state) => 7 | ` 8 | 'use strict' 9 | 10 | const fs = require('fs') 11 | const path = require('path') 12 | const EasyGraphQLTester = require('easygraphql-tester') 13 | 14 | const schema = fs.readFileSync(path.join(__dirname, "${state.schemaFile}"), 'utf8') 15 | const resolvers = require("${state.resolverFile}") 16 | 17 | describe('Test queries and mutations', () => { 18 | let tester 19 | 20 | beforeEach(() => { 21 | tester = new EasyGraphQLTester(schema, resolvers) 22 | }) 23 | describe('${state.expectedRes}', () => { 24 | ` 25 | 26 | const queryValidQueryBoilerPlate = (state) => 27 | ` 28 | it('${state.expectedRes}', () => { 29 | const operation = \`${state.gqlOperationText}\` 30 | tester.test(${state.operationIsValid}, operation${addMutationObject}) 31 | }) 32 | ` 33 | 34 | const closingParens = ` 35 | }) 36 | }) 37 | ` 38 | 39 | return egqlBoilerplate(state) + queryValidQueryBoilerPlate(state) + closingParens; 40 | }; 41 | 42 | export default graphQLTestCreation; 43 | -------------------------------------------------------------------------------- /src/components/TestSuite/server/db/markets.dev.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "location": "losangeles", 4 | "cards": 6 5 | }, 6 | { 7 | "location": "sdfsdf", 8 | "cards": 0 9 | } 10 | ] -------------------------------------------------------------------------------- /src/components/TestSuite/server/db/markets.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | let writeLocation; 4 | let marketList; 5 | 6 | if (process.env.NODE_ENV === 'test') { 7 | writeLocation = `${__dirname}/markets.test.json`; 8 | marketList = JSON.parse(fs.readFileSync(writeLocation)); 9 | } else { 10 | writeLocation = `${__dirname}/markets.dev.json`; 11 | marketList = JSON.parse(fs.readFileSync(writeLocation)); 12 | } 13 | 14 | const db = {}; 15 | 16 | 17 | /** 18 | * #sync - Overwrites the current database with markets list from client 19 | * 20 | * @param {Array} markets - the new market list 21 | * @return {Array} the list of markets 22 | */ 23 | db.sync = (markets) => { 24 | if (!Array.isArray(markets)) { 25 | return new Error(`Market list must be an array, received ${typeof markets}`); 26 | } 27 | if (markets.some(m => m.location === undefined || m.cards === undefined)) { 28 | return new Error('Missing fields on some markets'); 29 | } 30 | if (markets.some(m => typeof m.location !== 'string')) { 31 | return new Error('TypeError in your market location'); 32 | } 33 | if (markets.some(m => typeof m.cards !== 'number')) { 34 | return new Error('TypeError in your market cards'); 35 | } 36 | db.write(markets); 37 | db.reset(); 38 | return marketList; 39 | }; 40 | 41 | 42 | /** 43 | * #find - Returns the entire list of markets from the appropriate 44 | * markets.env.json file. 45 | * 46 | * @return {Array} the list of markets 47 | */ 48 | db.find = () => { 49 | db.reset(); 50 | return marketList; 51 | }; 52 | 53 | 54 | /** 55 | * #drop - Deletes everything from the appropriate markets.env.json file and 56 | * writes an empty array in its place. 57 | */ 58 | db.drop = () => { 59 | marketList = []; 60 | db.write(marketList); 61 | }; 62 | 63 | 64 | db.write = (data) => { 65 | fs.writeFileSync(writeLocation, JSON.stringify(data, null, 2)); 66 | }; 67 | 68 | 69 | db.reset = () => { 70 | marketList = JSON.parse(fs.readFileSync(writeLocation)); 71 | }; 72 | 73 | 74 | module.exports = db; 75 | -------------------------------------------------------------------------------- /src/components/TestSuite/server/db/markets.test.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "location": "kansascity", 4 | "cards": 17 5 | } 6 | ] -------------------------------------------------------------------------------- /src/components/TestSuite/server/index.js: -------------------------------------------------------------------------------- 1 | /* eslint no-unused-vars: 0 */ 2 | const path = require('path'); 3 | const express = require('express'); 4 | const db = require('./db/markets'); 5 | 6 | const app = express(); 7 | const port = process.env.PORT || 3000; 8 | 9 | app.use(express.static(path.resolve(__dirname, '../dist'))); 10 | app.use(express.json()); 11 | 12 | app.put('/markets', (req, res, next) => { 13 | const syncResult = db.sync(req.body); 14 | if (syncResult instanceof Error) { 15 | return next({ code: 400, error: syncResult }); 16 | } 17 | res.json(syncResult); 18 | }); 19 | 20 | app.get('/markets', (req, res) => { 21 | res.json(db.find()); 22 | }); 23 | 24 | app.use(({ code, error }, req, res, next) => { 25 | res.status(code).json({ error }); 26 | }); 27 | 28 | module.exports = app.listen(port, () => console.log(`Listening on port ${port}`)); 29 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | eeQL 12 | 13 | 14 | 15 |
16 | 17 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | // eslint-disable-next-line no-use-before-define // 3 | import React from "react"; 4 | import ReactDom from "react-dom"; 5 | import { HashRouter } from "react-router-dom"; 6 | import StateProvider from "./provider/StateProvider.tsx"; 7 | import TestProvider from "./provider/TestProvider.tsx"; 8 | import DataProvider from "./provider/TestProvider.tsx"; 9 | 10 | import App from "./App"; 11 | 12 | ReactDom.render( 13 | // providing hashrouter for electron static application 14 | // need to add providers here v 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | , 24 | document.getElementById("root") 25 | ); 26 | -------------------------------------------------------------------------------- /src/pages/Home/Home.scss: -------------------------------------------------------------------------------- 1 | .react-resizable { 2 | position: relative; 3 | } 4 | .react-resizable-handle { 5 | position: absolute; 6 | width: 20px; 7 | height: 20px; 8 | background-repeat: no-repeat; 9 | background-origin: content-box; 10 | box-sizing: border-box; 11 | background.react-resizable { 12 | position: relative; 13 | } 14 | .react-resizable-handle { 15 | position: absolute; 16 | width: 20px; 17 | height: 20px; 18 | background-repeat: no-repeat; 19 | background-origin: content-box; 20 | box-sizing: border-box; 21 | background-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+'); 22 | background-position: bottom right; 23 | padding: 0 3px 3px 0; 24 | } 25 | .react-resizable-handle-sw { 26 | bottom: 0; 27 | left: 0; 28 | cursor: sw-resize; 29 | transform: rotate(90deg); 30 | } 31 | .react-resizable-handle-se { 32 | bottom: 0; 33 | right: 0; 34 | cursor: se-resize; 35 | } 36 | .react-resizable-handle-nw { 37 | top: 0; 38 | left: 0; 39 | cursor: nw-resize; 40 | transform: rotate(180deg); 41 | } 42 | .react-resizable-handle-ne { 43 | top: 0; 44 | right: 0; 45 | cursor: ne-resize; 46 | transform: rotate(270deg); 47 | } 48 | .react-resizable-handle-w { 49 | top: 50%; 50 | margin-top: -10px; 51 | cursor: ew-resize; 52 | left: 0; 53 | transform: rotate(135deg); 54 | } 55 | .react-resizable-handle-e { 56 | top: 50%; 57 | margin-top: -10px; 58 | cursor: ew-resize; 59 | right: 0; 60 | transform: rotate(315deg); 61 | } 62 | .react-resizable-handle-n { 63 | left: 50%; 64 | margin-left: -10px; 65 | cursor: ns-resize; 66 | top: 0; 67 | transform: rotate(225deg); 68 | } 69 | .react-resizable-handle-s { 70 | left: 50%; 71 | margin-left: -10px; 72 | cursor: ns-resize; 73 | bottom: 0; 74 | transform: rotate(45deg); 75 | } 76 | .react-grid-layout { 77 | position: relative; 78 | transition: height 200ms ease; 79 | } 80 | .react-grid-item { 81 | transition: all 200ms ease; 82 | transition-property: left, top; 83 | img { 84 | pointer-events: none; 85 | user-select: none; 86 | } 87 | >.react-resizable-handle { 88 | position: absolute; 89 | width: 20px; 90 | height: 20px; 91 | &::after { 92 | content: ""; 93 | position: absolute; 94 | right: 3px; 95 | bottom: 3px; 96 | width: 5px; 97 | height: 5px; 98 | border-right: 2px solid rgba(0, 0, 0, 0.4); 99 | border-bottom: 2px solid rgba(0, 0, 0, 0.4); 100 | } 101 | } 102 | >.react-resizable-handle.react-resizable-handle-sw { 103 | bottom: 0; 104 | left: 0; 105 | cursor: sw-resize; 106 | transform: rotate(90deg); 107 | } 108 | >.react-resizable-handle.react-resizable-handle-se { 109 | bottom: 0; 110 | right: 0; 111 | cursor: se-resize; 112 | } 113 | >.react-resizable-handle.react-resizable-handle-nw { 114 | top: 0; 115 | left: 0; 116 | cursor: nw-resize; 117 | transform: rotate(180deg); 118 | } 119 | >.react-resizable-handle.react-resizable-handle-ne { 120 | top: 0; 121 | right: 0; 122 | cursor: ne-resize; 123 | transform: rotate(270deg); 124 | } 125 | >.react-resizable-handle.react-resizable-handle-w { 126 | top: 50%; 127 | margin-top: -10px; 128 | cursor: ew-resize; 129 | left: 0; 130 | transform: rotate(135deg); 131 | } 132 | >.react-resizable-handle.react-resizable-handle-e { 133 | top: 50%; 134 | margin-top: -10px; 135 | cursor: ew-resize; 136 | right: 0; 137 | transform: rotate(315deg); 138 | } 139 | >.react-resizable-handle.react-resizable-handle-n { 140 | left: 50%; 141 | margin-left: -10px; 142 | cursor: ns-resize; 143 | top: 0; 144 | transform: rotate(225deg); 145 | } 146 | >.react-resizable-handle.react-resizable-handle-s { 147 | left: 50%; 148 | margin-left: -10px; 149 | cursor: ns-resize; 150 | bottom: 0; 151 | transform: rotate(45deg); 152 | } 153 | } 154 | .react-grid-item.cssTransforms { 155 | transition-property: transform; 156 | } 157 | .react-grid-item.resizing { 158 | z-index: 1; 159 | will-change: width, height; 160 | } 161 | .react-grid-item.react-draggable-dragging { 162 | transition: none; 163 | z-index: 3; 164 | will-change: transform; 165 | } 166 | .react-grid-item.dropping { 167 | visibility: hidden; 168 | } 169 | .react-grid-item.react-grid-placeholder { 170 | background: rgb(58, 58, 58); 171 | opacity: 0.2; 172 | transition-duration: 100ms; 173 | z-index: 2; 174 | -webkit-user-select: none; 175 | -moz-user-select: none; 176 | -ms-user-select: none; 177 | -o-user-select: none; 178 | user-select: none; 179 | } 180 | .react-resizable-hide { 181 | >.react-resizable-handle { 182 | display: none; 183 | } 184 | }-image: url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2IDYiIHN0eWxlPSJiYWNrZ3JvdW5kLWNvbG9yOiNmZmZmZmYwMCIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSI2cHgiIGhlaWdodD0iNnB4Ij48ZyBvcGFjaXR5PSIwLjMwMiI+PHBhdGggZD0iTSA2IDYgTCAwIDYgTCAwIDQuMiBMIDQgNC4yIEwgNC4yIDQuMiBMIDQuMiAwIEwgNiAwIEwgNiA2IEwgNiA2IFoiIGZpbGw9IiMwMDAwMDAiLz48L2c+PC9zdmc+'); 185 | background-position: bottom right; 186 | padding: 0 3px 3px 0; 187 | } 188 | .react-resizable-handle-sw { 189 | bottom: 0; 190 | left: 0; 191 | cursor: sw-resize; 192 | transform: rotate(90deg); 193 | } 194 | .react-resizable-handle-se { 195 | bottom: 0; 196 | right: 0; 197 | cursor: se-resize; 198 | } 199 | .react-resizable-handle-nw { 200 | top: 0; 201 | left: 0; 202 | cursor: nw-resize; 203 | transform: rotate(180deg); 204 | } 205 | .react-resizable-handle-ne { 206 | top: 0; 207 | right: 0; 208 | cursor: ne-resize; 209 | transform: rotate(270deg); 210 | } 211 | .react-resizable-handle-w, 212 | .react-resizable-handle-e { 213 | top: 50%; 214 | margin-top: -10px; 215 | cursor: ew-resize; 216 | } 217 | .react-resizable-handle-w { 218 | left: 0; 219 | transform: rotate(135deg); 220 | } 221 | .react-resizable-handle-e { 222 | right: 0; 223 | transform: rotate(315deg); 224 | } 225 | .react-resizable-handle-n, 226 | .react-resizable-handle-s { 227 | left: 50%; 228 | margin-left: -10px; 229 | cursor: ns-resize; 230 | } 231 | .react-resizable-handle-n { 232 | top: 0; 233 | transform: rotate(225deg); 234 | } 235 | .react-resizable-handle-s { 236 | bottom: 0; 237 | transform: rotate(45deg); 238 | } 239 | 240 | .react-grid-layout { 241 | margin: auto; 242 | transition: height 200ms ease; 243 | } 244 | .react-grid-item { 245 | transition: all 200ms ease; 246 | transition-property: left, top; 247 | } 248 | .react-grid-item img { 249 | pointer-events: none; 250 | user-select: none; 251 | } 252 | .react-grid-item.cssTransforms { 253 | transition-property: transform; 254 | } 255 | .react-grid-item.resizing { 256 | z-index: 1; 257 | will-change: width, height; 258 | } 259 | 260 | .react-grid-item.react-draggable-dragging { 261 | transition: none; 262 | z-index: 3; 263 | will-change: transform; 264 | } 265 | 266 | .react-grid-item.dropping { 267 | visibility: hidden; 268 | } 269 | 270 | .react-grid-item.react-grid-placeholder { 271 | background: red; 272 | opacity: 0.2; 273 | transition-duration: 100ms; 274 | z-index: 2; 275 | -webkit-user-select: none; 276 | -moz-user-select: none; 277 | -ms-user-select: none; 278 | -o-user-select: none; 279 | user-select: none; 280 | } 281 | 282 | .react-grid-item > .react-resizable-handle { 283 | position: absolute; 284 | width: 20px; 285 | height: 20px; 286 | } 287 | 288 | .react-grid-item > .react-resizable-handle::after { 289 | content: ""; 290 | position: absolute; 291 | right: 3px; 292 | bottom: 3px; 293 | width: 5px; 294 | height: 5px; 295 | border-right: 2px solid rgba(0, 0, 0, 0.4); 296 | border-bottom: 2px solid rgba(0, 0, 0, 0.4); 297 | } 298 | 299 | .react-resizable-hide > .react-resizable-handle { 300 | display: none; 301 | } 302 | 303 | .react-grid-item > .react-resizable-handle.react-resizable-handle-sw { 304 | bottom: 0; 305 | left: 0; 306 | cursor: sw-resize; 307 | transform: rotate(90deg); 308 | } 309 | .react-grid-item > .react-resizable-handle.react-resizable-handle-se { 310 | bottom: 0; 311 | right: 0; 312 | cursor: se-resize; 313 | } 314 | .react-grid-item > .react-resizable-handle.react-resizable-handle-nw { 315 | top: 0; 316 | left: 0; 317 | cursor: nw-resize; 318 | transform: rotate(180deg); 319 | } 320 | .react-grid-item > .react-resizable-handle.react-resizable-handle-ne { 321 | top: 0; 322 | right: 0; 323 | cursor: ne-resize; 324 | transform: rotate(270deg); 325 | } 326 | .react-grid-item > .react-resizable-handle.react-resizable-handle-w, 327 | .react-grid-item > .react-resizable-handle.react-resizable-handle-e { 328 | top: 50%; 329 | margin-top: -10px; 330 | cursor: ew-resize; 331 | } 332 | .react-grid-item > .react-resizable-handle.react-resizable-handle-w { 333 | left: 0; 334 | transform: rotate(135deg); 335 | } 336 | .react-grid-item > .react-resizable-handle.react-resizable-handle-e { 337 | right: 0; 338 | transform: rotate(315deg); 339 | } 340 | .react-grid-item > .react-resizable-handle.react-resizable-handle-n, 341 | .react-grid-item > .react-resizable-handle.react-resizable-handle-s { 342 | left: 50%; 343 | margin-left: -10px; 344 | cursor: ns-resize; 345 | } 346 | .react-grid-item > .react-resizable-handle.react-resizable-handle-n { 347 | top: 0; 348 | transform: rotate(225deg); 349 | } 350 | .react-grid-item > .react-resizable-handle.react-resizable-handle-s { 351 | bottom: 0; 352 | transform: rotate(45deg); 353 | } 354 | 355 | .grid-border { 356 | display: flex; 357 | flex-direction: row; 358 | flex-wrap: nowrap; 359 | justify-content: space-around; 360 | align-items: center; 361 | 362 | } 363 | .home-border { 364 | margin: 40px !important; 365 | // flex-grow: 2; 366 | background-color: #1e1e21; 367 | color: white; 368 | font-family: Arial, Helvetica, sans-serif; 369 | } 370 | 371 | .file { 372 | padding:5px; 373 | } 374 | .test { 375 | padding:5px; 376 | } 377 | .code { 378 | padding:5px; 379 | } -------------------------------------------------------------------------------- /src/pages/Home/Home.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import GridLayout from "react-grid-layout"; 3 | import "./Home.scss"; 4 | 5 | import CodeEditor from "../../components/CodeEditor/CodeEditor"; 6 | import FileTree from "../../components/FileTree/FileTree"; 7 | import NavBar from "../../components/NavBar/NavBar"; 8 | import TestBuilder from "../../components/TestSuite/TestBuilder/TestBuilder"; 9 | import { StateContext } from "../../provider/StateProvider"; 10 | 11 | const Home = () => { 12 | //added state context 13 | const { activePortHandler }: any = useContext(StateContext); 14 | 15 | return ( 16 |
17 | 18 |
19 |
20 | 21 |
22 |
23 | 24 |
25 |
26 | 27 |
28 |
29 |
30 | ); 31 | }; 32 | 33 | export default Home; 34 | -------------------------------------------------------------------------------- /src/pages/Landing/Landing.scss: -------------------------------------------------------------------------------- 1 | 2 | #logo { 3 | display: block; 4 | margin-top: 250px; 5 | margin-left: auto; 6 | margin-right: auto; 7 | width: 50%; 8 | width: 400px; 9 | height: 140px; 10 | } 11 | 12 | 13 | .input { 14 | text-align: center; 15 | } 16 | 17 | .visual { 18 | color: white; 19 | font-family: Arial, Helvetica, sans-serif; 20 | padding: 20px 10px 10px 30px 21 | } 22 | .visualize { 23 | display: flex; 24 | position: absolute !important; 25 | bottom: 100px !important; 26 | width: 100%; 27 | align-items: center; 28 | justify-content: center; 29 | } 30 | 31 | // .darkMode, #global { 32 | // background-color: #201E21 !important; 33 | // } 34 | 35 | // .lightMode, body { 36 | // background-color: white; 37 | // } 38 | 39 | 40 | 41 | 42 | .aws-btn { 43 | font-family: Arial, Helvetica, sans-serif; 44 | padding: 2px; 45 | margin: 5px; 46 | --button-default-height: 50px; 47 | --button-default-font-size: 14px; 48 | --button-default-border-radius: 7px; 49 | --button-horizontal-padding: 25px; 50 | --button-raise-level: 7px; 51 | --button-hover-pressure: 3; 52 | --transform-speed: 0.175s; 53 | --button-primary-color: #32c2b5; 54 | --button-primary-color-dark: #167970; 55 | --button-primary-color-light: #000000; 56 | --button-primary-color-hover: #b1ece6; 57 | --button-primary-border: 0px solid #ffffff; 58 | --button-secondary-color: #f8ac40; 59 | --button-secondary-color-dark: #7e5011; 60 | --button-secondary-color-light: #000000; 61 | --button-secondary-color-hover: #ffd9a3; 62 | --button-secondary-border: none; 63 | --button-anchor-color: #b30000; 64 | --button-anchor-color-dark: #791616; 65 | --button-anchor-color-light: #000000; 66 | --button-anchor-color-hover: #ff0000; 67 | --button-anchor-border: 1px solid #922a2a; 68 | } 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/pages/Landing/Landing.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState, useEffect } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { StateContext } from "../../provider/StateProvider"; 4 | // @ts-ignore 5 | import eeQL2 from "../../../assets/img/eeql_logo_copy.png"; 6 | import FileUpload from "../../components/FileUpload/FileUpload"; 7 | import "react-awesome-button/dist/styles.scss"; 8 | import "./Landing.scss"; 9 | // @ts-ignore 10 | import { AwesomeButton, AwesomeButtonSocial } from "react-awesome-button"; 11 | const shell = require("electron").shell; 12 | 13 | 14 | 15 | const Landing = () => { 16 | return ( 17 |
18 |
19 | 20 | { 24 | window.alert( 25 | "This feature is currently in beta, we appeciate your patience. -eeQL" 26 | ); 27 | }} 28 | > 29 | VISUALIZE 30 | 31 | 32 | { 35 | shell.openExternal("https://github.com/oslabs-beta/eeQL"); 36 | }} 37 | > 38 | SOURCE 39 | 40 | 41 | 42 | 43 |
44 |
45 | 46 |
47 | 48 |
49 |
50 |
51 | ); 52 | }; 53 | 54 | export default Landing; 55 | -------------------------------------------------------------------------------- /src/pages/Visualize/Visualize.scss: -------------------------------------------------------------------------------- 1 | #graph { 2 | display: inline-flex; 3 | margin-left: 550px; 4 | margin-top: 250px; 5 | } -------------------------------------------------------------------------------- /src/pages/Visualize/Visualize.tsx: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import { Graph } from "react-d3-graph"; 3 | import { Link } from "react-router-dom"; 4 | import { DataContext } from "../../provider/DataProvider"; 5 | import "./Visualize.scss"; 6 | 7 | const Visualize = () => { 8 | const { database }: any = useContext(DataContext); 9 | console.log(database); 10 | const data = { 11 | nodes: [{ id: "Harry" }, { id: "Sally" }, { id: "Alice" }], 12 | focusedNodeId: "nodeIdToTriggerZoomAnimation", 13 | links: [ 14 | { source: "Harry", target: "Sally" }, 15 | { source: "Harry", target: "Alice" }, 16 | ], 17 | }; 18 | 19 | const myConfig = { 20 | automaticRearrangeAfterDropNode: false, 21 | collapsible: true, 22 | directed: false, 23 | focusAnimationDuration: 0.75, 24 | focusZoom: 1, 25 | freezeAllDragEvents: false, 26 | height: 400, 27 | highlightDegree: 1, 28 | highlightOpacity: 1, 29 | linkHighlightBehavior: false, 30 | maxZoom: 8, 31 | minZoom: 0.1, 32 | nodeHighlightBehavior: false, 33 | panAndZoom: false, 34 | staticGraph: false, 35 | staticGraphWithDragAndDrop: false, 36 | width: 800, 37 | d3: { 38 | alphaTarget: 0.05, 39 | gravity: -100, 40 | linkLength: 100, 41 | linkStrength: 1, 42 | disableLinkForce: false, 43 | }, 44 | node: { 45 | color: "#32c2b5", 46 | fontColor: "white", 47 | fontSize: 10, 48 | fontWeight: "normal", 49 | highlightColor: "SAME", 50 | highlightFontSize: 8, 51 | highlightFontWeight: "normal", 52 | highlightStrokeColor: "SAME", 53 | highlightStrokeWidth: "SAME", 54 | labelProperty: "id", 55 | mouseCursor: "pointer", 56 | opacity: 1, 57 | renderLabel: true, 58 | size: 200, 59 | strokeColor: "none", 60 | strokeWidth: 1.5, 61 | svg: "", 62 | symbolType: "circle", 63 | }, 64 | link: { 65 | color: "#d3d3d3", 66 | fontColor: "black", 67 | fontSize: 8, 68 | fontWeight: "normal", 69 | highlightColor: "SAME", 70 | highlightFontSize: 8, 71 | highlightFontWeight: "normal", 72 | labelProperty: "label", 73 | mouseCursor: "pointer", 74 | opacity: 1, 75 | renderLabel: false, 76 | semanticStrokeWidth: false, 77 | strokeWidth: 1.5, 78 | markerHeight: 6, 79 | markerWidth: 6, 80 | strokeDasharray: 0, 81 | strokeDashoffset: 0, 82 | strokeLinecap: "butt", 83 | }, 84 | }; 85 | 86 | const onClickNode = function(nodeId) { 87 | console.log(`Clicked node ${nodeId}`); 88 | }; 89 | 90 | const onClickLink = function(source, target) { 91 | console.log(`Clicked link between ${source} and ${target}`); 92 | }; 93 | 94 | return ( 95 |
96 | 103 | ; 104 | 105 | 106 | 107 |
108 | ); 109 | }; 110 | 111 | export default Visualize; 112 | -------------------------------------------------------------------------------- /src/provider/DataProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | //create init state w/ empty object 4 | export const DataContext = React.createContext({}); 5 | //create handler that can edit state w/ user input 6 | 7 | //initial state 8 | const DataProviders = ({ children }: any) => { 9 | const [database, setData] = useState("test"); 10 | 11 | //handler for all states with k-v's that are added/updated 12 | const dataHandler = (ref) => { 13 | setData(ref); 14 | }; 15 | 16 | return ( 17 | 23 | {children} 24 | 25 | ); 26 | }; 27 | 28 | export default DataProviders; 29 | -------------------------------------------------------------------------------- /src/provider/StateProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | // set a variable to export state/handlers into components 4 | export const StateContext = React.createContext({}); 5 | 6 | const StateProvider = ({ children }: any) => { 7 | 8 | const [darkMode, setDarkMode] = useState(true) 9 | const darkModeHandler = () => { 10 | setDarkMode(!darkMode) 11 | } 12 | 13 | // update and set state for user active chosen file 14 | const [updateTree, setUpdateTree] = useState(false) 15 | const updateTreeHandler = () => { 16 | setUpdateTree(!updateTree) 17 | } 18 | const [activePort, setActivePort] = useState('') 19 | const activePortHandler = (port: string): void => { 20 | setActivePort(port) 21 | } 22 | const [activeFile, setActiveFile] = useState(''); 23 | const activeFileHandler = (chosen: string): void => { 24 | setActiveFile(chosen) }; 25 | // update and set the current directory for the user's chosen project 26 | const [userPath, setUserPath] = useState(''); 27 | const pathHandler = (pathValue: string):void => { 28 | setUserPath(pathValue) }; 29 | // update the on/off of the file tree set below 30 | const [toggleFileTree, setToggleFileTree] = useState(true); 31 | const handleToggleFileTree = (): void => { 32 | setToggleFileTree(!toggleFileTree) }; 33 | // update and set the file tree for the directory 34 | const [fileTree, setFileTree] = useState([]); 35 | const fileTreeHandler = (tree: any): void => { 36 | setFileTree(tree) }; 37 | // update and set the name of the file that is being exported to the user's fs 38 | const [testFileName, setTestFileName] = useState(''); 39 | 40 | // return a wrapped component with the values of state and the functions to change them. 41 | return ( 42 | 52 | { children } 53 | 54 | )} 55 | 56 | // export the stateProvider to be accessed when wrapping the head of our react application 57 | export default StateProvider; -------------------------------------------------------------------------------- /src/provider/TestProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | 3 | //create init state w/ empty object 4 | export const TestContext = React.createContext({}); 5 | //create handler that can edit state w/ user input 6 | 7 | const initialState = { 8 | operationIsValid : true, 9 | } 10 | 11 | //initial state 12 | const TestProviders = ({ children }: any) => { 13 | const [test, setTest] = useState(initialState); 14 | 15 | const [monaco, monacoPoster] = useState(''); 16 | 17 | //handler for all states with k-v's that are added/updated 18 | const testHandler = (altKey: string, altValue: any) => { 19 | setTest({ ...test, [altKey]: altValue }); 20 | console.log(altKey, altValue, 'updatedState: ',test); 21 | }; 22 | 23 | const resetHandler = () =>{ 24 | setTest({}); 25 | } 26 | 27 | return ( 28 | 37 | {children} 38 | 39 | ); 40 | }; 41 | 42 | export default TestProviders; 43 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | // strict mode if off to prevent strong type errors 4 | "strict": false, 5 | // transpilation method to help interact with node.js 6 | "module": "commonjs", 7 | // importing document object model and previous ES libraries 8 | "lib": ["ES2015", "ES2016", "ES2017", "ES2018", "dom"], 9 | // sets the browser transpilation type 10 | "target": "es5", 11 | 12 | // set to true to allow imported JSX files in TSX modules. 13 | "allowJs": true, 14 | "jsx": "react", 15 | 16 | // set to true to allow editing of previously compiled ts files. 17 | // further serve importing between JSX and TSX files. 18 | "sourceMap": true, 19 | "esModuleInterop": true, 20 | "allowSyntheticDefaultImports": true, 21 | 22 | // export conditions to webpack build folder 23 | "outDir": "./dist" 24 | }, 25 | // applies to: 26 | "include": ["./src", "public"], 27 | "exclude": ["node_modules", "dist"] 28 | } 29 | -------------------------------------------------------------------------------- /webpack.electron.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | 3 | module.exports = { 4 | // build details 5 | entry: "./public/electron.js", 6 | // resolve imports missing extensions 7 | resolve: { extensions: [".tsx", ".ts", ".js", ".jsx"] }, 8 | // allow for use of node modules without altername name in minification 9 | node: { 10 | __dirname: false, 11 | }, 12 | // electron build output location 13 | output: { 14 | path: path.resolve(__dirname, "./dist"), 15 | filename: "main.js", 16 | }, 17 | // compiles electron when set 18 | target: "electron-main", 19 | devtool: "source-map", 20 | module: { 21 | // define type and loader method 22 | rules: [ 23 | { 24 | test: /\.(js|ts|tsx|jsx)$/, 25 | // exclude transpilations of node modules 26 | exclude: /node_modules/, 27 | use: "babel-loader", 28 | }, 29 | ], 30 | }, 31 | }; 32 | -------------------------------------------------------------------------------- /webpack.react.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const HtmlWebpackPlugin = require("html-webpack-plugin"); 3 | const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); 4 | 5 | const monacoEditor = path.resolve(__dirname, "./node_modules/monaco-editor"); 6 | const semanticUI = path.resolve(__dirname, "./node_modules/semantic-ui-react"); 7 | 8 | module.exports = { 9 | entry: "./src/index.js", 10 | // performance, allow for tsx editing after transpilation 11 | // create dev server conditions 12 | devtool: "source-map", 13 | devServer: { 14 | contentBase: path.join(__dirname, "./dist"), 15 | // allow fallback to origin 16 | historyApiFallback: true, 17 | compress: true, 18 | hot: true, 19 | port: 4000, 20 | // help serve assets to app 21 | publicPath: "/", 22 | }, 23 | // switched target from node to electron renderer. Was receiving errors in monaco client 24 | target: "electron-renderer", 25 | // describe compilation environment 26 | // resolve missing extensions and allow import of node modules 27 | resolve: { 28 | extensions: [".tsx", ".ts", ".js", ".jsx", ".css"], 29 | mainFields: ["main", "module", "browser"], 30 | }, 31 | // allow use of code editor and html render plugins 32 | plugins: [ 33 | new HtmlWebpackPlugin({ 34 | template: path.join(__dirname, "./src/index.html"), 35 | }), 36 | new MonacoWebpackPlugin(), 37 | ], 38 | // define type and loader methods 39 | module: { 40 | rules: [ 41 | { 42 | test: /\.ttf$/, 43 | use: "file-loader", 44 | }, 45 | { 46 | test: /\.(png|jpe?g|gif)$/i, 47 | use: "file-loader", 48 | }, 49 | { 50 | test: /\.(js|ts|tsx|jsx)$/, 51 | exclude: /node_modules/, 52 | use: "babel-loader", 53 | }, 54 | { 55 | test: /\.s[ac]ss$/i, 56 | use: ["style-loader", "css-loader", "sass-loader"], 57 | }, 58 | { 59 | test: /\.css$/, 60 | include: [monacoEditor, semanticUI], 61 | use: ["style-loader", "css-loader"], 62 | }, 63 | { 64 | test: /\.html$/i, 65 | loader: "html-loader", 66 | }, 67 | { 68 | test: /\.svg$/, 69 | use: [ 70 | { 71 | loader: "svg-url-loader", 72 | options: { 73 | limit: 10000, 74 | }, 75 | }, 76 | ], 77 | }, 78 | { 79 | test: /\.tsx?$/, 80 | use: [{ loader: "ts-loader", options: { happyPackMode: true } }], 81 | }, 82 | ], 83 | }, 84 | 85 | output: { 86 | path: path.resolve(__dirname, "./dist"), 87 | filename: "index.js", 88 | // altered to allow for access to public files 89 | publicPath: "./", 90 | }, 91 | }; 92 | --------------------------------------------------------------------------------