#-- copyright # OpenProject is a project management system. # Copyright (C) 2012-2013 the OpenProject Foundation (OPF) # # 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 doc/COPYRIGHT.rdoc for more details. #++ require 'spec_helper' describe ApplicationHelper do include ApplicationHelper include WorkPackagesHelper describe "format_activity_description" do it "truncates given text" do text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lore" format_activity_description(text).size.should == 120 end it "replaces escaped line breaks with html line breaks and should be html_safe" do text = "Lorem ipsum dolor sit \namet, consetetur sadipscing elitr, sed diam nonumy eirmod\r tempor invidunt" text_html = "Lorem ipsum dolor sit
amet, consetetur sadipscing elitr, sed diam nonumy eirmod
tempor invidunt" format_activity_description(text).should == text_html format_activity_description(text).html_safe?.should be_true end it "escapes potentially harmful code" do text = "Lorem ipsum dolor tempor invidunt" format_activity_description(text).include?("lt;script>alert('pwnd');</script>").should be_true end end describe "footer_content" do context "no additional footer content" do before do OpenProject::Footer.content = nil end it { footer_content.should == I18n.t(:text_powered_by, :link => link_to(OpenProject::Info.app_name, OpenProject::Info.url)) } end context "string as additional footer content" do before do OpenProject::Footer.content = nil OpenProject::Footer.add_content("openproject","footer") end it { footer_content.include?(I18n.t(:text_powered_by, :link => link_to(OpenProject::Info.app_name, OpenProject::Info.url))).should be_true } it { footer_content.include?("footer").should be_true } end context "proc as additional footer content" do before do OpenProject::Footer.content = nil OpenProject::Footer.add_content("openproject",Proc.new{Date.parse(Time.now.to_s)}) end it { footer_content.include?("#{Date.parse(Time.now.to_s)}").should be_true } end context "proc which returns nothing" do before do OpenProject::Footer.content = nil OpenProject::Footer.add_content("openproject",Proc.new{"footer" if false}) end it { footer_content.include?("").should be_false } end end describe ".link_to_if_authorized" do let(:project) { FactoryGirl.create :valid_project } let(:project_member) { FactoryGirl.create :user, :member_in_project => project, :member_through_role => FactoryGirl.create(:role, :permissions => [:view_work_packages, :edit_work_packages, :browse_repository, :view_changesets, :view_wiki_pages]) } let(:issue) { FactoryGirl.create :work_package, :project => project, :author => project_member, :type => project.types.first } context "if user is authorized" do before do self.should_receive(:authorize_for).and_return(true) @response = link_to_if_authorized('link_content', { :controller => 'issues', :action => 'show', :id => issue }, :class => 'fancy_css_class') end subject { @response } it { should match /href/ } it { should match /fancy_css_class/ } end context "if user is unauthorized" do before do self.should_receive(:authorize_for).and_return(false) @response = link_to_if_authorized('link_content', { :controller => 'issues', :action => 'show', :id => issue }, :class => 'fancy_css_class') end subject { @response } it { should be_nil } end context "allow using the :controller and :action for the target link" do before do self.should_receive(:authorize_for).and_return(true) @response = link_to_if_authorized("By controller/action", { :controller => 'issues', :action => 'edit', :id => issue.id }) end subject { @response } it { should match /href/ } end end describe ".textilizable" do let(:project) { FactoryGirl.create :valid_project } let(:identifier) { project.identifier } let(:project_member) { FactoryGirl.create :user, :member_in_project => project, :member_through_role => FactoryGirl.create(:role, :permissions => [:view_work_packages, :edit_work_packages, :browse_repository, :view_changesets, :view_wiki_pages]) } let(:issue) { FactoryGirl.create :work_package, :project => project, :author => project_member, :type => project.types.first } before do @project = project User.stubs(:current).returns(project_member) Setting.enabled_scm = Setting.enabled_scm << "Filesystem" unless Setting.enabled_scm.include? "Filesystem" end after do User.unstub(:current) Setting.enabled_scm.delete "Filesystem" end context "Changeset links" do let(:repository) { FactoryGirl.create :repository, :project => project } let(:changeset1) { FactoryGirl.create :changeset, :repository => repository, :comments => 'My very first commit' } let(:changeset2) { FactoryGirl.create :changeset, :repository => repository, :comments => 'This commit fixes #1, #2 and references #1 & #3' } let(:changeset_link) { link_to("r#{changeset1.revision}", {:controller => 'repositories', :action => 'revision', :project_id => identifier, :rev => changeset1.revision}, :class => 'changeset', :title => 'My very first commit') } let(:changeset_link2) { link_to("r#{changeset2.revision}", {:controller => 'repositories', :action => 'revision', :project_id => identifier, :rev => changeset2.revision}, :class => 'changeset', :title => 'This commit fixes #1, #2 and references #1 & #3') } before do project.repository = repository end context "Single link" do subject { textilizable("r#{changeset1.revision}") } it { should eq("

