Signup user flow
This commit is contained in:
@@ -0,0 +1,31 @@
|
|||||||
|
class SignupsController < ApplicationController
|
||||||
|
require_untenanted_access
|
||||||
|
|
||||||
|
rate_limit only: :create, name: "short-term", to: 5, within: 3.minutes,
|
||||||
|
with: -> { redirect_to saas.new_signup_path, alert: "Try again later." }
|
||||||
|
rate_limit only: :create, name: "long-term", to: 10, within: 30.minutes,
|
||||||
|
with: -> { redirect_to saas.new_signup_path, alert: "Try again later." }
|
||||||
|
|
||||||
|
http_basic_authenticate_with \
|
||||||
|
name: Rails.env.test? ? "testname" : Rails.application.credentials.account_signup_http_basic_auth.name,
|
||||||
|
password: Rails.env.test? ? "testpassword" : Rails.application.credentials.account_signup_http_basic_auth.password
|
||||||
|
|
||||||
|
def new
|
||||||
|
@signup = Signup.new
|
||||||
|
end
|
||||||
|
|
||||||
|
def create
|
||||||
|
@signup = Signup.new(signup_params)
|
||||||
|
|
||||||
|
if @signup.process
|
||||||
|
redirect_to root_url(script_name: @signup.account.slug)
|
||||||
|
else
|
||||||
|
render :new, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def signup_params
|
||||||
|
params.require(:signup).permit(*Signup::PERMITTED_KEYS)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<% @page_title = "Sign up for Fizzy" %>
|
||||||
|
|
||||||
|
<div class="panel shadow center margin-block-double <%= "shake" if flash[:alert] %>" style="--panel-size: 65ch;">
|
||||||
|
<h1 class="txt-xx-large margin-block-end-double">Fizzy</h1>
|
||||||
|
<h2 class="txt-large margin-block-end-double">Create your account</h2>
|
||||||
|
|
||||||
|
<%= form_with model: @signup, url: saas.signup_path, scope: "signup", class: "flex flex-column gap txt-large", data: { turbo: false } do |form| %>
|
||||||
|
<div class="flex align-center gap">
|
||||||
|
<label class="flex align-center gap input input--actor txt-large">
|
||||||
|
<%= form.text_field :full_name, class: "input", autocomplete: "name", placeholder: "Your name", autofocus: true, required: true %>
|
||||||
|
<%= icon_tag "person", class: "txt-large" %>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex align-center gap">
|
||||||
|
<label class="flex align-center gap input input--actor txt-large">
|
||||||
|
<%= form.email_field :email_address, class: "input", autocomplete: "username", placeholder: "Your email address", required: true %>
|
||||||
|
<%= icon_tag "email", class: "txt-large" %>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex align-center gap">
|
||||||
|
<label class="flex align-center gap input input--actor txt-large">
|
||||||
|
<%= form.text_field :company_name, class: "input", autocomplete: "organization", placeholder: "Your organization's name", required: true %>
|
||||||
|
<%= icon_tag "building", class: "txt-large" %>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex align-center gap">
|
||||||
|
<label class="flex align-center gap input input--actor txt-large">
|
||||||
|
<%= form.password_field :password, class: "input", autocomplete: "new-password", placeholder: "Password (at least 12 characters)", required: true, maxlength: 72, minlength: 12 %>
|
||||||
|
<%= icon_tag "password", class: "txt-large" %>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<% if @signup.errors.any? %>
|
||||||
|
<div class="alert alert--error">
|
||||||
|
<ul class="margin-block-none">
|
||||||
|
<% @signup.errors.full_messages.each do |message| %>
|
||||||
|
<li><%= message %></li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn--reversed center">
|
||||||
|
<%= icon_tag "arrow-right" %>
|
||||||
|
<span>Create your account</span>
|
||||||
|
</button>
|
||||||
|
<% end %>
|
||||||
|
|
||||||
|
<!-- TODO: Add this back when we have Identities and magic link login working
|
||||||
|
<footer class="margin-block-start-double txt-center">
|
||||||
|
<p class="txt-small txt-muted">
|
||||||
|
Already have an account?
|
||||||
|
<%= link_to "Sign in", login_url, class: "decorated" %>
|
||||||
|
</p>
|
||||||
|
</footer>
|
||||||
|
-->
|
||||||
|
</div>
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
Fizzy::Saas::Engine.routes.draw do
|
Fizzy::Saas::Engine.routes.draw do
|
||||||
|
resource :signup, only: [ :new, :create ]
|
||||||
Queenbee.routes(self)
|
Queenbee.routes(self)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
require "test_helper"
|
||||||
|
|
||||||
|
class SignupsControllerTest < ActionDispatch::IntegrationTest
|
||||||
|
setup do
|
||||||
|
@signup_params = {
|
||||||
|
full_name: "Brian Wilson",
|
||||||
|
email_address: "brian@example.com",
|
||||||
|
company_name: "Beach Boys",
|
||||||
|
password: SecureRandom.hex(16)
|
||||||
|
}
|
||||||
|
@starting_tenants = ApplicationRecord.tenants
|
||||||
|
|
||||||
|
# Clear script_name for untenanted signup tests
|
||||||
|
integration_session.default_url_options[:script_name] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should require http basic authentication" do
|
||||||
|
get saas.new_signup_url
|
||||||
|
|
||||||
|
assert_response :unauthorized
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should get new" do
|
||||||
|
get saas.new_signup_url, headers: http_basic_auth_headers
|
||||||
|
|
||||||
|
assert_response :success
|
||||||
|
assert_select "h2", "Create your account"
|
||||||
|
assert_select "input[name='signup[full_name]']"
|
||||||
|
assert_select "input[name='signup[email_address]']"
|
||||||
|
assert_select "input[name='signup[company_name]']"
|
||||||
|
assert_select "input[name='signup[password]']"
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should create signup and redirect to tenant root on success" do
|
||||||
|
Account.any_instance.expects(:setup_basic_template).once
|
||||||
|
|
||||||
|
assert_difference -> { ApplicationRecord.tenants.count }, 1 do
|
||||||
|
post saas.signup_url, params: { signup: @signup_params }, headers: http_basic_auth_headers
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :redirect
|
||||||
|
|
||||||
|
new_tenant = (ApplicationRecord.tenants - @starting_tenants).first
|
||||||
|
ApplicationRecord.with_tenant(new_tenant) do
|
||||||
|
account = Account.sole
|
||||||
|
assert account, "Account should have been created"
|
||||||
|
assert_equal @signup_params[:company_name], account.name
|
||||||
|
|
||||||
|
user = User.find_by(email_address: @signup_params[:email_address])
|
||||||
|
assert user, "User should have been created"
|
||||||
|
assert_equal @signup_params[:full_name], user.name
|
||||||
|
assert_equal @signup_params[:email_address], user.email_address
|
||||||
|
|
||||||
|
assert_redirected_to root_url(script_name: account.slug)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should render new with errors when signup fails validation" do
|
||||||
|
invalid_params = @signup_params.merge(password: "")
|
||||||
|
|
||||||
|
assert_no_difference -> { ApplicationRecord.tenants.count } do
|
||||||
|
post saas.signup_url, params: { signup: invalid_params }, headers: http_basic_auth_headers
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :unprocessable_entity
|
||||||
|
assert_select ".alert--error"
|
||||||
|
assert_select ".alert--error li", /Password can't be blank/
|
||||||
|
end
|
||||||
|
|
||||||
|
test "should render new with errors when signup processing fails" do
|
||||||
|
Queenbee::Remote::Account.stubs(:create!).raises(RuntimeError, "Invalid account data")
|
||||||
|
|
||||||
|
assert_no_difference -> { ApplicationRecord.tenants.count } do
|
||||||
|
post saas.signup_url, params: { signup: @signup_params }, headers: http_basic_auth_headers
|
||||||
|
end
|
||||||
|
|
||||||
|
assert_response :unprocessable_entity
|
||||||
|
assert_select ".alert--error"
|
||||||
|
assert_select ".alert--error li", /An error occurred during signup/
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
def http_basic_auth_headers
|
||||||
|
credentials = ActionController::HttpAuthentication::Basic.encode_credentials("testname", "testpassword")
|
||||||
|
{ "HTTP_AUTHORIZATION" => credentials }
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user