ama_layout 6.3.0.pre → 6.10.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -0
  3. data/ama_layout.gemspec +22 -20
  4. data/app/assets/javascripts/ama_layout/desktop/foundation-custom.js +10 -8
  5. data/app/assets/javascripts/ama_layout/desktop/index.js +1 -0
  6. data/app/assets/javascripts/ama_layout/notifications.coffee +17 -0
  7. data/app/controllers/ama_layout/api/v1/notifications_controller.rb +18 -0
  8. data/app/helpers/ama_layout_path_helper.rb +4 -4
  9. data/app/views/ama_layout/_notification.html.erb +10 -0
  10. data/app/views/ama_layout/_notification_sidebar.html.erb +22 -0
  11. data/app/views/ama_layout/_notifications.html.erb +6 -0
  12. data/app/views/ama_layout/_siteheader.html.erb +8 -3
  13. data/config/routes.rb +9 -0
  14. data/lib/ama_layout.rb +27 -20
  15. data/lib/ama_layout/decorators/navigation_decorator.rb +47 -0
  16. data/lib/ama_layout/decorators/notification_decorator.rb +45 -0
  17. data/lib/ama_layout/navigation.rb +3 -0
  18. data/lib/ama_layout/navigation.yml +58 -6
  19. data/lib/ama_layout/navigation_helper.rb +31 -0
  20. data/lib/ama_layout/notification.rb +89 -0
  21. data/lib/ama_layout/notification_scrubber.rb +13 -0
  22. data/lib/ama_layout/notification_set.rb +140 -0
  23. data/lib/ama_layout/notifications.rb +73 -0
  24. data/lib/ama_layout/notifications/abstract_store.rb +17 -0
  25. data/lib/ama_layout/notifications/redis_store.rb +38 -0
  26. data/lib/ama_layout/version.rb +1 -1
  27. data/spec/ama_layout/controllers/ama_layout/api/v1/notifications_controller_spec.rb +13 -0
  28. data/spec/ama_layout/decorators/navigation_decorator_spec.rb +121 -2
  29. data/spec/ama_layout/decorators/notification_decorator_spec.rb +57 -0
  30. data/spec/ama_layout/navigation_helper_spec.rb +63 -0
  31. data/spec/ama_layout/navigation_spec.rb +13 -43
  32. data/spec/ama_layout/notification_scrubber_spec.rb +10 -0
  33. data/spec/ama_layout/notification_set_spec.rb +281 -0
  34. data/spec/ama_layout/notification_spec.rb +193 -0
  35. data/spec/ama_layout/notifications/abstract_store_spec.rb +23 -0
  36. data/spec/ama_layout/notifications/redis_store_spec.rb +94 -0
  37. data/spec/ama_layout/notifications_spec.rb +109 -0
  38. data/spec/factories/users.rb +35 -0
  39. data/spec/helpers/ama_layout_path_helper_spec.rb +6 -6
  40. data/spec/internal/app/controllers/application_controller.rb +21 -0
  41. data/spec/internal/config/routes.rb +1 -0
  42. data/spec/spec_helper.rb +9 -14
  43. data/spec/support/shared_examples/member_navigation.rb +105 -0
  44. metadata +81 -18
  45. data/styles.scss +0 -0
