diff --git a/README.md b/README.md index eda5a4219..1d673e74c 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ TestGrid strengthens the positioning of WSO2 products and adds major value to th Currently, we only have a crude list that simply detail names and descriptions though. [3] ------------------------ -Current TestGrid-Live dashboard can be found here - https://testgrid-live.private.wso2.com/testgrid/dashboard/ +Current TestGrid-Live dashboard can be found here - https://testgrid-live.private.wso2.com/ Once we have the TestGrid beta, * You will find the TestGrid home page here - https://testgrid.wso2.com diff --git a/web/src/main/java/org/wso2/testgrid/web/api/ProductService.java b/web/src/main/java/org/wso2/testgrid/web/api/ProductService.java index f759a1507..2708baa69 100644 --- a/web/src/main/java/org/wso2/testgrid/web/api/ProductService.java +++ b/web/src/main/java/org/wso2/testgrid/web/api/ProductService.java @@ -122,11 +122,11 @@ public Response getAllProductStatuses() { try { for (Product product : productUOW.getProducts()) { ProductStatus status = new ProductStatus(); - status.setId(product.getId()); - status.setName(StringUtil.concatStrings(product.getName())); + status.setProductId(product.getId()); + status.setProductName(product.getName()); status.setLastfailed(APIUtil.getTestPlanBean(testPlanUOW.getLastFailure(product), false)); status.setLastBuild(APIUtil.getTestPlanBean(testPlanUOW.getLastBuild(product), false)); - status.setStatus(testPlanUOW.getCurrentStatus(product).toString()); + status.setProductStatus(testPlanUOW.getCurrentStatus(product).toString()); list.add(status); } } catch (TestGridDAOException e) { @@ -138,6 +138,46 @@ public Response getAllProductStatuses() { return Response.status(Response.Status.OK).entity(list).build(); } + /** + * This method returns the product if the requested product name exists in the TestGrid. + * + *

The product is returned as a json response with the last build information and + * the last failed build information

