Fix/hook service to openproject (#9214)
* move specs from legacy * move Hook into OpenProject namespace * replace class variablespull/9217/head
parent
71b330ea80
commit
5cb227796c
@ -1,5 +1,5 @@ |
||||
module OpenProject::XlsExport::Hooks |
||||
class CostReportHook < Redmine::Hook::ViewListener |
||||
class CostReportHook < OpenProject::Hook::ViewListener |
||||
render_on :view_cost_report_toolbar, partial: 'hooks/xls_report/view_cost_report_toolbar' |
||||
end |
||||
end |
||||
|
@ -0,0 +1,117 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 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-2013 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 'spec_helper' |
||||
|
||||
describe HookHelper do |
||||
describe '#call_hook' do |
||||
context 'when called within a controller' do |
||||
let(:test_hook_controller_class) do |
||||
# Also tests that the application controller has the model included |
||||
Class.new(ApplicationController) |
||||
end |
||||
let(:instance) do |
||||
test_hook_controller_class.new.tap do |inst| |
||||
inst.instance_variable_set(:@project, project) |
||||
allow(inst) |
||||
.to receive(:request) |
||||
.and_return(request) |
||||
end |
||||
end |
||||
let(:project) do |
||||
instance_double('Project') |
||||
end |
||||
let(:request) do |
||||
instance_double('ActiveSupport::Request') |
||||
end |
||||
|
||||
it 'adds to the context' do |
||||
allow(OpenProject::Hook) |
||||
.to receive(:call_hook) |
||||
|
||||
instance.call_hook(:some_hook_identifier, {}) |
||||
|
||||
expect(OpenProject::Hook) |
||||
.to have_received(:call_hook) |
||||
.with(:some_hook_identifier, { project: project, |
||||
controller: instance, |
||||
request: request, |
||||
hook_caller: instance }) |
||||
end |
||||
end |
||||
|
||||
context 'when called within a view' do |
||||
let(:test_hook_view_class) do |
||||
# Also tests that the application controller has the model included |
||||
Class.new(ActionView::Base) do |
||||
include HookHelper |
||||
end |
||||
end |
||||
let(:instance) do |
||||
test_hook_view_class |
||||
.new(ActionView::LookupContext.new(Rails.root.join('app/views')), {}, nil) |
||||
.tap do |inst| |
||||
inst.instance_variable_set(:@project, project) |
||||
allow(inst) |
||||
.to receive(:request) |
||||
.and_return(request) |
||||
allow(inst) |
||||
.to receive(:controller) |
||||
.and_return(controller_instance) |
||||
end |
||||
end |
||||
let(:project) do |
||||
instance_double('Project') |
||||
end |
||||
let(:request) do |
||||
instance_double('ActiveSupport::Request') |
||||
end |
||||
let(:controller_instance) do |
||||
instance_double('ApplicationController') |
||||
end |
||||
|
||||
it 'adds to the context' do |
||||
# mimicks having two different classes registered for the hook |
||||
allow(OpenProject::Hook) |
||||
.to receive(:call_hook) |
||||
.and_return(%w[response1 response2]) |
||||
|
||||
expect(instance.call_hook(:some_hook_identifier, {})) |
||||
.to eql "response1 response2" |
||||
|
||||
expect(OpenProject::Hook) |
||||
.to have_received(:call_hook) |
||||
.with(:some_hook_identifier, { project: project, |
||||
controller: controller_instance, |
||||
request: request, |
||||
hook_caller: instance }) |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,273 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 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-2013 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 'spec_helper' |
||||
|
||||
describe OpenProject::Hook do |
||||
let(:test_hook_class) do |
||||
Class.new(OpenProject::Hook::ViewListener) |
||||
end |
||||
let(:test_hook1_class) do |
||||
Class.new(test_hook_class) do |
||||
def view_layouts_base_html_head(_context) |
||||
'Test hook 1 listener.' |
||||
end |
||||
end |
||||
end |
||||
let(:test_hook2_class) do |
||||
Class.new(test_hook_class) do |
||||
def view_layouts_base_html_head(_context) |
||||
'Test hook 2 listener.' |
||||
end |
||||
end |
||||
end |
||||
let(:test_hook3_class) do |
||||
Class.new(test_hook_class) do |
||||
def view_layouts_base_html_head(context) |
||||
"Context keys: #{context.keys.map(&:to_s).sort.join(', ')}." |
||||
end |
||||
end |
||||
end |
||||
let!(:previous_listener_classes) { described_class.listener_classes.dup } |
||||
|
||||
before do |
||||
described_class.clear_listeners |
||||
end |
||||
|
||||
after do |
||||
described_class.clear_listeners |
||||
described_class.instance_variable_set(:@listener_classes, previous_listener_classes) |
||||
end |
||||
|
||||
describe '#add_listeners' do |
||||
context 'when inheriting from the class' do |
||||
it 'is automatically added' do |
||||
expect(described_class.hook_listeners(:view_layouts_base_html_head)) |
||||
.to be_empty |
||||
|
||||
test_hook1_class |
||||
|
||||
expect(described_class.hook_listeners(:view_layouts_base_html_head)) |
||||
.to match_array([test_hook1_class]) |
||||
end |
||||
end |
||||
|
||||
context 'when explicitly adding' do |
||||
let(:test_class) do |
||||
Class.new do |
||||
include Singleton |
||||
|
||||
def view_layouts_base_html_head(_context) |
||||
'Test hook listener.' |
||||
end |
||||
end |
||||
end |
||||
|
||||
it 'adds listeners' do |
||||
described_class.add_listener(test_class) |
||||
expect(described_class.hook_listeners(:view_layouts_base_html_head)) |
||||
.to match_array([test_class]) |
||||
end |
||||
end |
||||
|
||||
context 'when not having the Singleton module included' do |
||||
let(:test_class) do |
||||
Class.new do |
||||
def view_layouts_base_html_head(_context) |
||||
'Test hook listener.' |
||||
end |
||||
end |
||||
end |
||||
|
||||
it 'adds listeners' do |
||||
expect { described_class.add_listener(test_class) } |
||||
.to raise_error ArgumentError |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe '#clear_listeners' do |
||||
before do |
||||
# implicitly adding by class creation |
||||
test_hook1_class |
||||
end |
||||
|
||||
it 'clears the registered listeners' do |
||||
described_class.clear_listeners |
||||
expect(described_class.hook_listeners(:view_layouts_base_html_head)) |
||||
.to be_empty |
||||
end |
||||
end |
||||
|
||||
describe '#call_hook' do |
||||
context 'with a class registered for the hook' do |
||||
before do |
||||
# implicitly adding by class creation |
||||
test_hook1_class |
||||
end |
||||
|
||||
it 'calls the registered method' do |
||||
expect(described_class.call_hook(:view_layouts_base_html_head)) |
||||
.to match_array test_hook1_class.instance.view_layouts_base_html_head(nil) |
||||
end |
||||
end |
||||
|
||||
context 'without a class registered for the hook' do |
||||
it 'calls the registered method' do |
||||
expect(described_class.call_hook(:view_layouts_base_html_head)) |
||||
.to be_empty |
||||
end |
||||
end |
||||
|
||||
context 'with multiple listeners' do |
||||
before do |
||||
# implicitly adding by class creation |
||||
test_hook1_class |
||||
test_hook2_class |
||||
end |
||||
|
||||
it 'calls all registered methods' do |
||||
expect(described_class.call_hook(:view_layouts_base_html_head)) |
||||
.to match_array [test_hook1_class.instance.view_layouts_base_html_head(nil), |
||||
test_hook2_class.instance.view_layouts_base_html_head(nil)] |
||||
end |
||||
end |
||||
|
||||
context 'with a context' do |
||||
let!(:test_hook_context_class) do |
||||
# implicitly adding by class creation |
||||
Class.new(test_hook_class) do |
||||
def view_layouts_base_html_head(context) |
||||
context |
||||
end |
||||
end |
||||
end |
||||
|
||||
let(:context) { { foo: 1, bar: 'a'} } |
||||
|
||||
it 'passes the context through' do |
||||
expect(described_class.call_hook(:view_layouts_base_html_head, **context)) |
||||
.to match_array [context] |
||||
end |
||||
end |
||||
|
||||
context 'with a link rendered in the hooked to method' do |
||||
let!(:test_hook_link_class) do |
||||
# implicitly adding by class creation |
||||
Class.new(test_hook_class) do |
||||
def view_layouts_base_html_head(_context) |
||||
link_to('Work packages', controller: '/work_packages') |
||||
end |
||||
end |
||||
end |
||||
|
||||
it 'renders the link' do |
||||
expect(described_class.call_hook(:view_layouts_base_html_head)) |
||||
.to match_array ['<a href="/work_packages">Work packages</a>'] |
||||
end |
||||
end |
||||
|
||||
context 'when called within a controller' do |
||||
let(:test_hook_controller_class) do |
||||
# Also tests that the application controller has the model included |
||||
Class.new(ApplicationController) |
||||
end |
||||
let!(:test_hook_context_class) do |
||||
# implicitly adding by class creation |
||||
Class.new(test_hook_class) do |
||||
def view_layouts_base_html_head(context) |
||||
context |
||||
end |
||||
end |
||||
end |
||||
let(:instance) do |
||||
test_hook_controller_class.new.tap do |inst| |
||||
inst.instance_variable_set(:@project, project) |
||||
allow(inst) |
||||
.to receive(:request) |
||||
.and_return(request) |
||||
end |
||||
end |
||||
let(:project) do |
||||
instance_double('Project') |
||||
end |
||||
let(:request) do |
||||
instance_double('ActiveSupport::Request') |
||||
end |
||||
|
||||
it 'adds to the context' do |
||||
expect(instance.call_hook(:view_layouts_base_html_head, {})) |
||||
.to match_array [{ project: project, controller: instance, request: request, hook_caller: instance }] |
||||
end |
||||
end |
||||
end |
||||
|
||||
context 'called within email rendering' do |
||||
let!(:test_hook_link_class) do |
||||
# implicitly adding by class creation |
||||
Class.new(test_hook_class) do |
||||
def view_layouts_base_html_head(_context) |
||||
link_to('Work packages', controller: '/work_packages') |
||||
end |
||||
end |
||||
end |
||||
let(:test_hook_controller_class) do |
||||
# Also tests that the application controller has the model included |
||||
Class.new(ApplicationController) |
||||
end |
||||
|
||||
let(:user) { FactoryBot.build_stubbed(:user) } |
||||
let(:author) { FactoryBot.build_stubbed(:user) } |
||||
let(:work_package) do |
||||
FactoryBot.build_stubbed(:work_package, |
||||
type: FactoryBot.build_stubbed(:type), |
||||
status: FactoryBot.build_stubbed(:status)).tap do |wp| |
||||
|
||||
allow(wp) |
||||
.to receive(:reload) |
||||
.and_return(wp) |
||||
end |
||||
end |
||||
let(:journal) { FactoryBot.build_stubbed(:work_package_journal, journable: work_package) } |
||||
let!(:comparison_mail) do |
||||
UserMailer.work_package_added(user, journal, author).deliver_now |
||||
ActionMailer::Base.deliveries.last |
||||
end |
||||
|
||||
it 'does not_change_the_default_url_for_email_notifications' do |
||||
test_hook_controller_class.new.call_hook(:view_layouts_base_html_head) |
||||
|
||||
ActionMailer::Base.deliveries.clear |
||||
UserMailer.work_package_added(user, journal, author).deliver_now |
||||
mail2 = ActionMailer::Base.deliveries.last |
||||
|
||||
assert_equal comparison_mail.text_part.body.encoded, mail2.text_part.body.encoded |
||||
end |
||||
end |
||||
end |
@ -1,39 +0,0 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 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-2013 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_relative '../legacy_spec_helper' |
||||
require 'application_controller' |
||||
|
||||
describe ApplicationController, type: :controller do |
||||
include Redmine::I18n |
||||
|
||||
it 'should call hook mixed in' do |
||||
assert @controller.respond_to?(:call_hook) |
||||
end |
||||
end |
@ -1,182 +0,0 @@ |
||||
#-- encoding: UTF-8 |
||||
|
||||
#-- copyright |
||||
# OpenProject is an open source project management software. |
||||
# Copyright (C) 2012-2021 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-2013 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_relative '../../../legacy_spec_helper' |
||||
|
||||
describe 'Redmine::Hook::Manager' do # FIXME: naming (RSpec-port) |
||||
fixtures :all |
||||
|
||||
# Some hooks that are manually registered in these tests |
||||
class TestHook < Redmine::Hook::ViewListener; end |
||||
|
||||
class TestHook1 < TestHook |
||||
def view_layouts_base_html_head(_context) |
||||
'Test hook 1 listener.' |
||||
end |
||||
end |
||||
|
||||
class TestHook2 < TestHook |
||||
def view_layouts_base_html_head(_context) |
||||
'Test hook 2 listener.' |
||||
end |
||||
end |
||||
|
||||
class TestHook3 < TestHook |
||||
def view_layouts_base_html_head(context) |
||||
"Context keys: #{context.keys.map(&:to_s).sort.join(', ')}." |
||||
end |
||||
end |
||||
|
||||
class TestLinkToHook < TestHook |
||||
def view_layouts_base_html_head(_context) |
||||
link_to('Issues', controller: '/work_packages') |
||||
end |
||||
end |
||||
|
||||
class TestHookHelperController < ActionController::Base |
||||
include HookHelper |
||||
end |
||||
|
||||
class TestHookHelperView < ActionView::Base |
||||
include HookHelper |
||||
end |
||||
|
||||
Redmine::Hook.clear_listeners |
||||
|
||||
before do |
||||
@hook_module = Redmine::Hook |
||||
end |
||||
|
||||
after do |
||||
@hook_module.clear_listeners |
||||
end |
||||
|
||||
it 'should clear_listeners' do |
||||
assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
||||
@hook_module.add_listener(TestHook1) |
||||
@hook_module.add_listener(TestHook2) |
||||
assert_equal 2, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
||||
|
||||
@hook_module.clear_listeners |
||||
assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
||||
end |
||||
|
||||
it 'should add_listener' do |
||||
assert_equal 0, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
||||
@hook_module.add_listener(TestHook1) |
||||
assert_equal 1, @hook_module.hook_listeners(:view_layouts_base_html_head).size |
||||
end |
||||
|
||||
it 'should call_hook' do |
||||
@hook_module.add_listener(TestHook1) |
||||
assert_equal ['Test hook 1 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) |
||||
end |
||||
|
||||
it 'should call_hook_with_context' do |
||||
@hook_module.add_listener(TestHook3) |
||||
assert_equal ['Context keys: bar, controller, foo, hook_caller, project, request.'], |
||||
hook_helper.call_hook(:view_layouts_base_html_head, foo: 1, bar: 'a') |
||||
end |
||||
|
||||
it 'should call_hook_with_multiple_listeners' do |
||||
@hook_module.add_listener(TestHook1) |
||||
@hook_module.add_listener(TestHook2) |
||||
assert_equal ['Test hook 1 listener.', 'Test hook 2 listener.'], hook_helper.call_hook(:view_layouts_base_html_head) |
||||
end |
||||
|
||||
# Context: HookHelper.call_hook default_url |
||||
it 'should call_hook_default_url_options' do |
||||
@hook_module.add_listener(TestLinkToHook) |
||||
|
||||
assert_equal ['<a href="/work_packages">Issues</a>'], hook_helper.call_hook(:view_layouts_base_html_head) |
||||
end |
||||
|
||||
# Context: HookHelper.call_hook |
||||
it 'should call_hook_with_project_added_to_context' do |
||||
@hook_module.add_listener(TestHook3) |
||||
assert_match /project/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] |
||||
end |
||||
|
||||
it 'should call_hook_from_controller_with_controller_added_to_context' do |
||||
@hook_module.add_listener(TestHook3) |
||||
assert_match /controller/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] |
||||
end |
||||
|
||||
it 'should call_hook_from_controller_with_request_added_to_context' do |
||||
@hook_module.add_listener(TestHook3) |
||||
assert_match /request/i, hook_helper.call_hook(:view_layouts_base_html_head)[0] |
||||
end |
||||
|
||||
it 'should call_hook_from_view_with_project_added_to_context' do |
||||
@hook_module.add_listener(TestHook3) |
||||
assert_match /project/i, view_hook_helper.call_hook(:view_layouts_base_html_head) |
||||
end |
||||
|
||||
it 'should call_hook_from_view_with_controller_added_to_context' do |
||||
@hook_module.add_listener(TestHook3) |
||||
assert_match /controller/i, view_hook_helper.call_hook(:view_layouts_base_html_head) |
||||
end |
||||
|
||||
it 'should call_hook_from_view_with_request_added_to_context' do |
||||
@hook_module.add_listener(TestHook3) |
||||
assert_match /request/i, view_hook_helper.call_hook(:view_layouts_base_html_head) |
||||
end |
||||
|
||||
it 'should call_hook_from_view_should_join_responses_with_a_space' do |
||||
@hook_module.add_listener(TestHook1) |
||||
@hook_module.add_listener(TestHook2) |
||||
assert_equal 'Test hook 1 listener. Test hook 2 listener.', |
||||
view_hook_helper.call_hook(:view_layouts_base_html_head) |
||||
end |
||||
|
||||
it 'should call_hook_should_not_change_the_default_url_for_email_notifications' do |
||||
user = User.find(1) |
||||
issue = FactoryBot.create(:work_package) |
||||
|
||||
UserMailer.work_package_added(user, issue.journals.first, user).deliver_now |
||||
mail = ActionMailer::Base.deliveries.last |
||||
|
||||
@hook_module.add_listener(TestLinkToHook) |
||||
hook_helper.call_hook(:view_layouts_base_html_head) |
||||
|
||||
ActionMailer::Base.deliveries.clear |
||||
UserMailer.work_package_added(user, issue.journals.first, user).deliver_now |
||||
mail2 = ActionMailer::Base.deliveries.last |
||||
|
||||
assert_equal mail.text_part.body.encoded, mail2.text_part.body.encoded |
||||
end |
||||
|
||||
def hook_helper |
||||
@hook_helper ||= TestHookHelperController.new |
||||
end |
||||
|
||||
def view_hook_helper |
||||
@view_hook_helper ||= TestHookHelperView.new(ActionView::LookupContext.new(Rails.root.to_s + '/app/views'), {}, nil) |
||||
end |
||||
end |
Loading…
Reference in new issue