|
2 | 2 |
|
3 | 3 | require 'rails_helper' |
4 | 4 |
|
5 | | -# Specs in this file have access to a helper object that includes |
6 | | -# the HubHelper. For example: |
7 | | -# |
8 | | -# describe HubHelper do |
9 | | -# describe "string concat" do |
10 | | -# it "concats two strings with spaces" do |
11 | | -# expect(helper.concat_strings("this","that")).to eq("this that") |
12 | | -# end |
13 | | -# end |
14 | | -# end |
15 | 5 | module BetterTogether |
16 | 6 | RSpec.describe HubHelper do |
17 | | - it 'exists' do |
18 | | - expect(described_class).to be # rubocop:todo RSpec/Be |
| 7 | + describe '#activities' do |
| 8 | + let(:user) { create(:user) } |
| 9 | + let(:page) { create(:page) } |
| 10 | + |
| 11 | + before do |
| 12 | + allow(helper).to receive(:current_user).and_return(user) |
| 13 | + # Create some test activities |
| 14 | + create_list(:activity, 3, owner: user.person, trackable: page) |
| 15 | + end |
| 16 | + |
| 17 | + it 'returns scoped activities based on policy' do |
| 18 | + activities = helper.activities |
| 19 | + expect(activities).to be_a(ActiveRecord::Relation) |
| 20 | + end |
| 21 | + |
| 22 | + it 'uses ActivityPolicy::Scope to filter activities' do |
| 23 | + expect(BetterTogether::ActivityPolicy::Scope).to receive(:new) |
| 24 | + .with(user, PublicActivity::Activity) |
| 25 | + .and_call_original |
| 26 | + |
| 27 | + helper.activities |
| 28 | + end |
| 29 | + end |
| 30 | + |
| 31 | + describe '#timeago' do |
| 32 | + let(:test_time) { Time.zone.parse('2025-11-24 12:00:00 UTC') } |
| 33 | + |
| 34 | + context 'with valid time' do |
| 35 | + it 'generates abbr tag with timeago class' do |
| 36 | + result = helper.timeago(test_time) |
| 37 | + expect(result).to have_css('abbr.timeago') |
| 38 | + end |
| 39 | + |
| 40 | + it 'includes ISO8601 formatted time in title attribute' do |
| 41 | + result = helper.timeago(test_time) |
| 42 | + expect(result).to have_css("abbr[title='#{test_time.getutc.iso8601}']") |
| 43 | + end |
| 44 | + |
| 45 | + it 'displays time string as content' do |
| 46 | + result = helper.timeago(test_time) |
| 47 | + expect(result).to include(test_time.to_s) |
| 48 | + end |
| 49 | + |
| 50 | + it 'accepts custom CSS class' do |
| 51 | + result = helper.timeago(test_time, class: 'custom-class') |
| 52 | + expect(result).to have_css('abbr.custom-class') |
| 53 | + end |
| 54 | + |
| 55 | + it 'merges custom options with defaults' do |
| 56 | + result = helper.timeago(test_time, class: 'custom', id: 'my-time') |
| 57 | + expect(result).to have_css('abbr#my-time.custom') |
| 58 | + end |
| 59 | + end |
| 60 | + |
| 61 | + context 'with nil time' do |
| 62 | + it 'returns nil' do |
| 63 | + expect(helper.timeago(nil)).to be_nil |
| 64 | + end |
| 65 | + end |
| 66 | + |
| 67 | + context 'with different time zones' do |
| 68 | + it 'converts to UTC for title' do |
| 69 | + tokyo_time = Time.zone.parse('2025-11-24 21:00:00 +0900') |
| 70 | + result = helper.timeago(tokyo_time) |
| 71 | + utc_time = tokyo_time.getutc.iso8601 |
| 72 | + expect(result).to have_css("abbr[title='#{utc_time}']") |
| 73 | + end |
| 74 | + end |
| 75 | + end |
| 76 | + |
| 77 | + describe '#whose?' do |
| 78 | + let(:user) { create(:user) } |
| 79 | + let(:other_user) { create(:user) } |
| 80 | + let(:page) { create(:page, creator: user.person) } |
| 81 | + let(:other_page) { create(:page, creator: other_user.person) } |
| 82 | + |
| 83 | + context 'when user owns the object' do |
| 84 | + it 'returns "his"' do |
| 85 | + result = helper.whose?(user, page) |
| 86 | + expect(result).to eq('his') |
| 87 | + end |
| 88 | + end |
| 89 | + |
| 90 | + context 'when user does not own the object' do |
| 91 | + it 'returns owner nickname with possessive' do |
| 92 | + result = helper.whose?(user, other_page) |
| 93 | + expect(result).to eq("#{other_user.person.nickname}'s") |
| 94 | + end |
| 95 | + end |
| 96 | + |
| 97 | + context 'when user is nil' do |
| 98 | + it 'returns empty string' do |
| 99 | + result = helper.whose?(nil, page) |
| 100 | + expect(result).to eq('') |
| 101 | + end |
| 102 | + end |
| 103 | + |
| 104 | + context 'when object has no owner' do |
| 105 | + let(:orphaned_page) { build(:page, creator: nil) } |
| 106 | + |
| 107 | + it 'returns empty string' do |
| 108 | + result = helper.whose?(user, orphaned_page) |
| 109 | + expect(result).to eq('') |
| 110 | + end |
| 111 | + end |
| 112 | + |
| 113 | + context 'when both user and owner are nil' do |
| 114 | + it 'returns empty string' do |
| 115 | + result = helper.whose?(nil, build(:page, creator: nil)) |
| 116 | + expect(result).to eq('') |
| 117 | + end |
| 118 | + end |
| 119 | + end |
| 120 | + |
| 121 | + describe '#link_to_trackable' do |
| 122 | + context 'when object exists' do |
| 123 | + let(:page) { create(:page, title: 'Test Page') } |
| 124 | + |
| 125 | + it 'returns link to the object' do |
| 126 | + result = helper.link_to_trackable(page, 'Page') |
| 127 | + expect(result).to include(page.title) |
| 128 | + expect(result).to have_link(page.title) |
| 129 | + end |
| 130 | + |
| 131 | + it 'includes model name as prefix' do |
| 132 | + result = helper.link_to_trackable(page, 'Page') |
| 133 | + expect(result).to include(page.class.model_name.human) |
| 134 | + end |
| 135 | + |
| 136 | + it 'uses object.url if available' do |
| 137 | + allow(page).to receive(:url).and_return('/custom-url') |
| 138 | + result = helper.link_to_trackable(page, 'Page') |
| 139 | + expect(result).to have_link(page.title, href: '/custom-url') |
| 140 | + end |
| 141 | + |
| 142 | + it 'falls back to object itself for URL' do |
| 143 | + # Remove url method to test fallback |
| 144 | + allow(page).to receive(:respond_to?).with(:url).and_return(false) |
| 145 | + result = helper.link_to_trackable(page, 'Page') |
| 146 | + expect(result).to be_present |
| 147 | + end |
| 148 | + |
| 149 | + it 'adds text-decoration-none class to link' do |
| 150 | + result = helper.link_to_trackable(page, 'Page') |
| 151 | + expect(result).to have_css('a.text-decoration-none') |
| 152 | + end |
| 153 | + end |
| 154 | + |
| 155 | + context 'when object is nil' do |
| 156 | + it 'returns message about deleted object' do |
| 157 | + result = helper.link_to_trackable(nil, 'Post') |
| 158 | + expect(result).to eq('a post which does not exist anymore') |
| 159 | + end |
| 160 | + |
| 161 | + it 'downcases object type' do |
| 162 | + result = helper.link_to_trackable(nil, 'ARTICLE') |
| 163 | + expect(result).to eq('a article which does not exist anymore') |
| 164 | + end |
| 165 | + end |
| 166 | + |
| 167 | + context 'with different object types' do |
| 168 | + it 'handles different model types' do |
| 169 | + community = create(:community, name: 'Test Community') |
| 170 | + result = helper.link_to_trackable(community, 'Community') |
| 171 | + expect(result).to include(community.class.model_name.human) |
| 172 | + expect(result).to have_link(community.name) |
| 173 | + end |
| 174 | + end |
| 175 | + end |
| 176 | + |
| 177 | + describe 'helper integration' do |
| 178 | + it 'includes all expected methods' do |
| 179 | + expect(helper).to respond_to(:activities) |
| 180 | + expect(helper).to respond_to(:timeago) |
| 181 | + expect(helper).to respond_to(:whose?) |
| 182 | + expect(helper).to respond_to(:link_to_trackable) |
| 183 | + end |
| 184 | + end |
| 185 | + |
| 186 | + describe 'edge cases' do |
| 187 | + describe '#timeago with edge times' do |
| 188 | + it 'handles very old dates' do |
| 189 | + old_time = 100.years.ago |
| 190 | + result = helper.timeago(old_time) |
| 191 | + expect(result).to be_present |
| 192 | + expect(result).to have_css('abbr.timeago') |
| 193 | + end |
| 194 | + |
| 195 | + it 'handles future dates' do |
| 196 | + future_time = 10.years.from_now |
| 197 | + result = helper.timeago(future_time) |
| 198 | + expect(result).to be_present |
| 199 | + expect(result).to have_css('abbr.timeago') |
| 200 | + end |
| 201 | + end |
| 202 | + |
| 203 | + describe '#whose? with complex ownership' do |
| 204 | + it 'handles users with special characters in nicknames' do |
| 205 | + user = create(:user) |
| 206 | + special_user = create(:user, person: create(:person, name: "O'Brien")) |
| 207 | + page = create(:page, creator: special_user.person) |
| 208 | + |
| 209 | + result = helper.whose?(user, page) |
| 210 | + expect(result).to include("O'Brien") |
| 211 | + end |
| 212 | + end |
19 | 213 | end |
20 | 214 | end |
21 | 215 | end |
0 commit comments