Skip to content

Commit 8c791cd

Browse files
committed
feat(bdd): setup and /admin/users CRUD exmaple
1 parent 5789a38 commit 8c791cd

27 files changed

+338
-0
lines changed

.rspec

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
--color
22
--require spec_helper
3+
--require turnip/rspec
34
--profile
45
--format progress

Gemfile

+1
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ end
8989
group :test do
9090
gem 'database_cleaner'
9191
gem 'timecop'
92+
gem 'turnip'
9293
gem 'webmock'
9394
end
9495

Gemfile.lock

+5
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ GEM
170170
fog-core
171171
nokogiri (>= 1.5.11, < 2.0.0)
172172
formatador (0.2.5)
173+
gherkin (4.1.3)
173174
globalid (0.4.0)
174175
activesupport (>= 4.2.0)
175176
hashdiff (0.3.6)
@@ -433,6 +434,9 @@ GEM
433434
turbolinks (5.0.1)
434435
turbolinks-source (~> 5)
435436
turbolinks-source (5.0.3)
437+
turnip (3.0.0)
438+
gherkin (~> 4.0)
439+
rspec (>= 3.0, < 4.0)
436440
tzinfo (1.2.3)
437441
thread_safe (~> 0.1)
438442
uglifier (3.2.0)
@@ -533,6 +537,7 @@ DEPENDENCIES
533537
spring-commands-rspec
534538
timecop
535539
turbolinks (~> 5)
540+
turnip
536541
uglifier (>= 1.3.0)
537542
unicorn
538543
web-console (~> 3.0)

lib/tasks/spec.rake

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
if Rails.env.development? || Rails.env.test?
2+
namespace :spec do
3+
4+
RSpec::Core::RakeTask.new(:features) do |task|
5+
file_list = FileList['spec/**/*.feature']
6+
task.pattern = file_list
7+
end
8+
end
9+
end

spec/acceptance/.gitkeep

Whitespace-only changes.

spec/acceptance/admin/index.feature

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Feature: 基本驗證
2+
Scenario: 沒登入
3+
When 前往 /admin
4+
Then 頁面轉跳
5+
When 前往 /sidekiq
6+
Then 頁面轉跳
7+
Scenario: 非 admin 登入
8+
Given user 登入
9+
When 前往 /admin
10+
Then 頁面轉跳
11+
When 前往 /sidekiq
12+
Then 頁面回應 404
13+
Scenario: 非 admin 登入
14+
Given 管理者 登入
15+
When 前往 /admin
16+
Then 頁面回應 正常
17+
When 前往 /sidekiq
18+
Then 頁面回應 正常
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Feature: 從後台新增 user
2+
Background:
3+
Given 管理者 登入
4+
And 已註冊 users:
5+
| email |
6+
| marsz@5fpro.com |
7+
Scenario: 新增頁面可正常載入
8+
When 前往 後台新增使用者頁面
9+
Then 頁面回應 正常
10+
Scenario: 不能新增重複 Email 的 user
11+
When 後台建立使用者:
12+
| email | marsz@5fpro.com |
13+
Then 頁面回應 正常
14+
And 使用者數 不變
15+
Scenario: 成功建立 user
16+
When 後台建立使用者:
17+
| email | marsz123@5fpro.com |
18+
Then 頁面轉跳
19+
And 使用者數 +1
20+
Scenario: 夾帶檔案(頭像)測試
21+
When 後台建立使用者:
22+
| name | marsz123 |
23+
| avatar | y |
24+
Then 使用者(marsz123)有頭像
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
Feature: 從後台更新 user
2+
Background:
3+
Given 管理者 登入
4+
And 已註冊 users:
5+
| name | email |
6+
| marsz | marsz@5fpro.com |
7+
Scenario: 刪除使用者
8+
When 後台刪除使用者 User(marsz)
9+
Then 頁面轉跳
10+
And User(marsz@5fpro.com) 不存在
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
Feature: 使用者後台列表
2+
Background:
3+
Given 管理者 登入
4+
And 已註冊 users:
5+
| name |
6+
| Mars |
7+
| Peter |
8+
Scenario: 基本列表
9+
When 前往 /admin/users
10+
Then 頁面回應 正常
11+
And 頁面包含 Mars
12+
And 頁面包含 Peter
13+
Scenario: 可搜尋
14+
When 前往 /admin/users?q[name_cont]=mars
15+
Then 頁面回應 正常
16+
And 頁面包含 Mars
17+
And 頁面不包含 Peter
18+
Scenario: 可 CSV 格式匯出
19+
When 前往 /admin/users.csv
20+
Then 頁面回應 正常
21+
And 頁面包含 Mars
22+
And 頁面包含 Peter
23+
When 前往 /admin/users.csv?q[name_cont]=mars
24+
Then 頁面回應 正常
25+
And 頁面包含 Mars
26+
And 頁面不包含 Peter
+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Feature: 使用者後台列表
2+
Background:
3+
Given 管理者 登入
4+
And 已註冊 users:
5+
| name |
6+
| Mars |
7+
Scenario: 前往後台使用者頁面
8+
When 前往 User(Mars) 的 後台使用者頁面
9+
Then 頁面回應 正常
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Feature: 從後台更新 user
2+
Background:
3+
Given 管理者 登入
4+
And 已註冊 users:
5+
| name | email |
6+
| marsz | marsz@5fpro.com |
7+
| venus | venus@5fpro.com |
8+
Scenario: 編輯頁面可正常載入
9+
When 前往 User(marsz) 的 後台使用者編輯頁面
10+
Then 頁面回應 正常
11+
Scenario: 不能把 email 更新為已存在的 user
12+
When 後台更新使用者 User(marsz):
13+
| email | venus@5fpro.com |
14+
Then 頁面回應 200
15+
And User(marsz) 的 email 沒有更新
16+
Scenario: 成功更新 user
17+
When 後台更新使用者 User(marsz):
18+
| name | jupiter |
19+
Then 頁面轉跳
20+
And User(marsz@5fpro.com) 的 name 更新為 'jupiter'

