diff --git a/Dockerfile b/Dockerfile
index a82f3095b..d74eb171c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -16,7 +16,7 @@ ARG geppettoSimulationRelease=vfb_20200604_a
ARG geppettoDatasourceRelease=vfb_20200604_a
ARG geppettoModelSwcRelease=v1.0.1
ARG geppettoFrontendRelease=development
-ARG geppettoClientRelease=VFBv2.2.0.7
+ARG geppettoClientRelease=VFBv2.2.0.7-feature/1238
ARG ukAcVfbGeppettoRelease=download
ARG mvnOpt="-Dhttps.protocols=TLSv1.2 -DskipTests --quiet -Pmaster"
@@ -38,6 +38,7 @@ ENV VFB_OWL_SERVER=${VFB_OWL_SERVER_ARG}
ENV VFB_R_SERVER=${VFB_R_SERVER_ARG}
ENV SOLR_SERVER=${SOLR_SERVER_ARG}
ENV googleAnalyticsSiteCode=${googleAnalyticsSiteCode_ARG}
+ENV LOG4J_FORMAT_MSG_NO_LOOKUPS=true
RUN /bin/echo -e "\e[1;35mORIGIN BRANCH ------------ $originBranch\e[0m" &&\
/bin/echo -e "\e[1;35mTARGET BRANCH ------------ $targetBranch\e[0m" &&\
diff --git a/components/VFBMain.js b/components/VFBMain.js
index e16e44f06..56ebdda25 100644
--- a/components/VFBMain.js
+++ b/components/VFBMain.js
@@ -9,6 +9,7 @@ import VFBTermInfoWidget from './interface/VFBTermInfo/VFBTermInfo';
import Logo from '@geppettoengine/geppetto-client/components/interface/logo/Logo';
import Canvas from '@geppettoengine/geppetto-client/components/interface/3dCanvas/Canvas';
import QueryBuilder from '@geppettoengine/geppetto-client/components/interface/query/queryBuilder';
+import VFBDownloadContents from './interface/VFBDownloadContents/VFBDownloadContents';
import VFBUploader from './interface/VFBUploader/VFBUploader';
import HTMLViewer from '@geppettoengine/geppetto-ui/html-viewer/HTMLViewer';
import VFBListViewer from './interface/VFBListViewer/VFBListViewer';
@@ -52,6 +53,7 @@ class VFBMain extends React.Component {
quickHelpVisible: undefined,
UIUpdated: true,
wireframeVisible: false,
+ downloadContentsVisible : true,
uploaderContentsVisible : true
};
@@ -488,6 +490,12 @@ class VFBMain extends React.Component {
[buttonState]: !this.state[buttonState]
});
break;
+ case 'downloadContentsVisible':
+ this.refs.downloadContentsRef?.openDialog();
+ break;
+ case 'uploaderContentsVisible':
+ this.refs.uploaderContentsRef?.openDialog();
+ break;
case 'quickHelpVisible':
if (this.state[buttonState] === undefined) {
this.setState({
@@ -527,6 +535,9 @@ class VFBMain extends React.Component {
case 'triggerSetTermInfo':
this.handlerInstanceUpdate(click.value[0]);
break;
+ case 'downloadContentsVisible':
+ this.refs.downloadContentsRef?.openDialog();
+ break;
case 'uploaderContentsVisible':
this.refs.uploaderContentsRef?.openDialog();
break;
@@ -1752,7 +1763,10 @@ class VFBMain extends React.Component {
searchConfiguration={this.searchConfiguration}
datasourceConfiguration={this.datasourceConfiguration} />
+
+
+
{this.htmlToolbarRender}
);
diff --git a/components/configuration/VFBCircuitBrowser/circuitBrowserConfiguration.js b/components/configuration/VFBCircuitBrowser/circuitBrowserConfiguration.js
index 66339c535..75d1d2952 100644
--- a/components/configuration/VFBCircuitBrowser/circuitBrowserConfiguration.js
+++ b/components/configuration/VFBCircuitBrowser/circuitBrowserConfiguration.js
@@ -19,11 +19,20 @@ var locationCypherQuery = ( instances, paths, weight ) => ({
+ " WITH * ORDER BY index DESC"
+ " UNWIND relationships(path) as sr"
+ " OPTIONAL MATCH cp=(x:Neuron:has_neuron_connectivity)-[:synapsed_to]-(y:Neuron:has_neuron_connectivity) WHERE x=apoc.rel.startNode(sr) AND y=apoc.rel.endNode(sr) OPTIONAL MATCH fp=(x)-[r:synapsed_to]->(y) WHERE r.weight[0] >= " + weight?.toString()
- + " RETURN distinct a as root, collect(distinct fp) as pp, collect(distinct cp) as p, collect(distinct id(r)) as fr, sourceNode as source, targetNode as target, max(length(path)) as maxHops, collect(distinct toString(id(r))+':'+toString(index)) as relationshipY ",
+ + " OPTIONAL MATCH (x)-[xio:INSTANCEOF]->(xpc:Class) OPTIONAL MATCH (y)-[yio:INSTANCEOF]->(ypc:Class) WITH *,'\"'+ x.short_form+'\":{\"'+xpc.short_form+'\":\"' + xpc.label + '\"},\"'+ y.short_form+'\":{\"'+ypc.short_form+'\":\"' + ypc.label + '\"}' as Class"
+ + " RETURN distinct a as root, collect(distinct fp) as pp, collect(distinct cp) as p, collect(distinct id(r)) as fr, sourceNode as source, targetNode as target, max(length(path)) as maxHops, collect(distinct toString(id(r))+':'+toString(index)) as relationshipY, "
+ + " apoc.convert.fromJsonMap('{' + apoc.text.join(collect(Class),',') + '}') as class ",
"resultDataContents": ["row", "graph"]
}
]
});
+
+var Neo4jLabels = {
+ FAFB : "FAFB",
+ L1EM : "L1EM",
+ FlyEM_HB : "FlyEM_HB"
+}
+
// See query explanation on https://github.com/VirtualFlyBrain/graph_queries/blob/main/weighted_path.md
var configuration = {
@@ -120,5 +129,6 @@ module.exports = {
configuration,
styling,
restPostConfig,
- locationCypherQuery
+ locationCypherQuery,
+ Neo4jLabels
};
diff --git a/components/configuration/VFBDownloadContents/configuration.json b/components/configuration/VFBDownloadContents/configuration.json
new file mode 100644
index 000000000..55ad5f5ce
--- /dev/null
+++ b/components/configuration/VFBDownloadContents/configuration.json
@@ -0,0 +1,34 @@
+{
+ "postURL":"https://zip.virtualflybrain.org/download",
+ "contentType": "application/json",
+ "zipName" : "VFB Files.zip",
+ "options" :{
+ "obj": {
+ "label" : "OBJ",
+ "tooltip" : "Download OBJ"
+ },
+ "swc": {
+ "label" : "SWC",
+ "tooltip" : "Download SWC"
+ },
+ "nrrd": {
+ "label" : "NRRD",
+ "tooltip" : "Download NRRD"
+ },
+ "reference": {
+ "label" : "References",
+ "tooltip" : "Download References"
+ }
+ },
+ "text" : {
+ "title" : "Download Data",
+ "typesSubtitle" : "Please select the desired types",
+ "variablesSubtitle" : "Please select Variables:",
+ "noVariablesSubtitle" : "No loaded variables",
+ "errorMessage" : "Something went wrong... We were not able to download the data. Please try again.",
+ "noEntriesFound" : "No entries found for the types and variables selected.",
+ "cancelButton" : "Cancel",
+ "downloadButton" : "Download",
+ "tryAgainButton" : "Try Again"
+ }
+}
\ No newline at end of file
diff --git a/components/configuration/VFBDownloadContents/nrrd.png b/components/configuration/VFBDownloadContents/nrrd.png
new file mode 100644
index 000000000..beee12a5c
Binary files /dev/null and b/components/configuration/VFBDownloadContents/nrrd.png differ
diff --git a/components/configuration/VFBDownloadContents/obj.png b/components/configuration/VFBDownloadContents/obj.png
new file mode 100644
index 000000000..fbcc914c7
Binary files /dev/null and b/components/configuration/VFBDownloadContents/obj.png differ
diff --git a/components/configuration/VFBDownloadContents/reference.png b/components/configuration/VFBDownloadContents/reference.png
new file mode 100644
index 000000000..b821fe7e1
Binary files /dev/null and b/components/configuration/VFBDownloadContents/reference.png differ
diff --git a/components/configuration/VFBDownloadContents/swc.png b/components/configuration/VFBDownloadContents/swc.png
new file mode 100644
index 000000000..3eb5d02b6
Binary files /dev/null and b/components/configuration/VFBDownloadContents/swc.png differ
diff --git a/components/configuration/VFBMain/searchConfiguration.js b/components/configuration/VFBMain/searchConfiguration.js
index 859ef3327..4ce0ff102 100644
--- a/components/configuration/VFBMain/searchConfiguration.js
+++ b/components/configuration/VFBMain/searchConfiguration.js
@@ -60,7 +60,7 @@ var searchStyle = {
singleResult: {
"color": "white",
"fontSize": "18px",
-
+ "whiteSpace" : "normal",
":hover": {
"color": "#11bffe",
"background-color": "#252323",
@@ -99,7 +99,7 @@ var datasourceConfiguration = {
],
"rows": "100",
"wt": "json",
- "bq": "shortform_autosuggest:VFB*^110.0 shortform_autosuggest:FBbt*^100.0 label_s:\"\"^2 synonym_s:\"\" short_form:FBbt_00003982^2 facets_annotation:Deprecated^0.001"
+ "bq": "shortform_autosuggest:VFBexp*^10.0 shortform_autosuggest:VFB*^100.0 shortform_autosuggest:FBbt*^100.0 label_s:\"\"^2 synonym_s:\"\" short_form:FBbt_00003982^2 facets_annotation:Deprecated^0.001"
}
};
@@ -107,9 +107,13 @@ var searchConfiguration = {
"resultsMapping":
{
"name": "label",
- "id": "short_form"
+ "id": "short_form",
+ "labels" : "facets_annotation"
},
+ "label_manipulation" : label => label,
"filters_expanded": true,
+ "filter_positive" : "^100",
+ "filter_negative" : "^0.001",
"filters": [
{
"key": "facets_annotation",
@@ -305,6 +309,13 @@ var searchConfiguration = {
if (b.label.toLowerCase().indexOf(InputString.toLowerCase()) > -1 && b.label.toLowerCase().indexOf(InputString.toLowerCase()) < a.label.toLowerCase().indexOf(InputString.toLowerCase())) {
return 1;
}
+ // move up expression (VFBexp) terms
+ if (a.id.indexOf("VFBexp") > -1 && b.id.indexOf("VFBexp") < 0) {
+ return -1;
+ }
+ if (b.id.indexOf("VFBexp") > -1 && a.id.indexOf("VFBexp") < 0) {
+ return 1;
+ }
// if the match in the id is closer to start then move up
if (a.id.toLowerCase().indexOf(InputString.toLowerCase()) > -1 && a.id.toLowerCase().indexOf(InputString.toLowerCase()) < b.id.toLowerCase().indexOf(InputString.toLowerCase())) {
return -1;
@@ -323,11 +334,16 @@ var searchConfiguration = {
},
"clickHandler": function (id) {
window.addVfbId(id);
+ },
+ "Neo4jLabels" : {
+ "FAFB" : "FAFB",
+ "L1EM" : "L1EM",
+ "FlyEM_HB" : "FlyEM_HB"
}
};
module.exports = {
searchStyle,
searchConfiguration,
- datasourceConfiguration,
+ datasourceConfiguration
};
diff --git a/components/configuration/VFBToolbar/vfbtoolbarMenuConfiguration.js b/components/configuration/VFBToolbar/vfbtoolbarMenuConfiguration.js
index 68c59639d..e2206036f 100644
--- a/components/configuration/VFBToolbar/vfbtoolbarMenuConfiguration.js
+++ b/components/configuration/VFBToolbar/vfbtoolbarMenuConfiguration.js
@@ -253,6 +253,14 @@ var toolbarMenu = {
parameters: ["circuitBrowserVisible"]
}
},
+ {
+ label: "Download Contents",
+ icon: "fa fa-download",
+ action: {
+ handlerAction: "downloadContentsVisible",
+ parameters: []
+ }
+ },
{
label: "NBLAST Uploader",
icon: "fa fa-upload",
diff --git a/components/configuration/VFBTree/VFBTreeConfiguration.js b/components/configuration/VFBTree/VFBTreeConfiguration.js
index c30f27754..abd9f9a69 100644
--- a/components/configuration/VFBTree/VFBTreeConfiguration.js
+++ b/components/configuration/VFBTree/VFBTreeConfiguration.js
@@ -8,7 +8,7 @@ var treeCypherQuery = instance => ({
{
"statement": "MATCH (root:Class)<-[:INSTANCEOF]-(t:Template {short_form:'" + instance + "'})"
+ "<-[:depicts]-(tc:Template)<-[ie:in_register_with]-(c:Individual)-[:depicts]->(image:"
- + "Individual)-[r:INSTANCEOF]->(anat:Class:Nervous_system) WHERE exists(ie.index) WITH root, anat,r,image"
+ + "Individual)-[r:INSTANCEOF]->(anat:Class:Anatomy) WHERE exists(ie.index) WITH root, anat,r,image"
+ " MATCH p=allshortestpaths((root)<-[:SUBCLASSOF|part_of*..]-(anat)) "
+ "UNWIND nodes(p) as n UNWIND nodes(p) as m WITH * WHERE id(n) < id(m) "
+ "MATCH path = allShortestPaths( (n)-[:SUBCLASSOF|part_of*..1]-(m) ) "
diff --git a/components/configuration/VFBUploader/configuration.json b/components/configuration/VFBUploader/configuration.json
index a6d1a1091..45126dc53 100644
--- a/components/configuration/VFBUploader/configuration.json
+++ b/components/configuration/VFBUploader/configuration.json
@@ -1,12 +1,27 @@
{
- "nblastURL" : "https://zip.virtualflybrain.org/download",
- "contentType" : "",
- "templates" : [
- { "VFB_00101567" : "VFB_00101567 Template" },
- { "VFB_00000001" : "VFB_00000001 Template" }
- ],
- "acceptedFiles" : [".swc"],
- "filesLimit" : 10,
+ "nblastURL" : "http://upload.virtualflybrain.org/files/UNIQUE_ID?token=bec3a40f0ab377c39103",
+ "contentType" : "multipart/form-data",
+ "templates" : [{"short_form":"VFB_00101567","label":"JRC2018Unisex"},{"short_form":"VFB_00200000","label":"JRC2018UnisexVNC"}] ,
+ "acceptedFiles" : [".swc", ".nrrd"],
+ "filesLimit" : 1,
"maxFileSize" : 3000000,
- "dropZoneMessage" : "Drag and drop a SWC file here or click"
+ "queryType": "uploaderQuery",
+ "cookieStorageDays" : 100,
+ "cookiesLearnLink" : "https://en.wikipedia.org/wiki/HTTP_cookie",
+ "text" : {
+ "dialogTitle" : "Upload Data File",
+ "dialogSubtitle" : "Generate a nblast query from your own data.",
+ "selectTemplate" : "1. Select a Template",
+ "select" : "Select",
+ "addYourFile" : "2. Add your file (Please use a .swc)",
+ "dropZoneMessage" : "Click or Drag & Drop your file here",
+ "agreeTerms" : "I agree to the use of cookies to store the NBLAST QUERY URL. ",
+ "learnMore" : "Learn More",
+ "blastButtonText" : "Generate a nblast query link",
+ "restartButtonText" : "Restart",
+ "copyButtonText" : "Copy",
+ "infoMessage" : "Generating a nblast query can take up to 30 min. Save this link to find the result of your query.",
+ "errorDialog" : "Something went wrong... We were not able to generate a nblast query link from your data. Please try again.",
+ "errorButtonText" : "Try Again"
+ }
}
\ No newline at end of file
diff --git a/components/configuration/VFBUploader/file-icon.png b/components/configuration/VFBUploader/file-icon.png
new file mode 100644
index 000000000..7ac1b6ff8
Binary files /dev/null and b/components/configuration/VFBUploader/file-icon.png differ
diff --git a/components/configuration/VFBUploader/upload-icon.png b/components/configuration/VFBUploader/upload-icon.png
new file mode 100644
index 000000000..d10bcbbb2
Binary files /dev/null and b/components/configuration/VFBUploader/upload-icon.png differ
diff --git a/components/interface/ErrorCatcher.js b/components/interface/ErrorCatcher.js
index 63f6fcf64..181793148 100644
--- a/components/interface/ErrorCatcher.js
+++ b/components/interface/ErrorCatcher.js
@@ -38,22 +38,44 @@ const styles = {
class ErrorCatcher extends React.Component {
constructor (props) {
super(props);
- this.state = {
- hasError: false,
+ this.state = {
+ hasError: false,
open: true,
error: undefined
};
}
-
handleClose = () => {
var url = "https://github.com/VirtualFlyBrain/VFB2/issues/new";
var customMessage = "Steps to reproduce the problem: \n\nPlease fill the below with the necessary steps to reproduce the problem\n\n\n\nError Information:\n\n"
- var body = customMessage + this.state.error.message + "\n\n" + this.state.error.stack.replace("#",escape("#")) + "\n\n```diff\n" + window.console.logs.slice(-50).join('\n').replace("#",escape("#")) + "\n```\n";
+ // return as much of the log up to the last 10 events < 1000 characters:
+ var logLength = -1;
+ var limitedLog = window.console.logs.slice(logLength).join('%0A').replace(
+ /\&/g,escape('&')
+ ).replace(
+ /\#/g,escape('#')
+ ).replace(
+ /\-/g,'%2D'
+ ).replace(
+ /\+/g,'%2B'
+ );
+ while (limitedLog.length < 1000 && logLength > -50) {
+ logLength -= 1;
+ limitedLog = window.console.logs.slice(logLength).join('%0A').replace(
+ /\&/g,escape('&')
+ ).replace(
+ /\#/g,escape('#')
+ ).replace(
+ /\-/g,'%2D'
+ ).replace(
+ /\+/g,'%2B'
+ );
+ }
+ var body = customMessage + this.state.error.message + "\n\n" + this.state.error.stack.replace("#",escape("#")) + "\n\n```diff\n" + limitedLog + "\n```\n";
var form = document.createElement("form");
form.setAttribute("method", "get");
form.setAttribute("action", url);
form.setAttribute("target", "view");
- var hiddenField = document.createElement("input");
+ var hiddenField = document.createElement("input");
hiddenField.setAttribute("type", "hidden");
hiddenField.setAttribute("name", "body");
hiddenField.setAttribute("value", body);
@@ -62,7 +84,7 @@ class ErrorCatcher extends React.Component {
window.open('', 'view');
form.submit();
};
-
+
componentDidCatch (error, info) {
// Report error to GA
window.ga('vfb.send', 'event', 'error', 'react', error.message + " - " + error.stack.replace("#",escape("#")));
diff --git a/components/interface/VFBCircuitBrowser/Controls.js b/components/interface/VFBCircuitBrowser/Controls.js
index a6c6e1838..20d778f5a 100644
--- a/components/interface/VFBCircuitBrowser/Controls.js
+++ b/components/interface/VFBCircuitBrowser/Controls.js
@@ -120,9 +120,11 @@ const configuration = require('../../configuration/VFBCircuitBrowser/circuitBrow
const restPostConfig = require('../../configuration/VFBCircuitBrowser/circuitBrowserConfiguration').restPostConfig;
const cypherQuery = require('../../configuration/VFBCircuitBrowser/circuitBrowserConfiguration').locationCypherQuery;
const stylingConfiguration = require('../../configuration/VFBCircuitBrowser/circuitBrowserConfiguration').styling;
+const Neo4jLabels = require('../../configuration/VFBCircuitBrowser/circuitBrowserConfiguration').Neo4jLabels;
const searchConfiguration = require('./../../configuration/VFBCircuitBrowser/datasources/SOLRclient').searchConfiguration;
-const datasourceConfiguration = require('./../../configuration/VFBCircuitBrowser/datasources/SOLRclient').datasourceConfiguration;
+const defaultDatasourceConfiguration = require('./../../configuration/VFBCircuitBrowser/datasources/SOLRclient').datasourceConfiguration;
+const datasourceConfiguration = JSON.parse(JSON.stringify(defaultDatasourceConfiguration));
/**
* Create custom marks for Paths slider.
@@ -161,11 +163,15 @@ class AutocompleteResults extends Component {
this.setState({ filteredResults : results });
}
+ clearResults () {
+ this.setState({ filteredResults : {} });
+ }
+
getFilteredResults (){
return this.state.filteredResults;
}
- shouldComponentUpdate(nextProps, nextState) {
+ shouldComponentUpdate (nextProps, nextState) {
this.fieldLabel = nextProps.getLatestNeuronFields()[this.props.index].label;
return true;
}
@@ -179,8 +185,10 @@ class AutocompleteResults extends Component {
fullWidth
freeSolo
disableClearable
+ clearOnEscape
disablePortal
autoHighlight
+ clearOnBlur
value={this.fieldLabel}
id={this.props.index.toString()}
ListboxProps={{ style: { maxHeight: "10rem" } }}
@@ -267,6 +275,12 @@ class Controls extends Component {
this.props.vfbCircuitBrowser(UPDATE_CIRCUIT_QUERY, neurons);
delete this.autocompleteRef[id.toString()];
this.neuronFields = neurons;
+
+ if ( !this.state.neurons.find( neuron => neuron.id != "") ) {
+ // reset configuration of fq to default
+ datasourceConfiguration.query_settings.fq = defaultDatasourceConfiguration.query_settings.fq;
+ }
+
this.forceUpdate();
}
@@ -360,6 +374,12 @@ class Controls extends Component {
getResultsSOLR( event.target.value, this.autocompleteRef[this.setInputValue].current.handleResults,searchConfiguration.sorter,datasourceConfiguration );
}
this.neuronFields = neurons;
+
+ if ( !this.neuronFields.find( neuron => neuron.id != "") ) {
+ // reset configuration of fq to default
+ this.autocompleteRef[this.setInputValue].current.clearResults();
+ datasourceConfiguration.query_settings.fq = defaultDatasourceConfiguration.query_settings.fq;
+ }
}
/**
@@ -369,9 +389,17 @@ class Controls extends Component {
// Copy neurons and add selection to correct array index
let neurons = this.neuronFields;
let textFieldId = event.target.id.toString().split("-")[0];
- let shortForm = this.autocompleteRef[textFieldId].current.getFilteredResults()[value] && this.autocompleteRef[textFieldId].current.getFilteredResults()[value].short_form;
+ let result = this.autocompleteRef[textFieldId].current.getFilteredResults()[value];
+ let shortForm = result && result.short_form;
neurons[index] = { id : shortForm, label : value };
+ result.facets_annotation.forEach( annotation => {
+ let facet = "facets_annotation:" + annotation;
+ if ( Object.values(Neo4jLabels).includes(annotation) && !datasourceConfiguration.query_settings.fq.includes(facet) ) {
+ datasourceConfiguration.query_settings.fq.push(facet);
+ }
+ });
+
// Keep track of query selected, and send an event to redux store that circuit has been updated
this.circuitQuerySelected = neurons;
this.props.vfbCircuitBrowser(UPDATE_CIRCUIT_QUERY, neurons);
diff --git a/components/interface/VFBDownloadContents/VFBDownloadContents.js b/components/interface/VFBDownloadContents/VFBDownloadContents.js
new file mode 100644
index 000000000..6b86cf888
--- /dev/null
+++ b/components/interface/VFBDownloadContents/VFBDownloadContents.js
@@ -0,0 +1,571 @@
+import React from "react";
+import Button from "@material-ui/core/Button";
+import Grid from "@material-ui/core/Grid";
+import Dialog from "@material-ui/core/Dialog";
+import DialogActions from "@material-ui/core/DialogActions";
+import DialogContent from "@material-ui/core/DialogContent";
+import DialogTitle from "@material-ui/core/DialogTitle";
+import Tooltip from "@material-ui/core/Tooltip";
+import Typography from "@material-ui/core/Typography";
+import FormControlLabel from "@material-ui/core/FormControlLabel";
+import CircularProgress from '@material-ui/core/CircularProgress';
+import ChevronRightIcon from "@material-ui/icons/ChevronRight";
+import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
+import { Checkbox, Divider, IconButton } from "@material-ui/core";
+import Box from "@material-ui/core/Box";
+import { createMuiTheme, ThemeProvider } from "@material-ui/core/styles";
+import { withStyles } from "@material-ui/styles";
+import axios from "axios";
+import TreeView from "@material-ui/lab/TreeView";
+import TreeItem from "@material-ui/lab/TreeItem";
+import NRRDIcon from "../../configuration/VFBDownloadContents/nrrd.png";
+import OBJIcon from "../../configuration/VFBDownloadContents/obj.png";
+import SWCIcon from "../../configuration/VFBDownloadContents/swc.png";
+import ReferenceIcon from "../../configuration/VFBDownloadContents/reference.png";
+import CloseIcon from "@material-ui/icons/Close";
+import { connect } from "react-redux";
+
+const iconsMap = {
+ obj: OBJIcon,
+ swc: SWCIcon,
+ reference: ReferenceIcon,
+ nrrd: NRRDIcon,
+};
+
+const ALL_INSTANCES = { id: "ALL_INSTANCES", name: "All Instances" };
+
+const styles = theme => ({
+ downloadButton: { backgroundColor: "#0AB7FE", color: "white !important" },
+ downloadErrorButton: { backgroundColor: "#FCE7E7", color: "#E53935", border : "1px solid #E53935" },
+ error: { color: "#E53935" },
+ errorMessage: { wordWrap: "break-word" },
+ downloadButtonText: { color: "white !important" },
+ checkedBox: { borderColor: "#0AB7FE" },
+ footer: { backgroundColor: "#EEF9FF" },
+ errorFooter: { backgroundColor: "#FCE7E7" },
+ listItemText: { fontSize: "1em" },
+ customizedButton: {
+ position: "absolute",
+ left: "95%",
+ top: "2%",
+ backgroundColor: "#F5F5F5",
+ color: "gray",
+ },
+ dialog: {
+ overflow: "unset",
+ margin: "0 auto",
+ },
+ dialogContent: { overflow: "hidden" },
+ checked: { "&$checked": { color: "#0AB7FE" } },
+ "@global": {
+ ".MuiTreeItem-root.Mui-selected > .MuiTreeItem-content .MuiTreeItem-label": { backgroundColor: "white" },
+ ".MuiTreeItem-root.Mui-selected > .MuiTreeItem-content .MuiTreeItem-label:hover, .MuiTreeItem-root.Mui-selected:focus > .MuiTreeItem-content .MuiTreeItem-label": { backgroundColor: "white" }
+ },
+});
+
+const theme = createMuiTheme({
+ typography: {
+ h2: {
+ fontSize: 22,
+ fontWeight: 400,
+ fontStyle: "normal",
+ lineHeight: "26.4px",
+ color: "#181818",
+ fontFamily: "Barlow",
+ },
+ h5: {
+ fontSize: 11,
+ fontWeight: 500,
+ fontStyle: "normal",
+ lineHeight: "13.2px",
+ fontFamily: "Barlow",
+ color: "rgba(0, 0, 0, 0.54)",
+ },
+ subtitle2: {
+ fontSize: 11,
+ fontWeight: 500,
+ fontStyle: "normal",
+ lineHeight: "13.2px",
+ fontFamily: "Barlow",
+ color: "rgba(0, 0, 0, 0.24)",
+ },
+ error: {
+ fontSize: 11,
+ fontWeight: 500,
+ fontStyle: "normal",
+ lineHeight: "13.2px",
+ fontFamily: "Barlow",
+ color: "#E53935",
+ },
+ button: {
+ fontSize: 11,
+ fontWeight: 600,
+ fontStyle: "normal",
+ lineHeight: "13.2px",
+ fontFamily: "Barlow",
+ color: "#0AB7FE",
+ },
+ },
+ Button: {
+ "&:hover": {
+ backgroundColor: "#0AB7FE",
+ boxShadow: "none",
+ },
+ "&:active": {
+ boxShadow: "none",
+ backgroundColor: "#0AB7FE",
+ },
+ },
+});
+
+/**
+ * Component to download files contents
+ */
+class VFBDownloadContents extends React.Component {
+ constructor (props) {
+ super(props);
+
+ this.state = {
+ open: false,
+ typesChecked: [],
+ downloadError: false,
+ downloading: false,
+ selectedVariables: [],
+ allVariablesSelectedFlag: false,
+ errorMessage : ""
+ };
+
+ this.configuration = require("../../configuration/VFBDownloadContents/configuration");
+ this.configurationOptions = this.configuration.options;
+ this.handleCloseDialog = this.handleCloseDialog.bind(this);
+ this.openDialog = this.openDialog.bind(this);
+ this.handleTypeSelection = this.handleTypeSelection.bind(this);
+ this.handleDownload = this.handleDownload.bind(this);
+ this.extractVariableFileMeta = this.extractVariableFileMeta.bind(this);
+ this.getAllLoadedVariables = this.getAllLoadedVariables.bind(this);
+ this.requestZipDownload = this.requestZipDownload.bind(this);
+ this.getVariableById = this.getVariableById.bind(this);
+ this.toggleVariable = this.toggleVariable.bind(this);
+ this.variables = [ALL_INSTANCES];
+ }
+
+ handleCloseDialog () {
+ this.setState({ open: false });
+ }
+
+ openDialog () {
+ this.variables = this.getAllLoadedVariables();
+ this.setState({
+ open: true,
+ downloadError : false,
+ downloading : false,
+ downloadEnabled : this.state.typesChecked.length > 0 && this.state.selectedVariables.length > 0
+ });
+ }
+
+ handleDownload () {
+ if ( this.state.downloading ) {
+ return;
+ }
+
+ let json = { entries: [] };
+
+ this.state.selectedVariables.map( variable => {
+ const filemeta = this.extractVariableFileMeta(variable);
+ json.entries = json.entries.concat(filemeta);
+ });
+
+ json.entries.length > 0 ? this.requestZipDownload(json) : this.setState({ downloadError : true, errorMessage : this.configuration.text.noEntriesFound });
+ }
+
+ /**
+ * Extract filemeta from geppetto model, using variable id to find it
+ */
+ extractVariableFileMeta (variable) {
+ let filemetaText = variable.filemeta?.values[0]?.value?.text;
+ filemetaText = filemetaText?.replace(/'/g, '"');
+
+ const filemetaObject = JSON.parse(filemetaText);
+ let filesArray = [];
+
+ this.state.typesChecked.map( check => {
+ filemetaObject[check]
+ && filesArray.push({
+ Url: filemetaObject[check]?.url,
+ ZipPath: filemetaObject[check]?.local,
+ });
+ });
+
+ return filesArray;
+ }
+
+ /**
+ * Get array of all loaded variables in application
+ */
+ getAllLoadedVariables () {
+ let entities = GEPPETTO.ModelFactory.allPaths;
+ var visuals = [];
+
+ for (var i = 0; i < entities.length; i++) {
+ if ( entities[i].metaType === "VisualType" || entities[i].metaType === "CompositeVisualType" ) {
+ const variable = entities[i]?.path?.split(".")[0];
+ const instance = window.Instances[variable];
+ const filemeta = instance[variable + "_meta"]?.variable?.types[0]?.filemeta;
+ visuals.push({ id: variable, name: instance?.name, filemeta: filemeta });
+ }
+ }
+
+ return visuals;
+ }
+
+ /**
+ * Make axios call to download the zip
+ */
+ requestZipDownload (jsonRequest) {
+ let self = this;
+
+ this.setState({ downloading: true, downloadEnabled : false });
+ // Axios HTTP Post request with post query
+ axios({
+ method: "post",
+ url: this.configuration.postURL,
+ headers: { "content-type": this.configuration.contentType },
+ data: jsonRequest,
+ responseType: "arraybuffer",
+ })
+ .then(function (response) {
+ const url = window.URL.createObjectURL(new Blob([response.data]));
+ const link = document.createElement("a");
+ link.href = url;
+ link.setAttribute("download", self.configuration.zipName);
+ document.body.appendChild(link);
+ link.click();
+ setTimeout(
+ () =>
+ self.setState({
+ downloading: false,
+ open: false,
+ downloadEnabled : true
+ }),
+ 500
+ );
+ })
+ .catch(function (error) {
+ self.setState({
+ downloadError: true,
+ downloading: false,
+ errorMessage : this.props.classes.errorMessage
+ });
+ });
+ }
+
+ /**
+ * Handle checkbox selection of different types to download
+ */
+ handleTypeSelection (value) {
+ const currentIndex = this.state.typesChecked.indexOf(value);
+ const newTypesChecked = [...this.state.typesChecked];
+
+ if (currentIndex === -1) {
+ newTypesChecked.push(value);
+ } else {
+ newTypesChecked.splice(currentIndex, 1);
+ }
+
+ this.setState({ typesChecked: newTypesChecked, downloadEnabled : newTypesChecked.length > 0 && this.state.selectedVariables.length > 0 });
+ }
+
+ /**
+ * Get variable by id, trigger by checkbox selection of variables
+ */
+ getVariableById (nodes, id) {
+ let variablesMatched = [];
+
+ if (id === ALL_INSTANCES.id) {
+ variablesMatched = nodes;
+ } else {
+ nodes.forEach(node => {
+ if (node.id === id) {
+ variablesMatched.push(node);
+ }
+ });
+ }
+
+ return variablesMatched;
+ }
+
+ /**
+ * Toggle variable selection from checklist
+ */
+ toggleVariable (checked, node) {
+ const allNode = this.getVariableById(this.variables, node.id);
+ let updatedVariables = checked
+ ? [...this.state.selectedVariables, ...allNode]
+ : this.state.selectedVariables.filter(
+ value => !allNode.find( node => node.id === value.id )
+ );
+
+ updatedVariables = updatedVariables.filter((v, i) => updatedVariables.indexOf(v) === i);
+
+ this.setState({
+ selectedVariables: updatedVariables,
+ allVariablesSelectedFlag: updatedVariables.length > 0,
+ downloadEnabled : this.state.typesChecked.length > 0 && updatedVariables.length > 0
+ });
+ }
+
+ render () {
+ let self = this;
+ const { idsMap } = this.props;
+ this.variables = this.getAllLoadedVariables();
+
+ return (
+
+
+
+ );
+ }
+}
+
+function mapStateToProps (state) {
+ return {
+ instanceDeleted : state.generals.ui.canvas.instanceDeleted,
+ instanceOnFocus : state.generals.instanceOnFocus,
+ idsMap : state.generals.idsMap,
+ idsList : state.generals.idsList
+ }
+}
+
+export default connect(mapStateToProps, null, null, { forwardRef : true } )(withStyles(styles)(VFBDownloadContents));
\ No newline at end of file
diff --git a/components/interface/VFBFocusTerm/VFBFocusTerm.js b/components/interface/VFBFocusTerm/VFBFocusTerm.js
index 482347f9b..28d41946b 100644
--- a/components/interface/VFBFocusTerm/VFBFocusTerm.js
+++ b/components/interface/VFBFocusTerm/VFBFocusTerm.js
@@ -568,6 +568,20 @@ class VFBFocusTerm extends React.Component {
:
}
+
+ {
+ this.props.UIUpdateManager("uploaderContentsVisible");
+ }} />
+
+
+ {
+ this.props.UIUpdateManager("downloadContentsVisible");
+ }} />
+
({
- listItemText: { fontSize:'1em' },
- templateSelection: {
- width : "30% !important",
- height : "5rem"
- },
- templateContent : { fontSize : "14px" },
- dialogActions : { justifyContent : "space-evenly" }
-});
+import React from "react";
+import {
+ Dialog, DialogActions, DialogContent, DialogTitle, FormControl, FormControlLabel,
+ FormGroup, InputLabel, Select, Typography, IconButton, Divider, Box, TextField,
+ ListItemIcon, ListItemText, Checkbox, MenuItem, Button, LinearProgress, CircularProgress, Grid
+} from "@material-ui/core";
+import FileCopyIcon from "@material-ui/icons/FileCopy";
+import CloseIcon from "@material-ui/icons/Close";
+import DeleteIcon from "@material-ui/icons/Delete";
+import CheckIcon from '@material-ui/icons/Check';
+import InfoIcon from '@material-ui/icons/Info';
+import ReplayIcon from '@material-ui/icons/Replay';
+import { createMuiTheme, ThemeProvider } from "@material-ui/core/styles";
+import { withStyles } from "@material-ui/styles";
+import axios from "axios";
+import { DropzoneArea } from "material-ui-dropzone";
+import UploadIcon from "../../configuration/VFBUploader/upload-icon.png";
+import { nanoid } from 'nanoid';
+import FileIcon from "../../configuration/VFBUploader/file-icon.png";
+import { CustomStyle, CustomTheme } from "./styles";
+const UNIQUE_ID = "UNIQUE_ID";
class VFBUploader extends React.Component {
constructor (props) {
super(props);
this.state = {
open: false,
- uploading : false,
- nblastEnabled : false,
- files : [],
- permissionsChecked : false,
- templateSelected : ""
- }
-
- this.configuration = require('../../configuration/VFBUploader/configuration');
+ fileNBLASTURL: "",
+ nblastEnabled: false,
+ files: [],
+ templateSelected: "",
+ progress: 100,
+ cookies : false,
+ error : false,
+ uploading : false
+ };
+
+ this.configuration = require("../../configuration/VFBUploader/configuration");
this.handleCloseDialog = this.handleCloseDialog.bind(this);
this.openDialog = this.openDialog.bind(this);
- this.handlePermissionsCheck = this.handlePermissionsCheck.bind(this);
this.handleNBLASTAction = this.handleNBLASTAction.bind(this);
+ this.handleFileDelete = this.handleFileDelete.bind(this);
this.requestUpload = this.requestUpload.bind(this);
+ this.getTitleHead = this.getTitleHead.bind(this);
+ this.getUploaderComponents = this.getUploaderComponents.bind(this);
+ this.getErrorDialog = this.getErrorDialog.bind(this);
+ this.getUploadActions = this.getUploadActions.bind(this);
+ this.handleCookieEvent = this.handleCookieEvent.bind(this);
+ }
+
+ handleCookieEvent (event) {
+ this.setState({ cookies : event.target.checked })
}
handleCloseDialog () {
- this.setState({ open : false });
+ this.setState({ open: false });
}
-
- handleDropZoneChange (files){
+
+ handleDropZoneChange (files) {
this.setState({ files: files });
}
-
+
+ handleFileDelete () {
+ this.setState({ files: [], nblastEnabled: false });
+ }
+
handleTemplateChange (event) {
- this.setState({ templateSelected : event.target.value })
+ this.setState({ templateSelected: event.target.value });
}
-
+
openDialog () {
- this.setState({ open : true });
+ this.setState({ open: true });
}
-
+
handleNBLASTAction () {
- this.requestUpload({});
+ let newId = "VFBu_" + nanoid(8);
+ let url = this.configuration.nblastURL.replace(UNIQUE_ID, this.state.templateSelected + "&" + newId);
+ var formData = new FormData();
+ formData.append("file", this.state.files[0]);
+ formData.append("vfbID", newId);
+ formData.append("templateID", this.state.templateSelected);
+ this.requestUpload(formData, url);
}
-
+
/**
- * Make axios call to download the zip
+ * Make axios call to upload to server
*/
- requestUpload (jsonRequest) {
+ requestUpload (formData, url) {
let self = this;
+ let _id = formData.get("vfbID");
+ let newURL = window.location.origin + window.location.pathname + "&q=" + _id + "," + this.configuration.queryType;
- this.setState( { uploading : true } );
- // Axios HTTP Post request with post query
- axios({
- method: 'post',
- url: this.configuration.nblastURL,
- headers: { 'content-type': this.configuration.contentType },
- data: jsonRequest,
- responseType: "arraybuffer"
- }).then( function (response) {
- const url = window.URL.createObjectURL(new Blob([response.data]));
- setTimeout( () => self.setState( { nblastEnabled : true, uploading : false } ), 500);
- }).catch( function (error) {
- self.downloadErrorMessage = error?.message;
- self.setState( { nblastEnabled : true, uploading : false } );
+ this.setState({ fileNBLASTURL: newURL, uploading : true });
+ window.setCookie(_id, newURL, this.configuration.cookieStorageDays);
+
+ axios.put(url,
+ formData, { headers: { 'Content-Type': this.configuration.contentType } }
+ ).then(function (response) {
+ console.log('SUCCESS!!', response);
+ self.setState({ uploading : false, nblastEnabled: true });
})
+ .catch(function (error) {
+ console.log('FAILURE!!', error);
+ self.setState({ error : true, uploading : false });
+ });
}
- /**
- * Handle checkbox selection of different types to download
- */
- handlePermissionsCheck (event) {
- this.setState({ permissionsChecked : event.target.checked });
+ getTitleHead () {
+ return (
+
+ {this.configuration.text.dialogTitle}
+
+
+ {this.configuration.text.dialogSubtitle}
+
+ );
}
- render () {
- let self = this;
+ getUploaderComponents () {
const { classes } = this.props;
-
+ let self = this;
return (
-
- )
+
+
+
+
+
+
+
+ {this.configuration.text.infoMessage}
+
+
+
+
+ );
+ }
+
+ getErrorDialog () {
+ const { classes } = this.props;
+
+ return (
+
+
+
+
+
+
+
+ {this.configuration.text.errorDialog}
+
+
+
+
+ );
+ }
+
+ getUploadActions () {
+ const { classes } = this.props;
+ let self = this;
+
+ return (
+ <>
+
+
+
+
+ { !this.state.error
+ ? !this.state.nblastEnabled ? (
+
+
+ {this.state.uploading ? : this.configuration.text.blastButtonText}
+
+
+ ) : (
+
+ }
+ onClick={() => self.setState({ fileNBLASTURL : "", nblastEnabled : false, files : [], templateSelected: "" }) }
+ variant="outlined"
+ >
+ {this.configuration.text.restartButtonText}
+
+
+ )
+ :
+ }
+ onClick={() => self.setState({ fileNBLASTURL : "", error : false, nblastEnabled : false, files : [], templateSelected: "" }) }
+ >
+ {this.configuration.text.errorButtonText}
+
+
+ }
+
+ >
+ );
+ }
+
+ render () {
+ let self = this;
+ const { classes } = this.props;
+
+ return (
+
+
+
+ );
}
}
-export default (withStyles(styles)(VFBUploader));
\ No newline at end of file
+export default withStyles(CustomStyle)(VFBUploader);
\ No newline at end of file
diff --git a/components/interface/VFBUploader/styles.js b/components/interface/VFBUploader/styles.js
new file mode 100644
index 000000000..a4a796f76
--- /dev/null
+++ b/components/interface/VFBUploader/styles.js
@@ -0,0 +1,107 @@
+import { createMuiTheme } from "@material-ui/core/styles";
+
+export const CustomStyle = theme => ({
+ dropzoneArea: { minHeight: "20vh !important" },
+ marginTop: { marginTop: "2vh !important" },
+ checked: { "&$checked": { color: "#0AB7FE" } },
+ dialog: {
+ overflowY: 'unset',
+ maxWidth: "60vh",
+ width: "60vh",
+ margin: "0 auto"
+ },
+ customizedButton: {
+ position: 'absolute',
+ left: '90%',
+ top: '2%',
+ backgroundColor: '#F5F5F5',
+ color: 'gray',
+ },
+ errorButton : {
+ backgroundColor : "rgba(252, 231, 231, 1)",
+ color : "red",
+ borderColor : "red",
+ "&:hover": {
+ backgroundColor: "rgba(252, 231, 231, 1)",
+ color: "red"
+ }
+ },
+ vfbColor : { backgroundColor : "#EEF9FF" },
+ cookiesBox : {
+ width : "100%",
+ display : "contents"
+ }
+})
+
+export const CustomTheme = createMuiTheme({
+ typography: {
+ h2: {
+ fontSize: 22,
+ fontWeight: 400,
+ fontStyle: "normal",
+ color : "#181818",
+ lineHeight: "26.4px",
+ fontFamily: "Barlow Condensed",
+ },
+ caption: {
+ fontSize: 11,
+ fontWeight: 500,
+ fontStyle: "normal",
+ color : "#181818",
+ lineHeight: "13.2px",
+ fontFamily: "Barlow Condensed",
+ },
+ h5: {
+ fontSize: 11,
+ fontWeight: 500,
+ fontStyle: "normal",
+ color : "rgba(0, 0, 0, 0.4)",
+ lineHeight: "13.2px",
+ fontFamily: "Barlow Condensed",
+ }
+ },
+ palette: { primary: { main: '#0AB7FE' }, secondary : { main : "#fff" }, error : { main : "#ff0000" } },
+ overrides: {
+ MuiButton: {
+ contained: {
+ color: "#f1f1f1",
+ backgroundColor : "#0AB7FE",
+ "&:hover": {
+ backgroundColor: "#0AB7FE",
+ color: "#f1f1f1"
+ },
+ "&:disabled": {
+ backgroundColor: "rgba(10, 183, 254, .4)",
+ color: "#f1f1f1"
+ }
+ },
+ outlined: {
+ color: "#0AB7FE",
+ borderColor : "#0AB7FE",
+ "&:hover": {
+ backgroundColor: "#0AB7FE",
+ color: "#f1f1f1"
+ }
+ },
+ },
+ MuiFilledInput : {
+ root : { backgroundColor : "#EEF9FF" },
+ input : {
+ color : "#0AB7FE !important",
+ borderColor : "#0AB7FE !important"
+ }
+ },
+ MuiSelect : {
+ root : {
+ textAlign : "start",
+ display : "flex"
+ }
+ },
+ MuiListItemIcon : {
+ root : {
+ color : "#0AB7FE",
+ padding : "4px"
+ }
+ }
+ }
+});
diff --git a/package.json b/package.json
index eb74472e6..3d7a8389a 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
"license": "MIT",
"scripts": {
"test": "jest --verbose",
+ "update-tests-snapshots": "jest --updateSnapshot",
"prebuild": "eslint . --color",
"build": "webpack -p --progress",
"prebuild-dev": "eslint . --color",
@@ -25,7 +26,7 @@
"@babel/runtime": "^7.4.5",
"@babel/plugin-transform-runtime": "^7.4.5",
"@geppettoengine/geppetto-client": "file:./geppetto-client",
- "@material-ui/icons": "3.0.1",
+ "@material-ui/icons": "^4.0.0",
"@material-ui/lab": "^4.0.0-alpha.57",
"@types/react-rnd": "^8.0.0",
"axios": "^0.19.2",
@@ -45,7 +46,7 @@
"puppeteer": "^1.17.0",
"react-collapsible": "^2.3.1",
"react-color": "^2.17.3",
- "react-tabs": "3.0.0",
+ "react-tabs": "^3.2.3",
"react-redux": "^7.0.3",
"redux": "^4.0.1",
"style-loader": "^0.13.2",
diff --git a/tests/jest/resources/volume.nrrd b/tests/jest/resources/volume.nrrd
new file mode 100644
index 000000000..2191e6b26
Binary files /dev/null and b/tests/jest/resources/volume.nrrd differ
diff --git a/tests/jest/vfb/batch1/focus-term-tests.js b/tests/jest/vfb/batch1/focus-term-tests.js
index 3c103529e..04e8a8ff8 100644
--- a/tests/jest/vfb/batch1/focus-term-tests.js
+++ b/tests/jest/vfb/batch1/focus-term-tests.js
@@ -50,7 +50,7 @@ describe('VFB Focus Term Tests', () => {
await page.evaluate(async () => {
let tabs = document.getElementsByClassName('MuiListItem-root ');
for ( var i = 0; i < tabs.length ; i ++ ) {
- if ( tabs[i].innerText === "medulla (FBbt_00003748)" ) {
+ if ( tabs[i].innerText.split('\n')[0] === "medulla (FBbt_00003748)" ) {
tabs[i].click();
}
}
diff --git a/tests/jest/vfb/batch1/spotlight-tests.js b/tests/jest/vfb/batch1/spotlight-tests.js
index dd13c25a6..a4e81778a 100644
--- a/tests/jest/vfb/batch1/spotlight-tests.js
+++ b/tests/jest/vfb/batch1/spotlight-tests.js
@@ -81,10 +81,10 @@ describe('VFB Spotlight Tests', () => {
await page.evaluate(async () => {
let tabs = document.getElementsByClassName('MuiListItem-root ');
for ( var i = 0; i < tabs.length ; i ++ ) {
- if ( tabs[i].innerText === "fru-M-200266 (VFB_00000001)" ) {
+ if ( tabs[i].innerText.split('\n')[0] === "fru-M-200266 (VFB_00000001)" ) {
tabs[i].click();
}
- }
+ }
});
await wait4selector(page, ST.SPOT_LIGHT_SELECTOR, { hidden: true, timeout : 50000 });
})
diff --git a/tests/jest/vfb/batch2/tree-browser-tests.js b/tests/jest/vfb/batch2/tree-browser-tests.js
index 51fe67ea5..6754d8d7b 100644
--- a/tests/jest/vfb/batch2/tree-browser-tests.js
+++ b/tests/jest/vfb/batch2/tree-browser-tests.js
@@ -184,7 +184,7 @@ describe('VFB Tree Browser Component Tests', () => {
await page.evaluate(async () => {
let tabs = document.getElementsByClassName('MuiListItem-root ');
for ( var i = 0; i < tabs.length ; i ++ ) {
- if ( tabs[i].innerText === "medulla (FBbt_00003748)" ) {
+ if ( tabs[i].innerText.split('\n')[0] === "medulla (FBbt_00003748)" ) {
tabs[i].click();
}
}
diff --git a/tests/jest/vfb/batch3/term-context-tests.js b/tests/jest/vfb/batch3/term-context-tests.js
index c1bd76d25..b0e3ef109 100644
--- a/tests/jest/vfb/batch3/term-context-tests.js
+++ b/tests/jest/vfb/batch3/term-context-tests.js
@@ -73,7 +73,7 @@ describe('VFB Term Context Component Tests', () => {
await page.evaluate(async () => {
let tabs = document.getElementsByClassName('MuiListItem-root ');
for ( var i = 0; i < tabs.length ; i ++ ) {
- if ( tabs[i].innerText === "medulla (FBbt_00003748)" ) {
+ if ( tabs[i].innerText.split('\n')[0] === "medulla (FBbt_00003748)" ) {
tabs[i].click();
}
}
diff --git a/tests/jest/vfb/review/downloader-tests.js b/tests/jest/vfb/review/downloader-tests.js
new file mode 100644
index 000000000..d9b35ea05
--- /dev/null
+++ b/tests/jest/vfb/review/downloader-tests.js
@@ -0,0 +1,82 @@
+const puppeteer = require('puppeteer');
+const { TimeoutError } = require('puppeteer/Errors');
+
+import { getUrlFromProjectId } from '../cmdline.js';
+import { wait4selector, click, closeModalWindow, findElementByText } from '../utils';
+import * as ST from '../selectors';
+
+const baseURL = process.env.url || 'http://localhost:8081/org.geppetto.frontend';
+const PROJECT_URL = baseURL + "/geppetto?id=VFB_00102107&i=VFB_00101567,VFB_00102271,VFB_00102107";
+
+/**
+ * Query Builder component tests
+ */
+describe('VFB Downloader Tests', () => {
+ beforeAll(async () => {
+ jest.setTimeout(1800000);
+ await page.goto(PROJECT_URL);
+ });
+
+ describe('Test landing page', () => {
+ it('Loading spinner goes away', async () => {
+ await wait4selector(page, ST.SPINNER_SELECTOR, { hidden: true, timeout : 120000 })
+ // Close tutorial window
+ closeModalWindow(page);
+ })
+
+ it('VFB Title shows up', async () => {
+ const title = await page.title();
+ expect(title).toMatch("Virtual Fly Brain");
+ })
+
+ it('Zoom button for VFB_00102107 appears in button bar inside the term info component', async () => {
+ await wait4selector(page, 'button[id=VFB_00102107_zoom_buttonBar_btn]', { visible: true , timeout : 120000 })
+ })
+
+ it('Term info component created after load', async () => {
+ await wait4selector(page, 'div#bar-div-vfbterminfowidget', { visible: true })
+ })
+
+ //Tests canvas has 5 meshes rendered
+ it('Canvas container component has 3 meshes rendered', async () => {
+ expect(
+ await page.evaluate(async () => Object.keys(CanvasContainer.engine.meshes).length)
+ ).toBe(3)
+ })
+ })
+
+ describe('Tests Download Contents', () => {
+ it('Open Download Component Files', async () => {
+ await page.click('i.fa-download');
+
+ await page.waitForSelector('#downloadContents');
+ })
+
+ it('Download disabled by default', async () => {
+ expect(
+ await page.evaluate(async () => document.querySelector('#downloadContentsButton').disabled )
+ ).toBe(true)
+ })
+
+ it('Download all files for all instances', async () => {
+ await page.evaluate(async selector => {
+ let inputs = document.querySelectorAll(".MuiDialogContent-root input");
+ inputs = Array.prototype.slice.call(inputs).filter(input => input.id != "ALL_INSTANCES");
+ inputs.forEach( input => {
+ input.click();
+ });
+ });
+
+ expect(
+ await page.evaluate(async () => document.querySelector('#downloadContentsButton').disabled )
+ ).toBe(false)
+ })
+
+
+ it('Download Contents Dialog goes away', async () => {
+ await page.click('#downloadContentsButton');
+ await wait4selector(page, '#downloadContents', { hidden: true , timeout : 120000 })
+ })
+
+ })
+})
diff --git a/tests/jest/vfb/review/nblast-uploader-tests.js b/tests/jest/vfb/review/nblast-uploader-tests.js
new file mode 100644
index 000000000..3a81c62a7
--- /dev/null
+++ b/tests/jest/vfb/review/nblast-uploader-tests.js
@@ -0,0 +1,75 @@
+const puppeteer = require('puppeteer');
+const { TimeoutError } = require('puppeteer/Errors');
+
+import { getUrlFromProjectId } from '../cmdline.js';
+import { wait4selector, click, closeModalWindow, findElementByText } from '../utils';
+import * as ST from '../selectors';
+
+const baseURL = process.env.url || 'http://localhost:8081/org.geppetto.frontend';
+const PROJECT_URL = baseURL + "/geppetto?id=VFB_00017894";
+
+/**
+ * Query Builder component tests
+ */
+describe('VFB Uploader Tests', () => {
+ beforeAll(async () => {
+ jest.setTimeout(1800000);
+ await page.goto(PROJECT_URL);
+ });
+
+ describe('Test landing page', () => {
+ it('Loading spinner goes away', async () => {
+ await wait4selector(page, ST.SPINNER_SELECTOR, { hidden: true, timeout : 120000 })
+ // Close tutorial window
+ closeModalWindow(page);
+ })
+
+ it('VFB Title shows up', async () => {
+ const title = await page.title();
+ expect(title).toMatch("Virtual Fly Brain");
+ })
+
+ it('Zoom button for VFB_00017894 appears in button bar inside the term info component', async () => {
+ await wait4selector(page, 'button[id=VFB_00017894_zoom_buttonBar_btn]', { visible: true , timeout : 120000 })
+ })
+
+ it('Term info component created after load', async () => {
+ await wait4selector(page, 'div#bar-div-vfbterminfowidget', { visible: true })
+ })
+ })
+
+ describe('Tests NBLAST Uploader', () => {
+ it('Open Uploader', async () => {
+ await page.click('#fa-upload');
+
+ await page.waitForSelector('div.MuiDialog-root');
+ })
+
+ it('Templates Populated', async () => {
+ await page.evaluate(async () => {
+ var dropdown = document.getElementById('mui-component-select-template');
+ var event = document.createEvent('MouseEvents');
+ event.initMouseEvent('mousedown', true, true, window);
+ dropdown.dispatchEvent(event);
+ });
+
+ await page.waitForSelector('li.MuiListItem-root');
+
+ const list = await page.evaluate(async () => {
+ document.querySelectorAll('.MuiListItem-root')[1].click();
+ return document.querySelectorAll('.MuiListItem-root').length;
+ });
+
+ expect(list).toBe(3);
+ })
+
+ it('Template Selected', async () => {
+ const selection = await page.evaluate(async () => {
+ return document.getElementById("template-selection").value;
+ });
+
+ expect(selection).toBe("JRC2018Unisex");
+ })
+
+ })
+})
diff --git a/tests/jest/vfb/snapshots/term-context/adult-brain/term-context-tests-js-vfb-term-context-component-tests-add-medulla-snapshot-comparison-of-term-context-after-medulla-loaded-graph-remains-the-same-1-snap.png b/tests/jest/vfb/snapshots/term-context/adult-brain/term-context-tests-js-vfb-term-context-component-tests-add-medulla-snapshot-comparison-of-term-context-after-medulla-loaded-graph-remains-the-same-1-snap.png
index cab033cce..3a5593dc3 100644
Binary files a/tests/jest/vfb/snapshots/term-context/adult-brain/term-context-tests-js-vfb-term-context-component-tests-add-medulla-snapshot-comparison-of-term-context-after-medulla-loaded-graph-remains-the-same-1-snap.png and b/tests/jest/vfb/snapshots/term-context/adult-brain/term-context-tests-js-vfb-term-context-component-tests-add-medulla-snapshot-comparison-of-term-context-after-medulla-loaded-graph-remains-the-same-1-snap.png differ
diff --git a/tests/jest/vfb/snapshots/term-context/adult-brain/term-context-tests-js-vfb-term-context-component-tests-test-term-context-component-snapshot-comparison-of-term-context-1-snap.png b/tests/jest/vfb/snapshots/term-context/adult-brain/term-context-tests-js-vfb-term-context-component-tests-test-term-context-component-snapshot-comparison-of-term-context-1-snap.png
index 682c069a8..9291d978d 100644
Binary files a/tests/jest/vfb/snapshots/term-context/adult-brain/term-context-tests-js-vfb-term-context-component-tests-test-term-context-component-snapshot-comparison-of-term-context-1-snap.png and b/tests/jest/vfb/snapshots/term-context/adult-brain/term-context-tests-js-vfb-term-context-component-tests-test-term-context-component-snapshot-comparison-of-term-context-1-snap.png differ
diff --git a/tests/jest/vfb/snapshots/term-context/medulla/term-context-tests-js-vfb-term-context-component-tests-add-medulla-snapshot-comparison-of-term-context-after-sync-trigger-graph-displays-medulla-1-snap.png b/tests/jest/vfb/snapshots/term-context/medulla/term-context-tests-js-vfb-term-context-component-tests-add-medulla-snapshot-comparison-of-term-context-after-sync-trigger-graph-displays-medulla-1-snap.png
index 41a04136b..8ebbb926b 100644
Binary files a/tests/jest/vfb/snapshots/term-context/medulla/term-context-tests-js-vfb-term-context-component-tests-add-medulla-snapshot-comparison-of-term-context-after-sync-trigger-graph-displays-medulla-1-snap.png and b/tests/jest/vfb/snapshots/term-context/medulla/term-context-tests-js-vfb-term-context-component-tests-add-medulla-snapshot-comparison-of-term-context-after-sync-trigger-graph-displays-medulla-1-snap.png differ