diff --git a/app/components/matey/active_users_component.rb b/app/components/matey/active_users_component.rb index 6477594..a773d71 100644 --- a/app/components/matey/active_users_component.rb +++ b/app/components/matey/active_users_component.rb @@ -2,8 +2,7 @@ class Matey::ActiveUsersComponent < Matey::ApplicationComponent def initialize(events:, time_window: 1.week, color_scheme: "neutral") - raise ArgumentError unless events.is_a?(ActiveRecord::Relation) - raise ArgumentError unless time_window.is_a?(Integer) + super(records: events, time_window: time_window) @current_period = events.where(time: time_window.ago..Time.current).pluck(:user_id).uniq.count previous_period = events.where(time: (2 * time_window).ago..time_window.ago).pluck(:user_id).uniq.count diff --git a/app/components/matey/application_component.rb b/app/components/matey/application_component.rb index de90959..b900bee 100644 --- a/app/components/matey/application_component.rb +++ b/app/components/matey/application_component.rb @@ -6,6 +6,12 @@ class Matey::ApplicationComponent < ViewComponent::Base include ActiveModel::Validations include ColorSchemeHelper + def initialize(records:, time_window:) + super + raise ArgumentError unless records.is_a?(ActiveRecord::Relation) + raise ArgumentError unless time_window.is_a?(Integer) + end + def before_render validate! end diff --git a/app/components/matey/browser_os_breakdown_component.rb b/app/components/matey/browser_os_breakdown_component.rb index bc3d856..b8d3bf6 100644 --- a/app/components/matey/browser_os_breakdown_component.rb +++ b/app/components/matey/browser_os_breakdown_component.rb @@ -1,5 +1,7 @@ class Matey::BrowserOsBreakdownComponent < Matey::ApplicationComponent def initialize(visits:, time_window:, color_scheme: "neutral") + super(records: visits, time_window: time_window) + visits_in_time_window = visits.where(started_at: time_window.ago..) @visits_in_time_window = visits_in_time_window.count @browsers = visits_in_time_window.group(:browser).count diff --git a/app/components/matey/daily_active_users_component.rb b/app/components/matey/daily_active_users_component.rb index 8bcbbcb..0c58975 100644 --- a/app/components/matey/daily_active_users_component.rb +++ b/app/components/matey/daily_active_users_component.rb @@ -1,5 +1,7 @@ class Matey::DailyActiveUsersComponent < Matey::ApplicationComponent def initialize(visits:, time_window:, color_scheme: "neutral") + super(records: visits, time_window: time_window) + @visits = visits @time_window = time_window visits_in_time_window = visits.where(started_at: time_window.ago..) diff --git a/app/components/matey/new_activity_component.rb b/app/components/matey/new_activity_component.rb index e2a3e56..88ca3f4 100644 --- a/app/components/matey/new_activity_component.rb +++ b/app/components/matey/new_activity_component.rb @@ -1,7 +1,6 @@ class Matey::NewActivityComponent < Matey::ApplicationComponent def initialize(events:, time_window: 1.week, color_scheme: "neutral") - raise ArgumentError unless events.is_a?(ActiveRecord::Relation) - raise ArgumentError unless time_window.is_a?(Integer) + super(records: events, time_window: time_window) @current_period = events.where(time: time_window.ago..Time.current).count previous_period = events.where(time: (2 * time_window).ago..time_window.ago).pluck(:user_id).count diff --git a/app/components/matey/new_users_component.rb b/app/components/matey/new_users_component.rb index 44ae75d..04f3fc2 100644 --- a/app/components/matey/new_users_component.rb +++ b/app/components/matey/new_users_component.rb @@ -1,5 +1,7 @@ class Matey::NewUsersComponent < Matey::ApplicationComponent def initialize(users:, time_window: 1.week, color_scheme: "neutral") + super(records: users, time_window: time_window) + @current_period = users.where(created_at: time_window.ago..Time.current).count previous_period = users.where(created_at: (2 * time_window).ago..time_window.ago).count diff --git a/app/components/matey/top_events_component.rb b/app/components/matey/top_events_component.rb index 097915d..29a3d5e 100644 --- a/app/components/matey/top_events_component.rb +++ b/app/components/matey/top_events_component.rb @@ -2,8 +2,7 @@ class Matey::TopEventsComponent < Matey::ApplicationComponent def initialize(events:, time_window: 1.week, limit: 5, color_scheme: "neutral") - raise ArgumentError unless events.is_a?(ActiveRecord::Relation) - raise ArgumentError unless time_window.is_a?(Integer) + super(records: events, time_window: time_window) @events = events.where(time: time_window.ago..Time.current).limit(limit).order("count(name) DESC").group(:name).count @time_window = time_window diff --git a/app/components/matey/top_visited_pages_table_component.rb b/app/components/matey/top_visited_pages_table_component.rb index 3d12482..fb0af0c 100644 --- a/app/components/matey/top_visited_pages_table_component.rb +++ b/app/components/matey/top_visited_pages_table_component.rb @@ -1,5 +1,7 @@ class Matey::TopVisitedPagesTableComponent < Matey::ApplicationComponent def initialize(events:, time_window: 1.week, limit: 10, color_scheme: "neutral") + super(records: events, time_window: time_window) + # Group events by controller (:name) and action. Aggregate number of unique user actions @user_count_by_event = events.where(started_at: time_window.ago..).pluck(:landing_page).tally diff --git a/app/components/matey/user_engagement_component.rb b/app/components/matey/user_engagement_component.rb index f2f8e3f..5b7a8ad 100644 --- a/app/components/matey/user_engagement_component.rb +++ b/app/components/matey/user_engagement_component.rb @@ -2,6 +2,8 @@ class Matey::UserEngagementComponent < Matey::ApplicationComponent def initialize(events:, user_id:, time_window: 1.week, limit: 10, color_scheme: "neutral") + super(records: events, time_window: time_window) + @events_for_user = events.where_props(user_id: user_id).where(time: time_window.ago..Time.current).group(:name).count @count_by_event = @events_for_user.sort_by { |event, count| count }.last(limit).reverse @time_window = time_window diff --git a/app/components/matey/visits_by_day_of_week_component.rb b/app/components/matey/visits_by_day_of_week_component.rb index f92db79..2c04883 100644 --- a/app/components/matey/visits_by_day_of_week_component.rb +++ b/app/components/matey/visits_by_day_of_week_component.rb @@ -3,6 +3,8 @@ class Matey::VisitsByDayOfWeekComponent < Matey::ApplicationComponent def initialize(visits:, time_window: 1.month, exclude_days: [], color_scheme: "neutral") + super(records: visits, time_window: time_window) + @visits = visits @time_window = time_window @exclude_days = exclude_days diff --git a/spec/matey/bounce_rate_component_spec.rb b/spec/matey/bounce_rate_component_spec.rb new file mode 100644 index 0000000..a81261e --- /dev/null +++ b/spec/matey/bounce_rate_component_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Matey::BounceRateComponent, type: :component do + context "renders component" do + let(:color_scheme) { "bg-light text-dark border-dark" } + it "with a card element" do + subject = render_inline(Matey::BounceRateComponent.new(events: Ahoy::Event.all, visits: Ahoy::Visit.all)) + + expect(subject.css("div[class='card #{color_scheme}']").to_html).not_to be_empty + end + end + + context "raises an exception when" do + it "events are missing" do + expect { Matey::BounceRateComponent.new }.to raise_error(ArgumentError) + end + + it "events are invalid" do + expect { Matey::BounceRateComponent.new(events: nil) }.to raise_error(ArgumentError) + end + + it "time_window is invalid" do + expect { Matey::BounceRateComponent.new(events: [], time_window: nil) }.to raise_error(ArgumentError) + end + end +end diff --git a/spec/matey/browser_os_breakdown_component_spec.rb b/spec/matey/browser_os_breakdown_component_spec.rb new file mode 100644 index 0000000..5f5a65f --- /dev/null +++ b/spec/matey/browser_os_breakdown_component_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Matey::BrowserOsBreakdownComponent, type: :component do + context "renders component" do + let(:color_scheme) { "bg-light text-dark border-dark" } + it "with a card element" do + subject = render_inline(Matey::BrowserOsBreakdownComponent.new(visits: Ahoy::Visit.all, time_window: 1.week)) + + expect(subject.css("div[class='card #{color_scheme}']").to_html).not_to be_empty + end + end + + context "raises an exception when" do + it "visits are missing" do + expect { Matey::BrowserOsBreakdownComponent.new }.to raise_error(ArgumentError) + end + + it "visits are invalid" do + expect { Matey::BrowserOsBreakdownComponent.new(visits: nil) }.to raise_error(ArgumentError) + end + + it "time_window is invalid" do + expect { Matey::BrowserOsBreakdownComponent.new(visits: [], time_window: nil) }.to raise_error(ArgumentError) + end + end +end diff --git a/spec/matey/daily_active_users_component_spec.rb b/spec/matey/daily_active_users_component_spec.rb new file mode 100644 index 0000000..bb5c35b --- /dev/null +++ b/spec/matey/daily_active_users_component_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Matey::DailyActiveUsersComponent, type: :component do + context "renders component" do + let(:color_scheme) { "bg-light text-dark border-dark" } + it "with a card element" do + subject = render_inline(Matey::DailyActiveUsersComponent.new(visits: Ahoy::Visit.all, time_window: 1.month)) + + expect(subject.css("div[class='card #{color_scheme}']").to_html).not_to be_empty + end + end + + context "raises an exception when" do + it "visits are missing" do + expect { Matey::DailyActiveUsersComponent.new }.to raise_error(ArgumentError) + end + + it "visits are invalid" do + expect { Matey::DailyActiveUsersComponent.new(visits: nil) }.to raise_error(ArgumentError) + end + + it "time_window is invalid" do + expect { Matey::DailyActiveUsersComponent.new(visits: [], time_window: nil) }.to raise_error(ArgumentError) + end + end +end diff --git a/spec/matey/new_activity_component_spec.rb b/spec/matey/new_activity_component_spec.rb new file mode 100644 index 0000000..273c5e0 --- /dev/null +++ b/spec/matey/new_activity_component_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Matey::NewActivityComponent, type: :component do + context "when ahoy events are present" do + let(:color_scheme) { "bg-light text-dark border-dark" } + it "renders the card element" do + subject = render_inline(Matey::NewActivityComponent.new(events: Ahoy::Event.all)) + + expect(subject.css("div[class='card #{color_scheme}']").to_html).not_to be_empty + end + end + + context "raises an exception when" do + it "events are missing" do + expect { Matey::NewActivityComponent.new }.to raise_error(ArgumentError) + end + + it "events are invalid" do + expect { Matey::NewActivityComponent.new(events: nil) }.to raise_error(ArgumentError) + end + + it "time_window is invalid" do + expect { Matey::NewActivityComponent.new(events: [], time_window: nil) }.to raise_error(ArgumentError) + end + end +end diff --git a/spec/matey/new_users_component_spec.rb b/spec/matey/new_users_component_spec.rb new file mode 100644 index 0000000..c9904f4 --- /dev/null +++ b/spec/matey/new_users_component_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Matey::NewUsersComponent, type: :component do + context "renders component" do + let(:color_scheme) { "bg-light text-dark border-dark" } + it "with a card element" do + subject = render_inline(Matey::NewUsersComponent.new(users: User.all)) + + expect(subject.css("div[class='card #{color_scheme}']").to_html).not_to be_empty + end + end + + context "raises an exception when" do + it "users are missing" do + expect { Matey::NewUsersComponent.new }.to raise_error(ArgumentError) + end + + it "users are invalid" do + expect { Matey::NewUsersComponent.new(users: nil) }.to raise_error(ArgumentError) + end + + it "time_window is invalid" do + expect { Matey::NewUsersComponent.new(users: [], time_window: nil) }.to raise_error(ArgumentError) + end + end +end diff --git a/spec/matey/top_visited_pages_table_component_spec.rb b/spec/matey/top_visited_pages_table_component_spec.rb new file mode 100644 index 0000000..fc1a07c --- /dev/null +++ b/spec/matey/top_visited_pages_table_component_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe Matey::TopVisitedPagesTableComponent, type: :component do + context "renders component" do + let(:color_scheme) { "bg-light text-dark border-dark" } + it "with a card element" do + subject = render_inline(Matey::TopVisitedPagesTableComponent.new(events: Ahoy::Visit.all)) + + expect(subject.css("div[class='card #{color_scheme}']").to_html).not_to be_empty + end + end + + context "raises an exception when" do + it "events are missing" do + expect { Matey::TopVisitedPagesTableComponent.new }.to raise_error(ArgumentError) + end + + it "events are invalid" do + expect { Matey::TopVisitedPagesTableComponent.new(events: nil) }.to raise_error(ArgumentError) + end + + it "time_window is invalid" do + expect { Matey::TopVisitedPagesTableComponent.new(events: [], time_window: nil) }.to raise_error(ArgumentError) + end + end +end diff --git a/spec/matey/user_engagement_component_spec.rb b/spec/matey/user_engagement_component_spec.rb new file mode 100644 index 0000000..b25cc55 --- /dev/null +++ b/spec/matey/user_engagement_component_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Matey::UserEngagementComponent, type: :component do + context "when ahoy events are present" do + let(:user) { create(:user) } + let(:color_scheme) { "bg-light text-dark border-dark" } + it "renders the card element" do + subject = render_inline(Matey::UserEngagementComponent.new(events: Ahoy::Event.all, user_id: user.id)) + + expect(subject.css("div[class='card #{color_scheme}']").to_html).not_to be_empty + end + end + + context "raises an exception when" do + it "events are missing" do + expect { Matey::UserEngagementComponent.new }.to raise_error(ArgumentError) + end + + it "events are invalid" do + expect { Matey::UserEngagementComponent.new(events: nil) }.to raise_error(ArgumentError) + end + + it "time_window is invalid" do + expect { Matey::UserEngagementComponent.new(events: [], time_window: nil) }.to raise_error(ArgumentError) + end + end +end diff --git a/spec/matey/visits_by_day_of_week_component_spec.rb b/spec/matey/visits_by_day_of_week_component_spec.rb new file mode 100644 index 0000000..b6689d4 --- /dev/null +++ b/spec/matey/visits_by_day_of_week_component_spec.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Matey::VisitsByDayOfWeekComponent, type: :component do + context "when ahoy visits are present" do + let(:color_scheme) { "bg-light text-dark border-dark" } + it "renders the card element" do + subject = render_inline(Matey::VisitsByDayOfWeekComponent.new(visits: Ahoy::Visit.all, exclude_days: ["Monday", "Tuesday"])) + + expect(subject.css("div[class='card #{color_scheme}']").to_html).not_to be_empty + end + end + + context "raises an exception when" do + it "visits are missing" do + expect { Matey::VisitsByDayOfWeekComponent.new }.to raise_error(ArgumentError) + end + + it "visits are invalid" do + expect { Matey::VisitsByDayOfWeekComponent.new(visits: nil) }.to raise_error(ArgumentError) + end + + it "time_window is invalid" do + expect { Matey::VisitsByDayOfWeekComponent.new(visits: [], time_window: nil) }.to raise_error(ArgumentError) + end + end +end