spec/acceptance/api/index.feature

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Feature: 首頁
2+
Scenario: 首頁預設為 json
3+
When 前往 API首頁
4+
Then 頁面回應 正常
5+
And 回應格式為 JSON
6+
Scenario: 各種 404
7+
When 對 API首頁 打 POST
8+
Then 頁面回應 404
9+
When 前往 /_dwr/interface/WbmemberDWR.js
10+
Then 頁面回應 404
11+
When 前往 /asdasads
12+
Then 頁面回應 404

spec/acceptance/index.feature

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Feature: 基本頁面
2+
Scenario: 首頁正常
3+
When 前往 首頁
4+
Then 頁面回應 正常
5+
Scenario: robots.txt 正常回應
6+
When 前往 /robots.txt
7+
Then 頁面回應 正常
8+
And 回應內容不含 '<html'
9+
And 回應格式為 text
10+
When 前往 /robots
11+
Then 頁面回應 404
12+
Scenario: 各種 404
13+
When 對 首頁 打 POST
14+
Then 頁面回應 404
15+
When 前往 /_dwr/interface/WbmemberDWR.js
16+
Then 頁面回應 404
17+
When 前往 /asdasads
18+
Then 頁面回應 404

spec/factories/users.rb

+4
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,9 @@
4242
factory :user_with_avatar do
4343
avatar { File.open(Rails.root.join('spec', 'fixtures', '5fpro.png')) }
4444
end
45+
46+
trait :admin_creation do
47+
confirmed_at nil
48+
end
4549
end
4650
end

spec/steps/.gitkeep

Whitespace-only changes.

spec/steps/admin/users_steps.rb

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
step '後台建立使用者:' do |table|
2+
@users_count = User.count
3+
params = { user: attributes_for(:user, :admin_creation, table.rows_hash.symbolize_keys) }
4+
params[:user][:avatar] = file_data if params[:user][:avatar].present?
5+
post '/admin/users', params: params
6+
end
7+
8+
step '後台更新使用者 :model_finder:' do |instance, table|
9+
@user = instance
10+
params = {
11+
user: table.rows_hash.symbolize_keys
12+
}
13+
put "/admin/users/#{instance.id}", params: params
14+
end
15+
16+
step '後台刪除使用者 :model_finder' do |instance|
17+
@user = instance
18+
delete "/admin/users/#{instance.id}"
19+
end
20+
21+
step '使用者數 :count_changed' do |changed|
22+
expect(@users_count + changed).to eq(User.count)
23+
end
24+
25+
step '使用者(:name)有頭像' do |name|
26+
@user ||= User.last
27+
expect(@user.name).to eq(name)
28+
expect(@user.avatar.url).to be_present
29+
end

spec/steps/data_steps.rb

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
step '已註冊 users:' do |table|
2+
@users = table.hashes.map { |h| create(:user, h) }
3+
end

