Skip to content
Snippets Groups Projects
Commit d0a01f75 authored by Nithya's avatar Nithya
Browse files
Changes updated based on review comment
parent fdc05a8e
No related branches found
No related tags found
1 merge request!196Resolve TMSS-239
Showing
with 1195 additions and 271 deletions
...@@ -37,8 +37,19 @@ label { ...@@ -37,8 +37,19 @@ label {
margin-bottom: 10px; margin-bottom: 10px;
} }
.main-content span,a{ h2, .h2 {
font-size: 1.5rem;
}
h3, .h3 {
font-size: 1.25rem;
}
a{
margin-bottom: 10px; margin-bottom: 10px;
}
.main-content span,a{
font-size: 14px; font-size: 14px;
} }
...@@ -102,8 +113,9 @@ p { ...@@ -102,8 +113,9 @@ p {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.fa { .layout-main .fa {
color: #005b9f; color: #005b9f;
font-size: 20px;
} }
thead { thead {
...@@ -116,10 +128,24 @@ thead { ...@@ -116,10 +128,24 @@ thead {
font-weight: 400; font-weight: 400;
} }
.info {
color: #b4b2b2;
font-size: 80%;
font-weight: 400;
}
.input-error { .input-error {
border-color: #dc3545 !important; border-color: #dc3545 !important;
} }
.p-tooltip-custom .p-tooltip-text {
/* background-color: #49b0ff !important; */
}
.p-link:focus {
box-shadow: none !important;
}
.pi-primary { .pi-primary {
color: #007ad9; color: #007ad9;
} }
......
import React, {Component} from 'react'; import React, {Component} from 'react';
import { BrowserRouter as Router } from 'react-router-dom'; import { BrowserRouter as Router } from 'react-router-dom';
import classNames from 'classnames'; import classNames from 'classnames';
import {AppTopbar} from './layout/components/AppTopbar'; import {AppTopbar} from './layout/components/AppTopbar';
import {AppMenu} from './layout/components/AppMenu'; import {AppMenu} from './layout/components/AppMenu';
import { AppFooter } from './layout/components/AppFooter'; import {AppFooter } from './layout/components/AppFooter';
import {RoutedContent} from './routes'; import {RoutedContent} from './routes';
import {AppBreadcrumb } from "./layout/components/AppBreadcrumb";
// import {Dashboard} from './routes/dashboard/dashboard'; import {withRouter } from 'react-router';
import 'primeicons/primeicons.css'; import 'primeicons/primeicons.css';
import 'primereact/resources/themes/nova-light/theme.css'; import 'primereact/resources/themes/nova-light/theme.css';
import 'primereact/resources/primereact.css'; import 'primereact/resources/primereact.css';
import './layout/layout.scss'; import './layout/layout.scss';
import 'primeflex/primeflex.css';
import './App.scss'; import './App.scss';
import './App.css'; import './App.css';
class App extends Component { class App extends Component {
constructor() {
constructor() {
super(); super();
this.state = { this.state = {
layoutMode: 'static', layoutMode: 'static',
...@@ -28,22 +27,23 @@ class App extends Component { ...@@ -28,22 +27,23 @@ class App extends Component {
overlayMenuActive: false, overlayMenuActive: false,
mobileMenuActive: false, mobileMenuActive: false,
}; };
this.onWrapperClick = this.onWrapperClick.bind(this); this.onWrapperClick = this.onWrapperClick.bind(this);
this.onToggleMenu = this.onToggleMenu.bind(this); this.onToggleMenu = this.onToggleMenu.bind(this);
this.onSidebarClick = this.onSidebarClick.bind(this); this.onSidebarClick = this.onSidebarClick.bind(this);
this.onMenuItemClick = this.onMenuItemClick.bind(this); this.onMenuItemClick = this.onMenuItemClick.bind(this);
this.menu = [ this.menu = [
{label: 'Dashboard', icon: 'pi pi-fw pi-home', to:'/dashboard'}, {label: 'Dashboard', icon: 'pi pi-fw pi-home', to:'/dashboard'},
{label: 'Scheduling Units', icon: 'pi pi-fw pi-calendar', to:'/scheduling'}, {label: 'Cycle', icon: 'pi pi-fw pi-spinner', to:'/cycle'},
{label: 'Scheduling Units', icon: 'pi pi-fw pi-calendar', to:'/schedulingunit'},
{label: 'Tasks', icon: 'pi pi-fw pi-check-square', to:'/task'}, {label: 'Tasks', icon: 'pi pi-fw pi-check-square', to:'/task'},
{label: 'Cycle', icon: 'pi pi-fw pi-spinner', to:'/cycle'} {label: 'Project', icon: 'fa fa-fw fa-binoculars', to:'/project'}
]; ];
// this.menuComponent = {'Dashboard': Dashboard} // this.menuComponent = {'Dashboard': Dashboard}
} }
onWrapperClick(event) { onWrapperClick(event) {
if (!this.menuClick) { if (!this.menuClick) {
this.setState({ this.setState({
overlayMenuActive: false, overlayMenuActive: false,
...@@ -56,7 +56,6 @@ class App extends Component { ...@@ -56,7 +56,6 @@ class App extends Component {
onToggleMenu(event) { onToggleMenu(event) {
this.menuClick = true; this.menuClick = true;
if (this.isDesktop()) { if (this.isDesktop()) {
if (this.state.layoutMode === 'overlay') { if (this.state.layoutMode === 'overlay') {
this.setState({ this.setState({
...@@ -75,46 +74,48 @@ class App extends Component { ...@@ -75,46 +74,48 @@ class App extends Component {
mobileMenuActive: !mobileMenuActive mobileMenuActive: !mobileMenuActive
}); });
} }
event.preventDefault();
event.preventDefault();
} }
onSidebarClick(event) { onSidebarClick(event) {
this.menuClick = true; this.menuClick = true;
} }
onMenuItemClick(event) {
this.setState({currentMenu:event.item.label, currentPath: event.item.path}); onMenuItemClick(event) {
} this.setState({currentMenu:event.item.label, currentPath: event.item.path});
}
isDesktop() { isDesktop() {
return window.innerWidth > 1024; return window.innerWidth > 1024;
} }
render() { render() {
const wrapperClass = classNames('layout-wrapper', { const wrapperClass = classNames('layout-wrapper', {
'layout-overlay': this.state.layoutMode === 'overlay', 'layout-overlay': this.state.layoutMode === 'overlay',
'layout-static': this.state.layoutMode === 'static', 'layout-static': this.state.layoutMode === 'static',
'layout-static-sidebar-inactive': this.state.staticMenuInactive && this.state.layoutMode === 'static', 'layout-static-sidebar-inactive': this.state.staticMenuInactive && this.state.layoutMode === 'static',
'layout-overlay-sidebar-active': this.state.overlayMenuActive && this.state.layoutMode === 'overlay', 'layout-overlay-sidebar-active': this.state.overlayMenuActive && this.state.layoutMode === 'overlay',
'layout-mobile-sidebar-active': this.state.mobileMenuActive 'layout-mobile-sidebar-active': this.state.mobileMenuActive
}); });
return ( const AppBreadCrumbWithRouter = withRouter(AppBreadcrumb);
return (
<React.Fragment> <React.Fragment>
<div className="App"> <div className="App">
{/* <div className={wrapperClass} onClick={this.onWrapperClick}> */} {/* <div className={wrapperClass} onClick={this.onWrapperClick}> */}
<div className={wrapperClass}> <div className={wrapperClass}>
<AppTopbar onToggleMenu={this.onToggleMenu}></AppTopbar> <AppTopbar onToggleMenu={this.onToggleMenu}></AppTopbar>
<Router basename={ this.state.currentPath }> <Router basename={ this.state.currentPath }>
<AppMenu model={this.menu} onMenuItemClick={this.onMenuItemClick} /> <AppMenu model={this.menu} onMenuItemClick={this.onMenuItemClick} />
<div className="layout-main"> <div className="layout-main">
<RoutedContent /> <AppBreadCrumbWithRouter/>
</div> <RoutedContent />
</div>
</Router> </Router>
<AppFooter></AppFooter> <AppFooter></AppFooter>
</div> </div>
</div> </div>
</React.Fragment> </React.Fragment>
); );
} }
} }
......
import "babel-polyfill"; export default{
import React from 'react';
import { render,fireEvent } from '@testing-library/react';
import CycleList from './CycleList';
import UnitConversion from '../../utils/unit.converter';
jest.mock('../../services/cycle.service', () => {
return {
getProjects: () => Promise.resolve({ data: mockData.getProjects }),
getCycleQuota: () => Promise.resolve({ data: mockData.getCycleQuota }),
getAllCycle: () => Promise.resolve({ data: mockData.getAllCycle })
}
});
const flushPromises = () => new Promise(setImmediate);
describe('<CycleList />', () => {
test('render table in the cycle list', async () => {
const { container } = render(<CycleList />);
await flushPromises();
expect(container.querySelector('table')).toBeInTheDocument();
});
test('render cycle list in row', async () => {
const { container } = render(<CycleList />);
await flushPromises();
expect(container.querySelectorAll('tr').length).toBe(4);
});
test('render columns in the cycle list', async () => {
const { container } = render(<CycleList />);
await flushPromises();
expect(container.querySelectorAll('th').length).toBe(container.querySelectorAll('th').length);
});
test('render cycleId - cycle name conversion', async () => {
const { container } = render(<CycleList />);
await flushPromises();
expect(container.querySelectorAll('tr')[1].innerHTML.includes('Cycle00')).toBeTruthy();
});
test('render observing time in hours', async () => {
const { container } = render(<CycleList />);
await flushPromises();
const observing_time = UnitConversion.getUIResourceUnit('seconds',mockData.getCycleQuota.results[0].value);
expect(container.querySelectorAll('tr')[1].innerHTML.includes(observing_time)).toBeTruthy();
});
test('render commissioning time in hours', async () => {
const { container } = render(<CycleList />);
await flushPromises();
const commissioning_time = UnitConversion.getUIResourceUnit('seconds',mockData.getCycleQuota.results[0].value);
expect(container.querySelectorAll('tr')[1].innerHTML.includes(commissioning_time)).toBeTruthy();
});
test('toggle columns in table', async () => {
const { container } = render(<CycleList />);
await flushPromises();
const panel = container.querySelector('#overlay_panel');
expect(panel).toHaveStyle('display: block');
fireEvent.click(container.querySelector('.col-filter-btn'));
await flushPromises();
expect(panel).toHaveStyle('display: none');
expect(container.querySelectorAll("input[type=checkbox]:checked").length).toBe(container.querySelectorAll('th').length)
});
});
const mockData = {
getProjects: { getProjects: {
"results": [{ "results": [{
"name": "TMSS-Commissioning", "name": "TMSS-Commissioning",
...@@ -138,6 +71,90 @@ const mockData = { ...@@ -138,6 +71,90 @@ const mockData = {
"tags": [], "tags": [],
"updated_at": "2020-08-06T12:06:09.107234" "updated_at": "2020-08-06T12:06:09.107234"
}] }]
},
getresources: {
"results": [{
"name": "lta_storage",
"url": "http://localhost:3000/api/resource_type/lta_storage/",
"created_at": "2020-08-10T11:33:27.742938",
"description": "Amount of storage in the LTA (in bytes)",
"quantity": "http://localhost:3000/api/quantity/bytes/",
"quantity_value": "bytes",
"tags": [],
"updated_at": "2020-08-10T11:33:27.742956"
}, {
"name": "cep_storage",
"url": "http://localhost:3000/api/resource_type/cep_storage/",
"created_at": "2020-08-10T11:33:27.747482",
"description": "Amount of storage on the CEP processing cluster (in bytes)",
"quantity": "http://localhost:3000/api/quantity/bytes/",
"quantity_value": "bytes",
"tags": [],
"updated_at": "2020-08-10T11:33:27.747498"
}, {
"name": "cep_processing_time",
"url": "http://localhost:3000/api/resource_type/cep_processing_time/",
"created_at": "2020-08-10T11:33:27.751195",
"description": "Processing time on the CEP processing cluster (in seconds)",
"quantity": "http://localhost:3000/api/quantity/time/",
"quantity_value": "time",
"tags": [],
"updated_at": "2020-08-10T11:33:27.751218"
}, {
"name": "observing_time",
"url": "http://localhost:3000/api/resource_type/observing_time/",
"created_at": "2020-08-10T11:33:27.754681",
"description": "Observing time (in seconds)",
"quantity": "http://localhost:3000/api/quantity/time/",
"quantity_value": "time",
"tags": [],
"updated_at": "2020-08-10T11:33:27.754705"
}, {
"name": "observing_time_prio_a",
"url": "http://localhost:3000/api/resource_type/observing_time_prio_a/",
"created_at": "2020-08-10T11:33:27.758486",
"description": "Observing time with priority A (in seconds)",
"quantity": "http://localhost:3000/api/quantity/time/",
"quantity_value": "time",
"tags": [],
"updated_at": "2020-08-10T11:33:27.758511"
}, {
"name": "observing_time_prio_b",
"url": "http://localhost:3000/api/resource_type/observing_time_prio_b/",
"created_at": "2020-08-10T11:33:27.762276",
"description": "Observing time with priority B (in seconds)",
"quantity": "http://localhost:3000/api/quantity/time/",
"quantity_value": "time",
"tags": [],
"updated_at": "2020-08-10T11:33:27.762296"
}, {
"name": "observing_time_commissioning",
"url": "http://localhost:3000/api/resource_type/observing_time_commissioning/",
"created_at": "2020-08-10T11:33:27.765809",
"description": "Observing time for Commissioning/DDT (in seconds)",
"quantity": "http://localhost:3000/api/quantity/time/",
"quantity_value": "time",
"tags": [],
"updated_at": "2020-08-10T11:33:27.765834"
}, {
"name": "support_time",
"url": "http://localhost:3000/api/resource_type/support_time/",
"created_at": "2020-08-10T11:33:27.769402",
"description": "Support time by human (in seconds)",
"quantity": "http://localhost:3000/api/quantity/time/",
"quantity_value": "time",
"tags": [],
"updated_at": "2020-08-10T11:33:27.769427"
}, {
"name": "number_of_triggers",
"url": "http://localhost:3000/api/resource_type/number_of_triggers/",
"created_at": "2020-08-10T11:33:27.773406",
"description": "Number of trigger events (as integer)",
"quantity": "http://localhost:3000/api/quantity/number/",
"quantity_value": "number",
"tags": [],
"updated_at": "2020-08-10T11:33:27.773434"
}]
} }
}
}
\ No newline at end of file
const ProjectServiceMock= { const ProjectServiceMock= {
project_categories: [{url: "Regular", value: 'Regular'}, {url: "User Shared Support", value: 'User Shared Support'}],
period_categories: [{url: "Single Cycle", value: 'Single Cycle'}, {url: "Long Term", value: 'Long Term'}],
resources: [{ resources: [{
"name": "LOFAR Observing Time", "name": "LOFAR Observing Time",
"url": "http://localhost:3000/api/resource_type/LOFAR%20Observing%20Time/", "url": "http://localhost:3000/api/resource_type/LOFAR%20Observing%20Time/",
...@@ -189,7 +191,125 @@ const ProjectServiceMock= { ...@@ -189,7 +191,125 @@ const ProjectServiceMock= {
'LTA Storage': 1024*1024*1024*1024, 'LTA Storage': 1024*1024*1024*1024,
'Number of triggers': 1, 'Number of triggers': 1,
'LOFAR Support Time': 3600 'LOFAR Support Time': 3600
} },
project: [{
"name": "OSR-11",
"url": "http://192.168.99.100:8008/api/project/OSR-11/",
"can_trigger": true,
"created_at": "2020-07-29T18:20:06.187276",
"cycles": [
"http://192.168.99.100:8008/api/cycle/Cycle%200/"
],
"cycles_ids": [
"Cycle 0"
],
"description": "OSR-11",
"expert": false,
"filler": false,
"period_category": "Single Cycle",
"period_category_value": "Single Cycle",
"priority_rank": 5,
"private_data": true,
"project_category": "Regular",
"project_category_value": "Regular",
"project_quota": [
"http://192.168.99.100:8008/api/project_quota/70/",
"http://192.168.99.100:8008/api/project_quota/71/",
"http://192.168.99.100:8008/api/project_quota/72/",
"http://192.168.99.100:8008/api/project_quota/73/",
"http://192.168.99.100:8008/api/project_quota/74/",
"http://192.168.99.100:8008/api/project_quota/75/",
"http://192.168.99.100:8008/api/project_quota/76/",
"http://192.168.99.100:8008/api/project_quota/77/"
],
"project_quota_ids": [
70,
71,
72,
73,
74,
75,
76,
77
],
"tags": [],
"trigger_priority": 990,
"updated_at": "2020-07-29T18:20:06.187342"
}],
projectQuota: [
{
"id": 70,
"url": "http://192.168.99.100:8008/api/project_quota/70/",
"project": "http://192.168.99.100:8008/api/project/OSR-11/",
"project_id": "OSR-11",
"resource_type": "http://192.168.99.100:8008/api/resource_type/CEP%20Processing%20Time/",
"resource_type_id": "CEP Processing Time",
"value": 36000
},
{
"id": 71,
"url": "http://192.168.99.100:8008/api/project_quota/71/",
"project": "http://192.168.99.100:8008/api/project/OSR-11/",
"project_id": "OSR-11",
"resource_type": "http://192.168.99.100:8008/api/resource_type/LOFAR%20Observing%20Time/",
"resource_type_id": "LOFAR Observing Time",
"value": 72000
},
{
"id": 72,
"url": "http://192.168.99.100:8008/api/project_quota/72/",
"project": "http://192.168.99.100:8008/api/project/OSR-11/",
"project_id": "OSR-11",
"resource_type": "http://192.168.99.100:8008/api/resource_type/LOFAR%20Observing%20Time%20prio%20A/",
"resource_type_id": "LOFAR Observing Time prio A",
"value": 108000
},
{
"id": 73,
"url": "http://192.168.99.100:8008/api/project_quota/73/",
"project": "http://192.168.99.100:8008/api/project/OSR-11/",
"project_id": "OSR-11",
"resource_type": "http://192.168.99.100:8008/api/resource_type/LOFAR%20Observing%20Time%20prio%20B/",
"resource_type_id": "LOFAR Observing Time prio B",
"value": 144000
},
{
"id": 74,
"url": "http://192.168.99.100:8008/api/project_quota/74/",
"project": "http://192.168.99.100:8008/api/project/OSR-11/",
"project_id": "OSR-11",
"resource_type": "http://192.168.99.100:8008/api/resource_type/LOFAR%20Support%20Time/",
"resource_type_id": "LOFAR Support Time",
"value": 180000
},
{
"id": 75,
"url": "http://192.168.99.100:8008/api/project_quota/75/",
"project": "http://192.168.99.100:8008/api/project/OSR-11/",
"project_id": "OSR-11",
"resource_type": "http://192.168.99.100:8008/api/resource_type/LTA%20Storage/",
"resource_type_id": "LTA Storage",
"value": 6597069766656
},
{
"id": 76,
"url": "http://192.168.99.100:8008/api/project_quota/76/",
"project": "http://192.168.99.100:8008/api/project/OSR-11/",
"project_id": "OSR-11",
"resource_type": "http://192.168.99.100:8008/api/resource_type/Number%20of%20triggers/",
"resource_type_id": "Number of triggers",
"value": 7
},
{
"id": 77,
"url": "http://192.168.99.100:8008/api/project_quota/77/",
"project": "http://192.168.99.100:8008/api/project/OSR-11/",
"project_id": "OSR-11",
"resource_type": "http://192.168.99.100:8008/api/resource_type/Support%20hours/",
"resource_type_id": "Support hours",
"value": 8
}
]
} }
export default ProjectServiceMock; export default ProjectServiceMock;
\ No newline at end of file
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {Link, matchPath} from 'react-router-dom';
import { routes } from '../../routes';
export class AppBreadcrumb extends Component {
static propTypes = {
match: PropTypes.object,
}
constructor(props) {
super(props);
this.state = {
breadcrumbs: []
}
}
componentDidUpdate(prev) {
if (prev.location.pathname !== this.props.location.pathname) {
this.onRoute();
}
}
componentDidMount() {
this.onRoute();
}
onRoute() {
const { breadcrumbs } = this.state;
const currentRoute = routes.find(route => matchPath(this.props.location.pathname, {path: route.path, exact: true, strict: true}));
//for intial route ,there wont be any route object so it failed
if(!currentRoute){
return;
}
if (!breadcrumbs.length) {
this.setState({ breadcrumbs: [{...this.props.location, name: currentRoute.name}] });
return;
}
const index = breadcrumbs.map(i => i.name).indexOf(currentRoute.name);
if (index === -1) {
this.setState({ breadcrumbs: [...breadcrumbs, {...this.props.location, name: currentRoute.name}] });
return;
}
this.setState({ breadcrumbs: breadcrumbs.slice(0, index+1) });
}
onNavigate(item) {
this.props.history.push({
pathname: item.pathname,
state: item.state
});
}
render() {
const { breadcrumbs } = this.state;
return (
<div className="p-breadcrumb" >
<span className="pi b-home"><Link className="b-link pi pi-home" to="/"/></span>
{breadcrumbs.map((item, index) => (
<span key={"bc_" + index} >
<li className="pi pi-chevron-right b-separator"></li>
{index !== breadcrumbs.length - 1 ?
<span className="b-link" onClick={() => this.onNavigate(item)}>{item.name}</span>
: <span className="b-text">{item.name}</span>}
</span>
))}
</div>
);
}
}
\ No newline at end of file
import Loader from 'react-loader-spinner';
import React, { Component } from 'react'
export class AppLoader extends Component{
render() {
const load = {
width: "100%",
height: "100",
display: "flex",
justifyContent: "center"
}
return (
<div style={load}>
<Loader type="ThreeDots" color="#004B93" height={80} width={80} />
</div>
);
}
}
export default AppLoader
\ No newline at end of file
import React, { Component } from 'react'; import React, { Component } from 'react';
import {NavLink} from 'react-router-dom' import {NavLink} from 'react-router-dom'
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import classNames from 'classnames'; import classNames from 'classnames';
...@@ -52,6 +52,7 @@ class AppSubmenu extends Component { ...@@ -52,6 +52,7 @@ class AppSubmenu extends Component {
} }
} }
renderLinkContent(item) { renderLinkContent(item) {
let submenuIcon = item.items && <i className="pi pi-fw pi-angle-down menuitem-toggle-icon"></i>; let submenuIcon = item.items && <i className="pi pi-fw pi-angle-down menuitem-toggle-icon"></i>;
let badge = item.badge && <span className="menuitem-badge">{item.badge}</span>; let badge = item.badge && <span className="menuitem-badge">{item.badge}</span>;
......
body .p-breadcrumb{
background-color: #F1F1F1;
padding: 0em;
padding-left: 5px;
padding-top: 5px;
margin-bottom: 5px;
box-shadow: 2px 2px #d6d9d9;
border: none;
}
.p-breadcrumb .pi{
color: #535252;
font-size : large;
}
.b-link {
font-size: 14px;
color:#007bff;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
.b-text {
font-size: 14px;
columns: #464748;
}
.b-separator {
font-size: 0.9rem !important;
margin: 0 5px;
}
.b-home {
position: relative;
top: 2px;
}
...@@ -10,4 +10,5 @@ ...@@ -10,4 +10,5 @@
@import "./_responsive"; @import "./_responsive";
@import "./_utils"; @import "./_utils";
@import "./_dashboard"; @import "./_dashboard";
@import "./_viewtable"; @import "./_breadcrumb";
\ No newline at end of file @import "./_viewtable";
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
position: fixed; position: fixed;
width: 250px; width: 250px;
height: 100%; height: 100%;
// z-index: 999;
overflow-y: auto; overflow-y: auto;
user-select: none; user-select: none;
-moz-user-select: none; -moz-user-select: none;
...@@ -11,11 +10,6 @@ ...@@ -11,11 +10,6 @@
@include transition(left $transitionDuration); @include transition(left $transitionDuration);
@include shadow(0 0 6px 0 rgba(0, 0, 0, 0.16)); @include shadow(0 0 6px 0 rgba(0, 0, 0, 0.16));
.layout-logo {
text-align: center;
margin-top: 24px;
}
.menuitem-badge { .menuitem-badge {
display: inline-block; display: inline-block;
margin-left: 4px; margin-left: 4px;
...@@ -28,4 +22,5 @@ ...@@ -28,4 +22,5 @@
background-color: $menuitemBadgeBgColor; background-color: $menuitemBadgeBgColor;
@include border-radius(50%); @include border-radius(50%);
} }
} }
\ No newline at end of file
import React, {Component} from 'react'; import Cyclelist from './list';
import CycleList from './CycleList' export {Cyclelist};
\ No newline at end of file
export class Cycle extends Component {
constructor(props){
super(props)
this.state = {
cyclelist: []
}
}
render() {
return (
<>
<h2>Cycle List</h2>
<CycleList/>
</>
);
}
}
export default Cycle;
\ No newline at end of file
import React, { Component } from 'react' import React, { Component } from 'react'
import 'primeflex/primeflex.css'; import 'primeflex/primeflex.css';
import ViewTable from './../../components/ViewTable'; import { Link } from 'react-router-dom/cjs/react-router-dom.min';
import ViewTable from '../../components/ViewTable';
import CycleService from '../../services/cycle.service'; import CycleService from '../../services/cycle.service';
import UnitConversion from '../../utils/unit.converter'; import UnitConversion from '../../utils/unit.converter';
import AppLoader from '../../layout/components/AppLoader';
class CycleList extends Component{ class CycleList extends Component{
constructor(props){ constructor(props){
...@@ -50,28 +52,28 @@ class CycleList extends Component{ ...@@ -50,28 +52,28 @@ class CycleList extends Component{
"Lofar Observing Time Commissioning (hr)" : "filter-input-50", "Lofar Observing Time Commissioning (hr)" : "filter-input-50",
"Lofar Observing Time Prio A (hr)" : "filter-input-50", "Lofar Observing Time Prio A (hr)" : "filter-input-50",
"Lofar Observing Time Prio B (hr)" : "filter-input-50" "Lofar Observing Time Prio B (hr)" : "filter-input-50"
}] }],
isprocessed: false,
isLoading: true
} }
} }
secondsToHours(d,type='seconds') { conversion(d,type) {
return UnitConversion.getUIResourceUnit(type,d) const coversionType = this.state.resources.find(i => i.name === type).quantity_value;
return UnitConversion.getUIResourceUnit(coversionType,d)
} }
toBytes(d,type='bytes'){ componentDidMount(){
return UnitConversion.getUIResourceUnit(type,d)
}
componentDidMount(){
const { projectCategory} = this.state; const { projectCategory} = this.state;
const { periodCategory} = this.state; const { periodCategory} = this.state;
const promises = [CycleService.getProjects(), CycleService.getCycleQuota()] const promises = [CycleService.getProjects(), CycleService.getCycleQuota(),CycleService.getResources()]
Promise.all(promises).then(responses => { Promise.all(promises).then(responses => {
const projects = responses[0]; const projects = responses[0];
const cycleQuota = responses[1]; const cycleQuota = responses[1];
CycleService.getAllCycle().then(cyclelist =>{ this.setState({ resources: responses[2].data.results });
const results = cyclelist.data.results || []; CycleService.getAllCycles().then(cyclelist =>{
const results = cyclelist || [];
results.map(cycle => { results.map(cycle => {
const regularProjects = projects.data.results.filter(project => project.cycles_ids.includes(cycle.name) && projectCategory.includes(project.project_category_value)); const regularProjects = projects.data.results.filter(project => project.cycles_ids.includes(cycle.name) && projectCategory.includes(project.project_category_value));
const longterm = projects.data.results.filter(project => project.cycles_ids.includes(cycle.name) && periodCategory.includes(project.period_category_value)); const longterm = projects.data.results.filter(project => project.cycles_ids.includes(cycle.name) && periodCategory.includes(project.period_category_value));
...@@ -81,18 +83,20 @@ class CycleList extends Component{ ...@@ -81,18 +83,20 @@ class CycleList extends Component{
cycle.id = cycle.name ? cycle.name.split(' ').join('') : cycle.name; cycle.id = cycle.name ? cycle.name.split(' ').join('') : cycle.name;
cycle.regularProjects = regularProjects.length; cycle.regularProjects = regularProjects.length;
cycle.longterm = longterm.length; cycle.longterm = longterm.length;
cycle.observingTime = this.secondsToHours((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'observing_time') || {value: 0}).value) cycle.observingTime = this.conversion((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'observing_time') || {value: 0}).value, 'observing_time')
cycle.processingTime = this.secondsToHours((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'cep_processing_time') || {value: 0}).value) cycle.processingTime = this.conversion((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'cep_processing_time') || {value: 0}).value, 'cep_processing_time')
cycle.ltaResources = this.toBytes((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'lta_storage') || {value: 0}).value) cycle.ltaResources = this.conversion((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'lta_storage') || {value: 0}).value, 'lta_storage')
cycle.support = this.secondsToHours((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'support_time') || {value: 0}).value) cycle.support = this.conversion((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'support_time') || {value: 0}).value, 'support_time')
cycle.observingTimeDDT = this.secondsToHours((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'observing_time_commissioning') || {value: 0}).value) cycle.observingTimeDDT = this.conversion((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'observing_time_commissioning') || {value: 0}).value, 'observing_time_commissioning')
cycle.observingTimePrioA = this.secondsToHours((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'observing_time_prio_a') || {value: 0}).value) cycle.observingTimePrioA = this.conversion((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'observing_time_prio_a') || {value: 0}).value, 'observing_time_prio_a')
cycle.observingTimePrioB = this.secondsToHours((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'observing_time_prio_b') || {value: 0}).value) cycle.observingTimePrioB = this.conversion((cycleQuota.data.results.find(quota => quota.cycle_id === cycle.name && quota.resource_type_id === 'observing_time_prio_b') || {value: 0}).value, 'observing_time_prio_b')
cycle.actionpath = "/cycle"; cycle.actionpath = "/cycle";
return cycle; return cycle;
}); });
this.setState({ this.setState({
cyclelist : results cyclelist : results,
isprocessed: true,
isLoading: false
}); });
}) })
}) })
...@@ -101,6 +105,16 @@ class CycleList extends Component{ ...@@ -101,6 +105,16 @@ class CycleList extends Component{
render(){ render(){
return ( return (
<> <>
<div className="p-grid">
<div className="p-col-10 p-lg-10 p-md-10">
<h2>Cycle - List </h2>
</div>
<div className="p-col-2 p-lg-2 p-md-2">
<Link to={{ pathname: '/cycle'}} title="Add New Cycle" style={{float: "right"}}>
<i className="fa fa-plus-square" style={{marginTop: "10px"}}></i>
</Link>
</div>
</div>
{/* {/*
* Call View table to show table data, the parameters are, * Call View table to show table data, the parameters are,
data - Pass API data data - Pass API data
...@@ -108,7 +122,9 @@ class CycleList extends Component{ ...@@ -108,7 +122,9 @@ class CycleList extends Component{
showaction - {true/false} -> to show the action column showaction - {true/false} -> to show the action column
paths - specify the path for navigation - Table will set "id" value for each row in action button paths - specify the path for navigation - Table will set "id" value for each row in action button
*/} */}
{(this.state.cyclelist && this.state.cyclelist.length) ?
{this.state.isLoading? <AppLoader /> : this.state.isprocessed &&(this.state.cyclelist && this.state.cyclelist.length) ?
<ViewTable <ViewTable
data={this.state.cyclelist} data={this.state.cyclelist}
defaultcolumns={this.state.defaultcolumns} defaultcolumns={this.state.defaultcolumns}
...@@ -116,8 +132,11 @@ class CycleList extends Component{ ...@@ -116,8 +132,11 @@ class CycleList extends Component{
columnclassname = {this.state.columnclassname} columnclassname = {this.state.columnclassname}
showaction="true" showaction="true"
paths={this.state.paths} paths={this.state.paths}
/> : <></> /> : <></>
} }
</> </>
) )
} }
......
import "babel-polyfill";
import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { render, fireEvent } from '@testing-library/react';
import CycleList from './list';
import UnitConversion from '../../utils/unit.converter';
import mockData from '../../__mocks__/cycle.service.data';
jest.mock('../../services/cycle.service', () => {
return {
getProjects: () => Promise.resolve({ data: mockData.getProjects }),
getCycleQuota: () => Promise.resolve({ data: mockData.getCycleQuota }),
getAllCycles: () => Promise.resolve(mockData.getAllCycle.results ),
getResources: () => Promise.resolve({ data: mockData.getresources })
}
});
const flushPromises = () => new Promise(setImmediate);
describe('<CycleList />', () => {
test('render table in the cycle list', async () => {
const { container } = render(<MemoryRouter><CycleList /></MemoryRouter>);
await flushPromises();
expect(container.querySelector('table')).toBeInTheDocument();
});
test('render cycle list in row', async () => {
const { container } = render(<MemoryRouter><CycleList /></MemoryRouter>);
await flushPromises();
expect(container.querySelectorAll('tr').length).toBe(4);
});
test('render columns in the cycle list', async () => {
const { container } = render(<MemoryRouter><CycleList /></MemoryRouter>);
await flushPromises();
expect(container.querySelectorAll('th').length).toBe(11);
});
test('render cycleId - cycle name conversion', async () => {
const { container } = render(<MemoryRouter><CycleList /></MemoryRouter>);
await flushPromises();
expect(container.querySelectorAll('tr')[1].innerHTML.includes('Cycle00')).toBeTruthy();
});
test('render observing time in hours', async () => {
const { container } = render(<MemoryRouter><CycleList /></MemoryRouter>);
await flushPromises();
const observing_time = Math.floor(Number(mockData.getCycleQuota.results[0].value) / 3600);
expect(container.querySelectorAll('tr')[1].innerHTML.includes(observing_time)).toBeTruthy();
});
test('render commissioning time in hours', async () => {
const { container } = render(<MemoryRouter><CycleList /></MemoryRouter>);
await flushPromises();
const commissioning_time = UnitConversion.getUIResourceUnit('bytes',Number(mockData.getCycleQuota.results[1].value));
expect(container.querySelectorAll('tr')[1].innerHTML.includes(commissioning_time)).toBeTruthy();
});
test('toggle columns in table', async () => {
const { container } = render(<MemoryRouter><CycleList /></MemoryRouter>);
await flushPromises();
const panel = container.querySelector('#overlay_panel');
expect(panel).toHaveStyle('display: block');
fireEvent.click(container.querySelector('.col-filter-btn'));
await flushPromises();
expect(panel).toHaveStyle('display: none');
expect(container.querySelectorAll("input[type=checkbox]:checked").length).toBe(container.querySelectorAll('th').length)
});
});
\ No newline at end of file
import React, {Component} from 'react'; import React, {Component} from 'react';
export class Dashboard extends Component { export class Dashboard extends Component {
constructor(props){ constructor(props){
......
import React, {Component} from 'react';
/**
* Component to get input for Resource allocation while creating and editing Project
*/
class ResourceDisplayList extends Component {
constructor(props) {
super(props);
this.state = {
projectQuota: props.projectQuota
}
}
render(){
return (
<>
{this.props.projectQuota.length>0 && this.props.projectQuota.map((item, index) => (
<React.Fragment key={index+10}>
<label key={'label1-'+ index} className="col-lg-3 col-md-3 col-sm-12">{item.resource.name}</label>
<span key={'div1-'+ index} className="col-lg-3 col-md-3 col-sm-12">
{item.value/(this.props.unitMap[item.resource.quantity_value]?this.props.unitMap[item.resource.quantity_value].conversionFactor:1)}
{` ${this.props.unitMap[item.resource.quantity_value]?this.props.unitMap[item.resource.quantity_value].display:''}`}
</span>
</React.Fragment>
))}
</>
);
}
}
export default ResourceDisplayList;
\ No newline at end of file
...@@ -16,7 +16,7 @@ export class ResourceInputList extends Component { ...@@ -16,7 +16,7 @@ export class ResourceInputList extends Component {
} }
shouldComponentUpdate() { shouldComponentUpdate() {
return this.updateEnabled; return true;
} }
onInputChange(field, event) { onInputChange(field, event) {
...@@ -39,9 +39,10 @@ export class ResourceInputList extends Component { ...@@ -39,9 +39,10 @@ export class ResourceInputList extends Component {
<label key={'label1-'+ index} className="col-lg-3 col-md-3 col-sm-12">{item.name}</label> <label key={'label1-'+ index} className="col-lg-3 col-md-3 col-sm-12">{item.name}</label>
<div key={'div1-'+ index} className="col-lg-3 col-md-3 col-sm-12"> <div key={'div1-'+ index} className="col-lg-3 col-md-3 col-sm-12">
<InputNumber key={'item1-'+ index} id={'item1-'+ index} name={'item1-'+ index} <InputNumber key={'item1-'+ index} id={'item1-'+ index} name={'item1-'+ index}
suffix={` ${this.props.unitMap[item.resourceUnit.name].display}`} suffix={` ${this.props.unitMap[item.quantity_value]?this.props.unitMap[item.quantity_value].display:''}`}
placeholder={item.name} placeholder={` ${this.props.unitMap[item.quantity_value]?this.props.unitMap[item.quantity_value].display:item.name}`} min={0} useGrouping={false}
value={this.state.projectQuota[item.name]} value={this.state.projectQuota[item.name]}
onChange={(e) => this.onInputChange(item.name, e)}
onBlur={(e) => this.onInputChange(item.name, e)} onBlur={(e) => this.onInputChange(item.name, e)}
style={{width:"90%", marginRight: "5px"}} style={{width:"90%", marginRight: "5px"}}
/> />
......
...@@ -14,6 +14,7 @@ import {Growl} from 'primereact/components/growl/Growl'; ...@@ -14,6 +14,7 @@ import {Growl} from 'primereact/components/growl/Growl';
import {ResourceInputList} from './ResourceInputList'; import {ResourceInputList} from './ResourceInputList';
import AppLoader from '../../layout/components/AppLoader';
import CycleService from '../../services/cycle.service'; import CycleService from '../../services/cycle.service';
import ProjectService from '../../services/project.service'; import ProjectService from '../../services/project.service';
import UnitConverter from '../../utils/unit.converter'; import UnitConverter from '../../utils/unit.converter';
...@@ -25,11 +26,12 @@ export class ProjectCreate extends Component { ...@@ -25,11 +26,12 @@ export class ProjectCreate extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
isLoading: true,
dialog: { header: '', detail: ''}, dialog: { header: '', detail: ''},
project: { project: {
trigger_priority: 1000, trigger_priority: 1000,
priority_rank: null, priority_rank: null,
project_quota: [], // Mandatory Field in the back end, so an empty array is passed quota: [], // Mandatory Field in the back end, so an empty array is passed
can_trigger: false can_trigger: false
}, },
projectQuota: {}, // Resource Allocations projectQuota: {}, // Resource Allocations
...@@ -59,6 +61,7 @@ export class ProjectCreate extends Component { ...@@ -59,6 +61,7 @@ export class ProjectCreate extends Component {
this.projectResourceDefaults = {}; // Default values for default resources this.projectResourceDefaults = {}; // Default values for default resources
this.resourceUnitMap = UnitConverter.resourceUnitMap; // Resource unit conversion factor and constraints this.resourceUnitMap = UnitConverter.resourceUnitMap; // Resource unit conversion factor and constraints
this.cycleOptionTemplate = this.cycleOptionTemplate.bind(this); // Template for cycle multiselect this.cycleOptionTemplate = this.cycleOptionTemplate.bind(this); // Template for cycle multiselect
this.tooltipOptions = {position: 'left', event: 'hover', className:"p-tooltip-custom"};
this.setProjectQuotaDefaults = this.setProjectQuotaDefaults.bind(this); this.setProjectQuotaDefaults = this.setProjectQuotaDefaults.bind(this);
this.setProjectParams = this.setProjectParams.bind(this); this.setProjectParams = this.setProjectParams.bind(this);
...@@ -93,7 +96,7 @@ export class ProjectCreate extends Component { ...@@ -93,7 +96,7 @@ export class ProjectCreate extends Component {
resourceList = _.sortBy(resourceList, "name"); resourceList = _.sortBy(resourceList, "name");
const resources = _.remove(resourceList, function(resource) { return _.find(defaultResources, {'name': resource.name})!=null }); const resources = _.remove(resourceList, function(resource) { return _.find(defaultResources, {'name': resource.name})!=null });
const projectQuota = this.setProjectQuotaDefaults(resources); const projectQuota = this.setProjectQuotaDefaults(resources);
this.setState({resourceList: resourceList, resources: resources, projectQuota: projectQuota}); this.setState({resourceList: resourceList, resources: resources, projectQuota: projectQuota, isLoading: false});
}); });
// ProjectService.getProjects().then(projects => { // ProjectService.getProjects().then(projects => {
// console.log(projects); // console.log(projects);
...@@ -118,7 +121,8 @@ export class ProjectCreate extends Component { ...@@ -118,7 +121,8 @@ export class ProjectCreate extends Component {
setProjectQuotaDefaults(resources) { setProjectQuotaDefaults(resources) {
let projectQuota = this.state.projectQuota; let projectQuota = this.state.projectQuota;
for (const resource of resources) { for (const resource of resources) {
projectQuota[resource['name']] = this.projectResourceDefaults[resource.name]/this.resourceUnitMap[resource.resourceUnit.name].conversionFactor; const conversionFactor = this.resourceUnitMap[resource.quantity_value]?this.resourceUnitMap[resource.quantity_value].conversionFactor:1;
projectQuota[resource['name']] = this.projectResourceDefaults[resource.name]/conversionFactor;
} }
return projectQuota; return projectQuota;
} }
...@@ -156,9 +160,19 @@ export class ProjectCreate extends Component { ...@@ -156,9 +160,19 @@ export class ProjectCreate extends Component {
* @param {string} key * @param {string} key
* @param {any} value * @param {any} value
*/ */
setProjectParams(key, value) { setProjectParams(key, value, type) {
let project = this.state.project; let project = this.state.project;
project[key] = value; switch(type) {
case 'NUMBER': {
console.log("Parsing Number");
project[key] = value?parseInt(value):0;
break;
}
default: {
project[key] = value;
break;
}
}
this.setState({project: project, validForm: this.validateForm(key)}); this.setState({project: project, validForm: this.validateForm(key)});
} }
...@@ -168,19 +182,23 @@ export class ProjectCreate extends Component { ...@@ -168,19 +182,23 @@ export class ProjectCreate extends Component {
* @param {InputEvent} event * @param {InputEvent} event
*/ */
setProjectQuotaParams(key, event) { setProjectQuotaParams(key, event) {
let projectQuota = this.state.projectQuota;
if (event.target.value) { if (event.target.value) {
let projectQuota = this.state.projectQuota;
let resource = _.find(this.state.resources, {'name': key}); let resource = _.find(this.state.resources, {'name': key});
const resourceUnit = resource?resource.resourceUnit:null;
// console.log(resourceUnit); let newValue = 0;
if (resourceUnit) { if (this.resourceUnitMap[resource.quantity_value] &&
projectQuota[key] = event.target.value.replace(this.resourceUnitMap[resourceUnit.name].display,''); event.target.value.toString().indexOf(this.resourceUnitMap[resource.quantity_value].display)>=0) {
newValue = event.target.value.replace(this.resourceUnitMap[resource.quantity_value].display,'');
} else { } else {
projectQuota[key] = event.target.value; newValue = event.target.value;
} }
// console.log(`${key} - ${event.target.value}`); projectQuota[key] = (newValue==="NaN" || isNaN(newValue))?0:newValue;
this.setState({projectQuota: projectQuota}); } else {
let projectQuota = this.state.projectQuota;
projectQuota[key] = 0;
} }
this.setState({projectQuota: projectQuota});
} }
/** /**
...@@ -238,7 +256,7 @@ export class ProjectCreate extends Component { ...@@ -238,7 +256,7 @@ export class ProjectCreate extends Component {
let resourceType = _.find(this.state.resources, {'name': resource}); let resourceType = _.find(this.state.resources, {'name': resource});
let quota = { project: this.state.project.name, let quota = { project: this.state.project.name,
resource_type: resourceType['url'], resource_type: resourceType['url'],
value: this.state.projectQuota[resource] * this.resourceUnitMap[resourceType.resourceUnit.name].conversionFactor}; value: this.state.projectQuota[resource] * (this.resourceUnitMap[resourceType.quantity_value]?this.resourceUnitMap[resourceType.quantity_value].conversionFactor:1)};
projectQuota.push(quota); projectQuota.push(quota);
} }
ProjectService.saveProject(this.state.project, this.defaultResourcesEnabled?projectQuota:[]) ProjectService.saveProject(this.state.project, this.defaultResourcesEnabled?projectQuota:[])
...@@ -263,7 +281,7 @@ export class ProjectCreate extends Component { ...@@ -263,7 +281,7 @@ export class ProjectCreate extends Component {
* Function to cancel form creation and navigate to other page/component * Function to cancel form creation and navigate to other page/component
*/ */
cancelCreate() { cancelCreate() {
this.setState({redirect: '/project/list'}); this.setState({redirect: '/project'});
} }
/** /**
...@@ -274,7 +292,6 @@ export class ProjectCreate extends Component { ...@@ -274,7 +292,6 @@ export class ProjectCreate extends Component {
let prevResources = this.state.resources; let prevResources = this.state.resources;
let resourceList = []; let resourceList = [];
let resources = []; let resources = [];
const defaultResources = this.defaultResources;
if (resources) { if (resources) {
// const nonDefaultResources = _.remove(resources, function(resource) { return _.find(defaultResources, {'name': resource.name})==null }); // const nonDefaultResources = _.remove(resources, function(resource) { return _.find(defaultResources, {'name': resource.name})==null });
// resourceList = nonDefaultResources.concat(this.state.resourceList); // resourceList = nonDefaultResources.concat(this.state.resourceList);
...@@ -317,15 +334,17 @@ export class ProjectCreate extends Component { ...@@ -317,15 +334,17 @@ export class ProjectCreate extends Component {
<div className="p-grid"> <div className="p-grid">
<Growl ref={(el) => this.growl = el} /> <Growl ref={(el) => this.growl = el} />
<div className="p-col-10 p-lg-3 p-md-4"> <div className="p-col-10 p-lg-10 p-md-10">
<h2>Project - Add</h2> <h2>Project - Add</h2>
</div> </div>
<div className="p-col-2 p-lg-3 p-md-4"> <div className="p-col-2 p-lg-2 p-md-2">
<Link to={{ pathname: '/project'}} tooltip="Close Edit" > <Link to={{ pathname: '/project'}} tite="Close Edit" style={{float: "right"}}>
<i className="fa fa-window-close" style={{marginTop: "10px"}}></i> <i className="fa fa-window-close" style={{marginTop: "10px"}}></i>
</Link> </Link>
</div> </div>
</div> </div>
{ this.state.isLoading ? <AppLoader /> :
<>
<div> <div>
<div className="p-fluid"> <div className="p-fluid">
<div className="p-field p-grid" style={{display: 'none'}}> <div className="p-field p-grid" style={{display: 'none'}}>
...@@ -338,21 +357,23 @@ export class ProjectCreate extends Component { ...@@ -338,21 +357,23 @@ export class ProjectCreate extends Component {
<label htmlFor="projectName" className="col-lg-2 col-md-2 col-sm-12">Name <span style={{color:'red'}}>*</span></label> <label htmlFor="projectName" className="col-lg-2 col-md-2 col-sm-12">Name <span style={{color:'red'}}>*</span></label>
<div className="col-lg-4 col-md-4 col-sm-12"> <div className="col-lg-4 col-md-4 col-sm-12">
<InputText className={this.state.errors.name ?'input-error':''} id="projectName" data-testid="name" <InputText className={this.state.errors.name ?'input-error':''} id="projectName" data-testid="name"
tooltip="Enter name of the project" tooltipOptions={this.tooltipOptions} maxLength="128"
value={this.state.project.name} value={this.state.project.name}
onChange={(e) => this.setProjectParams('name', e.target.value)} onChange={(e) => this.setProjectParams('name', e.target.value)}
onBlur={(e) => this.setProjectParams('name', e.target.value)}/> onBlur={(e) => this.setProjectParams('name', e.target.value)}/>
<label className="error"> <label className={this.state.errors.name?"error":"info"}>
{this.state.errors.name ? this.state.errors.name : ""} {this.state.errors.name ? this.state.errors.name : "Max 128 characters"}
</label> </label>
</div> </div>
<label htmlFor="description" className="col-lg-2 col-md-2 col-sm-12">Description <span style={{color:'red'}}>*</span></label> <label htmlFor="description" className="col-lg-2 col-md-2 col-sm-12">Description <span style={{color:'red'}}>*</span></label>
<div className="col-lg-4 col-md-4 col-sm-12"> <div className="col-lg-4 col-md-4 col-sm-12">
<InputTextarea className={this.state.errors.description ?'input-error':''} rows={3} cols={30} <InputTextarea className={this.state.errors.description ?'input-error':''} rows={3} cols={30}
tooltip="Short description of the project" tooltipOptions={this.tooltipOptions} maxLength="128"
data-testid="description" value={this.state.project.description} data-testid="description" value={this.state.project.description}
onChange={(e) => this.setProjectParams('description', e.target.value)} onChange={(e) => this.setProjectParams('description', e.target.value)}
onBlur={(e) => this.setProjectParams('description', e.target.value)}/> onBlur={(e) => this.setProjectParams('description', e.target.value)}/>
<label className="error"> <label className={this.state.errors.description ?"error":"info"}>
{this.state.errors.description ? this.state.errors.description : ""} {this.state.errors.description ? this.state.errors.description : "Max 255 characters"}
</label> </label>
</div> </div>
</div> </div>
...@@ -360,9 +381,10 @@ export class ProjectCreate extends Component { ...@@ -360,9 +381,10 @@ export class ProjectCreate extends Component {
<label htmlFor="triggerPriority" className="col-lg-2 col-md-2 col-sm-12">Trigger Priority </label> <label htmlFor="triggerPriority" className="col-lg-2 col-md-2 col-sm-12">Trigger Priority </label>
<div className="col-lg-4 col-md-4 col-sm-12" data-testid="trig_prio"> <div className="col-lg-4 col-md-4 col-sm-12" data-testid="trig_prio">
<InputNumber inputId="trig_prio" name="trig_prio" value={this.state.project.trigger_priority} <InputNumber inputId="trig_prio" name="trig_prio" value={this.state.project.trigger_priority}
tooltip="Priority of this project w.r.t. triggers" tooltipOptions={this.tooltipOptions}
mode="decimal" showButtons min={0} max={1001} step={10} useGrouping={false} mode="decimal" showButtons min={0} max={1001} step={10} useGrouping={false}
onChange={(e) => this.setProjectParams('trigger_priority', e.target.value)} onChange={(e) => this.setProjectParams('trigger_priority', e.value)}
onBlur={(e) => this.setProjectParams('trigger_priority', e.target.value)} /> onBlur={(e) => this.setProjectParams('trigger_priority', e.target.value, 'NUMBER')} />
<label className="error"> <label className="error">
{this.state.errors.trigger_priority ? this.state.errors.trigger_priority : ""} {this.state.errors.trigger_priority ? this.state.errors.trigger_priority : ""}
...@@ -370,13 +392,17 @@ export class ProjectCreate extends Component { ...@@ -370,13 +392,17 @@ export class ProjectCreate extends Component {
</div> </div>
<label htmlFor="trigger" className="col-lg-2 col-md-2 col-sm-12">Allows Trigger Submission</label> <label htmlFor="trigger" className="col-lg-2 col-md-2 col-sm-12">Allows Trigger Submission</label>
<div className="col-lg-4 col-md-4 col-sm-12" data-testid="trigger"> <div className="col-lg-4 col-md-4 col-sm-12" data-testid="trigger">
<Checkbox inputId="trigger" role="trigger" checked={this.state.project.can_trigger} onChange={e => this.setProjectParams('can_trigger', e.target.checked)}></Checkbox> <Checkbox inputId="trigger" role="trigger"
tooltip="Is this project allowed to supply observation requests on the fly, possibly interrupting currently running observations (responsive telescope)?"
tooltipOptions={this.tooltipOptions}
checked={this.state.project.can_trigger} onChange={e => this.setProjectParams('can_trigger', e.target.checked)}></Checkbox>
</div> </div>
</div> </div>
<div className="p-field p-grid"> <div className="p-field p-grid">
<label htmlFor="projCat" className="col-lg-2 col-md-2 col-sm-12">Project Category </label> <label htmlFor="projCat" className="col-lg-2 col-md-2 col-sm-12">Project Category </label>
<div className="col-lg-4 col-md-4 col-sm-12" data-testid="projCat" > <div className="col-lg-4 col-md-4 col-sm-12" data-testid="projCat" >
<Dropdown inputId="projCat" optionLabel="name" optionValue="id" <Dropdown inputId="projCat" optionLabel="value" optionValue="url"
tooltip="Project Category" tooltipOptions={this.tooltipOptions}
value={this.state.project.project_category} value={this.state.project.project_category}
options={this.state.projectCategories} options={this.state.projectCategories}
onChange={(e) => {this.setProjectParams('project_category', e.value)}} onChange={(e) => {this.setProjectParams('project_category', e.value)}}
...@@ -384,7 +410,8 @@ export class ProjectCreate extends Component { ...@@ -384,7 +410,8 @@ export class ProjectCreate extends Component {
</div> </div>
<label htmlFor="periodCategory" className="col-lg-2 col-md-2 col-sm-12">Period Category</label> <label htmlFor="periodCategory" className="col-lg-2 col-md-2 col-sm-12">Period Category</label>
<div className="col-lg-4 col-md-4 col-sm-12"> <div className="col-lg-4 col-md-4 col-sm-12">
<Dropdown data-testid="period-cat" id="period-cat" optionLabel="name" optionValue="id" <Dropdown data-testid="period-cat" id="period-cat" optionLabel="value" optionValue="url"
tooltip="Period Category" tooltipOptions={this.tooltipOptions}
value={this.state.project.period_category} value={this.state.project.period_category}
options={this.state.periodCategories} options={this.state.periodCategories}
onChange={(e) => {this.setProjectParams('period_category',e.value)}} onChange={(e) => {this.setProjectParams('period_category',e.value)}}
...@@ -395,6 +422,7 @@ export class ProjectCreate extends Component { ...@@ -395,6 +422,7 @@ export class ProjectCreate extends Component {
<label htmlFor="triggerPriority" className="col-lg-2 col-md-2 col-sm-12">Cycle(s)</label> <label htmlFor="triggerPriority" className="col-lg-2 col-md-2 col-sm-12">Cycle(s)</label>
<div className="col-lg-4 col-md-4 col-sm-12"> <div className="col-lg-4 col-md-4 col-sm-12">
<MultiSelect data-testid="cycle" id="cycle" optionLabel="name" optionValue="url" filter={true} <MultiSelect data-testid="cycle" id="cycle" optionLabel="name" optionValue="url" filter={true}
tooltip="Cycle(s) to which this project belongs" tooltipOptions={this.tooltipOptions}
value={this.state.project.cycles} value={this.state.project.cycles}
options={this.state.cycles} options={this.state.cycles}
onChange={(e) => {this.setProjectParams('cycles',e.value)}} onChange={(e) => {this.setProjectParams('cycles',e.value)}}
...@@ -404,9 +432,11 @@ export class ProjectCreate extends Component { ...@@ -404,9 +432,11 @@ export class ProjectCreate extends Component {
<label htmlFor="projRank" className="col-lg-2 col-md-2 col-sm-12">Project Rank <span style={{color:'red'}}>*</span></label> <label htmlFor="projRank" className="col-lg-2 col-md-2 col-sm-12">Project Rank <span style={{color:'red'}}>*</span></label>
<div className="col-lg-4 col-md-4 col-sm-12" data-testid="proj-rank" > <div className="col-lg-4 col-md-4 col-sm-12" data-testid="proj-rank" >
<InputNumber inputId="proj-rank" name="rank" data-testid="rank" value={this.state.project.priority_rank} <InputNumber inputId="proj-rank" name="rank" data-testid="rank" value={this.state.project.priority_rank}
tooltip="Priority of this project w.r.t. other projects. Projects can interrupt observations of lower-priority projects."
tooltipOptions={this.tooltipOptions}
mode="decimal" showButtons min={0} max={100} mode="decimal" showButtons min={0} max={100}
onChange={(e) => this.setProjectParams('priority_rank', e.target.value)} onChange={(e) => this.setProjectParams('priority_rank', e.value)}
onBlur={(e) => this.setProjectParams('priority_rank', e.target.value)} /> onBlur={(e) => this.setProjectParams('priority_rank', e.target.value, 'NUMBER')} />
<label className="error"> <label className="error">
{this.state.errors.priority_rank ? this.state.errors.priority_rank : ""} {this.state.errors.priority_rank ? this.state.errors.priority_rank : ""}
</label> </label>
...@@ -440,17 +470,19 @@ export class ProjectCreate extends Component { ...@@ -440,17 +470,19 @@ export class ProjectCreate extends Component {
</div> </div>
</div> </div>
<div className="p-grid p-justify-start"> <div className="p-grid p-justify-start">
<div className="p-col-1"> <div className="col-lg-1 col-md-2 col-sm-6">
<Button label="Save" className="p-button-primary" id="save-btn" data-testid="save-btn" icon="pi pi-check" onClick={this.saveProject} disabled={!this.state.validForm} /> <Button label="Save" className="p-button-primary" id="save-btn" data-testid="save-btn" icon="pi pi-check" onClick={this.saveProject} disabled={!this.state.validForm} />
</div> </div>
<div className="p-col-1"> <div className="col-lg-1 col-md-2 col-sm-6">
<Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.cancelCreate} /> <Button label="Cancel" className="p-button-danger" icon="pi pi-times" onClick={this.cancelCreate} />
</div> </div>
</div> </div>
</>
}
{/* Dialog component to show messages and get input */} {/* Dialog component to show messages and get input */}
<div className="p-grid" data-testid="confirm_dialog"> <div className="p-grid" data-testid="confirm_dialog">
<Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{width: '50vw'}} inputId="confirm_dialog" <Dialog header={this.state.dialog.header} visible={this.state.dialogVisible} style={{width: '25vw'}} inputId="confirm_dialog"
modal={true} onHide={() => {this.setState({dialogVisible: false})}} modal={true} onHide={() => {this.setState({dialogVisible: false})}}
footer={<div> footer={<div>
<Button key="back" onClick={() => {this.setState({dialogVisible: false}); this.cancelCreate();}} label="No" /> <Button key="back" onClick={() => {this.setState({dialogVisible: false}); this.cancelCreate();}} label="No" />
...@@ -458,7 +490,7 @@ export class ProjectCreate extends Component { ...@@ -458,7 +490,7 @@ export class ProjectCreate extends Component {
</div> </div>
} > } >
<div className="p-grid"> <div className="p-grid">
<div className="col-lg-1 col-md-1 col-sm-2"> <div className="col-lg-2 col-md-2 col-sm-2">
<i className="pi pi-check-circle pi-large pi-success"></i> <i className="pi pi-check-circle pi-large pi-success"></i>
</div> </div>
<div className="col-lg-10 col-md-10 col-sm-10"> <div className="col-lg-10 col-md-10 col-sm-10">
......
...@@ -27,9 +27,9 @@ afterEach(() => { ...@@ -27,9 +27,9 @@ afterEach(() => {
*/ */
const setMockSpy = (() => { const setMockSpy = (() => {
projectCategoriesSpy = jest.spyOn(ProjectService, 'getProjectCategories'); projectCategoriesSpy = jest.spyOn(ProjectService, 'getProjectCategories');
projectCategoriesSpy.mockImplementation(() => { return Promise.resolve([{id: 1, name: 'Regular'}])}); projectCategoriesSpy.mockImplementation(() => { return Promise.resolve(ProjectServiceMock.project_categories)});
periodCategoriesSpy = jest.spyOn(ProjectService, 'getPeriodCategories'); periodCategoriesSpy = jest.spyOn(ProjectService, 'getPeriodCategories');
periodCategoriesSpy.mockImplementation(() => { return Promise.resolve([{id: 1, name: 'Single Cycle'}])}); periodCategoriesSpy.mockImplementation(() => { return Promise.resolve(ProjectServiceMock.period_categories)});
allCycleSpy = jest.spyOn(CycleService, 'getAllCycles'); allCycleSpy = jest.spyOn(CycleService, 'getAllCycles');
allCycleSpy.mockImplementation(() => { allCycleSpy.mockImplementation(() => {
return Promise.resolve([{url: "http://localhost:3000/api/cycle/Cycle-0", name: 'Cycle-0'}, return Promise.resolve([{url: "http://localhost:3000/api/cycle/Cycle-0", name: 'Cycle-0'},
...@@ -143,7 +143,7 @@ it("renders Save button enabled when all data entered", async () => { ...@@ -143,7 +143,7 @@ it("renders Save button enabled when all data entered", async () => {
// Before selecting Project Category // Before selecting Project Category
expect(content.queryAllByText('Select Project Category').length).toBe(2); expect(content.queryAllByText('Select Project Category').length).toBe(2);
expect(content.queryAllByText('Regular').length).toBe(1); expect(content.queryAllByText('Regular').length).toBe(1);
expect(content.getAllByRole("listbox")[0].children.length).toBe(1); expect(content.getAllByRole("listbox")[0].children.length).toBe(2);
fireEvent.click(projCatInput); fireEvent.click(projCatInput);
// After selecting Project Category // After selecting Project Category
expect(content.queryAllByText('Select Project Category').length).toBe(1); expect(content.queryAllByText('Select Project Category').length).toBe(1);
...@@ -152,7 +152,7 @@ it("renders Save button enabled when all data entered", async () => { ...@@ -152,7 +152,7 @@ it("renders Save button enabled when all data entered", async () => {
// Before selecting Period Category // Before selecting Period Category
expect(content.queryAllByText('Select Period Category').length).toBe(2); expect(content.queryAllByText('Select Period Category').length).toBe(2);
expect(content.queryAllByText('Single Cycle').length).toBe(1); expect(content.queryAllByText('Single Cycle').length).toBe(1);
expect(content.getAllByRole("listbox")[1].children.length).toBe(1); expect(content.getAllByRole("listbox")[1].children.length).toBe(2);
fireEvent.click(projPeriodInput); fireEvent.click(projPeriodInput);
// After selecting Period Category // After selecting Period Category
expect(content.queryAllByText('Select Period Category').length).toBe(1); expect(content.queryAllByText('Select Period Category').length).toBe(1);
......
This diff is collapsed.
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { act } from "react-dom/test-utils";
import { render, cleanup, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom/extend-expect';
import _ from 'lodash';
import moment from 'moment';
import {ProjectEdit} from './edit';
import ProjectService from '../../services/project.service';
import CycleService from '../../services/cycle.service';
import ProjectServiceMock from '../../__mocks__/project.service.data';
let projectCategoriesSpy, allCycleSpy, periodCategoriesSpy, projectDetailsSpy, resourcesSpy, projectQuotaSpy,
updateProjectSpy, savePQSpy, updatePQSpy, deletePQSpy;
beforeEach(() => {
setMockSpy();
});
afterEach(() => {
// cleanup on exiting
clearMockSpy();
cleanup();
});
/**
* To set mock spy for Services that have API calls to the back end to fetch data
*/
const setMockSpy = (() => {
projectCategoriesSpy = jest.spyOn(ProjectService, 'getProjectCategories');
projectCategoriesSpy.mockImplementation(() => { return Promise.resolve([{url: "Regular", value: 'Regular'}, {url: "User Shared Support", value: 'User Shared Support'}])});
periodCategoriesSpy = jest.spyOn(ProjectService, 'getPeriodCategories');
periodCategoriesSpy.mockImplementation(() => { return Promise.resolve([{url: "Single Cycle", value: 'Single Cycle'}, {url: "Long Term", value: 'Long Term'}])});
allCycleSpy = jest.spyOn(CycleService, 'getAllCycles');
allCycleSpy.mockImplementation(() => {
return Promise.resolve([{url: "http://localhost:3000/api/cycle/Cycle-0/", name: 'Cycle-0'},
{url: "http://localhost:3000/api/cycle/Cycle-1/", name: 'Cycle-1'},
{url: "http://192.168.99.100:8008/api/cycle/Cycle%200/", name: 'Cycle 0'}]);
});
projectDetailsSpy = jest.spyOn(ProjectService, 'getProjectDetails');
projectDetailsSpy.mockImplementation((id) => {
return Promise.resolve(_.find(ProjectServiceMock.project, {name: id}))});
resourcesSpy = jest.spyOn(ProjectService, 'getResources');
resourcesSpy.mockImplementation(() => {
// console.log(ProjectServiceMock.resources);
let resourceList= [];
Object.assign(resourceList, ProjectServiceMock.resources);
return Promise.resolve(resourceList);
});
projectQuotaSpy = jest.spyOn(ProjectService, 'getProjectQuota');
projectQuotaSpy.mockImplementation((id) => {
let quota = {};
Object.assign(quota, _.find(ProjectServiceMock.projectQuota, {id: id}));
return Promise.resolve(quota);
});
updateProjectSpy = jest.spyOn(ProjectService, 'updateProject');
updateProjectSpy.mockImplementation((id, project) => {
let updatedProject = {};
Object.assign(updatedProject, _.find(ProjectServiceMock.project, {name: id}));
updatedProject.name = project.name;
updatedProject.updated_at = new Date();
return Promise.resolve(updatedProject);
});
savePQSpy = jest.spyOn(ProjectService, 'saveProjectQuota');
savePQSpy.mockImplementation(() => {
return Promise.resolve(ProjectServiceMock.projectQuota[0]);
});
updatePQSpy = jest.spyOn(ProjectService, 'updateProjectQuota');
updatePQSpy.mockImplementation((quota) => {
return Promise.resolve(_.find(ProjectServiceMock.projectQuota, {id: quota.id}));
});
deletePQSpy = jest.spyOn(ProjectService, 'deleteProjectQuota');
deletePQSpy.mockImplementation(() => {
return Promise.resolve({message: 'deleted'});
});
});
const clearMockSpy = (() => {
projectCategoriesSpy.mockRestore();
periodCategoriesSpy.mockRestore();
projectDetailsSpy.mockRestore();
resourcesSpy.mockRestore();
projectQuotaSpy.mockRestore();
updateProjectSpy.mockRestore();
savePQSpy.mockRestore();
updatePQSpy.mockRestore();
deletePQSpy.mockRestore();
});
it("renders nothing if no project details found", async () => {
console.log("renders nothing if no project details found..........");
let content;
await act(async () => {
content = render(<Router><ProjectEdit match={{params:{id: "OSR-12"}}} location={{}} /></Router>);
});
expect(content.queryByText("Project - Edit")).toBe(null);
});
it("renders input fields with Project details if found", async () => {
console.log("renders input fields with Project details if found..........");
let content;
await act(async () => {
content = render(<Router><ProjectEdit match={{params:{id: "OSR-11"}}} location={{}} /></Router>);
});
// expect(content.baseElement).toBe(null);
expect(content.queryByText("Project - Edit")).not.toBe(null);
expect(content.queryByTestId("name").value).toBe('OSR-11');
const spinButtons = content.queryAllByRole("spinbutton");
const trigPrioInput = spinButtons.filter(function(element) { return element.id==="trig_prio"})[0];
expect(trigPrioInput.value).toBe("990");
const rankInput = spinButtons.filter(function(element) { return element.id==="proj-rank"})[0];
expect(rankInput.value).toBe("5");
const trigger = content.getAllByLabelText(/trigger/i).filter((element) => { return element.id==="trigger"})[0];
expect(trigger.hasAttribute("checked")).toBeTruthy();
const projCatInput = content.getAllByRole("listbox")[0].children[0] ;
expect(content.queryAllByText('Select Project Category').length).toBe(1);
expect(content.queryAllByText('Regular').length).toBe(3);
const projPeriodInput = content.getAllByRole("listbox")[1].children[0] ;
expect(content.queryAllByText('Select Period Category').length).toBe(1);
expect(content.queryAllByText('Single Cycle').length).toBe(3);
const cycleInput = content.getAllByRole("listbox")[2] ;
expect(content.queryAllByText('Cycle 0').length).toBe(2);
expect(content.queryByPlaceholderText("CEP Processing Time").value).toBe("10 Hours");
expect(content.queryByPlaceholderText("LOFAR Observing Time").value).toBe("20 Hours");
expect(content.queryByPlaceholderText("LOFAR Observing Time prio A").value).toBe("30 Hours");
expect(content.queryByPlaceholderText("LOFAR Observing Time prio B").value).toBe("40 Hours");
expect(content.queryByPlaceholderText("LOFAR Support Time").value).toBe("50 Hours");
expect(content.queryByPlaceholderText("LTA Storage").value).toBe("6 TB");
expect(content.queryByPlaceholderText("Number of triggers").value).toBe("7 Numbers");
expect(content.queryByPlaceholderText("Support hours").value).toBe("8 ");
expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy();
});
it("save Project after editing fields", async () => {
console.log("save Project after editing fields ..........");
let content;
await act(async () => {
content = render(<Router><ProjectEdit match={{params:{id: "OSR-11"}}} location={{}} /></Router>);
});
// expect(content.baseElement).toBe(null);
expect(content.queryByText("Project - Edit")).not.toBe(null);
expect(content.queryByTestId("name").value).toBe('OSR-11');
const spinButtons = content.queryAllByRole("spinbutton");
const trigPrioInput = spinButtons.filter(function(element) { return element.id==="trig_prio"})[0];
fireEvent.blur(trigPrioInput, { target: { value: 900 } });
expect(trigPrioInput.value).toBe("900");
const rankInput = spinButtons.filter(function(element) { return element.id==="proj-rank"})[0];
fireEvent.blur(rankInput, { target: { value: 2 } });
expect(rankInput.value).toBe("2");
const trigger = content.getAllByLabelText(/trigger/i).filter((element) => { return element.id==="trigger"})[0];
fireEvent.click(trigger);
expect(trigger.hasAttribute("checked")).toBeFalsy();
const projCatInput = content.getAllByRole("listbox")[0].children[1] ;
fireEvent.click(projCatInput);
// After selecting Project Category
expect(content.queryAllByText('Select Project Category').length).toBe(1);
expect(content.queryAllByText('Regular').length).toBe(1);
expect(content.queryAllByText('User Shared Support').length).toBe(3);
const projPeriodInput = content.getAllByRole("listbox")[1].children[1] ;
fireEvent.click(projPeriodInput);
expect(content.queryAllByText('Select Period Category').length).toBe(1);
expect(content.queryAllByText('Single Cycle').length).toBe(1);
expect(content.queryAllByText('Long Term').length).toBe(3);
const oldCycleInput = content.getAllByRole("listbox")[2].children[2] ;
const newCycleInput = content.getAllByRole("listbox")[2].children[0] ;
fireEvent.click(oldCycleInput);
fireEvent.click(newCycleInput);
// After selecting Cycle
expect(content.queryAllByText('Cycle-0').length).toBe(2);
expect(content.queryAllByText('Cycle 0').length).toBe(1);
const lofarObsTimeInput = content.queryByPlaceholderText('LOFAR Observing Time');
fireEvent.blur(lofarObsTimeInput, { target: { value: 10 } });
expect(lofarObsTimeInput.value).toBe('10 Hours');
const cepProcTimeInput = content.queryByPlaceholderText('CEP Processing Time');
fireEvent.blur(cepProcTimeInput, { target: { value: 5 } });
expect(cepProcTimeInput.value).toBe('5 Hours');
const ltaStorageInput = content.queryByPlaceholderText('LTA Storage');
fireEvent.blur(ltaStorageInput, { target: { value: 2 } });
expect(ltaStorageInput.value).toBe('2 TB');
const noOfTriggerInput = content.queryByPlaceholderText('Number of triggers');
fireEvent.blur(noOfTriggerInput, { target: { value: 3 } });
expect(noOfTriggerInput.value).toBe('3 Numbers');
expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy();
await act(async () => {
fireEvent.click(content.queryByTestId('save-btn'));
});
// After saving project, Success dialog should be displayed
expect(updateProjectSpy).toHaveBeenCalledTimes(1);
expect(updatePQSpy).toHaveBeenCalledTimes(4);
expect(content.queryByText("Success")).not.toBe(null);
});
it("save Project after adding, modifying and deleting resources", async () => {
console.log("save Project after adding, modifying and deleting resource..........");
let content;
await act(async () => {
content = render(<Router><ProjectEdit match={{params:{id: "OSR-11"}}} location={{}} /></Router>);
});
// expect(content.baseElement).toBe(null);
expect(content.queryByText("Project - Edit")).not.toBe(null);
expect(content.queryByTestId("name").value).toBe('OSR-11');
// Adding new resource
const addResourceInput = content.getAllByRole("listbox")[3].children[0] ;
fireEvent.click(addResourceInput);
// After selecting New Resource
expect(content.queryAllByText('Add Resources').length).toBe(1);
expect(content.queryAllByText('LOFAR Support hours').length).toBe(3);
const addResourceBtn = content.queryByTestId('add_res_btn');
fireEvent.click(addResourceBtn);
expect(content.queryAllByText('Add Resources').length).toBe(2);
expect(content.queryByPlaceholderText('LOFAR Support hours')).not.toBe(null);
const lofarSupHrsInput = content.queryByPlaceholderText('LOFAR Support hours');
fireEvent.blur(lofarSupHrsInput, { target: { value: 100 } });
expect(lofarSupHrsInput.value).toBe('100 ');
// Editing existing resource
const lofarObsTimeInput = content.queryByPlaceholderText('LOFAR Observing Time');
fireEvent.blur(lofarObsTimeInput, { target: { value: 10 } });
expect(lofarObsTimeInput.value).toBe('10 Hours');
// Deleting existing resource
const removeResourceBtn = content.queryByTestId('Support hours-btn');
fireEvent.click(removeResourceBtn);
expect(content.queryByTestId('save-btn').hasAttribute("disabled")).toBeFalsy();
await act(async () => {
fireEvent.click(content.queryByTestId('save-btn'));
});
// After saving project, Success dialog should be displayed
expect(updateProjectSpy).toHaveBeenCalledTimes(1);
expect(savePQSpy).toHaveBeenCalledTimes(1);
expect(updatePQSpy).toHaveBeenCalledTimes(1);
expect(deletePQSpy).toHaveBeenCalledTimes(1);
expect(content.queryByText("Success")).not.toBe(null);
});
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment