Commit 634dc3b59638a5007248fb91db29c2d9051d33a3
1 parent
10e51901
Exists in
master
and in
29 other branches
Search content of a profile
* new controller profile_search * new block profile_search_block * added on style.css background images for mimetypes icons * new route profile/foo/search?q=bar (ActionItem1734)
Showing
24 changed files
with
468 additions
and
7 deletions
Show diff stats
app/controllers/my_profile/profile_design_controller.rb
... | ... | @@ -5,7 +5,7 @@ class ProfileDesignController < BoxOrganizerController |
5 | 5 | protect 'edit_profile_design', :profile |
6 | 6 | |
7 | 7 | def available_blocks |
8 | - blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock ] | |
8 | + blocks = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock ] | |
9 | 9 | |
10 | 10 | # blocks exclusive for organizations |
11 | 11 | if profile.has_members? | ... | ... |
... | ... | @@ -0,0 +1,26 @@ |
1 | +class ProfileSearchController < PublicController | |
2 | + | |
3 | + include SearchHelper | |
4 | + | |
5 | + needs_profile | |
6 | + before_filter :check_access_to_profile | |
7 | + | |
8 | + def index | |
9 | + @q = params[:q].blank? ? '' : params[:q] | |
10 | + @filtered_query = remove_stop_words(@q) | |
11 | + if params[:where] == 'environment' | |
12 | + redirect_to :controller => 'search', :query => @q | |
13 | + else | |
14 | + @results = profile.articles.published.find_by_contents(@filtered_query).paginate(:per_page => 10, :page => params[:page]) | |
15 | + end | |
16 | + end | |
17 | + | |
18 | + protected | |
19 | + | |
20 | + def check_access_to_profile | |
21 | + unless profile.display_info_to?(user) | |
22 | + redirect_to :controller => 'profile', :action => 'index' | |
23 | + end | |
24 | + end | |
25 | + | |
26 | +end | ... | ... |
app/helpers/folder_helper.rb
app/helpers/forms_helper.rb
... | ... | @@ -41,8 +41,7 @@ module FormsHelper |
41 | 41 | the_class << ' ' << html_options[:class] |
42 | 42 | end |
43 | 43 | |
44 | - # FIXME: should be in stylesheet | |
45 | - bt_submit = submit_tag(label, html_options.merge(:style => 'height:28px; cursor:pointer', :class => the_class)) | |
44 | + bt_submit = submit_tag(label, html_options.merge(:class => the_class)) | |
46 | 45 | |
47 | 46 | bt_submit + bt_cancel |
48 | 47 | end | ... | ... |
app/models/article.rb
... | ... | @@ -473,6 +473,10 @@ class Article < ActiveRecord::Base |
473 | 473 | abstract.blank? ? first_paragraph : abstract |
474 | 474 | end |
475 | 475 | |
476 | + def short_lead | |
477 | + truncate sanitize_html(self.lead), 170, '...' | |
478 | + end | |
479 | + | |
476 | 480 | def creator |
477 | 481 | creator_id = versions[0][:last_changed_by_id] |
478 | 482 | creator_id && Profile.find(creator_id) |
... | ... | @@ -493,4 +497,9 @@ class Article < ActiveRecord::Base |
493 | 497 | tag_name.gsub(/[<>]/, '') |
494 | 498 | end |
495 | 499 | |
500 | + def sanitize_html(text) | |
501 | + sanitizer = HTML::FullSanitizer.new | |
502 | + sanitizer.sanitize(text) | |
503 | + end | |
504 | + | |
496 | 505 | end | ... | ... |
... | ... | @@ -0,0 +1,22 @@ |
1 | +class ProfileSearchBlock < Block | |
2 | + | |
3 | + def self.description | |
4 | + _('Display a form to search the profile') | |
5 | + end | |
6 | + | |
7 | + def default_title | |
8 | + _('Profile search') | |
9 | + end | |
10 | + | |
11 | + def content | |
12 | + title = self.title | |
13 | + lambda do | |
14 | + render :file => 'blocks/profile_search', :locals => { :title => title } | |
15 | + end | |
16 | + end | |
17 | + | |
18 | + def editable? | |
19 | + true | |
20 | + end | |
21 | + | |
22 | +end | ... | ... |
app/models/text_article.rb
... | ... | @@ -4,4 +4,13 @@ class TextArticle < Article |
4 | 4 | xss_terminate :only => [ :name, :abstract, :body ], :on => 'validation' |
5 | 5 | |
6 | 6 | include Noosfero::TranslatableContent |
7 | + | |
8 | + def self.icon_name(article = nil) | |
9 | + if article && !article.parent.nil? && article.parent.kind_of?(Blog) | |
10 | + Blog.icon_name | |
11 | + else | |
12 | + Article.icon_name | |
13 | + end | |
14 | + end | |
15 | + | |
7 | 16 | end | ... | ... |
app/models/uploaded_file.rb
... | ... | @@ -47,8 +47,8 @@ class UploadedFile < Article |
47 | 47 | delay_attachment_fu_thumbnails |
48 | 48 | |
49 | 49 | def self.icon_name(article = nil) |
50 | - if article && article.image? | |
51 | - article.public_filename(:icon) | |
50 | + if article | |
51 | + article.image? ? article.public_filename(:icon) : article.mime_type.gsub(/[\/+.]/, '-') | |
52 | 52 | else |
53 | 53 | 'upload-file' |
54 | 54 | end | ... | ... |
app/views/profile/index.rhtml
... | ... | @@ -0,0 +1,5 @@ |
1 | +<li> | |
2 | + <%= link_to article.title, article.view_url, :class => 'result-title ' + icon_for_article(article) %> | |
3 | + <p><%= link_to article.short_lead, article.url, {:class => 'article-details'} %></p> | |
4 | + <div><%= link_to url_for(article.url), article.url, :class => 'article-url' %></div> | |
5 | +</li> | ... | ... |
... | ... | @@ -0,0 +1,5 @@ |
1 | +<li> | |
2 | + <%= link_to article.title, article.view_url, :class => 'result-title ' + icon_for_article(article) %> | |
3 | + <p><%= link_to article.body.to_s, article.url, {:class => 'article-details'} %></p> | |
4 | + <div><%= link_to url_for(article.url), article.url, :class => 'article-url' %></div> | |
5 | +</li> | ... | ... |
... | ... | @@ -0,0 +1,11 @@ |
1 | +<% if article.image? %> | |
2 | + <li class='result-image'> | |
3 | + <%= link_to image_tag(article.public_filename(:thumb), :alt => article.display_title), article.view_url, :class => 'article-details' %> | |
4 | + </li> | |
5 | +<% else %> | |
6 | + <li> | |
7 | + <%= link_to article.title, article.view_url, :class => 'result-title ' + icon_for_article(article) %> | |
8 | + <p><%= link_to article.title, article.short_lead, :class => 'article-details' %></p> | |
9 | + <div><%= link_to url_for(article.url), article.url, :class => 'article-url' %></div> | |
10 | + </li> | |
11 | +<% end %> | ... | ... |
... | ... | @@ -0,0 +1,18 @@ |
1 | +<div id='profile-search-results'> | |
2 | + <h1><%= _("Search results on %s's profile") % profile.short_name %></h1> | |
3 | + | |
4 | + <%= render :partial => 'shared/profile_search_form' %> | |
5 | + | |
6 | + <div class='results-found-message'> | |
7 | + <%= _("%s results found") % @results.total_entries %> | |
8 | + </div> | |
9 | + | |
10 | + <ul class='results-list'> | |
11 | + <% @results.sort_by { |r| r.is_image? ? 0 : 1}.each do |result| %> | |
12 | + <%= render :partial => partial_for_class(result.class), :locals => { :article => result } %> | |
13 | + <% end %> | |
14 | + </ul> | |
15 | + | |
16 | + <%= pagination_links @results %> | |
17 | + | |
18 | +</div> | ... | ... |
... | ... | @@ -0,0 +1,12 @@ |
1 | +<% form_tag( { :controller => 'profile_search', :profile => profile.identifier}, :method => 'get', :class => 'search_form' ) do %> | |
2 | + <div class="search-field"> | |
3 | + <span class="formfield"> | |
4 | + <%= text_field_tag 'q', @q, :title => _("Find %s's content") % profile.short_name %> | |
5 | + </span> | |
6 | + <%= submit_button(:search, _('Search')) %> | |
7 | + <div> | |
8 | + <%= labelled_radio_button __('on profile'), 'where', 'profile', true %> | |
9 | + <%= labelled_radio_button _('on %s') % environment.name, 'where', 'environment', false %> | |
10 | + </div> | |
11 | + </div> | |
12 | +<% end %> | ... | ... |
config/routes.rb
... | ... | @@ -75,6 +75,9 @@ ActionController::Routing::Routes.draw do |map| |
75 | 75 | map.tag 'profile/:profile/tags/:id', :controller => 'profile', :action => 'content_tagged', :id => /.+/, :profile => /#{Noosfero.identifier_format}/ |
76 | 76 | map.tag 'profile/:profile/tags', :controller => 'profile', :action => 'tags', :profile => /#{Noosfero.identifier_format}/ |
77 | 77 | |
78 | + # profile search | |
79 | + map.profile_search 'profile/:profile/search', :controller => 'profile_search', :action => 'index', :profile => /#{Noosfero.identifier_format}/ | |
80 | + | |
78 | 81 | # public profile information |
79 | 82 | map.profile 'profile/:profile/:action/:id', :controller => 'profile', :action => 'index', :id => /.*/, :profile => /#{Noosfero.identifier_format}/ |
80 | 83 | ... | ... |
... | ... | @@ -0,0 +1,66 @@ |
1 | +Feature: search inside a profile | |
2 | + As a noosfero user | |
3 | + I want to search | |
4 | + In order to find stuff from a profile | |
5 | + | |
6 | + Background: | |
7 | + Given the following users | |
8 | + | login | name | | |
9 | + | joaosilva | Joao Silva | | |
10 | + And the following articles | |
11 | + | owner | name | body | | |
12 | + | joaosilva | bees and butterflies | this is an article about bees and butterflies | | |
13 | + | joaosilva | whales and dolphins | this is an article about whales and dolphins | | |
14 | + | |
15 | + Scenario: search on profile | |
16 | + Given I go to Joao Silva's profile | |
17 | + And I fill in "q" with "bees" | |
18 | + And I press "Search" | |
19 | + Then I should see "bees and butterflies" within ".main-block" | |
20 | + And I should not see "whales and dolphins" within ".main-block" | |
21 | + | |
22 | + Scenario: search for event on profile | |
23 | + Given the following events | |
24 | + | owner | name | start_date | | |
25 | + | joaosilva | Group meeting | 2009-10-01 | | |
26 | + | joaosilva | John Doe's birthday | 2009-09-01 | | |
27 | + When I go to Joao Silva's profile | |
28 | + And I fill in "q" with "birthday" | |
29 | + And I press "Search" | |
30 | + Then I should see "John Doe's birthday" within ".main-block" | |
31 | + And I should not see "Group meeting" within ".main-block" | |
32 | + | |
33 | + Scenario: simple search for event on profile search block | |
34 | + Given the following blocks | |
35 | + | owner | type | | |
36 | + | joaosilva | ProfileSearchBlock | | |
37 | + When I go to Joao Silva's profile | |
38 | + And I fill in "q" with "bees" within ".profile-search-block" | |
39 | + And I press "Search" | |
40 | + Then I should see "bees and butterflies" within ".main-block" | |
41 | + | |
42 | + Scenario: not display unpublished articles | |
43 | + Given the following articles | |
44 | + | owner | name | body | published | | |
45 | + | joaosilva | published article | this is a public article | true | | |
46 | + | joaosilva | unpublished article | this is a private article | false | | |
47 | + And I go to Joao Silva's profile | |
48 | + And I fill in "q" with "article" | |
49 | + And I press "Search" | |
50 | + Then I should see "public article" within ".main-block" | |
51 | + And I should not see "private article" within ".main-block" | |
52 | + | |
53 | + Scenario: search on environment | |
54 | + Given I go to Joao Silva's profile | |
55 | + And I fill in "q" with "bees" | |
56 | + And I choose "on Colivre.net" | |
57 | + And I press "Search" | |
58 | + Then I should be on the search page | |
59 | + And I should see "bees and butterflies" within "#search-page" | |
60 | + | |
61 | + Scenario: not display search on private profiles | |
62 | + Given the following users | |
63 | + | login | name | public_profile | | |
64 | + | mariasilva | Maria Silva | false | | |
65 | + And I go to /profile/mariasilva/search | |
66 | + Then I should see "friends only" | ... | ... |
public/designs/icons/tango/style.css
... | ... | @@ -69,7 +69,19 @@ |
69 | 69 | .icon-newupload-file { background-image: url(Tango/16x16/actions/filesave.png) } |
70 | 70 | .icon-slideshow { background-image: url(Tango/16x16/mimetypes/x-office-presentation.png) } |
71 | 71 | .icon-photos { background-image: url(Tango/16x16/devices/camera-photo.png) } |
72 | + | |
72 | 73 | .icon-text-html { background-image: url(Tango/16x16/mimetypes/text-html.png) } |
74 | +.icon-text-plain { background-image: url(Tango/16x16/mimetypes/text-x-generic.png) } | |
75 | +.icon-image-svg-xml { background-image: url(Tango/16x16/mimetypes/image-x-generic.png) } | |
76 | +.icon-application-octet-stream { background-image: url(Tango/16x16/mimetypes/binary.png) } | |
77 | +.icon-application-x-gzip { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-x-gzip.png) } | |
78 | +.icon-application-postscript { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-postscript.png) } | |
79 | +.icon-application-pdf { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-pdf.png) } | |
80 | +.icon-application-ogg { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-ogg.png) } | |
81 | +.icon-video-mpeg { background-image: url(Tango/16x16/mimetypes/video-x-generic.png) } | |
82 | +.icon-application-vnd-oasis-opendocument-text { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.text.png) } | |
83 | +.icon-application-vnd-oasis-opendocument-spreadsheet { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.spreadsheet.png) } | |
84 | +.icon-application-vnd-oasis-opendocument-presentation { background-image: url(Tango/16x16/mimetypes/gnome-mime-application-vnd.oasis.opendocument.presentation.png) } | |
73 | 85 | |
74 | 86 | .icon-media-pause { background-image: url(Tango/16x16/actions/media-playback-pause.png) } |
75 | 87 | .icon-media-play { background-image: url(Tango/16x16/actions/media-playback-start.png) } | ... | ... |
public/stylesheets/application.css
... | ... | @@ -1567,6 +1567,10 @@ input.button { |
1567 | 1567 | input.button { |
1568 | 1568 | background-position: 2px 50%; |
1569 | 1569 | } |
1570 | +#content form input.button.submit { | |
1571 | + height: 28px; | |
1572 | + cursor: pointer; | |
1573 | +} | |
1570 | 1574 | |
1571 | 1575 | .button span { |
1572 | 1576 | display: none; |
... | ... | @@ -4372,6 +4376,98 @@ h1#agenda-title { |
4372 | 4376 | text-align: left; |
4373 | 4377 | } |
4374 | 4378 | |
4379 | +/* * * Profile search * * * * * * * */ | |
4380 | + | |
4381 | +#public-profile-search, | |
4382 | +#profile-search-results form, | |
4383 | +.profile-search-block form { | |
4384 | + padding: 10px; | |
4385 | + margin-bottom: 15px; | |
4386 | + background-color: #E6E6E6; | |
4387 | + -moz-border-radius: 5px; | |
4388 | + -webkit-border-radius: 5px; | |
4389 | +} | |
4390 | + | |
4391 | +#public-profile-search .formfield input { | |
4392 | + width: 400px; | |
4393 | +} | |
4394 | + | |
4395 | +/* * * Profile Search Results * * * * * * * */ | |
4396 | + | |
4397 | +#profile-search-results ul { | |
4398 | + padding-left: 0px; | |
4399 | +} | |
4400 | + | |
4401 | +#profile-search-results form .formfield input { | |
4402 | + width: 395px; | |
4403 | +} | |
4404 | + | |
4405 | +#profile-search-results form .search_form .button { | |
4406 | + margin-top: 5px; | |
4407 | +} | |
4408 | + | |
4409 | +#profile-search-results li { | |
4410 | + list-style: none; | |
4411 | + margin-bottom: 20px; | |
4412 | + clear: left; | |
4413 | +} | |
4414 | + | |
4415 | +#profile-search-results li.result-image { | |
4416 | + float: left; | |
4417 | + clear: none; | |
4418 | + height: 150px; | |
4419 | + margin-right: 10px; | |
4420 | + margin-left: 10px; | |
4421 | +} | |
4422 | + | |
4423 | +#profile-search-results .result-title { | |
4424 | + font-size: 18px; | |
4425 | +} | |
4426 | + | |
4427 | +#profile-search-results p { | |
4428 | + margin-top: 5px; | |
4429 | + margin-bottom: 0px; | |
4430 | +} | |
4431 | + | |
4432 | +#profile-search-results .article-details { | |
4433 | + color: #000; | |
4434 | + text-decoration: none; | |
4435 | +} | |
4436 | + | |
4437 | +#profile-search-results a.article-url { | |
4438 | + text-decoration: none; | |
4439 | + color: #77AA44; | |
4440 | +} | |
4441 | + | |
4442 | +#profile-search-results a.article-url:hover { | |
4443 | + text-decoration: underline; | |
4444 | +} | |
4445 | + | |
4446 | +#profile-search-results .results-list .icon { | |
4447 | + background-repeat: no-repeat; | |
4448 | + background-position: left; | |
4449 | + padding: 0px 0px 3px 20px; | |
4450 | + border: none; | |
4451 | +} | |
4452 | + | |
4453 | +#profile-search-results .results-list .icon:hover { | |
4454 | + background-color: transparent; | |
4455 | +} | |
4456 | + | |
4457 | +#profile-search-results .results-found-message { | |
4458 | + margin-top: 10px; | |
4459 | + font-style: italic; | |
4460 | +} | |
4461 | + | |
4462 | +/* * * Profile search block * * * * * * * */ | |
4463 | + | |
4464 | +.profile-search-block .formfield input { | |
4465 | + width: 100%; | |
4466 | +} | |
4467 | + | |
4468 | +.profile-search-block .button.icon-search { | |
4469 | + display: none; | |
4470 | +} | |
4375 | 4471 | |
4376 | 4472 | /* * * Sub-category stuff * * * * * * * */ |
4377 | 4473 | ... | ... |
test/functional/profile_design_controller_test.rb
... | ... | @@ -5,7 +5,7 @@ class ProfileDesignController; def rescue_action(e) raise e end; end |
5 | 5 | |
6 | 6 | class ProfileDesignControllerTest < Test::Unit::TestCase |
7 | 7 | |
8 | - COMMOM_BLOCKS = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock] | |
8 | + COMMOM_BLOCKS = [ ArticleBlock, TagsBlock, RecentDocumentsBlock, ProfileInfoBlock, LinkListBlock, MyNetworkBlock, FeedReaderBlock, ProfileImageBlock, LocationBlock, SlideshowBlock, ProfileSearchBlock ] | |
9 | 9 | PERSON_BLOCKS = COMMOM_BLOCKS + [FriendsBlock, FavoriteEnterprisesBlock, CommunitiesBlock, EnterprisesBlock ] |
10 | 10 | PERSON_BLOCKS_WITH_MEMBERS = PERSON_BLOCKS + [MembersBlock] |
11 | 11 | PERSON_BLOCKS_WITH_BLOG = PERSON_BLOCKS + [BlogArchivesBlock] | ... | ... |
... | ... | @@ -0,0 +1,106 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | +require 'profile_search_controller' | |
3 | + | |
4 | +# Re-raise errors caught by the controller. | |
5 | +class ProfileSearchController; def rescue_action(e) raise e end; end | |
6 | + | |
7 | +class ProfileSearchControllerTest < Test::Unit::TestCase | |
8 | + def setup | |
9 | + @controller = ProfileSearchController.new | |
10 | + @request = ActionController::TestRequest.new | |
11 | + @response = ActionController::TestResponse.new | |
12 | + | |
13 | + @person = fast_create(Person) | |
14 | + end | |
15 | + attr_reader :person | |
16 | + | |
17 | + should 'filter stop words' do | |
18 | + @controller.expects(:locale).returns('en').at_least_once | |
19 | + get 'index', :profile => person.identifier, :q => 'an article about something' | |
20 | + assert_response :success | |
21 | + assert_template 'index' | |
22 | + assert_equal 'article something', assigns('filtered_query') | |
23 | + end | |
24 | + | |
25 | + should 'espape xss attack' do | |
26 | + @controller.expects(:profile).returns(person).at_least_once | |
27 | + get 'index', :profile => person.identifier, :q => '<wslite>' | |
28 | + assert_no_tag :tag => 'wslite' | |
29 | + end | |
30 | + | |
31 | + should 'render success in search' do | |
32 | + get :index, :profile => person.identifier, :q => 'something not important' | |
33 | + assert_response :success | |
34 | + end | |
35 | + | |
36 | + should 'search for articles' do | |
37 | + article = TextileArticle.create(:name => 'My article', :body => 'Article to test profile search', :profile => person) | |
38 | + | |
39 | + get 'index', :profile => person.identifier, :q => 'article profile' | |
40 | + assert_includes assigns(:results), article | |
41 | + end | |
42 | + | |
43 | + should 'display search results' do | |
44 | + article1 = fast_create(Article, :body => '<p>Article to test profile search</p>', :profile_id => person.id) | |
45 | + article2 = fast_create(Article, :body => '<p>Another article to test profile search</p>', :profile_id => person.id) | |
46 | + | |
47 | + get 'index', :profile => person.identifier, :q => 'article' | |
48 | + | |
49 | + [article1, article2].each do |article| | |
50 | + assert_tag :tag => 'li', :descendant => { :tag => 'a', :content => article.short_lead, :attributes => { :class => /article-details/ }} | |
51 | + end | |
52 | + end | |
53 | + | |
54 | + should 'paginate results listing' do | |
55 | + (1..11).each do |i| | |
56 | + TextileArticle.create!(:name => "Article #{i}", :profile => person, :language => 'en') | |
57 | + end | |
58 | + | |
59 | + get 'index', :profile => person.identifier, :q => 'Article' | |
60 | + | |
61 | + assert_equal 10, assigns(:results).size | |
62 | + assert_tag :tag => 'a', :attributes => { :href => "/profile/#{person.identifier}/search?page=2&q=Article", :rel => 'next' } | |
63 | + end | |
64 | + | |
65 | + should 'display abstract if given' do | |
66 | + article1 = TextileArticle.create(:name => 'Article 1', :abstract => 'Abstract to test', :body => 'Article to test profile search', :profile => person) | |
67 | + article2 = TextileArticle.create(:name => 'Article 2', :body => 'Another article to test profile search', :profile => person) | |
68 | + | |
69 | + get 'index', :profile => person.identifier, :q => 'article profile' | |
70 | + | |
71 | + assert_tag :tag => 'li', :descendant => { :tag => 'a', :content => article1.abstract, :attributes => { :class => /article-details/ }} | |
72 | + assert_no_tag :tag => 'li', :descendant => { :tag => 'a', :content => article1.body, :attributes => { :class => /article-details/ }} | |
73 | + | |
74 | + assert_tag :tag => 'li', :descendant => { :tag => 'a', :content => article2.body, :attributes => { :class => /article-details/ }} | |
75 | + end | |
76 | + | |
77 | + should 'display nothing if search is blank' do | |
78 | + article1 = TextileArticle.create(:name => 'Article 1', :body => 'Article to test profile search', :profile => person) | |
79 | + article2 = TextileArticle.create(:name => 'Article 2', :body => 'Another article to test profile search', :profile => person) | |
80 | + | |
81 | + get 'index', :profile => person.identifier, :q => '' | |
82 | + | |
83 | + assert_no_tag :tag => 'ul', :attributes => { :id => 'profile-search-results'}, :descendant => { :tag => 'li' } | |
84 | + end | |
85 | + | |
86 | + should 'not display private articles' do | |
87 | + article1 = TextileArticle.create(:name => 'Article 1', :body => 'Article to test profile search', :profile => person, :published => false) | |
88 | + article2 = TextileArticle.create(:name => 'Article 2', :body => 'Another article to test profile search', :profile => person) | |
89 | + | |
90 | + get 'index', :profile => person.identifier, :q => 'article profile' | |
91 | + | |
92 | + assert_no_tag :tag => 'li', :descendant => { :tag => 'a', :content => article1.body, :attributes => { :class => /article-details/ }} | |
93 | + | |
94 | + assert_tag :tag => 'li', :descendant => { :tag => 'a', :content => article2.body, :attributes => { :class => /article-details/ }} | |
95 | + end | |
96 | + | |
97 | + should 'display number of results found' do | |
98 | + article1 = TextileArticle.create(:name => 'Article 1', :body => 'Article to test profile search', :body => 'Article to test profile search', :profile => person) | |
99 | + article2 = TextileArticle.create(:name => 'Article 2', :body => 'Another article to test profile search', :profile => person) | |
100 | + | |
101 | + get 'index', :profile => person.identifier, :q => 'article profile' | |
102 | + | |
103 | + assert_tag :tag => 'div', :attributes => { :class => 'results-found-message' }, :content => /2 results found/ | |
104 | + end | |
105 | + | |
106 | +end | ... | ... |
test/unit/article_test.rb
... | ... | @@ -921,6 +921,16 @@ class ArticleTest < Test::Unit::TestCase |
921 | 921 | assert_equal '', a.lead |
922 | 922 | end |
923 | 923 | |
924 | + should 'have short lead' do | |
925 | + a = fast_create(TinyMceArticle, :body => '<p>' + ('a' *180) + '</p>') | |
926 | + assert_equal 170, a.short_lead.length | |
927 | + end | |
928 | + | |
929 | + should 'remove html from short lead' do | |
930 | + a = Article.new(:body => "<p>an article with html that should be <em>removed</em></p>") | |
931 | + assert_equal 'an article with html that should be removed', a.short_lead | |
932 | + end | |
933 | + | |
924 | 934 | should 'track action when a published article is created outside a community' do |
925 | 935 | article = TinyMceArticle.create! :name => 'Tracked Article', :profile_id => profile.id |
926 | 936 | assert article.published? | ... | ... |
... | ... | @@ -0,0 +1,32 @@ |
1 | +require File.dirname(__FILE__) + '/../test_helper' | |
2 | + | |
3 | +class ProfileSearchBlockTest < Test::Unit::TestCase | |
4 | + | |
5 | + should 'describe itself' do | |
6 | + assert_not_equal Block.description, ProfileSearchBlock.description | |
7 | + end | |
8 | + | |
9 | + should 'provide a default title' do | |
10 | + assert_not_equal Block.new.default_title, ProfileSearchBlock.new.default_title | |
11 | + end | |
12 | + | |
13 | + should 'render profile search' do | |
14 | + person = fast_create(Person) | |
15 | + | |
16 | + block = ProfileSearchBlock.new | |
17 | + block.stubs(:owner).returns(person) | |
18 | + | |
19 | + self.expects(:render).with(:file => 'blocks/profile_search', :locals => { :title => block.title}) | |
20 | + instance_eval(& block.content) | |
21 | + end | |
22 | + | |
23 | + should 'provide view_title' do | |
24 | + person = fast_create(Person) | |
25 | + person.boxes << Box.new | |
26 | + block = ProfileSearchBlock.new(:title => 'Title from block') | |
27 | + person.boxes.first.blocks << block | |
28 | + block.save! | |
29 | + assert_equal 'Title from block', block.view_title | |
30 | + end | |
31 | + | |
32 | +end | ... | ... |
test/unit/text_article_test.rb
... | ... | @@ -43,4 +43,14 @@ class TextArticleTest < Test::Unit::TestCase |
43 | 43 | assert_kind_of Noosfero::TranslatableContent, TextArticle.new |
44 | 44 | end |
45 | 45 | |
46 | + should 'return article icon name' do | |
47 | + assert_equal Article.icon_name, TextArticle.icon_name | |
48 | + end | |
49 | + | |
50 | + should 'return blog icon name if the article is a blog post' do | |
51 | + blog = fast_create(Blog) | |
52 | + article = TextArticle.new(:parent => blog) | |
53 | + assert_equal Blog.icon_name, TextArticle.icon_name(article) | |
54 | + end | |
55 | + | |
46 | 56 | end | ... | ... |