diff --git a/components/tests/python/library/__init__.py b/components/tests/python/library/__init__.py
index 4825ca12388..991bde3e29c 100644
--- a/components/tests/python/library/__init__.py
+++ b/components/tests/python/library/__init__.py
@@ -117,6 +117,20 @@ def teardown_class(cls):
cls.root = None
cls.__clients.__del__()
+ def keepRootAlive(self):
+ """
+ Keeps root connection alive.
+ """
+ try:
+ if self.root.sf is None:
+ p = Ice.createProperties(sys.argv)
+ rootpass = p.getProperty("omero.rootpass")
+ self.root.createSession("root", rootpass)
+ else:
+ self.root.sf.keepAlive(None)
+ except Exception:
+ raise
+
@classmethod
def omeropydir(self):
count = 10
diff --git a/components/tests/ui/resources/web/tree.txt b/components/tests/ui/resources/web/tree.txt
index 21f421ef09f..f00fa4a42f2 100644
--- a/components/tests/ui/resources/web/tree.txt
+++ b/components/tests/ui/resources/web/tree.txt
@@ -283,11 +283,11 @@ Wait Until Right Panel Loads Everything
Wait Until Right Panel Loads ${containerType} ${containerId}
Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'${containerType} Details')]
- Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Tags')]
- Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Key-Value Pairs')]
- Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Attachments')]
- Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Ratings')]
- Wait Until Element Is Visible xpath=//*[@id="general_tab"]/h1[contains(text(),'Comments')]
+ Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/h1[contains(text(),'Tags')]
+ Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/h1[contains(text(),'Key-Value Pairs')]
+ Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/h1[contains(text(),'Attachments')]
+ Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/h1[contains(text(),'Ratings')]
+ Wait Until Element Is Visible xpath=//*[@id="general_tab"]/div/h1[contains(text(),'Comments')]
Run Keyword If '${containerType}' == 'Project' Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Creation Date:')]
Run Keyword If '${containerType}' == 'Dataset' Wait Until Element Is Visible ${detailsPane}//th[contains(text(),'Creation Date:')]
Run Keyword If '${containerType}' == 'Image' Check Right Panel Image
diff --git a/components/tests/ui/robot_setup.sh b/components/tests/ui/robot_setup.sh
index 10b6ee71489..9cad4094e6a 100644
--- a/components/tests/ui/robot_setup.sh
+++ b/components/tests/ui/robot_setup.sh
@@ -21,6 +21,7 @@ TINY_IMAGE_NAME=${TINY_IMAGE_NAME:-test&acquisitionDate=2012-01-01_00-00-00.fake
MIF_IMAGE_NAME=${MIF_IMAGE_NAME:-test&series=3.fake}
PLATE_NAME=${PLATE_NAME:-test&plates=1&plateAcqs=1&plateRows=2&plateCols=3&fields=1&screens=0.fake}
BULK_ANNOTATION_CSV=${BULK_ANNOTATION_CSV:-bulk_annotation.csv}
+FILE_ANNOTATION=${FILE_ANNOTATION:-robot_file_annotation.txt}
# Create robot user and group
bin/omero login root@$HOSTNAME:$PORT -w $ROOT_PASSWORD
@@ -41,6 +42,9 @@ echo "Well,Well Type,Concentration" > "$BULK_ANNOTATION_CSV"
echo "A1,Control,0" >> "$BULK_ANNOTATION_CSV"
echo "A2,Treatment,10" >> "$BULK_ANNOTATION_CSV"
+# Create file for upload as File Annotation
+echo "Robot test file annotations" > "$FILE_ANNOTATION"
+
# Create robot setup
bin/omero login $USER_NAME@$HOSTNAME:$PORT -w $USER_PASSWORD
# Parse the sessions file to get session key
@@ -113,6 +117,11 @@ do
bin/omero import $TINY_IMAGE_NAME --debug ERROR
done
+# Uplodad file and create FileAnnotation
+bin/omero upload $FILE_ANNOTATION > file_upload.log
+fileId=$(sed "s/^Uploaded $FILE_ANNOTATION as //" file_upload.log)
+bin/omero obj new FileAnnotation file=OriginalFile:$fileId
+
# Logout
bin/omero logout
diff --git a/components/tests/ui/testcases/web/annotate_test.txt b/components/tests/ui/testcases/web/annotate_test.txt
new file mode 100644
index 00000000000..6d4fa6c3138
--- /dev/null
+++ b/components/tests/ui/testcases/web/annotate_test.txt
@@ -0,0 +1,221 @@
+*** Settings ***
+Documentation Tests submission of forms.
+
+Resource ../../resources/config.txt
+Resource ../../resources/web/login.txt
+Resource ../../resources/web/tree.txt
+
+Suite Setup Run Keywords User "${USERNAME}" logs in with password "${PASSWORD}" Maximize Browser Window
+Suite Teardown Close all browsers
+
+*** Variables ***
+${commentText} Robot test adding this comment
+${commentTextTwo} A second comment added by Robot test
+${commentTextThree} This will be added to Two Datasets
+${commentTextFour} I (Robot) just love adding comments!
+${fileName} robot_file_annotation.txt
+${fileNameTwo} bulk_annotations
+${SEARCH URL} ${WELCOME URL}search/
+
+
+*** Keywords ***
+
+Check For Comment
+ [Arguments] ${text}
+ Wait Until Page Contains Element xpath=//div[@class='commentText'][contains(text(), '${text}')]
+
+Check Comment Gone
+ [Arguments] ${text}
+ Page Should Not Contain Element xpath=//div[@class='commentText'][contains(text(), '${text}')]
+
+Add Comment
+ [Arguments] ${text}
+ Wait Until Element Is Visible id=id_comment
+ Input Text comment ${text}
+ Submit Form add_comment_form
+ Check For Comment ${text}
+
+Remove Comment
+ [Arguments] ${text}
+ Click Element xpath=//div[contains(@class, 'ann_comment_text')][descendant::div[contains(text(), '${text}')]]/img[@class='removeComment']
+ Click Dialog Button OK
+ Wait Until Keyword Succeeds ${TIMEOUT} ${INTERVAL} Check Comment Gone ${text}
+
+
+Add File Annotation
+ [Arguments] ${fileAnnotationName}
+ Wait Until Element Is Visible id=choose_file_anns
+ Click Element id=choose_file_anns
+ Wait Until Page Contains Element id=id_files
+ # Click Element xpath=//select[@id='id_files']/option[contains(text(), ${fileName})]
+ Select From List By Label id=id_files ${fileAnnotationName}
+ Click Dialog Button Accept
+ Check For File Annotation ${fileAnnotationName}
+
+Check For File Annotation
+ [Arguments] ${fileAnnotationName}
+ Wait Until Element Is Visible xpath=//li[contains(@class, 'file_ann_wrapper')][descendant::a[contains(text(), '${fileAnnotationName}')]]//*[@class='removeFile action']
+ Wait Until Element Is Visible xpath=//li[contains(@class, 'file_ann_wrapper')][descendant::a[contains(text(), '${fileAnnotationName}')]]//a[@class='deleteFile action']
+ Wait Until Element Is Visible xpath=//ul[@id="fileanns_container"]//a[contains(text(), '${fileAnnotationName}')]
+ Wait Until Element Is Visible xpath=//li[contains(@class, 'file_ann_wrapper')][descendant::a[contains(text(), '${fileAnnotationName}')]]//span
+ # Mouse Over //ul[@id="fileanns_container"]//a[contains(@class, 'tooltip')]
+ # Wait Until Element Is Visible //ul[@id="fileanns_container"]//a[contains(@class, 'tooltip')][contains(@aria-describedby, 'ui-tooltip')]
+
+Remove File Annotation
+ [Arguments] ${fileAnnotationName}
+
+ Wait Until Element Is Visible xpath=//li[contains(@class, 'file_ann_wrapper')][descendant::a[contains(text(), '${fileAnnotationName}')]]//*[@class='removeFile action']
+ Click Element xpath=//li[contains(@class, 'file_ann_wrapper')][descendant::*[contains(text(), '${fileAnnotationName}')]]//*[@class='removeFile action']
+
+ Click Dialog Button OK
+ Wait Until Keyword Succeeds ${TIMEOUT} ${INTERVAL} Check File Annotation Gone ${fileAnnotationName}
+
+Check File Annotation Gone
+ [Arguments] ${fileAnnotationName}
+ Page Should Not Contain Element xpath=//ul[@id="fileanns_container"]//a[contains(text(), '${fileAnnotationName}')]
+
+
+*** Test Cases ***
+
+Test Comments
+
+ Select Experimenter
+ ${dsId_One}= Create Dataset robot test comments_1
+ ${dsId_Two}= Create Dataset robot test comments_2
+
+ # Comment a single Dataset
+ Click Element xpath=//h1[@data-name='comments']
+ Add Comment ${commentText}
+
+ # Refresh (select other Dataset and re-select)
+ ${nodeId}= Select Dataset By Id ${dsId_One}
+ Wait Until Right Panel Loads Dataset ${dsId_One}
+ Select Dataset By Id ${dsId_Two}
+ # Check and add another Comment
+ Check For Comment ${commentText}
+ Add Comment ${commentTextTwo}
+ # Remove first comment
+ Remove Comment ${commentText}
+
+ # Now select both Datasets...
+ Meta Click Node ${nodeId}
+ Wait Until Page Contains Element id=batch_ann_title
+ # Previously added Comment will show up
+ Check For Comment ${commentTextTwo}
+ Page Should Not Contain Element xpath=//div[@class='commentText'][contains(text(), '${commentText}')]
+ # Add Comments to Both Datasets
+ Add Comment ${commentTextThree}
+ Add Comment ${commentTextFour}
+
+ # Select each single Dataset to check for Comment(s)
+ Select Dataset By Id ${dsId_One}
+ Check Comment Gone ${commentTextTwo}
+ Check For Comment ${commentTextThree}
+ Select Dataset By Id ${dsId_Two}
+ Check For Comment ${commentTextTwo}
+ Check For Comment ${commentTextThree}
+
+ # Select both Datasets and Remove Comments
+ Meta Click Node ${nodeId}
+ Wait Until Page Contains Element id=batch_ann_title
+ Remove Comment ${commentTextTwo}
+ Remove Comment ${commentTextThree}
+
+ # Select each single Dataset again to check for Comment(s)
+ Select Dataset By Id ${dsId_One}
+ Check For Comment ${commentTextFour}
+ Check Comment Gone ${commentTextTwo}
+ Check Comment Gone ${commentTextThree}
+ Select Dataset By Id ${dsId_Two}
+ Check For Comment ${commentTextFour}
+ Check Comment Gone ${commentTextTwo}
+ Check Comment Gone ${commentTextThree}
+
+ Select Dataset By Id ${dsId_One}
+ Delete Container
+
+ Select Dataset By Id ${dsId_Two}
+ Delete Container
+
+
+Test File Annotations
+
+ Select Experimenter
+ ${sId_One}= Create Screen robot file annotations_1
+ ${sId_Two}= Create Screen robot file annotations_2
+
+ # Annotate single Screen
+ Click Element xpath=//h1[@data-name='attachments']
+ Add File Annotation ${fileName}
+
+ # Refresh (select other Screen and re-select)
+ ${nodeId}= Select Screen By Id ${sId_One}
+ Wait Until Right Panel Loads Screen ${sId_One}
+ Select Screen By Id ${sId_Two}
+ # Check and add another File Annotation
+ Check For File Annotation ${fileName}
+ Add File Annotation ${fileNameTwo}
+ # Remove first File Annotation
+ Remove File Annotation ${fileName}
+
+ # Now select both Screens...
+ Meta Click Node ${nodeId}
+ Wait Until Page Contains Element id=batch_ann_title
+ # Previously added File Annotation will show up
+ Check For File Annotation ${fileNameTwo}
+ Check File Annotation Gone ${fileName}
+ # Add File Annotation to Both Screens
+ Add File Annotation ${fileName}
+ Select Screen By Id ${sId_One}
+ Check For File Annotation ${fileName}
+ Select Screen By Id ${sId_Two}
+ Check For File Annotation ${fileName}
+
+ # Now select both Screens...
+ Meta Click Node ${nodeId}
+ Wait Until Page Contains Element id=batch_ann_title
+ # Remove first File Annotation
+ Remove File Annotation ${fileName}
+ Select Screen By Id ${sId_One}
+ Check File Annotation Gone ${fileName}
+ Select Screen By Id ${sId_Two}
+ Check File Annotation Gone ${fileName}
+
+ Delete Container
+ Select Screen By Id ${sId_One}
+ Delete Container
+
+# Test Rating
+
+Test Search Results File Annotations
+
+ Select Experimenter
+ ${sId_One}= Create Screen robot file annotations_1
+
+ # Annotate single Screen
+ Add File Annotation ${fileNameTwo}
+
+ Input Text id=id_search_query ${fileNameTwo}
+ Submit Form
+ Location Should Be ${SEARCH URL}
+
+ Wait Until Page Contains Element xpath=//img[contains(@alt, 'plate')]
+ Click Element xpath=//img[contains(@alt, 'plate')]
+ Wait Until Page Contains Element //*[@id="general_tab"]//th[contains(text(), 'Plate ID:')]
+
+ #Minimal checking to check if you can add Annotations on the search result page
+ Click Element xpath=//h1[@data-name='attachments']
+ Check For File Annotation ${fileNameTwo}
+ Remove File Annotation ${fileNameTwo}
+ Add File Annotation ${fileNameTwo}
+ Click Element xpath=//h1[@data-name='comments']
+ Add Comment ${commentText}
+
+ Go To ${WELCOME URL}
+ Select Experimenter
+ Select Screen By Id ${sId_One}
+ Delete Container
+
+
+
+
\ No newline at end of file
diff --git a/components/tests/ui/testcases/web/center_right_panel_tests.txt b/components/tests/ui/testcases/web/center_right_panel_tests.txt
index 543e505f231..63492e0607d 100644
--- a/components/tests/ui/testcases/web/center_right_panel_tests.txt
+++ b/components/tests/ui/testcases/web/center_right_panel_tests.txt
@@ -34,22 +34,25 @@ Wait Until Right Panel Loads For MultiSelection
Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/button[contains(@title, 'Download Image as...')]
Wait Until Element Is Visible xpath=//*[@id="show_link_btn"]/span
- Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h2[contains(text(), 'Annotations')]
+ Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Attachments')]
Wait Until Element Is Visible xpath=//*[@id="annotationFilter"]
- Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h2[contains(text(), 'Rating')]
- Wait Until Element Is Visible xpath=//*[@id="add_rating"]/span
+ Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Ratings')]
+ Click Element xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Ratings')]
+ Wait Until Element Is Visible xpath=//*[@id="rating_annotations"]
- Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h2[contains(text(), 'Tags')]
+ Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Tags')]
+ Click Element xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Tags')]
Wait Until Element Is Visible xpath=//*[@id="launch_tags_form"]/span
- Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h2[contains(text(), 'Attach')]
- Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/input[contains(@title, 'Select Files for Scripts')]
+ Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Attachments')]
+ Click Element xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Attachments')]
+ Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/input[contains(@title, 'Select files for scripts')]
Wait Until Element Is Visible xpath=//*[@id="choose_file_anns"]/span
- Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h2[contains(text(), 'Comment')]
+ Wait Until Element Is Visible xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Comments')]
+ Click Element xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Comments')]
Wait Until Element Is Visible xpath=//*[@id="id_comment"]
- Wait Until Element Is Visible xpath=//*[@id="add_comment_form"]//td/input[contains(@value, 'Add Comment')]
Element Should Not Be Visible xpath=//*[@id="general_tab"]
diff --git a/components/tests/ui/testcases/web/forms_test.txt b/components/tests/ui/testcases/web/forms_test.txt
index 3303c2232e2..69124220f1f 100644
--- a/components/tests/ui/testcases/web/forms_test.txt
+++ b/components/tests/ui/testcases/web/forms_test.txt
@@ -144,11 +144,15 @@ Test Batch Annotate
Wait Until Page Contains Element id=batch_ann_title
# Comment Form
+ Click Element xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Comments')]
+ Wait Until Element Is Visible id=id_comment
Input Text comment test add comment
- Click Button Add Comment
+ Submit Form add_comment_form
Wait Until Page Contains test add comment
# Tags
+ Click Element xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Tags')]
+ Wait Until Element Is Visible id=launch_tags_form
Click Element launch_tags_form
Wait Until Page Contains Element id_tag
# Wait for tag panel being loded
@@ -160,6 +164,8 @@ Test Batch Annotate
Wait Until Page Contains Element xpath=//div[@class='tag']/a[contains(text(), testSeleniumTag${pid})] ${WAIT}
# Files
+ Click Element xpath=//*[@id="metadata_general"]//div/h1[contains(text(), 'Attachments')]
+ Wait Until Element Is Visible id=choose_file_anns
Click Element choose_file_anns
Wait Until Page Contains Element id_files
Click Element xpath=//select[@id='id_files']/option # just pick first file
diff --git a/components/tests/ui/testcases/web/map_annotations_test.txt b/components/tests/ui/testcases/web/map_annotations_test.txt
index cff946c4e34..57a67dc2ce1 100644
--- a/components/tests/ui/testcases/web/map_annotations_test.txt
+++ b/components/tests/ui/testcases/web/map_annotations_test.txt
@@ -17,7 +17,7 @@ Test Map Annotation
Wait Until Page Contains Element css=div.mapAnnContainer
Click Element xpath=//h1[@data-name='keyvaluepairs']
# No rows selected. Toolbar should allow 'Insert' only.
- Xpath Should Not Have Class xpath=//ul[contains(@class, 'mapAnnToolbar')]//input[@title='Insert rows'] button-disabled
+ Xpath Should Have Class xpath=//ul[contains(@class, 'mapAnnToolbar')]//input[@title='Insert row'] button-disabled
Xpath Should Have Class xpath=//ul[contains(@class, 'mapAnnToolbar')]//input[@title='Copy rows'] button-disabled
Xpath Should Have Class xpath=//ul[contains(@class, 'mapAnnToolbar')]//input[@title='Paste rows'] button-disabled
Xpath Should Have Class xpath=//ul[contains(@class, 'mapAnnToolbar')]//input[@title='Delete rows'] button-disabled
diff --git a/components/tests/ui/testcases/web/search.txt b/components/tests/ui/testcases/web/search.txt
index 2e15d72e8b3..4241c3bea22 100644
--- a/components/tests/ui/testcases/web/search.txt
+++ b/components/tests/ui/testcases/web/search.txt
@@ -21,7 +21,7 @@ ${XpathProjectThumb} xpath=//table[@id='dataTable']//img[contains(@src,'folde
${XpathPlate} xpath=//table[@id='dataTable']//img[@alt='plate']
${XpathPlateThumb} xpath=//table[@id='dataTable']//img[contains(@src,'folder_plate16.png')]
${XpathScreen} xpath=//table[@id='dataTable']//img[@alt='screen']
-${XpathScreenValid} xpath=//table[@id='dataTable']//img[contains(@src,'folder_screen16.png')]
+${XpathScreenThumb} xpath=//table[@id='dataTable']//img[contains(@src,'folder_screen16.png')]
${XpathTable} xpath=//table[@id='dataTable']
${XpathTableColumn} xpath=//table[@id='dataTable']//tr/td[position()=3]
${XPathAlertText} xpath=//div[contains(@class,'ui-dialog')][contains(@style,'display: block')]//p
diff --git a/components/tests/ui/testcases/web/tagging_test.txt b/components/tests/ui/testcases/web/tagging_test.txt
index 680e43c48cc..57576cc9fa1 100644
--- a/components/tests/ui/testcases/web/tagging_test.txt
+++ b/components/tests/ui/testcases/web/tagging_test.txt
@@ -24,19 +24,20 @@ Test Tag
Click Element xpath=//h1[@data-name='tags']
Click Element launch_tags_form
Wait Until Page Contains Element id_tag
- Sleep 5 # allow tags to load
+ Wait Until Page Contains Element xpath=//div[contains(@class,'ui-progressbar')][@aria-valuenow='100'] ${WAIT}
Input Text id_tag robotTagTest${pid}TagOne
Click Element id_add_new_tag
Click Dialog Button Save
Wait Until Page Contains Element xpath=//div[@class='tag']/a[contains(text(), 'robotTagTest${pid}TagOne')] 10
- # Refresh, check and add another Tag, remove first one
- Go To ${WELCOME URL}?show=project-${pId}
- Wait Until Page Contains Element xpath=//h1[@data-name='tags']
- Click Element xpath=//h1[@data-name='tags']
+ # Refresh (select other Project and re-select)
+ ${nodeId}= Select Project By Id ${projectId}
+ Wait Until Right Panel Loads Project ${projectId}
+ Select Project By Id ${pId}
+ # Check and add another Tag, remove first one
Wait Until Page Contains Element xpath=//div[@class='tag']/a[contains(text(), 'robotTagTest${pid}TagOne')] 10
Click Element launch_tags_form
Wait Until Page Contains Element id_tag
- Sleep 5 # allow tags to load
+ Wait Until Page Contains Element xpath=//div[contains(@class,'ui-progressbar')][@aria-valuenow='100'] ${WAIT}
# Create a second Tag
Input Text id_tag robotTagTest${pid}TagTwo
Click Element id_add_new_tag
@@ -51,12 +52,14 @@ Test Tag
# Now select both Projects...
- Go To ${WELCOME URL}?show=project-${projectId}|project-${pId}
+ Meta Click Node ${nodeId}
Wait Until Page Contains Element id=batch_ann_title
-
+ # Previously added Tag will show up
+ Wait Until Page Contains Element xpath=//div[@class='tag']/a[contains(text(), 'robotTagTest${pid}TagTwo')]
+ Wait Until Element Is Visible launch_tags_form
Click Element launch_tags_form
Wait Until Page Contains Element id_tag
- Sleep 5 # allow tags to load
+ Wait Until Page Contains Element xpath=//div[contains(@class,'ui-progressbar')][@aria-valuenow='100'] ${WAIT}
# Tags created above should be available to add to second Project
Wait Until Page Contains Element xpath=//div[@id='id_all_tags']/div[contains(text(),'robotTagTest${pid}TagOne')]
@@ -82,7 +85,7 @@ Test Tag
# Open Tag dialog again...
Click Element launch_tags_form
Wait Until Page Contains Element id_tag
- Sleep 5 # allow tags to load
+ Wait Until Page Contains Element xpath=//div[contains(@class,'ui-progressbar')][@aria-valuenow='100'] ${WAIT}
# Same tag as before should be on right
Wait Until Page Contains Element xpath=//div[@id='id_selected_tags']/div[contains(text(),'robotTagTest${pid}TagOne')]
Page Should Not Contain Element xpath=//div[@id='id_selected_tags']/div[contains(text(),'robotTagTest${pid}TagTwo')]
@@ -96,6 +99,8 @@ Test Tag
# After Save, Tags Two and Three should be added, Tag One removed
# Seems we need a proper refresh here to be sure that TagOne is removed.
Go To ${WELCOME URL}?show=project-${projectId}|project-${pId}
+ Wait Until Element Is Visible xpath=//h1[@data-name='tags']
+ Click Element xpath=//h1[@data-name='tags']
Wait Until Page Contains Element xpath=//div[@class='tag']/a[contains(text(), 'robotTagTest${pid}TagThree')] 10
Page Should Contain Element xpath=//div[@class='tag']/a[contains(text(), 'robotTagTest${pid}TagTwo')]
Page Should Not Contain Element xpath=//div[@class='tag']/a[contains(text(), 'robotTagTest${pid}TagOne')]
diff --git a/components/tools/OmeroPy/src/omero/plugins/web.py b/components/tools/OmeroPy/src/omero/plugins/web.py
index 91956092058..bed0922225b 100644
--- a/components/tools/OmeroPy/src/omero/plugins/web.py
+++ b/components/tools/OmeroPy/src/omero/plugins/web.py
@@ -58,6 +58,12 @@
"""
+APACHE_MOD_WSGI_ERR = ("[ERROR] You are deploying OMERO.web using Apache and"
+ " mod_wsgi. OMERO.web does not provide any management"
+ " for the daemon process which communicates with"
+ " Apache child processes using UNIX sockets to handle"
+ " a request.")
+
def config_required(func):
"""Decorator validating Django dependencies and omeroweb/settings.py"""
@@ -516,11 +522,10 @@ def start(self, args, settings):
deploy = getattr(settings, 'APPLICATION_SERVER')
if deploy in (settings.WSGI,):
- self.ctx.die(609, "You are deploying OMERO.web using apache and"
- " mod_wsgi. Generate apache config using"
+ self.ctx.die(609, "%s\nGenerate apache config using"
" 'omero web config apache' or"
" 'omero web config apache24' and reload"
- " web server.")
+ " web server." % APACHE_MOD_WSGI_ERR)
else:
self.ctx.out("Starting OMERO.web... ", newline=False)
@@ -615,8 +620,8 @@ def status(self, args, settings):
else:
self.ctx.err("[NOT STARTED]")
elif deploy in (settings.WSGI,):
- self.ctx.err("You are deploying OMERO.web using apache and"
- " mod_wsgi. Cannot check status.")
+ self.ctx.err("%s Please check Apache "
+ "directly." % APACHE_MOD_WSGI_ERR)
elif deploy in (settings.DEVELOPMENT,):
self.ctx.err(
"DEVELOPMENT: You will have to kill processes by hand!")
diff --git a/components/tools/OmeroPy/test/integration/clitest/test_import.py b/components/tools/OmeroPy/test/integration/clitest/test_import.py
index beb1e93f596..04661c1aee6 100644
--- a/components/tools/OmeroPy/test/integration/clitest/test_import.py
+++ b/components/tools/OmeroPy/test/integration/clitest/test_import.py
@@ -120,6 +120,7 @@ def setup_method(self, method):
self.cli.register("import", plugin.ImportControl, "TEST")
self.args += ["import"]
self.add_client_dir()
+ self.keepRootAlive()
def set_conn_args(self):
host = self.root.getProperty("omero.host")
diff --git a/components/tools/OmeroPy/test/unit/clitest/test_web.py b/components/tools/OmeroPy/test/unit/clitest/test_web.py
index 0d600d0bd52..a838fc32cbc 100644
--- a/components/tools/OmeroPy/test/unit/clitest/test_web.py
+++ b/components/tools/OmeroPy/test/unit/clitest/test_web.py
@@ -185,13 +185,17 @@ def testWebStart(self, app_server, monkeypatch, capsys):
csout = "Clearing expired sessions. This may take some time... [OK]"
assert csout == o.split(os.linesep)[0]
if app_server in ('wsgi',):
- stderr = (
- ("You are deploying OMERO.web using apache and mod_wsgi. "
- "Generate apache config using 'omero web config apache' "
- "or 'omero web config apache24' "
- "and reload web server."))
- assert stderr == e.split(os.linesep)[0]
- assert 1 == len(e.split(os.linesep))-1
+ stderr0 = ("[ERROR] You are deploying OMERO.web using Apache and"
+ " mod_wsgi. OMERO.web does not provide any management"
+ " for the daemon process which communicates"
+ " with Apache child processes using UNIX sockets"
+ " to handle a request.")
+ stderr1 = ("Generate apache config using"
+ " 'omero web config apache' or"
+ " 'omero web config apache24' and reload web server.")
+ assert stderr0 == e.split(os.linesep)[0]
+ assert stderr1 == e.split(os.linesep)[1]
+ assert 2 == len(e.split(os.linesep))-1
elif app_server in ('wsgi-tcp',):
startout = "Starting OMERO.web... [OK]"
assert startout == o.split(os.linesep)[1]
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py b/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py
index b5d31368c0f..7864a6fb371 100644
--- a/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py
+++ b/components/tools/OmeroWeb/omeroweb/webclient/controller/container.py
@@ -241,7 +241,7 @@ def canDownload(self, objDict=None):
# As used in metadata_general panel
else:
return self.image.canDownload() or \
- self.well.canDownload() or self.plate.canDonwload()
+ self.well.canDownload() or self.plate.canDownload()
def listFigureScripts(self, objDict=None):
"""
@@ -1223,8 +1223,8 @@ def move(self, parent, destination):
def remove(self, parents, index, tag_owner_id=None):
"""
Removes the current object (file, tag, comment, dataset, plate, image)
- from its parents by manually deleting the link.
- For Comments, we check whether it becomes an orphan & delete if true
+ from its parents by manually deleting the link. Orphaned comments will
+ be deleted server side.
If self.tag and owner_id is specified, only remove the tag if it is
owned by that owner
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/decorators.py b/components/tools/OmeroWeb/omeroweb/webclient/decorators.py
index d0d3c1a1459..c8f7158cc6f 100644
--- a/components/tools/OmeroWeb/omeroweb/webclient/decorators.py
+++ b/components/tools/OmeroWeb/omeroweb/webclient/decorators.py
@@ -26,6 +26,7 @@
import logging
import omeroweb.decorators
+from omero import constants
from django.http import HttpResponse
from django.conf import settings
@@ -115,6 +116,13 @@ def prepare_context(self, request, context, *args, **kwargs):
return
conn = kwargs['conn']
+ # omero constants
+ context['omero'] = {'constants': {
+ 'NSCOMPANIONFILE': constants.namespaces.NSCOMPANIONFILE,
+ 'ORIGINALMETADATA': constants.annotation.file.ORIGINALMETADATA,
+ 'NSCLIENTMAPANNOTATION': constants.metadata.NSCLIENTMAPANNOTATION
+ }}
+
context.setdefault('ome', {}) # don't overwrite existing ome
context['ome']['eventContext'] = conn.getEventContext
context['ome']['user'] = conn.getUser
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/dusty.css b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/dusty.css
index 78871dc8302..047a44c674c 100755
--- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/dusty.css
+++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/dusty.css
@@ -512,7 +512,7 @@ button::-moz-focus-inner {
margin-left:5px;
overflow:hidden;
display:block !important;
- background:url(../image/icon_tag_delete.png) center center no-repeat;
+ background:url(../image/icon_tag_remove.png) center center no-repeat;
text-indent:-99px;
}
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/layout.css b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/layout.css
index c50546b447b..ec4ac382aaa 100644
--- a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/layout.css
+++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/css/layout.css
@@ -430,7 +430,7 @@
}
/* Rating */
- #rating_annotations {
+ #rating_annotations ul {
border-color: transparent;
}
.rating {
@@ -442,7 +442,7 @@
border-bottom: solid 1px hsl(210,20%,85%) !important;
}
.lnfiles:empty {
- display: none;
+ opacity: 0.1;
}
.myRating img, .addRating img{
cursor: pointer;
@@ -980,7 +980,10 @@
-
+ /* Bulk Annotations */
+ .bulk_annotations_table td {
+ white-space: normal !important;
+ }
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/image/icon_tag_remove.png b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/image/icon_tag_remove.png
new file mode 100644
index 00000000000..a7da64a5d01
Binary files /dev/null and b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/image/icon_tag_remove.png differ
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.right_panel_comments_pane.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.right_panel_comments_pane.js
new file mode 100644
index 00000000000..fbddf834602
--- /dev/null
+++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.right_panel_comments_pane.js
@@ -0,0 +1,154 @@
+// Copyright (C) 2016 University of Dundee & Open Microscopy Environment.
+// All rights reserved.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see
Loading attachments");
+ $attach_form.load(load_url);
+ return false;
+ });
+
+
+ // Show/hide checkboxes beside files to select files for scripts
+ $(".toolbar input[type=button]", $body).click(
+ OME.toggleFileAnnotationCheckboxes
+ );
+ $("#fileanns_container").on(
+ "change", "li input[type=checkbox]",
+ OME.fileAnnotationCheckboxChanged
+ );
+
+ $("#fileanns_container").on("click", ".removeFile", function(event) {
+ var url = $(this).attr('href'),
+ parents = objects.join("|"); // E.g image-123|image-456
+ OME.removeItem(event, ".file_ann_wrapper", url, parents);
+ return false;
+ });
+
+ // delete action (files)
+ $("#fileanns_container").on("click", ".deleteFile", function(event) {
+ var url = $(this).attr('href');
+ OME.deleteItem(event, "file_ann_wrapper", url);
+ });
+
+
+ var isNotCompanionFile = function isNotCompanionFile(ann) {
+ return ann.ns !== OMERO.constants.namespaces.NSCOMPANIONFILE;
+ };
+
+ var compareParentName = function(a, b){
+ return a.parent.name.toLowerCase() > b.parent.name.toLowerCase() ? 1 : -1;
+ };
+
+
+ this.render = function render() {
+
+ if ($fileanns_container.is(":visible")) {
+
+ if ($fileanns_container.is(":empty")) {
+ $fileanns_container.html("Loading attachments...");
+ }
+
+ var request = objects.map(function(o){
+ return o.replace("-", "=");
+ });
+ request = request.join("&");
+
+ $.getJSON(WEBCLIENT.URLS.webindex + "api/annotations/?type=file&" + request, function(data){
+
+ var checkboxesAreVisible = $(
+ "#fileanns_container input[type=checkbox]:visible"
+ ).length > 0;
+
+ // manipulate data...
+ // make an object of eid: experimenter
+ var experimenters = data.experimenters.reduce(function(prev, exp){
+ prev[exp.id + ""] = exp;
+ return prev;
+ }, {});
+
+ // Populate experimenters within anns
+ var anns = data.annotations.map(function(ann){
+ ann.owner = experimenters[ann.owner.id];
+ if (ann.link && ann.link.owner) {
+ ann.link.owner = experimenters[ann.link.owner.id];
+ }
+ // AddedBy IDs for filtering
+ ann.addedBy = [ann.link.owner.id];
+ ann.description = _.escape(ann.description);
+ ann.file.size = ann.file.size.filesizeformat();
+ return ann;
+ });
+ // Don't show companion files
+ anns = anns.filter(isNotCompanionFile);
+
+
+ // If we are batch annotating multiple objects, we show a summary of each tag
+ if (objects.length > 1) {
+
+ // Map tag.id to summary for that tag
+ var summary = {};
+ anns.forEach(function(ann){
+ var annId = ann.id,
+ linkOwner = ann.link.owner.id;
+ if (summary[annId] === undefined) {
+ ann.canRemove = false;
+ ann.canRemoveCount = 0;
+ ann.links = [];
+ ann.addedBy = [];
+ summary[annId] = ann;
+ }
+ // Add link to list...
+ var l = ann.link;
+ // slice parent class 'ProjectI' > 'Project'
+ l.parent.class = l.parent.class.slice(0, -1);
+ summary[annId].links.push(l);
+
+ // ...and summarise other properties on the ann
+ if (l.permissions.canDelete) {
+ summary[annId].canRemoveCount += 1;
+ }
+ summary[annId].canRemove = summary[annId].canRemove || l.permissions.canDelete;
+ if (summary[annId].addedBy.indexOf(linkOwner) === -1) {
+ summary[annId].addedBy.push(linkOwner);
+ }
+ });
+
+ // convert summary back to list of 'anns'
+ anns = [];
+ for (var annId in summary) {
+ if (summary.hasOwnProperty(annId)) {
+ summary[annId].links.sort(compareParentName);
+ anns.push(summary[annId]);
+ }
+ }
+ }
+
+ // Update html...
+ var html = "";
+ if (anns.length > 0) {
+ html = filesTempl({'anns': anns,
+ 'webindex': WEBCLIENT.URLS.webindex,
+ 'userId': WEBCLIENT.USER.id});
+ }
+ $fileanns_container.html(html);
+
+ // Finish up...
+ OME.filterAnnotationsAddedBy();
+ if (checkboxesAreVisible) {
+ $("#fileanns_container input[type=checkbox]:not(:visible)").toggle();
+ }
+ $(".tooltip", $fileanns_container).tooltip_init();
+ });
+
+ }
+ };
+
+
+ initEvents();
+
+ if (OME.getPaneExpanded('files')) {
+ $header.toggleClass('closed');
+ $body.show();
+ }
+
+ this.render();
+};
\ No newline at end of file
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.right_panel_mapanns_pane.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.right_panel_mapanns_pane.js
new file mode 100644
index 00000000000..570ee24fe54
--- /dev/null
+++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.right_panel_mapanns_pane.js
@@ -0,0 +1,134 @@
+// Copyright (C) 2016 University of Dundee & Open Microscopy Environment.
+// All rights reserved.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see
Loading attachments");
+ $attach_form.load(load_url);
+ return false;
+ });
+
+
+ // Show/hide checkboxes beside files to select files for scripts
+ $(".toolbar input[type=button]", $body).click(
+ OME.toggleFileAnnotationCheckboxes
+ );
+ $("#fileanns_container").on(
+ "change", "li input[type=checkbox]",
+ OME.fileAnnotationCheckboxChanged
+ );
+
+ $("#fileanns_container").on("click", ".removeFile", function(event) {
+ var url = $(this).attr('href'),
+ parents = objects.join("|"); // E.g image-123|image-456
+ OME.removeItem(event, ".file_ann_wrapper", url, parents);
+ return false;
+ });
+
+ // delete action (files)
+ $("#fileanns_container").on("click", ".deleteFile", function(event) {
+ var url = $(this).attr('href');
+ OME.deleteItem(event, "file_ann_wrapper", url);
+ });
+
+
+ var isNotCompanionFile = function isNotCompanionFile(ann) {
+ return ann.ns !== OMERO.constants.namespaces.NSCOMPANIONFILE;
+ };
+
+ var compareParentName = function(a, b){
+ return a.parent.name.toLowerCase() > b.parent.name.toLowerCase() ? 1 : -1;
+ };
+
+
+ this.render = function render() {
+
+ if ($fileanns_container.is(":visible")) {
+
+ if ($fileanns_container.is(":empty")) {
+ $fileanns_container.html("Loading attachments...");
+ }
+
+ var request = objects.map(function(o){
+ return o.replace("-", "=");
+ });
+ request = request.join("&");
+
+ $.getJSON(WEBCLIENT.URLS.webindex + "api/annotations/?type=file&" + request, function(data){
+
+ var checkboxesAreVisible = $(
+ "#fileanns_container input[type=checkbox]:visible"
+ ).length > 0;
+
+ // manipulate data...
+ // make an object of eid: experimenter
+ var experimenters = data.experimenters.reduce(function(prev, exp){
+ prev[exp.id + ""] = exp;
+ return prev;
+ }, {});
+
+ // Populate experimenters within anns
+ var anns = data.annotations.map(function(ann){
+ ann.owner = experimenters[ann.owner.id];
+ if (ann.link && ann.link.owner) {
+ ann.link.owner = experimenters[ann.link.owner.id];
+ }
+ // AddedBy IDs for filtering
+ ann.addedBy = [ann.link.owner.id];
+ ann.description = _.escape(ann.description);
+ ann.file.size = ann.file.size.filesizeformat();
+ return ann;
+ });
+ // Don't show companion files
+ anns = anns.filter(isNotCompanionFile);
+
+
+ // If we are batch annotating multiple objects, we show a summary of each tag
+ if (objects.length > 1) {
+
+ // Map tag.id to summary for that tag
+ var summary = {};
+ anns.forEach(function(ann){
+ var annId = ann.id,
+ linkOwner = ann.link.owner.id;
+ if (summary[annId] === undefined) {
+ ann.canRemove = false;
+ ann.canRemoveCount = 0;
+ ann.links = [];
+ ann.addedBy = [];
+ summary[annId] = ann;
+ }
+ // Add link to list...
+ var l = ann.link;
+ // slice parent class 'ProjectI' > 'Project'
+ l.parent.class = l.parent.class.slice(0, -1);
+ summary[annId].links.push(l);
+
+ // ...and summarise other properties on the ann
+ if (l.permissions.canDelete) {
+ summary[annId].canRemoveCount += 1;
+ }
+ summary[annId].canRemove = summary[annId].canRemove || l.permissions.canDelete;
+ if (summary[annId].addedBy.indexOf(linkOwner) === -1) {
+ summary[annId].addedBy.push(linkOwner);
+ }
+ });
+
+ // convert summary back to list of 'anns'
+ anns = [];
+ for (var annId in summary) {
+ if (summary.hasOwnProperty(annId)) {
+ summary[annId].links.sort(compareParentName);
+ anns.push(summary[annId]);
+ }
+ }
+ }
+
+ // Update html...
+ var html = "";
+ if (anns.length > 0) {
+ html = filesTempl({'anns': anns,
+ 'webindex': WEBCLIENT.URLS.webindex,
+ 'userId': WEBCLIENT.USER.id});
+ }
+ $fileanns_container.html(html);
+
+ // Finish up...
+ OME.filterAnnotationsAddedBy();
+ if (checkboxesAreVisible) {
+ $("#fileanns_container input[type=checkbox]:not(:visible)").toggle();
+ }
+ $(".tooltip", $fileanns_container).tooltip_init();
+ });
+
+ }
+ };
+
+
+ initEvents();
+
+ if (OME.getPaneExpanded('files')) {
+ $header.toggleClass('closed');
+ $body.show();
+ }
+
+ this.render();
+};
\ No newline at end of file
diff --git a/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.right_panel_mapanns_pane.js b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.right_panel_mapanns_pane.js
new file mode 100644
index 00000000000..570ee24fe54
--- /dev/null
+++ b/components/tools/OmeroWeb/omeroweb/webclient/static/webclient/javascript/ome.right_panel_mapanns_pane.js
@@ -0,0 +1,134 @@
+// Copyright (C) 2016 University of Dundee & Open Microscopy Environment.
+// All rights reserved.
+
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as
+// published by the Free Software Foundation, either version 3 of the
+// License, or (at your option) any later version.
+
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+
+// You should have received a copy of the GNU Affero General Public License
+// along with this program. If not, see