diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 00000000..b512c09d
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1 @@
+node_modules
\ No newline at end of file
diff --git a/.eslintrc b/.eslintrc
new file mode 100644
index 00000000..97e17131
--- /dev/null
+++ b/.eslintrc
@@ -0,0 +1,15 @@
+{
+ "extends": ["airbnb-typescript"],
+ "parserOptions": {
+ "project": "./tsconfig.json"
+ },
+ "rules": {
+ "arrow-body-style": "off",
+ "no-plusplus": "off",
+ "no-param-reassign": "off"
+ },
+ "env": {
+ "jest": true,
+ "browser": true
+ }
+}
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
index 3a03d76c..43c0622e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,6 +4,8 @@ node_js:
cache:
directories:
- node_modules
-deplloy:
+script:
+ - npm test
+deploy:
skip_cleanup: true
- on: master
+ on: master
\ No newline at end of file
diff --git a/README.md b/README.md
index acf6d330..f0a38775 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
![](https://lh5.googleusercontent.com/5Gr2dZXHJdmIiASsPw9put-6mR20e4g1gOk-af4krREaJ7NqkZnqXLD5QgiotfNHYhGRh387HSqdhjRwxdwOvQzg9ChhfIrZz0FdxVu6gktBtG-sy1MX6Xq36Gmrzu_6G_K7LDQZ)
-Spearmint helps developers easily create functional React/Redux/Endpoint tests without writing any code. It dynamically converts user inputs into executable Jest test code by using DOM query selectors provided by @testing-library.
+Spearmint helps developers easily create functional React/Redux/Endpoint/Paint Timing tests without writing any code. It dynamically converts user inputs into executable Jest test code by using DOM query selectors provided by @testing-library.
## How to use
@@ -22,6 +22,10 @@ To run Endpoint tests generated by spearmint, install the following in your dev
npm i -D jest supertest
+To run Puppeteer tests generated by spearmint, install the following in your dev dependencies.
+
+ npm i -D jest puppeteer
+
## How it works
1. On the initial screen, enter the URL of your project and load your application to start creating tests.
@@ -30,23 +34,27 @@ To run Endpoint tests generated by spearmint, install the following in your dev
2. Utilize our auto-complete, drop-down options, and tooltips features to easily create arrangement, action, and assertion test statements for React; reducer, action creator, asynchronous action creator, and middleware test statements for Redux; and hooks, context, and endpoint test statements.
-![](https://lh5.googleusercontent.com/5VYUlGG5VDdZxdZEh5aokuilhKRp8B5QyVmxvtW_abLYCAzYN-s-el1oV5WMtGuTzbEO2I6l8Ys_yK2gC0fCi8ISHwjh4LlgezsrPWd7mDEtLbPqBYf1J4pgkGmfIV4yq4I_dpQg)
+![](/public/newReact.png?raw=true)
3. Refer to the browser view of your app that is displayed on the right to quickly identify values for your selectors and use the file directory to open up a code editor view to easily refer to your codebase for props information.
4. Spearmint will then convert user input to dynamically generate a test file that will be saved inside a **tests** folder, which you can use to run ‘npm test’ on.
-![](https://lh4.googleusercontent.com/zE-ty31I22R7P9-ubFIb4ko6MOBb7r1-56cHX65h9AcF9IsE22RhPKMEdhyR0MJ3PbqNovj1uwxF2T5_klwZ2sWniXfCiXfaEysjQgH5NRskaDFNd-TOGce0oTMn2jPaPEGFHlEq)
+![](/public/testfile.png?raw=true)
## Team
> Alex [@apark0720](https://github.com/apark0720) ·
+> Charlie [@charlie-maloney](https://github.com/charlie-maloney) ·
> Chloe [@HeyItsChloe](https://github.com/HeyItsChloe) ·
> Cornelius [@corneeltron](https://github.com/corneeltron) ·
-> Dave [@davefranz](https://github.com/davefranz) ·
-> Johnny [@johnny-lim](https://github.com/johnny-lim)
+> Dave [@davefranz](https://github.com/davefranz)
+> Johnny [@johnny-lim](https://github.com/johnny-lim) ·
> Julie [@julicious100](https://github.com/julicious100) ·
> Karen [@karenpinilla](https://github.com/karenpinilla) ·
> Linda [@lcwish](https://github.com/lcwish) ·
+> Mike [@mbcoker](https://github.com/mbcoker)
> Natlyn [@natlynp](https://github.com/natlynp) ·
-> Rachel [@rachethecreator](https://github.com/rachethecreator)
+> Rachel [@rachethecreator](https://github.com/rachethecreator) ·
+> Sieun [@sieunjang](https://github.com/sieunjang) ·
+> Tristen [@twastell](https://github.com/twastell)
diff --git a/package.json b/package.json
index e1da9c36..ca811c9e 100644
--- a/package.json
+++ b/package.json
@@ -18,9 +18,16 @@
"homepage": "./",
"private": true,
"dependencies": {
+ "@types/jest": "^25.2.3",
+ "@types/react-beautiful-dnd": "^12.1.2",
+ "@types/react-dom": "^16.9.8",
+ "@types/react-modal": "^3.10.5",
+ "classnames": "^2.2.6",
"concurrently": "^4.1.2",
"cross-env": "^5.2.1",
+ "dotenv": "^8.2.0",
"electron-is-dev": "^1.1.0",
+ "fibers": "^5.0.0",
"js-beautify": "^1.10.0",
"monaco-editor": "^0.17.0",
"node-sass": "^4.12.0",
@@ -30,23 +37,21 @@
"react-dom": "^16.8.6",
"react-modal": "^3.8.1",
"react-monaco-editor": "^0.25.1",
- "react-scripts": "3.0.1",
- "typescript": "^3.5.1",
+ "react-scripts": "3.4.1",
+ "sass": "^1.26.5",
+ "typescript": "^3.9.2",
"wait-on": "^3.3.0"
},
"scripts": {
"react-start": "react-scripts start",
- "react-build": "react-scripts build",
+ "react-build": "NODE_ENV=production react-scripts build",
"test": "react-scripts test --env=jsdom",
"test:watch": "jest --watch",
"react-eject": "react-scripts eject",
- "electron-build": "electron-builder -mwl",
+ "electron-build": "NODE_ENV=production electron-builder -mwl",
"release": "npm run react-build && electron-builder --publish=always",
- "build": "npm run react-build && npm run electron-build",
- "start": "concurrently \"cross-env BROWSER=none npm run react-start\" \"wait-on http://localhost:3000 && electron .\""
- },
- "eslintConfig": {
- "extends": "react-app"
+ "build": "NODE_ENV=production npm run react-build && npm run electron-build",
+ "start": "NODE_ENV=development concurrently \"cross-env BROWSER=none npm run react-start\" \"wait-on http://localhost:3000 && electron .\""
},
"browserslist": {
"production": [
@@ -61,12 +66,22 @@
]
},
"devDependencies": {
- "@testing-library/react": "^8.0.1",
+ "@testing-library/jest-dom": "^5.8.0",
+ "@testing-library/react": "^8.0.9",
"@testing-library/react-hooks": "^3.2.1",
+ "@types/jest": "^25.2.3",
+ "@typescript-eslint/eslint-plugin": "^2.33.0",
+ "@typescript-eslint/parser": "^2.33.0",
"electron": "^5.0.3",
- "electron-builder": "^20.43.0",
+ "electron-builder": "^22.6.1",
+ "electron-devtools-installer": "^3.0.0",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.14.0",
+ "eslint-config-airbnb-typescript": "^7.2.1",
+ "eslint-plugin-import": "^2.20.2",
+ "eslint-plugin-jsx-a11y": "^6.2.3",
+ "eslint-plugin-react": "^7.20.0",
+ "eslint-plugin-react-hooks": "^2.5.1",
"jest-dom": "^3.5.0",
"react-test-renderer": "^16.12.0",
"spectron": "^5.0.0",
diff --git a/public/electron.js b/public/electron.js
index 0d12b6b1..660ff62d 100644
--- a/public/electron.js
+++ b/public/electron.js
@@ -1,14 +1,22 @@
+require('dotenv').config()
const { app, BrowserWindow } = require('electron');
const path = require('path');
const isDev = require('electron-is-dev');
-
let mainWindow;
-let testView;
+if (process.env.NODE_ENV === 'development') {
+ const { default: installExtension, REACT_DEVELOPER_TOOLS } = require('electron-devtools-installer');
+ function addDevTools() {
+ app.whenReady().then(() => {
+ installExtension(REACT_DEVELOPER_TOOLS)
+ .then((name) => console.log(`Added Extension: ${name}`))
+ .catch((err) => console.log('An error occurred: ', err));
+ });
+ };
+}
function createWindow() {
mainWindow = new BrowserWindow({
- width: 1400,
- minWidth: 1400,
+ width: 1550,
height: 750,
minHeight: 750,
icon: path.join(__dirname, 'public/icon.png'),
@@ -17,23 +25,24 @@ function createWindow() {
webviewTag: true,
},
});
-
mainWindow.loadURL(
isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, '../build/index.html')}`
);
mainWindow.on('closed', () => (mainWindow = null));
}
-app.on('ready', createWindow);
+if (process.env.NODE_ENV === 'development') {
+ app.on('ready', addDevTools);
+}
+app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
-
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
-});
+});
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 48be9f3f..d8464cb2 100644
--- a/public/index.html
+++ b/public/index.html
@@ -9,6 +9,7 @@
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
+
+ describe('Middlware', () => {
+ describe('Add Middleware', () => {
+ const action = {
+ type: 'ADD_MIDDLEWARE',
+ };
+
+ it('should add a middleware test case to reduxTestStatements', () => {
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].type).toEqual('middleware');
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state);
+ });
+ });
+
+ describe('Delete Middleware', () => {
+ const action = {
+ type: 'DELETE_MIDDLEWARE',
+ payload: 1,
+ };
+
+ it('should remove middleware test based on id from action payload', () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [],
+ hasRedux: 1,
+ };
+
+ state.reduxStatements.push({
+ id: 1,
+ type: 'middleware',
+ middlewaresFileName: '',
+ middlewaresFilePath: '',
+ queryType: '',
+ eventValue: null,
+ queryVariant: '',
+ querySelector: '',
+ queryValue: '',
+ queryFunction: '',
+ });
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0]).toBe(undefined);
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state)
+ });
+ });
+
+ describe('Update Middleware', () => {
+ const action = {
+ type: 'UPDATE_MIDDLEWARE',
+ payload: {
+ id: 3,
+ queryType: 'queryType',
+ eventValue: 'eventValue',
+ queryVariant: 'queryVariant',
+ querySelector: 'querySelector',
+ queryValue: 'queryValue',
+ queryFunction: 'queryFunction',
+ suggestions: 'suggestions',
+ },
+ };
+
+ it('updates the values of the middleware test based on action payload id and string values', () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [],
+ hasRedux: 1,
+ };
+
+ state.reduxStatements.push({
+ id: 3,
+ type: 'middleware',
+ queryType: '',
+ eventValue: null,
+ queryVariant: '',
+ querySelector: '',
+ queryValue: '',
+ queryFunction: '',
+ suggestions: ''
+ });
+
+ const {reduxStatements} = subject(state, action);
+
+ expect(reduxStatements[0].id).toEqual(3);
+ expect(reduxStatements[0].type).toEqual('middleware');
+ expect(reduxStatements[0].queryType).toEqual('queryType');
+ expect(reduxStatements[0].eventValue).toEqual('eventValue');
+ expect(reduxStatements[0].queryVariant).toEqual('queryVariant');
+ expect(reduxStatements[0].querySelector).toEqual('querySelector');
+ expect(reduxStatements[0].queryFunction).toEqual('queryFunction');
+ expect(reduxStatements[0].suggestions).toEqual('suggestions');
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state);
+ });
+ });
+
+ describe('Update Middleware File Path', () => {
+ const action = {
+ type: 'UPDATE_MIDDLEWARES_FILEPATH',
+ payload: {
+ middlewaresFileName: 'Hello',
+ middlewaresFilePath: 'World',
+ }
+ };
+
+ it("should update the middleware's file path", () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [
+ {
+ id: 3,
+ type: 'middleware',
+ middlewaresFileName: '',
+ middlewaresFilePath: '',
+ queryType: '',
+ eventValue: null,
+ queryVariant: '',
+ querySelector: '',
+ queryValue: '',
+ queryFunction: '',
+ }
+ ],
+ hasRedux: 1,
+ };
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].middlewaresFileName).toEqual('Hello');
+ expect(reduxStatements[0].middlewaresFilePath).toEqual('World');
+ expect(reduxStatements[0].id).toEqual(3);
+ });
+ });
+ });
+
+ //------- Action Creator Test Statment Tests---------->
+ describe('Action Creator', () => {
+ describe('Add Action Creator', () => {
+ const action = {
+ type: 'ADD_ACTIONCREATOR',
+ };
+
+ it('should add a action creator test case to reduxTestStatements', () => {
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].type).toEqual('action-creator');
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state);
+ });
+ });
+
+ describe('Delete Action Creator', () => {
+ const action = {
+ type: 'DELETE_ACTIONCREATOR',
+ payload: 1,
+ };
+
+ it('should remove action creator test based on id from action payload', () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [],
+ hasRedux: 1,
+ };
+
+ state.reduxStatements.push({
+ id: 1,
+ actionsFileName: '',
+ filePath: '',
+ typesFileName: '',
+ typesFilePath: '',
+ type: 'action-creator',
+ actionCreatorFunc: '',
+ actionType: '',
+ payloadKey: null,
+ payloadType: null,
+ });
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0]).toBe(undefined);
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state)
+ });
+ });
+
+ describe('Update Action Creator', () => {
+ const action = {
+ type: 'UPDATE_ACTIONCREATOR',
+ payload: {
+ id: 3,
+ actionsFile: 'actionsFile',
+ filePath: 'filePath',
+ typesFileName: 'typesFilename',
+ typesFilePath: 'typesFilePath',
+ actionCreatorFunc: 'actionCreatorFunc',
+ payloadKey: 'payloadKey',
+ payloadType: 'payloadType',
+ actionType: 'actionType',
+ },
+ };
+
+ it('updates the values of the action creator test based on action payload id and string values', () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [],
+ hasRedux: 1,
+ };
+
+ state.reduxStatements.push({
+ id: 3,
+ actionsFileName: '',
+ filePath: '',
+ typesFileName: '',
+ typesFilePath: '',
+ type: 'action-creator',
+ actionCreatorFunc: '',
+ actionType: '',
+ payloadKey: null,
+ payloadType: null,
+ });
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].id).toEqual(3);
+ expect(reduxStatements[0].type).toEqual('action-creator');
+ expect(reduxStatements[0].actionsFile).toEqual('actionsFile');
+ expect(reduxStatements[0].filePath).toEqual('filePath');
+ expect(reduxStatements[0].typesFileName).toEqual('typesFilename');
+ expect(reduxStatements[0].typesFilePath).toEqual('typesFilePath');
+ expect(reduxStatements[0].actionCreatorFunc).toEqual('actionCreatorFunc');
+ expect(reduxStatements[0].payloadKey).toEqual('payloadKey');
+ expect(reduxStatements[0].payloadType).toEqual('payloadType');
+ expect(reduxStatements[0].actionType).toEqual('actionType');
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state)
+ });
+ });
+
+ describe('Update Actions File Path', () => {
+ const action = {
+ type: 'UPDATE_ACTIONS_FILEPATH',
+ payload: {
+ actionsFileName: 'Hello',
+ filePath: 'World',
+ id: 3,
+ }
+ };
+
+ it("should update the action's file path", () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [
+ {
+ id: 3,
+ actionsFileName: '',
+ filePath: '',
+ typesFileName: '',
+ typesFilePath: '',
+ type: 'action-creator',
+ actionCreatorFunc: '',
+ actionType: '',
+ payloadKey: null,
+ payloadType: null,
+ }
+ ],
+ hasRedux: 1,
+ };
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].actionsFileName).toEqual('Hello');
+ expect(reduxStatements[0].filePath).toEqual('World');
+ expect(reduxStatements[0].id).toEqual(3);
+ });
+ });
+ });
+
+ //------- Reducer Test Statment Tests---------->
+ describe('Reducer', () => {
+ describe('Add Redcuer', () => {
+ const action = {
+ type: 'ADD_REDUCER',
+ };
+
+ it('should add a reducer test case to reduxTestStatements', () => {
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].type).toEqual('reducer');
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state);
+ });
+ });
+
+ describe('Delete Reducer', () => {
+ const action = {
+ type: 'DELETE_REDUCER',
+ payload: 1,
+ };
+
+ it('should remove reducer test based on id from action payload', () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [],
+ hasRedux: 1,
+ };
+
+ state.reduxStatements.push({
+ id: 1,
+ type: 'reducer',
+ typesFileName: '',
+ typesFilePath: '',
+ reducersFileName: '',
+ reducersFilePath: '',
+ reducerAction: '',
+ initialState: '',
+ reducerName: '',
+ expectedState: '',
+ });
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0]).toBe(undefined);
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state);
+ });
+ });
+
+ describe('Update Reducer', () => {
+ const action = {
+ type: 'UPDATE_REDUCER',
+ payload: {
+ id: 3,
+ reducersFileName: 'reducersFileName',
+ reducersFilePath:'reducersFilePath',
+ typesFilePath: 'typesFilePath',
+ typesFileName: 'typesFileName',
+ reducerAction: 'reducerAction',
+ initialState: 'initalState',
+ reducerName: 'reducerName',
+ expectedState: 'expectedState',
+ },
+ };
+
+ it('updates the values of the reducer test based on action payload id and string values', () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [],
+ hasRedux: 1,
+ };
+
+ state.reduxStatements.push({
+ id: 3,
+ type: 'reducer',
+ typesFileName: '',
+ typesFilePath: '',
+ reducersFileName: '',
+ reducersFilePath: '',
+ reducerAction: '',
+ initialState: '',
+ reducerName: '',
+ expectedState: '',
+ });
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].id).toEqual(3);
+ expect(reduxStatements[0].type).toEqual('reducer');
+ expect(reduxStatements[0].reducersFileName).toEqual('reducersFileName');
+ expect(reduxStatements[0].reducersFilePath).toEqual('reducersFilePath');
+ expect(reduxStatements[0].typesFilePath).toEqual('typesFilePath');
+ expect(reduxStatements[0].typesFileName).toEqual('typesFileName');
+ expect(reduxStatements[0].reducerAction).toEqual('reducerAction');
+ expect(reduxStatements[0].initialState).toEqual('initalState');
+ expect(reduxStatements[0].reducerName).toEqual('reducerName');
+ expect(reduxStatements[0].expectedState).toEqual('expectedState');
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state)
+ });
+ });
+
+ describe('Update Reducers File Path', () => {
+ const action = {
+ type: 'UPDATE_REDUCERS_FILEPATH',
+ payload: {
+ reducersFileName: 'Hello',
+ reducersFilePath: 'World',
+ }
+ };
+
+ it("should update the reducer's file path", () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [
+ {
+ id: 3,
+ type: 'reducer',
+ typesFileName: '',
+ typesFilePath: '',
+ reducersFileName: '',
+ reducersFilePath: '',
+ reducerAction: '',
+ initialState: '',
+ reducerName: '',
+ expectedState: '',
+ }
+ ],
+ hasRedux: 1,
+ };
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].reducersFileName).toEqual('Hello');
+ expect(reduxStatements[0].reducersFilePath).toEqual('World');
+ });
+ });
+ });
+
+ //-------Async Test Statment Tests---------->
+ describe('Async', () => {
+ describe('Add Async', () => {
+ const action = {
+ type: 'ADD_ASYNC',
+ };
+
+ it('should add an async test case to reduxTestStatements', () => {
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].type).toEqual('async');
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state);
+ });
+ });
+
+ describe('Delete Async', () => {
+ const action = {
+ type: 'DELETE_ASYNC',
+ payload: 1,
+ };
+
+ it('should remove async test based on id from action payload', () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [],
+ hasRedux: 1,
+ };
+
+ state.reduxStatements.push({
+ id: 1,
+ type: 'async',
+ actionsFileName: '',
+ filePath: '',
+ typesFileName: '',
+ typesFilePath: '',
+ asyncFunction: '',
+ method: '',
+ route: '',
+ requestBody: '',
+ store: '',
+ matcher: '',
+ expectedResponse: '',
+ });
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0]).toBe(undefined);
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state);
+ });
+ });
+
+ describe('Update Async', () => {
+ const action = {
+ type: 'UPDATE_ASYNC',
+ payload: {
+ id: 3,
+ actionsFile: 'actionsFile',
+ typesFileName: 'typesFileName',
+ typesFilePath: 'typesFilePath',
+ asyncFunction: 'asyncFunction',
+ method: 'method',
+ route: 'route',
+ requestBody: 'requestBody',
+ store: 'store',
+ matcher: 'matcher',
+ expectedResponse: 'expectedResponse',
+ },
+ };
+
+ it('updates the values of the async/thunk test based on action payload id and string values', () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [],
+ hasRedux: 1,
+ };
+
+ state.reduxStatements.push({
+ id: 3,
+ type: 'async',
+ actionsFileName: '',
+ filePath: '',
+ typesFileName: '',
+ typesFilePath: '',
+ asyncFunction: '',
+ method: '',
+ route: '',
+ requestBody: '',
+ store: '',
+ matcher: '',
+ expectedResponse: '',
+ });
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].id).toEqual(3);
+ expect(reduxStatements[0].type).toEqual('async');
+ expect(reduxStatements[0].typesFileName).toEqual('typesFileName');
+ expect(reduxStatements[0].typesFilePath).toEqual('typesFilePath');
+ expect(reduxStatements[0].asyncFunction).toEqual('asyncFunction');
+ expect(reduxStatements[0].method).toEqual('method');
+ expect(reduxStatements[0].route).toEqual('route');
+ expect(reduxStatements[0].requestBody).toEqual('requestBody');
+ expect(reduxStatements[0].store).toEqual('store');
+ expect(reduxStatements[0].matcher).toEqual('matcher');
+ expect(reduxStatements[0].expectedResponse).toEqual('expectedResponse');
+ });
+
+ it('should return copy of state object', () => {
+ expect(subject(state, action)).not.toBe(state);
+ });
+ });
+ });
+
+//-----------Update Statments Order ------------>
+ describe('Update Statments Order', () => {
+ const action = {
+ type: 'UPDATE_STATEMENTS_ORDER',
+ payload: [
+ {
+ id: 1,
+ type: 'reducer',
+ typesFileName: '',
+ typesFilePath: '',
+ reducersFileName: '',
+ reducersFilePath: '',
+ reducerAction: '',
+ initialState: '',
+ reducerName: '',
+ expectedState: '',
+ },
+ {
+ id: 3,
+ type: 'async',
+ actionsFileName: '',
+ filePath: '',
+ typesFileName: '',
+ typesFilePath: '',
+ asyncFunction: '',
+ method: '',
+ route: '',
+ requestBody: '',
+ store: '',
+ matcher: '',
+ expectedResponse: '',
+ },
+ ]
+ };
+
+ it('should return the reorderd redux test statments', () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [
+ {
+ id: 3,
+ type: 'async',
+ actionsFileName: '',
+ filePath: '',
+ typesFileName: '',
+ typesFilePath: '',
+ asyncFunction: '',
+ method: '',
+ route: '',
+ requestBody: '',
+ store: '',
+ matcher: '',
+ expectedResponse: '',
+ },
+ {
+ id: 1,
+ type: 'reducer',
+ typesFileName: '',
+ typesFilePath: '',
+ reducersFileName: '',
+ reducersFilePath: '',
+ reducerAction: '',
+ initialState: '',
+ reducerName: '',
+ expectedState: '',
+ }
+ ],
+ hasRedux: 1,
+ };
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].id).toEqual(1);
+ expect(reduxStatements[1].id).toEqual(3);
+ });
+ });
+//----------- Update Types FilePath ------->
+ describe('Update Types File Path', () => {
+ const action = {
+ type: 'UPDATE_TYPES_FILEPATH',
+ payload: {
+ typesFileName: 'Hello',
+ typesFilePath: 'World',
+ id: 3,
+ }
+ };
+
+ it("should update the types's file path", () => {
+ state = {
+ reduxTestStatement: '',
+ reduxStatements: [
+ {
+ id: 3,
+ actionsFileName: '',
+ filePath: '',
+ typesFileName: '',
+ typesFilePath: '',
+ type: 'action-creator',
+ actionCreatorFunc: '',
+ actionType: '',
+ payloadKey: null,
+ payloadType: null,
+ }
+ ],
+ hasRedux: 1,
+ };
+
+ const { reduxStatements } = subject(state, action);
+
+ expect(reduxStatements[0].typesFileName).toEqual('Hello');
+ expect(reduxStatements[0].typesFilePath).toEqual('World');
+ expect(reduxStatements[0].id).toEqual(3);
+ });
+ });
+});
diff --git a/src/__tests__/spec.test.js b/src/__tests__/spec.test.js
index 361c9b41..17e918eb 100644
--- a/src/__tests__/spec.test.js
+++ b/src/__tests__/spec.test.js
@@ -7,7 +7,7 @@ describe('Application launch', function() {
beforeEach(() => {
this.app = new Application({
path: electronPath,
- args: [path.join(__dirname, '../../electron/main.js')],
+ args: [path.join(__dirname, '../../public/electron.js')],
});
return this.app.start();
});
diff --git a/src/assets/icons/add-black-18dp.svg b/src/assets/icons/add-black-18dp.svg
new file mode 100644
index 00000000..ca2fbd67
--- /dev/null
+++ b/src/assets/icons/add-black-18dp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/components/Action/Action.jsx b/src/components/Action/Action.jsx
new file mode 100644
index 00000000..1d0e9bda
--- /dev/null
+++ b/src/components/Action/Action.jsx
@@ -0,0 +1,141 @@
+import React, { useContext } from 'react';
+import styles from '../Action/Action.module.scss';
+import { deleteAction, updateAction } from '../../context/actions/reactTestCaseActions';
+import AutoComplete from '../AutoComplete/AutoComplete';
+import AutoCompleteMockData from '../AutoComplete/AutoCompleteMockData';
+import ToolTip from '../ToolTip/ToolTip';
+import { MockDataContext } from '../../context/reducers/mockDataReducer';
+import { ReactTestCaseContext } from '../../context/reducers/reactTestCaseReducer';
+const questionIcon = require('../../assets/images/help-circle.png');
+const closeIcon = require('../../assets/images/close.png');
+
+// Action box in middle panel (testCase.jsx)
+const Action = ({ statement, statementId, describeId, itId }) => {
+ const [{ mockData }] = useContext(MockDataContext);
+ const [{}, dispatchToReactTestCase] = useContext(ReactTestCaseContext);
+
+ const handleChangeActionFields = (e, field) => {
+ let updatedAction = { ...statement };
+ updatedAction[field] = e.target.value;
+ dispatchToReactTestCase(updateAction(updatedAction));
+ };
+
+ const handleClickDeleteAction = (e) => {
+ dispatchToReactTestCase(deleteAction(statement.id));
+ };
+ //conditional rendering for events with values
+ const needsEventValue = (eventType) => {
+ const eventsWithValues = [
+ 'keyDown',
+ 'keyPress',
+ 'keyUp',
+ 'change',
+ 'input',
+ 'invalid',
+ 'submit',
+ ];
+ return eventsWithValues.includes(eventType);
+ };
+
+ return (
+