+ * + * @return product + */ + @GET + @Path("/product-status/{productName}") + public Response getProductStatus( + @PathParam("productName") String productName) { + try { + TestPlanUOW testPlanUOW = new TestPlanUOW(); + ProductUOW productUOW = new ProductUOW(); + ProductStatus productStatus = new ProductStatus(); + Optional productInstance = productUOW.getProduct(productName); + Product product; + if (productInstance.isPresent()) { + product = productInstance.get(); + productStatus.setProductId(product.getId()); + productStatus.setProductName(product.getName()); + productStatus.setLastfailed(APIUtil.getTestPlanBean(testPlanUOW.getLastFailure(product), false)); + productStatus.setLastBuild(APIUtil.getTestPlanBean(testPlanUOW.getLastBuild(product), false)); + productStatus.setProductStatus(testPlanUOW.getCurrentStatus(product).toString()); + } else { + String msg = "Could not found the product:" + productName + " in TestGrid. Please check the " + + "infrastructure_parameter table"; + logger.error(msg); + return Response.status(Response.Status.NOT_FOUND).entity(msg).build(); + } + return Response.status(Response.Status.OK).entity(productStatus).build(); + } catch (TestGridDAOException e) { + String msg = "Error occurred while fetching the statuses of the product: " + productName + ". Please " + + "check the database configurations"; + logger.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + } + /** * This method is able to get the latest report of a given product from the remote storage and return. *

@@ -149,9 +189,9 @@ public Response getAllProductStatuses() { @Path("/reports") @Produces(MediaType.APPLICATION_OCTET_STREAM) public Response getProductReport( - @QueryParam("product-name") String productName, - @DefaultValue("false") @QueryParam("show-success") Boolean showSuccess, - @DefaultValue("SCENARIO") @QueryParam("group-by") String groupBy) { + @QueryParam("product-name") String productName, + @DefaultValue("false") @QueryParam("show-success") Boolean showSuccess, + @DefaultValue("SCENARIO") @QueryParam("group-by") String groupBy) { try { ProductUOW productUOW = new ProductUOW(); Optional productInstance = productUOW.getProduct(productName); diff --git a/web/src/main/java/org/wso2/testgrid/web/bean/ProductStatus.java b/web/src/main/java/org/wso2/testgrid/web/bean/ProductStatus.java index 9a0cf594e..290f1109e 100644 --- a/web/src/main/java/org/wso2/testgrid/web/bean/ProductStatus.java +++ b/web/src/main/java/org/wso2/testgrid/web/bean/ProductStatus.java @@ -1,72 +1,72 @@ /* -* Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ + * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package org.wso2.testgrid.web.bean; /** - * Bean class for Aggregated product status reponses. + * Bean class for Aggregated product productStatus reponses. * * @since 1.0.0 */ public class ProductStatus { - private String id; - private String name; + private String productId; + private String productName; private String reportLink; - private String status; + private String productStatus; private TestPlan lastBuild; private TestPlan lastfailed; private String executeLink; private String configLink; /** - * Returns the id of the product. + * Returns the productId of the product. * - * @return id of the product + * @return productId of the product */ - public String getId() { - return id; + public String getProductId() { + return productId; } /** - * Sets the id of the product. + * Sets the productId of the product. * - * @param id product-id + * @param productId product-productId */ - public void setId(String id) { - this.id = id; + public void setProductId(String productId) { + this.productId = productId; } /** - * Returns the name of product. + * Returns the productName of product. * - * @return product name + * @return product productName */ - public String getName() { - return name; + public String getProductName() { + return productName; } /** - * Sets the name of product. + * Sets the productName of product. * - * @param name name to set to product + * @param productName productName to set to product */ - public void setName(String name) { - this.name = name; + public void setProductName(String productName) { + this.productName = productName; } /** @@ -90,21 +90,21 @@ public void setReportLink(String reportLink) { /** * - * Returns the status of product. + * Returns the productStatus of product. * - * @return the status of product + * @return the productStatus of product */ - public String getStatus() { - return status; + public String getProductStatus() { + return productStatus; } /** - * Sets the status of the product. + * Sets the productStatus of the product. * - * @param status the status of product + * @param productStatus the productStatus of product */ - public void setStatus(String status) { - this.status = status; + public void setProductStatus(String productStatus) { + this.productStatus = productStatus; } /** diff --git a/web/src/main/java/org/wso2/testgrid/web/sso/SSOSessionCheckFilter.java b/web/src/main/java/org/wso2/testgrid/web/sso/SSOSessionCheckFilter.java index 9370a9843..65551ff24 100644 --- a/web/src/main/java/org/wso2/testgrid/web/sso/SSOSessionCheckFilter.java +++ b/web/src/main/java/org/wso2/testgrid/web/sso/SSOSessionCheckFilter.java @@ -76,6 +76,7 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo } else { httpResponse.sendRedirect(ssoLoginUrl); } + return; } } } diff --git a/web/src/main/java/org/wso2/testgrid/web/utils/Constants.java b/web/src/main/java/org/wso2/testgrid/web/utils/Constants.java index 969975c6f..42914f295 100644 --- a/web/src/main/java/org/wso2/testgrid/web/utils/Constants.java +++ b/web/src/main/java/org/wso2/testgrid/web/utils/Constants.java @@ -30,7 +30,7 @@ public class Constants extends TestGridConstants { public static final String JENKINS_CRUMB_ISSUER_URI = "/crumbIssuer/api/json"; public static final String CRUMB = "crumb"; public static final String JENKINS_CRUMB_HEADER_NAME = "Jenkins-Crumb"; - public static final String WEBAPP_CONTEXT = "/testgrid/dashboard"; + public static final String WEBAPP_CONTEXT = "/"; /* Terms used in Jenkins template job. */ public static final String PRODUCT_NAME = "$productName"; @@ -44,12 +44,12 @@ public class Constants extends TestGridConstants { public static final String SCENARIOS_LOCATION = "$scenariosLocation"; /* Constants relates to SSO configurations. */ - public static final String LOGIN_URI = WEBAPP_CONTEXT + "/login"; - public static final String STATIC_DATA_URI = WEBAPP_CONTEXT + "/static"; - public static final String ACS_URI = WEBAPP_CONTEXT + "/api/acs"; + public static final String LOGIN_URI = WEBAPP_CONTEXT + "login"; + public static final String STATIC_DATA_URI = WEBAPP_CONTEXT + "static"; + public static final String ACS_URI = WEBAPP_CONTEXT + "api/acs"; public static final String JKS_FILE_NAME = "wso2carbon.jks"; public static final String SSO_DIRECTORY = "SSO"; - public static final String BACKEND_API_URI = WEBAPP_CONTEXT + "/api/"; + public static final String BACKEND_API_URI = WEBAPP_CONTEXT + "api/"; public static final String PROPERTYNAME_KEYSTORE_PASSWORD = "KeyStorePassword"; public static final String PROPERTYNAME_PRIVATE_KEY_ALIAS = "PrivateKeyAlias"; diff --git a/web/src/main/react-dashboard/package.json b/web/src/main/react-dashboard/package.json index bb9d839e0..1d2fb9540 100644 --- a/web/src/main/react-dashboard/package.json +++ b/web/src/main/react-dashboard/package.json @@ -2,7 +2,7 @@ "name": "testgrid-dashboard", "version": "0.1.0", "private": true, - "homepage": "/testgrid/dashboard", + "homepage": "/", "dependencies": { "bootstrap": "^4.1.0", "downloadjs": "^1.4.7", diff --git a/web/src/main/react-dashboard/src/App.js b/web/src/main/react-dashboard/src/App.js index 0950afcec..ad1951ed3 100644 --- a/web/src/main/react-dashboard/src/App.js +++ b/web/src/main/react-dashboard/src/App.js @@ -16,7 +16,7 @@ * under the License. */ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import './App.css'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import ProductContainer from './containers/productContainer.js'; @@ -28,14 +28,14 @@ import testRunContainer from './containers/testRunContainer.js'; import Login from './components/Login.js' import { Route, - Switch, + Switch } from 'react-router-dom'; import AppBar from 'material-ui/AppBar'; -import { createStore } from 'redux'; -import { Provider } from 'react-redux'; +import {createStore} from 'redux'; +import {Provider} from 'react-redux'; import testGrid from './reducers/testGrid.js'; -import { persistStore, persistCombineReducers } from 'redux-persist'; -import { PersistGate } from 'redux-persist/es/integration/react'; +import {persistStore, persistCombineReducers} from 'redux-persist'; +import {PersistGate} from 'redux-persist/es/integration/react'; import storage from 'redux-persist/es/storage'; import Drawer from 'material-ui/Drawer'; import MenuItem from 'material-ui/MenuItem'; @@ -57,12 +57,11 @@ class App extends Component { handleClose = () => { var b = !this.state.open; var w = b ? 240 : 20; - this.setState({ open: b, navWidth: w }); + this.setState({open: b, navWidth: w}); }; constructor(props) { super(props); - this.baseURL = "/testgrid/dashboard" this.state = { open: false, navWidth: 20 @@ -70,28 +69,40 @@ class App extends Component { } render() { - const paperStyle = { margin: '80px 20px 50px ' + this.state.navWidth + 'px' }; + const paperStyle = {margin: '80px 20px 50px ' + this.state.navWidth + 'px'}; return ( -

+
{this.state.open ? : }}> - - TestGrid AdminPortal + backgroundColor: '#424242', position: 'fixed' + }} + iconElementLeft={{this.state.open ? : + }}> + + TestGrid + AdminPortal - - - - - - - + + + + + + +
@@ -100,7 +111,6 @@ class App extends Component { ); } - } export default App; diff --git a/web/src/main/react-dashboard/src/actions/testGridActions.js b/web/src/main/react-dashboard/src/actions/testGridActions.js index 968e985b2..8ea39f951 100644 --- a/web/src/main/react-dashboard/src/actions/testGridActions.js +++ b/web/src/main/react-dashboard/src/actions/testGridActions.js @@ -17,22 +17,22 @@ */ export const add_current_product = (product) => ({ - type: "ADD_CURRENT_PRODUCT", - product -}) + type: "ADD_CURRENT_PRODUCT", + product +}); export const add_current_deployment = (deployment) => ({ - type: "ADD_CURRENT_DEPLOYMENT", - deployment -}) + type: "ADD_CURRENT_DEPLOYMENT", + deployment +}); export const add_current_infra = (infra) => ({ - type: "ADD_CURRENT_INFRA", - infra -}) + type: "ADD_CURRENT_INFRA", + infra +}); export const add_current_scenario = (scenario) => ({ - type: "ADD_CURRENT_SCENARIO", - scenario + type: "ADD_CURRENT_SCENARIO", + scenario }) diff --git a/web/src/main/react-dashboard/src/components/DeploymentPatternView.js b/web/src/main/react-dashboard/src/components/DeploymentPatternView.js index 00334e09c..d996f37cf 100644 --- a/web/src/main/react-dashboard/src/components/DeploymentPatternView.js +++ b/web/src/main/react-dashboard/src/components/DeploymentPatternView.js @@ -16,55 +16,74 @@ * under the License. */ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import '../App.css'; -import { - Table, - TableBody, - TableHeader, - TableHeaderColumn, - TableRow, - TableRowColumn, -} from 'material-ui/Table'; import Subheader from 'material-ui/Subheader'; import SingleRecord from './SingleRecord.js'; import {add_current_deployment, add_current_infra} from '../actions/testGridActions.js'; -import Moment from 'moment' import ReactTooltip from 'react-tooltip' import FlatButton from 'material-ui/FlatButton'; -import {FAIL, SUCCESS, ERROR, PENDING, RUNNING, HTTP_UNAUTHORIZED, LOGIN_URI, TESTGRID_CONTEXT} from '../constants.js'; +import {HTTP_UNAUTHORIZED, LOGIN_URI, TESTGRID_CONTEXT} from '../constants.js'; +import {Table} from 'reactstrap'; class DeploymentPatternView extends Component { constructor(props) { - super(props) + super(props); this.state = { - hits: [] + hits: [], + product: null }; } handleError(response) { - if (response.status.toString() === HTTP_UNAUTHORIZED) { - window.location.replace(LOGIN_URI); - } else if(!response.ok){ - throw Error(response.statusText) - } - return response; + if (response.status.toString() === HTTP_UNAUTHORIZED) { + window.location.replace(LOGIN_URI); + } else if (!response.ok) { + throw Error(response.statusText) + } + return response; } componentDidMount() { - var url = TESTGRID_CONTEXT + '/api/test-plans/product/' + this.props.active.reducer.currentProduct.productId; + let currentProduct = this.props.active.reducer.currentProduct; + if (!currentProduct) { + let url = TESTGRID_CONTEXT + '/api/products/product-status/' + window.location.href.split("/").pop(); + fetch(url, { + method: "GET", + credentials: 'same-origin', + headers: { + 'Accept': 'application/json' + } + }).then(this.handleError) + .then(response => { + return response.json(); + }) + .then(data => { + currentProduct = data; + return currentProduct; + }) + .then(currentProduct => this.getDeploymentDetails(currentProduct)) + .catch(error => console.error(error)); + } else { + this.getDeploymentDetails(currentProduct); + } + } + + getDeploymentDetails(currentProduct) { + let url = TESTGRID_CONTEXT + '/api/test-plans/product/' + currentProduct.productId; fetch(url, { method: "GET", credentials: 'same-origin', headers: { 'Accept': 'application/json' } - }) - .then(this.handleError) - .then(response => { return response.json()}) - .then(data => this.setState({ hits: data })) - .catch(error => console.error(error)); + }).then(this.handleError) + .then(response => { + return response.json() + }) + .then(data => this.setState({hits: data, product: currentProduct})) + .catch(error => console.error(error)); } navigateToRoute(route, deployment, testPlan) { @@ -84,339 +103,365 @@ class DeploymentPatternView extends Component { var groupByDeployment = {}; this.state.hits.map((value, index) => { - if (groupByDeployment[value.lastBuild.deploymentPattern] == undefined) { - groupByDeployment[value.lastBuild.deploymentPattern] = [{ 'lastBuild': value.lastBuild, 'lastFailed': value.lastFailure }] + if (groupByDeployment[value.lastBuild.deploymentPattern] === undefined) { + groupByDeployment[value.lastBuild.deploymentPattern] = [{ + 'lastBuild': value.lastBuild, 'lastFailed': + value.lastFailure + }] } else { - groupByDeployment[value.lastBuild.deploymentPattern].push({ 'lastBuild': value.lastBuild, 'lastFailed': value.lastFailure }) + groupByDeployment[value.lastBuild.deploymentPattern].push({ + 'lastBuild': value.lastBuild, 'lastFailed': + value.lastFailure + }) } - }) + }); return (
- {(() => { - switch (this.props.active.reducer.currentProduct.productStatus) { + {this.state && this.state.product && (() => { + switch (this.state.product.productStatus) { case FAIL: return - +
- - - {this.props.active.reducer.currentProduct.productName + " "} - + + + {this.state.product.productName + " "} + -
- -
+ +
+
; case SUCCESS : return - +
- - - {this.props.active.reducer.currentProduct.productName + " "} - + + + {this.state.product.productName + " "} + + +
- -
+ +
+
; + case ERROR : + return + + + + + {this.state.product.productName + " "} + + +
+ +
+
; + case INCOMPLETE : + return + + + + + {this.state.product.productName + " "} + + +
+ +
+
; + case DID_NOT_RUN : + return + + + + + {this.state.product.productName + " "} + -
+ +
+
; - case ERROR : - return - - - - - {this.props.active.reducer.currentProduct.productName + " "} - - -
- -
-
; - case INCOMPLETE : - return - - - - - {this.props.active.reducer.currentProduct.productName + " "} - - -
- -
-
; - case DID_NOT_RUN : - return - - - - - {this.props.active.reducer.currentProduct.productName + " "} - - -
- -
-
; case PENDING: - return - - - - - {this.props.active.reducer.currentProduct.productName + " "} - - -
- -
-
; + return + + + + + {this.state.product.productName + " "} + + +
+ +
+
; case RUNNING: default: return - +
- - - {this.props.active.reducer.currentProduct.productName + " "} - + + + {this.state.product.productName + " "} + -
- -
+ +
+
; } })()} - - - -

Deployment Pattern

-

Infra Combination

-

Last Build

-

Last Failure

-

Execute

-
- - -

OS

-

Database

-

JDK

-
-
-
- - {Object.keys(groupByDeployment).map((key) => { - return ( - groupByDeployment[key].map((value, index) => { - var infraParameters = JSON.parse(value.lastBuild.infraParams); - if (index == 0) { - return ( - - {key} - - this.navigateToRoute(TESTGRID_CONTEXT + "/testplans/history/" - + value.lastBuild.id, { - deploymentPatternName: key - }, { - testPlanId: value.lastBuild.id, - infraParameters: value.lastBuild.infraParams, - testPlanStatus: value.lastBuild.status - })}> -

- {infraParameters.OS} {infraParameters.OSVersion} -

-

- {infraParameters.DBEngine} {infraParameters.DBEngineVersion} -

-

- {infraParameters.JDK} -

-
- -
- - this.navigateToRoute(TESTGRID_CONTEXT + "/testplans/" + value.lastBuild.id, { - deploymentPatternName: key - }, { - testPlanId: value.lastBuild.id, - infraParameters: value.lastBuild.infraParams, - testPlanStatus: value.lastBuild.status - })}> - - - - - {(() => { - if (value.lastFailed.modifiedTimestamp) { - return ( - this.navigateToRoute(TESTGRID_CONTEXT + "/testplans/" - + value.lastFailed.id, { deploymentPatternName: key }, - { - testPlanId: value.lastFailed.id, - infraParameters: value.lastFailed.infraParams, - testPlanStatus: value.lastFailed.status - } - )}> - - - ); - } else { - return ( - No failed builds yet! - ) - } - })()} - - { window.location = '/job/'+ this.props.active.reducer.currentProduct.productName +'/build' }} /> -
- ) - } else { - return ( - - - this.navigateToRoute(TESTGRID_CONTEXT + "/testplans/history/" + value.lastBuild.id, { - deploymentPatternName: key - }, { - testPlanId: value.lastBuild.id, - infraParameters: value.lastBuild.infraParams, - testPlanStatus: value.lastBuild.status - })}> -

- {infraParameters.OS} {infraParameters.OSVersion} -

-

- {infraParameters.DBEngine} {infraParameters.DBEngineVersion} -

-

- {infraParameters.JDK} -

-
- -
- - this.navigateToRoute(TESTGRID_CONTEXT + "/testplans/" + value.lastBuild.id, { - deploymentPatternName: key - }, { - testPlanId: value.lastBuild.id, - infraParameters: value.lastBuild.infraParams, - testPlanStatus: value.lastBuild.status - })}> - - - - - {(() => { - if (value.lastFailed.modifiedTimestamp) { - return ( - this.navigateToRoute(TESTGRID_CONTEXT + "/testplans/" + value.lastFailed.id, - { deploymentPatternName: key }, - { - testPlanId: value.lastFailed.id, - infraParameters: value.lastFailed.infraParams, - testPlanStatus: value.lastFailed.status - } - )}> - - - ); - } else { - return ( - No failed builds yet! - ) - } - })()} - - { window.location = '/job/'+ this.props.active.reducer.currentProduct.productName +'/build' }} /> -
- ) - } - }) - ) - })} -
+
+ + + + + + + + + + + + + + {this.state && this.state.product && Object.keys(groupByDeployment).map((key) => { + return ( + groupByDeployment[key].map((value, index) => { + var infraParameters = JSON.parse(value.lastBuild.infraParams); + if (index === 0) { + return ( + + + + + + + + ) + } else { + return ( + + + + + + + ) + } + }) + ) + })} +
Deployment PatternInfra CombinationLast BuildLast FailureExecute
+

OS

+

Database

+

JDK

+
{key} + this.navigateToRoute(TESTGRID_CONTEXT + "/" + + this.state.product.productName + "/" + key + "/" + + value.lastBuild.id + + "/infra", {deploymentPatternName: key}, { + testPlanId: value.lastBuild.id, + infraParameters: value.lastBuild.infraParams, + testPlanStatus: value.lastBuild.status + })}> +

