ajax_url = quick_edit_issues_edit_path(:ids => issue_ids, :target_specifier => target_specifier, :back_url => back_url)
- sprintf('<li>%s</li>',
+ html_id = "quick_edit_context_#{attribute_name}"
+ html_id += "_#{additional_index}" unless additional_index.nil?
+
+ sprintf("<li id='#{html_id}'>%s</li>",
context_menu_link(
h(caption),
ajax_url,
- :class => 'icon-edit',
+ :class => 'quick_edit icon-edit',
:disabled => disabled,
:remote => true
)
%>
<li class="folder">
- <a href="#" class="submenu"><%= l(:button_quick_edit) %></a>
+ <a id="quick_edit_context" href="#" class="submenu"><%= l(:button_quick_edit) %></a>
<ul>
<%= quick_edit_link_to(@issue_ids, get_attribute_caption(:subject), :subject, nil, back_url, !editable(:subject,@can,readonly_fields)) %>
<%= quick_edit_link_to(@issue_ids, get_attribute_caption(:description), :description, nil, back_url, !editable(:description,@can,readonly_fields)) %>
--- /dev/null
+--color
+--format d
+--require spec_helper
--- /dev/null
+# coding: utf-8
+
+require "json"
+require "selenium-webdriver"
+$: << File.expand_path('../../', __FILE__)
+require 'spec_helper'
+Dir[File.dirname(__FILE__) + '/pages/page.rb'].each {|file| require file }
+Dir[File.dirname(__FILE__) + '/pages/*.rb'].each {|file| require file }
+require "uri"
+require "net/http"
+include RSpec::Expectations
+
+describe "Edit" do
+
+ before(:each) do
+ profile = Selenium::WebDriver::Firefox::Profile.new
+ @driver = Selenium::WebDriver.for :firefox, :profile => profile
+ @driver.manage.window.maximize
+ @base_url = "http://localhost:3000/"
+ @accept_next_alert = true
+ @driver.manage.timeouts.implicit_wait = 10
+ @verification_errors = []
+ @default_project = "test"
+ @default_user = "admin"
+ @default_password = "dummy"
+ @issue_id = 1
+ start_page = QuickEdit::Test::Pages::StartPage.new(@driver, @base_url, @default_project)
+ first_page = start_page.login @default_user, @default_password
+ @issues_page = first_page.open_issues
+ end
+
+ after(:each) do
+ @driver.quit
+ expect(@verification_errors).to match_array []
+ end
+
+ it "subject can edit" do
+ new_value = 'dummy'
+ expect( edit(@issue_id, :subject, new_value) ).to eq new_value
+
+ new_value = 'subject: new_value'
+ expect( edit(@issue_id, :subject, new_value) ).to eq new_value
+
+ expect( edit_with_alert(@issue_id, :subject, "") ).to eq new_value
+ end
+
+ it "start_date can edit" do
+ new_value = '1900-01-01'
+ expect( edit(@issue_id, :start_date, new_value) ).to eq new_value
+
+ new_value = '1900-01-02'
+ expect( edit(@issue_id, :start_date, new_value) ).to eq new_value
+
+ expect( edit_with_alert(@issue_id, :start_date, "") ).to eq new_value
+ end
+
+ it "due_date can edit" do
+ new_value = '2000-01-01'
+ expect( edit(@issue_id, :due_date, new_value) ).to eq new_value
+
+ new_value = '2000-01-02'
+ expect( edit(@issue_id, :due_date, new_value) ).to eq new_value
+
+ expect( edit_with_alert(@issue_id, :due_date, "") ).to eq new_value
+ end
+
+ it "description can edit" do
+ new_value = 'dummy'
+ expect( edit(@issue_id, :description, new_value) ).to eq new_value
+
+ new_value = 'description: new_value'
+ expect( edit(@issue_id, :description, new_value) ).to eq new_value
+
+ expect( edit_with_alert(@issue_id, :description, "") ).to eq new_value
+ end
+
+ def edit(issue_id, attribute_name, new_value)
+ @issues_page.quick_edit_for_core_field issue_id, attribute_name, new_value
+
+ field_value = get_core_field(issue_id, attribute_name)
+ field_value
+ end
+
+ def edit_with_alert(issue_id, attribute_name, new_value)
+ @issues_page.quick_edit_for_core_field issue_id, attribute_name, new_value, true
+ @issues_page.alert.accept
+
+ field_value = get_core_field(issue_id, attribute_name)
+ field_value
+ end
+
+ def edit_custom_field(issue_id, custom_field_name, new_value)
+ cf = get_custom_field(issue_id, custom_field_name)
+ cf_id = cf["id"]
+
+ @issues_page.quick_edit_for_custom_field issue_id, cf_id, new_value
+
+ cf = get_custom_field(issue_id, custom_field_name)
+ cf["value"]
+ end
+
+ def get_core_field(issue_id, attribute_name)
+ json = get_json("issues/#{issue_id}.json")
+
+ json["issue"][attribute_name.to_s]
+ end
+
+ def get_custom_field(issue_id, custom_field_name)
+ cf_hash_list = get_custom_fields(issue_id)
+
+ cf_hash = cf_hash_list.select do |cf_hash|
+ cf_hash["name"] == custom_field_name.to_s
+ end
+
+ cf_hash.first
+ end
+
+ def get_custom_fields(issue_id)
+ json = get_json("issues/#{issue_id}.json")
+
+ json["issue"]["custom_fields"]
+ end
+
+ def get_json(path)
+ uri = URI::parse "#{@base_url}#{path}"
+ res = Net::HTTP::get_response(uri)
+ JSON.parse(res.body)
+ end
+
+
+end
--- /dev/null
+# coding: utf-8
+
+require "json"
+require "selenium-webdriver"
+$: << File.expand_path('../../', __FILE__)
+require 'spec_helper'
+Dir[File.dirname(__FILE__) + '/pages/page.rb'].each {|file| require file }
+Dir[File.dirname(__FILE__) + '/pages/*.rb'].each {|file| require file }
+require "uri"
+require "net/http"
+include RSpec::Expectations
+
+describe "Edit" do
+
+ before(:each) do
+ profile = Selenium::WebDriver::Firefox::Profile.new
+ @driver = Selenium::WebDriver.for :firefox, :profile => profile
+ @driver.manage.window.maximize
+ @base_url = "http://localhost:3000/"
+ @accept_next_alert = true
+ @driver.manage.timeouts.implicit_wait = 10
+ @verification_errors = []
+ @default_project = "test"
+ @default_user = "admin"
+ @default_password = "dummy"
+ @issue_id = 1
+ start_page = QuickEdit::Test::Pages::StartPage.new(@driver, @base_url, @default_project)
+ first_page = start_page.login @default_user, @default_password
+ @issues_page = first_page.open_issues
+ end
+
+ after(:each) do
+ @driver.quit
+ expect(@verification_errors).to match_array []
+ end
+
+ it "custom_text can edit" do
+ new_value = 'dummy'
+ expect( edit_custom_field(@issue_id, :custom_text, new_value) ).to eq new_value
+
+ new_value = 'custom_text: new_value'
+ expect( edit_custom_field(@issue_id, :custom_text, new_value) ).to eq new_value
+
+ invalid_value = ''
+ expect( edit_custom_field_with_alert(@issue_id, :custom_text, invalid_value) ).to eq new_value
+ end
+
+ it "custom_int can edit" do
+ new_value = '0'
+ expect( edit_custom_field(@issue_id, :custom_int, new_value) ).to eq new_value
+
+ new_value = '2147483647'
+ expect( edit_custom_field(@issue_id, :custom_int, new_value) ).to eq new_value
+
+ new_value = '+10'
+ expect( edit_custom_field(@issue_id, :custom_int, new_value).to_i ).to eq new_value.to_i
+
+ new_value = '-10'
+ expect( edit_custom_field(@issue_id, :custom_int, new_value).to_i ).to eq new_value.to_i
+
+ invalid_value = 'a'
+ expect( edit_custom_field_with_alert(@issue_id, :custom_int, invalid_value) ).to eq new_value
+
+ invalid_value = ''
+ expect( edit_custom_field_with_alert(@issue_id, :custom_int, "") ).to eq new_value
+ end
+
+ it "custom_date can edit" do
+ new_value = '1900-01-01'
+ expect( edit_custom_field(@issue_id, :custom_date, new_value) ).to eq new_value
+
+ new_value = '2015-01-01'
+ expect( edit_custom_field(@issue_id, :custom_date, new_value) ).to eq new_value
+
+ invalid_value = '2015-01-0a'
+ expect( edit_custom_field_with_alert(@issue_id, :custom_date, invalid_value) ).to eq new_value
+
+ invalid_value = ''
+ expect( edit_custom_field_with_alert(@issue_id, :custom_date, invalid_value) ).to eq new_value
+ end
+
+ it "custom_long can edit" do
+ new_value = 'dummy'
+ expect( edit_custom_field(@issue_id, :custom_long, new_value) ).to eq new_value
+
+ new_value = 'custom_long: new_value '
+ expect( edit_custom_field(@issue_id, :custom_long, new_value) ).to eq new_value
+
+ invalid_value = ''
+ expect( edit_custom_field_with_alert(@issue_id, :custom_long, invalid_value) ).to eq new_value
+ end
+
+ it "custom_float can edit" do
+ new_value = '0'
+ expect( edit_custom_field(@issue_id, :custom_float, new_value) ).to eq new_value
+
+ new_value = '0.1'
+ expect( edit_custom_field(@issue_id, :custom_float, new_value).to_f ).to eq new_value.to_f
+
+ new_value = '+0.1'
+ expect( edit_custom_field(@issue_id, :custom_float, new_value).to_f ).to eq new_value.to_f
+
+ new_value = '-0.1'
+ expect( edit_custom_field(@issue_id, :custom_float, new_value).to_f ).to eq new_value.to_f
+
+ new_value = '0.1e2'
+ expect( edit_custom_field(@issue_id, :custom_float, new_value).to_f ).to eq new_value.to_f
+
+ new_value = '0.1e-2'
+ expect( edit_custom_field(@issue_id, :custom_float, new_value).to_f ).to eq new_value.to_f
+
+ invalid_value = ''
+ expect( edit_custom_field_with_alert(@issue_id, :custom_float, invalid_value) ).to eq new_value
+ end
+
+ def edit(issue_id, attribute_name, new_value)
+ @issues_page.quick_edit issue_id, attribute_name, new_value
+
+ field_value = get_core_field(issue_id, attribute_name)
+ field_value
+ end
+
+ def edit_custom_field(issue_id, custom_field_name, new_value)
+ cf = get_custom_field(issue_id, custom_field_name)
+ cf_id = cf["id"]
+
+ @issues_page.quick_edit_for_custom_field issue_id, cf_id, new_value
+
+ cf = get_custom_field(issue_id, custom_field_name)
+ cf["value"]
+ end
+
+ def edit_custom_field_with_alert(issue_id, custom_field_name, new_value="")
+ cf = get_custom_field(issue_id, custom_field_name)
+ cf_id = cf["id"]
+
+ @issues_page.quick_edit_for_custom_field issue_id, cf_id, new_value, true
+ @issues_page.alert.accept
+
+ cf = get_custom_field(issue_id, custom_field_name)
+ cf["value"]
+ end
+
+
+ def get_core_field(issue_id, attribute_name)
+ json = get_json("issues/#{issue_id}.json")
+
+ json["issue"][attribute_name.to_s]
+ end
+
+ def get_custom_field(issue_id, custom_field_name)
+ cf_hash_list = get_custom_fields(issue_id)
+
+ cf_hash = cf_hash_list.select do |cf_hash|
+ cf_hash["name"] == custom_field_name.to_s
+ end
+
+ cf_hash.first
+ end
+
+ def get_custom_fields(issue_id)
+ json = get_json("issues/#{issue_id}.json")
+
+ json["issue"]["custom_fields"]
+ end
+
+ def get_json(path)
+ uri = URI::parse "#{@base_url}#{path}"
+ res = Net::HTTP::get_response(uri)
+ JSON.parse(res.body)
+ end
+
+
+end
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class CustomFieldNewPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-custom_fields action-new']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/custom_fields/new?type=IssueCustomField"
+ CustomFieldNewPage.new driver, base_url, project
+ end
+
+ def create(name, format)
+ input_text :id, :custom_field_name, name
+ select :id, :custom_field_field_format, format
+
+ # check bug tracker
+ click :id, 'custom_field_tracker_ids_1'
+
+ # check for all projects
+ click :id, 'custom_field_is_for_all'
+
+ # submit
+ click :name, 'commit'
+
+ CustomFieldsPage.new @driver, @base_url, @project
+ end
+
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class CustomFieldsPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-custom_fields action-index']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/custom_fields/"
+ CustomFieldsPage.new driver, base_url, project
+ end
+
+ def open_new_page
+ CustomFieldNewPage.open @driver, @base_url, @project
+ end
+
+ def find_field(name)
+ elements = find_elements(:css, 'td > a')
+ elements = elements.select do |e|
+ e.text == name.to_s
+ end
+
+ if elements.empty?
+ return nil
+ else
+ /custom_fields\/(\d+)\// =~ elements[0].attribute('href')
+ Regexp.last_match(1)
+ end
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class IssueNewPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-issues action-new']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/projects/#{project}/issues/new"
+ IssueNewPage.new driver, base_url, project
+ end
+
+ def create(tracker, subject)
+ select_tracker tracker
+ input_text :id, :issue_subject, subject
+ click :name, :commit
+
+ IssueShowPage.new @driver, @base_url, @project
+ end
+
+ def select_tracker(tracker)
+ select :id, :issue_tracker_id, {:bug=>1, :feature=>2, :support=>3}[tracker.to_sym]
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class IssueShowPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-issues action-show']"
+ end
+
+ def self.open(driver, base_url, project, issue_id)
+ driver.get "#{base_url}/projects/#{project}/issues/#{issue_id}"
+ IssueShowPage.new driver, base_url, project, issue_id
+ end
+
+ def id
+ title = @driver.getTitle()
+ /^(\w+) #(\d+)/ =~ title
+ Regexp.last_match(1)
+ end
+
+ def subject
+ find_element :css, "div.subject h3"
+ end
+
+ def start_date
+ find_element :css, "td.start-date"
+ end
+
+ def due_date
+ find_element :css, "td.due-date"
+ end
+
+ def description
+ find_element :css, "div.description > div.wiki > p"
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class IssuesPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-issues action-index']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/projects/#{project}/issues/"
+ IssuesPage.new driver, base_url, project
+ end
+
+ def issue_ids_on_page
+ issue_elements = find_elements(:css, "tr.issue")
+ issue_ids = issue_elements.map do |e|
+ #p "find:" + e.attribute("id")
+ /issue-(\d+)/ =~ e.attribute("id")
+ Regexp.last_match(1)
+ end
+ issue_ids
+ end
+
+ def open_new_page()
+ IssueNewPage.open @driver, @base_url, @project
+ end
+
+ def open_context(issue_id)
+ wait
+
+ element = find_element(:css, "#issue-#{issue_id} > td.subject")
+ action.move_to(element).context_click(element).perform
+ end
+
+ def quick_edit(issue_id, menu_selector, new_value)
+ open_context issue_id
+
+ menu_element = find_element(:id, "quick_edit_context")
+ menu_item_element = find_element(:css, menu_selector)
+ action.move_to(menu_element).click(menu_item_element).perform
+
+ input_text :id, "new_value", new_value
+
+ buttons = find_elements(:css, "button > span")
+ submit_button = buttons.select {|button| button.text =~ /Submit/}
+ submit_button.first.click
+ end
+
+ def quick_edit_for_core_field(issue_id, attribute_name, new_value, desire_alerting = false)
+ menu_selector = "#quick_edit_context_#{attribute_name} > a"
+
+ quick_edit(issue_id, menu_selector, new_value)
+
+ IssuesPage.new @driver, @base_url, @project unless desire_alerting
+ end
+
+ def quick_edit_for_custom_field(issue_id, custom_field_id, new_value, desire_alerting = false)
+ open_context issue_id
+
+ menu_element = find_element(:id, "quick_edit_context")
+ menu_item_element = find_element(:css, "#quick_edit_context_custom_field_values_#{custom_field_id} > a")
+
+ action.move_to(menu_element).click(menu_item_element).perform
+
+ input_text :id, "new_value", new_value
+
+ buttons = find_elements(:css, "button > span")
+ submit_button = buttons.select {|button| button.text =~ /Submit/}
+ submit_button.first.click
+
+ IssuesPage.new @driver, @base_url, @project unless desire_alerting
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class MyPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-my action-page']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/my/page"
+ MyPage.new driver, base_url, project
+ end
+
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class Page
+ def initialize(driver, base_url, project)
+ @driver = driver
+ @base_url = base_url
+ @project = project
+ @default_wait = 0.2
+ @retry = 0
+ @retry_limit = 2
+ end
+
+ def get(url)
+ @driver.get url
+
+ wait
+ end
+
+ def wait(sec=nil)
+ sec = @default_wait if sec.nil?
+ sleep sec
+ end
+
+ def find_element(by, target)
+ @driver.find_element(by, target)
+ end
+
+ def find_elements(by, target)
+ @driver.find_elements(by, target)
+ end
+
+ def input_text(by, target, value)
+ wait
+
+ element = find_element(by, target)
+ element.clear
+ element.send_keys value.to_s unless value.nil? || value.empty?
+ end
+
+ def click(by, target)
+ wait
+
+ begin
+ element = find_element(by, target)
+ element.click
+ @retry = 0
+ rescue Selenium::WebDriver::Error::StaleElementReferenceError => e
+ if @retry < @retry_limit
+ @retry += 1
+ p "+++ retry for click(#{by},#{target})"
+ retry
+ else
+ raise e
+ end
+ end
+ end
+
+ def select(by, target, value)
+ wait
+
+ element = find_element(by, target)
+ select = Selenium::WebDriver::Support::Select.new(element)
+ select.select_by :value, value.to_s
+ end
+
+ def selected(by_or_element, target=nil)
+ if target.nil?
+ element = by_or_element
+ else
+ element = find_element(by, target)
+ end
+ select = Selenium::WebDriver::Support::Select.new(element)
+ select.selected_options
+ end
+
+ def action
+ @driver.action
+ end
+
+ def alert
+ wait 3
+
+ @driver.switch_to.alert
+ end
+
+ def open_projects
+ ProjectsPage.open @driver, @base_url, @project
+ end
+
+ def open_users
+ UsersPage.open @driver, @base_url, @project
+ end
+
+ def open_custom_fields
+ CustomFieldsPage.open @driver, @base_url, @project
+ end
+
+ def open_workflow_edit
+ WorkflowEditPage.open @driver, @base_url, @project
+ end
+
+ def open_issues
+ IssuesPage.open @driver, @base_url, @project
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class ProjectNewPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-projects action-new']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/projects/new"
+ ProjectNewPage.new driver, base_url, project
+ end
+
+ def create(id, name)
+ input_text :id, 'project_name', name
+ input_text :id, 'project_identifier', id
+ click :name, 'commit'
+
+ ProjectSettingsPage.new @driver, @base_url, @project
+ end
+
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class ProjectSettingsPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-projects action-settings']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/projects/#{project}/settings"
+ ProjectSettingsPage.new driver, base_url, project
+ end
+
+ def open_members
+ ProjectSettingsMembersPage.open @driver, @base_url, @project
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class ProjectSettingsMembersPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-projects action-settings']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/projects/#{project}/settings/members"
+ ProjectSettingsMembersPage.new driver, base_url, project
+ end
+
+ def find_role(user_id)
+ begin
+ pnodata = find_element(:css, "p.nodata")
+ if pnodata.displayed?
+ return nil
+ end
+ rescue Selenium::WebDriver::Error::NoSuchElementError
+ # next
+ end
+
+ member_elements = find_elements(:css, "tr.member")
+ target_tr = member_elements.select do | element |
+ e = element.find_element(:css, "td.user > a.user")
+ href = e.attribute("href")
+ /\/users\/(\d+)/ =~ href
+ id = Regexp.last_match(1)
+
+ id.to_i == user_id.to_i
+ end
+
+ if target_tr.empty?
+ nil
+ else
+ target_tr.first.find_element(:css, "td.roles > span").text
+ end
+ end
+
+ def add(user_id, role_id)
+ membership_elements = find_elements(:css, 'input[name^=membership]')
+ userid_elements = membership_elements.select do |membership_element|
+ name = membership_element.attribute("name")
+ /membership\[user_ids\]\[\]/ =~ name
+ end
+
+ userid_element = userid_elements.select do |userid_element|
+ value = userid_element.attribute("value")
+ value.to_i() == user_id.to_i()
+ end
+
+ userid_element.first.click
+
+ roleid_elements = membership_elements.select do |membership_element|
+ name = membership_element.attribute("name")
+ /membership\[role_ids\]\[\]/ =~ name
+ end
+
+ roleid_element = roleid_elements.select do |roleid_element|
+ value = roleid_element.attribute("value")
+ value.to_i == role_id.to_i
+ end
+
+ roleid_element.first.click
+
+ click :id, "member-add-submit"
+
+ ProjectSettingsMembersPage.new @driver, @base_url, @project
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class ProjectShowPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-projects action-show']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/projects/#{project}"
+ ProjectShowPage.new driver, base_url, project
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class ProjectsPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-projects action-index']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/projects"
+ ProjectsPage.new driver, base_url, project
+ end
+
+ def open_new_page
+ ProjectNewPage.open @driver, @base_url, @project
+ end
+
+ def open_settings_page(project)
+ ProjectSettingsPage.open @driver, @base_url, project
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class StartPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ get "#{base_url}/login"
+ find_element :id, "username"
+ end
+
+ def login(login_id, password)
+ input_text :id, "username", login_id
+ input_text :id, "password", password
+ click :name, "login"
+
+ MyPage.new @driver, @base_url, @project
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class UserEditPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-users action-edit']"
+ end
+
+ def self.open(driver, base_url, project, user_id)
+ driver.get "#{base_url}/users/#{user_id}/edit"
+ UserEditPage.new driver, base_url, project
+ end
+
+ def id
+ url = @driver.getCurrentUrl()
+ /users\/(\d+)\/edit/ =~ url
+ Regexp.last_match(1)
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class UserNewPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-users action-new']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/users/new"
+ UserNewPage.new driver, base_url, project
+ end
+
+ def create(username, firstname, lastname, mail, password)
+ input_text :id, :user_login, username
+ input_text :id, :user_firstname, firstname
+ input_text :id, :user_lastname, lastname
+ input_text :id, :user_mail, mail
+ input_text :id, :user_password, password
+ input_text :id, :user_password_confirmation, password
+ click :name, :commit
+ UserEditPage.new @driver, @base_url, @project
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class UsersPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-users action-index']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/users"
+ UsersPage.new driver, base_url, project
+ end
+
+ def open_new_page
+ UserNewPage.open @driver, @base_url, @project
+ end
+
+ def open_user_page(user_id)
+ UserShowPage.open @driver, @base_url, @project, user_id
+ end
+
+
+ def find_user(username)
+ elements = find_elements(:css, "tr.user>td>a")
+ users = elements.select do |u|
+ u.text == username.to_s
+ end
+
+ if users.empty?
+ return nil
+ else
+ url = users.first.attribute("href")
+ /users\/(\d+)\/edit/ =~ url
+ return Regexp.last_match(1)
+ end
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class WelcomePage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-welcome action-index']"
+ end
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class WorkflowEditPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-workflows action-edit']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/workflows/edit"
+ WorkflowEditPage.new driver, base_url, project
+ end
+
+ def open_field_permission_page
+ WorkflowPermissionsPage.open @driver, @base_url, @project
+ end
+
+ end
+ end
+ end
+end
+
--- /dev/null
+#coding: utf-8
+
+module QuickEdit
+ module Test
+ module Pages
+ class WorkflowPermissionsPage < Page
+ def initialize(driver, base_url, project)
+ super(driver, base_url, project)
+
+ find_element :css, "body[class='controller-workflows action-permissions']"
+ end
+
+ def self.open(driver, base_url, project)
+ driver.get "#{base_url}/workflows/permissions"
+ WorkflowPermissionsPage.new driver, base_url, project
+ end
+
+ def get_permissions(role_id, tracker, target_state, target_field_ids)
+ select :id, :role_id, role_id
+ click :css, "input[type='submit']"
+
+ permission_elements = find_elements(:css, 'select[name^=permissions]')
+
+ permissions = {}
+ permission_elements.each do |permission_element|
+ name = permission_element.attribute("name")
+ /permissions\[(.+?)\]\[(\d+)\]/ =~ name
+ id = Regexp.last_match(1)
+ state = Regexp.last_match(2)
+
+ if state == target_state && target_field_ids.include?(id)
+ permissions[id] = { state => selected(permission_element).first.attribute("value") }
+ end
+ end
+
+ target_field_ids.each do |id|
+ unless permissions.has_key? id
+ permissions[id] = {}
+ end
+ end
+
+ permissions
+ end
+
+ def update(role_id, tracker, permissions)
+ select :id, :role_id, role_id
+ click :css, "input[type='submit']"
+
+ find_elements :class, :fields_permissions
+
+ permissions.each do |k,v|
+ v.each do |state, permission|
+ select :name, "permissions[#{k}][#{state}]", permission
+ end
+ end
+ click :name, :commit
+
+ WorkflowPermissionsPage.new @driver, @base_url, @project
+ end
+
+ end
+ end
+ end
+end
+
--- /dev/null
+# coding: utf-8
+
+require "json"
+require "selenium-webdriver"
+$: << File.expand_path('../../', __FILE__)
+require 'spec_helper'
+Dir[File.dirname(__FILE__) + '/pages/page.rb'].each {|file| require file }
+Dir[File.dirname(__FILE__) + '/pages/*.rb'].each {|file| require file }
+require "uri"
+require "net/http"
+include RSpec::Expectations
+
+describe "Edit" do
+
+ before(:each) do
+ profile = Selenium::WebDriver::Firefox::Profile.new
+ @driver = Selenium::WebDriver.for :firefox, :profile => profile
+ @driver.manage.window.maximize
+ @base_url = "http://localhost:3000/"
+ @accept_next_alert = true
+ @driver.manage.timeouts.implicit_wait = 10
+ @verification_errors = []
+ @default_project = "test"
+ @default_user = "admin"
+ @default_password = "dummy"
+ start_page = QuickEdit::Test::Pages::StartPage.new(@driver, @base_url, @default_project)
+ @first_page = start_page.login @default_user, @default_password
+ end
+
+ after(:each) do
+ @driver.quit
+ expect(@verification_errors).to match_array []
+ end
+
+ it "setup project" do
+ json = get_json("projects.json")
+ test_project = json["projects"].select do |project|
+ project["name"] == "test"
+ end
+ if test_project.empty?
+ projects_page = @first_page.open_projects
+ project_new_page = projects_page.open_new_page
+ project_settings_page = project_new_page.create @default_project, @default_project
+ end
+ end
+
+ it "setup issues" do
+ issues_page = @first_page.open_issues
+ issue_ids = issues_page.issue_ids_on_page
+ if issue_ids.empty?
+ issue_new_page = issues_page.open_new_page()
+ issue_show_page = issue_new_page.create(:bug, 'first subject')
+ @issue_id = issue_show_page.id
+ else
+ @issue_id = issue_ids.first
+ end
+ end
+
+ it "setup custom fields" do
+ custom_fields_page = @first_page.open_custom_fields
+ id = custom_fields_page.find_field(:custom_text)
+ if id.nil?
+ new_page = custom_fields_page.open_new_page
+ custom_fields_page = new_page.create :custom_text, :string
+ end
+
+ id = custom_fields_page.find_field(:custom_long)
+ if id.nil?
+ new_page = custom_fields_page.open_new_page
+ custom_fields_page = new_page.create :custom_long, :text
+ end
+
+ id = custom_fields_page.find_field(:custom_int)
+ if id.nil?
+ new_page = custom_fields_page.open_new_page
+ custom_fields_page = new_page.create :custom_int, :int
+ end
+
+ id = custom_fields_page.find_field(:custom_float)
+ if id.nil?
+ new_page = custom_fields_page.open_new_page
+ custom_fields_page = new_page.create :custom_float, :float
+ end
+
+ id = custom_fields_page.find_field(:custom_date)
+ if id.nil?
+ new_page = custom_fields_page.open_new_page
+ custom_fields_page = new_page.create :custom_date, :date
+ end
+
+ id = custom_fields_page.find_field(:readonly_in_progress)
+ if id.nil?
+ new_page = custom_fields_page.open_new_page
+ custom_fields_page = new_page.create :readonly_in_progress, :string
+ end
+ end
+
+ it "setup users" do
+ users_page = @first_page.open_users
+ rep_user_id = users_page.find_user("rep1")
+ if rep_user_id.nil?
+ user_new_page = users_page.open_new_page
+ user_edit_page = user_new_page.create("rep1", "1", "rep", "rep1@localhost.com", "dummy")
+ end
+ end
+
+ it "setup roles" do
+ users_page = @first_page.open_users
+ rep_user_id = users_page.find_user("rep1")
+
+ projects_page = users_page.open_projects
+ project_page = projects_page.open_settings_page(@default_project)
+ members_page = project_page.open_members
+ role_name = members_page.find_role(rep_user_id)
+ #p role_name
+ if role_name.nil?
+ reporter_role_id = 5 #reporter_role_id
+ members_page = members_page.add rep_user_id, reporter_role_id
+ members_page.find_role(rep_user_id)
+ end
+ end
+
+ it "setup permissions" do
+ role_reporter = "5" #reporter
+ tracker_bug = "1" #bug
+ state_new = "1" #new
+ custom_field_readonly = get_custom_field(1, "readonly_in_progress")["id"].to_s
+ #p "readonly in progress's id = #{custom_field_readonly}"
+
+ workflow_edit_page = @first_page.open_workflow_edit
+ permission_page = workflow_edit_page.open_field_permission_page
+ permissions = permission_page.get_permissions(role_reporter, tracker_bug, state_new, [custom_field_readonly])
+ #p permissions.inspect
+
+ permission = permissions[custom_field_readonly][state_new]
+ if permission.nil? || permission != "readonly"
+ permissions = { custom_field_readonly => {state_new => "readonly"} }
+ permission_page.update role_reporter, tracker_bug, permissions
+ end
+ end
+
+ def get_custom_field(issue_id, custom_field_name)
+ cf_hash_list = get_custom_fields(issue_id)
+
+ cf_hash = cf_hash_list.select do |cf_hash|
+ cf_hash["name"] == custom_field_name.to_s
+ end
+
+ cf_hash.first
+ end
+
+ def get_custom_fields(issue_id)
+ json = get_json("issues/#{issue_id}.json")
+
+ json["issue"]["custom_fields"]
+ end
+
+ def get_json(path)
+ uri = URI::parse "#{@base_url}#{path}"
+ res = Net::HTTP::get_response(uri)
+ JSON.parse(res.body)
+ end
+
+end
--- /dev/null
+# This file was generated by the `rspec --init` command. Conventionally, all
+# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
+# The generated `.rspec` file contains `--require spec_helper` which will cause
+# this file to always be loaded, without a need to explicitly require it in any
+# files.
+#
+# Given that it is always loaded, you are encouraged to keep this file as
+# light-weight as possible. Requiring heavyweight dependencies from this file
+# will add to the boot time of your test suite on EVERY test run, even for an
+# individual file that may not need all of that loaded. Instead, consider making
+# a separate helper file that requires the additional dependencies and performs
+# the additional setup, and require it from the spec files that actually need
+# it.
+#
+# The `.rspec` file also contains a few flags that are not defaults but that
+# users commonly want.
+#
+# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
+RSpec.configure do |config|
+ # rspec-expectations config goes here. You can use an alternate
+ # assertion/expectation library such as wrong or the stdlib/minitest
+ # assertions if you prefer.
+ config.expect_with :rspec do |expectations|
+ # This option will default to `true` in RSpec 4. It makes the `description`
+ # and `failure_message` of custom matchers include text for helper methods
+ # defined using `chain`, e.g.:
+ # be_bigger_than(2).and_smaller_than(4).description
+ # # => "be bigger than 2 and smaller than 4"
+ # ...rather than:
+ # # => "be bigger than 2"
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
+ end
+
+ # rspec-mocks config goes here. You can use an alternate test double
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
+ config.mock_with :rspec do |mocks|
+ # Prevents you from mocking or stubbing a method that does not exist on
+ # a real object. This is generally recommended, and will default to
+ # `true` in RSpec 4.
+ mocks.verify_partial_doubles = true
+ end
+
+# The settings below are suggested to provide a good initial experience
+# with RSpec, but feel free to customize to your heart's content.
+=begin
+ # These two settings work together to allow you to limit a spec run
+ # to individual examples or groups you care about by tagging them with
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
+ # get run.
+ config.filter_run :focus
+ config.run_all_when_everything_filtered = true
+
+ # Limits the available syntax to the non-monkey patched syntax that is
+ # recommended. For more details, see:
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
+ config.disable_monkey_patching!
+
+ # This setting enables warnings. It's recommended, but in some cases may
+ # be too noisy due to issues in dependencies.
+ config.warnings = true
+
+ # Many RSpec users commonly either run the entire suite or an individual
+ # file, and it's useful to allow more verbose output when running an
+ # individual spec file.
+ if config.files_to_run.one?
+ # Use the documentation formatter for detailed output,
+ # unless a formatter has already been configured
+ # (e.g. via a command-line flag).
+ config.default_formatter = 'doc'
+ end
+
+ # Print the 10 slowest examples and example groups at the
+ # end of the spec run, to help surface which specs are running
+ # particularly slow.
+ config.profile_examples = 10
+
+ # Run specs in random order to surface order dependencies. If you find an
+ # order dependency and want to debug it, you can fix the order by providing
+ # the seed, which is printed after each run.
+ # --seed 1234
+ config.order = :random
+
+ # Seed global randomization in this process using the `--seed` CLI option.
+ # Setting this allows you to use `--seed` to deterministically reproduce
+ # test failures related to randomization by passing the same `--seed` value
+ # as the one that triggered the failure.
+ Kernel.srand config.seed
+=end
+end