katapult 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.ruby-version +1 -1
- data/README.md +61 -35
- data/bin/katapult +24 -34
- data/features/authenticate.feature +344 -0
- data/features/basics.feature +590 -0
- data/features/binary.feature +33 -1
- data/features/model.feature +9 -13
- data/features/step_definitions/rails_steps.rb +3 -2
- data/features/wui.feature +49 -4
- data/lib/generators/katapult/basics/basics_generator.rb +63 -17
- data/lib/generators/katapult/basics/templates/.gitignore +1 -0
- data/lib/generators/katapult/basics/templates/.ruby-version +1 -1
- data/lib/generators/katapult/basics/templates/Capfile +25 -0
- data/lib/generators/katapult/basics/templates/Gemfile +14 -6
- data/lib/generators/katapult/basics/templates/Guardfile +44 -0
- data/lib/generators/katapult/basics/templates/config/database.sample.yml +3 -2
- data/lib/generators/katapult/basics/templates/config/database.yml +3 -2
- data/lib/generators/katapult/basics/templates/config/deploy/production.rb +8 -0
- data/lib/generators/katapult/basics/templates/config/deploy/staging.rb +7 -0
- data/lib/generators/katapult/basics/templates/config/deploy.rb +37 -0
- data/lib/generators/katapult/basics/templates/config/initializers/ext.rb +3 -0
- data/lib/generators/katapult/basics/templates/features/support/factory_girl.rb +1 -0
- data/lib/generators/katapult/basics/templates/features/support/paths.rb +6 -0
- data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/db.rake +28 -0
- data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/deploy.rake +15 -0
- data/lib/generators/katapult/basics/templates/lib/capistrano/tasks/passenger.rake +8 -0
- data/lib/generators/katapult/basics/templates/{config/initializers → lib/ext/action_view}/form_for_with_development_errors.rb +0 -2
- data/lib/generators/katapult/basics/templates/lib/ext/action_view/spec_label.rb +46 -0
- data/lib/generators/katapult/basics/templates/{config/initializers → lib/ext/active_record}/find_by_anything.rb +0 -0
- data/lib/generators/katapult/basics/templates/lib/ext/active_record/these.rb +7 -0
- data/lib/generators/katapult/basics/templates/lib/ext/array/xss_aware_join.rb +10 -0
- data/lib/generators/katapult/basics/templates/lib/ext/enumerable/natural_sort.rb +15 -0
- data/lib/generators/katapult/basics/templates/lib/ext/hash/infinite.rb +7 -0
- data/lib/generators/katapult/basics/templates/lib/ext/string/html_entities.rb +11 -0
- data/lib/generators/katapult/basics/templates/lib/ext/string/to_sort_atoms.rb +52 -0
- data/lib/generators/katapult/basics/templates/lib/tasks/pending_migrations.rake +24 -0
- data/lib/generators/katapult/basics/templates/spec/factories/factories.rb +9 -0
- data/lib/generators/katapult/basics/templates/spec/support/factory_girl.rb +3 -0
- data/lib/generators/katapult/clearance/clearance_generator.rb +125 -0
- data/lib/generators/katapult/clearance/templates/app/controllers/passwords_controller.rb +16 -0
- data/lib/generators/katapult/clearance/templates/app/views/clearance_mailer/change_password.html.haml +6 -0
- data/lib/generators/katapult/clearance/templates/app/views/clearance_mailer/change_password.text.erb +3 -0
- data/lib/generators/katapult/clearance/templates/app/views/passwords/create.html.haml +5 -0
- data/lib/generators/katapult/clearance/templates/app/views/passwords/edit.html.haml +16 -0
- data/lib/generators/katapult/clearance/templates/app/views/passwords/new.html.haml +14 -0
- data/lib/generators/katapult/clearance/templates/app/views/sessions/new.html.haml +19 -0
- data/lib/generators/katapult/clearance/templates/config/initializers/clearance.rb +15 -0
- data/lib/generators/katapult/clearance/templates/config/locales/clearance.en.yml +59 -0
- data/lib/generators/katapult/clearance/templates/features/authentication.feature +94 -0
- data/lib/generators/katapult/clearance/templates/features/step_definitions/authentication_steps.rb +4 -0
- data/lib/generators/katapult/cucumber_features/templates/feature.feature +11 -7
- data/lib/generators/katapult/haml/haml_generator.rb +5 -0
- data/lib/generators/katapult/haml/templates/_form.html.haml +4 -4
- data/lib/generators/katapult/haml/templates/app/views/layouts/_flashes.html.haml +3 -0
- data/lib/generators/katapult/haml/templates/app/views/layouts/application.html.haml +9 -3
- data/lib/generators/katapult/haml/templates/index.html.haml +1 -1
- data/lib/generators/katapult/haml/templates/show.html.haml +2 -4
- data/lib/generators/katapult/install/templates/lib/katapult/application_model.rb +9 -7
- data/lib/generators/katapult/model/model_generator.rb +1 -1
- data/lib/generators/katapult/w_u_i/templates/controller.rb +1 -1
- data/lib/generators/katapult/w_u_i/w_u_i_generator.rb +4 -2
- data/lib/katapult/application_model.rb +8 -1
- data/lib/katapult/attribute.rb +10 -11
- data/lib/katapult/authentication.rb +25 -0
- data/lib/katapult/binary_util.rb +37 -0
- data/lib/katapult/element.rb +1 -1
- data/lib/katapult/generator.rb +6 -0
- data/lib/katapult/model.rb +13 -1
- data/lib/katapult/parser.rb +7 -0
- data/lib/katapult/version.rb +1 -1
- data/lib/katapult/wui.rb +4 -0
- data/lib/katapult.rb +2 -0
- data/spec/attribute_spec.rb +13 -0
- data/spec/element_spec.rb +5 -0
- data/spec/model_spec.rb +5 -4
- data/spec/util_spec.rb +8 -8
- data/spec/wui_spec.rb +19 -0
- metadata +44 -8
- data/features/katapult.feature +0 -271
- data/lib/katapult/util.rb +0 -16
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Enumerable
|
|
2
|
+
|
|
3
|
+
def natural_sort
|
|
4
|
+
natural_sort_by
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def natural_sort_by(&stringifier)
|
|
8
|
+
sort_by do |element|
|
|
9
|
+
element = stringifier.call(element) if stringifier
|
|
10
|
+
element = element.to_s unless element.respond_to?(:to_sort_atoms)
|
|
11
|
+
element.to_sort_atoms
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class SmartSortAtom
|
|
2
|
+
|
|
3
|
+
attr_reader :value
|
|
4
|
+
|
|
5
|
+
def initialize(value)
|
|
6
|
+
@value = value
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def <=>(other)
|
|
10
|
+
other.is_a?(self.class) or raise "Can only smart compare with other SmartSortAtom"
|
|
11
|
+
left_value = value
|
|
12
|
+
right_value = other.value
|
|
13
|
+
if left_value.class == right_value.class
|
|
14
|
+
left_value <=> right_value
|
|
15
|
+
elsif left_value.is_a?(Float)
|
|
16
|
+
-1
|
|
17
|
+
else
|
|
18
|
+
1
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.parse(string)
|
|
23
|
+
# Loosely based on http://stackoverflow.com/a/4079031
|
|
24
|
+
string.scan(/[^\d\.]+|[\d\.]+/).collect do |atom|
|
|
25
|
+
if atom.match(/\d+(\.\d+)?/)
|
|
26
|
+
atom = atom.to_f
|
|
27
|
+
else
|
|
28
|
+
atom = normalize_string(atom)
|
|
29
|
+
end
|
|
30
|
+
new(atom)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def self.normalize_string(string)
|
|
38
|
+
string = ActiveSupport::Inflector.transliterate(string)
|
|
39
|
+
string = string.downcase
|
|
40
|
+
string
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
String.class_eval do
|
|
46
|
+
|
|
47
|
+
def to_sort_atoms
|
|
48
|
+
SmartSortAtom.parse(self)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
namespace :db do
|
|
2
|
+
|
|
3
|
+
desc 'Warns if there are pending migrations'
|
|
4
|
+
task :warn_if_pending_migrations => :environment do
|
|
5
|
+
if defined? ActiveRecord
|
|
6
|
+
all_migrations = ActiveRecord::Migrator.migrations('db/migrate')
|
|
7
|
+
pending_migrations = ActiveRecord::Migrator.new(:up, all_migrations).pending_migrations
|
|
8
|
+
|
|
9
|
+
if pending_migrations.any?
|
|
10
|
+
puts ''
|
|
11
|
+
puts '======================================================='
|
|
12
|
+
puts "You have #{ pending_migrations.size } pending migrations:"
|
|
13
|
+
pending_migrations.each do |pending_migration|
|
|
14
|
+
puts ' %4d %s' % [pending_migration.version, pending_migration.name]
|
|
15
|
+
end
|
|
16
|
+
puts 'Run cap <stage> deploy:migrations'
|
|
17
|
+
puts '======================================================='
|
|
18
|
+
puts ''
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Generate authentication with Clearance
|
|
2
|
+
|
|
3
|
+
require 'katapult/generator'
|
|
4
|
+
|
|
5
|
+
module Katapult
|
|
6
|
+
module Generators
|
|
7
|
+
class ClearanceGenerator < Katapult::Generator
|
|
8
|
+
|
|
9
|
+
desc 'Generate authentication with Clearance'
|
|
10
|
+
|
|
11
|
+
check_class_collision
|
|
12
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def migrate
|
|
16
|
+
rake 'db:migrate' # Clearance must see the users table in the db
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def install_clearance
|
|
20
|
+
insert_into_file 'Gemfile', <<-GEM, before: "gem 'katapult'"
|
|
21
|
+
gem 'clearance', '< 1.14.0' # Has broken InstallGenerator :(
|
|
22
|
+
|
|
23
|
+
GEM
|
|
24
|
+
run 'bundle install --quiet'
|
|
25
|
+
generate 'clearance:install'
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def require_login
|
|
29
|
+
file = 'app/controllers/application_controller.rb'
|
|
30
|
+
insert_into_file file, <<-CONTENT, after: "Clearance::Controller\n"
|
|
31
|
+
|
|
32
|
+
before_action :require_login
|
|
33
|
+
CONTENT
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def overwrite_clearance_controllers
|
|
37
|
+
template 'app/controllers/passwords_controller.rb'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def create_clearance_views
|
|
41
|
+
directory 'app/views/clearance_mailer'
|
|
42
|
+
directory 'app/views/passwords'
|
|
43
|
+
directory 'app/views/sessions'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def install_backdoor
|
|
47
|
+
# This creepy indentation leads to correct formatting in the file
|
|
48
|
+
application <<-CONTENT, env: 'test'
|
|
49
|
+
# Enable quick-signin in tests: `visit homepage(as: User.last!)`
|
|
50
|
+
config.middleware.use Clearance::BackDoor
|
|
51
|
+
CONTENT
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def create_initializer
|
|
55
|
+
template 'config/initializers/clearance.rb', force: true
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def create_translations
|
|
59
|
+
template 'config/locales/clearance.en.yml'
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def create_routes
|
|
63
|
+
route <<-ROUTES
|
|
64
|
+
resources :users do
|
|
65
|
+
resource :password, controller: 'passwords',
|
|
66
|
+
only: %i[edit update]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Clearance
|
|
70
|
+
get '/login', to: 'clearance/sessions#new', as: 'sign_in'
|
|
71
|
+
resource :session, controller: 'clearance/sessions', only: [:create]
|
|
72
|
+
resources :passwords, controller: 'passwords', only: [:create, :new]
|
|
73
|
+
delete '/logout', to: 'clearance/sessions#destroy', as: 'sign_out'
|
|
74
|
+
ROUTES
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def add_sign_in_background_to_all_features
|
|
78
|
+
Dir['features/*.feature'].each do |file|
|
|
79
|
+
inject_into_file file, <<-CONTENT, after: /^Feature: .*$/
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
Background:
|
|
83
|
+
Given there is a user
|
|
84
|
+
And I sign in as the user above
|
|
85
|
+
CONTENT
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def create_authentication_feature
|
|
90
|
+
template 'features/authentication.feature'
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def create_authentication_steps
|
|
94
|
+
template 'features/step_definitions/authentication_steps.rb'
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def add_authentication_paths
|
|
98
|
+
inject_into_file 'features/support/paths.rb', <<-CONTENT, after: 'case page_name'
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Authentication
|
|
102
|
+
when 'the sign-in form'
|
|
103
|
+
sign_in_path
|
|
104
|
+
when 'the reset password page'
|
|
105
|
+
new_password_path
|
|
106
|
+
when 'the new password page for the user above'
|
|
107
|
+
edit_user_password_path(User.last!)
|
|
108
|
+
|
|
109
|
+
CONTENT
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def add_user_factory
|
|
113
|
+
inject_into_file 'spec/factories/factories.rb', <<-'CONTENT', after: 'FactoryGirl.define do'
|
|
114
|
+
|
|
115
|
+
factory :user do
|
|
116
|
+
sequence(:email) { |i| "user-#{ i }@example.com" }
|
|
117
|
+
password 'password'
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
CONTENT
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
class PasswordsController < Clearance::PasswordsController
|
|
2
|
+
|
|
3
|
+
def update
|
|
4
|
+
@user = find_user_for_update
|
|
5
|
+
|
|
6
|
+
if @user.update_password password_reset_params
|
|
7
|
+
sign_in @user
|
|
8
|
+
flash[:notice] = 'Password successfully changed' # <<- added
|
|
9
|
+
redirect_to url_after_update
|
|
10
|
+
else
|
|
11
|
+
flash_failure_after_update
|
|
12
|
+
render template: 'passwords/edit'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
%h1
|
|
2
|
+
Reset Password
|
|
3
|
+
|
|
4
|
+
%p
|
|
5
|
+
Choose your new password:
|
|
6
|
+
|
|
7
|
+
= form_for :password_reset,
|
|
8
|
+
url: user_password_path(@user, token: @user.confirmation_token),
|
|
9
|
+
html: { method: :put } do |form|
|
|
10
|
+
|
|
11
|
+
.form-group
|
|
12
|
+
= form.label :password
|
|
13
|
+
= form.password_field :password, class: 'form-control',
|
|
14
|
+
placeholder: 'New Password'
|
|
15
|
+
|
|
16
|
+
= form.submit 'Update Password', class: 'btn btn-primary'
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
%h1
|
|
2
|
+
Password Reset
|
|
3
|
+
|
|
4
|
+
%p
|
|
5
|
+
Please enter your email address. We will send you instructions on how
|
|
6
|
+
to reset your password.
|
|
7
|
+
|
|
8
|
+
= form_for :password, url: passwords_path do |form|
|
|
9
|
+
.form-group
|
|
10
|
+
= form.label :email
|
|
11
|
+
= form.email_field :email, class: 'form-control',
|
|
12
|
+
placeholder: 'Email Address'
|
|
13
|
+
|
|
14
|
+
= form.submit 'Request Instructions', class: 'btn btn-primary'
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
%h1
|
|
2
|
+
Please sign in
|
|
3
|
+
|
|
4
|
+
= form_for :session, url: session_path do |form|
|
|
5
|
+
.form-group
|
|
6
|
+
= form.label :email
|
|
7
|
+
= form.email_field :email, class: 'form-control',
|
|
8
|
+
placeholder: 'Email Address', required: true, autofocus: true
|
|
9
|
+
|
|
10
|
+
.form-group
|
|
11
|
+
= form.label :password
|
|
12
|
+
= form.password_field :password, class: 'form-control', required: true,
|
|
13
|
+
placeholder: 'Password'
|
|
14
|
+
|
|
15
|
+
%p
|
|
16
|
+
= form.submit 'Sign in', class: 'btn btn-primary'
|
|
17
|
+
|
|
18
|
+
%p
|
|
19
|
+
= link_to 'Forgot password', new_password_path, class: 'text-muted'
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Clearance.configure do |config|
|
|
2
|
+
config.allow_sign_up = false
|
|
3
|
+
# config.cookie_domain = '.example.com'
|
|
4
|
+
# config.cookie_expiration = lambda { |cookies| 1.year.from_now.utc }
|
|
5
|
+
# config.cookie_name = 'remember_token'
|
|
6
|
+
# config.cookie_path = '/'
|
|
7
|
+
config.routes = false
|
|
8
|
+
# config.httponly = true
|
|
9
|
+
config.mailer_sender = 'system@example.com'
|
|
10
|
+
# config.password_strategy = Clearance::PasswordStrategies::BCrypt
|
|
11
|
+
# config.redirect_url = '/'
|
|
12
|
+
# config.secure_cookie = true
|
|
13
|
+
# config.sign_in_guards = []
|
|
14
|
+
# config.user_model = User
|
|
15
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
en:
|
|
3
|
+
clearance:
|
|
4
|
+
models:
|
|
5
|
+
clearance_mailer:
|
|
6
|
+
change_password: Change your password
|
|
7
|
+
clearance_mailer:
|
|
8
|
+
change_password:
|
|
9
|
+
closing: If you didn't request this, ignore this email. Your password has
|
|
10
|
+
not been changed.
|
|
11
|
+
link_text: Change my password
|
|
12
|
+
opening: "Someone, hopefully you, requested we send you a link to change
|
|
13
|
+
your password:"
|
|
14
|
+
flashes:
|
|
15
|
+
failure_after_create: Bad email or password.
|
|
16
|
+
failure_after_update: Password can't be blank.
|
|
17
|
+
failure_when_forbidden: Please double check the URL or try submitting
|
|
18
|
+
the form again.
|
|
19
|
+
failure_when_not_signed_in: Please sign in to continue.
|
|
20
|
+
helpers:
|
|
21
|
+
label:
|
|
22
|
+
password:
|
|
23
|
+
email: Email address
|
|
24
|
+
password_reset:
|
|
25
|
+
password: Choose password
|
|
26
|
+
submit:
|
|
27
|
+
password:
|
|
28
|
+
submit: Reset password
|
|
29
|
+
password_reset:
|
|
30
|
+
submit: Save this password
|
|
31
|
+
session:
|
|
32
|
+
submit: Sign in
|
|
33
|
+
user:
|
|
34
|
+
create: Sign up
|
|
35
|
+
layouts:
|
|
36
|
+
application:
|
|
37
|
+
sign_in: Sign in
|
|
38
|
+
sign_out: Sign out
|
|
39
|
+
passwords:
|
|
40
|
+
create:
|
|
41
|
+
description: You will receive an email within the next few minutes. It
|
|
42
|
+
contains instructions for changing your password.
|
|
43
|
+
edit:
|
|
44
|
+
description: Your password has been reset. Choose a new password below.
|
|
45
|
+
title: Change your password
|
|
46
|
+
new:
|
|
47
|
+
description: To be emailed a link to reset your password, please enter
|
|
48
|
+
your email address.
|
|
49
|
+
title: Reset your password
|
|
50
|
+
sessions:
|
|
51
|
+
form:
|
|
52
|
+
forgot_password: Forgot password?
|
|
53
|
+
sign_up: Sign up
|
|
54
|
+
new:
|
|
55
|
+
title: Sign in
|
|
56
|
+
users:
|
|
57
|
+
new:
|
|
58
|
+
sign_in: Sign in
|
|
59
|
+
title: Sign up
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Feature: Everything about user authentication
|
|
2
|
+
|
|
3
|
+
Scenario: Login is required to visit the homepage
|
|
4
|
+
When I go to the homepage
|
|
5
|
+
Then I should see "Please sign in to continue" within the flash
|
|
6
|
+
And I should be on the sign-in form
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
Scenario: Login
|
|
10
|
+
Given there is a user with the email "henry@example.com" and the password "password"
|
|
11
|
+
|
|
12
|
+
When I go to the homepage
|
|
13
|
+
Then I should be on the sign-in form
|
|
14
|
+
And I should see "Please sign in"
|
|
15
|
+
|
|
16
|
+
# Wrong email
|
|
17
|
+
When I fill in "Email" with "nonsense"
|
|
18
|
+
And I fill in "Password" with "password"
|
|
19
|
+
And I press "Sign in"
|
|
20
|
+
Then I should see "Bad email or password" within the flash
|
|
21
|
+
And I should see "Please sign in"
|
|
22
|
+
|
|
23
|
+
# Wrong password
|
|
24
|
+
When I fill in "Email" with "admin@example.com"
|
|
25
|
+
And I fill in "Password" with "wrong"
|
|
26
|
+
And I press "Sign in"
|
|
27
|
+
Then I should see "Bad email or password" within the flash
|
|
28
|
+
And I should see "Please sign in"
|
|
29
|
+
|
|
30
|
+
# Correct credentials
|
|
31
|
+
When I fill in "Email" with "henry@example.com"
|
|
32
|
+
And I fill in "Password" with "password"
|
|
33
|
+
And I press "Sign in"
|
|
34
|
+
Then I should be on the homepage
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
Scenario: Logout
|
|
38
|
+
Given there is a user
|
|
39
|
+
And I am signed in as the user above
|
|
40
|
+
|
|
41
|
+
When I follow "Sign out"
|
|
42
|
+
Then I should be on the sign-in form
|
|
43
|
+
|
|
44
|
+
# Logged out
|
|
45
|
+
When I go to the homepage
|
|
46
|
+
Then I should be on the sign-in form
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
Scenario: Reset password as a signed-in user
|
|
50
|
+
Given there is a user with the email "henry@example.com"
|
|
51
|
+
And I sign in as the user above
|
|
52
|
+
|
|
53
|
+
When I go to the homepage
|
|
54
|
+
And I follow "henry@example.com" within the current user
|
|
55
|
+
Then I should be on the form for the user above
|
|
56
|
+
|
|
57
|
+
When I fill in "Password" with "new-password"
|
|
58
|
+
And I press "Save"
|
|
59
|
+
Then I should be on the page for the user above
|
|
60
|
+
|
|
61
|
+
When I follow "Sign out"
|
|
62
|
+
And I fill in "Email" with "henry@example.com"
|
|
63
|
+
And I fill in "Password" with "new-password"
|
|
64
|
+
And I press "Sign in"
|
|
65
|
+
Then I should be on the homepage
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
Scenario: Reset password as a signed-out user
|
|
69
|
+
Given there is a user with the email "henry@example.com"
|
|
70
|
+
|
|
71
|
+
When I go to the sign-in form
|
|
72
|
+
And I follow "Forgot password"
|
|
73
|
+
Then I should be on the reset password page
|
|
74
|
+
And I should see "Password Reset"
|
|
75
|
+
|
|
76
|
+
When I fill in "Email" with "henry@example.com"
|
|
77
|
+
And I press "Request Instructions"
|
|
78
|
+
Then an email should have been sent with:
|
|
79
|
+
"""
|
|
80
|
+
From: system@example.com
|
|
81
|
+
To: henry@example.com
|
|
82
|
+
Subject: Change your password
|
|
83
|
+
|
|
84
|
+
To reset your password, please follow this link:
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
When I follow the first link in the email
|
|
88
|
+
Then I should be on the new password page for the user above
|
|
89
|
+
And I should see "Reset Password"
|
|
90
|
+
|
|
91
|
+
When I fill in "Choose password" with "new-password"
|
|
92
|
+
And I press "Update Password"
|
|
93
|
+
Then I should see "Password successfully changed" within the flash
|
|
94
|
+
And I should be on the homepage
|
|
@@ -4,16 +4,18 @@ Feature: <%= model.name(:humans).titleize %>
|
|
|
4
4
|
Given I am on the list of <%= model.name(:variables) %>
|
|
5
5
|
|
|
6
6
|
# create
|
|
7
|
-
When I follow "Add <%= model.name(:
|
|
7
|
+
When I follow "Add <%= model.name(:human) %>"
|
|
8
8
|
<% model.attrs.each do |attr| -%>
|
|
9
9
|
<%- if attr.assignable_values -%>
|
|
10
10
|
And I select "<%= attr.test_value %>" from "<%= attr.name.humanize %>"
|
|
11
11
|
<%- else -%>
|
|
12
12
|
<%- case attr.type -%>
|
|
13
|
-
<%- when :string, :email, :url, :integer, :money, :text, :
|
|
13
|
+
<%- when :string, :email, :url, :integer, :money, :text, :password -%>
|
|
14
14
|
And I fill in "<%= attr.name.humanize %>" with "<%= attr.test_value %>"
|
|
15
15
|
<%- when :flag -%>
|
|
16
16
|
And I check "<%= attr.name.humanize %>"
|
|
17
|
+
<%- when :datetime -%>
|
|
18
|
+
And I fill in "<%= attr.name.humanize %>" with "<%= attr.test_value.to_date %>"
|
|
17
19
|
<%- end -%>
|
|
18
20
|
<%- end -%>
|
|
19
21
|
<% end -%>
|
|
@@ -23,12 +25,12 @@ Feature: <%= model.name(:humans).titleize %>
|
|
|
23
25
|
Then I should be on the page for the <%= model.name(:variable) %> above
|
|
24
26
|
<% model.attrs.each do |attr| -%>
|
|
25
27
|
<%- case attr.type -%>
|
|
26
|
-
<%- when :string, :email, :url, :integer, :money, :text
|
|
28
|
+
<%- when :string, :email, :url, :integer, :money, :text -%>
|
|
27
29
|
And I should see "<%= attr.test_value %>"
|
|
28
30
|
<%- when :flag -%>
|
|
29
31
|
And I should see "<%= attr.name.humanize %> Yes"
|
|
30
32
|
<%- when :datetime -%>
|
|
31
|
-
And I should see "<%= I18n.localize(attr.test_value) %>"
|
|
33
|
+
And I should see "<%= I18n.localize(attr.test_value.to_date) %>"
|
|
32
34
|
<%- end -%>
|
|
33
35
|
<% end -%>
|
|
34
36
|
|
|
@@ -40,20 +42,22 @@ Feature: <%= model.name(:humans).titleize %>
|
|
|
40
42
|
And "<%= attr.test_value %>" should be selected for "<%= attr.name.humanize %>"
|
|
41
43
|
<%- else -%>
|
|
42
44
|
<%- case attr.type -%>
|
|
43
|
-
<%- when :string, :email, :url, :integer, :money, :text
|
|
45
|
+
<%- when :string, :email, :url, :integer, :money, :text -%>
|
|
44
46
|
And the "<%= attr.name.humanize %>" field should contain "<%= attr.test_value %>"
|
|
45
47
|
<%- when :flag -%>
|
|
46
48
|
And the "<%= attr.name.humanize %>" checkbox should be checked
|
|
49
|
+
<%- when :datetime -%>
|
|
50
|
+
And the "<%= attr.name.humanize %>" field should contain "<%= attr.test_value.to_date %>"
|
|
47
51
|
<%- end -%>
|
|
48
52
|
<%- end -%>
|
|
49
53
|
<% end -%>
|
|
50
54
|
|
|
51
|
-
<% if model.label_attr
|
|
55
|
+
<% if model.label_attr -%>
|
|
52
56
|
# destroy
|
|
53
57
|
When I go to the list of <%= model.name(:variables) %>
|
|
54
58
|
Then I should see "<%= model.label_attr.test_value %>"
|
|
55
59
|
|
|
56
|
-
When I follow "Destroy"
|
|
60
|
+
When I follow "Destroy <%= model.label_attr.test_value %>"
|
|
57
61
|
Then I should be on the list of <%= model.name(:variables) %>
|
|
58
62
|
But I should not see "<%= model.label_attr.test_value %>"
|
|
59
63
|
<% end -%>
|
|
@@ -14,6 +14,7 @@ module Katapult
|
|
|
14
14
|
def install_application_layout
|
|
15
15
|
remove_file 'app/views/layouts/application.html.erb'
|
|
16
16
|
template 'app/views/layouts/application.html.haml'
|
|
17
|
+
template 'app/views/layouts/_flashes.html.haml'
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def create_views_directory
|
|
@@ -63,6 +64,10 @@ module Katapult
|
|
|
63
64
|
def navigation
|
|
64
65
|
wui.application_model.navigation
|
|
65
66
|
end
|
|
67
|
+
|
|
68
|
+
def authentication
|
|
69
|
+
wui.application_model.authentication
|
|
70
|
+
end
|
|
66
71
|
end
|
|
67
72
|
|
|
68
73
|
private
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
= form_for <%= model_name(:ivar) %> do |form|
|
|
2
2
|
|
|
3
3
|
%dl.controls
|
|
4
|
-
<% wui.model.
|
|
4
|
+
<% wui.model.editable_attrs.each do |attribute| -%>
|
|
5
5
|
%dt
|
|
6
6
|
= form.label <%= attribute.name(:symbol) %>
|
|
7
7
|
%dd
|
|
@@ -13,6 +13,8 @@
|
|
|
13
13
|
= form.text_field <%= attribute.name(:symbol) %>
|
|
14
14
|
<%- when :email -%>
|
|
15
15
|
= form.email_field <%= attribute.name(:symbol) %>
|
|
16
|
+
<%- when :password -%>
|
|
17
|
+
= form.password_field <%= attribute.name(:symbol) %>
|
|
16
18
|
<%- when :url -%>
|
|
17
19
|
= form.url_field <%= attribute.name(:symbol) %>
|
|
18
20
|
<%- when :integer -%>
|
|
@@ -22,12 +24,10 @@
|
|
|
22
24
|
€
|
|
23
25
|
<%- when :text -%>
|
|
24
26
|
= form.text_area <%= attribute.name(:symbol) %>, rows: 5
|
|
25
|
-
<%- when :markdown -%>
|
|
26
|
-
= form.text_area <%= attribute.name(:symbol) %>, rows: 15
|
|
27
27
|
<%- when :flag -%>
|
|
28
28
|
= form.check_box <%= attribute.name(:symbol) %>
|
|
29
29
|
<%- when :datetime -%>
|
|
30
|
-
= form.
|
|
30
|
+
= form.date_field <%= attribute.name(:symbol) %>
|
|
31
31
|
<%- end -%>
|
|
32
32
|
<%- end -%>
|
|
33
33
|
<% end -%>
|
|
@@ -15,12 +15,18 @@
|
|
|
15
15
|
|
|
16
16
|
.layout__head
|
|
17
17
|
%h2 <%= app_name.titlecase %>
|
|
18
|
-
|
|
18
|
+
<%- if navigation -%>
|
|
19
19
|
= render_navigation Navigation.<%= navigation.name(:variable) %>
|
|
20
|
-
|
|
20
|
+
<%- end -%>
|
|
21
|
+
<%- if authentication -%>
|
|
22
|
+
- if signed_in?
|
|
23
|
+
.current-user
|
|
24
|
+
= link_to current_user.email, edit_user_path(current_user)
|
|
25
|
+
= link_to 'Sign out', sign_out_path, method: :delete
|
|
26
|
+
<%- end -%>
|
|
21
27
|
|
|
22
28
|
.layout__main
|
|
23
|
-
|
|
29
|
+
= render 'layouts/flashes'
|
|
24
30
|
= yield
|
|
25
31
|
|
|
26
32
|
.layout__tail
|