+ {infraParameters.OS} {infraParameters.OSVersion} +

+

+ {infraParameters.DBEngine} {infraParameters.DBEngineVersion} +

+

+ {infraParameters.JDK} +

+
+ +
+ this.navigateToRoute(TESTGRID_CONTEXT + "/" + + this.state.product.productName + "/" + key + "/test-plans/" + + value.lastBuild.id, { + deploymentPatternName: key + }, { + testPlanId: value.lastBuild.id, + infraParameters: value.lastBuild.infraParams, + testPlanStatus: value.lastBuild.status + })}> + + + + {(() => { + if (value.lastFailed.modifiedTimestamp) { + return ( + this.navigateToRoute(TESTGRID_CONTEXT + + "/" + this.state.product.productName + "/" + key + + "/test-plans/" + value.lastFailed.id, + {deploymentPatternName: key}, { + testPlanId: value.lastFailed.id, + infraParameters: value.lastFailed.infraParams, + testPlanStatus: value.lastFailed.status + } + )}> + + + ); + } else { + return ( + No failed builds yet! + + ) + } + })()} + { + window.location = '/admin/job/' + + this.state.product.productName + '/build' + }}/> +
+ this.navigateToRoute(TESTGRID_CONTEXT + "/" + + this.state.product.productName + "/" + key + "/" + + value.lastBuild.id + "/infra", + {deploymentPatternName: key}, { + testPlanId: value.lastBuild.id, + infraParameters: value.lastBuild.infraParams, + testPlanStatus: value.lastBuild.status + })}> +

+ {infraParameters.OS} {infraParameters.OSVersion} +

+

+ {infraParameters.DBEngine} {infraParameters.DBEngineVersion} +

+

+ {infraParameters.JDK} +

