Tobias Gritschacher has submitted this change and it was merged. Change subject: Module for interacting with Wikibase-API in browser tests ......................................................................
Module for interacting with Wikibase-API in browser tests Change-Id: Ibb10814ce9e8a4306e162e113423baf25459e657 --- M selenium_cuc/features/statement.feature M selenium_cuc/features/step_definitions/entity_steps.rb M selenium_cuc/features/support/env.rb A selenium_cuc/features/support/modules/wikibase_api_module.rb M selenium_cuc/features/support/pages/item_page.rb M selenium_cuc/features/support/utils/utils.rb 6 files changed, 208 insertions(+), 101 deletions(-) Approvals: Tobias Gritschacher: Verified; Looks good to me, approved diff --git a/selenium_cuc/features/statement.feature b/selenium_cuc/features/statement.feature index e9210a6..9dbf908 100644 --- a/selenium_cuc/features/statement.feature +++ b/selenium_cuc/features/statement.feature @@ -46,7 +46,7 @@ | click the statement cancel button | | press the ESC key in the entity selector input field | - @ui_only + @ui_only @repo_login Scenario: Select a property Given There are properties with the following handles and datatypes: | stringprop | string | @@ -58,7 +58,7 @@ And Entity selector input element should be there And Statement value input element should be there - @ui_only + @ui_only @repo_login Scenario: Select a property and enter a statement value Given There are properties with the following handles and datatypes: | stringprop | string | @@ -71,7 +71,7 @@ And Entity selector input element should be there And Statement value input element should be there - @ui_only + @ui_only @repo_login Scenario Outline: Cancel statement after selecting a property Given There are properties with the following handles and datatypes: | stringprop | string | @@ -91,7 +91,7 @@ | press the ESC key in the entity selector input field | | press the ESC key in the statement value input field | - @ui_only + @ui_only @repo_login Scenario: Select a property, enter a statement value and clear the property Given There are properties with the following handles and datatypes: | stringprop | string | diff --git a/selenium_cuc/features/step_definitions/entity_steps.rb b/selenium_cuc/features/step_definitions/entity_steps.rb index 48d1e91..0827acd 100644 --- a/selenium_cuc/features/step_definitions/entity_steps.rb +++ b/selenium_cuc/features/step_definitions/entity_steps.rb @@ -8,17 +8,20 @@ Given /^I am on an item page$/ do item_data = '{"labels":{"en":{"language":"en","value":"' + generate_random_string(8) + '"}},"descriptions":{"en":{"language":"en","value":"' + generate_random_string(20) + '"}}}' - item = create_new_entity(item_data, "item") - @item_under_test = item - on(ItemPage).navigate_to_entity item["url"] + wb_api = WikibaseAPI::Gateway.new(URL.repo_api) + @item_under_test = wb_api.wb_create_entity(item_data, "item") + on(ItemPage).navigate_to_entity @item_under_test["url"] end Given /^There are properties with the following handles and datatypes:$/ do |props| - @properties = create_new_properties(props.raw) + wb_api = WikibaseAPI::Gateway.new(URL.repo_api) + wb_api.login(ENV["WB_REPO_USERNAME"], ENV["WB_REPO_PASSWORD"]) + @properties = wb_api.wb_create_properties(props.raw) end Given /^There are items with the following handles:$/ do |handles| - @items = create_new_items(handles.raw) + wb_api = WikibaseAPI::Gateway.new(URL.repo_api) + @items = wb_api.wb_create_items(handles.raw) end Given /^The copyright warning has been dismissed$/ do @@ -31,14 +34,15 @@ Given /^I am on an item page with empty label and description$/ do item_data = '{"labels":{"en":{"language":"en","value":"' + '' + '"}},"descriptions":{"en":{"language":"en","value":"' + '' + '"}}}' - item = create_new_entity(item_data, "item") - @item_under_test = item - on(ItemPage).navigate_to_entity item["url"] + wb_api = WikibaseAPI::Gateway.new(URL.repo_api) + @item_under_test = wb_api.wb_create_entity(item_data, "item") + on(ItemPage).navigate_to_entity @item_under_test["url"] end Given /^The following sitelinks do not exist:$/ do |sitelinks| + wb_api = WikibaseAPI::Gateway.new(URL.repo_api) sitelinks.raw.each do |sitelink| - remove_sitelink(sitelink[0], sitelink[1]).should be_true + wb_api.wb_remove_sitelink(sitelink[0], sitelink[1]).should be_true end end diff --git a/selenium_cuc/features/support/env.rb b/selenium_cuc/features/support/env.rb index 14d2bb6..cc9ee8b 100644 --- a/selenium_cuc/features/support/env.rb +++ b/selenium_cuc/features/support/env.rb @@ -46,3 +46,8 @@ browser end + +Before("@repo_login") do + abort("WB_REPO_USERNAME environment variable is not defined! Please export a value for that variable before proceeding.") unless ENV["WB_REPO_USERNAME"] + abort("WB_REPO_PASSWORD environment variable is not defined! Please export a value for that variable before proceeding.") unless ENV["WB_REPO_PASSWORD"] +end diff --git a/selenium_cuc/features/support/modules/wikibase_api_module.rb b/selenium_cuc/features/support/modules/wikibase_api_module.rb new file mode 100644 index 0000000..84a5357 --- /dev/null +++ b/selenium_cuc/features/support/modules/wikibase_api_module.rb @@ -0,0 +1,186 @@ +# Author:: Tobias Gritschacher (tobias.gritschac...@wikimedia.de) +# License:: GNU GPL v2+ +# +# Parts reused and modified from http://rubygems.org/gems/mediawiki-gateway +# +# module for interacting with WikibaseAPI + +require "rest_client" +require "uri" +require "active_support" + +module WikibaseAPI + + class Gateway + # Set up a WikibaseAPI::Gateway + # + # [url] Path to API of target Wikibase Installation + def initialize(url) + @wiki_url = url + @headers = { "User-Agent" => "WikibaseAPI::Gateway", "Accept-Encoding" => "gzip" } + @cookies = {} + end + + # Login + # + # [username] Username + # [password] Password + # + # Throws WikibaseAPI::Unauthorized if login fails + def login(username, password, domain = "local") + form_data = {"action" => "login", "lgname" => username, "lgpassword" => password, "lgdomain" => domain} + make_api_request(form_data) + end + + # creates a new entity via the API + def wb_create_entity(data, type = "item") + form_data = {"action" => "wbeditentity", "new" => type, "data" => data, + "summary" => "entity created by selenium test", "token" => get_token("edit")} + resp = make_api_request(form_data) + check_wb_api_success(resp) + + id = resp["entity"]["id"] + url = URL.repo_url(ITEM_NAMESPACE + id + "?setlang=" + LANGUAGE_CODE) + entity_data = ActiveSupport::JSON.decode(data) + + {"id" => id, "url" => url, "label" => entity_data["labels"]["en"]["value"], + "description" => entity_data["descriptions"]["en"]["value"]} + end + + # creates new properties by calling wb_create_entity multiple times + def wb_create_properties(props) + properties = Hash.new + + props.each do |prop| + handle = prop[0] + type = prop[1] + data = '{"labels":{"en":{"language":"en","value":"' + generate_random_string(8) + + '"}},"descriptions":{"en":{"language":"en","value":"' + generate_random_string(20) + + '"}},"datatype":"' + type + '"}' + property = wb_create_entity(data, "property") + properties[handle] = property + end + + properties + end + + # creates items by calling wb_create_entity multiple times + def wb_create_items(handles) + items = Hash.new + + handles.each do |handle| + data = '{"labels":{"en":{"language":"en","value":"' + generate_random_string(8) + + '"}},"descriptions":{"en":{"language":"en","value":"' + generate_random_string(20) + '"}}}' + item = wb_create_entity(data, "item") + items[handle] = item + end + + items + end + + # removes a sitelink + def wb_set_sitelink(entity_identifier, linksite, linktitle) + form_data = entity_identifier.merge({"action" => "wbsetsitelink", "linksite" => linksite, "linktitle" => linktitle, + "summary" => "Sitelink set by Selenium test API", "token" => get_token("edit")}) + + make_api_request(form_data) + end + + def wb_remove_sitelink(siteid, pagename) + entity_identifier = {"site" => siteid, "title" => pagename} + resp = wb_set_sitelink(entity_identifier, siteid, "") + + if resp["success"] != 1 && resp["error"]["code"] != "no-such-entity-link" + msg = "Failed to remove sitelink " + siteid + ": API error" + raise APIError.new("error", msg) + end + + return true + end + + private + + # Fetch token (type "delete", "edit", "email", "import", "move", "protect") + def get_token(type) + form_data = {"action" => "tokens", "type" => type} + res = make_api_request(form_data) + token = res["tokens"][type + "token"] + raise Unauthorized.new "User is not permitted to perform this operation: #{type}" if token.nil? + token + end + + # Make generic request to API + # + # [form_data] hash or string of attributes to post + # + # Returns JSON document + def make_api_request(form_data) + form_data.merge!("format" => "json") + http_send(@wiki_url, form_data, @headers.merge({:cookies => @cookies})) do |response, &block| + # Check response for errors and return JSON + raise MediaWiki::Exception.new "Bad response: #{response}" unless response.code >= 200 and response.code < 300 + json_resp = get_response(response.dup) + if form_data["action"] == "login" + login_result = json_resp["login"]["result"] + @cookies.merge!(response.cookies) + case login_result + when "Success" then # do nothing + when "NeedToken" then make_api_request(form_data.merge("lgtoken" => json_resp["login"]["token"])) + else raise Unauthorized.new "Login failed: " + login_result + end + end + return json_resp + end + end + + # Execute the HTTP request using either GET or POST as appropriate + def http_send(url, form_data, headers, &block) + if form_data["action"] == "query" + headers[:params] = form_data + RestClient.get url, headers, &block + else + RestClient.post url, form_data, headers, &block + end + end + + # Get JSON response + def get_response(response) + ActiveSupport::JSON.decode(response) + end + + def warning(msg) + raise APIError.new("warning", msg) + end + + def check_wb_api_success(response) + if response["success"] != 1 + msg = "API error: " + response["error"]["info"] + raise APIError.new("error", msg) + end + end + + end + + # General exception occurred within WikibaseAPI::Gateway, and parent class for WikibaseAPI::APIError, WikibaseAPI::Unauthorized. + class WBException < Exception + end + + # Wrapper for errors returned by MediaWiki API. Possible codes are defined in http://www.mediawiki.org/wiki/API:Errors_and_warnings. + # Warnings also throw errors with code "warning" + class APIError < WikibaseAPI::WBException + + def initialize(code, info) + @code = code + @info = info + @message = "API error: code '#{code}', info '#{info}'" + end + + def to_s + "#{self.class.to_s}: #{@message}" + end + end + + # User is not authorized to perform this operation. Also thrown if login fails. + class Unauthorized < WikibaseAPI::WBException + end +end diff --git a/selenium_cuc/features/support/pages/item_page.rb b/selenium_cuc/features/support/pages/item_page.rb index f5a4b66..01f1da6 100644 --- a/selenium_cuc/features/support/pages/item_page.rb +++ b/selenium_cuc/features/support/pages/item_page.rb @@ -6,8 +6,6 @@ # # page object for item page -#require "ruby_selenium" - class ItemPage include PageObject include EntityPage diff --git a/selenium_cuc/features/support/utils/utils.rb b/selenium_cuc/features/support/utils/utils.rb index 433d295..0462a50 100644 --- a/selenium_cuc/features/support/utils/utils.rb +++ b/selenium_cuc/features/support/utils/utils.rb @@ -15,89 +15,3 @@ length.times { string << chars[rand(chars.size)] } return string end - -def create_new_properties(props) - properties = Hash.new - - props.each do |prop| - handle = prop[0] - type = prop[1] - data = '{"labels":{"en":{"language":"en","value":"' + generate_random_string(8) + - '"}},"descriptions":{"en":{"language":"en","value":"' + generate_random_string(20) + - '"}},"datatype":"' + type + '"}' - property = create_new_entity(data, "property") - properties[handle] = property - end - - properties -end - -def create_new_items(handles) - items = Hash.new - - handles.each do |handle| - data = '{"labels":{"en":{"language":"en","value":"' + generate_random_string(8) + - '"}},"descriptions":{"en":{"language":"en","value":"' + generate_random_string(20) + '"}}}' - item = create_new_entity(data, "item") - items[handle] = item - end - - items -end - -# creates a new entity via the API -def create_new_entity(data, type = "item") - uri = URI(URL.repo_api) - - request = Net::HTTP::Post.new(uri.to_s) - request.set_form_data( - "action" => "wbeditentity", - "token" => "+\\", - "new" => type, - "data" => data, - "format" => "json", - "summary" => "entity created by selenium test" - ) - - response = Net::HTTP.start(uri.hostname, uri.port) do |http| - http.request(request) - end - resp = ActiveSupport::JSON.decode(response.body) - - if resp["success"] != 1 - abort("Failed to create new entity: API error: " + resp["error"]["info"]) - end - - id = resp["entity"]["id"] - url = URL.repo_url(ITEM_NAMESPACE + id + "?setlang=" + LANGUAGE_CODE) - entity_data = ActiveSupport::JSON.decode(data) - entity = {"id" => id, "url" => url, "label" => entity_data["labels"]["en"]["value"], "description" => entity_data["descriptions"]["en"]["value"]} - return entity -end - -# removes a sitelink -def remove_sitelink(siteid, pagename) - uri = URI(URL.repo_api) - - request = Net::HTTP::Post.new(uri.to_s) - request.set_form_data( - "action" => "wbsetsitelink", - "token" => "+\\", - "site" => siteid, - "title" => pagename, - "linksite" => siteid, - "format" => "json", - "summary" => "sitelink removed by selenium test" - ) - - response = Net::HTTP.start(uri.hostname, uri.port) do |http| - http.request(request) - end - resp = ActiveSupport::JSON.decode(response.body) - - if resp["success"] != 1 && resp["error"]["code"] != "no-such-entity-link" - abort("Failed to remove sitelink " + siteid + ": API error") - end - - return true -end -- To view, visit https://gerrit.wikimedia.org/r/98163 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: merged Gerrit-Change-Id: Ibb10814ce9e8a4306e162e113423baf25459e657 Gerrit-PatchSet: 7 Gerrit-Project: mediawiki/extensions/Wikibase Gerrit-Branch: master Gerrit-Owner: Tobias Gritschacher <tobias.gritschac...@wikimedia.de> Gerrit-Reviewer: Cmcmahon <cmcma...@wikimedia.org> Gerrit-Reviewer: Jhall <jh...@wikimedia.org> Gerrit-Reviewer: Tobias Gritschacher <tobias.gritschac...@wikimedia.de> Gerrit-Reviewer: Zfilipin <zfili...@wikimedia.org> Gerrit-Reviewer: jenkins-bot _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits