kanbanworkflowstimelinescrumrubyroadmapproject-planningproject-managementopenprojectangularissue-trackerifcgantt-chartganttbug-trackerboardsbcf
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
535 lines
16 KiB
535 lines
16 KiB
# encoding: utf-8
|
|
require 'active_record/fixtures'
|
|
require "rack_session_access/capybara"
|
|
|
|
Before do |scenario|
|
|
unless ScenarioDisabler.empty_if_disabled(scenario)
|
|
# Reset the DB and load the minimal fixtures for a functional system before each scenario
|
|
system "RAILS_ENV=test db:test:load >/dev/null 2>/dev/null"
|
|
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
|
|
ActiveRecord::Schema.verbose = false
|
|
|
|
fixtures_path = File.expand_path(Rails.root.join('test/fixtures'))
|
|
FactoryGirl.create(:admin) unless User.find_by_login("admin")
|
|
FactoryGirl.create(:anonymous) unless AnonymousUser.count > 0
|
|
Setting.notified_events = [] #can not test mailer
|
|
Dir.glob(File.join(fixtures_path, "*.yml")) do |table_name|
|
|
ActiveRecord::Fixtures.create_fixtures(fixtures_path, File.basename(table_name).gsub(".yml", ""))
|
|
end
|
|
|
|
if Capybara.current_driver.to_s.include?("selenium")
|
|
Capybara.current_session.driver.browser.manage.window.resize_to(3000, 3000)
|
|
end
|
|
end
|
|
end
|
|
|
|
Given /^(?:|I )am not logged in$/ do
|
|
User.current = AnonymousUser.first
|
|
end
|
|
|
|
Given /^(?:|I )am [aA]dmin$/ do
|
|
FactoryGirl.create :admin unless User.where(:login => 'admin').any?
|
|
FactoryGirl.create :anonymous unless AnonymousUser.count > 0
|
|
login('admin', 'admin')
|
|
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)
|
|
end
|
|
|
|
Given /^(?:|I )am logged in as "([^\"]*)"$/ do |username|
|
|
FactoryGirl.create :admin unless User.where(:login => 'admin').any?
|
|
FactoryGirl.create :anonymous unless AnonymousUser.count > 0
|
|
login(username, 'admin')
|
|
end
|
|
|
|
Given /^there is 1 [pP]roject with(?: the following)?:$/ do |table|
|
|
p = FactoryGirl.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 [Pp]roject "([^\"]*)" has 1 [wW]iki(?: )?[pP]age with the following:$/ do |project, table|
|
|
p = Project.find_by_name(project)
|
|
|
|
p.wiki.create! unless p.wiki
|
|
|
|
page = FactoryGirl.create(:wiki_page, :wiki => p.wiki)
|
|
content = FactoryGirl.create(:wiki_content, :page => page)
|
|
|
|
send_table_to_object(page, table)
|
|
end
|
|
|
|
Given /^there is 1 [Uu]ser with(?: the following)?:$/ do |table|
|
|
login = table.rows_hash[:Login].to_s + table.rows_hash[:login].to_s
|
|
user = User.find_by_login(login) unless login.blank?
|
|
|
|
if user
|
|
table = table.reject_key(/(L|l)ogin/)
|
|
else
|
|
user = FactoryGirl.create(:user)
|
|
user.password = user.password_confirmation = nil
|
|
end
|
|
|
|
modify_user(user, table)
|
|
end
|
|
|
|
Given /^the [Uu]ser "([^\"]*)" has:$/ do |user, table|
|
|
u = User.find_by_login(user)
|
|
raise "No such user: #{user}" unless u
|
|
modify_user(u, table)
|
|
end
|
|
|
|
Given /^there are the following users:$/ do |table|
|
|
table.raw.flatten.each do |login|
|
|
FactoryGirl.create(:user, :login => login)
|
|
end
|
|
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 { |row| row.first }
|
|
p.reload
|
|
end
|
|
|
|
Given /^the [Uu]ser "([^\"]*)" is a "([^\"]*)" (?:in|of) the [Pp]roject "([^\"]*)"$/ do |user, role, project|
|
|
u = User.find_by_login(user)
|
|
r = Role.find_by_name(role)
|
|
p = Project.find_by_name(project) || Project.find_by_identifier(project)
|
|
as_admin do
|
|
Member.new.tap do |m|
|
|
m.user = u
|
|
m.privacy_unnecessary = true if plugin_loaded?("redmine_dtag_privacy")
|
|
m.roles << r
|
|
m.project = p
|
|
end.save!
|
|
end
|
|
end
|
|
|
|
Given /^the [Uu]ser "([^\"]*)" has the following preferences$/ do |user, table|
|
|
u = User.find_by_login(user)
|
|
|
|
send_table_to_object(u.pref, table)
|
|
end
|
|
|
|
|
|
Given /^there is a(?:n)? (default )?(?:issue)?status with:$/ do |default, table|
|
|
name = table.raw.select { |ary| ary.include? "name" }.first[table.raw.first.index("name") + 1].to_s
|
|
IssueStatus.find_by_name(name) || IssueStatus.create(:name => name.to_s, :is_default => !!default)
|
|
end
|
|
|
|
Given /^there is a(?:n)? (default )?issuepriority with:$/ do |default, table|
|
|
name = table.raw.select { |ary| ary.include? "name" }.first[table.raw.first.index("name") + 1].to_s
|
|
project = get_project
|
|
IssuePriority.new.tap do |prio|
|
|
prio.name = name
|
|
prio.is_default = !!default
|
|
prio.project = project
|
|
prio.save!
|
|
end
|
|
end
|
|
|
|
Given /^there is a [rR]ole "([^\"]*)"$/ do |name|
|
|
Role.spawn.tap { |r| r.name = name }.save! unless Role.find_by_name(name)
|
|
end
|
|
|
|
Given /^there are the following roles:$/ do |table|
|
|
table.raw.flatten.each do |name|
|
|
FactoryGirl.create(:role, :name => name) unless Role.find_by_name(name)
|
|
end
|
|
end
|
|
|
|
Given /^the [rR]ole "([^\"]*)" may have the following [rR]ights:$/ do |role, table|
|
|
r = Role.find_by_name(role)
|
|
raise "No such role was defined: #{role}" unless r
|
|
as_admin do
|
|
available_perms = Redmine::AccessControl.permissions.collect(&:name)
|
|
r.permissions = []
|
|
|
|
table.raw.each do |_perm|
|
|
perm = _perm.first
|
|
unless perm.blank?
|
|
perm = perm.gsub(" ", "_").underscore.to_sym
|
|
if available_perms.include?(:"#{perm}")
|
|
r.permissions << perm
|
|
end
|
|
end
|
|
end
|
|
|
|
r.save!
|
|
end
|
|
end
|
|
|
|
Given /^the [rR]ole "(.+?)" has no (?:[Pp]ermissions|[Rr]ights)$/ do |role_name|
|
|
role = Role.find_by_name(role_name)
|
|
raise "No such role was defined: #{role_name}" unless role
|
|
as_admin do
|
|
role.permissions = []
|
|
role.save!
|
|
end
|
|
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 = Issue.generate_for_project!(p)
|
|
t = TimeEntry.generate
|
|
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 = TimeEntry.generate
|
|
i = Issue.generate_for_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 [Uu]ser "([^\"]*)" has (\d+) [iI]ssue(?:s)? with(?: the following)?:$/ do |user, count, table|
|
|
u = User.find_by_login user
|
|
raise "This user must be member of a project to have issues" unless u.projects.last
|
|
as_admin count do
|
|
i = Issue.generate_for_project!(u.projects.last)
|
|
i.author = u
|
|
i.assigned_to = u
|
|
i.tracker = Tracker.find_by_name(table.rows_hash.delete("tracker")) if table.rows_hash["tracker"]
|
|
send_table_to_object(i, table, {}, method(:add_custom_value_to_issue))
|
|
i.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 = TimeEntry.generate
|
|
i = Issue.generate_for_project!(p)
|
|
t.project = p
|
|
t.issue = 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 (\d+) [iI]ssue(?:s)? with(?: the following)?:$/ do |project, count, table|
|
|
p = Project.find_by_name(project) || Project.find_by_identifier(project)
|
|
as_admin count do
|
|
i = Issue.generate_for_project!(p)
|
|
send_table_to_object(i, table, {}, method(:add_custom_value_to_issue))
|
|
end
|
|
end
|
|
|
|
Given /^the [Pp]roject "([^\"]*)" has (\d+) [Dd]ocument with(?: the following)?:$/ do |project, count, table|
|
|
p = Project.find_by_name(project) || Project.find_by_identifier(project)
|
|
as_admin count do
|
|
d = Document.spawn
|
|
d.project = p
|
|
d.category = DocumentCategory.first
|
|
d.save!
|
|
send_table_to_object(d, table)
|
|
end
|
|
end
|
|
|
|
Given /^the [Pp]roject (.+) has 1 version with(?: the following)?:$/ do |project, table|
|
|
project.gsub!("\"", "")
|
|
p = Project.find_by_name(project) || Project.find_by_identifier(project)
|
|
table.rows_hash["effective_date"] = eval(table.rows_hash["effective_date"]).to_date if table.rows_hash["effective_date"]
|
|
|
|
as_admin do
|
|
v = Version.generate
|
|
send_table_to_object(v, table)
|
|
p.versions << v
|
|
end
|
|
end
|
|
|
|
Given /^the [pP]roject "([^\"]*)" has 1 [sS]ubproject$/ do |project|
|
|
parent = Project.find_by_name(project)
|
|
p = Project.generate
|
|
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 = FactoryGirl.build(:project)
|
|
as_admin do
|
|
send_table_to_object(p, table)
|
|
end
|
|
|
|
p.set_parent!(parent)
|
|
p.save!
|
|
end
|
|
|
|
Given /^there are the following trackers:$/ do |table|
|
|
|
|
table.hashes.each_with_index do |t, i|
|
|
tracker = Tracker.find_by_name(t['name'])
|
|
tracker = Tracker.new :name => t['name'] if tracker.nil?
|
|
tracker.position = t['position'] ? t['position'] : i
|
|
tracker.is_in_roadmap = t['is_in_roadmap'] ? t['is_in_roadmap'] : true
|
|
tracker.save!
|
|
end
|
|
end
|
|
|
|
Given /^there are the following issue status:$/ do |table|
|
|
|
|
table.hashes.each_with_index do |t, i|
|
|
status = IssueStatus.find_by_name(t['name'])
|
|
status = IssueStatus.new :name => t['name'] if status.nil?
|
|
status.is_closed = t['is_closed'] == 'true' ? true : false
|
|
status.is_default = t['is_default'] == 'true' ? true : false
|
|
status.position = t['position'] ? t['position'] : i
|
|
status.default_done_ratio = t['default_done_ratio']
|
|
status.save!
|
|
end
|
|
end
|
|
|
|
Given /^the tracker "(.+?)" has the default workflow for the role "(.+?)"$/ do |tracker_name, role_name|
|
|
role = Role.find_by_name(role_name)
|
|
tracker = Tracker.find_by_name(tracker_name)
|
|
tracker.workflows = []
|
|
|
|
IssueStatus.all(:order => "id ASC").collect(&:id).combination(2).each do |c|
|
|
tracker.workflows.build(:old_status_id => c[0], :new_status_id => c[1], :role => role)
|
|
end
|
|
tracker.save!
|
|
end
|
|
|
|
|
|
Given /^the [iI]ssue "([^\"]*)" has (\d+) [tT]ime(?: )?[eE]ntr(?:ies|y) with the following:$/ do |issue, count, table|
|
|
i = Issue.find(:last, :conditions => ["subject = '#{issue}'"])
|
|
raise "No such issue: #{issue}" unless i
|
|
as_admin count do
|
|
t = TimeEntry.generate
|
|
t.project = i.project
|
|
t.spent_on = DateTime.now
|
|
t.issue = 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 select to see [cC]olumn "([^\"]*)"$/ do |column_name|
|
|
steps %Q{
|
|
When I select \"#{column_name}\" from \"available_columns\"
|
|
When I click on \"→\"
|
|
When I click on \"Apply\"
|
|
}
|
|
end
|
|
|
|
Given /^I select to see [cC]olumn(?:s)?$/ do |table|
|
|
params = "?set_filter=1&" + table.raw.collect(&:first).collect do |name|
|
|
page.source =~ /<option value="(.*?)">#{name}<\/option>/
|
|
column_name = $1 || name.gsub(" ", "_").downcase
|
|
"query[column_names][]=#{column_name}"
|
|
end.join("&")
|
|
visit(current_path + params)
|
|
end
|
|
|
|
Given /^I start debugging$/ do
|
|
save_and_open_page
|
|
binding.pry
|
|
true
|
|
end
|
|
|
|
Given /^I (?:stop|pause) (?:step )?execution$/ do
|
|
loop do
|
|
$stdout.puts "\nPausing step execution. Press <Enter> 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.gsub("\"", "")
|
|
login(username, password)
|
|
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[contains(.,'#{user.name}')]")
|
|
|
|
User.current = user
|
|
end
|
|
|
|
When /^(?:|I )login as (.+)?$/ do |username|
|
|
steps %Q{
|
|
When I login as #{username} with password admin
|
|
}
|
|
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 %Q{the project "#{get_project}" uses the following modules:}, table
|
|
end
|
|
|
|
|
|
Given /^the user "(.*?)" is a "([^\"]*?)"$/ do |user, role|
|
|
step %Q{the user "#{user}" is a "#{role}" in the project "#{get_project.name}"}
|
|
end
|
|
|
|
Given /^the [pP]roject(?: "([^\"]*)")? has the following trackers:$/ do |project_name, table|
|
|
p = get_project(project_name)
|
|
table.hashes.each_with_index do |t, i|
|
|
tracker = Tracker.find_by_name(t['name'])
|
|
tracker = Tracker.new :name => t['name'] if tracker.nil?
|
|
tracker.position = t['position'] ? t['position'] : i
|
|
tracker.is_in_roadmap = t['is_in_roadmap'] ? t['is_in_roadmap'] : true
|
|
tracker.save!
|
|
p.trackers << tracker
|
|
p.save!
|
|
end
|
|
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
|
|
|
|
# Encapsule the logic to set a custom field on an issue
|
|
def add_custom_value_to_issue(object, key, value)
|
|
if IssueCustomField.all.collect(&:name).include? key.to_s
|
|
cv = CustomValue.find(:first, :conditions => ["customized_id = '#{object.id}'"])
|
|
cv ||= CustomValue.new
|
|
cv.customized_type = "Issue"
|
|
cv.customized_id = object.id
|
|
cv.custom_field_id = IssueCustomField.first(:joins => :translations, :conditions => ["custom_field_translations.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! if object.changed?
|
|
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
|
|
|
|
|