spec/steps/model_steps.rb

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
step ':model_finder 的 :attr :have更新' do |instance, attr, have|
2+
expect {
3+
instance.reload
4+
}.public_send have ? :to : :not_to, change { instance.public_send(attr) }
5+
end
6+
7+
step ':model_finder 的 :attr 更新為 \':value\'' do |instance, attr, value|
8+
expect(instance.public_send(attr).to_s).to eq(value)
9+
end
10+
11+
step ':model_finder 不存在' do |instance|
12+
expect(instance).to eq(false)
13+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
placeholder :count_changed do
2+
match /[\+\-][0-9]+/, &:to_i
3+
4+
match /不變/ do |_count|
5+
0
6+
end
7+
end

spec/steps/placeholders/have_steps.rb

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
placeholder :have do
2+
match /沒有/ do
3+
false
4+
end
5+
6+
match /^有$/ do
7+
true
8+
end
9+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
MODEL_FINDER_MAP = {
2+
'User' => [:name, :email, :id]
3+
}.freeze
4+
5+
placeholder :model_finder do
6+
match /([A-Z][a-zA-Z0-9:]+)\((.+?)\)/ do |klass, value|
7+
klass = klass.constantize
8+
instance = nil
9+
MODEL_FINDER_MAP[klass.to_s].each do |col|
10+
instance ||= klass.find_by(col => value)
11+
break if instance
12+
end
13+
instance || false
14+
end
15+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
placeholder :page_caller do
2+
match /後台使用者頁面/ do
3+
->(u) { "/admin/users/#{u.id}" }
4+
end
5+
6+
match /後台使用者編輯頁面/ do
7+
->(u) { "/admin/users/#{u.id}/edit" }
8+
end
9+
end

spec/steps/placeholders/page_steps.rb

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
placeholder :page do
2+
match /API首頁/ do
3+
'/api'
4+
end
5+
6+
match /後台新增使用者頁面/ do
7+
'/admin/users/new'
8+
end
9+
10+
match /首頁/ do
11+
'/'
12+
end
13+
14+
match /\/.+/ do |page|
15+
page
16+
end
17+
end

spec/steps/request_steps.rb

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
step '前往 :page' do |page|
2+
get page
3+
end
4+
5+
step '前往 :model_finder 的 :page_caller' do |model_finder, page_caller|
6+
get page_caller.call(model_finder)
7+
end
8+
9+
step '對 :page 打 :req_method' do |page, req_method|
10+
public_send(req_method, page)
11+
end
12+
13+
placeholder :req_method do
14+
match /(POST|post|PUT|put|DELETE|delete)/ do |method|
15+
method.to_s.downcase
16+
end
17+
end

spec/steps/response_steps.rb

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
step '頁面回應 :http_status' do |http_status|
2+
if http_status.is_a?(Integer)
3+
expect(response.status).to eq(http_status)
4+
end
5+
end
6+
7+
placeholder :http_status do
8+
match /正常/ do
9+
200
10+
end
11+
12+
match /\d+/, &:to_i
13+
end
14+
15+
step '回應內容:is_matched \':matched_content\'' do |is_matched, matched_content|
16+
if is_matched == '不含'
17+
expect(response.body).not_to include(matched_content)
18+
elsif is_matched.index('含')
19+
expect(response.body).to include(matched_content)
20+
end
21+
end
22+
23+
step '回應格式為 :format' do |format|
24+
expect(response.headers['Content-Type']).to include(format.to_s.downcase)
25+
end
26+
27+
step '頁面轉跳' do
28+
expect(response).to be_redirect
29+
end
30+
31+
step '頁面包含 :content' do |content|
32+
expect(response.body).to include(content)
33+
end
34+
35+
step '頁面不包含 :content' do |content|
36+
expect(response.body).not_to include(content)
37+
end

spec/steps/user_login_steps.rb

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
step ':user 登入' do |user|
2+
@current_user ||= user
3+
signin_user(@current_user)
4+
end
5+
6+
placeholder :user do
7+
match /user/ do
8+
create(:user)
9+
end
10+
11+
match /管理者/ do
12+
create(:admin_user)
13+
end
14+
end

spec/turnip_helper.rb

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
Turnip.type = :request
2+
require 'rails_helper'
3+
Dir[Rails.root.join('spec/steps/**/*_steps.rb')].each { |f| load f }
4+
include FactoryGirl::Syntax::Methods
5+
6+
RSpec.configure do |config|
7+
config.raise_error_for_unimplemented_steps = true
8+
9+
config.include FactoryGirl::Syntax::Methods
10+
config.include RequestClient, type: :request
11+
end

0 commit comments

Comments
 (0)