#{changeset_link}

") } end context "Single link with dot" do subject { textilizable("r#{changeset1.revision}.") } it { should eq("

#{changeset_link}.

") } end context "Two links comma separated" do subject { textilizable("r#{changeset1.revision}, r#{changeset2.revision}") } it { should eq("

#{changeset_link}, #{changeset_link2}

") } end context "Single link comma separated without a space" do subject { textilizable("r#{changeset1.revision},r#{changeset2.revision}") } it { should eq("

#{changeset_link},#{changeset_link2}

") } end context "Escaping" do subject { textilizable("!r#{changeset1.id}") } it { should eq("

r#{changeset1.id}

") } end end context "Version link" do let(:version) { FactoryGirl.create :version, :name => '1.0', :project => project } let(:version_link) { link_to('1.0', {:controller => 'versions', :action => 'show', :id => version.id}, :class => 'version') } context "Link with version id" do subject { textilizable("version##{version.id}") } it { should eq("

#{version_link}

") } end context "Link with version" do subject { textilizable("version:1.0") } it { should eq("

#{version_link}

") } end context "Link with quoted version" do subject { textilizable('version:"1.0"') } it { should eq("

#{version_link}

") } end context "Escaping link with version id" do subject { textilizable("!version##{version.id}") } it { should eq("

version##{version.id}

") } end context "Escaping link with version" do subject { textilizable("!version:1.0") } it { should eq("

version:1.0

") } end context "Escaping link with quoted version" do subject { textilizable('!version:"1.0"') } it { should eq('

version:"1.0"

') } end end context "Message links" do let(:board) { FactoryGirl.create :board, :project => project } let(:message1) { FactoryGirl.create :message, :board => board } let(:message2) { FactoryGirl.create :message, :board => board, :parent => message1 } before do message1.reload @message_url = {:controller => 'messages', :action => 'show', :board_id => board.id, :id => message1.id} end context "Plain message" do subject { textilizable("message##{message1.id}") } it { should eq("

#{link_to(message1.subject, @message_url, :class => 'message')}

") } end context "Message with parent" do subject { textilizable("message##{message2.id}") } it { should eq("

#{link_to(message2.subject, @message_url.merge(:anchor => "message-#{message2.id}", :r => message2.id), :class => 'message')}

") } end end context "Issue links" do let(:issue_link) { link_to("##{issue.id}", work_package_path(issue), :class => 'issue work_package status-3 priority-1 created-by-me', :title => "#{issue.subject} (#{issue.status})") } context "Plain issue link" do subject { textilizable("##{issue.id}, [##{issue.id}], (##{issue.id}) and ##{issue.id}.") } it { should eq("

#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.

") } end context "Plain issue link to non-existing element" do subject { textilizable('#0123456789') } it { should eq('

#0123456789

') } end context "Escaping issue link" do subject { textilizable("!##{issue.id}.") } it { should eq("

##{issue.id}.

") } end context "Cyclic Description Links" do let(:issue2) { FactoryGirl.create :work_package, :project => project, :author => project_member, :type => project.types.first } before do issue2.description = "####{issue.id}" issue2.save! issue.description = "####{issue2.id}" issue.save! end subject { textilizable issue, :description } it "doesn't replace description links with a cycle" do expect(subject).to match("###{issue.id}") end end context "Description links" do subject { textilizable issue, :description } it "replaces the macro with the issue description" do expect(subject).to eq("

#{issue.description}

") end end end context "Project links" do let(:subproject) { FactoryGirl.create :valid_project, :parent => project, :is_public => true } let(:project_url) { {:controller => 'projects', :action => 'show', :id => subproject.identifier} } context "Plain project link" do subject { textilizable("project##{subproject.id}") } it { should eq("

#{link_to(subproject.name, project_url, :class => 'project')}

") } end context "Plain project link via identifier" do subject { textilizable("project:#{subproject.identifier}") } it { should eq("

#{link_to(subproject.name, project_url, :class => 'project')}

