Converted Menus to a Tree structure to allow submenus.

* Bundle the rubytree gem
* Patched RubyTree's TreeNode to add some additional methods.
* Converted the menu rendering to walk the Tree of MenuItems to render
  each item
* Added a menu option for :parent_menu to make this menu a child of the parent
* Added a bunch of tests
* Made MenuItem a subclass of Tree::TreeNode in order to use it's methods
  directly
* Changed the exceptions in MenuItem#new to be ArgumentErrors instead of the
  generic RuntimeError

  #4250

git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@3090 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Eric Davis
2009-11-25 05:36:44 +00:00
parent 5a9528cf3d
commit 1f06cf8899
20 changed files with 4602 additions and 41 deletions
@@ -0,0 +1,166 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# 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.
require File.dirname(__FILE__) + '/../../../../test_helper'
class Redmine::MenuManager::MapperTest < Test::Unit::TestCase
context "Mapper#initialize" do
should "be tested"
end
def test_push_onto_root
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.exists?(:test_overview)
end
def test_push_onto_parent
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent_menu => :test_overview}
assert menu_mapper.exists?(:test_child)
assert_equal :test_child, menu_mapper.find(:test_child).name
end
def test_push_onto_grandparent
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent_menu => :test_overview}
menu_mapper.push :test_grandchild, { :controller => 'projects', :action => 'show'}, {:parent_menu => :test_child}
assert menu_mapper.exists?(:test_grandchild)
grandchild = menu_mapper.find(:test_grandchild)
assert_equal :test_grandchild, grandchild.name
assert_equal :test_child, grandchild.parent_menu
end
def test_push_first
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {:first => true}
root = menu_mapper.find(:root)
assert_equal 5, root.children.size
{0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
assert_not_nil root.children[position]
assert_equal name, root.children[position].name
end
end
def test_push_before
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {:before => :test_fourth}
root = menu_mapper.find(:root)
assert_equal 5, root.children.size
{0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
assert_not_nil root.children[position]
assert_equal name, root.children[position].name
end
end
def test_push_after
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {:after => :test_third}
root = menu_mapper.find(:root)
assert_equal 5, root.children.size
{0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
assert_not_nil root.children[position]
assert_equal name, root.children[position].name
end
end
def test_push_last
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_first, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_second, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_third, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_fifth, { :controller => 'projects', :action => 'show'}, {:last => true}
menu_mapper.push :test_fourth, { :controller => 'projects', :action => 'show'}, {}
root = menu_mapper.find(:root)
assert_equal 5, root.children.size
{0 => :test_first, 1 => :test_second, 2 => :test_third, 3 => :test_fourth, 4 => :test_fifth}.each do |position, name|
assert_not_nil root.children[position]
assert_equal name, root.children[position].name
end
end
def test_exists_for_child_node
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
menu_mapper.push :test_child, { :controller => 'projects', :action => 'show'}, {:parent_menu => :test_overview }
assert menu_mapper.exists?(:test_child)
end
def test_exists_for_invalid_node
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
assert !menu_mapper.exists?(:nothing)
end
def test_find
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
item = menu_mapper.find(:test_overview)
assert_equal :test_overview, item.name
assert_equal({:controller => 'projects', :action => 'show'}, item.url)
end
def test_find_missing
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
item = menu_mapper.find(:nothing)
assert_equal nil, item
end
def test_delete
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
menu_mapper.push :test_overview, { :controller => 'projects', :action => 'show'}, {}
assert_not_nil menu_mapper.delete(:test_overview)
assert_nil menu_mapper.find(:test_overview)
end
def test_delete_missing
menu_mapper = Redmine::MenuManager::Mapper.new(:test_menu, {})
assert_nil menu_mapper.delete(:test_missing)
end
end
@@ -0,0 +1,161 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# 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.
require File.dirname(__FILE__) + '/../../../../test_helper'
class Redmine::MenuManager::MenuHelperTest < HelperTestCase
include Redmine::MenuManager::MenuHelper
include ActionController::Assertions::SelectorAssertions
fixtures :users, :members, :projects, :enabled_modules
# Used by assert_select
def html_document
HTML::Document.new(@response.body)
end
def setup
super
@response = ActionController::TestResponse.new
# Stub the current menu item in the controller
def @controller.current_menu_item
:index
end
end
context "MenuManager#current_menu_item" do
should "be tested"
end
context "MenuManager#render_main_menu" do
should "be tested"
end
context "MenuManager#render_menu" do
should "be tested"
end
context "MenuManager#menu_item_and_children" do
should "be tested"
end
context "MenuManager#extract_node_details" do
should "be tested"
end
def test_render_single_menu_node
node = Redmine::MenuManager::MenuItem.new(:testing, '/test', { })
@response.body = render_single_menu_node(node, 'This is a test', node.url, false)
assert_select("a.testing", "This is a test")
end
def test_render_menu_node
single_node = Redmine::MenuManager::MenuItem.new(:single_node, '/test', { })
@response.body = render_menu_node(single_node, nil)
assert_select("li") do
assert_select("a.single-node", "Single node")
end
end
def test_render_menu_node_with_nested_items
parent_node = Redmine::MenuManager::MenuItem.new(:parent_node, '/test', { })
parent_node << Redmine::MenuManager::MenuItem.new(:child_one_node, '/test', { })
parent_node << Redmine::MenuManager::MenuItem.new(:child_two_node, '/test', { })
parent_node <<
Redmine::MenuManager::MenuItem.new(:child_three_node, '/test', { }) <<
Redmine::MenuManager::MenuItem.new(:child_three_inner_node, '/test', { })
@response.body = render_menu_node(parent_node, nil)
assert_select("li") do
assert_select("a.parent-node", "Parent node")
assert_select("ul") do
assert_select("li a.child-one-node", "Child one node")
assert_select("li a.child-two-node", "Child two node")
assert_select("li") do
assert_select("a.child-three-node", "Child three node")
assert_select("ul") do
assert_select("li a.child-three-inner-node", "Child three inner node")
end
end
end
end
end
def test_menu_items_for_should_yield_all_items_if_passed_a_block
menu_name = :test_menu_items_for_should_yield_all_items_if_passed_a_block
Redmine::MenuManager.map menu_name do |menu|
menu.push(:a_menu, '/', { })
menu.push(:a_menu_2, '/', { })
menu.push(:a_menu_3, '/', { })
end
items_yielded = []
menu_items_for(menu_name) do |item|
items_yielded << item
end
assert_equal 3, items_yielded.size
end
def test_menu_items_for_should_return_all_items
menu_name = :test_menu_items_for_should_return_all_items
Redmine::MenuManager.map menu_name do |menu|
menu.push(:a_menu, '/', { })
menu.push(:a_menu_2, '/', { })
menu.push(:a_menu_3, '/', { })
end
items = menu_items_for(menu_name)
assert_equal 3, items.size
end
def test_menu_items_for_should_skip_unallowed_items_on_a_project
menu_name = :test_menu_items_for_should_skip_unallowed_items_on_a_project
Redmine::MenuManager.map menu_name do |menu|
menu.push(:a_menu, {:controller => 'issues', :action => 'index' }, { })
menu.push(:a_menu_2, {:controller => 'issues', :action => 'index' }, { })
menu.push(:unallowed, {:controller => 'issues', :action => 'unallowed' }, { })
end
User.current = User.find(2)
items = menu_items_for(menu_name, Project.find(1))
assert_equal 2, items.size
end
def test_menu_items_for_should_skip_items_that_fail_the_conditions
menu_name = :test_menu_items_for_should_skip_items_that_fail_the_conditions
Redmine::MenuManager.map menu_name do |menu|
menu.push(:a_menu, {:controller => 'issues', :action => 'index' }, { })
menu.push(:unallowed,
{:controller => 'issues', :action => 'index' },
{ :if => Proc.new { false }})
end
User.current = User.find(2)
items = menu_items_for(menu_name, Project.find(1))
assert_equal 1, items.size
end
end
@@ -0,0 +1,108 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# 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.
require File.dirname(__FILE__) + '/../../../../test_helper'
module RedmineMenuTestHelper
# Helpers
def get_menu_item(menu_name, item_name)
Redmine::MenuManager.items(menu_name).find {|item| item.name == item_name.to_sym}
end
end
class Redmine::MenuManager::MenuItemTest < Test::Unit::TestCase
include RedmineMenuTestHelper
Redmine::MenuManager.map :test_menu do |menu|
menu.push(:parent_menu, '/test', { })
menu.push(:child_menu, '/test', { :parent_menu => :parent_menu})
menu.push(:child2_menu, '/test', { :parent_menu => :parent_menu})
end
context "MenuItem#caption" do
should "be tested"
end
context "MenuItem#html_options" do
should "be tested"
end
# context new menu item
def test_new_menu_item_should_require_a_name
assert_raises ArgumentError do
Redmine::MenuManager::MenuItem.new
end
end
def test_new_menu_item_should_require_an_url
assert_raises ArgumentError do
Redmine::MenuManager::MenuItem.new(:test_missing_url)
end
end
def test_new_menu_item_should_require_the_options
assert_raises ArgumentError do
Redmine::MenuManager::MenuItem.new(:test_missing_options, '/test')
end
end
def test_new_menu_item_with_all_required_parameters
assert Redmine::MenuManager::MenuItem.new(:test_good_menu, '/test', {})
end
def test_new_menu_item_should_require_a_proc_to_use_for_the_if_condition
assert_raises ArgumentError do
Redmine::MenuManager::MenuItem.new(:test_error, '/test',
{
:if => ['not_a_proc']
})
end
assert Redmine::MenuManager::MenuItem.new(:test_good_if, '/test',
{
:if => Proc.new{}
})
end
def test_new_menu_item_should_allow_a_hash_for_extra_html_options
assert_raises ArgumentError do
Redmine::MenuManager::MenuItem.new(:test_error, '/test',
{
:html => ['not_a_hash']
})
end
assert Redmine::MenuManager::MenuItem.new(:test_good_html, '/test',
{
:html => { :onclick => 'doSomething'}
})
end
def test_new_should_not_allow_setting_the_parent_menu_item_to_the_current_item
assert_raises ArgumentError do
Redmine::MenuManager::MenuItem.new(:test_error, '/test', { :parent_menu => :test_error })
end
end
def test_has_children
parent_item = get_menu_item(:test_menu, :parent_menu)
assert parent_item.hasChildren?
assert_equal 2, parent_item.children.size
assert_equal get_menu_item(:test_menu, :child_menu), parent_item.children[0]
assert_equal get_menu_item(:test_menu, :child2_menu), parent_item.children[1]
end
end
@@ -0,0 +1,28 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# 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.
require File.dirname(__FILE__) + '/../../../test_helper'
class Redmine::MenuManagerTest < Test::Unit::TestCase
context "MenuManager#map" do
should "be tested"
end
context "MenuManager#items" do
should "be tested"
end
end
+84
View File
@@ -0,0 +1,84 @@
# Redmine - project management software
# Copyright (C) 2006-2009 Jean-Philippe Lang
#
# 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.
require File.dirname(__FILE__) + '/../../test_helper'
module RedmineMenuTestHelper
# Assertions
def assert_number_of_items_in_menu(menu_name, count)
assert Redmine::MenuManager.items(menu_name).size >= count, "Menu has less than #{count} items"
end
def assert_menu_contains_item_named(menu_name, item_name)
assert Redmine::MenuManager.items(menu_name).collect(&:name).include?(item_name.to_sym), "Menu did not have an item named #{item_name}"
end
# Helpers
def get_menu_item(menu_name, item_name)
Redmine::MenuManager.items(menu_name).find {|item| item.name == item_name.to_sym}
end
end
class RedmineTest < Test::Unit::TestCase
include RedmineMenuTestHelper
def test_top_menu
assert_number_of_items_in_menu :top_menu, 5
assert_menu_contains_item_named :top_menu, :home
assert_menu_contains_item_named :top_menu, :my_page
assert_menu_contains_item_named :top_menu, :projects
assert_menu_contains_item_named :top_menu, :administration
assert_menu_contains_item_named :top_menu, :help
end
def test_account_menu
assert_number_of_items_in_menu :account_menu, 4
assert_menu_contains_item_named :account_menu, :login
assert_menu_contains_item_named :account_menu, :register
assert_menu_contains_item_named :account_menu, :my_account
assert_menu_contains_item_named :account_menu, :logout
end
def test_application_menu
assert_number_of_items_in_menu :application_menu, 0
end
def test_admin_menu
assert_number_of_items_in_menu :admin_menu, 0
end
def test_project_menu
assert_number_of_items_in_menu :project_menu, 12
assert_menu_contains_item_named :project_menu, :overview
assert_menu_contains_item_named :project_menu, :activity
assert_menu_contains_item_named :project_menu, :roadmap
assert_menu_contains_item_named :project_menu, :issues
assert_menu_contains_item_named :project_menu, :new_issue
assert_menu_contains_item_named :project_menu, :news
assert_menu_contains_item_named :project_menu, :documents
assert_menu_contains_item_named :project_menu, :wiki
assert_menu_contains_item_named :project_menu, :boards
assert_menu_contains_item_named :project_menu, :files
assert_menu_contains_item_named :project_menu, :repository
assert_menu_contains_item_named :project_menu, :settings
end
def test_new_issue_should_have_root_as_a_parent
new_issue = get_menu_item(:project_menu, :new_issue)
assert_equal :root, new_issue.parent.name
end
end