Skip to content

Commit dd092f3

Browse files
committed
Add component specs
1 parent ae958f3 commit dd092f3

3 files changed

Lines changed: 283 additions & 0 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# frozen_string_literal: true
2+
3+
#-- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
#++
30+
31+
require "rails_helper"
32+
33+
RSpec.describe QuickFilter::BooleanComponent, type: :component do
34+
include QuickFilterHelpers
35+
36+
let(:project) { build_stubbed(:project) }
37+
let(:query) { build_meeting_query }
38+
39+
subject(:component) do
40+
described_class.new(
41+
name: "Boolean filter",
42+
query:,
43+
filter_key: :type,
44+
true_label: "Yes",
45+
false_label: "No",
46+
path_args: [project, :meetings]
47+
)
48+
end
49+
50+
context "when rendering" do
51+
before { render_inline(component) }
52+
53+
it "renders both items with the provided labels" do
54+
expect(page).to have_text("Yes")
55+
expect(page).to have_text("No")
56+
end
57+
58+
it "renders exactly two items" do
59+
expect(page).to have_css("a", count: 2)
60+
end
61+
62+
it "uses 't' and 'f' as filter values" do
63+
expect(filters_from_link(page.find("a", text: "Yes")))
64+
.to include({ "type" => { "operator" => "=", "values" => ["t"] } })
65+
expect(filters_from_link(page.find("a", text: "No")))
66+
.to include({ "type" => { "operator" => "=", "values" => ["f"] } })
67+
end
68+
end
69+
70+
context "when the true value is active" do
71+
let(:query) { build_meeting_query.where("type", "=", ["t"]) }
72+
73+
before do
74+
render_inline(component)
75+
end
76+
77+
it "marks the true item as selected" do
78+
expect(page).to have_css("[aria-current='true']", text: "Yes")
79+
end
80+
81+
it "does not mark the false item as selected" do
82+
expect(page).to have_no_css("[aria-current='true']", text: "No")
83+
end
84+
end
85+
end
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
# frozen_string_literal: true
2+
3+
#-- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
#++
30+
31+
require "rails_helper"
32+
33+
RSpec.describe QuickFilter::SegmentedComponent, type: :component do
34+
include QuickFilterHelpers
35+
36+
let(:project) { build_stubbed(:project) }
37+
let(:query) { build_meeting_query }
38+
let(:orders) { nil }
39+
40+
subject(:component) do
41+
described_class.new(
42+
name: "Test filter",
43+
query:,
44+
filter_key: :time,
45+
path_args: [project, :meetings],
46+
orders:
47+
)
48+
end
49+
50+
context "when rendering with items" do
51+
before do
52+
render_inline(component) do |c|
53+
c.with_item(label: "Upcoming", value: "future")
54+
c.with_item(label: "Past", value: "past")
55+
end
56+
end
57+
58+
it "renders all items" do
59+
expect(page).to have_text("Upcoming")
60+
expect(page).to have_text("Past")
61+
end
62+
63+
it "renders a segmented control" do
64+
expect(page).to have_css("segmented-control [aria-label='Test filter']")
65+
end
66+
end
67+
68+
context "when no items are given" do
69+
before do
70+
render_inline(component)
71+
end
72+
73+
it "does not render" do
74+
expect(page).to have_no_css("segmented-control [aria-label='Test filter']")
75+
end
76+
end
77+
78+
context "when an item matches the active filter value" do
79+
let(:query) { build_meeting_query.where("time", "=", ["future"]) }
80+
81+
before do
82+
render_inline(component) do |c|
83+
c.with_item(label: "Upcoming", value: "future")
84+
c.with_item(label: "Past", value: "past")
85+
end
86+
end
87+
88+
it "marks the matching item as selected" do
89+
expect(page).to have_css("[aria-current='true']", text: "Upcoming")
90+
end
91+
92+
it "does not mark the other item as selected" do
93+
expect(page).to have_no_css("[aria-current='true']", text: "Past")
94+
end
95+
end
96+
97+
context "when no filter is active" do
98+
before do
99+
render_inline(component) do |c|
100+
c.with_item(label: "All", value: nil)
101+
c.with_item(label: "Upcoming", value: "future")
102+
c.with_item(label: "Past", value: "past")
103+
end
104+
end
105+
106+
it "marks the nil value item as selected" do
107+
expect(page).to have_css("[aria-current='true']", text: "All")
108+
end
109+
end
110+
111+
context "when other filters are active" do
112+
let(:query) { build_meeting_query.where("time", "=", ["future"]) }
113+
114+
subject(:component) do
115+
described_class.new(
116+
name: "Type filter",
117+
query:,
118+
filter_key: :type,
119+
path_args: [project, :meetings]
120+
)
121+
end
122+
123+
before do
124+
render_inline(component) do |c|
125+
c.with_item(label: "All", value: nil)
126+
c.with_item(label: "One-time", value: "f")
127+
c.with_item(label: "Recurring", value: "t")
128+
end
129+
end
130+
131+
it "excludes the target filter when value is nil, leaving others unchanged" do
132+
filter_keys = filters_from_link(page.find("a", text: "All")).map { |f| f.keys.first }
133+
134+
expect(filter_keys).to include("time")
135+
expect(filter_keys).not_to include("type")
136+
end
137+
end
138+
139+
context "with order overrides" do
140+
let(:orders) { { "future" => { start_time: :asc }, "past" => { start_time: :desc } } }
141+
142+
before do
143+
render_inline(component) do |c|
144+
c.with_item(label: "Upcoming", value: "future")
145+
c.with_item(label: "Past", value: "past")
146+
end
147+
end
148+
149+
it "uses the override sort for the matching value" do
150+
expect(sort_from_link(page.find("a", text: "Past"))).to eq([["start_time", "desc"]])
151+
end
152+
end
153+
end
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# frozen_string_literal: true
2+
3+
#-- copyright
4+
# OpenProject is an open source project management software.
5+
# Copyright (C) the OpenProject GmbH
6+
#
7+
# This program is free software; you can redistribute it and/or
8+
# modify it under the terms of the GNU General Public License version 3.
9+
#
10+
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
11+
# Copyright (C) 2006-2013 Jean-Philippe Lang
12+
# Copyright (C) 2010-2013 the ChiliProject Team
13+
#
14+
# This program is free software; you can redistribute it and/or
15+
# modify it under the terms of the GNU General Public License
16+
# as published by the Free Software Foundation; either version 2
17+
# of the License, or (at your option) any later version.
18+
#
19+
# This program is distributed in the hope that it will be useful,
20+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22+
# GNU General Public License for more details.
23+
#
24+
# You should have received a copy of the GNU General Public License
25+
# along with this program; if not, write to the Free Software
26+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
27+
#
28+
# See COPYRIGHT and LICENSE files for more details.
29+
#++
30+
31+
module QuickFilterHelpers
32+
def build_meeting_query(user: build_stubbed(:user))
33+
Queries::Meetings::MeetingQuery.new(user:)
34+
end
35+
36+
def filters_from_link(link)
37+
json = CGI.unescape(link[:href].match(/filters=([^&]+)/)[1])
38+
JSON.parse(json)
39+
end
40+
41+
def sort_from_link(link)
42+
json = CGI.unescape(link[:href].match(/sortBy=([^&]+)/)[1])
43+
JSON.parse(json)
44+
end
45+
end

0 commit comments

Comments
 (0)