") } end context "Plain project link via name" do subject { textilizable("project:\"#{subproject.name}\"") } it { should eq("

#{link_to(subproject.name, project_url, :class => 'project')}

") } end end context "Url links" do subject { textilizable("http://foo.bar/FAQ#3") } it { should eq('

http://foo.bar/FAQ#3

') } end context "Wiki links" do let(:project_2) { FactoryGirl.create :valid_project, :identifier => 'onlinestore' } let(:wiki_1) { FactoryGirl.create :wiki, :start_page => "CookBook documentation", :project => project } let(:wiki_page_1_1) { FactoryGirl.create :wiki_page_with_content, :wiki => wiki_1, :title => "CookBook_documentation" } let(:wiki_page_1_2) { FactoryGirl.create :wiki_page_with_content, :wiki => wiki_1, :title => "Another page" } before do project_2.reload wiki_page_2_1 = FactoryGirl.create :wiki_page_with_content, :wiki => project_2.wiki, :title => "Start_page" project_2.wiki.pages << wiki_page_2_1 project_2.wiki.start_page = "Start Page" project_2.wiki.save! project.wiki = wiki_1 wiki_1.pages << wiki_page_1_1 wiki_1.pages << wiki_page_1_2 end context "Plain wiki link" do subject { textilizable('[[CookBook documentation]]') } it { should eq("

CookBook documentation

") } end context "Plain wiki page link" do subject { textilizable('[[Another page|Page]]') } it { should eq("

Page

") } end context "Wiki link with anchor" do subject { textilizable('[[CookBook documentation#One-section]]') } it { should eq("

CookBook documentation

") } end context "Wiki page link with anchor" do subject { textilizable('[[Another page#anchor|Page]]') } it { should eq("

Page

") } end context "Wiki link to an unknown page" do subject { textilizable('[[Unknown page]]') } it { should eq("

Unknown page

") } end context "Wiki page link to an unknown page" do subject { textilizable('[[Unknown page|404]]') } it { should eq("

404

") } end context "Link to another project's wiki" do subject { textilizable('[[onlinestore:]]') } it { should eq("

onlinestore

") } end context "Link to another project's wiki with label" do subject { textilizable('[[onlinestore:|Wiki]]') } it { should eq("

Wiki

") } end context "Link to another project's wiki page" do subject { textilizable('[[onlinestore:Start page]]') } it { should eq("

Start page

") } end context "Link to another project's wiki page with label" do subject { textilizable('[[onlinestore:Start page|Text]]') } it { should eq("

Text

") } end context "Link to an unknown wiki page in another project" do subject { textilizable('[[onlinestore:Unknown page]]') } it { should eq("

Unknown page

") } end context "Striked through link to wiki page" do subject { textilizable('-[[Another page|Page]]-') } it { should eql("

Page

") } end context "Named striked through link to wiki page" do subject { textilizable('-[[Another page|Page]] link-') } it { should eql("

Page link

") } end context "Escaped link to wiki page" do subject { textilizable('![[Another page|Page]]') } it { should eql('

[[Another page|Page]]

') } end context "Link to wiki of non-existing project" do subject { textilizable('[[unknowproject:Start]]') } it { should eql('

[[unknowproject:Start]]

') } end context "Link to wiki page of non-existing project" do subject { textilizable('[[unknowproject:Start|Page title]]') } it { should eql('

[[unknowproject:Start|Page title]]

') } end end context "Redmine links" do let(:repository) { FactoryGirl.create :repository, :project => project } let(:source_url) { {:controller => 'repositories', :action => 'entry', :project_id => identifier, :path => 'some/file'} } let(:source_url_with_ext) { {:controller => 'repositories', :action => 'entry', :project_id => identifier, :path => 'some/file.ext'} } before do project.repository = repository @to_test = { # source 'source:/some/file' => link_to('source:/some/file', source_url, :class => 'source'), 'source:/some/file.' => link_to('source:/some/file', source_url, :class => 'source') + ".", 'source:/some/file.ext.' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".", 'source:/some/file. ' => link_to('source:/some/file', source_url, :class => 'source') + ".", 'source:/some/file.ext. ' => link_to('source:/some/file.ext', source_url_with_ext, :class => 'source') + ".", 'source:/some/file, ' => link_to('source:/some/file', source_url, :class => 'source') + ",", 'source:/some/file@52' => link_to('source:/some/file@52', source_url.merge(:rev => 52), :class => 'source'), 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'), 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'), 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'), 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'), 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'), # escaping '!source:/some/file' => 'source:/some/file', # invalid expressions 'source:' => 'source:' } end it "" do @to_test.each do |text, result| textilizable(text).should eql("

#{result}

") end end end context "Pre content should not parse wiki and redmine links" do let(:wiki) { FactoryGirl.create :wiki, :start_page => "CookBook documentation", :project => project } let(:wiki_page) { FactoryGirl.create :wiki_page_with_content, :wiki => wiki, :title => "CookBook_documentation" } let(:raw) { <<-RAW [[CookBook documentation]] ##{issue.id}
[[CookBook documentation]]

##{issue.id}
RAW } let(:expected) { <<-EXPECTED

CookBook documentation

##{issue.id}

[[CookBook documentation]]

##{issue.id}
EXPECTED } before do project.wiki = wiki wiki.pages << wiki_page end subject { textilizable(raw).gsub(%r{[\r\n\t]}, '')} it { should eql(expected.gsub(%r{[\r\n\t]}, ''))} end end describe "other_formats_links" do context "link given" do before do @links = other_formats_links{|f| f.link_to 'Atom', :url => {:controller => :projects, :action => :index} } end it { @links.should == "

Also available in:Atom

"} end context "link given but disabled" do before do Setting.stub(:feeds_enabled?).and_return(false) @links = other_formats_links{|f| f.link_to 'Atom', :url => {:controller => :projects, :action => :index} } end it { @links.should be_nil} end end end