#-- encoding: UTF-8 #-- copyright # OpenProject is an open source project management software. # Copyright (C) 2012-2020 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. # # OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: # Copyright (C) 2006-2017 Jean-Philippe Lang # Copyright (C) 2010-2013 the ChiliProject Team # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # See docs/COPYRIGHT.rdoc for more details. #++ require 'active_record/fixtures' require 'rack_session_access/capybara' Before do |scenario| unless ScenarioDisabler.empty_if_disabled(scenario) FactoryBot.create(:admin) unless User.find_by_login('admin') FactoryBot.create(:anonymous) unless AnonymousUser.count > 0 Setting.notified_events = [] # can not test mailer end end Given /^I am logged in$/ do @user = FactoryBot.create :user page.set_rack_session(user_id: @user.id, updated_at: Time.now) end When(/^I log out in the background$/) do page.execute_script("jQuery.ajax('/logout', { success: function () { jQuery(document.body).addClass('logout-ajax') } })") page.should have_selector('body.logout-ajax') end Given /^(?:|I )am not logged in$/ do User.current = AnonymousUser.first end Given /^(?:|I )am [aA]dmin$/ do admin = User.find_by(admin: true) login(admin.login, 'adminADMIN!') end Given /^(?:|I )am already [aA]dmin$/ do admin = User.find_by(admin: true) # see https://github.com/railsware/rack_session_access page.set_rack_session(user_id: admin.id, updated_at: Time.now) end Given /^I am already logged in as "(.+?)"$/ do |login| user = User.find_by_login(login) # see https://github.com/railsware/rack_session_access page.set_rack_session(user_id: user.id, updated_at: Time.now) end Given /^(?:|I )am logged in as "([^\"]*)"$/ do |username| login(username, 'adminADMIN!') end Given /^there is 1 [pP]roject with(?: the following)?:$/ do |table| p = FactoryBot.build(:project) send_table_to_object(p, table) end Then /^the project "([^"]*)" is( not)? public$/ do |project_name, negation| p = Project.find_by(name: project_name) p.update_attribute(:is_public, !negation) end Given /^the plugin (.+) is loaded$/ do |plugin_name| plugin_name = plugin_name.gsub("\"", '') Redmine::Plugin.all.detect { |x| x.id == plugin_name.to_sym }.present? ? nil : pending("Plugin #{plugin_name} not loaded") end Given /^(?:the )?[pP]roject "([^\"]*)" uses the following [mM]odules:$/ do |project, table| p = Project.find_by(name: project) p.enabled_module_names += table.raw.map(&:first) p.reload end Given /^(?:the )?[pP]roject "([^\"]*)" does not use the following [mM]odules:$/ do |project, table| p = Project.find_by(name: project) p.enabled_module_names -= table.raw.map(&:first) p.reload end Given /^the [Uu]ser "([^\"]*)" has 1 time [eE]ntry$/ do |user| u = User.find_by login: user p = u.projects.last raise 'This user must be member of a project to have issues' unless p i = FactoryBot.create(:work_package, project: p) t = FactoryBot.build(:time_entry) t.user = u t.issue = i t.project = p t.activity.project = p t.activity.save! t.save! end Given /^the [Uu]ser "([^\"]*)" has 1 time entry with (\d+\.?\d*) hours? at the project "([^\"]*)"$/ do |user, hours, project| p = Project.find_by(name: project) || Project.find_by(identifier: project) as_admin do t = FactoryBot.build(:time_entry) i = FactoryBot.create(:work_package, project: p) t.project = p t.issue = i t.hours = hours.to_f t.user = User.find_by login: user t.activity.project = p t.activity.save! t.save! end end Given /^the [Pp]roject "([^\"]*)" has (\d+) [tT]ime(?: )?[eE]ntr(?:ies|y) with the following:$/ do |project, count, table| p = Project.find_by(name: project) || Project.find_by(identifier: project) as_admin count do t = FactoryBot.build(:time_entry) i = FactoryBot.create(:work_package, project: p) t.project = p t.work_package = i t.activity.project = p t.activity.save! send_table_to_object(t, table, user: Proc.new do |o, v| o.user = User.find_by_login(v) o.save! end, spent_on: Proc.new do |object, value| # This works for definitions like "2 years ago" number, time_unit, tempus = value.split time = number.to_i.send(time_unit.to_sym).send(tempus.to_sym) object.spent_on = time object.save! end ) end end Given /^the [pP]roject "([^\"]*)" has 1 [sS]ubproject$/ do |project| parent = Project.find_by(name: project) p = FactoryBot.create(:project) p.set_parent!(parent) p.save! end Given /^the [pP]roject "([^\"]*)" has 1 [sS]ubproject with the following:$/ do |project, table| parent = Project.find_by(name: project) p = FactoryBot.build(:project) as_admin do send_table_to_object(p, table) end p.set_parent!(parent) p.save! end Given /^there are the following types:$/ do |table| table = table.map_headers { |header| header.underscore.gsub(' ', '_') } table.hashes.each_with_index do |t, i| type = ::Type.find_by(name: t['name']) type = ::Type.new name: t['name'] if type.nil? type.position = t['position'] ? t['position'] : i type.is_in_roadmap = t['is_in_roadmap'] ? t['is_in_roadmap'] : true type.is_milestone = t['is_milestone'] ? t['is_milestone'] : true type.is_default = t['is_default'] ? t['is_default'] : false type.is_standard = t['is_standard'] ? t['is_standard'] : false type.save! end end Given /^there are the following issue status:$/ do |table| table.hashes.each_with_index do |t, i| status = Status.find_by(name: t['name']) status = Status.new name: t['name'] if status.nil? status.is_closed = t['is_closed'] == 'true' status.is_default = t['is_default'] == 'true' status.position = t['position'] ? t['position'] : i status.default_done_ratio = t['default_done_ratio'] status.save! end end Given /^the type "(.+?)" has the default workflow for the role "(.+?)"$/ do |type_name, role_name| role = Role.find_by(name: role_name) type = ::Type.find_by(name: type_name) type.workflows = [] Status.order(Arel.sql('id ASC')).map(&:id).combination(2).each do |c| type.workflows.build(old_status_id: c[0], new_status_id: c[1], role: role) end type.save! end Given /^the [iI]ssue "([^\"]*)" has (\d+) [tT]ime(?: )?[eE]ntr(?:ies|y) with the following:$/ do |issue, count, table| i = WorkPackage.where(["subject = '#{issue}'"]).last raise "No such issue: #{issue}" unless i as_admin count do t = FactoryBot.build(:time_entry) t.project = i.project t.spent_on = DateTime.now t.work_package = i send_table_to_object(t, table, user: Proc.new do |o, v| o.user = User.find_by_login(v) o.save! end) end end Given /^I start debugging$/ do save_and_open_page require 'pry' binding.pry true end Given /^I (?:stop|pause) (?:step )?execution$/ do loop do $stdout.puts "\nPausing step execution. Press to continue. Enter `debug` to start debugging." text = $stdin.readline step 'I start debugging' if text =~ /debug/ break if text.strip.empty? end end When /^(?:|I )login as (.+?)(?: with password (.+))?$/ do |username, password| username = username.gsub("\"", '') password = password.nil? ? 'adminADMIN!' : password.gsub("\"", '') login(username, password) end When /^(?:|I )login with autologin enabled as (.+?)(?: with password (.+))?$/ do |username, password| username = username.gsub("\"", '') password = password.nil? ? 'adminADMIN!' : password.gsub("\"", '') page.driver.post signin_path(username: username, password: password, autologin: 1) end When 'I logout' do visit '/logout' end Then /^I should be logged in as "([^\"]*)"?$/ do |username| user = User.find_by_login(username) || User.anonymous page.should have_xpath("//div[contains(., 'Logged in as #{username}')] | //a[@title='#{user.name}']") User.current = user end Then 'I should be logged out' do page.should have_css('a.login') end When /^I satisfy the "(.+)" plugin to (.+)$/ do |plugin_name, action| if plugin_loaded?(plugin_name) action_name = action.gsub("\"", '') plugin_action(plugin_name, action_name) end end Given /^I am working in [pP]roject "(.+?)"$/ do |project_name| @project = Project.find_by(name: project_name) end Given /^the [pP]roject uses the following modules:$/ do |table| step %{the project "#{get_project}" uses the following modules:}, table end Given(/^the user "(.*?)" is responsible$/) do |user| project = get_project project.responsible_id = User.find_by_login(user).id project.save end Given /^the [pP]roject(?: "([^\"]*)")? has the following types:$/ do |project_name, table| p = get_project(project_name) table.hashes.each_with_index do |t, i| type = ::Type.find_by(name: t['name']) type = ::Type.new name: t['name'] if type.nil? type.position = t['position'] ? t['position'] : i type.is_in_roadmap = t['is_in_roadmap'] ? t['is_in_roadmap'] : true type.save! if !p.types.include?(type) p.types << type p.save! end end end When(/^I wait for "(.*?)" minutes$/) do |number_of_minutes| page.set_rack_session(updated_at: Time.now - number_of_minutes.to_i.minutes) end def get_project(project_name = nil) if project_name.blank? project = @project else project = Project.find_by(name: project_name) end if project.nil? if project_name.blank? raise "Could not identify the current project. Make sure to use the 'I am working in project \"Project Name\" step beforehand." else raise "Could not find project with the name \"#{project_name}\"." end end project end # Modify a given user using the specified table def modify_user(u, table) as_admin do send_table_to_object(u, table, default_rate: Proc.new do |user, value| user.save! DefaultHourlyRate.new.tap do |r| r.valid_from = 3.years.ago.to_date r.rate = value r.user_id = user.id end.save! end, name: Proc.new { |user, _value| user.login = name; user.save! }, hourly_rate: Proc.new do |user, value| user.save! HourlyRate.new.tap do |r| r.valid_from = (2.years.ago + HourlyRate.count.days).to_date r.rate = value r.user_id = user.id r.project = user.projects.last end.save! end ) u.save! end u end # Encapsulate the logic to set a custom field on an issue def add_custom_value_to_issue(object, key, value) if WorkPackageCustomField.all.map(&:name).include? key.to_s cv = CustomValue.where(["customized_id = '#{object.id}'"]).first cv ||= CustomValue.new cv.customized_type = 'WorkPackage' cv.customized_id = object.id cv.custom_field_id = WorkPackageCustomField.find_by(name: key).id cv.value = value cv.save! end end # Try to assign an object the values set in a table def send_table_to_object(object, table, except = {}, rescue_block = nil) return unless table.raw.present? as_admin do table.rows_hash.each do |key, value| _key = key.gsub(' ', '_').underscore.to_sym if except[_key] except[_key].call(object, value) elsif except[key] except[key].call(object, value) elsif object.respond_to? :"#{_key}=" object.send(:"#{_key}=", value) elsif rescue_block rescue_block.call(object, key, value) else raise "No such method #{_key} on a #{object.class}" end end object.save! end end # Do something as admin def as_admin(count = 1) cur_user = User.current User.current = User.find_by_login('admin') retval = nil count.to_i.times do retval = yield end User.current = cur_user retval end def plugin_loaded?(name) Redmine::Plugin.all.detect { |x| x.id == name.to_sym }.present? end