@@ -0,0 +1,193 @@
1
+ describe AmaLayout::Notification do
2
+ let(:instance) do
3
+ described_class.new(
4
+ header: 'test',
5
+ content: 'test',
6
+ active: true,
7
+ created_at: Time.current
8
+ )
9
+ end
10
+
11
+ describe '#initialize' do
12
+ let(:lifespan) { 1.day }
13
+
14
+ subject do
15
+ described_class.new(
16
+ header: 'test',
17
+ 'content': 'test',
18
+ 'active': true,
19
+ 'created_at': time,
20
+ 'lifespan': lifespan
21
+ )
22
+ end
23
+
24
+ context 'with a time created_at attribute' do
25
+ let(:time) { Time.current }
26
+
27
+ it 'accepts both symbols and strings as hash keys' do
28
+ expect(subject.header).to eq('test')
29
+ expect(subject.active).to be true
30
+ end
31
+
32
+ it 'sets a version by default' do
33
+ expect(subject.version).to eq(described_class::FORMAT_VERSION)
34
+ end
35
+ end
36
+
37
+ context 'with a string created_at attribute' do
38
+ let(:time) { '1984-01-01' }
39
+
40
+ it 'parses the string as a time' do
41
+ expect(subject.created_at).to eq(Time.zone.parse(time))
42
+ end
43
+ end
44
+
45
+ context 'with an Integer/Fixnum lifespan attribute' do
46
+ let(:time) { Time.current }
47
+ let(:lifespan) { 3600 }
48
+
49
+ it 'parses the integer to a duration of seconds' do
50
+ expect(subject.lifespan).to eq(lifespan.seconds)
51
+ end
52
+ end
53
+
54
+ context 'with an invalid type attribute' do
55
+ let(:parameters) do
56
+ {
57
+ type: :invalid,
58
+ header: 'test',
59
+ content: 'test',
60
+ created_at: Time.current,
61
+ active: true
62
+ }
63
+ end
64
+
65
+ it 'raises ArgumentError' do
66
+ expect { described_class.new(parameters) }.to raise_error(ArgumentError)
67
+ end
68
+ end
69
+ end
70
+
71
+ describe '#<=>' do
72
+ let(:old) do
73
+ described_class.new(
74
+ header: 'test',
75
+ content: 'test',
76
+ active: true,
77
+ created_at: Date.new(1984)
78
+ )
79
+ end
80
+ let(:new) do
81
+ described_class.new(
82
+ header: 'test',
83
+ content: 'test',
84
+ active: true,
85
+ created_at: Date.new(2017)
86
+ )
87
+ end
88
+
89
+ it 'sorts by created_at date' do
90
+ expect(old <=> new).to eq(-1)
91
+ end
92
+ end
93
+
94
+ describe '#active?' do
95
+ subject do
96
+ described_class.new(
97
+ header: 'test',
98
+ content: 'test',
99
+ active: active,
100
+ created_at: Time.current
101
+ )
102
+ end
103
+
104
+ context 'when active' do
105
+ let(:active) { true }
106
+
107
+ it 'returns true' do
108
+ expect(subject.active?).to be true
109
+ end
110
+ end
111
+
112
+ context 'when inactive' do
113
+ let(:active) { false }
114
+
115
+ it 'returns false' do
116
+ expect(subject.active?).to be false
117
+ end
118
+ end
119
+ end
120
+
121
+ describe 'dismissed?' do
122
+ subject do
123
+ described_class.new(
124
+ header: 'test',
125
+ content: 'test',
126
+ active: active,
127
+ created_at: Time.current
128
+ )
129
+ end
130
+
131
+ context 'when active' do
132
+ let(:active) { true }
133
+
134
+ it 'returns false' do
135
+ expect(subject.dismissed?).to be false
136
+ end
137
+ end
138
+
139
+ context 'when inactive' do
140
+ let(:active) { false }
141
+
142
+ it 'returns true' do
143
+ expect(subject.dismissed?).to be true
144
+ end
145
+ end
146
+ end
147
+
148
+ describe '#dismiss!' do
149
+ it 'returns true' do
150
+ expect(instance.dismiss!).to be true
151
+ end
152
+
153
+ it 'sets the :active flag to false' do
154
+ instance.dismiss!
155
+ expect(instance.active?).to be false
156
+ end
157
+ end
158
+
159
+ describe '#digest' do
160
+ context 'with the same objects' do
161
+ let(:other) { instance.dup }
162
+
163
+ it 'produces the same digest' do
164
+ expect(instance.digest).to eq(other.digest)
165
+ end
166
+ end
167
+
168
+ context 'with different objects' do
169
+ let(:other) do
170
+ described_class.new(
171
+ header: 'other',
172
+ content: 'other',
173
+ active: true,
174
+ created_at: Time.current
175
+ )
176
+ end
177
+
178
+ it 'produces different digests' do
179
+ expect(instance.digest).to_not eq(other.digest)
180
+ end
181
+ end
182
+ end
183
+
184
+ describe '#to_h' do
185
+ it 'returns a hash' do
186
+ expect(instance.to_h).to be_a(Hash)
187
+ end
188
+
189
+ it 'is not empty' do
190
+ expect(instance.to_h).to_not be_empty
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,23 @@
1
+ describe AmaLayout::Notifications::AbstractStore do
2
+ context 'when inheriting' do
3
+ subject { Class.new(described_class).new }
4
+
5
+ describe '#get' do
6
+ it 'raises NotImplementedError' do
7
+ expect { subject.get('test') }.to raise_error(NotImplementedError)
8
+ end
9
+ end
10
+
11
+ describe '#set' do
12
+ it 'raises NotImplementedError' do
13
+ expect { subject.set('test', 'test') }.to raise_error(NotImplementedError)
14
+ end
15
+ end
16
+
17
+ describe '#delete' do
18
+ it 'raises NotImplementedError' do
19
+ expect { subject.delete('test') }.to raise_error(NotImplementedError)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,94 @@
1
+ describe AmaLayout::Notifications::RedisStore do
2
+ subject do
3
+ described_class.new(
4
+ db: 4,
5
+ namespace: 'test_notifications',
6
+ host: 'localhost'
7
+ )
8
+ end
9
+
10
+ around(:each) do |example|
11
+ subject.clear
12
+ example.run
13
+ subject.clear
14
+ end
15
+
16
+ describe '#get' do
17
+ context 'when a key is not present' do
18
+ it 'returns nil' do
19
+ expect(subject.get('missing')).to be nil
20
+ end
21
+ end
22
+
23
+ context 'when a key is present' do
24
+ before(:each) do
25
+ subject.set('key', 'value')
26
+ end
27
+
28
+ it 'returns the value' do
29
+ expect(subject.get('key')).to eq('value')
30
+ end
31
+ end
32
+
33
+ context 'with a default value' do
34
+ it 'sets a nil key to the default value' do
35
+ subject.get('missing', default: 'test')
36
+ expect(subject.get('missing')).to eq('test')
37
+ end
38
+ end
39
+ end
40
+
41
+ describe '#set' do
42
+ it 'sets the value for a given key' do
43
+ subject.set('test', 'value')
44
+ expect(subject.get('test')).to eq('value')
45
+ end
46
+
47
+ it 'returns true' do
48
+ expect(subject.set('test', 'value')).to be true
49
+ end
50
+ end
51
+
52
+ describe '#delete' do
53
+ context 'when a value is deleted' do
54
+ before(:each) do
55
+ subject.set('key', 'value')
56
+ end
57
+
58
+ it 'returns true' do
59
+ expect(subject.delete('key')).to be true
60
+ end
61
+
62
+ it 'deletes the key' do
63
+ subject.delete('key')
64
+ expect(subject.get('key')).to be nil
65
+ end
66
+ end
67
+
68
+ context 'when a value is not deleted' do
69
+ it 'returns false' do
70
+ expect(subject.delete('key')).to be false
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#transaction' do
76
+ it 'does not commit if an exception is raised' do
77
+ begin
78
+ subject.transaction do |store|
79
+ store.set('key', 'value')
80
+ raise StandardError
81
+ end
82
+ rescue StandardError
83
+ end
84
+ expect(subject.get('key')).to be nil
85
+ end
86
+
87
+ it 'commits to redis successfully' do
88
+ subject.transaction do |store|
89
+ store.set('key', 'value')
90
+ end
91
+ expect(subject.get('key')).to eq('value')
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,109 @@
1
+ describe AmaLayout::Notifications do
2
+ let(:store) do
3
+ AmaLayout::Notifications::RedisStore.new(
4
+ db: 4,
5
+ namespace: 'test_notifications',
6
+ host: 'localhost'
7
+ )
8
+ end
9
+ let(:json) do
10
+ <<-JSON
11
+ {
12
+ "02ac263cea5660e9f9020cb46e93772ed7755f2a60c40ad8961d2a15c1f99e6f": {
13
+ "type": "notice",
14
+ "header": "test",
15
+ "content": "test",
16
+ "created_at": "2017-06-19T06:00:00.000Z",
17
+ "active": true,
18
+ "lifespan": 31557600,
19
+ "version": "1.0.0"
20
+ }
21
+ }
22
+ JSON
23
+ end
24
+
25
+ around(:each) do |example|
26
+ Timecop.freeze(Time.zone.local(2017, 6, 19)) do
27
+ store.clear
28
+ example.run
29
+ store.clear
30
+ end
31
+ end
32
+
33
+ context 'when including module' do
34
+ let(:klass) { Class.new.include(described_class) }
35
+
36
+ context 'class methods' do
37
+ before(:each) do
38
+ klass.class_eval do
39
+ notification_store AmaLayout::Notifications::RedisStore.new(
40
+ db: 4,
41
+ namespace: 'test_notifications',
42
+ host: 'localhost'
43
+ )
44
+ notification_foreign_key :my_id
45
+
46
+ def my_id
47
+ @id ||= SecureRandom.uuid
48
+ end
49
+ end
50
+ end
51
+
52
+ describe '#_notification_foreign_key' do
53
+ it 'returns the id method as a proc' do
54
+ expect(klass._notification_foreign_key).to be_a(Proc)
55
+ end
56
+ end
57
+
58
+ describe '#_notification_store' do
59
+ it 'returns the set data store' do
60
+ expect(klass._notification_store).to be_an(AmaLayout::Notifications::RedisStore)
61
+ end
62
+ end
63
+ end
64
+
65
+ context 'instance methods' do
66
+ context 'with a valid notification store' do
67
+ subject { klass.new }
68
+
69
+ before(:each) do
70
+ klass.class_eval do
71
+ notification_store AmaLayout::Notifications::RedisStore.new(
72
+ db: 4,
73
+ namespace: 'test_notifications',
74
+ host: 'localhost'
75
+ )
76
+ notification_foreign_key :my_id
77
+
78
+ def my_id
79
+ @id ||= SecureRandom.uuid
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#notifications' do
85
+ before(:each) do
86
+ store.set(subject.my_id, json)
87
+ end
88
+
89
+ it 'fetches notifications from data store' do
90
+ expect(subject.notifications.size).to eq(1)
91
+ end
92
+ end
93
+
94
+ describe '#notifications=' do
95
+ it 'resets the notifications to nil' do
96
+ subject.notifications = nil
97
+ expect(subject.notifications).to be_empty
98
+ end
99
+ end
100
+ end
101
+
102
+ context 'with an undefined notification store' do
103
+ it 'raises InvalidNotificationStore' do
104
+ expect { klass.new.notifications }.to raise_error(AmaLayout::Notifications::InvalidNotificationStore)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,35 @@
1
+ FactoryGirl.define do
2
+ factory :user, class: Class.new(OpenStruct) do
3
+ in_renewal false
4
+ member_type 'P'
5
+ renew_type 'R'
6
+ status 'A'
7
+ has_outstanding_balance false
8
+ member? true
9
+
10
+ trait :non_member do
11
+ member? false
12
+ end
13
+
14
+ trait :with_accr do
15
+ renew_type 'A'
16
+ end
17
+
18
+ trait :with_mpp do
19
+ renew_type 'M'
20
+ end
21
+
22
+ trait :in_renewal do
23
+ in_renewal true
24
+ end
25
+
26
+ trait :in_renewal_late do
27
+ in_renewal true
28
+ status 'AL'
29
+ end
30
+
31
+ trait :outstanding_balance do
32
+ has_outstanding_balance true
33
+ end
34
+ end
35
+ end