From e80cf799de3dee1885d454eb01f64f8479677eb0 Mon Sep 17 00:00:00 2001 From: Bilel Kihal <61744974+Bilelkihal@users.noreply.github.com> Date: Thu, 6 Jun 2024 22:29:19 +0200 Subject: [PATCH] Feature: add Annotator UI tests (#627) * setup annotator page UI tests * check if all the inputs and filters are present in annotator page test * add annotator http requests to test fixtures * test annotator results and count them * test annotator empty illustration * add comments in annotator page tests * use dynamic api in recommender tests * test that we have the exact correct annotations in the annotator test * undo adding ids for elements to run annotator tests * add a default ANNOTATOR_URL value for test config --------- Co-authored-by: Syphax bouazzouni --- app/controllers/annotator_controller.rb | 2 +- app/views/annotator/index.html.haml | 1 + config/bioportal_config_test.rb | 7 +- docker-compose.yml | 7 +- test/fixtures/annotator.yml | 181 ++++++++++++++++++++++++ test/system/annotator_page_test.rb | 108 ++++++++++++++ test/test_helper.rb | 2 + 7 files changed, 299 insertions(+), 9 deletions(-) create mode 100644 test/fixtures/annotator.yml create mode 100644 test/system/annotator_page_test.rb diff --git a/app/controllers/annotator_controller.rb b/app/controllers/annotator_controller.rb index d0cd478007..1cab357290 100644 --- a/app/controllers/annotator_controller.rb +++ b/app/controllers/annotator_controller.rb @@ -72,7 +72,7 @@ def annotator_results(uri) else @results_table_header.push(t('annotator.score')) end - annotations = LinkedData::Client::HTTP.get(uri, api_params) + annotations = LinkedData::Client::HTTP.get(uri.dup.to_s, api_params) @ontologies = get_simplified_ontologies_hash @semantic_types = get_semantic_types @results = [] diff --git a/app/views/annotator/index.html.haml b/app/views/annotator/index.html.haml index 4d5636ed6c..8b83bab08c 100644 --- a/app/views/annotator/index.html.haml +++ b/app/views/annotator/index.html.haml @@ -37,6 +37,7 @@ = render Input::SelectComponent.new(label: t('annotator.include_ancestors'), id: 'ancestors_level', name: 'class_hierarchy_max_level', value: @ancestors_levels, selected: params[:class_hierarchy_max_level]) .filters_line - include_score_helper = 'Score annotations following previous NCBO 2009 measure (old) or Score annotations following C-Value measure (cvalue) or Score annotations following C-Value measure with hierarchy expansion (cvalueh).' + = render Input::SelectComponent.new(label: t('annotator.include_score'), id: 'include_score', name: 'score', value: @include_score, tooltip: include_score_helper, selected: params[:score]) = render Input::NumberComponent.new(label: t('annotator.score_threshold'), name: "score_threshold", value: params[:score_threshold] || 0, tooltip: 'Specify minimum score value for annotations.') = render Input::NumberComponent.new(label: t('annotator.confidence_threshold'), name: 'confidence_threshold', value: params[:confidence_threshold] || 0, tooltip: 'Specify the minimum position in the score distribution (between 1 and 100).') diff --git a/config/bioportal_config_test.rb b/config/bioportal_config_test.rb index 44169096be..6fb4312b12 100644 --- a/config/bioportal_config_test.rb +++ b/config/bioportal_config_test.rb @@ -8,7 +8,7 @@ $API_KEY = ENV['API_KEY'] $REST_URL = ENV['API_URL'] $BIOMIXER_URL = ENV['BIOMIXER_URL'] -$ANNOTATOR_URL = $PROXY_URL = ENV['ANNOTATOR_URL'] +$ANNOTATOR_URL = $PROXY_URL = ENV['ANNOTATOR_URL'].blank? ? "https://services.tesportal.lirmm.fr/annotator" : ENV['ANNOTATOR_URL'] $FAIRNESS_URL = ENV['FAIRNESS_URL'] # Resource term @@ -218,7 +218,4 @@ } ] -$UI_THEME = :stageportal -if File.exist?('config/bioportal_config_development_testportal.lirmm.fr.rb') - require_relative 'bioportal_config_development_testportal.lirmm.fr' # local credentials -end \ No newline at end of file +$UI_THEME = :stageportal \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 644e1ca6ed..575407eb2e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -103,9 +103,10 @@ services: chrome-server: image: selenium/standalone-chrome:112.0-chromedriver-112.0-grid-4.9.0-20230421 shm_size: 2g - ports: - - "4444:4444" - - "7900:7900" + network_mode: 'host' +# ports: +# - "4444:4444" +# - "7900:7900" volumes: mysql-data: diff --git a/test/fixtures/annotator.yml b/test/fixtures/annotator.yml new file mode 100644 index 0000000000..d678eb033a --- /dev/null +++ b/test/fixtures/annotator.yml @@ -0,0 +1,181 @@ +yaml_structure: regular +sample_response: + - annotatedClass: + prefLabel: Melanom + synonym: + - cilt kanseri + "@id": http://aims.fao.org/aos/agrovoc/c_4713 + "@type": http://www.w3.org/2002/07/owl#Class + links: + self: https://data.stageportal.lirmm.fr/ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713 + ontology: http://localhost:9393/ontologies/STY + children: https://data.stageportal.lirmm.fr/ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713/children + parents: https://data.stageportal.lirmm.fr/ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713/parents + descendants: https://data.stageportal.lirmm.fr/ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713/descendants + ancestors: https://data.stageportal.lirmm.fr/ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713/ancestors + instances: https://data.stageportal.lirmm.fr/ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713/instances + tree: https://data.stageportal.lirmm.fr/ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713/tree + notes: https://data.stageportal.lirmm.fr/ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713/notes + mappings: https://data.stageportal.lirmm.fr/ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713/mappings + ui: http://stageportal.lirmm.fr/ontologies/AGROVOC?p=classes&conceptid=http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713 + "@context": + self: http://www.w3.org/2002/07/owl#Class + ontology: http://localhost:9393/ontologies/STY + children: http://www.w3.org/2002/07/owl#Class + parents: http://www.w3.org/2002/07/owl#Class + descendants: http://www.w3.org/2002/07/owl#Class + ancestors: http://www.w3.org/2002/07/owl#Class + instances: http://data.bioontology.org/metadata/Instance + tree: http://www.w3.org/2002/07/owl#Class + notes: http://data.bioontology.org/metadata/Note + mappings: http://data.bioontology.org/metadata/Mapping + ui: http://www.w3.org/2002/07/owl#Class + "@context": + "@vocab": http://data.bioontology.org/metadata/ + prefLabel: http://www.w3.org/2004/02/skos/core#prefLabel + synonym: http://www.w3.org/2004/02/skos/core#altLabel + "@language": en + hierarchy: [] + annotations: + - from: 1 + to: 8 + matchType: PREF + text: MELANOMA + mappings: [] + - annotatedClass: + prefLabel: Melanom + synonym: + - malignant melanoma + - melanoma patient + - melanocytes + "@id": http://data.europa.eu/8mn/euroscivoc/276b8c99-a318-48df-aa31-1f9f3e0ba910 + "@type": http://www.w3.org/2002/07/owl#Class + links: + self: https://data.stageportal.lirmm.fr/ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910 + ontology: http://localhost:9393/ontologies/STY + children: https://data.stageportal.lirmm.fr/ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910/children + parents: https://data.stageportal.lirmm.fr/ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910/parents + descendants: https://data.stageportal.lirmm.fr/ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910/descendants + ancestors: https://data.stageportal.lirmm.fr/ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910/ancestors + instances: https://data.stageportal.lirmm.fr/ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910/instances + tree: https://data.stageportal.lirmm.fr/ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910/tree + notes: https://data.stageportal.lirmm.fr/ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910/notes + mappings: https://data.stageportal.lirmm.fr/ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910/mappings + ui: http://stageportal.lirmm.fr/ontologies/EUROSCIVOC?p=classes&conceptid=http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910 + "@context": + self: http://www.w3.org/2002/07/owl#Class + ontology: http://localhost:9393/ontologies/STY + children: http://www.w3.org/2002/07/owl#Class + parents: http://www.w3.org/2002/07/owl#Class + descendants: http://www.w3.org/2002/07/owl#Class + ancestors: http://www.w3.org/2002/07/owl#Class + instances: http://data.bioontology.org/metadata/Instance + tree: http://www.w3.org/2002/07/owl#Class + notes: http://data.bioontology.org/metadata/Note + mappings: http://data.bioontology.org/metadata/Mapping + ui: http://www.w3.org/2002/07/owl#Class + "@context": + "@vocab": http://data.bioontology.org/metadata/ + prefLabel: http://www.w3.org/2004/02/skos/core#prefLabel + synonym: http://www.w3.org/2004/02/skos/core#altLabel + "@language": en + hierarchy: [] + annotations: + - from: 1 + to: 8 + matchType: PREF + text: MELANOMA + - from: 34 + to: 44 + matchType: SYN + text: MELANOCYTES + mappings: [] + - annotatedClass: + definition: + - http://opendata.inrae.fr/thesaurusINRAE/note_f18c0fe1 + prefLabel: mélanome + synonym: + - malignant melanoma + - mélanome malin + "@id": http://opendata.inrae.fr/thesaurusINRAE/c_11970 + "@type": http://www.w3.org/2002/07/owl#Class + links: + self: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970 + ontology: http://localhost:9393/ontologies/STY + children: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970/children + parents: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970/parents + descendants: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970/descendants + ancestors: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970/ancestors + instances: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970/instances + tree: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970/tree + notes: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970/notes + mappings: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970/mappings + ui: http://stageportal.lirmm.fr/ontologies/INRAETHES?p=classes&conceptid=http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970 + "@context": + self: http://www.w3.org/2002/07/owl#Class + ontology: http://localhost:9393/ontologies/STY + children: http://www.w3.org/2002/07/owl#Class + parents: http://www.w3.org/2002/07/owl#Class + descendants: http://www.w3.org/2002/07/owl#Class + ancestors: http://www.w3.org/2002/07/owl#Class + instances: http://data.bioontology.org/metadata/Instance + tree: http://www.w3.org/2002/07/owl#Class + notes: http://data.bioontology.org/metadata/Note + mappings: http://data.bioontology.org/metadata/Mapping + ui: http://www.w3.org/2002/07/owl#Class + "@context": + "@vocab": http://data.bioontology.org/metadata/ + prefLabel: http://www.w3.org/2004/02/skos/core#prefLabel + synonym: http://www.w3.org/2004/02/skos/core#altLabel + definition: http://www.w3.org/2004/02/skos/core#definition + "@language": en + hierarchy: [] + annotations: + - from: 1 + to: 8 + matchType: PREF + text: MELANOMA + mappings: [] + - annotatedClass: + prefLabel: tumeur + synonym: + - néoplasme + - néoplasie + "@id": http://opendata.inrae.fr/thesaurusINRAE/c_11887 + "@type": http://www.w3.org/2002/07/owl#Class + links: + self: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887 + ontology: http://localhost:9393/ontologies/STY + children: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887/children + parents: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887/parents + descendants: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887/descendants + ancestors: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887/ancestors + instances: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887/instances + tree: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887/tree + notes: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887/notes + mappings: https://data.stageportal.lirmm.fr/ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887/mappings + ui: http://stageportal.lirmm.fr/ontologies/INRAETHES?p=classes&conceptid=http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887 + "@context": + self: http://www.w3.org/2002/07/owl#Class + ontology: http://localhost:9393/ontologies/STY + children: http://www.w3.org/2002/07/owl#Class + parents: http://www.w3.org/2002/07/owl#Class + descendants: http://www.w3.org/2002/07/owl#Class + ancestors: http://www.w3.org/2002/07/owl#Class + instances: http://data.bioontology.org/metadata/Instance + tree: http://www.w3.org/2002/07/owl#Class + notes: http://data.bioontology.org/metadata/Note + mappings: http://data.bioontology.org/metadata/Mapping + ui: http://www.w3.org/2002/07/owl#Class + "@context": + "@vocab": http://data.bioontology.org/metadata/ + prefLabel: http://www.w3.org/2004/02/skos/core#prefLabel + synonym: http://www.w3.org/2004/02/skos/core#altLabel + "@language": en + hierarchy: [] + annotations: + - from: 25 + to: 29 + matchType: PREF + text: TUMOR + mappings: [] diff --git a/test/system/annotator_page_test.rb b/test/system/annotator_page_test.rb new file mode 100644 index 0000000000..5956a75f36 --- /dev/null +++ b/test/system/annotator_page_test.rb @@ -0,0 +1,108 @@ +require "application_system_test_case" +require 'webmock/minitest' + +class AnnotatorPageTest < ApplicationSystemTestCase + def setup + WebMock.disable! + @apikey = LinkedData::Client.settings.apikey + @annotator_api = $ANNOTATOR_URL + @host = "#{@annotator_api.split('/')[-2]}:443" + @annotator_text_area = ".annotator-page-text-area > textarea" + @sample_response = fixtures(:annotator)["sample_response"] + end + + def teardown + WebMock.disable! + end + + test "go to annotator page and check if all the inputs and filters are there" do + visit root_url + click_link(href: '/annotator') + assert_selector @annotator_text_area + assert_selector 'div.insert-sample-text-button' + # Check if there are the annotator's options + assert_selector 'label[for="chips-whole_word_only-check"]' + assert_selector 'label[for="chips-longest_only-check"]' + assert_selector 'label[for="chips-expand_mappings-check"]' + assert_selector 'label[for="chips-exclude_numbers-check"]' + assert_selector 'label[for="chips-exclude_synonyms-check"]' + assert_selector 'div.select-ontologies' + + # Open the advanced options + find('div.advanced-options-button').click + + # Check if there are the advanced options + assert_selector 'input#select_umls_semantic_types-ts-control', visible: :all + assert_selector 'input#select_umls_semantic_groups-ts-control', visible: :all + assert_selector 'input#select_ancestors_level-ts-control', visible: :all + assert_selector 'input#select_include_score-ts-control', visible: :all + assert_selector 'input[name="score_threshold"]' + assert_selector 'input[name="confidence_threshold"]' + assert_selector 'label[for="chips-fast_context-check"]' + assert_selector 'label[for="chips-lemmatize-check"]' + end + + test "go to annotator page insert sample text and get annotations" do + visit root_url + click_link(href: '/annotator') + + # Fill the annotator's text area input by a sample text + find(@annotator_text_area).fill_in(with: 'Melanoma is a malignant tumor of melanocytes found mainly') + + # Mock the api call for the annotator with the entered text + WebMock.enable! + stub_request(:get, "#{@annotator_api}?class_hierarchy_max_level=None&confidence_threshold=0&score_threshold=0&text=Melanoma%20is%20a%20malignant%20tumor%20of%20melanocytes%20found%20mainly&whole_word_only=true") + .with( + headers: { + 'Accept'=>'application/json', + 'Authorization'=>"apikey token=#{@apikey}", + 'Host'=> @host, + 'User-Agent'=>'NCBO API Ruby Client v0.1.0' + }) + .to_return(status: 200, body: @sample_response.to_json, headers: {}) + + + find(".annotator-page-button #annotator").click + + # Check if we get the table of annotations + assert_selector 'table#annotator-table' + + # Check if the number of annotations is 4 + assert_equal 5, page.all('tr').count + + # Check if we got the correct annotations + assert_selector 'a[href="ontologies/AGROVOC/classes/http%3A%2F%2Faims.fao.org%2Faos%2Fagrovoc%2Fc_4713"]', text: 'Melanom' + assert_selector 'a[href="ontologies/EUROSCIVOC/classes/http%3A%2F%2Fdata.europa.eu%2F8mn%2Feuroscivoc%2F276b8c99-a318-48df-aa31-1f9f3e0ba910"]', text: 'Melanom' + assert_selector 'a[href="ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11970"]', text: 'mélanome' + assert_selector 'a[href="ontologies/INRAETHES/classes/http%3A%2F%2Fopendata.inrae.fr%2FthesaurusINRAE%2Fc_11887"]', text: 'tumeur' + + # Check if the action buttons below the table are there (json, rdf, cite us and api doc buttons) + assert_selector '#annotator_json' + assert_selector '#annotator_rdf' + assert_selector '#annotator_cite_us' + assert_selector '#annotator_api_doc' + + # Clear the sample text in the annotator text area + find(@annotator_text_area).native.clear + + # Fill it with a text that will return an empty state + find(@annotator_text_area).fill_in(with: 'mainly') + + # Mock the api call of the annotator to get and return an empty result + stub_request(:get, "#{@annotator_api}?class_hierarchy_max_level=None&confidence_threshold=0&score_threshold=0&text=mainly&whole_word_only=true") + .with( + headers: { + 'Accept'=>'application/json', + 'Authorization'=>"apikey token=#{@apikey}", + 'Host'=> @host, + 'User-Agent'=>'NCBO API Ruby Client v0.1.0' + }) + .to_return(status: 200, body: ([]).to_json, headers: {}) + + find(".annotator-page-button #annotator").click + + # Check if we got the empty state correctly + assert_selector 'div.browse-empty-illustration' + end + +end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index e8f7cf84af..af79730627 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -2,6 +2,7 @@ require_relative '../config/environment' require 'rails/test_help' require 'simplecov' +require 'webmock/minitest' SimpleCov.start 'rails' do add_filter '/bin/' @@ -16,6 +17,7 @@ class ActiveSupport::TestCase # Add more helper methods to be used by all tests here... + WebMock.allow_net_connect! Capybara.server_host = "0.0.0.0" Capybara.app_host = "http://#{Socket.gethostname}:#{Capybara.server_port}"