Skip to content

Commit 7bb02d8

Browse files
jonasjabarigitbook-bot
authored andcommitted
GitBook: [main] 3 pages and 8 assets modified
1 parent da891bc commit 7bb02d8

File tree

7 files changed

+261
-4
lines changed

7 files changed

+261
-4
lines changed

docs/about/team.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Matestack heavily relies on a decent amount of time and energy following people
2727
> **Twitter**: [@rowellkeith](https://twitter.com/rowellkeith)
2828
>
2929
> Keith discovered Matestack in early 2021 and was hooked right away. He has been developing software since the late 80s and moved into web development at the turn of the century. For twenty years he has dreamed of being able to develop component-based UIs in ruby and now that reality is here. An email and a video call later, Keith and Jonas decided to work closely together to push Matestack forward. He describes Matestack as the most exciting thing he's seen in the software world since discovering Ruby.
30-
30+
3131
## Former core team members
3232

3333
Following awesome people have been a very important member of the Matestack core team in the past. Thank you!
@@ -58,5 +58,3 @@ Matestack would not be where it is right now if these mates have not helped maki
5858

5959
> Back in 2020, Tobias and Sebastian were hired as external freelancers to help Jonas refactor the early 0.7 version of Matestack. They designed and implemented a new core architecture building the foundation of the 1.0 release. It was a pleasure to work with them and we're happy that we had a chance to use their expertise around Ruby! Hopefully our paths might cross in future again!
6060
61-
62-

docs/getting-started/concepts-rails-integration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Based on a strong foundation - Rails with all the sugar you love - Matestack ena
1212

1313
**which leads to beautiful reactive web UI implemented in pure Ruby:**
1414

15-
![](../.gitbook/assets/image%20%289%29%20%281%29%20%282%29.png)
15+
![](../.gitbook/assets/image%20%289%29%20%281%29%20%283%29%20%281%29.png)
1616

1717
yielding following advantages:
1818

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,261 @@
11
# Capybara & Rspec \[WIP\]
22

3+
Matestack apps, pages and components can be tested with various test setups. We're using Rspec and Capybara a lot when creating apps with Matestack or work on Matestack's core itself and want to show you some basic elements of this setup.
4+
5+
We will show you how to setup a **headless chrome** for testing, because a headless browser approach gives you performance benefits and is better suited to be integrated in a CI/CD pipeline.
6+
7+
## Setup
8+
9+
In this guide we assume that you know the basics of Rspec and Capybara and have both gems installed. If not, please read the basics about these tools here:
10+
11+
* [https://github.com/rspec/rspec-rails](https://github.com/rspec/rspec-rails)
12+
* [https://github.com/teamcapybara/capybara](https://github.com/teamcapybara/capybara)
13+
14+
Additionally you need a Chrome browser installed on your system.
15+
16+
We recommend to configure Capybara in a separate file and require it in your `rails_helper.rb`
17+
18+
{% tabs %}
19+
{% tab title="spec/rails\_helper.rb" %}
20+
```ruby
21+
# This file is copied to spec/ when you run 'rails generate rspec:install'
22+
require "spec_helper"
23+
ENV["RAILS_ENV"] ||= "test"
24+
require File.expand_path("../config/environment", __dir__)
25+
# Prevent database truncation if the environment is production
26+
abort("The Rails environment is running in production mode!") if Rails.env.production?
27+
require "rspec/rails"
28+
29+
Dir[File.join File.dirname(__FILE__), "support", "**", "*.rb"].each { |f| require f }
30+
# Add additional requires below this line. Rails is not loaded until this point!
31+
```
32+
{% endtab %}
33+
34+
{% tab title="spec/support/capybara.rb" %}
35+
```ruby
36+
require "capybara/rspec"
37+
require "capybara/rails"
38+
require "selenium/webdriver"
39+
40+
# port used for debugging (explained later)
41+
Capybara.server_port = 33123
42+
Capybara.server_host = "0.0.0.0"
43+
44+
Capybara.register_driver :headless_chrome do |app|
45+
chrome_options = Selenium::WebDriver::Chrome::Options.new.tap do |o|
46+
o.add_argument "--headless"
47+
o.add_argument "--no-sandbox"
48+
o.add_argument "--disable-dev-shm-usage"
49+
o.add_argument "--disable-gpu"
50+
o.add_argument "--enable-features=NetworkService,NetworkServiceInProcess"
51+
end
52+
Capybara::Selenium::Driver.new(app, browser: :chrome, options: chrome_options)
53+
end
54+
Capybara.default_driver = :headless_chrome
55+
56+
```
57+
{% endtab %}
58+
{% endtabs %}
59+
60+
## Writing basic specs
61+
62+
Imagine having implemented a Matestack page like:
63+
64+
{% tabs %}
65+
{% tab title="app/matestack/some\_page.rb" %}
66+
```ruby
67+
class SomePage < Matestack::Ui::Page
68+
69+
def response
70+
plain "hello world!"
71+
end
72+
73+
end
74+
```
75+
{% endtab %}
76+
77+
{% tab title="app/controllers/some\_controller.rb" %}
78+
```ruby
79+
class SomeController < ApplicationController
80+
81+
include Matestack::Ui::Core::Helper
82+
83+
def some_page
84+
render SomePage
85+
end
86+
87+
end
88+
```
89+
{% endtab %}
90+
91+
{% tab title="config/routes.rb" %}
92+
```ruby
93+
Rails.application.routes.draw do
94+
95+
get 'some_page', to: 'some#some_page'
96+
97+
end
98+
```
99+
{% endtab %}
100+
{% endtabs %}
101+
102+
A spec might look like this:
103+
104+
{% tabs %}
105+
{% tab title="spec/features/hello\_world\_spec.rb" %}
106+
```ruby
107+
require "rails_helper"
108+
109+
describe "Some Page", type: :feature do
110+
111+
it "should render hello world" do
112+
visit some_page_path
113+
expect(page).to have_content("hello world!")
114+
end
115+
116+
end
117+
118+
```
119+
{% endtab %}
120+
{% endtabs %}
121+
122+
and then run this spec with `bundle exec rspec spec/features/hello_world_spec.rb`
123+
124+
This should start a webserver and trigger the headless chrome to request the specified page from it. Just like Capybara is working.
125+
126+
## Testing asynchronous features
127+
128+
{% hint style="info" %}
129+
Above, we just tested a static "hello world" rendering and didn't use any JavaScript based functionality. We need to activate the JavaScript driver in specs where Matestack's built-in \(or your own\) JavaScript is required.
130+
{% endhint %}
131+
132+
Let's add some basic built-in reactivity of Matestack, which requires JavaScript to work:
133+
134+
```ruby
135+
class SomePage < Matestack::Ui::Page
136+
137+
def response
138+
onclick emit: "show_hello" do
139+
button "click me"
140+
end
141+
async show_on: "show_hello", id: "hello" do
142+
plain "hello world!"
143+
end
144+
end
145+
146+
end
147+
```
148+
149+
The spec could look like this: **Note that you now have to add the `js: true` on line 3!**
150+
151+
{% tabs %}
152+
{% tab title="spec/features/hello\_world\_spec.rb" %}
153+
```ruby
154+
require "rails_helper"
155+
156+
describe "Some Page", type: :feature, js: true do
157+
158+
it "should render hello world after clicking on a button" do
159+
visit some_page_path
160+
expect(page).not_to have_content("hello world!")
161+
click "click me"
162+
expect(page).to have_content("hello world!")
163+
end
164+
165+
end
166+
```
167+
{% endtab %}
168+
{% endtabs %}
169+
170+
Capybara by default will wait for 2000ms before failing on an expectation. `expect(page).to have_content("hello world!")` therefore may take up to 2000ms to become truthy without breaking the spec. Following the documentation of Capybara, you can adjust the default wait time or set it individually on specific expectations. This built-in wait mechanism is especially useful when working with features requiring client-server communication, like page transitions, form or action submissions!
171+
172+
## Testing forms and actions
173+
174+
Imagine a `matestack_form` used for creating new `User` ActiveRecord Model instances:
175+
176+
```ruby
177+
class SomePage < Matestack::Ui::Page
178+
179+
def response
180+
matestack_form form_config do
181+
form_input key: :name, type: :text, label: "Name"
182+
button "submit me", type: :submit
183+
end
184+
toggle show_on: "succeeded" do
185+
plain "succeeded!"
186+
end
187+
toggle show_on: "failed" do
188+
plain "failed!"
189+
end
190+
end
191+
192+
def form_config
193+
{
194+
for: User.new,
195+
path: users_path,
196+
method: :post,
197+
success: { emit: "succeeded" },
198+
failure: { emit: "failed" }
199+
}
200+
end
201+
202+
end
203+
```
204+
205+
The according spec might look like this:
206+
207+
{% tabs %}
208+
{% tab title="spec/features/form\_submission\_spec.rb" %}
209+
```ruby
210+
require "rails_helper"
211+
212+
describe "Some Page", type: :feature, js: true do
213+
214+
it "should render hello world" do
215+
visit some_page_path
216+
fill_in "Name", with: "Foo"
217+
click "submit me"
218+
219+
expect(page).to have_content("succeeded!")
220+
end
221+
222+
end
223+
```
224+
{% endtab %}
225+
{% endtabs %}
226+
227+
If you want to test if the User model was correctly saved in the Database, you could do something like this:
228+
229+
{% tabs %}
230+
{% tab title="spec/features/form\_submission\_spec.rb" %}
231+
```ruby
232+
describe "Some Page", type: :feature, js: true do
233+
234+
it "should render hello world" do
235+
visit some_page_path
236+
fill_in "Name", with: "Foo"
237+
238+
expect {
239+
click "submit me"
240+
expect(page).to have_content("succeeded!") #required to work properly!
241+
}.to change { User.count }.by(1)
242+
243+
# from here on, we know for sure that the form was submitted
244+
expect(User.last.name).to eq "Foo"
245+
end
246+
247+
end
248+
```
249+
{% endtab %}
250+
{% endtabs %}
251+
252+
{% hint style="danger" %}
253+
**Beware of the timing trap!**
254+
255+
Without adding `expect(page).to have_content("succeeded!")` after `click "submit me"` the spec would fail. The `User.count` would be executed too early! You somehow need to use Capybara's built-in wait mechanisim in order to identify a successful asynchronous form submission. Otherwise the spec would just click the submit button and immediately expect a database state change. Unlike Capybara, plain Rspec expectations do not wait a certain amount of time before failing! Gems like [https://github.com/laserlemon/rspec-wait](https://github.com/laserlemon/rspec-wait) are trying to address this issue. In our experience, you're better of using Capybara's built-in wait mechanism like shown in the example, though.
256+
{% endhint %}
257+
258+
Above described approaches and hints apply for actions as well!
259+
260+
## Optional: Dockerized test setup \[WIP\]
261+

0 commit comments

Comments
 (0)