+
+ +
+ this.navigateToRoute(TESTGRID_CONTEXT + "/" + + this.state.product.productName + "/" + key + "/test-plans/" + + value.lastFailed.id, { + deploymentPatternName: key + }, { + testPlanId: value.lastBuild.id, + infraParameters: value.lastBuild.infraParams, + testPlanStatus: value.lastBuild.status + })}> + + + + {(() => { + if (value.lastFailed.modifiedTimestamp) { + return ( + this.navigateToRoute(TESTGRID_CONTEXT + + "/" + this.state.product.productName + "/" + key + + "/test-plans/" + value.lastFailed.id, + {deploymentPatternName: key}, + { + testPlanId: value.lastFailed.id, + infraParameters: value.lastFailed.infraParams, + testPlanStatus: value.lastFailed.status + } + )}> + + + ); + } else { + return ( + + No failed builds yet! + + ) + } + })()} + { + window.location = + '/admin/job/' + this.state.product.productName + '/build' + }}/> +
); } } -export default DeploymentPatternView; \ No newline at end of file +export default DeploymentPatternView; diff --git a/web/src/main/react-dashboard/src/components/InfraCombinationView.js b/web/src/main/react-dashboard/src/components/InfraCombinationView.js index 07ee242b9..2c168d521 100644 --- a/web/src/main/react-dashboard/src/components/InfraCombinationView.js +++ b/web/src/main/react-dashboard/src/components/InfraCombinationView.js @@ -16,16 +16,8 @@ * under the License. */ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import '../App.css'; -import { - Table, - TableBody, - TableHeader, - TableHeaderColumn, - TableRow, - TableRowColumn, -} from 'material-ui/Table'; import Subheader from 'material-ui/Subheader'; import SingleRecord from './SingleRecord.js'; import {add_current_infra, add_current_deployment} from '../actions/testGridActions.js'; @@ -33,283 +25,309 @@ import FlatButton from 'material-ui/FlatButton'; import Divider from 'material-ui/Divider'; import Moment from 'moment'; import {FAIL, SUCCESS, ERROR, PENDING, RUNNING, HTTP_UNAUTHORIZED, LOGIN_URI, TESTGRID_CONTEXT, DID_NOT_RUN, INCOMPLETE} - from '../constants.js'; + from '../constants.js'; +import {Table} from 'reactstrap'; + class InfraCombinationView extends Component { - constructor(props) { - super(props); - this.state = { - hits: [] - } + constructor(props) { + super(props); + this.state = { + hits: [], + currentInfra: null } + } - handleError(response) { - if (response.status.toString() === HTTP_UNAUTHORIZED) { - window.location.replace(LOGIN_URI); - } else if(!response.ok){ - throw Error(response.statusText) - } - return response; + handleError(response) { + if (response.status.toString() === HTTP_UNAUTHORIZED) { + window.location.replace(LOGIN_URI); + } else if (!response.ok) { + throw Error(response.statusText) } + return response; + } - navigateToRoute(route, deployment, testPlan) { - this.props.dispatch(add_current_deployment(deployment)); - this.props.dispatch(add_current_infra(testPlan)); - this.props.history.push(route); - } + navigateToRoute(route, deployment, testPlan) { + this.props.dispatch(add_current_deployment(deployment)); + this.props.dispatch(add_current_infra(testPlan)); + this.props.history.push(route); + } - componentDidMount() { - var url = TESTGRID_CONTEXT + "/api/test-plans/history/" + this.props.active.reducer.currentInfra.testPlanId; - fetch(url, { - method: "GET", - credentials: 'same-origin', - headers: { - 'Accept': 'application/json' - } - }) - .then(this.handleError) - .then(response => { return response.json()} ) - .then(data => this.setState({ hits: data })) - .catch(error => console.error(error)); - } - render() { - return ( -
- {(() => { - switch (this.props.active.reducer.currentProduct.productStatus) { - case FAIL: - return - - - - - {this.props.active.reducer.currentProduct.productName} / - {this.props.active.reducer.currentDeployment.deploymentPatternName}
- {this.props.active.reducer.currentInfra.infraParameters} -
- - -
- -
-
; - case SUCCESS: - return - - - - - {this.props.active.reducer.currentProduct.productName} / - {this.props.active.reducer.currentDeployment.deploymentPatternName}
- {this.props.active.reducer.currentInfra.infraParameters} -
- - -
- -
-
; - case PENDING: - return - - - - - {this.props.active.reducer.currentProduct.productName} / - {this.props.active.reducer.currentDeployment.deploymentPatternName}
- {this.props.active.reducer.currentInfra.infraParameters} -
- - -
- -
-
; - case DID_NOT_RUN: - return - - - - - {this.props.active.reducer.currentProduct.productName} / - {this.props.active.reducer.currentDeployment.deploymentPatternName}
- {this.props.active.reducer.currentInfra.infraParameters} -
- - -
- -
-
; - case INCOMPLETE: - return - - - - - {this.props.active.reducer.currentProduct.productName} / - {this.props.active.reducer.currentDeployment.deploymentPatternName}
- {this.props.active.reducer.currentInfra.infraParameters} -
- - -
- -
-
; - case ERROR: - return - - - - - {this.props.active.reducer.currentProduct.productName} / - {this.props.active.reducer.currentDeployment.deploymentPatternName}
- {this.props.active.reducer.currentInfra.infraParameters} -
- - -
- -
-
; - case RUNNING: - default: - return - - - - - {this.props.active.reducer.currentProduct.productName} / - {this.props.active.reducer.currentDeployment.deploymentPatternName}
- {this.props.active.reducer.currentInfra.infraParameters} -
- - -
- -
-
; - } - })()} - - - - -

#

-

Status

-

Date

-

Duration

-
-
- + componentDidMount() { + let currentUrl = window.location.href.split("/"); + let url = TESTGRID_CONTEXT + "/api/test-plans/history/" + currentUrl[currentUrl.length - 2]; + let currentInfra; + fetch(url, { + method: "GET", + credentials: 'same-origin', + headers: { + 'Accept': 'application/json' + } + }) + .then(this.handleError) + .then(response => { + return response.json() + }) + .then(data => { + let currentUrl = window.location.href.split("/"); + if (this.props.active.reducer.currentInfra) { + currentInfra = this.props.active.reducer.currentInfra; + } else { + currentInfra = {}; + currentInfra.testPlanId = data[0].id; + currentInfra.infraParameters = data[0].infraParams; + currentInfra.testPlanStatus = data[0].status; + this.props.active.reducer.currentInfra = currentInfra; + } + currentInfra.relatedProduct = currentUrl[currentUrl.length - 4]; + currentInfra.relatedDeplymentPattern = currentUrl[currentUrl.length - 3]; + this.setState({hits: data, currentInfra: currentInfra}); + }) + .catch(error => console.error(error)); + } - {this.state.hits - .sort((a, b) => b.createdTimestamp - a.createdTimestamp) - .map((data, index) => { - return ( - {index + 1} - - this.navigateToRoute(TESTGRID_CONTEXT + "/testplans/" + data.id, { - deploymentPatternName: this.props.active.reducer.currentDeployment.deploymentPatternName - }, { - testPlanId: data.id, - infraParameters: data.infraParams, - testPlanStatus: data.status - })}> - - - - {Moment(data.createdTimestamp).calendar()} - - {(() => { - var start = Moment(data.createdTimestamp); - var end = Moment(data.modifiedTimestamp); - var min = end.diff(start, 'minutes'); - var hours = Math.floor(min / 60); - var minutes = min % 60; - if (hours > 0) { - return ( -

{hours} Hours {minutes} Minutes

- ) - } else { - return ( -

{minutes} Minutes

- ) - } - })()} -
-
) - })} -
+ render() { + return ( +
+ {this.state && this.state.currentInfra && (() => { + switch (this.state.currentInfra.testPlanStatus) { + case FAIL: + return +
+ + + + {this.state.currentInfra.relatedProduct} / + {this.state.currentInfra.relatedDeplymentPattern}
+ {this.state.currentInfra.infraParameters} +
+ +
+ +
-
- ); - } + ; + case SUCCESS: + return + + + + + {this.state.currentInfra.relatedProduct} / + {this.state.currentInfra.relatedDeplymentPattern}
+ {this.state.currentInfra.infraParameters} +
+ + +
+ +
+
; + case PENDING: + return + + + + + {this.state.currentInfra.relatedProduct} / + {this.state.currentInfra.relatedDeplymentPattern}
+ {this.state.currentInfra.infraParameters} +
+ + +
+ +
+
; + case DID_NOT_RUN: + return + + + + + {this.state.currentInfra.relatedProduct} / + {this.state.currentInfra.relatedDeplymentPattern}
+ {this.state.currentInfra.infraParameters} +
+ + +
+ +
+
; + case INCOMPLETE: + return + + + + + {this.state.currentInfra.relatedProduct} / + {this.state.currentInfra.relatedDeplymentPattern}
+ {this.state.currentInfra.infraParameters} +
+ + +
+ +
+
; + case ERROR: + return + + + + + {this.state.currentInfra.relatedProduct} / + {this.state.currentInfra.relatedDeplymentPattern}
+ {this.state.currentInfra.infraParameters} +
+ + +
+ +
+
; + case RUNNING: + default: + return + + + + + {this.state.currentInfra.relatedProduct} / + {this.state.currentInfra.relatedDeplymentPattern}
+ {this.state.currentInfra.infraParameters} +
+ + +
+ +
+
; + } + })()} + + + + + + + + + + + + + {this.state.hits + .sort((a, b) => b.createdTimestamp - a.createdTimestamp) + .map((data, index) => { + return ( + + + + + ) + })} + +
#StatusDateDuration
{index + 1} + this.navigateToRoute(TESTGRID_CONTEXT + "/" + + this.state.currentInfra.relatedProduct + "/" + + this.state.currentInfra.relatedDeplymentPattern + "/test-plans/" + + data.id, { + deploymentPatternName: + this.state.currentInfra.relatedProduct + }, { + testPlanId: data.id, + infraParameters: data.infraParams, + testPlanStatus: data.status + })}> + + + {Moment(data.createdTimestamp).calendar()} + {(() => { + let start = Moment(data.createdTimestamp); + let end = Moment(data.modifiedTimestamp); + let min = end.diff(start, 'minutes'); + let hours = Math.floor(min / 60); + let minutes = min % 60; + if (hours > 0) { + return ( +

{hours} Hours {minutes} Minutes

+ ) + } else { + return ( +

{minutes} Minutes

+ ) + } + })()} +
+
+ ); + } } export default InfraCombinationView; diff --git a/web/src/main/react-dashboard/src/components/Login.js b/web/src/main/react-dashboard/src/components/Login.js index 1795c57f0..3a43a5c84 100644 --- a/web/src/main/react-dashboard/src/components/Login.js +++ b/web/src/main/react-dashboard/src/components/Login.js @@ -16,19 +16,19 @@ * under the License. */ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import '../App.css'; class Login extends Component { - render() { - return ( -
- -

Click here to login.

-
- ); - } + render() { + return ( +
+ +

Click here to login.

+
+ ); + } } export default Login; diff --git a/web/src/main/react-dashboard/src/components/ProductStatusView.js b/web/src/main/react-dashboard/src/components/ProductStatusView.js index a63371677..286aa289e 100644 --- a/web/src/main/react-dashboard/src/components/ProductStatusView.js +++ b/web/src/main/react-dashboard/src/components/ProductStatusView.js @@ -16,14 +16,14 @@ * under the License. */ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import '../App.css'; import SingleRecord from './SingleRecord.js'; import {add_current_product} from '../actions/testGridActions.js'; import Moment from 'moment' import ReactTooltip from 'react-tooltip'; import {HTTP_OK, HTTP_NOT_FOUND, HTTP_UNAUTHORIZED, LOGIN_URI, TESTGRID_CONTEXT} from '../constants.js'; -import { Button, Table, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap'; +import {Button, Table, Modal, ModalHeader, ModalBody, ModalFooter} from 'reactstrap'; class ProductStatusView extends Component { @@ -31,29 +31,29 @@ class ProductStatusView extends Component { super(props); this.state = { hits: [], - modal: false, - errorMassage : "" + modal: false, + errorMassage: "" }; - this.toggle = this.toggle.bind(this); + this.toggle = this.toggle.bind(this); } - handleError(response){ + handleError(response) { if (response.status.toString() === HTTP_UNAUTHORIZED) { - window.location.replace(LOGIN_URI); - } else if(!response.ok){ + window.location.replace(LOGIN_URI); + } else if (!response.ok) { throw Error(response.statusText) } return response; } - toggle(Message) { - this.setState({ - modal: !this.state.modal, - errorMassage : Message - }); - } + toggle(Message) { + this.setState({ + modal: !this.state.modal, + errorMassage: Message + }); + } componentDidMount() { var url = TESTGRID_CONTEXT + '/api/products/product-status'; @@ -64,10 +64,12 @@ class ProductStatusView extends Component { 'Accept': 'application/json' } }) - .then(this.handleError) - .then(response => {return response.json()}) - .then(data => this.setState({ hits: data })) - .catch(error => console.error(error)); + .then(this.handleError) + .then(response => { + return response.json() + }) + .then(data => this.setState({hits: data})) + .catch(error => console.error(error)); } navigateToRoute(route, product) { @@ -75,111 +77,117 @@ class ProductStatusView extends Component { this.props.history.push(route); } - downloadReport(productName) { - let url = TESTGRID_CONTEXT + '/api/products/reports?product-name=' + productName; - fetch(url, { - method: "GET", - credentials: 'same-origin', - headers: { - 'Accept': 'application/html' - } - }).then(response => { - if (response.status === HTTP_NOT_FOUND) { - let errorMessage = "Unable to locate report in the remote storage, please contact the administrator."; - this.toggle(errorMessage); - } else if (response.status !== HTTP_OK){ - let errorMessage = "Internal server error. Couldn't download the report at the moment, please " + - "contact the administrator."; - this.toggle(errorMessage); - } - } - ).catch(error => console.error(error)); - } + downloadReport(productName) { + let url = TESTGRID_CONTEXT + '/api/products/reports?product-name=' + productName; + fetch(url, { + method: "GET", + credentials: 'same-origin', + headers: { + 'Accept': 'application/html' + } + }).then(response => { + if (response.status === HTTP_NOT_FOUND) { + let errorMessage = "Unable to locate report in the remote storage, please contact the administrator."; + this.toggle(errorMessage); + } else if (response.status !== HTTP_OK) { + let errorMessage = "Internal server error. Couldn't download the report at the moment, please " + + "contact the administrator."; + this.toggle(errorMessage); + } + } + ).catch(error => console.error(error)); + } render() { const products = this.state.hits.map((product, index) => { return ( - - this.navigateToRoute( TESTGRID_CONTEXT+ "/deployments/product/" + product.id, { - productId: product.id, - productName: product.name, - productStatus :product.status + + this.navigateToRoute(TESTGRID_CONTEXT + "/" + product.productName, { + productId: product.productId, + productName: product.productName, + productStatus: product.productStatus })} scope="row"> - {product.name} + {product.productName} - + {(() => { if (product.lastBuild.modifiedTimestamp) { return ( this.navigateToRoute(TESTGRID_CONTEXT + "/deployments/product/" + product.id, { - productId: product.id, - productName: product.name, - productStatus :product.status - })} time={product.lastBuild.modifiedTimestamp} + nevigate={() => this.navigateToRoute(TESTGRID_CONTEXT + "/" + + product.productName, { + productId: product.productId, + productName: product.productName, + productStatus: product.productStatus + })} time={product.lastBuild.modifiedTimestamp} />) } else { return ("No builds yet!"); } })()} - + {(() => { if (product.lastfailed.modifiedTimestamp) { return ( - this.navigateToRoute(TESTGRID_CONTEXT + "/deployments/product/" + product.id, { - productId: product.id, - productName: product.name, - productStatus :product.status - })} style={{ cursor: 'pointer' }}> - {Moment(product.lastfailed.modifiedTimestamp).fromNow()} + this.navigateToRoute(TESTGRID_CONTEXT + "/" + product.productName, { + productId: product.productId, + productName: product.productName, + productStatus: product.productStatus + })} style={{cursor: 'pointer'}}> + {Moment(product.lastfailed.modifiedTimestamp).fromNow()} ); } else { return ("No failed builds yet!") } })()} - { window - .location = '/job/'+ - product.name +'/build' }} /> - { window.location = '/job/'+ - product.name +'/configure' }} data-tip="Configure job" style={{ cursor: 'pointer' }}/> + { + window.location = '/admin/job/' + product.productName + '/build' + }}/> + { + window.location = + '/admin/job/' + product.productName + '/configure' + }} data-tip="Configure job" style={{cursor: 'pointer'}}/> - + + ) }); return ( -
- - - - - - - - - - - - - - {products} - -
StatusJobLatest BuildLast FailureExecuteConfigureReport
- - this.toggle("")}>Error - - {this.state.errorMassage} - - - {' '} - - -
+
+ + + + + + + + + + + + + + {products} + +
StatusJobLatest BuildLast FailureExecuteConfigureReport
+ + this.toggle("")}>Error + + {this.state.errorMassage} + + + {' '} + + +
) } } -export default ProductStatusView; \ No newline at end of file +export default ProductStatusView; diff --git a/web/src/main/react-dashboard/src/components/ScenarioView.js b/web/src/main/react-dashboard/src/components/ScenarioView.js index c6e8845ba..fbdec5317 100644 --- a/web/src/main/react-dashboard/src/components/ScenarioView.js +++ b/web/src/main/react-dashboard/src/components/ScenarioView.js @@ -16,96 +16,91 @@ * under the License. */ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import '../App.css'; -import { - Table, - TableBody, - TableHeader, - TableHeaderColumn, - TableRow, - TableRowColumn, -} from 'material-ui/Table'; import Subheader from 'material-ui/Subheader'; import SingleRecord from './SingleRecord.js'; import {add_current_scenario} from '../actions/testGridActions.js'; import {TESTGRID_CONTEXT, HTTP_UNAUTHORIZED, LOGIN_URI} from '../constants.js'; +import {Table} from 'reactstrap'; class ScenarioView extends Component { - constructor(props) { - super(props); - this.state = { - hits:{ - testScenarios:[] - } - } + constructor(props) { + super(props); + this.state = { + hits: { + testScenarios: [] + } } + } - nevigateToRoute(route, scenario) { - this.props.dispatch(add_current_scenario(scenario)); - this.props.history.push(route); - } + nevigateToRoute(route, scenario) { + this.props.dispatch(add_current_scenario(scenario)); + this.props.history.push(route); + } - handleError(response) { - if (response.status.toString() === HTTP_UNAUTHORIZED) { - window.location.replace(LOGIN_URI); - return response; - } - return response; + handleError(response) { + if (response.status.toString() === HTTP_UNAUTHORIZED) { + window.location.replace(LOGIN_URI); + return response; } + return response; + } - componentDidMount() { - var url = TESTGRID_CONTEXT + "/api/test-plans/"+this.props.active.reducer.currentInfra.infrastructureId+"?require-test-scenario-info=true"; + componentDidMount() { + var url = TESTGRID_CONTEXT + "/api/test-plans/" + + this.props.active.reducer.currentInfra.infrastructureId + "?require-test-scenario-info=true"; - fetch(url, { - method: "GET", - credentials: 'same-origin', - headers: { - 'Accept': 'application/json' - } - }) - .then(this.handleError) - .then(response => { - return response.json() - }) - .then(data => this.setState({ hits: data })); - } + fetch(url, { + method: "GET", + credentials: 'same-origin', + headers: { + 'Accept': 'application/json' + } + }) + .then(this.handleError) + .then(response => { + return response.json() + }) + .then(data => this.setState({hits: data})); + } - render() { - console.log(this.props); - var infraString = ""; - return (
- {this.props.active.reducer.currentProduct.productName} {this.props.active.reducer.currentProduct.productVersion} - {this.props.active.reducer.currentProduct.productChannel} / {this.props.active.reducer.currentDeployment.deploymentName} / {this.props.active.reducer.currentInfra.deploymentName} - - - -

Scenario ID

-

Scenario Description

-

Scenario Status

-
-
- - {this.state.hits.testScenarios.map((data, index) => { - return ( - -

{data.name}

- -

{data.description}

- - this.nevigateToRoute(TESTGRID_CONTEXT + "/testcases/scenario/" + data.id, { - scenarioId: data.id, - scenarioName: data.name, - })} /> - -
) - })} -
-
-
); - } + render() { + console.log(this.props); + var infraString = ""; + return (
+ + {this.props.active.reducer.currentProduct.productName} {this.props.active.reducer.currentProduct.productVersion} + {this.props.active.reducer.currentProduct.productChannel} / {this.props.active.reducer.currentDeployment.deploymentName} / {this.props.active.reducer.currentInfra.deploymentName} + + + + + + + + + + + {this.state.hits.testScenarios.map((data, index) => { + return ( + + + + ) + })} + +
Scenario IDScenario DescriptionScenario Status
{data.name}{data.description} + this.nevigateToRoute(TESTGRID_CONTEXT + "/testcases/scenario/" + + data.id, { + scenarioId: data.id, + scenarioName: data.name, + })}/> +
+
); + } } -export default ScenarioView; \ No newline at end of file +export default ScenarioView; diff --git a/web/src/main/react-dashboard/src/components/SingleRecord.js b/web/src/main/react-dashboard/src/components/SingleRecord.js index 2b99c8c1e..8f6548c12 100644 --- a/web/src/main/react-dashboard/src/components/SingleRecord.js +++ b/web/src/main/react-dashboard/src/components/SingleRecord.js @@ -16,9 +16,9 @@ * under the License. */ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import Moment from 'moment'; -import {FAIL,SUCCESS,ERROR,PENDING,RUNNING,INCOMPLETE,DID_NOT_RUN} from '../constants.js'; +import {SUCCESS, ERROR, PENDING, RUNNING, INCOMPLETE, DID_NOT_RUN} from '../constants.js'; class SingleRecord extends Component { constructor(props) { @@ -28,12 +28,12 @@ class SingleRecord extends Component { render() { if (this.props.value === SUCCESS || this.props.value === true) { return ( -
+
Succesful + onClick={this.props.nevigate} + style={{cursor: 'pointer'}} + title='Successful' + alt='Succesful'/> {(() => { if (this.props.time) { return ( {Moment(this.props.time).fromNow()}); @@ -42,87 +42,87 @@ class SingleRecord extends Component {
) } else if (this.props.value === INCOMPLETE) { - return ( -
- Incomplete - {(() => { - if (this.props.time) { - return ( {Moment(this.props.time).fromNow()}); - } - })()} -
- ) + return ( +
+ Incomplete + {(() => { + if (this.props.time) { + return ( {Moment(this.props.time).fromNow()}); + } + })()} +
+ ) } else if (this.props.value === DID_NOT_RUN) { - return ( -
- Did not Run - {(() => { - if (this.props.time) { - return ( {Moment(this.props.time).fromNow()}); - } - })()} -
- ) + return ( +
+ Did not Run + {(() => { + if (this.props.time) { + return ( {Moment(this.props.time).fromNow()}); + } + })()} +
+ ) } else if (this.props.value === ERROR) { - return ( -
- Error - {(() => { - if (this.props.time) { - return ( {Moment(this.props.time).fromNow()}); - } - })()} -
- ) + return ( +
+ Error + {(() => { + if (this.props.time) { + return ( {Moment(this.props.time).fromNow()}); + } + })()} +
+ ) } else if (this.props.value === RUNNING) { return ( -
- Running - {(() => { - if (this.props.time) { - return ( {Moment(this.props.time).fromNow()}); - } - })()} -
+
+ Running + {(() => { + if (this.props.time) { + return ( {Moment(this.props.time).fromNow()}); + } + })()} +
) } else if (this.props.value === PENDING) { - return ( -
- Pending - {(() => { - if (this.props.time) { - return ( {Moment(this.props.time).fromNow()}); - } - })()} -
- ) + return ( +
+ Pending + {(() => { + if (this.props.time) { + return ( {Moment(this.props.time).fromNow()}); + } + })()} +
+ ) } else { return ( -
+
Failed + width="28" height="28" + onClick={this.props.nevigate} + style={{cursor: 'pointer'}} alt='Failed'/> {(() => { if (this.props.time) { return ( {Moment(this.props.time).fromNow()}); @@ -134,4 +134,4 @@ class SingleRecord extends Component { } } -export default SingleRecord \ No newline at end of file +export default SingleRecord; diff --git a/web/src/main/react-dashboard/src/components/TestCaseView.js b/web/src/main/react-dashboard/src/components/TestCaseView.js index 6134e8d49..cd7294194 100644 --- a/web/src/main/react-dashboard/src/components/TestCaseView.js +++ b/web/src/main/react-dashboard/src/components/TestCaseView.js @@ -16,92 +16,84 @@ * under the License. */ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import '../App.css'; -import { - Table, - TableBody, - TableHeader, - TableHeaderColumn, - TableRow, - TableRowColumn, -} from 'material-ui/Table'; import Subheader from 'material-ui/Subheader'; import SingleRecord from './SingleRecord.js'; import {HTTP_UNAUTHORIZED, LOGIN_URI, TESTGRID_CONTEXT} from '../constants.js'; +import {Table} from 'reactstrap'; class TestCaseView extends Component { - constructor(props) { - super(props); - this.state = { - hits: { - testCases: [] - } - } + constructor(props) { + super(props); + this.state = { + hits: { + testCases: [] + } } + } - handleError(response) { - if (response.status.toString() === HTTP_UNAUTHORIZED) { - window.location.replace(LOGIN_URI); - return response; - } - return response; + handleError(response) { + if (response.status.toString() === HTTP_UNAUTHORIZED) { + window.location.replace(LOGIN_URI); + return response; } + return response; + } - componentDidMount() { - var url = TESTGRID_CONTEXT + '/api/test-scenarios/'+this.props.active.reducer.currentScenario.scenarioId; + componentDidMount() { + var url = TESTGRID_CONTEXT + '/api/test-scenarios/' + this.props.active.reducer.currentScenario.scenarioId; - fetch(url, { - method: "GET", - credentials: 'same-origin', - headers: { - 'Accept': 'application/json' - } - }) - .then(this.handleError) - .then(response => { - return response.json(); - }) - .then(data => this.setState({ hits: data })); + fetch(url, { + method: "GET", + credentials: 'same-origin', + headers: { + 'Accept': 'application/json' + } + }) + .then(this.handleError) + .then(response => { + return response.json(); + }) + .then(data => this.setState({hits: data})); - } + } - render() { - return ( -
- {this.props.active.reducer.currentProduct.productName} {this.props.active.reducer.currentProduct.productVersion} {this.props.active.reducer.currentProduct.productChannel} / {this.props.active.reducer.currentDeployment.deploymentName} /{this.props.active.reducer.currentInfra.deploymentName} / - {this.props.active.reducer.currentScenario.scenarioName} - + render() { + return ( +
+ + {this.props.active.reducer.currentProduct.productName} {this.props.active.reducer.currentProduct.productVersion} {this.props.active.reducer.currentProduct.productChannel} / {this.props.active.reducer.currentDeployment.deploymentName} /{this.props.active.reducer.currentInfra.deploymentName} / + {this.props.active.reducer.currentScenario.scenarioName} + - - - -

TestCase

-

TestResult

-

Error message

-
-
- +
+ + + + + + + + - {this.state.hits.testCases.map((data, index) => { - return ( - -

{data.name}

- - -

{data.errorMsg}

-
) - })} - -
TestCaseTestResultError message
-
- ); - } + {this.state.hits.testCases.map((data, index) => { + return ( + {data.name} + + {data.errorMsg} + ) + })} + + +
+ ); + } } -export default TestCaseView; \ No newline at end of file +export default TestCaseView; diff --git a/web/src/main/react-dashboard/src/components/TestRunView.js b/web/src/main/react-dashboard/src/components/TestRunView.js index f15097162..f6785035a 100644 --- a/web/src/main/react-dashboard/src/components/TestRunView.js +++ b/web/src/main/react-dashboard/src/components/TestRunView.js @@ -16,10 +16,10 @@ * under the License. */ -import React, { Component } from 'react'; +import React, {Component} from 'react'; import '../App.css'; import Subheader from 'material-ui/Subheader'; -import { Card, CardMedia } from 'material-ui/Card'; +import {Card, CardMedia} from 'material-ui/Card'; import FlatButton from 'material-ui/FlatButton'; import Avatar from 'material-ui/Avatar'; import List from 'material-ui/List/List'; @@ -28,19 +28,12 @@ import Divider from 'material-ui/Divider'; import AceEditor from 'react-ace'; import LinearProgress from 'material-ui/LinearProgress'; import CircularProgress from 'material-ui/CircularProgress'; -import { - Table, - TableBody, - TableHeader, - TableHeaderColumn, - TableRow, - TableRowColumn, -} from 'material-ui/Table'; import Download from 'downloadjs' import Websocket from 'react-websocket'; import Snackbar from 'material-ui/Snackbar'; import {FAIL, SUCCESS, ERROR, PENDING, RUNNING, HTTP_UNAUTHORIZED, LOGIN_URI, TESTGRID_CONTEXT, DID_NOT_RUN, INCOMPLETE} - from '../constants.js'; + from '../constants.js'; +import {Table} from 'reactstrap'; /** * View responsible for displaying test run log and summary information. @@ -59,15 +52,49 @@ class TestRunView extends Component { logDownloadStatus: PENDING, isLogTruncated: false, inputStreamSize: "", - showLogDownloadErrorDialog: false + showLogDownloadErrorDialog: false, + currentInfra: null }; } componentDidMount() { - const testScenarioSummaryUrl = TESTGRID_CONTEXT + '/api/test-plans/test-summary/' + - this.props.active.reducer.currentInfra.testPlanId; - const logTruncatedContentUrl = TESTGRID_CONTEXT + '/api/test-plans/log/' + - this.props.active.reducer.currentInfra.testPlanId + "?truncate=" + true; + let currentInfra = {}; + let currentUrl = window.location.href.split("/"); + currentInfra.relatedProduct = currentUrl[currentUrl.length - 4]; + currentInfra.relatedDeplymentPattern = currentUrl[currentUrl.length - 3]; + if (this.props.active.reducer.currentInfra) { + currentInfra.testPlanId = this.props.active.reducer.currentInfra.testPlanId; + currentInfra.infraParameters = this.props.active.reducer.currentInfra.infraParameters; + currentInfra.testPlanStatus = this.props.active.reducer.currentInfra.testPlanStatus; + this.getReportData(currentInfra); + this.setState({currentInfra: currentInfra}); + } else { + let url = TESTGRID_CONTEXT + "/api/test-plans/" + currentUrl.pop(); + fetch(url, { + method: "GET", + credentials: 'same-origin', + headers: { + 'Accept': 'application/json' + } + }) + .then(this.handleError) + .then(response => { + return response.json(); + }).then(data => { + currentInfra.testPlanId = data.id; + currentInfra.infraParameters = data.infraParams; + currentInfra.testPlanStatus = data.status; + this.props.active.reducer.currentInfra = currentInfra; + this.getReportData(currentInfra); + this.setState({currentInfra: currentInfra}) + }); + } + } + + getReportData(currentInfra) { + const testScenarioSummaryUrl = TESTGRID_CONTEXT + '/api/test-plans/test-summary/' + currentInfra.testPlanId; + const logTruncatedContentUrl = TESTGRID_CONTEXT + '/api/test-plans/log/' + currentInfra.testPlanId + + "?truncate=" + true; fetch(testScenarioSummaryUrl, { method: "GET", @@ -76,19 +103,18 @@ class TestRunView extends Component { 'Accept': 'application/json' } }) - .then(this.handleError) - .then(response => { - this.setState({ - testSummaryLoadStatus: response.ok ? SUCCESS : ERROR - }); - return response.json(); - }).then(data => this.setState({ + .then(this.handleError) + .then(response => { + this.setState({ + testSummaryLoadStatus: response.ok ? SUCCESS : ERROR + }); + return response.json(); + }).then(data => this.setState({ testScenarioSummaries: data.scenarioSummaries, scenarioTestCaseEntries: data.scenarioTestCaseEntries })); - if (this.props.active.reducer.currentInfra.testPlanStatus === FAIL || - this.props.active.reducer.currentInfra.testPlanStatus === SUCCESS) { + if (currentInfra.testPlanStatus === FAIL || currentInfra.testPlanStatus === SUCCESS) { fetch(logTruncatedContentUrl, { method: "GET", credentials: 'same-origin', @@ -96,13 +122,13 @@ class TestRunView extends Component { 'Accept': 'application/json' } }) - .then(this.handleError) - .then(response => { - this.setState({ - logDownloadStatus: response.ok ? SUCCESS : ERROR - }); - return response.json(); - }).then(data => + .then(this.handleError) + .then(response => { + this.setState({ + logDownloadStatus: response.ok ? SUCCESS : ERROR + }); + return response.json(); + }).then(data => this.setState({ logContent: data.inputStreamContent, isLogTruncated: data.truncated, @@ -118,77 +144,75 @@ class TestRunView extends Component { }; handleLiveLogData(data) { - this.setState({ logContent: this.state.logContent + data }); + this.setState({logContent: this.state.logContent + data}); } handleError(response) { - if (response.status.toString() === HTTP_UNAUTHORIZED) { - window.location.replace(LOGIN_URI); - } - return response; + if (response.status.toString() === HTTP_UNAUTHORIZED) { + window.location.replace(LOGIN_URI); + } + return response; } render() { - const subHeader = ( - {this.props.active.reducer.currentProduct.productName} - {this.props.active.reducer.currentProduct.productVersion} - {this.props.active.reducer.currentProduct.productChannel} / - {this.props.active.reducer.currentDeployment.deploymentPatternName} / - {this.props.active.reducer.currentInfra.infraParameters} - ); - const divider = (); + const divider = (); const logAllContentUrl = TESTGRID_CONTEXT + '/api/test-plans/log/' + - this.props.active.reducer.currentInfra.testPlanId + "?truncate=" + false; + window.location.href.split("/").pop() + "?truncate=" + false; let isFailedTestsTitleAdded = false; return (
{/*Sub header*/} - {(() => { - switch (this.props.active.reducer.currentInfra.testPlanStatus) { + {this.state && this.state.currentInfra && (() => { + const subHeader = ( + {this.state.currentInfra.relatedProduct}/ + {this.state.currentInfra.relatedDeplymentPattern} / + {this.state.currentInfra.infraParameters} + ); + switch (this.state.currentInfra.testPlanStatus) { case FAIL: return - +
- - - {subHeader} - + + + {subHeader} + -
- -
+ +
+
; case SUCCESS: return - +
- - - {subHeader} - + + + {subHeader} + -
- -
+ +
+
; case PENDING: case RUNNING: @@ -197,53 +221,53 @@ class TestRunView extends Component { fontSize: '20px', backgroundColor: "#d7d9ff" }}> - +
- - - {subHeader} - + + + {subHeader} + -
- -
+ +
+ ; } })()} - + {/*TestGrid generated contents*/} - }> + leftAvatar={ + + }> (fetch(logAllContentUrl, { - method: "GET", - credentials: 'same-origin', - headers: { - 'Accept': 'application/json' - } - }) - .then(this.handleError) - .then(response => { - this.setState({ - showLogDownloadErrorDialog: !response.ok - }); - return response.json(); - }).then(data => { - if (!this.state.showLogDownloadErrorDialog) { - Download(data.inputStreamContent, "test-run.log", "plain/text"); - } - } - ))} + onClick={() => (fetch(logAllContentUrl, { + method: "GET", + credentials: 'same-origin', + headers: { + 'Accept': 'application/json' + } + }) + .then(this.handleError) + .then(response => { + this.setState({ + showLogDownloadErrorDialog: !response.ok + }); + return response.json(); + }).then(data => { + if (!this.state.showLogDownloadErrorDialog) { + Download(data.inputStreamContent, "test-run.log", "plain/text"); + } + } + ))} /> -
+
Oh snap! Error occurred when loading test summaries.
; case SUCCESS: return
- - - - - -

Scenario

-
- -

Total Success

-
- -

Total Failed

-
- -

Success Percentage

-
-
-
- - {this.state.testScenarioSummaries.map((data, index) => { - return ( - - {(() => { - switch (data.scenarioStatus) { - case SUCCESS: - return
- -
; - case FAIL: - return
- -
; - case PENDING: - return
- -
; - case INCOMPLETE: - return
- -
; - case DID_NOT_RUN: - return
- -
; - case ERROR: - return
- -
; - case "RUNNING": - default: - return
- -
- } - })()} -
- +
+ + + + + + + + + {this.state.testScenarioSummaries.map((data, index) => { + return ( + + + + + + ) + })} +
+ ScenarioTotal SuccessTotal FailedSuccess Percentage
+ {(() => { + switch (data.scenarioStatus) { + case SUCCESS: + return
+ +
; + case FAIL: + return
+ +
; + case PENDING: + return
+ +
; + case INCOMPLETE: + return
+ +
; + case DID_NOT_RUN: + return
+ +
; + case ERROR: + return
+ +
; + case "RUNNING": + default: + return
+ +
+ } + })()} +
+ {data.scenarioDescription} + + {data.scenarioDescription} - {data.totalSuccess} - {data.totalFail} - - { - isNaN(data.successPercentage) ? - "0.0" : - parseFloat(data.successPercentage).toFixed(2) - }% - - ) - })} - + whiteSpace: "wrap" + }}>{data.totalSuccess}{data.totalFail} + { + isNaN(data.successPercentage) ? + "0.0" : + parseFloat(data.successPercentage).toFixed(2) + }% +
-
-
+
+
{divider} {/*Detailed Report for failed test cases*/} {this.state.scenarioTestCaseEntries.map((data, index) => { @@ -404,70 +417,57 @@ class TestRunView extends Component { return (
{failedTestTitleContent} -

{data.scenarioDescription}

- - - - - -

Test Case

-
- + + {data.scenarioDescription} + + +
+ + + + + + + + {data.testCaseEntries.map((entry, index) => { + return ( + + + + + + ) + })} +
+ Test CaseFailure Message
+ {entry.isTestSuccess ? + + : + } + {entry.testCase} -

Failure Message

- - - - - {data.testCaseEntries.map((entry, index) => { - return ( - - - {entry.isTestSuccess ? - - : - } - - {entry.testCase} - - {entry.failureMessage} - - - ) - })} - + {entry.failureMessage} +
-
+
) } else { return ("") @@ -477,30 +477,30 @@ class TestRunView extends Component { case PENDING: default: return
-
-
+
+
Loading test summaries... -
- +
+
; } })()} {divider} -
+
{/*Test log*/}

Test Run Log

{/*Display log from file system*/} - {(() => { - switch (this.props.active.reducer.currentInfra.testPlanStatus) { + {this.state && this.state.currentInfra && (() => { + switch (this.state.currentInfra.testPlanStatus) { case PENDING: case "RUNNING": return (
{ this.handleLiveLogData(data) - }} /> + }}/> -
+ }}/> +
); case FAIL: case SUCCESS: @@ -530,7 +530,7 @@ class TestRunView extends Component { color: "#D8000C", backgroundColor: "#FFD2D2" }}> -
+
Oh snap! Error occurred when downloading the log file content.
; @@ -551,7 +551,7 @@ class TestRunView extends Component { }} style={{ width: this.props.containerWidth - }} /> + }}/> {this.state.isLogTruncated ?
@@ -564,16 +564,16 @@ class TestRunView extends Component { } }).then(this.handleError) .then(response => { - this.setState({ - logDownloadStatus: response.ok ? SUCCESS : ERROR - }); - return response; - }).then(data => data.json().then(json => - this.setState({ - logContent: json.inputStreamContent, - isLogTruncated: false - }), - )))} + this.setState({ + logDownloadStatus: response.ok ? SUCCESS : ERROR + }); + return response; + }).then(data => data.json().then(json => + this.setState({ + logContent: json.inputStreamContent, + isLogTruncated: false + }), + )))} label={"See More (" + this.state.inputStreamSize + ")"} labelStyle={{ fontSize: '20px', @@ -581,7 +581,7 @@ class TestRunView extends Component { }} style={{ color: '#0E457C' - }} /> + }}/>
: ""} @@ -589,19 +589,19 @@ class TestRunView extends Component { case PENDING: default: return
-
-
+
+
Loading test log... -
- +
+
; } } } })()} -
-
+
+
); diff --git a/web/src/main/react-dashboard/src/constants.js b/web/src/main/react-dashboard/src/constants.js index 1791827e9..a0d193aaa 100644 --- a/web/src/main/react-dashboard/src/constants.js +++ b/web/src/main/react-dashboard/src/constants.js @@ -8,5 +8,5 @@ export const DID_NOT_RUN = "DID_NOT_RUN"; export const HTTP_UNAUTHORIZED = "401"; export const HTTP_OK = 200; export const HTTP_NOT_FOUND = 404; -export const LOGIN_URI = "/testgrid/dashboard/login"; -export const TESTGRID_CONTEXT = "/testgrid/dashboard"; +export const LOGIN_URI = "/login"; +export const TESTGRID_CONTEXT = ""; diff --git a/web/src/main/react-dashboard/src/containers/InfraContainer.js b/web/src/main/react-dashboard/src/containers/InfraContainer.js index 1113e3a97..9fd796dca 100644 --- a/web/src/main/react-dashboard/src/containers/InfraContainer.js +++ b/web/src/main/react-dashboard/src/containers/InfraContainer.js @@ -16,15 +16,15 @@ * under the License. */ -import { connect } from 'react-redux' +import {connect} from 'react-redux' import InfraCombinationView from '../components/InfraCombinationView.js' -const mapStateToProps = (state ,ownProps) =>({ - active:state -}) +const mapStateToProps = (state, ownProps) => ({ + active: state +}); -const InfrastructureContainer= connect( - mapStateToProps -)(InfraCombinationView) +const InfrastructureContainer = connect( + mapStateToProps +)(InfraCombinationView); -export default InfrastructureContainer; \ No newline at end of file +export default InfrastructureContainer; diff --git a/web/src/main/react-dashboard/src/containers/ScenarioContainer.js b/web/src/main/react-dashboard/src/containers/ScenarioContainer.js index 516caaae2..4a1d37d90 100644 --- a/web/src/main/react-dashboard/src/containers/ScenarioContainer.js +++ b/web/src/main/react-dashboard/src/containers/ScenarioContainer.js @@ -16,15 +16,15 @@ * under the License. */ -import { connect } from 'react-redux' +import {connect} from 'react-redux' import ScenarioView from '../components/ScenarioView.js' -const mapStateToProps = (state ,ownProps) =>({ - active:state -}) +const mapStateToProps = (state, ownProps) => ({ + active: state +}); const ScenarioContainer = connect( - mapStateToProps -)(ScenarioView) + mapStateToProps +)(ScenarioView); -export default ScenarioContainer; \ No newline at end of file +export default ScenarioContainer; diff --git a/web/src/main/react-dashboard/src/containers/TestCaseContainer.js b/web/src/main/react-dashboard/src/containers/TestCaseContainer.js index 0a936da31..3abc145db 100644 --- a/web/src/main/react-dashboard/src/containers/TestCaseContainer.js +++ b/web/src/main/react-dashboard/src/containers/TestCaseContainer.js @@ -16,15 +16,15 @@ * under the License. */ -import { connect } from 'react-redux' +import {connect} from 'react-redux' import TestCaseView from '../components/TestCaseView.js' -const mapStateToProps = (state ,ownProps) =>({ - active:state -}) +const mapStateToProps = (state, ownProps) => ({ + active: state +}); const TestCaseContainer = connect( - mapStateToProps -)(TestCaseView) + mapStateToProps +)(TestCaseView); -export default TestCaseContainer; \ No newline at end of file +export default TestCaseContainer; diff --git a/web/src/main/react-dashboard/src/containers/deploymentContainer.js b/web/src/main/react-dashboard/src/containers/deploymentContainer.js index cbc93817c..1c4234be1 100644 --- a/web/src/main/react-dashboard/src/containers/deploymentContainer.js +++ b/web/src/main/react-dashboard/src/containers/deploymentContainer.js @@ -16,15 +16,15 @@ * under the License. */ -import { connect } from 'react-redux' +import {connect} from 'react-redux' import DeploymentPatternView from '../components/DeploymentPatternView.js' const mapStateToProps = (state, ownProps) => ({ - active: state -}) + active: state +}); const DeploymentContainer = connect( - mapStateToProps -)(DeploymentPatternView) + mapStateToProps +)(DeploymentPatternView); -export default DeploymentContainer; \ No newline at end of file +export default DeploymentContainer; diff --git a/web/src/main/react-dashboard/src/containers/productContainer.js b/web/src/main/react-dashboard/src/containers/productContainer.js index c626551ff..79ed730b3 100644 --- a/web/src/main/react-dashboard/src/containers/productContainer.js +++ b/web/src/main/react-dashboard/src/containers/productContainer.js @@ -16,20 +16,20 @@ * under the License. */ -import { connect } from 'react-redux' +import {connect} from 'react-redux' import ProductStatusView from '../components/ProductStatusView.js' const mapStateToProps = (state, ownProps) => ({ - active: state -}) + active: state +}); const mapDispatchToProps = (dispatch, ownProps) => ({ - dispatch: dispatch -}) + dispatch: dispatch +}); const ProductContainer = connect( - mapStateToProps, - mapDispatchToProps -)(ProductStatusView) + mapStateToProps, + mapDispatchToProps +)(ProductStatusView); -export default ProductContainer; \ No newline at end of file +export default ProductContainer; diff --git a/web/src/main/react-dashboard/src/containers/testRunContainer.js b/web/src/main/react-dashboard/src/containers/testRunContainer.js index 65854bdd7..75d708774 100644 --- a/web/src/main/react-dashboard/src/containers/testRunContainer.js +++ b/web/src/main/react-dashboard/src/containers/testRunContainer.js @@ -20,11 +20,11 @@ import {connect} from 'react-redux' import TestRunView from '../components/TestRunView.js' const mapStateToProps = (state, ownProps) => ({ - active: state + active: state }); const testRunContainer = connect( - mapStateToProps + mapStateToProps )(TestRunView); export default testRunContainer; diff --git a/web/src/main/react-dashboard/src/index.css b/web/src/main/react-dashboard/src/index.css index fd7bfad08..b26482dec 100644 --- a/web/src/main/react-dashboard/src/index.css +++ b/web/src/main/react-dashboard/src/index.css @@ -10,16 +10,16 @@ body { width: 100%; height: 100%; margin: 0; - padding-bottom: 0; + padding-bottom: 0; } .infra-param, .infra-param-header p { float: left; - width: 30%; - height: 100%; - text-align: left; + width: 30%; + height: 100%; + text-align: left; padding: 0 0 0 10px; - margin: 0; + margin: 0; } .infra-param-header p { @@ -27,7 +27,7 @@ body { } .infra-param { - font-size: 16px; + font-size: 16px; } .deployment-pattern-view thead tr { diff --git a/web/src/main/react-dashboard/src/index.js b/web/src/main/react-dashboard/src/index.js index 948c641de..108d027f5 100644 --- a/web/src/main/react-dashboard/src/index.js +++ b/web/src/main/react-dashboard/src/index.js @@ -19,11 +19,11 @@ import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; -import { BrowserRouter } from 'react-router-dom' +import {BrowserRouter} from 'react-router-dom' import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; import './index.css'; -/*This is the main component of the dash board*/ +/*This is the main component of the dash board*/ ReactDOM.render( - - - , document.getElementById('root')); + + + , document.getElementById('root'));