Remove old integration tests (#1207)
* Remove old integration tests * Move new_integration to integration We removed legacy integration tests, so we're free to retake the word
This commit is contained in:
parent
aac7fd5e5c
commit
be2cbce1ce
2
Rakefile
2
Rakefile
|
@ -8,7 +8,7 @@ require "hanami/devtools/rake_tasks"
|
|||
namespace :spec do
|
||||
RSpec::Core::RakeTask.new(:unit) do |task|
|
||||
file_list = FileList["spec/**/*_spec.rb"]
|
||||
file_list = file_list.exclude("spec/{integration,isolation}/**/*_spec.rb")
|
||||
file_list = file_list.exclude("spec/{isolation}/**/*_spec.rb")
|
||||
|
||||
task.pattern = file_list
|
||||
end
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "App middleware stack", type: :integration do
|
||||
it "mounts Rack middleware" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
generate_middleware
|
||||
|
||||
# Add apps/web/middleware to the load paths
|
||||
replace "apps/web/app.rb", "load_paths << [", "load_paths << ['middleware',"
|
||||
|
||||
# Require Rack::ETag
|
||||
unshift "apps/web/app.rb", "require 'rack/etag'"
|
||||
|
||||
# Mount middleware
|
||||
replace "apps/web/app.rb", "# middleware.use", "middleware.use 'Web::Middleware::Runtime'\nmiddleware.use 'Web::Middleware::Custom', 'OK'\nmiddleware.use Rack::ETag"
|
||||
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = "OK"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
|
||||
expect(last_response.headers["X-Runtime"]).to eq("1ms")
|
||||
expect(last_response.headers["X-Custom"]).to eq("OK")
|
||||
expect(last_response.headers["ETag"]).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_middleware # rubocop:disable Metrics/MethodLength
|
||||
write "apps/web/middleware/runtime.rb", <<~EOF
|
||||
module Web
|
||||
module Middleware
|
||||
class Runtime
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status, headers, body = @app.call(env)
|
||||
headers["X-Runtime"] = "1ms"
|
||||
|
||||
[status, headers, body]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
write "apps/web/middleware/custom.rb", <<~EOF
|
||||
module Web
|
||||
module Middleware
|
||||
class Custom
|
||||
def initialize(app, value)
|
||||
@app = app
|
||||
@value = value
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status, headers, body = @app.call(env)
|
||||
headers["X-Custom"] = @value
|
||||
|
||||
[status, headers, body]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
end
|
||||
end
|
|
@ -1,48 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "assets", type: :integration do
|
||||
describe "CDN mode" do
|
||||
it "servers assets with CDN url" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
write "apps/web/assets/javascripts/app.css", <<~EOF
|
||||
body { color: #333 };
|
||||
EOF
|
||||
rewrite "apps/web/templates/app.html.erb", <<~EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web</title>
|
||||
<%= favicon %>
|
||||
<%= stylesheet 'app' %>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
replace_last "apps/web/app.rb", "# scheme 'https'", "scheme 'https'"
|
||||
replace "apps/web/app.rb", "# host 'cdn.example.org'", "host 'cdn.example.org'"
|
||||
replace_last "apps/web/app.rb", "# port 443", "port 443"
|
||||
|
||||
#
|
||||
# Precompile
|
||||
#
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "production"
|
||||
RSpec::Support::Env["DATABASE_URL"] = "sqlite://#{Pathname.new('db').join('bookshelf.sqlite')}"
|
||||
RSpec::Support::Env["SMTP_HOST"] = "localhost"
|
||||
RSpec::Support::Env["SMTP_PORT"] = "25"
|
||||
|
||||
hanami "assets precompile"
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page.body).to include(%(<link href="https://cdn.example.org/assets/app-5df86b4e9cbd733a027762b2f6bf8693.css" type="text/css" rel="stylesheet" integrity="sha256-LxaTcWkL8TAWFQWeHJ7OqoSoEXXaYapNIS+TCvGNf48=" crossorigin="anonymous">))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,42 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "assets", type: :integration do
|
||||
describe "fingerprint mode" do
|
||||
it "servers assets with fingerprint url" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
write "apps/web/assets/javascripts/app.css", <<~EOF
|
||||
body { color: #333 };
|
||||
EOF
|
||||
rewrite "apps/web/templates/app.html.erb", <<~EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web</title>
|
||||
<%= favicon %>
|
||||
<%= stylesheet 'app' %>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
#
|
||||
# Precompile
|
||||
#
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "production"
|
||||
RSpec::Support::Env["DATABASE_URL"] = "sqlite://#{Pathname.new('db').join('bookshelf.sqlite')}"
|
||||
RSpec::Support::Env["SMTP_HOST"] = "localhost"
|
||||
RSpec::Support::Env["SMTP_PORT"] = "25"
|
||||
hanami "assets precompile"
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page.body).to include(%(<link href="/assets/app-5df86b4e9cbd733a027762b2f6bf8693.css" type="text/css" rel="stylesheet" integrity="sha256-LxaTcWkL8TAWFQWeHJ7OqoSoEXXaYapNIS+TCvGNf48=" crossorigin="anonymous">))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,50 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "assets", type: :integration do
|
||||
describe "helpers" do
|
||||
it "renders assets tags" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
rewrite "apps/web/templates/app.html.erb", <<~EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web</title>
|
||||
<%= favicon %>
|
||||
<%= stylesheet 'app' %>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
<%= javascript 'app' %>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
rewrite "apps/web/templates/home/index.html.erb", <<~EOF
|
||||
<%= image('app.jpg') %>
|
||||
<%= video('movie.mp4') %>
|
||||
<%=
|
||||
video do
|
||||
text "Your browser does not support the video tag"
|
||||
source src: view.asset_path('movie.mp4'), type: 'video/mp4'
|
||||
source src: view.asset_path('movie.ogg'), type: 'video/ogg'
|
||||
end
|
||||
%>
|
||||
EOF
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page.body).to include(%(<link href="/assets/favicon.ico" rel="shortcut icon" type="image/x-icon">))
|
||||
expect(page.body).to include(%(<link href="/assets/app.css" type="text/css" rel="stylesheet">))
|
||||
expect(page.body).to include(%(<script src="/assets/app.js" type="text/javascript"></script>))
|
||||
|
||||
expect(page.body).to include(%(<img src="/assets/app.jpg" alt="App">))
|
||||
expect(page.body).to include(%(<video src="/assets/movie.mp4"></video>))
|
||||
expect(page.body).to include(%(<video>\nYour browser does not support the video tag\n<source src="/assets/movie.mp4" type="video/mp4">\n<source src="/assets/movie.ogg" type="video/ogg">\n</video>))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,70 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "assets", type: :integration do
|
||||
describe "serve" do
|
||||
it "compiles and serves assets in development mode" do
|
||||
project = "bookshelf_serve_assets"
|
||||
|
||||
with_project(project, gems: ['sassc']) do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
write "apps/web/assets/javascripts/app.css.sass", <<~EOF
|
||||
$font-family: Helvetica, sans-serif
|
||||
|
||||
body
|
||||
font: 100% $font-family
|
||||
EOF
|
||||
rewrite "apps/web/templates/app.html.erb", <<~EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web</title>
|
||||
<%= favicon %>
|
||||
<%= stylesheet 'app' %>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
expect(page.body).to include(%(<link href="/assets/app.css" type="text/css" rel="stylesheet">))
|
||||
|
||||
visit "/assets/app.css"
|
||||
expect(page.body).to include(%(body {\n font: 100% Helvetica, sans-serif; }\n))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "serve assets with prefixes" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
replace(
|
||||
"apps/web/app.rb",
|
||||
"# Specify sources for assets",
|
||||
"prefix '/library/assets'\n# Specify sources for assets"
|
||||
)
|
||||
|
||||
replace(
|
||||
"apps/web/config/routes.rb",
|
||||
"/",
|
||||
"namespace :library { get '/', to: 'home#index' }"
|
||||
)
|
||||
|
||||
write "apps/web/assets/javascripts/app.js", <<~EOF
|
||||
console.log('test');
|
||||
EOF
|
||||
|
||||
hanami "assets precompile"
|
||||
|
||||
server do
|
||||
visit "/library/assets/app.js"
|
||||
expect(page).to have_content("console.log('test');")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,54 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "json"
|
||||
|
||||
RSpec.describe "assets", type: :integration do
|
||||
describe "subresource integrity" do
|
||||
it "precompiles assets with checksums calculated with given algorithms" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
write "apps/web/assets/javascripts/app.css", <<~EOF
|
||||
body { font: Helvetica; }
|
||||
EOF
|
||||
rewrite "apps/web/templates/app.html.erb", <<~EOF
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web</title>
|
||||
<%= favicon %>
|
||||
<%= stylesheet 'app' %>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
|
||||
replace "apps/web/app.rb", "subresource_integrity :sha256", "subresource_integrity :sha256, :sha512"
|
||||
|
||||
#
|
||||
# Precompile
|
||||
#
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "production"
|
||||
# FIXME: database connection shouldn't be required for `assets precompile`
|
||||
RSpec::Support::Env["DATABASE_URL"] = "sqlite://#{Pathname.new('db').join('bookshelf.sqlite')}"
|
||||
RSpec::Support::Env["SMTP_HOST"] = "localhost"
|
||||
RSpec::Support::Env["SMTP_PORT"] = "25"
|
||||
|
||||
hanami "assets precompile"
|
||||
|
||||
manifest = File.read("public/assets.json")
|
||||
expect(JSON.parse(manifest)).to be_kind_of(Hash) # assert it's a well-formed JSON
|
||||
|
||||
expect(manifest).to include(%("/assets/app.css":{"target":"/assets/app-068e7d97b3671a14824bc20437ac5d06.css","sri":["sha256-cHgODQTP2nNk9ER+ViDGp+lC+ddHRmuLgJk05glJ4+w=","sha512-TDyxo1Ow7UjBib6ykALJh7J1OHEcE0yX4X21s1714ZBAhwdOz7k9t8+QQDAwWAmeH97bNaZGY7oTfVrwyTQ3cw=="]}))
|
||||
expect(manifest).to include(%("/assets/favicon.ico":{"target":"/assets/favicon-b0979f93c7f7246ac70949a80f7cbdfd.ico","sri":["sha256-PLEDhpDsTBpxl1KtXjzBjg+PUG67zpf05B1z2db4iJU=","sha512-9NvaW7aVAywbLPPi3fw5s4wtWwd37i8VpzgfEo9uCOyr/mDT2dbYJtGNjfTJa4R8TOw69yHr4NhazPQsLt1WHw=="]}))
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
expect(page.body).to include(%(<link href="/assets/app-068e7d97b3671a14824bc20437ac5d06.css" type="text/css" rel="stylesheet" integrity="sha256-cHgODQTP2nNk9ER+ViDGp+lC+ddHRmuLgJk05glJ4+w= sha512-TDyxo1Ow7UjBib6ykALJh7J1OHEcE0yX4X21s1714ZBAhwdOz7k9t8+QQDAwWAmeH97bNaZGY7oTfVrwyTQ3cw==" crossorigin="anonymous">))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,50 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "body parsers", type: :integration do
|
||||
it "parses JSON payload for non-GET requests" do
|
||||
with_project do
|
||||
generate_action
|
||||
enable_json_body_parser
|
||||
|
||||
server do
|
||||
post "/books", %({"book": {"title": "CLI apps with Ruby"}}), "CONTENT_TYPE" => "app/json", "HTTP_ACCEPT" => "app/json"
|
||||
expect(last_response.body).to eq(%({"book":{"title":"CLI apps with Ruby"}}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't eval untrusted JSON" do
|
||||
with_project do
|
||||
generate_action
|
||||
enable_json_body_parser
|
||||
|
||||
server do
|
||||
post "/books", %({"json_class": "Foo"}), "CONTENT_TYPE" => "app/json", "HTTP_ACCEPT" => "app/json"
|
||||
expect(last_response.body).to eq(%({"json_class":"Foo"}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_action
|
||||
generate "action web books#create --url=/books --method=POST"
|
||||
|
||||
rewrite "apps/web/controllers/books/create.rb", <<~EOF
|
||||
require 'hanami/utils/json'
|
||||
module Web::Controllers::Books
|
||||
class Create
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = Hanami::Utils::Json.generate(params.to_hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
end
|
||||
|
||||
def enable_json_body_parser
|
||||
inject_line_after "apps/web/app.rb", "configure do", "body_parsers :json"
|
||||
end
|
||||
end
|
|
@ -1,147 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "json"
|
||||
|
||||
RSpec.describe "hanami assets", type: :integration do
|
||||
describe "precompile" do
|
||||
it "precompiles assets" do
|
||||
gems = %w[sassc coffee-script]
|
||||
|
||||
Platform.match do
|
||||
os(:linux).engine(:ruby) { gems.push("therubyracer") }
|
||||
os(:linux).engine(:jruby) { gems.push("therubyrhino") }
|
||||
end
|
||||
|
||||
with_project("bookshelf_assets_precompile", gems: gems) do
|
||||
#
|
||||
# Web assets
|
||||
#
|
||||
write "apps/web/assets/javascripts/app.js.coffee", <<~EOF
|
||||
class App
|
||||
constructor: () ->
|
||||
@init = true
|
||||
EOF
|
||||
write "apps/web/assets/stylesheets/_colors.scss", <<~EOF
|
||||
$background-color: #f5f5f5;
|
||||
EOF
|
||||
|
||||
write "apps/web/assets/stylesheets/app.css.scss", <<~EOF
|
||||
@import 'colors';
|
||||
|
||||
body {
|
||||
background-color: $background-color;
|
||||
}
|
||||
EOF
|
||||
#
|
||||
# Admin assets
|
||||
#
|
||||
generate "app admin"
|
||||
write "apps/admin/assets/javascripts/dashboard.js.coffee", <<~EOF
|
||||
class Dashboard
|
||||
constructor: (@data) ->
|
||||
EOF
|
||||
|
||||
#
|
||||
# Precompile
|
||||
#
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "production"
|
||||
hanami "assets precompile"
|
||||
|
||||
# rubocop:disable Lint/ImplicitStringConcatenation
|
||||
|
||||
#
|
||||
# Verify manifest
|
||||
#
|
||||
manifest = retry_exec(Errno::ENOENT) do
|
||||
File.read("public/assets.json")
|
||||
end
|
||||
|
||||
expect(JSON.parse(manifest)).to be_kind_of(Hash) # assert it's a well-formed JSON
|
||||
|
||||
expect(manifest).to include(%("/assets/admin/dashboard.js":{"target":"/assets/admin/dashboard-39744f9626a70683b6c2d46305798883.js","sri":["sha256-1myPVWoqrq+uAVP2DSkmAown+5dm0x61+E3AjlGOKEc="]}))
|
||||
expect(manifest).to include(%("/assets/admin/favicon.ico":{"target":"/assets/admin/favicon-b0979f93c7f7246ac70949a80f7cbdfd.ico","sri":["sha256-PLEDhpDsTBpxl1KtXjzBjg+PUG67zpf05B1z2db4iJU="]}))
|
||||
expect(manifest).to include(%("/assets/app.css":{"target":"/assets/app-adb4104884aadde9abfef0bd98ac461e.css","sri":["sha256-S6V565W2In9pWE0uzMASpp58xCg32TN3at3Fv4g9aRA="]}))
|
||||
expect(manifest).to include(%("/assets/app.js":{"target":"/assets/app-bb8f10498d83d401db238549409dc4c5.js","sri":["sha256-9m4OTbWigbDPp4oCe1LZz9isqidvW1c3jNL6mXMj2xs="]}))
|
||||
expect(manifest).to include(%("/assets/favicon.ico":{"target":"/assets/favicon-b0979f93c7f7246ac70949a80f7cbdfd.ico","sri":["sha256-PLEDhpDsTBpxl1KtXjzBjg+PUG67zpf05B1z2db4iJU="]}))
|
||||
|
||||
#
|
||||
# Verify web assets (w/ checksum)
|
||||
#
|
||||
expect("public/assets/app-adb4104884aadde9abfef0bd98ac461e.css").to have_file_content <<~EOF
|
||||
body {background-color: #f5f5f5}
|
||||
EOF
|
||||
|
||||
expect("public/assets/app-bb8f10498d83d401db238549409dc4c5.js").to have_file_content \
|
||||
"""
|
||||
(function(){var App;App=(function(){function App(){this.init=true;}
|
||||
return App;})();}).call(this);
|
||||
"""
|
||||
|
||||
expect("public/assets/favicon-b0979f93c7f7246ac70949a80f7cbdfd.ico").to be_an_existing_file
|
||||
|
||||
#
|
||||
# Verify web assets (w/o checksum)
|
||||
#
|
||||
expect("public/assets/app.css").to have_file_content <<~EOF
|
||||
body {background-color: #f5f5f5}
|
||||
EOF
|
||||
|
||||
expect("public/assets/app.js").to have_file_content \
|
||||
"""
|
||||
(function(){var App;App=(function(){function App(){this.init=true;}
|
||||
return App;})();}).call(this);
|
||||
"""
|
||||
|
||||
expect("public/assets/favicon.ico").to be_an_existing_file
|
||||
|
||||
#
|
||||
# Verify admin assets (w/ checksum)
|
||||
#
|
||||
expect("public/assets/admin/dashboard-39744f9626a70683b6c2d46305798883.js").to have_file_content \
|
||||
"""
|
||||
(function(){var Dashboard;Dashboard=(function(){function Dashboard(data){this.data=data;}
|
||||
return Dashboard;})();}).call(this);
|
||||
"""
|
||||
|
||||
expect("public/assets/admin/favicon-b0979f93c7f7246ac70949a80f7cbdfd.ico").to be_an_existing_file
|
||||
|
||||
#
|
||||
# Verify admin assets (w/o checksum)
|
||||
#
|
||||
expect("public/assets/admin/dashboard.js").to have_file_content \
|
||||
"""
|
||||
(function(){var Dashboard;Dashboard=(function(){function Dashboard(data){this.data=data;}
|
||||
return Dashboard;})();}).call(this);
|
||||
"""
|
||||
|
||||
expect("public/assets/admin/favicon.ico").to be_an_existing_file
|
||||
|
||||
# rubocop:enable Lint/ImplicitStringConcatenation
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami assets precompile
|
||||
|
||||
Usage:
|
||||
hanami assets precompile
|
||||
|
||||
Description:
|
||||
Precompile assets for deployment
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami assets precompile # Basic usage
|
||||
hanami assets precompile HANAMI_ENV=production # Precompile assets for production environment
|
||||
OUT
|
||||
|
||||
run_cmd "hanami assets precompile --help", output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,14 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami assets", type: :integration do
|
||||
it "prints subcommands" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Commands:
|
||||
hanami assets precompile # Precompile assets for deployment
|
||||
OUT
|
||||
|
||||
run_cmd "hanami assets", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,105 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami console", type: :integration do
|
||||
context "irb" do
|
||||
it "starts console" do
|
||||
project_name = "bookshelf_console_irb"
|
||||
with_project(project_name, console: :irb) do
|
||||
setup_model
|
||||
|
||||
console do |input, _, _|
|
||||
input.puts("Hanami::VERSION")
|
||||
input.puts("Web::App")
|
||||
input.puts("Web.routes")
|
||||
input.puts("BookRepository.new.all.to_a")
|
||||
input.puts("exit")
|
||||
end
|
||||
|
||||
expect(out).to include(Hanami::VERSION)
|
||||
expect(out).to include("Web::App")
|
||||
expect(out).to include("#<Hanami::Routes")
|
||||
expect(out).to include("[]")
|
||||
expect(out).to include("[#{project_name}] [INFO]")
|
||||
expect(out).to include("SELECT `id`, `title` FROM `books` ORDER BY `books`.`id`")
|
||||
end
|
||||
end
|
||||
|
||||
it "starts console with --engine option" do
|
||||
with_project("bookshelf_console_irb", console: :irb) do
|
||||
console(" --engine=irb") do |input, _, _|
|
||||
input.puts("Hanami::VERSION")
|
||||
input.puts("exit")
|
||||
end
|
||||
|
||||
expect(out).to include(Hanami::VERSION)
|
||||
end
|
||||
end
|
||||
|
||||
it "starts console without hanami-model" do
|
||||
project_without_hanami_model("bookshelf", gems: ["dry-struct"], console: :irb) do
|
||||
write "lib/entities/access_token.rb", <<~EOF
|
||||
require 'dry-struct'
|
||||
require 'securerandom'
|
||||
|
||||
module Types
|
||||
include Dry::Types.module
|
||||
end
|
||||
|
||||
class AccessToken < Dry::Struct
|
||||
attribute :id, Types::String.default { SecureRandom.uuid }
|
||||
attribute :secret, Types::String
|
||||
attribute :digest, Types::String
|
||||
end
|
||||
EOF
|
||||
console do |input, _, _|
|
||||
input.puts("AccessToken.new(id: '1', secret: 'shh', digest: 'def')")
|
||||
input.puts("exit")
|
||||
end
|
||||
|
||||
expect(out).to include('#<AccessToken id="1" secret="shh" digest="def">')
|
||||
end
|
||||
end
|
||||
end # irb
|
||||
|
||||
xit "returns error when known engine isn't bundled" do
|
||||
with_project("bookshelf_console_irb", console: :irb) do
|
||||
output = "Missing gem for `pry' console engine. Please make sure to add it to `Gemfile'."
|
||||
run_cmd "hanami console --engine=pry", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "returns error when unknown engine is requested" do
|
||||
with_project("bookshelf_console_irb", console: :irb) do
|
||||
output = "Unknown console engine: `foo'."
|
||||
run_cmd "hanami console --engine=foo", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami console
|
||||
|
||||
Usage:
|
||||
hanami console
|
||||
|
||||
Description:
|
||||
Starts Hanami console
|
||||
|
||||
Options:
|
||||
--engine=VALUE # Force a specific console engine: (pry/ripl/irb)
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami console # Uses the bundled engine
|
||||
hanami console --engine=pry # Force to use Pry
|
||||
OUT
|
||||
|
||||
run_cmd "hanami console --help", output
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: test with pry
|
||||
# TODO: test with ripl
|
||||
end
|
|
@ -1,74 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami db", type: :integration do
|
||||
describe "apply" do
|
||||
it "migrates, dumps structure, deletes migrations", if: RUBY_VERSION < '2.4' do
|
||||
with_project do
|
||||
versions = generate_migrations
|
||||
|
||||
hanami "db apply"
|
||||
|
||||
hanami "db version"
|
||||
expect(out).to include(versions.last.to_s)
|
||||
|
||||
db = Pathname.new("db")
|
||||
schema = db.join("schema.sql").to_s
|
||||
migrations = db.join("migrations")
|
||||
|
||||
expect(schema).to have_file_content <<~SQL
|
||||
CREATE TABLE `schema_migrations` (`filename` varchar(255) NOT NULL PRIMARY KEY);
|
||||
CREATE TABLE `users` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255), `age` integer);
|
||||
INSERT INTO "schema_migrations" VALUES('#{versions.first}_create_users.rb');
|
||||
INSERT INTO "schema_migrations" VALUES('#{versions.last}_add_age_to_users.rb');
|
||||
SQL
|
||||
|
||||
expect(migrations.children).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
it "migrates, dumps structure, deletes migrations", if: RUBY_VERSION >= '2.4' do
|
||||
with_project do
|
||||
versions = generate_migrations
|
||||
|
||||
hanami "db apply"
|
||||
|
||||
hanami "db version"
|
||||
expect(out).to include(versions.last.to_s)
|
||||
|
||||
db = Pathname.new('db')
|
||||
schema = db.join('schema.sql').to_s
|
||||
migrations = db.join('migrations')
|
||||
|
||||
expect(schema).to have_file_content <<-SQL
|
||||
CREATE TABLE `schema_migrations` (`filename` varchar(255) NOT NULL PRIMARY KEY);
|
||||
CREATE TABLE `users` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255), `age` integer);
|
||||
CREATE TABLE sqlite_sequence(name,seq);
|
||||
INSERT INTO schema_migrations VALUES('#{versions.first}_create_users.rb');
|
||||
INSERT INTO schema_migrations VALUES('#{versions.last}_add_age_to_users.rb');
|
||||
SQL
|
||||
|
||||
expect(migrations.children).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami db apply
|
||||
|
||||
Usage:
|
||||
hanami db apply
|
||||
|
||||
Description:
|
||||
Migrate, dump the SQL schema, and delete the migrations (experimental)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
run_cmd "hanami db apply --help", output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,40 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami db", type: :integration do
|
||||
describe "console" do
|
||||
it "starts database console" do
|
||||
with_project do
|
||||
generate_migrations
|
||||
hanami "db prepare"
|
||||
|
||||
db_console do |input, _, _|
|
||||
input.puts('INSERT INTO users (id, name, age) VALUES(1, "Luca", 34);')
|
||||
input.puts("SELECT * FROM users;")
|
||||
input.puts(".quit")
|
||||
end
|
||||
|
||||
expect(out).to include("1|Luca|34")
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami db console
|
||||
|
||||
Usage:
|
||||
hanami db console
|
||||
|
||||
Description:
|
||||
Starts a database console
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
run_cmd "hanami db console --help", output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,50 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami db", type: :integration do
|
||||
describe "create" do
|
||||
it "creates database" do
|
||||
project = "bookshelf_db_create"
|
||||
|
||||
with_project(project) do
|
||||
hanami "db create"
|
||||
|
||||
db = Pathname.new("db").join("#{project}_development.sqlite").to_s
|
||||
expect(db).to be_an_existing_file
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't create in production" do
|
||||
project = "bookshelf_db_create_production"
|
||||
|
||||
with_project(project) do
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "production"
|
||||
hanami "db create"
|
||||
|
||||
expect(exitstatus).to eq(1)
|
||||
|
||||
db = Pathname.new("db").join("#{project}.sqlite").to_s
|
||||
expect(db).to_not be_an_existing_file
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami db create
|
||||
|
||||
Usage:
|
||||
hanami db create
|
||||
|
||||
Description:
|
||||
Create the database (only for development/test)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami db create --help', output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,54 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami db", type: :integration do
|
||||
describe "drop" do
|
||||
it "drops database" do
|
||||
project = "bookshelf_db_drop"
|
||||
|
||||
with_project(project) do
|
||||
db = Pathname.new("db").join("#{project}_development.sqlite").to_s
|
||||
|
||||
hanami "db create"
|
||||
expect(db).to be_an_existing_file
|
||||
|
||||
hanami "db drop"
|
||||
expect(db).to_not be_an_existing_file
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't drop in production" do
|
||||
project = "bookshelf_db_drop_production"
|
||||
|
||||
with_project(project) do
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "production"
|
||||
db = Pathname.new("db").join("#{project}.sqlite").to_s
|
||||
FileUtils.touch(db) # simulate existing database
|
||||
|
||||
hanami "db drop"
|
||||
|
||||
expect(exitstatus).to eq(1)
|
||||
expect(db).to be_an_existing_file
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami db drop
|
||||
|
||||
Usage:
|
||||
hanami db drop
|
||||
|
||||
Description:
|
||||
Drop the database (only for development/test)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami db drop --help', output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,108 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami db", type: :integration do
|
||||
describe "migrate" do
|
||||
it "migrates database" do
|
||||
project = "bookshelf_db_migrate"
|
||||
|
||||
with_project(project) do
|
||||
generate_migrations
|
||||
|
||||
hanami "db create"
|
||||
hanami "db migrate"
|
||||
|
||||
db = Pathname.new("db").join("#{project}_development.sqlite")
|
||||
|
||||
users = `sqlite3 #{db} ".schema users"`
|
||||
expect(users).to include("CREATE TABLE `users` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255), `age` integer);")
|
||||
|
||||
version = `sqlite3 #{db} "SELECT filename FROM schema_migrations ORDER BY filename DESC LIMIT 1"`
|
||||
expect(version).to include("add_age_to_users")
|
||||
end
|
||||
end
|
||||
|
||||
it "migrates database up to a version" do
|
||||
project = "bookshelf_db_migrate_up_to_version"
|
||||
|
||||
with_project(project) do
|
||||
versions = generate_migrations
|
||||
|
||||
hanami "db create"
|
||||
hanami "db migrate #{versions.first}"
|
||||
|
||||
db = Pathname.new("db").join("#{project}_development.sqlite")
|
||||
|
||||
users = `sqlite3 #{db} ".schema users"`
|
||||
expect(users).to include("CREATE TABLE `users` (`id` integer NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255));")
|
||||
|
||||
version = `sqlite3 #{db} "SELECT filename FROM schema_migrations ORDER BY filename DESC LIMIT 1"`
|
||||
expect(version).to include("create_users")
|
||||
end
|
||||
end
|
||||
|
||||
it "migrates database down to a version" do
|
||||
project = "bookshelf_db_migrate_down_to_version"
|
||||
|
||||
with_project(project) do
|
||||
versions = generate_migrations
|
||||
|
||||
hanami "db create"
|
||||
hanami "db migrate" # up to latest version
|
||||
hanami "db migrate #{versions.first}"
|
||||
|
||||
db = Pathname.new("db").join("#{project}_development.sqlite")
|
||||
|
||||
users = `sqlite3 #{db} ".schema users"`
|
||||
expect(users).to include("CREATE TABLE `users`(`id` integer DEFAULT (NULL) NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255) DEFAULT (NULL) NULL);")
|
||||
|
||||
version = `sqlite3 #{db} "SELECT filename FROM schema_migrations ORDER BY filename DESC LIMIT 1"`
|
||||
expect(version).to include("create_users")
|
||||
end
|
||||
end
|
||||
|
||||
it "migrates database down to 0" do
|
||||
project = "bookshelf_db_migrate_down_to_zero"
|
||||
|
||||
with_project(project) do
|
||||
generate_migrations
|
||||
|
||||
hanami "db create"
|
||||
hanami "db migrate" # up to latest version
|
||||
hanami "db migrate 0"
|
||||
|
||||
db = Pathname.new("db").join("#{project}_development.sqlite")
|
||||
|
||||
users = `sqlite3 #{db} ".schema users"`
|
||||
expect(users).to eq("")
|
||||
|
||||
version = `sqlite3 #{db} "SELECT filename FROM schema_migrations ORDER BY filename DESC LIMIT 1"`
|
||||
expect(version).to eq("")
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
banner = <<~OUT
|
||||
Command:
|
||||
hanami db drop
|
||||
|
||||
Usage:
|
||||
hanami db drop
|
||||
|
||||
Description:
|
||||
Drop the database (only for development/test)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
output = [
|
||||
banner,
|
||||
# %r{ hanami db migrate [\d]{14} # Migrate to a specific version}
|
||||
]
|
||||
|
||||
run_cmd 'hanami db drop --help', output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,36 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami db", type: :integration do
|
||||
describe "prepare" do
|
||||
it "prepares database" do
|
||||
with_project do
|
||||
versions = generate_migrations
|
||||
|
||||
hanami "db prepare"
|
||||
hanami "db version"
|
||||
|
||||
expect(out).to include(versions.last.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami db prepare
|
||||
|
||||
Usage:
|
||||
hanami db prepare
|
||||
|
||||
Description:
|
||||
Drop, create, and migrate the database (only for development/test)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami db prepare --help', output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,96 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami db", type: :integration do
|
||||
describe "rollback" do
|
||||
it "rollbacks database" do
|
||||
project = "bookshelf_db_rollback"
|
||||
|
||||
with_project(project) do
|
||||
generate_migrations
|
||||
|
||||
hanami "db create"
|
||||
hanami "db migrate"
|
||||
hanami "db rollback"
|
||||
|
||||
db = Pathname.new("db").join("#{project}_development.sqlite")
|
||||
|
||||
users = `sqlite3 #{db} ".schema users"`
|
||||
expect(users).to include("CREATE TABLE `users`(`id` integer DEFAULT (NULL) NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255) DEFAULT (NULL) NULL);")
|
||||
|
||||
version = `sqlite3 #{db} "SELECT filename FROM schema_migrations ORDER BY filename DESC LIMIT 1"`
|
||||
expect(version).to_not include("add_age_to_users")
|
||||
end
|
||||
end
|
||||
|
||||
it "rollbacks database using custom steps" do
|
||||
project = "bookshelf_db_migrate_custom_steps"
|
||||
|
||||
with_project(project) do
|
||||
generate_migrations
|
||||
|
||||
hanami "db create"
|
||||
hanami "db migrate"
|
||||
hanami "db rollback 2"
|
||||
|
||||
db = Pathname.new("db").join("#{project}_development.sqlite")
|
||||
|
||||
users = `sqlite3 #{db} ".schema users"`
|
||||
expect(users).to_not include("CREATE TABLE `users`(`id` integer DEFAULT (NULL) NOT NULL PRIMARY KEY AUTOINCREMENT, `name` varchar(255) DEFAULT (NULL) NULL);")
|
||||
|
||||
version = `sqlite3 #{db} "SELECT filename FROM schema_migrations ORDER BY filename DESC LIMIT 1"`
|
||||
expect(version).to_not include("create_users")
|
||||
end
|
||||
end
|
||||
|
||||
it "returns an error when steps isn't an integer" do
|
||||
with_project do
|
||||
generate_migrations
|
||||
|
||||
hanami "db create"
|
||||
hanami "db migrate"
|
||||
|
||||
output = "the number of steps must be a positive integer (you entered `quindici')."
|
||||
run_cmd "hanami db rollback quindici", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "returns an error when steps is 0" do
|
||||
with_project do
|
||||
generate_migrations
|
||||
|
||||
hanami "db create"
|
||||
hanami "db migrate"
|
||||
|
||||
output = "the number of steps must be a positive integer (you entered `0')."
|
||||
run_cmd "hanami db rollback 0", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami db rollback
|
||||
|
||||
Usage:
|
||||
hanami db rollback [STEPS]
|
||||
|
||||
Description:
|
||||
Rollback migrations
|
||||
|
||||
Arguments:
|
||||
STEPS # Number of steps to rollback the database
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami db rollback # Rollbacks latest migration
|
||||
hanami db rollback 2 # Rollbacks last two migration
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami db rollback --help', output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,38 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami db", type: :integration do
|
||||
describe "version" do
|
||||
it "prints database version" do
|
||||
with_project do
|
||||
versions = generate_migrations
|
||||
|
||||
hanami "db create"
|
||||
hanami "db migrate"
|
||||
hanami "db version"
|
||||
|
||||
expect(out).to include(versions.last.to_s)
|
||||
expect(out).to_not include("SELECT * FROM")
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami db version
|
||||
|
||||
Usage:
|
||||
hanami db version
|
||||
|
||||
Description:
|
||||
Print the current migrated version
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami db version --help', output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,21 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami db", type: :integration do
|
||||
it "prints subcommands" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Commands:
|
||||
hanami db apply # Migrate, dump the SQL schema, and delete the migrations (experimental)
|
||||
hanami db console # Starts a database console
|
||||
hanami db create # Create the database (only for development/test)
|
||||
hanami db drop # Drop the database (only for development/test)
|
||||
hanami db migrate [VERSION] # Migrate the database
|
||||
hanami db prepare # Drop, create, and migrate the database (only for development/test)
|
||||
hanami db rollback [STEPS] # Rollback migrations
|
||||
hanami db version # Print the current migrated version
|
||||
OUT
|
||||
|
||||
run_cmd "hanami db", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,143 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami destroy", type: :integration do
|
||||
describe "action" do
|
||||
it "destroys action" do
|
||||
with_project do
|
||||
generate "action web books#index"
|
||||
output = [
|
||||
"subtract apps/web/config/routes.rb",
|
||||
"remove spec/web/views/books/index_spec.rb",
|
||||
"remove apps/web/templates/books/index.html.erb",
|
||||
"remove apps/web/views/books/index.rb",
|
||||
"remove apps/web/controllers/books/index.rb",
|
||||
"remove spec/web/controllers/books/index_spec.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami destroy action web books#index", output
|
||||
|
||||
expect("spec/web/controllers/books/index_spec.rb").to_not be_an_existing_file
|
||||
expect("apps/web/controllers/books/index.rb").to_not be_an_existing_file
|
||||
expect("apps/web/views/books/index.rb").to_not be_an_existing_file
|
||||
expect("apps/web/templates/books/index.html.erb").to_not be_an_existing_file
|
||||
expect("spec/web/views/books/index_spec.rb").to_not be_an_existing_file
|
||||
|
||||
expect("apps/web/config/routes.rb").to_not have_file_content(%r{get '/books', to: 'books#index'})
|
||||
end
|
||||
end
|
||||
|
||||
it "destroys namespaced action" do
|
||||
with_project do
|
||||
generate "action web api/books#index"
|
||||
output = [
|
||||
"subtract apps/web/config/routes.rb",
|
||||
"remove spec/web/views/api/books/index_spec.rb",
|
||||
"remove apps/web/templates/api/books/index.html.erb",
|
||||
"remove apps/web/views/api/books/index.rb",
|
||||
"remove apps/web/controllers/api/books/index.rb",
|
||||
"remove spec/web/controllers/api/books/index_spec.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami destroy action web api/books#index", output
|
||||
|
||||
expect("spec/web/controllers/api/books/index_spec.rb").to_not be_an_existing_file
|
||||
expect("apps/web/controllers/api/books/index.rb").to_not be_an_existing_file
|
||||
expect("apps/web/views/api/books/index.rb").to_not be_an_existing_file
|
||||
expect("apps/web/templates/api/books/index.html.erb").to_not be_an_existing_file
|
||||
expect("spec/web/views/api/books/index_spec.rb").to_not be_an_existing_file
|
||||
|
||||
expect("apps/web/config/routes.rb").to_not have_file_content(%r{get '/api/books', to: 'api/books#index'})
|
||||
end
|
||||
end
|
||||
|
||||
it "destroys action without view" do
|
||||
with_project do
|
||||
generate "action web home#ping --skip-view --url=/ping"
|
||||
output = [
|
||||
"subtract apps/web/config/routes.rb",
|
||||
"remove apps/web/controllers/home/ping.rb",
|
||||
"remove spec/web/controllers/home/ping_spec.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami destroy action web home#ping", output
|
||||
|
||||
expect("spec/web/controllers/home/ping_spec.rb").to_not be_an_existing_file
|
||||
expect("apps/web/controllers/home/ping.rb").to_not be_an_existing_file
|
||||
expect("apps/web/views/home/ping.rb").to_not be_an_existing_file
|
||||
expect("apps/web/templates/home/ping.html.erb").to_not be_an_existing_file
|
||||
expect("spec/web/views/home/ping_spec.rb").to_not be_an_existing_file
|
||||
|
||||
expect("apps/web/config/routes.rb").to_not have_file_content(%r{get '/ping', to: 'home#ping'})
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing arguments" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
ERROR: "hanami destroy action" was called with no arguments
|
||||
Usage: "hanami destroy action APP ACTION"
|
||||
OUT
|
||||
|
||||
run_cmd "hanami destroy action", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing app" do
|
||||
with_project("bookshelf_generate_action_without_app") do
|
||||
output = <<~OUT
|
||||
ERROR: "hanami destroy action" was called with arguments ["home#index"]
|
||||
Usage: "hanami destroy action APP ACTION"
|
||||
OUT
|
||||
|
||||
run_cmd "hanami destroy action home#index", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with unknown app" do
|
||||
with_project("bookshelf_generate_action_with_unknown_app") do
|
||||
output = "`foo' is not a valid APP. Please specify one of: `web'"
|
||||
|
||||
run_cmd "hanami destroy action foo home#index", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with unknown action" do
|
||||
with_project("bookshelf_generate_action_with_unknown_action") do
|
||||
output = <<~OUT
|
||||
cannot find `home#index' in `web' app.
|
||||
please run `hanami routes' to know the existing actions.
|
||||
OUT
|
||||
|
||||
run_cmd "hanami destroy action web home#index", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami destroy action
|
||||
|
||||
Usage:
|
||||
hanami destroy action APP ACTION
|
||||
|
||||
Description:
|
||||
Destroy an action from app
|
||||
|
||||
Arguments:
|
||||
APP # REQUIRED The app name (eg. `web`)
|
||||
ACTION # REQUIRED The action name (eg. `home#index`)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami destroy action web home#index # Basic usage
|
||||
hanami destroy action admin users#index # Destroy from `admin` app
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami destroy action --help', output
|
||||
end
|
||||
end
|
||||
end # action
|
||||
end
|
|
@ -1,118 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami destroy", type: :integration do
|
||||
describe "app" do
|
||||
it "destroys app" do
|
||||
with_project do
|
||||
generate "app admin"
|
||||
|
||||
output = [
|
||||
"subtract .env.test",
|
||||
"subtract .env.development",
|
||||
"subtract config/environment.rb",
|
||||
"subtract config/environment.rb",
|
||||
"remove spec/admin",
|
||||
"remove apps/admin"
|
||||
]
|
||||
|
||||
run_cmd "hanami destroy app admin", output
|
||||
|
||||
expect(".env.test").to_not have_file_content(%r{ADMIN_SESSIONS_SECRET})
|
||||
expect(".env.development").to_not have_file_content(%r{ADMIN_SESSIONS_SECRET})
|
||||
|
||||
expect("config/environment.rb").to_not have_file_content(%r{mount Admin::App})
|
||||
expect("config/environment.rb").to_not have_file_content("require_relative '../apps/admin/app'")
|
||||
|
||||
expect("public/assets/admin").to_not be_an_existing_path
|
||||
expect("public/assets.json").to_not be_an_existing_path
|
||||
|
||||
expect("spec/admin").to_not be_an_existing_path
|
||||
expect("apps/admin").to_not be_an_existing_path
|
||||
end
|
||||
end
|
||||
|
||||
it "destroys app with actions and assets" do
|
||||
with_project do
|
||||
generate "app api --app-base-url=/api/v1"
|
||||
generate "action api home#index"
|
||||
|
||||
asset = File.join("apps", "api", "assets", "javascripts", "app.js")
|
||||
touch asset
|
||||
|
||||
hanami "assets precompile"
|
||||
|
||||
output = [
|
||||
"subtract .env.test",
|
||||
"subtract .env.development",
|
||||
"subtract config/environment.rb",
|
||||
"subtract config/environment.rb",
|
||||
"remove public/assets/api/v1",
|
||||
"remove public/assets.json",
|
||||
"remove spec/api",
|
||||
"remove apps/api"
|
||||
]
|
||||
|
||||
run_cmd "hanami destroy app api", output
|
||||
|
||||
expect(".env.test").to_not have_file_content(%r{API_SESSIONS_SECRET})
|
||||
expect(".env.development").to_not have_file_content(%r{API_SESSIONS_SECRET})
|
||||
|
||||
expect("config/environment.rb").to_not have_file_content(%r{mount Api::App})
|
||||
expect("config/environment.rb").to_not have_file_content("require_relative '../apps/api/app'")
|
||||
|
||||
expect("public/assets/api/v1").to_not be_an_existing_path
|
||||
expect("public/assets.json").to_not be_an_existing_path
|
||||
|
||||
expect("spec/api").to_not be_an_existing_path
|
||||
expect("apps/api").to_not be_an_existing_path
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing argument" do
|
||||
with_project do
|
||||
output = <<-OUT
|
||||
ERROR: "hanami destroy app" was called with no arguments
|
||||
Usage: "hanami destroy app APP"
|
||||
OUT
|
||||
|
||||
run_cmd "hanami destroy app", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with unknown app" do
|
||||
with_project do
|
||||
output = <<-OUT
|
||||
`unknown' is not a valid APP. Please specify one of: `web'
|
||||
OUT
|
||||
|
||||
run_cmd "hanami destroy app unknown", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami destroy app
|
||||
|
||||
Usage:
|
||||
hanami destroy app APP
|
||||
|
||||
Description:
|
||||
Destroy an app
|
||||
|
||||
Arguments:
|
||||
APP # REQUIRED The app name (eg. `web`)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami destroy app admin # Destroy `admin` app
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami destroy app --help', output
|
||||
end
|
||||
end
|
||||
end # app
|
||||
end
|
|
@ -1,74 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami destroy", type: :integration do
|
||||
describe "mailer" do
|
||||
context "destroy a mailer" do
|
||||
let(:output) do
|
||||
["remove spec/bookshelf_generate_mailer/mailers/welcome_spec.rb",
|
||||
"remove lib/bookshelf_generate_mailer/mailers/templates/welcome.html.erb",
|
||||
"remove lib/bookshelf_generate_mailer/mailers/templates/welcome.txt.erb",
|
||||
"remove lib/bookshelf_generate_mailer/mailers/welcome.rb"]
|
||||
end
|
||||
|
||||
it "generate the mailer files" do
|
||||
with_project("bookshelf_generate_mailer", test: "rspec") do
|
||||
generate "mailer welcome"
|
||||
|
||||
run_cmd "hanami destroy mailer welcome", output
|
||||
|
||||
expect("spec/bookshelf_generate_mailer/mailers/welcome_spec.rb").to_not be_an_existing_file
|
||||
expect("lib/bookshelf_generate_mailer/mailers/templates/welcome.html.erb").to_not be_an_existing_file
|
||||
expect("lib/bookshelf_generate_mailer/mailers/templates/welcome.txt.erb").to_not be_an_existing_file
|
||||
expect("lib/bookshelf_generate_mailer/mailers/welcome.rb").to_not be_an_existing_file
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing arguments" do
|
||||
with_project("bookshelf_generate_mailer_without_args") do
|
||||
output = <<~OUT
|
||||
ERROR: "hanami generate mailer" was called with no arguments
|
||||
Usage: "hanami generate mailer MAILER"
|
||||
OUT
|
||||
|
||||
run_cmd "hanami generate mailer", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with unknown mailer" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
cannot find `unknown' mailer. Please have a look at `lib/bookshelf/mailers' directory to find an existing mailer.
|
||||
OUT
|
||||
|
||||
run_cmd "hanami destroy mailer unknown", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami destroy mailer
|
||||
|
||||
Usage:
|
||||
hanami destroy mailer MAILER
|
||||
|
||||
Description:
|
||||
Destroy a mailer
|
||||
|
||||
Arguments:
|
||||
MAILER # REQUIRED The mailer name (eg. `welcome`)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami destroy mailer welcome # Destroy `WelcomeMailer` mailer
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami destroy mailer --help', output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,70 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "pathname"
|
||||
|
||||
RSpec.describe "hanami destroy", type: :integration do
|
||||
describe "migration" do
|
||||
it "destroys migration" do
|
||||
with_project do
|
||||
migration = Pathname.new("db").join("migrations", "20170127165331_create_users.rb").to_s
|
||||
File.open(migration, "wb+") { |f| f.write("migration") }
|
||||
|
||||
output = [
|
||||
"remove #{migration}"
|
||||
]
|
||||
|
||||
run_cmd "hanami destroy migration create_users", output
|
||||
|
||||
expect(migration).to_not be_an_existing_file
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing argument" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
ERROR: "hanami destroy migration" was called with no arguments
|
||||
Usage: "hanami destroy migration MIGRATION"
|
||||
OUT
|
||||
run_command "hanami destroy migration", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with unknown migration" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
cannot find `create_unknowns'. Please have a look at `db/migrations' directory to find an existing migration
|
||||
OUT
|
||||
run_command "hanami destroy migration create_unknowns", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
banner = <<~OUT
|
||||
Command:
|
||||
hanami destroy migration
|
||||
|
||||
Usage:
|
||||
hanami destroy migration MIGRATION
|
||||
|
||||
Description:
|
||||
Destroy a migration
|
||||
|
||||
Arguments:
|
||||
MIGRATION # REQUIRED The migration name (eg. `create_users`)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
OUT
|
||||
output = [
|
||||
banner,
|
||||
%r{ hanami destroy migration create_users # Destroy `db/migrations/[\d]{14}_create_users.rb`}
|
||||
]
|
||||
|
||||
run_command "hanami destroy migration --help", output
|
||||
end
|
||||
end
|
||||
end # migration
|
||||
end
|
|
@ -1,113 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "pathname"
|
||||
|
||||
RSpec.describe "hanami destroy", type: :integration do
|
||||
describe "model" do
|
||||
it "destroys model" do
|
||||
with_project do
|
||||
generate "model user"
|
||||
migration = Dir.glob(Pathname.new("db").join("migrations", "*_create_users.rb")).first.to_s
|
||||
|
||||
output = [
|
||||
"remove spec/bookshelf/repositories/user_repository_spec.rb",
|
||||
"remove spec/bookshelf/entities/user_spec.rb",
|
||||
"remove lib/bookshelf/repositories/user_repository.rb",
|
||||
"remove lib/bookshelf/entities/user.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami destroy model user", output
|
||||
|
||||
expect(migration).to be_an_existing_file
|
||||
|
||||
expect("lib/bookshelf/entities/user.rb").to_not be_an_existing_file
|
||||
expect("lib/bookshelf/repositories/user_repository.rb").to_not be_an_existing_file
|
||||
expect("spec/bookshelf/entities/user_spec.rb").to_not be_an_existing_file
|
||||
expect("spec/bookshelf/repositories/user_repository_spec.rb").to_not be_an_existing_file
|
||||
end
|
||||
end
|
||||
|
||||
it "destroys model even if migration was deleted manually" do
|
||||
with_project do
|
||||
generate "model user"
|
||||
migration = Dir.glob(Pathname.new("db").join("migrations", "*_create_users.rb")).first.to_s
|
||||
|
||||
run_simple "rm #{migration}"
|
||||
|
||||
expect(migration).to_not be_an_existing_file
|
||||
|
||||
output = [
|
||||
"remove spec/bookshelf/repositories/user_repository_spec.rb",
|
||||
"remove spec/bookshelf/entities/user_spec.rb",
|
||||
"remove lib/bookshelf/repositories/user_repository.rb",
|
||||
"remove lib/bookshelf/entities/user.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami destroy model user", output
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing argument" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
ERROR: "hanami destroy model" was called with no arguments
|
||||
Usage: "hanami destroy model MODEL"
|
||||
OUT
|
||||
|
||||
run_cmd "hanami destroy model", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
xit "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Usage:
|
||||
hanami destroy model NAME
|
||||
|
||||
Description:
|
||||
`hanami destroy model` will destroy an entity along with repository and \n corresponding tests
|
||||
|
||||
> $ hanami destroy model car
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami destroy model --help', output
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with unknown model" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
cannot find `unknown' model. Please have a look at `lib/bookshelf/entities' directory to find an existing model.
|
||||
OUT
|
||||
|
||||
run_cmd "hanami destroy model unknown", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami destroy model
|
||||
|
||||
Usage:
|
||||
hanami destroy model MODEL
|
||||
|
||||
Description:
|
||||
Destroy a model
|
||||
|
||||
Arguments:
|
||||
MODEL # REQUIRED The model name (eg. `user`)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami destroy model user # Destroy `User` entity and `UserRepository` repository
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami destroy model --help', output
|
||||
end
|
||||
end
|
||||
end # model
|
||||
end
|
|
@ -1,18 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami destroy", type: :integration do
|
||||
it "prints subcommands" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Commands:
|
||||
hanami destroy action APP ACTION # Destroy an action from app
|
||||
hanami destroy app APP # Destroy an app
|
||||
hanami destroy mailer MAILER # Destroy a mailer
|
||||
hanami destroy migration MIGRATION # Destroy a migration
|
||||
hanami destroy model MODEL # Destroy a model
|
||||
OUT
|
||||
|
||||
run_cmd "hanami destroy", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,469 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami generate", type: :integration do
|
||||
describe "action" do
|
||||
it "generates action" do
|
||||
with_project("bookshelf_generate_action") do
|
||||
output = [
|
||||
"create spec/web/controllers/authors/index_spec.rb",
|
||||
"create apps/web/controllers/authors/index.rb",
|
||||
"create apps/web/views/authors/index.rb",
|
||||
"create apps/web/templates/authors/index.html.erb",
|
||||
"create spec/web/views/authors/index_spec.rb",
|
||||
"insert apps/web/config/routes.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate action web authors#index", output
|
||||
|
||||
#
|
||||
# apps/web/controllers/authors/index.rb
|
||||
#
|
||||
expect("apps/web/controllers/authors/index.rb").to have_file_content <<~END
|
||||
module Web
|
||||
module Controllers
|
||||
module Authors
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# apps/web/views/authors/index.rb
|
||||
#
|
||||
expect("apps/web/views/authors/index.rb").to have_file_content <<~END
|
||||
module Web
|
||||
module Views
|
||||
module Authors
|
||||
class Index
|
||||
include Web::View
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# apps/web/config/routes.rb
|
||||
#
|
||||
expect("apps/web/config/routes.rb").to have_file_content(%r{get '/authors', to: 'authors#index'})
|
||||
end
|
||||
end
|
||||
|
||||
it "generates namespaced action" do
|
||||
with_project("bookshelf_generate_action") do
|
||||
output = [
|
||||
"create spec/web/controllers/api/authors/index_spec.rb",
|
||||
"create apps/web/controllers/api/authors/index.rb",
|
||||
"create apps/web/views/api/authors/index.rb",
|
||||
"create apps/web/templates/api/authors/index.html.erb",
|
||||
"create spec/web/views/api/authors/index_spec.rb",
|
||||
"insert apps/web/config/routes.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate action web api/authors#index", output
|
||||
|
||||
#
|
||||
# apps/web/controllers/api/authors/index.rb
|
||||
#
|
||||
expect("apps/web/controllers/api/authors/index.rb").to have_file_content <<~END
|
||||
module Web
|
||||
module Controllers
|
||||
module Api
|
||||
module Authors
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# apps/web/views/api/authors/index.rb
|
||||
#
|
||||
expect("apps/web/views/api/authors/index.rb").to have_file_content <<~END
|
||||
module Web
|
||||
module Views
|
||||
module Api
|
||||
module Authors
|
||||
class Index
|
||||
include Web::View
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# apps/web/config/routes.rb
|
||||
#
|
||||
expect("apps/web/config/routes.rb").to have_file_content(%r{get '/api/authors', to: 'api/authors#index'})
|
||||
end
|
||||
end
|
||||
|
||||
it "generates non-RESTful actions" do
|
||||
with_project do
|
||||
run_cmd "hanami generate action web sessions#sign_out"
|
||||
|
||||
#
|
||||
# apps/web/config/routes.rb
|
||||
#
|
||||
expect("apps/web/config/routes.rb").to have_file_content(%r{get '/sessions/sign_out', to: 'sessions#sign_out'})
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing arguments" do
|
||||
with_project("bookshelf_generate_action_without_args") do
|
||||
output = <<~OUT
|
||||
ERROR: "hanami generate action" was called with no arguments
|
||||
Usage: "hanami generate action APP ACTION"
|
||||
OUT
|
||||
run_cmd "hanami generate action", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing app" do
|
||||
with_project("bookshelf_generate_action_without_app") do
|
||||
output = <<~OUT
|
||||
ERROR: "hanami generate action" was called with arguments ["home#index"]
|
||||
Usage: "hanami generate action APP ACTION"
|
||||
OUT
|
||||
|
||||
run_cmd "hanami generate action home#index", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with unknown app" do
|
||||
with_project("bookshelf_generate_action_with_unknown_app") do
|
||||
output = "`foo' is not a valid APP. Please specify one of: `web'"
|
||||
|
||||
run_cmd "hanami generate action foo home#index", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
context "--url" do
|
||||
it "generates action" do
|
||||
with_project("bookshelf_generate_action_url") do
|
||||
output = [
|
||||
"insert apps/web/config/routes.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate action web home#index --url=/", output
|
||||
|
||||
#
|
||||
# apps/web/config/routes.rb
|
||||
#
|
||||
expect("apps/web/config/routes.rb").to have_file_content(%r{get '/', to: 'home#index'})
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing argument" do
|
||||
with_project("bookshelf_generate_action_missing_url") do
|
||||
output = "`' is not a valid URL"
|
||||
run_cmd "hanami generate action web books#create --url=", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "--skip-view" do
|
||||
it "generates action" do
|
||||
with_project("bookshelf_generate_action_skip_view") do
|
||||
output = [
|
||||
"create apps/web/controllers/status/check.rb",
|
||||
"create spec/web/controllers/status/check_spec.rb",
|
||||
"insert apps/web/config/routes.rb"
|
||||
]
|
||||
run_cmd "hanami generate action web status#check --skip-view", output
|
||||
|
||||
#
|
||||
# apps/web/controllers/status/check.rb
|
||||
#
|
||||
expect("apps/web/controllers/status/check.rb").to have_file_content <<~END
|
||||
module Web
|
||||
module Controllers
|
||||
module Status
|
||||
class Check
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = 'OK'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
|
||||
it "generates namespaced action" do
|
||||
with_project("bookshelf_generate_action_skip_view") do
|
||||
output = [
|
||||
"create apps/web/controllers/api/authors/index.rb",
|
||||
"create spec/web/controllers/api/authors/index_spec.rb",
|
||||
"insert apps/web/config/routes.rb"
|
||||
]
|
||||
run_cmd "hanami generate action web api/authors#index --skip-view", output
|
||||
|
||||
#
|
||||
# apps/web/controllers/status/check.rb
|
||||
#
|
||||
expect("apps/web/controllers/api/authors/index.rb").to have_file_content <<~END
|
||||
module Web
|
||||
module Controllers
|
||||
module Api
|
||||
module Authors
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = 'OK'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "--method" do
|
||||
it "generates action" do
|
||||
with_project("bookshelf_generate_action_method") do
|
||||
output = [
|
||||
"insert apps/web/config/routes.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate action web books#create --method=POST", output
|
||||
|
||||
#
|
||||
# apps/web/config/routes.rb
|
||||
#
|
||||
expect("apps/web/config/routes.rb").to have_file_content(%r{post '/books', to: 'books#create'})
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing argument" do
|
||||
with_project("bookshelf_generate_action_missing_method") do
|
||||
output = "`' is not a valid HTTP method. Please use one of: `GET' `POST' `PUT' `DELETE' `HEAD' `OPTIONS' `TRACE' `PATCH' `OPTIONS' `LINK' `UNLINK'"
|
||||
run_cmd "hanami generate action web books#create --method=", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with unknown argument" do
|
||||
with_project('bookshelf_generate_action_unknown_method') do
|
||||
output = "`FOO' is not a valid HTTP method. Please use one of: `GET' `POST' `PUT' `DELETE' `HEAD' `OPTIONS' `TRACE' `PATCH' `OPTIONS' `LINK' `UNLINK'"
|
||||
run_cmd "hanami generate action web books#create --method=FOO", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "erb" do
|
||||
it "generates action" do
|
||||
with_project("bookshelf_generate_action_erb", template: "erb") do
|
||||
output = [
|
||||
"create apps/web/templates/books/index.html.erb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate action web books#index", output
|
||||
|
||||
#
|
||||
# apps/web/templates/books/index.html.erb
|
||||
#
|
||||
expect("apps/web/templates/books/index.html.erb").to have_file_content <<~END
|
||||
END
|
||||
|
||||
#
|
||||
# spec/web/views/books/index_spec.rb
|
||||
#
|
||||
expect("spec/web/views/books/index_spec.rb").to have_file_content %r{'apps/web/templates/books/index.html.erb'}
|
||||
end
|
||||
end
|
||||
end # erb
|
||||
|
||||
context "haml" do
|
||||
it "generates action" do
|
||||
with_project("bookshelf_generate_action_haml", template: "haml") do
|
||||
output = [
|
||||
"create apps/web/templates/books/index.html.haml"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate action web books#index", output
|
||||
|
||||
#
|
||||
# apps/web/templates/books/index.html.haml
|
||||
#
|
||||
expect("apps/web/templates/books/index.html.haml").to have_file_content <<~END
|
||||
END
|
||||
|
||||
#
|
||||
# spec/web/views/books/index_spec.rb
|
||||
#
|
||||
expect("spec/web/views/books/index_spec.rb").to have_file_content(%r{'apps/web/templates/books/index.html.haml'})
|
||||
end
|
||||
end
|
||||
end # haml
|
||||
|
||||
context "slim" do
|
||||
it "generates action" do
|
||||
with_project("bookshelf_generate_action_slim", template: "slim") do
|
||||
output = [
|
||||
"create apps/web/templates/books/index.html.slim"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate action web books#index", output
|
||||
|
||||
#
|
||||
# apps/web/templates/books/index.html.slim
|
||||
#
|
||||
expect("apps/web/templates/books/index.html.slim").to have_file_content <<~END
|
||||
END
|
||||
|
||||
#
|
||||
# spec/web/views/books/index_spec.rb
|
||||
#
|
||||
expect("spec/web/views/books/index_spec.rb").to have_file_content %r{'apps/web/templates/books/index.html.slim'}
|
||||
end
|
||||
end
|
||||
end # slim
|
||||
|
||||
context "minitest" do
|
||||
it "generates action" do
|
||||
with_project("bookshelf_generate_action_minitest", test: "minitest") do
|
||||
output = [
|
||||
"create spec/web/controllers/books/index_spec.rb",
|
||||
"create spec/web/views/books/index_spec.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate action web books#index", output
|
||||
|
||||
#
|
||||
# spec/web/controllers/books/index_spec.rb
|
||||
#
|
||||
expect("spec/web/controllers/books/index_spec.rb").to have_file_content <<~END
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe Web::Controllers::Books::Index do
|
||||
let(:action) { Web::Controllers::Books::Index.new }
|
||||
let(:params) { Hash[] }
|
||||
|
||||
it 'is successful' do
|
||||
response = action.call(params)
|
||||
_(response[0]).must_equal 200
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/web/views/books/index_spec.rb
|
||||
#
|
||||
expect("spec/web/views/books/index_spec.rb").to have_file_content <<~END
|
||||
require_relative '../../../spec_helper'
|
||||
|
||||
describe Web::Views::Books::Index do
|
||||
let(:exposures) { Hash[format: :html] }
|
||||
let(:template) { Hanami::View::Template.new('apps/web/templates/books/index.html.erb') }
|
||||
let(:view) { Web::Views::Books::Index.new(template, exposures) }
|
||||
let(:rendered) { view.render }
|
||||
|
||||
it 'exposes #format' do
|
||||
_(view.format).must_equal exposures.fetch(:format)
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
end # minitest
|
||||
|
||||
context "rspec" do
|
||||
it "generates action" do
|
||||
with_project("bookshelf_generate_action_rspec", test: "rspec") do
|
||||
output = [
|
||||
"create spec/web/controllers/books/index_spec.rb",
|
||||
"create spec/web/views/books/index_spec.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate action web books#index", output
|
||||
|
||||
#
|
||||
# spec/web/controllers/books/index_spec.rb
|
||||
#
|
||||
expect("spec/web/controllers/books/index_spec.rb").to have_file_content <<~END
|
||||
RSpec.describe Web::Controllers::Books::Index, type: :action do
|
||||
let(:action) { described_class.new }
|
||||
let(:params) { Hash[] }
|
||||
|
||||
it 'is successful' do
|
||||
response = action.call(params)
|
||||
expect(response[0]).to eq 200
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/web/views/books/index_spec.rb
|
||||
#
|
||||
expect("spec/web/views/books/index_spec.rb").to have_file_content <<~END
|
||||
RSpec.describe Web::Views::Books::Index, type: :view do
|
||||
let(:exposures) { Hash[format: :html] }
|
||||
let(:template) { Hanami::View::Template.new('apps/web/templates/books/index.html.erb') }
|
||||
let(:view) { described_class.new(template, exposures) }
|
||||
let(:rendered) { view.render }
|
||||
|
||||
it 'exposes #format' do
|
||||
expect(view.format).to eq exposures.fetch(:format)
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
end # rspec
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami generate action
|
||||
|
||||
Usage:
|
||||
hanami generate action APP ACTION
|
||||
|
||||
Description:
|
||||
Generate an action for app
|
||||
|
||||
Arguments:
|
||||
APP # REQUIRED The app name (eg. `web`)
|
||||
ACTION # REQUIRED The action name (eg. `home#index`)
|
||||
|
||||
Options:
|
||||
--url=VALUE # The action URL
|
||||
--method=VALUE # The action HTTP method
|
||||
--[no-]skip-view # Skip view and template, default: false
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami generate action web home#index # Basic usage
|
||||
hanami generate action admin home#index # Generate for `admin` app
|
||||
hanami generate action web home#index --url=/ # Specify URL
|
||||
hanami generate action web sessions#destroy --method=GET # Specify HTTP method
|
||||
hanami generate action web books#create --skip-view # Skip view and template
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami generate action --help', output
|
||||
end
|
||||
end
|
||||
end # action
|
||||
end
|
|
@ -1,215 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "hanami/utils/string"
|
||||
|
||||
RSpec.describe "hanami generate", type: :integration do
|
||||
describe "app" do
|
||||
context "with app name" do
|
||||
it_behaves_like "a new app" do
|
||||
let(:input) { "admin" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with underscored app name" do
|
||||
it_behaves_like "a new app" do
|
||||
let(:input) { "cool_app" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with dashed app name" do
|
||||
it_behaves_like "a new app" do
|
||||
let(:input) { "awesome-app" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with camel case app name" do
|
||||
it_behaves_like "a new app" do
|
||||
let(:input) { "CaMElAPp" }
|
||||
end
|
||||
end
|
||||
|
||||
context "without require_relative" do
|
||||
it "generates app" do
|
||||
with_project("bookshelf_generate_app_without_require_relative") do
|
||||
app = "no_req_relative"
|
||||
app_name = Hanami::Utils::String.new(app).classify
|
||||
output = [
|
||||
"insert config/environment.rb"
|
||||
]
|
||||
|
||||
File.write(
|
||||
"config/environment.rb",
|
||||
File
|
||||
.read("config/environment.rb")
|
||||
.lines
|
||||
.reject { |l| l[/^require_relative '.*'\n$/] }
|
||||
.reject { |l| l[/^ mount Web::App, at: '\/'\n$/] }
|
||||
.join("")
|
||||
)
|
||||
|
||||
run_cmd "hanami generate app #{app}", output
|
||||
|
||||
#
|
||||
# config/environment.rb
|
||||
#
|
||||
expect("config/environment.rb").to have_file_content(%r{require_relative '../apps/#{app}/app'})
|
||||
expect("config/environment.rb").to have_file_content(%r{mount #{app_name}::App, at: '/no_req_relative'})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "--app-base-url" do
|
||||
it "generates app" do
|
||||
with_project("bookshelf_generate_app_app_base_url") do
|
||||
app = "api"
|
||||
app_name = Hanami::Utils::String.new(app).classify
|
||||
output = [
|
||||
"insert config/environment.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate app #{app} --app-base-url=/api/v1", output
|
||||
|
||||
#
|
||||
# config/environment.rb
|
||||
#
|
||||
expect("config/environment.rb").to have_file_content(%r{require_relative '../apps/#{app}/app'})
|
||||
expect("config/environment.rb").to have_file_content(%r{mount #{app_name}::App, at: '/api/v1'})
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing argument" do
|
||||
with_project("bookshelf_generate_app_missing_app_base_url") do
|
||||
output = "`' is not a valid URL"
|
||||
run_cmd "hanami generate app foo --app-base-url=", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "erb" do
|
||||
it "generates app" do
|
||||
with_project("bookshelf_generate_app_erb", template: :erb) do
|
||||
app = "admin"
|
||||
app_name = Hanami::Utils::String.new(app).classify
|
||||
output = [
|
||||
"create apps/#{app}/templates/app.html.erb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate app #{app}", output
|
||||
|
||||
#
|
||||
# apps/admin/templates/app.html.erb
|
||||
#
|
||||
expect("apps/admin/templates/app.html.erb").to have_file_content <<~END
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>#{app_name}</title>
|
||||
<%= favicon %>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
END
|
||||
#
|
||||
# spec/admin/views/app_layout_spec.rb
|
||||
#
|
||||
expect("spec/admin/views/app_layout_spec.rb").to have_file_content(%r{Admin::Views::AppLayout})
|
||||
end
|
||||
end
|
||||
end # erb
|
||||
|
||||
context "haml" do
|
||||
it "generates app" do
|
||||
with_project("bookshelf_generate_app_haml", template: :haml) do
|
||||
app = "admin"
|
||||
app_name = Hanami::Utils::String.new(app).classify
|
||||
output = [
|
||||
"create apps/#{app}/templates/app.html.haml"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate app #{app}", output
|
||||
|
||||
#
|
||||
# apps/admin/templates/app.html.haml
|
||||
#
|
||||
expect("apps/admin/templates/app.html.haml").to have_file_content <<~END
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
%title #{app_name}
|
||||
= favicon
|
||||
%body
|
||||
= yield
|
||||
END
|
||||
|
||||
#
|
||||
# spec/admin/views/app_layout_spec.rb
|
||||
#
|
||||
expect("spec/admin/views/app_layout_spec.rb").to have_file_content(%r{Admin::Views::AppLayout})
|
||||
end
|
||||
end
|
||||
end # haml
|
||||
|
||||
context "slim" do
|
||||
it "generates app" do
|
||||
with_project("bookshelf_generate_app_slim", template: :slim) do
|
||||
app = "admin"
|
||||
app_name = Hanami::Utils::String.new(app).classify
|
||||
output = [
|
||||
"create apps/#{app}/templates/app.html.slim"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate app #{app}", output
|
||||
|
||||
#
|
||||
# apps/admin/templates/app.html.slim
|
||||
#
|
||||
expect("apps/admin/templates/app.html.slim").to have_file_content <<~END
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
title
|
||||
| #{app_name}
|
||||
= favicon
|
||||
body
|
||||
= yield
|
||||
END
|
||||
|
||||
#
|
||||
# spec/admin/views/app_layout_spec.rb
|
||||
#
|
||||
expect("spec/admin/views/app_layout_spec.rb").to have_file_content(%r{Admin::Views::AppLayout})
|
||||
end
|
||||
end
|
||||
end # slim
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami generate app
|
||||
|
||||
Usage:
|
||||
hanami generate app APP
|
||||
|
||||
Description:
|
||||
Generate an app
|
||||
|
||||
Arguments:
|
||||
APP # REQUIRED The app name (eg. `web`)
|
||||
|
||||
Options:
|
||||
--app-base-url=VALUE # The app base URL (eg. `/api/v1`)
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami generate app admin # Generate `admin` app
|
||||
hanami generate app api --app-base-url=/api/v1 # Generate `api` app and mount at `/api/v1`
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami generate app --help', output
|
||||
end
|
||||
end
|
||||
end # app
|
||||
end
|
|
@ -1,189 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami generate", type: :integration do
|
||||
describe "mailer" do
|
||||
context "generates a new mailer" do
|
||||
let(:output) do
|
||||
["create lib/bookshelf_generate_mailer/mailers/welcome.rb",
|
||||
"create spec/bookshelf_generate_mailer/mailers/welcome_spec.rb",
|
||||
"create lib/bookshelf_generate_mailer/mailers/templates/welcome.txt.erb",
|
||||
"create lib/bookshelf_generate_mailer/mailers/templates/welcome.html.erb"]
|
||||
end
|
||||
|
||||
it 'generate the mailer files' do
|
||||
with_project('bookshelf_generate_mailer', test: 'rspec') do
|
||||
run_cmd "hanami generate mailer welcome", output
|
||||
#
|
||||
# lib/bookshelf_generate_mailer/mailers/welcome.rb
|
||||
#
|
||||
expect("lib/bookshelf_generate_mailer/mailers/welcome.rb").to have_file_content <<~END
|
||||
module Mailers
|
||||
class Welcome
|
||||
include Hanami::Mailer
|
||||
|
||||
from '<from>'
|
||||
to '<to>'
|
||||
subject 'Hello'
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
expect("lib/bookshelf_generate_mailer/mailers/templates/welcome.txt.erb").to have_file_content ""
|
||||
expect("lib/bookshelf_generate_mailer/mailers/templates/welcome.html.erb").to have_file_content ""
|
||||
end
|
||||
end
|
||||
|
||||
it 'generates a proper minitest file' do
|
||||
with_project('bookshelf_generate_mailer', test: 'minitest') do
|
||||
run_cmd "hanami generate mailer welcome", output
|
||||
#
|
||||
# spec/bookshelf_generate_mailer/mailers/welcome_spec.rb
|
||||
#
|
||||
expect("spec/bookshelf_generate_mailer/mailers/welcome_spec.rb").to have_file_content <<~END
|
||||
require_relative '../../spec_helper'
|
||||
|
||||
describe Mailers::Welcome do
|
||||
it 'delivers email' do
|
||||
mail = Mailers::Welcome.deliver
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
|
||||
it 'generates a proper RSpec file' do
|
||||
with_project('bookshelf_generate_mailer', test: 'rspec') do
|
||||
run_cmd "hanami generate mailer welcome", output
|
||||
#
|
||||
# spec/bookshelf_generate_mailer/mailers/welcome_spec.rb
|
||||
#
|
||||
expect("spec/bookshelf_generate_mailer/mailers/welcome_spec.rb").to have_file_content <<~END
|
||||
RSpec.describe Mailers::Welcome, type: :mailer do
|
||||
it 'delivers email' do
|
||||
mail = Mailers::Welcome.deliver
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "generates mailer with options from, to and subject with single quotes" do
|
||||
with_project("bookshelf_generate_mailer_with_options") do
|
||||
output = [
|
||||
"create spec/bookshelf_generate_mailer_with_options/mailers/welcome_spec.rb",
|
||||
"create lib/bookshelf_generate_mailer_with_options/mailers/welcome.rb",
|
||||
"create lib/bookshelf_generate_mailer_with_options/mailers/templates/welcome.txt.erb",
|
||||
"create lib/bookshelf_generate_mailer_with_options/mailers/templates/welcome.html.erb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate mailer welcome --from=\"'mail@example.com'\" --to=\"'user@example.com'\" --subject=\"'Let\'s start'\"", output
|
||||
|
||||
expect("lib/bookshelf_generate_mailer_with_options/mailers/welcome.rb").to have_file_content <<~END
|
||||
module Mailers
|
||||
class Welcome
|
||||
include Hanami::Mailer
|
||||
|
||||
from 'mail@example.com'
|
||||
to 'user@example.com'
|
||||
subject 'Let\'s start'
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
|
||||
it "generates mailer with options from, to and subject with double quotes" do
|
||||
with_project("bookshelf_generate_mailer_with_options") do
|
||||
output = [
|
||||
"create spec/bookshelf_generate_mailer_with_options/mailers/welcome_spec.rb",
|
||||
"create lib/bookshelf_generate_mailer_with_options/mailers/welcome.rb",
|
||||
"create lib/bookshelf_generate_mailer_with_options/mailers/templates/welcome.txt.erb",
|
||||
"create lib/bookshelf_generate_mailer_with_options/mailers/templates/welcome.html.erb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate mailer welcome --from='\"mail@example.com\"' --to='\"user@example.com\"' --subject='\"Come on \"Folks\"\"'", output
|
||||
|
||||
expect("lib/bookshelf_generate_mailer_with_options/mailers/welcome.rb").to have_file_content <<~END
|
||||
module Mailers
|
||||
class Welcome
|
||||
include Hanami::Mailer
|
||||
|
||||
from 'mail@example.com'
|
||||
to 'user@example.com'
|
||||
subject 'Come on "Folks"'
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
|
||||
it "generates mailer with options from, to and subject without quotes" do
|
||||
with_project("bookshelf_generate_mailer_with_options") do
|
||||
output = [
|
||||
"create spec/bookshelf_generate_mailer_with_options/mailers/welcome_spec.rb",
|
||||
"create lib/bookshelf_generate_mailer_with_options/mailers/welcome.rb",
|
||||
"create lib/bookshelf_generate_mailer_with_options/mailers/templates/welcome.txt.erb",
|
||||
"create lib/bookshelf_generate_mailer_with_options/mailers/templates/welcome.html.erb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate mailer welcome --from=mail@example.com --to=user@example.com --subject=Welcome", output
|
||||
|
||||
expect("lib/bookshelf_generate_mailer_with_options/mailers/welcome.rb").to have_file_content <<~END
|
||||
module Mailers
|
||||
class Welcome
|
||||
include Hanami::Mailer
|
||||
|
||||
from 'mail@example.com'
|
||||
to 'user@example.com'
|
||||
subject 'Welcome'
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
|
||||
it "fails with missing arguments" do
|
||||
with_project("bookshelf_generate_mailer_without_args") do
|
||||
output = <<~OUT
|
||||
ERROR: "hanami generate mailer" was called with no arguments
|
||||
Usage: "hanami generate mailer MAILER"
|
||||
OUT
|
||||
|
||||
run_cmd "hanami generate mailer", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami generate mailer
|
||||
|
||||
Usage:
|
||||
hanami generate mailer MAILER
|
||||
|
||||
Description:
|
||||
Generate a mailer
|
||||
|
||||
Arguments:
|
||||
MAILER # REQUIRED The mailer name (eg. `welcome`)
|
||||
|
||||
Options:
|
||||
--from=VALUE # The default `from` field of the mail
|
||||
--to=VALUE # The default `to` field of the mail
|
||||
--subject=VALUE # The mail subject
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami generate mailer welcome # Basic usage
|
||||
hanami generate mailer welcome --from="noreply@example.com" # Generate with default `from` value
|
||||
hanami generate mailer announcement --to="users@example.com" # Generate with default `to` value
|
||||
hanami generate mailer forgot_password --subject="Your password reset" # Generate with default `subject`
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami generate mailer --help', output
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,72 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami generate", type: :integration do
|
||||
describe "migration" do
|
||||
context "with migration name" do
|
||||
it_behaves_like "a new migration" do
|
||||
let(:input) { "users" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with underscored name" do
|
||||
it_behaves_like "a new migration" do
|
||||
let(:input) { "create_users" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with dashed name" do
|
||||
it_behaves_like "a new migration" do
|
||||
let(:input) { "add-verified-at-to-users" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with camel case app name" do
|
||||
it_behaves_like "a new migration" do
|
||||
let(:input) { "AddUniqueIndexUsersEmail" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with missing argument" do
|
||||
it "fails" do
|
||||
with_project('bookshelf_generate_migration_missing_arguments') do
|
||||
output = <<-END
|
||||
ERROR: "hanami generate migration" was called with no arguments
|
||||
Usage: "hanami generate migration MIGRATION"
|
||||
END
|
||||
|
||||
run_cmd "hanami generate migration", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
banner = <<~OUT
|
||||
Command:
|
||||
hanami generate migration
|
||||
|
||||
Usage:
|
||||
hanami generate migration MIGRATION
|
||||
|
||||
Description:
|
||||
Generate a migration
|
||||
|
||||
Arguments:
|
||||
MIGRATION # REQUIRED The migration name (eg. `create_users`)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
OUT
|
||||
|
||||
output = [
|
||||
banner,
|
||||
%r{ hanami generate migration create_users # Generate `db/migrations/[\d]{14}_create_users.rb`},
|
||||
]
|
||||
|
||||
run_cmd 'hanami generate migration --help', output
|
||||
end
|
||||
end
|
||||
end # migration
|
||||
end
|
|
@ -1,290 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "hanami/utils/string"
|
||||
|
||||
RSpec.describe "hanami generate", type: :integration do
|
||||
describe "model" do
|
||||
context "with model name" do
|
||||
it_behaves_like "a new model" do
|
||||
let(:input) { "user" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with underscored name" do
|
||||
it_behaves_like "a new model" do
|
||||
let(:input) { "discounted_book" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with dashed name" do
|
||||
it_behaves_like "a new model" do
|
||||
let(:input) { "user-event" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with camel case name" do
|
||||
it_behaves_like "a new model" do
|
||||
let(:input) { "VerifiedUser" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with missing argument" do
|
||||
it "fails" do
|
||||
with_project('bookshelf_generate_model_missing_arguments') do
|
||||
output = <<-END
|
||||
ERROR: "hanami generate model" was called with no arguments
|
||||
Usage: "hanami generate model MODEL"
|
||||
END
|
||||
|
||||
run_cmd "hanami generate model", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with missing migrations directory" do
|
||||
it "will create directory and migration" do
|
||||
with_project do
|
||||
model_name = "book"
|
||||
directory = Pathname.new("db").join("migrations")
|
||||
FileUtils.rm_rf(directory)
|
||||
|
||||
run_cmd "hanami generate model #{model_name}"
|
||||
expect(directory).to be_directory
|
||||
|
||||
migration = directory.children.find do |m|
|
||||
m.to_s.include?(model_name)
|
||||
end
|
||||
|
||||
expect(migration).to_not be(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with skip-migration" do
|
||||
it "doesn't create a migration file" do
|
||||
model_name = "user"
|
||||
table_name = "users"
|
||||
project = "bookshelf_generate_model_skip_migration"
|
||||
with_project(project) do
|
||||
run_cmd "hanami generate model #{model_name} --skip-migration"
|
||||
#
|
||||
# db/migrations/<timestamp>_create_<models>.rb
|
||||
#
|
||||
migrations = Pathname.new("db").join("migrations").children
|
||||
file = migrations.find do |child|
|
||||
child.to_s.include?("create_#{table_name}")
|
||||
end
|
||||
|
||||
expect(file).to be_nil, "Expected to not find a migration matching: create_#{table_name}. Found #{file&.to_s}"
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't create a migration file when --relation is used" do
|
||||
model_name = "user"
|
||||
table_name = "accounts"
|
||||
project = "bookshelf_generate_model_skip_migration"
|
||||
with_project(project) do
|
||||
run_cmd "hanami generate model #{model_name} --skip-migration --relation=#{table_name}"
|
||||
#
|
||||
# db/migrations/<timestamp>_create_<models>.rb
|
||||
#
|
||||
migrations = Pathname.new("db").join("migrations").children
|
||||
file = migrations.find do |child|
|
||||
child.to_s.include?("create_#{table_name}")
|
||||
end
|
||||
|
||||
expect(file).to be_nil, "Expected to not find a migration matching: create_#{table_name}. Found #{file&.to_s}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with relation option" do
|
||||
let(:project) { "generate_model_with_relation_name" }
|
||||
let(:model_name) { "stimulus" }
|
||||
let(:class_name) { "Stimulus" }
|
||||
let(:relation_name) { "stimuli" }
|
||||
|
||||
it "creates correct entity, repository, and migration" do
|
||||
with_project(project) do
|
||||
output = [
|
||||
"create lib/#{project}/entities/#{model_name}.rb",
|
||||
"create lib/#{project}/repositories/#{model_name}_repository.rb",
|
||||
/create db\/migrations\/(\d+)_create_#{relation_name}.rb/
|
||||
]
|
||||
|
||||
run_cmd "hanami generate model #{model_name} --relation=#{relation_name}", output
|
||||
|
||||
expect("lib/#{project}/repositories/#{model_name}_repository.rb").to have_file_content <<~END
|
||||
class #{class_name}Repository < Hanami::Repository
|
||||
self.relation = :#{relation_name}
|
||||
end
|
||||
END
|
||||
|
||||
migration = Pathname.new("db").join("migrations").children.find do |child|
|
||||
child.to_s.include?("create_#{relation_name}")
|
||||
end
|
||||
|
||||
expect(migration.to_s).to have_file_content <<~END
|
||||
Hanami::Model.migration do
|
||||
change do
|
||||
create_table :#{relation_name} do
|
||||
primary_key :id
|
||||
|
||||
column :created_at, DateTime, null: false
|
||||
column :updated_at, DateTime, null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
|
||||
it "handles CamelCase arguments" do
|
||||
with_project(project) do
|
||||
model = "sheep"
|
||||
relation_name = "black_sheeps"
|
||||
output = [
|
||||
"create lib/#{project}/entities/#{model}.rb",
|
||||
"create lib/#{project}/repositories/#{model}_repository.rb",
|
||||
/create db\/migrations\/(\d+)_create_#{relation_name}.rb/
|
||||
]
|
||||
|
||||
run_cmd "hanami generate model #{model} --relation=BlackSheeps", output
|
||||
|
||||
expect("lib/#{project}/repositories/sheep_repository.rb").to have_file_content <<~END
|
||||
class SheepRepository < Hanami::Repository
|
||||
self.relation = :#{relation_name}
|
||||
end
|
||||
END
|
||||
|
||||
migration = Pathname.new("db").join("migrations").children.find do |child|
|
||||
child.to_s.include?("create_#{relation_name}")
|
||||
end
|
||||
|
||||
expect(migration.to_s).to have_file_content <<~END
|
||||
Hanami::Model.migration do
|
||||
change do
|
||||
create_table :#{relation_name} do
|
||||
primary_key :id
|
||||
|
||||
column :created_at, DateTime, null: false
|
||||
column :updated_at, DateTime, null: false
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
|
||||
it "returns error for blank option" do
|
||||
with_project(project) do
|
||||
run_cmd "hanami generate model #{model_name} --relation=", "`' is not a valid relation name", exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "minitest" do
|
||||
it "generates model" do
|
||||
project = "bookshelf_generate_model_minitest"
|
||||
|
||||
with_project(project, test: :minitest) do
|
||||
model = "book"
|
||||
class_name = Hanami::Utils::String.new(model).classify
|
||||
output = [
|
||||
"create spec/#{project}/entities/#{model}_spec.rb",
|
||||
"create spec/#{project}/repositories/#{model}_repository_spec.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate model #{model}", output
|
||||
|
||||
#
|
||||
# spec/<project>/entities/<model>_spec.rb
|
||||
#
|
||||
expect("spec/#{project}/entities/#{model}_spec.rb").to have_file_content <<~END
|
||||
require_relative '../../spec_helper'
|
||||
|
||||
describe #{class_name} do
|
||||
# place your tests here
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/<project>/repositories/<model>_repository_spec.rb
|
||||
#
|
||||
expect("spec/#{project}/repositories/#{model}_repository_spec.rb").to have_file_content <<~END
|
||||
require_relative '../../spec_helper'
|
||||
|
||||
describe #{class_name}Repository do
|
||||
# place your tests here
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
end # minitest
|
||||
|
||||
context "rspec" do
|
||||
it "generates model" do
|
||||
project = "bookshelf_generate_model_rspec"
|
||||
|
||||
with_project(project, test: :rspec) do
|
||||
model = "book"
|
||||
class_name = Hanami::Utils::String.new(model).classify
|
||||
output = [
|
||||
"create spec/#{project}/entities/#{model}_spec.rb",
|
||||
"create spec/#{project}/repositories/#{model}_repository_spec.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami generate model #{model}", output
|
||||
|
||||
#
|
||||
# spec/<project>/entities/<model>_spec.rb
|
||||
#
|
||||
expect("spec/#{project}/entities/#{model}_spec.rb").to have_file_content <<~END
|
||||
RSpec.describe #{class_name}, type: :entity do
|
||||
# place your tests here
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/<project>/repositories/<model>_repository_spec.rb
|
||||
#
|
||||
expect("spec/#{project}/repositories/#{model}_repository_spec.rb").to have_file_content <<~END
|
||||
RSpec.describe BookRepository, type: :repository do
|
||||
# place your tests here
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
end # rspec
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami generate model
|
||||
|
||||
Usage:
|
||||
hanami generate model MODEL
|
||||
|
||||
Description:
|
||||
Generate a model
|
||||
|
||||
Arguments:
|
||||
MODEL # REQUIRED Model name (eg. `user`)
|
||||
|
||||
Options:
|
||||
--[no-]skip-migration # Skip migration, default: false
|
||||
--relation=VALUE # Name of the database relation, default: pluralized model name
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami generate model user # Generate `User` entity, `UserRepository` repository, and the migration
|
||||
hanami generate model user --skip-migration # Generate `User` entity and `UserRepository` repository
|
||||
hanami generate model user --relation=accounts # Generate `User` entity, `UserRepository` and migration to create `accounts` table
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami generate model --help', output
|
||||
end
|
||||
end
|
||||
end # model
|
||||
end
|
|
@ -1,56 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami generate", type: :integration do
|
||||
describe "secret" do
|
||||
context "without app name" do
|
||||
it "prints secret" do
|
||||
with_project do
|
||||
generate "secret"
|
||||
|
||||
expect(out).to match(/[\w]{64}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with app name" do
|
||||
it "prints secret" do
|
||||
with_project do
|
||||
generate "secret web"
|
||||
|
||||
expect(out).to match(%r{WEB_SESSIONS_SECRET="[\w]{64}"})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it 'prints help message' do
|
||||
with_project do
|
||||
banner = <<~OUT
|
||||
Command:
|
||||
hanami generate secret
|
||||
|
||||
Usage:
|
||||
hanami generate secret [APP]
|
||||
|
||||
Description:
|
||||
Generate session secret
|
||||
|
||||
Arguments:
|
||||
APP # The app name (eg. `web`)
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
OUT
|
||||
|
||||
output = [
|
||||
banner,
|
||||
# %r{ hanami generate secret # Prints secret (eg. `[\w]{64}`)},
|
||||
# %r{ hanami generate secret web # Prints session secret (eg. `WEB_SESSIONS_SECRET=[\w]{64}`)}
|
||||
]
|
||||
|
||||
run_cmd 'hanami generate secret --help', output
|
||||
end
|
||||
end
|
||||
end # secret
|
||||
end
|
|
@ -1,19 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami generate", type: :integration do
|
||||
it "prints subcommands" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Commands:
|
||||
hanami generate action APP ACTION # Generate an action for app
|
||||
hanami generate app APP # Generate an app
|
||||
hanami generate mailer MAILER # Generate a mailer
|
||||
hanami generate migration MIGRATION # Generate a migration
|
||||
hanami generate model MODEL # Generate a model
|
||||
hanami generate secret [APP] # Generate session secret
|
||||
OUT
|
||||
|
||||
run_cmd "hanami generate", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,235 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami new", type: :integration do
|
||||
describe "--database" do
|
||||
context "postgres" do
|
||||
it "generates project" do
|
||||
project = "bookshelf_postgresql"
|
||||
output = [
|
||||
"create db/migrations/.gitkeep",
|
||||
"create db/schema.sql"
|
||||
]
|
||||
|
||||
run_cmd "hanami new #{project} --database=postgres", output
|
||||
|
||||
within_project_directory(project) do
|
||||
#
|
||||
# .env.development
|
||||
#
|
||||
development_url = Platform.match do
|
||||
engine(:ruby) { "postgresql://localhost/#{project}_development" }
|
||||
engine(:jruby) { "jdbc:postgresql://localhost/#{project}_development" }
|
||||
end
|
||||
|
||||
expect(".env.development").to have_file_content(%r{DATABASE_URL="#{development_url}"})
|
||||
|
||||
#
|
||||
# .env.test
|
||||
#
|
||||
test_url = Platform.match do
|
||||
engine(:ruby) { "postgresql://localhost/#{project}_test" }
|
||||
engine(:jruby) { "jdbc:postgresql://localhost/#{project}_test" }
|
||||
end
|
||||
|
||||
expect(".env.test").to have_file_content(%r{DATABASE_URL="#{test_url}"})
|
||||
|
||||
#
|
||||
# Gemfile
|
||||
#
|
||||
gem_name = Platform.match do
|
||||
engine(:ruby) { "pg" }
|
||||
engine(:jruby) { "jdbc-postgres" }
|
||||
end
|
||||
|
||||
expect("Gemfile").to have_file_content(%r{gem '#{gem_name}'})
|
||||
|
||||
#
|
||||
# config/environment.rb
|
||||
#
|
||||
expect("config/environment.rb").to have_file_content(%r{ adapter :sql, ENV.fetch\('DATABASE_URL'\)})
|
||||
expect("config/environment.rb").to have_file_content(%r{ migrations 'db/migrations'})
|
||||
expect("config/environment.rb").to have_file_content(%r{ schema 'db/schema.sql'})
|
||||
|
||||
#
|
||||
# db/migrations/.gitkeep
|
||||
#
|
||||
expect("db/migrations/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# db/schema.sql
|
||||
#
|
||||
expect("db/schema.sql").to be_an_existing_file
|
||||
|
||||
#
|
||||
# .gitignore
|
||||
#
|
||||
expect(".gitignore").to have_file_content <<-END
|
||||
/public/assets*
|
||||
/tmp
|
||||
.env.local
|
||||
.env.*.local
|
||||
END
|
||||
end
|
||||
end
|
||||
end # postgres
|
||||
|
||||
describe "sqlite" do
|
||||
it "generates project" do
|
||||
project = "bookshelf_sqlite"
|
||||
output = [
|
||||
"create db/migrations/.gitkeep",
|
||||
"create db/schema.sql"
|
||||
]
|
||||
|
||||
run_cmd "hanami new #{project} --database=sqlite", output
|
||||
|
||||
within_project_directory(project) do
|
||||
#
|
||||
# .env.development
|
||||
#
|
||||
development_url = Platform.match do
|
||||
engine(:ruby) { "sqlite://db/#{project}_development.sqlite" }
|
||||
engine(:jruby) { "jdbc:sqlite://db/#{project}_development.sqlite" }
|
||||
end
|
||||
|
||||
expect(".env.development").to have_file_content(%r{DATABASE_URL="#{development_url}"})
|
||||
|
||||
#
|
||||
# .env.test
|
||||
#
|
||||
test_url = Platform.match do
|
||||
engine(:ruby) { "sqlite://db/#{project}_test.sqlite" }
|
||||
engine(:jruby) { "jdbc:sqlite://db/#{project}_test.sqlite" }
|
||||
end
|
||||
|
||||
expect(".env.test").to have_file_content(%r{DATABASE_URL="#{test_url}"})
|
||||
|
||||
#
|
||||
# Gemfile
|
||||
#
|
||||
gem_name = Platform.match do
|
||||
engine(:ruby) { "sqlite3" }
|
||||
engine(:jruby) { "jdbc-sqlite3" }
|
||||
end
|
||||
|
||||
expect("Gemfile").to have_file_content(%r{gem '#{gem_name}'})
|
||||
|
||||
#
|
||||
# config/environment.rb
|
||||
#
|
||||
expect("config/environment.rb").to have_file_content(%r{ adapter :sql, ENV.fetch\('DATABASE_URL'\)})
|
||||
expect("config/environment.rb").to have_file_content(%r{ migrations 'db/migrations'})
|
||||
expect("config/environment.rb").to have_file_content(%r{ schema 'db/schema.sql'})
|
||||
|
||||
#
|
||||
# db/migrations/.gitkeep
|
||||
#
|
||||
expect("db/migrations/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# db/schema.sql
|
||||
#
|
||||
expect("db/schema.sql").to be_an_existing_file
|
||||
|
||||
#
|
||||
# .gitignore
|
||||
#
|
||||
expect(".gitignore").to have_file_content <<-END
|
||||
/db/*.sqlite
|
||||
/public/assets*
|
||||
/tmp
|
||||
.env.local
|
||||
.env.*.local
|
||||
END
|
||||
end
|
||||
end
|
||||
end # sqlite
|
||||
|
||||
context "mysql" do
|
||||
it "generates project" do
|
||||
project = "bookshelf_mysql"
|
||||
output = [
|
||||
"create db/migrations/.gitkeep",
|
||||
"create db/schema.sql"
|
||||
]
|
||||
|
||||
run_cmd "hanami new #{project} --database=mysql", output
|
||||
|
||||
within_project_directory(project) do
|
||||
#
|
||||
# .env.development
|
||||
#
|
||||
development_url = Platform.match do
|
||||
engine(:ruby) { "mysql2://localhost/#{project}_development" }
|
||||
engine(:jruby) { "jdbc:mysql://localhost/#{project}_development" }
|
||||
end
|
||||
|
||||
expect(".env.development").to have_file_content(%r{DATABASE_URL="#{development_url}"})
|
||||
|
||||
#
|
||||
# .env.test
|
||||
#
|
||||
test_url = Platform.match do
|
||||
engine(:ruby) { "mysql2://localhost/#{project}_test" }
|
||||
engine(:jruby) { "jdbc:mysql://localhost/#{project}_test" }
|
||||
end
|
||||
|
||||
expect(".env.test").to have_file_content(%r{DATABASE_URL="#{test_url}"})
|
||||
|
||||
#
|
||||
# Gemfile
|
||||
#
|
||||
gem_name = Platform.match do
|
||||
engine(:ruby) { "mysql2" }
|
||||
engine(:jruby) { "jdbc-mysql" }
|
||||
end
|
||||
|
||||
expect("Gemfile").to have_file_content(%r{gem '#{gem_name}'})
|
||||
|
||||
#
|
||||
# config/environment.rb
|
||||
#
|
||||
expect("config/environment.rb").to have_file_content(%r{ adapter :sql, ENV.fetch\('DATABASE_URL'\)})
|
||||
expect("config/environment.rb").to have_file_content(%r{ migrations 'db/migrations'})
|
||||
expect("config/environment.rb").to have_file_content(%r{ schema 'db/schema.sql'})
|
||||
|
||||
#
|
||||
# db/migrations/.gitkeep
|
||||
#
|
||||
expect("db/migrations/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# db/schema.sql
|
||||
#
|
||||
expect("db/schema.sql").to be_an_existing_file
|
||||
|
||||
#
|
||||
# .gitignore
|
||||
#
|
||||
expect(".gitignore").to have_file_content <<-END
|
||||
/public/assets*
|
||||
/tmp
|
||||
.env.local
|
||||
.env.*.local
|
||||
END
|
||||
end
|
||||
end
|
||||
end # mysql
|
||||
|
||||
context "missing" do
|
||||
it "returns error" do
|
||||
output = "`' is not a valid database engine"
|
||||
|
||||
run_cmd "hanami new bookshelf --database=", output, exit_status: 1
|
||||
end
|
||||
end # missing
|
||||
|
||||
context "unknown" do
|
||||
it "returns error" do
|
||||
output = "`foo' is not a valid database engine"
|
||||
|
||||
run_cmd "hanami new bookshelf --database=foo", output, exit_status: 1
|
||||
end
|
||||
end # unknown
|
||||
end # database
|
||||
end
|
|
@ -1,27 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami new", type: :integration do
|
||||
describe "--hanami-head" do
|
||||
it "generates project" do
|
||||
project = "bookshelf_hanami_head"
|
||||
|
||||
run_cmd "hanami new #{project} --hanami-head"
|
||||
|
||||
within_project_directory(project) do
|
||||
#
|
||||
# Gemfile
|
||||
#
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami-utils', require: false, git: 'https://github.com/hanami/utils.git', branch: 'develop'})
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami-validations', require: false, git: 'https://github.com/hanami/validations.git', branch: 'develop'})
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami-router', require: false, git: 'https://github.com/hanami/router.git', branch: 'develop'})
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami-controller', require: false, git: 'https://github.com/hanami/controller.git', branch: 'develop'})
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami-view', require: false, git: 'https://github.com/hanami/view.git', branch: 'develop'})
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami-helpers', require: false, git: 'https://github.com/hanami/helpers.git', branch: 'develop'})
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami-mailer', require: false, git: 'https://github.com/hanami/mailer.git', branch: 'develop'})
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami-assets', require: false, git: 'https://github.com/hanami/assets.git', branch: 'develop'})
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami-model', require: false, git: 'https://github.com/hanami/model.git', branch: 'develop'})
|
||||
expect('Gemfile').to have_file_content(%r{gem 'hanami', git: 'https://github.com/hanami/hanami.git', branch: 'develop'})
|
||||
end
|
||||
end
|
||||
end # hanami-head
|
||||
end
|
|
@ -1,118 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami new", type: :integration do
|
||||
describe "--template" do
|
||||
context "erb" do
|
||||
it "generates project" do
|
||||
project = "bookshelf_erb"
|
||||
output = [
|
||||
"create apps/web/templates/app.html.erb"
|
||||
]
|
||||
|
||||
run_cmd "hanami new #{project} --template=erb", output
|
||||
|
||||
within_project_directory(project) do
|
||||
#
|
||||
# .hanamirc
|
||||
#
|
||||
expect(".hanamirc").to have_file_content(%r{template=erb})
|
||||
|
||||
#
|
||||
# apps/web/templates/app.html.erb
|
||||
#
|
||||
expect("apps/web/templates/app.html.erb").to have_file_content <<~END
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web</title>
|
||||
<%= favicon %>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
END
|
||||
end
|
||||
end
|
||||
end # erb
|
||||
|
||||
context "haml" do
|
||||
it "generates project" do
|
||||
project = "bookshelf_erb"
|
||||
output = [
|
||||
"create apps/web/templates/app.html.haml"
|
||||
]
|
||||
|
||||
run_cmd "hanami new #{project} --template=haml", output
|
||||
|
||||
within_project_directory(project) do
|
||||
#
|
||||
# .hanamirc
|
||||
#
|
||||
expect(".hanamirc").to have_file_content(%r{template=haml})
|
||||
|
||||
#
|
||||
# apps/web/templates/app.html.haml
|
||||
#
|
||||
expect("apps/web/templates/app.html.haml").to have_file_content <<~END
|
||||
!!!
|
||||
%html
|
||||
%head
|
||||
%title Web
|
||||
= favicon
|
||||
%body
|
||||
= yield
|
||||
END
|
||||
end
|
||||
end
|
||||
end # haml
|
||||
|
||||
context "slim" do
|
||||
it "generates project" do
|
||||
project = "bookshelf_erb"
|
||||
output = [
|
||||
"create apps/web/templates/app.html.slim"
|
||||
]
|
||||
|
||||
run_cmd "hanami new #{project} --template=slim", output
|
||||
|
||||
within_project_directory(project) do
|
||||
#
|
||||
# .hanamirc
|
||||
#
|
||||
expect(".hanamirc").to have_file_content(%r{template=slim})
|
||||
|
||||
#
|
||||
# apps/web/templates/app.html.slim
|
||||
#
|
||||
expect("apps/web/templates/app.html.slim").to have_file_content <<~END
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
title
|
||||
| Web
|
||||
= favicon
|
||||
body
|
||||
= yield
|
||||
END
|
||||
end
|
||||
end
|
||||
end # slim
|
||||
|
||||
context "missing" do
|
||||
it "returns error" do
|
||||
output = "`' is not a valid template engine. Please use one of: `erb', `haml', `slim'"
|
||||
|
||||
run_cmd "hanami new bookshelf --template=", output, exit_status: 1
|
||||
end
|
||||
end # missing
|
||||
|
||||
context "unknown" do
|
||||
it "returns error" do
|
||||
output = "`foo' is not a valid template engine. Please use one of: `erb', `haml', `slim'"
|
||||
|
||||
run_cmd "hanami new bookshelf --template=foo", output, exit_status: 1
|
||||
end
|
||||
end # unknown
|
||||
end # template
|
||||
end
|
|
@ -1,274 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami new", type: :integration do
|
||||
describe "--test" do
|
||||
context "minitest" do
|
||||
it "generates project" do
|
||||
project = "bookshelf_minitest"
|
||||
output = [
|
||||
"create spec/spec_helper.rb",
|
||||
"create spec/features_helper.rb",
|
||||
"create spec/web/views/app_layout_spec.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami new #{project} --test=minitest", output
|
||||
|
||||
within_project_directory(project) do
|
||||
#
|
||||
# .hanamirc
|
||||
#
|
||||
expect(".hanamirc").to have_file_content(%r{test=minitest})
|
||||
|
||||
#
|
||||
# spec/spec_helper.rb
|
||||
#
|
||||
expect("spec/spec_helper.rb").to have_file_content <<~END
|
||||
# Require this file for unit tests
|
||||
ENV['HANAMI_ENV'] ||= 'test'
|
||||
|
||||
require_relative '../config/environment'
|
||||
require 'minitest/autorun'
|
||||
|
||||
Hanami.boot
|
||||
END
|
||||
|
||||
#
|
||||
# spec/features_helper.rb
|
||||
#
|
||||
expect("spec/features_helper.rb").to have_file_content <<~END
|
||||
# Require this file for feature tests
|
||||
require_relative './spec_helper'
|
||||
|
||||
require 'capybara'
|
||||
require 'capybara/dsl'
|
||||
|
||||
Capybara.app = Hanami.app
|
||||
|
||||
class MiniTest::Spec
|
||||
include Capybara::DSL
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/<app>/views/app_layout_spec.rb
|
||||
#
|
||||
expect("spec/web/views/app_layout_spec.rb").to have_file_content <<-END
|
||||
require "spec_helper"
|
||||
|
||||
describe Web::Views::AppLayout do
|
||||
let(:layout) { Web::Views::AppLayout.new({ format: :html }, "contents") }
|
||||
let(:rendered) { layout.render }
|
||||
|
||||
it 'contains app name' do
|
||||
_(rendered).must_include('Web')
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
end # minitest
|
||||
|
||||
describe "rspec" do
|
||||
it "generates project" do
|
||||
project = "bookshelf_rspec"
|
||||
output = [
|
||||
"create .rspec",
|
||||
"create spec/spec_helper.rb",
|
||||
"create spec/features_helper.rb",
|
||||
"create spec/support/capybara.rb",
|
||||
"create spec/web/views/app_layout_spec.rb"
|
||||
]
|
||||
|
||||
run_cmd "hanami new #{project} --test=rspec", output
|
||||
|
||||
within_project_directory(project) do
|
||||
#
|
||||
# .hanamirc
|
||||
#
|
||||
expect(".hanamirc").to have_file_content(%r{test=rspec})
|
||||
|
||||
#
|
||||
# .rspec
|
||||
#
|
||||
expect(".rspec").to have_file_content <<~END
|
||||
--color
|
||||
--require spec_helper
|
||||
END
|
||||
|
||||
#
|
||||
# spec/spec_helper.rb
|
||||
#
|
||||
expect("spec/spec_helper.rb").to have_file_content <<~END
|
||||
# Require this file for unit tests
|
||||
ENV['HANAMI_ENV'] ||= 'test'
|
||||
|
||||
require_relative '../config/environment'
|
||||
Hanami.boot
|
||||
Hanami::Utils.require!("\#{__dir__}/support")
|
||||
|
||||
# This file was generated by the `rspec --init` command. Conventionally, all
|
||||
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
||||
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
||||
# this file to always be loaded, without a need to explicitly require it in any
|
||||
# files.
|
||||
#
|
||||
# Given that it is always loaded, you are encouraged to keep this file as
|
||||
# light-weight as possible. Requiring heavyweight dependencies from this file
|
||||
# will add to the boot time of your test suite on EVERY test run, even for an
|
||||
# individual file that may not need all of that loaded. Instead, consider making
|
||||
# a separate helper file that requires the additional dependencies and performs
|
||||
# the additional setup, and require it from the spec files that actually need
|
||||
# it.
|
||||
#
|
||||
# The `.rspec` file also contains a few flags that are not defaults but that
|
||||
# users commonly want.
|
||||
#
|
||||
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
||||
RSpec.configure do |config|
|
||||
# rspec-expectations config goes here. You can use an alternate
|
||||
# assertion/expectation library such as wrong or the stdlib/minitest
|
||||
# assertions if you prefer.
|
||||
config.expect_with :rspec do |expectations|
|
||||
# This option will default to `true` in RSpec 4. It makes the `description`
|
||||
# and `failure_message` of custom matchers include text for helper methods
|
||||
# defined using `chain`, e.g.:
|
||||
# be_bigger_than(2).and_smaller_than(4).description
|
||||
# # => "be bigger than 2 and smaller than 4"
|
||||
# ...rather than:
|
||||
# # => "be bigger than 2"
|
||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||
end
|
||||
|
||||
# rspec-mocks config goes here. You can use an alternate test double
|
||||
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
||||
config.mock_with :rspec do |mocks|
|
||||
# Prevents you from mocking or stubbing a method that does not exist on
|
||||
# a real object. This is generally recommended, and will default to
|
||||
# `true` in RSpec 4.
|
||||
mocks.verify_partial_doubles = true
|
||||
end
|
||||
|
||||
# The settings below are suggested to provide a good initial experience
|
||||
# with RSpec, but feel free to customize to your heart's content.
|
||||
=begin
|
||||
# These two settings work together to allow you to limit a spec run
|
||||
# to individual examples or groups you care about by tagging them with
|
||||
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
||||
# get run.
|
||||
config.filter_run :focus
|
||||
config.run_all_when_everything_filtered = true
|
||||
|
||||
# Allows RSpec to persist some state between runs in order to support
|
||||
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
||||
# you configure your source control system to ignore this file.
|
||||
config.example_status_persistence_file_path = "spec/examples.txt"
|
||||
|
||||
# Limits the available syntax to the non-monkey patched syntax that is
|
||||
# recommended. For more details, see:
|
||||
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
||||
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
||||
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
||||
config.disable_monkey_patching!
|
||||
|
||||
# This setting enables warnings. It's recommended, but in many cases may
|
||||
# be too noisy due to issues in dependencies.
|
||||
config.warnings = false
|
||||
|
||||
# Many RSpec users commonly either run the entire suite or an individual
|
||||
# file, and it's useful to allow more verbose output when running an
|
||||
# individual spec file.
|
||||
if config.files_to_run.one?
|
||||
# Use the documentation formatter for detailed output,
|
||||
# unless a formatter has already been configured
|
||||
# (e.g. via a command-line flag).
|
||||
config.default_formatter = 'doc'
|
||||
end
|
||||
|
||||
# Print the 10 slowest examples and example groups at the
|
||||
# end of the spec run, to help surface which specs are running
|
||||
# particularly slow.
|
||||
config.profile_examples = 10
|
||||
|
||||
# Run specs in random order to surface order dependencies. If you find an
|
||||
# order dependency and want to debug it, you can fix the order by providing
|
||||
# the seed, which is printed after each run.
|
||||
# --seed 1234
|
||||
config.order = :random
|
||||
|
||||
# Seed global randomization in this process using the `--seed` CLI option.
|
||||
# Setting this allows you to use `--seed` to deterministically reproduce
|
||||
# test failures related to randomization by passing the same `--seed` value
|
||||
# as the one that triggered the failure.
|
||||
Kernel.srand config.seed
|
||||
=end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/features_helper.rb
|
||||
#
|
||||
expect("spec/features_helper.rb").to have_file_content <<~END
|
||||
# Require this file for feature tests
|
||||
require_relative './spec_helper'
|
||||
|
||||
require 'capybara'
|
||||
require 'capybara/rspec'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include RSpec::FeatureExampleGroup
|
||||
|
||||
config.include Capybara::DSL, feature: true
|
||||
config.include Capybara::RSpecMatchers, feature: true
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/support/capybara.rb
|
||||
#
|
||||
expect("spec/support/capybara.rb").to have_file_content <<~END
|
||||
module RSpec
|
||||
module FeatureExampleGroup
|
||||
def self.included(group)
|
||||
group.metadata[:type] = :feature
|
||||
Capybara.app = Hanami.app
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/<app>/views/app_layout_spec.rb
|
||||
#
|
||||
expect("spec/web/views/app_layout_spec.rb").to have_file_content <<~END
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe Web::Views::AppLayout, type: :view do
|
||||
let(:layout) { Web::Views::AppLayout.new({ format: :html }, "contents") }
|
||||
let(:rendered) { layout.render }
|
||||
|
||||
it 'contains app name' do
|
||||
expect(rendered).to include('Web')
|
||||
end
|
||||
end
|
||||
END
|
||||
end
|
||||
end
|
||||
end # rspec
|
||||
|
||||
context "missing" do
|
||||
it "returns error" do
|
||||
output = "`' is not a valid test framework. Please use one of: `rspec', `minitest'"
|
||||
|
||||
run_cmd "hanami new bookshelf --test=", output, exit_status: 1
|
||||
end
|
||||
end # missing
|
||||
|
||||
context "unknown" do
|
||||
it "returns error" do
|
||||
output = "`foo' is not a valid test framework. Please use one of: `rspec', `minitest'"
|
||||
|
||||
run_cmd "hanami new bookshelf --test=foo", output, exit_status: 1
|
||||
end
|
||||
end # unknown
|
||||
end # test
|
||||
end
|
|
@ -1,970 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami new", type: :integration do
|
||||
it "generates vanilla project" do
|
||||
project = "bookshelf"
|
||||
output = <<-OUT
|
||||
create .hanamirc
|
||||
create .env.development
|
||||
create .env.test
|
||||
create README.md
|
||||
create Gemfile
|
||||
create config.ru
|
||||
create config/boot.rb
|
||||
create config/environment.rb
|
||||
create lib/#{project}.rb
|
||||
create public/.gitkeep
|
||||
create config/initializers/.gitkeep
|
||||
create lib/#{project}/entities/.gitkeep
|
||||
create lib/#{project}/repositories/.gitkeep
|
||||
create lib/#{project}/mailers/.gitkeep
|
||||
create lib/#{project}/mailers/templates/.gitkeep
|
||||
create spec/#{project}/entities/.gitkeep
|
||||
create spec/#{project}/repositories/.gitkeep
|
||||
create spec/#{project}/mailers/.gitkeep
|
||||
create spec/support/.gitkeep
|
||||
create db/migrations/.gitkeep
|
||||
create Rakefile
|
||||
create .rspec
|
||||
create spec/spec_helper.rb
|
||||
create spec/features_helper.rb
|
||||
create spec/support/capybara.rb
|
||||
create db/schema.sql
|
||||
create .gitignore
|
||||
run git init . from "."
|
||||
create apps/web/app.rb
|
||||
create apps/web/config/routes.rb
|
||||
create apps/web/views/app_layout.rb
|
||||
create apps/web/templates/app.html.erb
|
||||
create apps/web/assets/favicon.ico
|
||||
create apps/web/controllers/.gitkeep
|
||||
create apps/web/assets/images/.gitkeep
|
||||
create apps/web/assets/javascripts/.gitkeep
|
||||
create apps/web/assets/stylesheets/.gitkeep
|
||||
create spec/web/features/.gitkeep
|
||||
create spec/web/controllers/.gitkeep
|
||||
create spec/web/views/app_layout_spec.rb
|
||||
insert config/environment.rb
|
||||
insert config/environment.rb
|
||||
append .env.development
|
||||
append .env.test
|
||||
OUT
|
||||
|
||||
run_cmd "hanami new #{project}", output
|
||||
|
||||
within_project_directory(project) do
|
||||
# Assert it's an initialized Git repository
|
||||
run_cmd "git status", default_git_branch
|
||||
|
||||
#
|
||||
# .hanamirc
|
||||
#
|
||||
expect(".hanamirc").to have_file_content <<~END
|
||||
project=#{project}
|
||||
test=rspec
|
||||
template=erb
|
||||
END
|
||||
|
||||
#
|
||||
# .env.development
|
||||
#
|
||||
expect(".env.development").to have_file_content(%r{# Define ENV variables for development environment})
|
||||
expect(".env.development").to have_file_content(%r{DATABASE_URL="sqlite://db/#{project}_development.sqlite"})
|
||||
expect(".env.development").to have_file_content(%r{SERVE_STATIC_ASSETS="true"})
|
||||
expect(".env.development").to have_file_content(%r{WEB_SESSIONS_SECRET="[\w]{64}"})
|
||||
|
||||
#
|
||||
# .env.test
|
||||
#
|
||||
expect(".env.test").to have_file_content(%r{# Define ENV variables for test environment})
|
||||
expect(".env.test").to have_file_content(%r{DATABASE_URL="sqlite://db/#{project}_test.sqlite"})
|
||||
expect(".env.test").to have_file_content(%r{SERVE_STATIC_ASSETS="true"})
|
||||
expect(".env.test").to have_file_content(%r{WEB_SESSIONS_SECRET="[\w]{64}"})
|
||||
|
||||
#
|
||||
# README.md
|
||||
#
|
||||
expect("README.md").to have_file_content <<~END
|
||||
# Bookshelf
|
||||
|
||||
Welcome to your new Hanami project!
|
||||
|
||||
## Setup
|
||||
|
||||
How to run tests:
|
||||
|
||||
```
|
||||
% bundle exec rake
|
||||
```
|
||||
|
||||
How to run the development console:
|
||||
|
||||
```
|
||||
% bundle exec hanami console
|
||||
```
|
||||
|
||||
How to run the development server:
|
||||
|
||||
```
|
||||
% bundle exec hanami server
|
||||
```
|
||||
|
||||
How to prepare (create and migrate) DB for `development` and `test` environments:
|
||||
|
||||
```
|
||||
% bundle exec hanami db prepare
|
||||
|
||||
% HANAMI_ENV=test bundle exec hanami db prepare
|
||||
```
|
||||
|
||||
Explore Hanami [guides](https://guides.hanamirb.org/), [API docs](http://docs.hanamirb.org/#{Hanami::VERSION}/), or jump in [chat](http://chat.hanamirb.org) for help. Enjoy! 🌸
|
||||
END
|
||||
|
||||
#
|
||||
# Gemfile
|
||||
#
|
||||
if Platform.match?(engine: :ruby)
|
||||
expect('Gemfile').to have_file_content <<-END
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rake'
|
||||
gem 'hanami', '#{Hanami::Version.gem_requirement}'
|
||||
gem 'hanami-model', '~> 1.3'
|
||||
|
||||
gem 'sqlite3'
|
||||
|
||||
group :development do
|
||||
# Code reloading
|
||||
# See: https://guides.hanamirb.org/projects/code-reloading
|
||||
gem 'shotgun', platforms: :ruby
|
||||
gem 'hanami-webconsole'
|
||||
end
|
||||
|
||||
group :test, :development do
|
||||
gem 'dotenv', '~> 2.4'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'rspec'
|
||||
gem 'capybara'
|
||||
end
|
||||
|
||||
group :production do
|
||||
# gem 'puma'
|
||||
end
|
||||
END
|
||||
end
|
||||
|
||||
if Platform.match?(engine: :jruby)
|
||||
expect("Gemfile").to have_file_content <<~END
|
||||
source 'https://rubygems.org'
|
||||
|
||||
gem 'rake'
|
||||
gem 'hanami', '#{Hanami::Version.gem_requirement}'
|
||||
gem 'hanami-model', '~> 1.3'
|
||||
|
||||
gem 'jdbc-sqlite3'
|
||||
|
||||
group :test, :development do
|
||||
gem 'dotenv', '~> 2.4'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'rspec'
|
||||
gem 'capybara'
|
||||
end
|
||||
|
||||
group :production do
|
||||
# gem 'puma'
|
||||
end
|
||||
END
|
||||
end
|
||||
|
||||
#
|
||||
# config.ru
|
||||
#
|
||||
expect('config.ru').to have_file_content <<-END
|
||||
require_relative 'config/environment'
|
||||
|
||||
run Hanami.app
|
||||
END
|
||||
|
||||
#
|
||||
# config/boot.rb
|
||||
#
|
||||
expect("config/boot.rb").to have_file_content <<~END
|
||||
require_relative './environment'
|
||||
Hanami.boot
|
||||
END
|
||||
|
||||
#
|
||||
# config/environment.rb
|
||||
#
|
||||
expect('config/environment.rb').to have_file_content <<-END
|
||||
require 'bundler/setup'
|
||||
require 'hanami/setup'
|
||||
require 'hanami/model'
|
||||
require_relative '../lib/#{project}'
|
||||
require_relative '../apps/web/app'
|
||||
|
||||
Hanami.configure do
|
||||
mount Web::App, at: '/'
|
||||
|
||||
model do
|
||||
##
|
||||
# Database adapter
|
||||
#
|
||||
# Available options:
|
||||
#
|
||||
# * SQL adapter
|
||||
# adapter :sql, 'sqlite://db/#{project}_development.sqlite3'
|
||||
# adapter :sql, 'postgresql://localhost/#{project}_development'
|
||||
# adapter :sql, 'mysql://localhost/#{project}_development'
|
||||
#
|
||||
adapter :sql, ENV.fetch('DATABASE_URL')
|
||||
|
||||
##
|
||||
# Migrations
|
||||
#
|
||||
migrations 'db/migrations'
|
||||
schema 'db/schema.sql'
|
||||
end
|
||||
|
||||
mailer do
|
||||
root 'lib/#{project}/mailers'
|
||||
|
||||
# See https://guides.hanamirb.org/mailers/delivery
|
||||
delivery :test
|
||||
end
|
||||
|
||||
environment :development do
|
||||
# See: https://guides.hanamirb.org/projects/logging
|
||||
logger level: :debug
|
||||
end
|
||||
|
||||
environment :production do
|
||||
logger level: :info, formatter: :json, filter: []
|
||||
|
||||
mailer do
|
||||
delivery :smtp, address: ENV.fetch('SMTP_HOST'), port: ENV.fetch('SMTP_PORT')
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
project_module = Hanami::Utils::String.new(project).classify
|
||||
#
|
||||
# lib/<project>.rb
|
||||
#
|
||||
expect("lib/#{project}.rb").to have_file_content <<~END
|
||||
module #{project_module}
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# public/.gitkeep
|
||||
#
|
||||
expect("public/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# config/initializers/.gitkeep
|
||||
#
|
||||
expect("config/initializers/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# lib/<project>/entities/.gitkeep
|
||||
#
|
||||
expect("lib/#{project}/entities/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# lib/<project>/mailers/.gitkeep
|
||||
#
|
||||
expect("lib/#{project}/mailers/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# lib/<project>/mailers/templates/.gitkeep
|
||||
#
|
||||
expect("lib/#{project}/mailers/templates/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# spec/<project>/entities/.gitkeep
|
||||
#
|
||||
expect("spec/#{project}/entities/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# spec/<project>/repositories/.gitkeep
|
||||
#
|
||||
expect("spec/#{project}/repositories/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# spec/<project>/mailers/.gitkeep
|
||||
#
|
||||
expect("spec/#{project}/mailers/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# spec/support/.gitkeep
|
||||
#
|
||||
expect("spec/support/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# Rakefile
|
||||
#
|
||||
expect("Rakefile").to have_file_content <<~END
|
||||
require 'rake'
|
||||
require 'hanami/rake_tasks'
|
||||
|
||||
begin
|
||||
require 'rspec/core/rake_task'
|
||||
RSpec::Core::RakeTask.new(:spec)
|
||||
task default: :spec
|
||||
rescue LoadError
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/spec_helper.rb
|
||||
#
|
||||
expect("spec/spec_helper.rb").to have_file_content <<~END
|
||||
# Require this file for unit tests
|
||||
ENV['HANAMI_ENV'] ||= 'test'
|
||||
|
||||
require_relative '../config/environment'
|
||||
Hanami.boot
|
||||
Hanami::Utils.require!("\#{__dir__}/support")
|
||||
|
||||
# This file was generated by the `rspec --init` command. Conventionally, all
|
||||
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
||||
# The generated `.rspec` file contains `--require spec_helper` which will cause
|
||||
# this file to always be loaded, without a need to explicitly require it in any
|
||||
# files.
|
||||
#
|
||||
# Given that it is always loaded, you are encouraged to keep this file as
|
||||
# light-weight as possible. Requiring heavyweight dependencies from this file
|
||||
# will add to the boot time of your test suite on EVERY test run, even for an
|
||||
# individual file that may not need all of that loaded. Instead, consider making
|
||||
# a separate helper file that requires the additional dependencies and performs
|
||||
# the additional setup, and require it from the spec files that actually need
|
||||
# it.
|
||||
#
|
||||
# The `.rspec` file also contains a few flags that are not defaults but that
|
||||
# users commonly want.
|
||||
#
|
||||
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
||||
RSpec.configure do |config|
|
||||
# rspec-expectations config goes here. You can use an alternate
|
||||
# assertion/expectation library such as wrong or the stdlib/minitest
|
||||
# assertions if you prefer.
|
||||
config.expect_with :rspec do |expectations|
|
||||
# This option will default to `true` in RSpec 4. It makes the `description`
|
||||
# and `failure_message` of custom matchers include text for helper methods
|
||||
# defined using `chain`, e.g.:
|
||||
# be_bigger_than(2).and_smaller_than(4).description
|
||||
# # => "be bigger than 2 and smaller than 4"
|
||||
# ...rather than:
|
||||
# # => "be bigger than 2"
|
||||
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
||||
end
|
||||
|
||||
# rspec-mocks config goes here. You can use an alternate test double
|
||||
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
||||
config.mock_with :rspec do |mocks|
|
||||
# Prevents you from mocking or stubbing a method that does not exist on
|
||||
# a real object. This is generally recommended, and will default to
|
||||
# `true` in RSpec 4.
|
||||
mocks.verify_partial_doubles = true
|
||||
end
|
||||
|
||||
# The settings below are suggested to provide a good initial experience
|
||||
# with RSpec, but feel free to customize to your heart's content.
|
||||
=begin
|
||||
# These two settings work together to allow you to limit a spec run
|
||||
# to individual examples or groups you care about by tagging them with
|
||||
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
||||
# get run.
|
||||
config.filter_run :focus
|
||||
config.run_all_when_everything_filtered = true
|
||||
|
||||
# Allows RSpec to persist some state between runs in order to support
|
||||
# the `--only-failures` and `--next-failure` CLI options. We recommend
|
||||
# you configure your source control system to ignore this file.
|
||||
config.example_status_persistence_file_path = "spec/examples.txt"
|
||||
|
||||
# Limits the available syntax to the non-monkey patched syntax that is
|
||||
# recommended. For more details, see:
|
||||
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
||||
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
||||
# - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
|
||||
config.disable_monkey_patching!
|
||||
|
||||
# This setting enables warnings. It's recommended, but in many cases may
|
||||
# be too noisy due to issues in dependencies.
|
||||
config.warnings = false
|
||||
|
||||
# Many RSpec users commonly either run the entire suite or an individual
|
||||
# file, and it's useful to allow more verbose output when running an
|
||||
# individual spec file.
|
||||
if config.files_to_run.one?
|
||||
# Use the documentation formatter for detailed output,
|
||||
# unless a formatter has already been configured
|
||||
# (e.g. via a command-line flag).
|
||||
config.default_formatter = 'doc'
|
||||
end
|
||||
|
||||
# Print the 10 slowest examples and example groups at the
|
||||
# end of the spec run, to help surface which specs are running
|
||||
# particularly slow.
|
||||
config.profile_examples = 10
|
||||
|
||||
# Run specs in random order to surface order dependencies. If you find an
|
||||
# order dependency and want to debug it, you can fix the order by providing
|
||||
# the seed, which is printed after each run.
|
||||
# --seed 1234
|
||||
config.order = :random
|
||||
|
||||
# Seed global randomization in this process using the `--seed` CLI option.
|
||||
# Setting this allows you to use `--seed` to deterministically reproduce
|
||||
# test failures related to randomization by passing the same `--seed` value
|
||||
# as the one that triggered the failure.
|
||||
Kernel.srand config.seed
|
||||
=end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# spec/features_helper.rb
|
||||
#
|
||||
expect("spec/features_helper.rb").to have_file_content <<~END
|
||||
# Require this file for feature tests
|
||||
require_relative './spec_helper'
|
||||
|
||||
require 'capybara'
|
||||
require 'capybara/rspec'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.include RSpec::FeatureExampleGroup
|
||||
|
||||
config.include Capybara::DSL, feature: true
|
||||
config.include Capybara::RSpecMatchers, feature: true
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# .gitignore
|
||||
#
|
||||
expect(".gitignore").to have_file_content <<-END
|
||||
/db/*.sqlite
|
||||
/public/assets*
|
||||
/tmp
|
||||
.env.local
|
||||
.env.*.local
|
||||
END
|
||||
|
||||
#
|
||||
# apps/web/app.rb
|
||||
#
|
||||
expect("apps/web/app.rb").to have_file_content <<-END
|
||||
require 'hanami/helpers'
|
||||
require 'hanami/assets'
|
||||
|
||||
module Web
|
||||
class App < Hanami::App
|
||||
configure do
|
||||
##
|
||||
# BASIC
|
||||
#
|
||||
|
||||
# Define the root path of this app.
|
||||
# All paths specified in this configuration are relative to path below.
|
||||
#
|
||||
root __dir__
|
||||
|
||||
# Relative load paths where this app will recursively load the
|
||||
# code.
|
||||
#
|
||||
# When you add new directories, remember to add them here.
|
||||
#
|
||||
load_paths << [
|
||||
'controllers',
|
||||
'views'
|
||||
]
|
||||
|
||||
# Handle exceptions with HTTP statuses (true) or don't catch them (false).
|
||||
# Defaults to true.
|
||||
# See: http://www.rubydoc.info/gems/hanami-controller/#Exceptions_management
|
||||
#
|
||||
# handle_exceptions true
|
||||
|
||||
##
|
||||
# HTTP
|
||||
#
|
||||
|
||||
# Routes definitions for this app
|
||||
# See: http://www.rubydoc.info/gems/hanami-router#Usage
|
||||
#
|
||||
routes 'config/routes'
|
||||
|
||||
# URI scheme used by the routing system to generate absolute URLs
|
||||
# Defaults to "http"
|
||||
#
|
||||
# scheme 'https'
|
||||
|
||||
# URI host used by the routing system to generate absolute URLs
|
||||
# Defaults to "localhost"
|
||||
#
|
||||
# host 'example.org'
|
||||
|
||||
# URI port used by the routing system to generate absolute URLs
|
||||
# Argument: An object coercible to integer, defaults to 80 if the scheme
|
||||
# is http and 443 if it's https
|
||||
#
|
||||
# This should only be configured if app listens to non-standard ports
|
||||
#
|
||||
# port 443
|
||||
|
||||
# Enable cookies
|
||||
# Argument: boolean to toggle the feature
|
||||
# A Hash with options
|
||||
#
|
||||
# Options:
|
||||
# :domain - The domain (String - nil by default, not required)
|
||||
# :path - Restrict cookies to a relative URI
|
||||
# (String - nil by default)
|
||||
# :max_age - Cookies expiration expressed in seconds
|
||||
# (Integer - nil by default)
|
||||
# :secure - Restrict cookies to secure connections
|
||||
# (Boolean - Automatically true when using HTTPS)
|
||||
# See #scheme and #ssl?
|
||||
# :httponly - Prevent JavaScript access (Boolean - true by default)
|
||||
#
|
||||
# cookies true
|
||||
# or
|
||||
# cookies max_age: 300
|
||||
|
||||
# Enable sessions
|
||||
# Argument: Symbol the Rack session adapter
|
||||
# A Hash with options
|
||||
#
|
||||
# See: http://www.rubydoc.info/gems/rack/Rack/Session/Cookie
|
||||
#
|
||||
# sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']
|
||||
|
||||
# Configure Rack middleware for this app
|
||||
#
|
||||
# middleware.use Rack::Protection
|
||||
|
||||
# Default format for the requests that don't specify an HTTP_ACCEPT header
|
||||
# Argument: A symbol representation of a mime type, defaults to :html
|
||||
#
|
||||
# default_request_format :html
|
||||
|
||||
# Default format for responses that don't consider the request format
|
||||
# Argument: A symbol representation of a mime type, defaults to :html
|
||||
#
|
||||
# default_response_format :html
|
||||
|
||||
##
|
||||
# TEMPLATES
|
||||
#
|
||||
|
||||
# The layout to be used by all views
|
||||
#
|
||||
layout :app # It will load Web::Views::AppLayout
|
||||
|
||||
# The relative path to templates
|
||||
#
|
||||
templates 'templates'
|
||||
|
||||
##
|
||||
# ASSETS
|
||||
#
|
||||
assets do
|
||||
# JavaScript compressor
|
||||
#
|
||||
# Supported engines:
|
||||
#
|
||||
# * :builtin
|
||||
# * :uglifier
|
||||
# * :yui
|
||||
# * :closure
|
||||
#
|
||||
# See: https://guides.hanamirb.org/assets/compressors
|
||||
#
|
||||
# In order to skip JavaScript compression comment the following line
|
||||
javascript_compressor :builtin
|
||||
|
||||
# Stylesheet compressor
|
||||
#
|
||||
# Supported engines:
|
||||
#
|
||||
# * :builtin
|
||||
# * :yui
|
||||
# * :sass
|
||||
#
|
||||
# See: https://guides.hanamirb.org/assets/compressors
|
||||
#
|
||||
# In order to skip stylesheet compression comment the following line
|
||||
stylesheet_compressor :builtin
|
||||
|
||||
# Specify sources for assets
|
||||
#
|
||||
sources << [
|
||||
'assets'
|
||||
]
|
||||
end
|
||||
|
||||
##
|
||||
# SECURITY
|
||||
#
|
||||
|
||||
# X-Frame-Options is a HTTP header supported by modern browsers.
|
||||
# It determines if a web page can or cannot be included via <frame> and
|
||||
# <iframe> tags by untrusted domains.
|
||||
#
|
||||
# Web apps can send this header to prevent Clickjacking attacks.
|
||||
#
|
||||
# Read more at:
|
||||
#
|
||||
# * https://developer.mozilla.org/en-US/docs/Web/HTTP/X-Frame-Options
|
||||
# * https://www.owasp.org/index.php/Clickjacking
|
||||
#
|
||||
security.x_frame_options 'DENY'
|
||||
|
||||
# X-Content-Type-Options prevents browsers from interpreting files as
|
||||
# something else than declared by the content type in the HTTP headers.
|
||||
#
|
||||
# Read more at:
|
||||
#
|
||||
# * https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#X-Content-Type-Options
|
||||
# * https://msdn.microsoft.com/en-us/library/gg622941%28v=vs.85%29.aspx
|
||||
# * https://blogs.msdn.microsoft.com/ie/2008/09/02/ie8-security-part-vi-beta-2-update
|
||||
#
|
||||
security.x_content_type_options 'nosniff'
|
||||
|
||||
# X-XSS-Protection is a HTTP header to determine the behavior of the
|
||||
# browser in case an XSS attack is detected.
|
||||
#
|
||||
# Read more at:
|
||||
#
|
||||
# * https://www.owasp.org/index.php/Cross-site_Scripting_(XSS)
|
||||
# * https://www.owasp.org/index.php/OWASP_Secure_Headers_Project#X-XSS-Protection
|
||||
#
|
||||
security.x_xss_protection '1; mode=block'
|
||||
|
||||
# Content-Security-Policy (CSP) is a HTTP header supported by modern
|
||||
# browsers. It determines trusted sources of execution for dynamic
|
||||
# contents (JavaScript) or other web related assets: stylesheets, images,
|
||||
# fonts, plugins, etc.
|
||||
#
|
||||
# Web apps can send this header to mitigate Cross Site Scripting
|
||||
# (XSS) attacks.
|
||||
#
|
||||
# The default value allows images, scripts, AJAX, fonts and CSS from the
|
||||
# same origin, and does not allow any other resources to load (eg object,
|
||||
# frame, media, etc).
|
||||
#
|
||||
# Inline JavaScript is NOT allowed. To enable it, please use:
|
||||
# "script-src 'unsafe-inline'".
|
||||
#
|
||||
# Content Security Policy introduction:
|
||||
#
|
||||
# * http://www.html5rocks.com/en/tutorials/security/content-security-policy/
|
||||
# * https://www.owasp.org/index.php/Content_Security_Policy
|
||||
# * https://www.owasp.org/index.php/Cross-site_Scripting_%28XSS%29
|
||||
#
|
||||
# Inline and eval JavaScript risks:
|
||||
#
|
||||
# * http://www.html5rocks.com/en/tutorials/security/content-security-policy/#inline-code-considered-harmful
|
||||
# * http://www.html5rocks.com/en/tutorials/security/content-security-policy/#eval-too
|
||||
#
|
||||
# Content Security Policy usage:
|
||||
#
|
||||
# * http://content-security-policy.com/
|
||||
# * https://developer.mozilla.org/en-US/docs/Web/Security/CSP/Using_Content_Security_Policy
|
||||
#
|
||||
# Content Security Policy references:
|
||||
#
|
||||
# * https://developer.mozilla.org/en-US/docs/Web/Security/CSP/CSP_policy_directives
|
||||
#
|
||||
security.content_security_policy %{
|
||||
form-action 'self';
|
||||
frame-ancestors 'self';
|
||||
base-uri 'self';
|
||||
default-src 'none';
|
||||
script-src 'self';
|
||||
connect-src 'self';
|
||||
img-src 'self' https: data:;
|
||||
style-src 'self' 'unsafe-inline' https:;
|
||||
font-src 'self';
|
||||
object-src 'none';
|
||||
plugin-types app/pdf;
|
||||
child-src 'self';
|
||||
frame-src 'self';
|
||||
media-src 'self'
|
||||
}
|
||||
|
||||
##
|
||||
# FRAMEWORKS
|
||||
#
|
||||
|
||||
# Configure the code that will yield each time Web::Action is included
|
||||
# This is useful for sharing common functionality
|
||||
#
|
||||
# See: http://www.rubydoc.info/gems/hanami-controller#Configuration
|
||||
controller.prepare do
|
||||
# include MyAuthentication # included in all the actions
|
||||
# before :authenticate! # run an authentication before callback
|
||||
end
|
||||
|
||||
# Configure the code that will yield each time Web::View is included
|
||||
# This is useful for sharing common functionality
|
||||
#
|
||||
# See: http://www.rubydoc.info/gems/hanami-view#Configuration
|
||||
view.prepare do
|
||||
include Hanami::Helpers
|
||||
include Web::Assets::Helpers
|
||||
end
|
||||
end
|
||||
|
||||
##
|
||||
# DEVELOPMENT
|
||||
#
|
||||
configure :development do
|
||||
# Don't handle exceptions, render the stack trace
|
||||
handle_exceptions false
|
||||
end
|
||||
|
||||
##
|
||||
# TEST
|
||||
#
|
||||
configure :test do
|
||||
# Don't handle exceptions, render the stack trace
|
||||
handle_exceptions false
|
||||
end
|
||||
|
||||
##
|
||||
# PRODUCTION
|
||||
#
|
||||
configure :production do
|
||||
# scheme 'https'
|
||||
# host 'example.org'
|
||||
# port 443
|
||||
|
||||
assets do
|
||||
# Don't compile static assets in production mode (eg. Sass, ES6)
|
||||
#
|
||||
# See: http://www.rubydoc.info/gems/hanami-assets#Configuration
|
||||
compile false
|
||||
|
||||
# Use fingerprint file name for asset paths
|
||||
#
|
||||
# See: https://guides.hanamirb.org/assets/overview
|
||||
fingerprint true
|
||||
|
||||
# Content Delivery Network (CDN)
|
||||
#
|
||||
# See: https://guides.hanamirb.org/assets/content-delivery-network
|
||||
#
|
||||
# scheme 'https'
|
||||
# host 'cdn.example.org'
|
||||
# port 443
|
||||
|
||||
# Subresource Integrity
|
||||
#
|
||||
# See: https://guides.hanamirb.org/assets/content-delivery-network/#subresource-integrity
|
||||
subresource_integrity :sha256
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# apps/web/config/routes.rb
|
||||
#
|
||||
expect("apps/web/config/routes.rb").to have_file_content <<-END
|
||||
# Configure your routes here
|
||||
# See: https://guides.hanamirb.org/routing/overview
|
||||
#
|
||||
# Example:
|
||||
# get '/hello', to: ->(env) { [200, {}, ['Hello from Hanami!']] }
|
||||
END
|
||||
|
||||
#
|
||||
# apps/web/views/app_layout.rb
|
||||
#
|
||||
expect("apps/web/views/app_layout.rb").to have_file_content <<~END
|
||||
module Web
|
||||
module Views
|
||||
class AppLayout
|
||||
include Web::Layout
|
||||
end
|
||||
end
|
||||
end
|
||||
END
|
||||
|
||||
#
|
||||
# apps/web/templates/app.html.erb
|
||||
#
|
||||
expect("apps/web/templates/app.html.erb").to have_file_content <<~END
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Web</title>
|
||||
<%= favicon %>
|
||||
</head>
|
||||
<body>
|
||||
<%= yield %>
|
||||
</body>
|
||||
</html>
|
||||
END
|
||||
|
||||
#
|
||||
# apps/web/assets/favicon.ico
|
||||
#
|
||||
expect("apps/web/assets/favicon.ico").to be_an_existing_file
|
||||
|
||||
#
|
||||
# apps/web/controllers/.gitkeep
|
||||
#
|
||||
expect("apps/web/controllers/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# apps/web/assets/images/.gitkeep
|
||||
#
|
||||
expect("apps/web/assets/images/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# apps/web/assets/javascripts/.gitkeep
|
||||
#
|
||||
expect("apps/web/assets/javascripts/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# apps/web/assets/stylesheets/.gitkeep
|
||||
#
|
||||
expect("apps/web/assets/stylesheets/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# spec/web/features/.gitkeep
|
||||
#
|
||||
expect("spec/web/features/.gitkeep").to be_an_existing_file
|
||||
|
||||
#
|
||||
# spec/web/controllers/.gitkeep
|
||||
#
|
||||
expect("spec/web/controllers/.gitkeep").to be_an_existing_file
|
||||
end
|
||||
end
|
||||
|
||||
context "with underscored project name" do
|
||||
it_behaves_like "a new project" do
|
||||
let(:input) { "cool_name" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with dashed project name" do
|
||||
it_behaves_like "a new project" do
|
||||
let(:input) { "awesome-project" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with camel case project name" do
|
||||
it_behaves_like "a new project" do
|
||||
let(:input) { "CaMElCaSE" }
|
||||
end
|
||||
end
|
||||
|
||||
context "with dot as project name" do
|
||||
before do
|
||||
root.mkpath
|
||||
end
|
||||
|
||||
let(:root) { Pathname.new(Dir.pwd).join("tmp", "aruba", dir) }
|
||||
let(:project) { "terrific_product" }
|
||||
let(:dir) { "terrific product" }
|
||||
|
||||
it "generates project" do
|
||||
cd(dir) do
|
||||
run_cmd "hanami new ."
|
||||
end
|
||||
|
||||
[
|
||||
"create lib/#{project}.rb",
|
||||
"create lib/#{project}/entities/.gitkeep",
|
||||
"create lib/#{project}/repositories/.gitkeep",
|
||||
"create lib/#{project}/mailers/.gitkeep",
|
||||
"create lib/#{project}/mailers/templates/.gitkeep",
|
||||
"create spec/#{project}/entities/.gitkeep",
|
||||
"create spec/#{project}/repositories/.gitkeep",
|
||||
"create spec/#{project}/mailers/.gitkeep"
|
||||
].each do |output|
|
||||
expect(all_output).to match(/#{output}/)
|
||||
end
|
||||
|
||||
within_project_directory(dir) do
|
||||
#
|
||||
# .hanamirc
|
||||
#
|
||||
expect(".hanamirc").to have_file_content %r{project=#{project}}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with missing name" do
|
||||
it "fails" do
|
||||
output = <<~OUT
|
||||
ERROR: "hanami new" was called with no arguments
|
||||
Usage: "hanami new PROJECT"
|
||||
OUT
|
||||
|
||||
run_cmd "hanami new", output, exit_status: 1
|
||||
end
|
||||
end
|
||||
|
||||
it 'prints help message' do
|
||||
output = <<-OUT
|
||||
Command:
|
||||
hanami new
|
||||
|
||||
Usage:
|
||||
hanami new PROJECT
|
||||
|
||||
Description:
|
||||
Generate a new Hanami project
|
||||
|
||||
Arguments:
|
||||
PROJECT # REQUIRED The project name
|
||||
|
||||
Options:
|
||||
--database=VALUE, -d VALUE # Database (mysql/mysql2/postgresql/postgres/sqlite/sqlite3), default: "sqlite"
|
||||
--app-name=VALUE # App name, default: "web"
|
||||
--app-base-url=VALUE # App base URL, default: "/"
|
||||
--template=VALUE # Template engine (erb/haml/slim), default: "erb"
|
||||
--test=VALUE # Project testing framework (rspec/minitest), default: "rspec"
|
||||
--[no-]hanami-head # Use Hanami HEAD (true/false), default: false
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami new bookshelf # Basic usage
|
||||
hanami new bookshelf --test=rspec # Setup RSpec testing framework
|
||||
hanami new bookshelf --database=postgres # Setup Postgres database
|
||||
hanami new bookshelf --template=slim # Setup Slim template engine
|
||||
hanami new bookshelf --hanami-head # Use Hanami HEAD
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami new --help', output
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def default_git_branch
|
||||
result = `git config --list`.scan(/init\.defaultBranch\=[[:alpha:]]*/i).first
|
||||
|
||||
branch = if result.nil?
|
||||
"(main|master)"
|
||||
else
|
||||
result.split("=").last
|
||||
end
|
||||
|
||||
/On branch #{branch}/
|
||||
end
|
||||
end
|
|
@ -1,39 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "pathname"
|
||||
|
||||
RSpec.describe "CLI plugins", type: :integration do
|
||||
xit "includes its commands in CLI output" do
|
||||
with_project do
|
||||
bundle_exec "hanami"
|
||||
expect(out).to include("hanami plugin [SUBCOMMAND]")
|
||||
end
|
||||
end
|
||||
|
||||
xit "executes command from plugin" do
|
||||
with_project do
|
||||
bundle_exec "hanami plugin version"
|
||||
expect(out).to include("v0.1.0")
|
||||
end
|
||||
end
|
||||
|
||||
# See https://github.com/hanami/hanami/issues/838
|
||||
it "guarantees 'hanami new' to generate a project" do
|
||||
project = "bookshelf_without_gemfile"
|
||||
|
||||
with_system_tmp_directory do
|
||||
run_cmd_with_clean_env "hanami new #{project}"
|
||||
destination = Pathname.new(Dir.pwd).join(project)
|
||||
|
||||
expect(destination).to exist
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def with_project
|
||||
super("bookshelf", gems: { "hanami-plugin" => { groups: [:plugins], path: Pathname.new(__dir__).join("..", "..", "..", "spec", "support", "fixtures", "hanami-plugin").realpath.to_s } }) do
|
||||
yield
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,49 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami routes", type: :integration do
|
||||
it "prints app routes" do
|
||||
with_project do
|
||||
generate "app admin"
|
||||
|
||||
write "lib/ping.rb", <<~EOF
|
||||
class Ping
|
||||
def call(env)
|
||||
[200, {}, ["PONG"]]
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
unshift "config/environment.rb", "require_relative '../lib/ping'"
|
||||
replace "config/environment.rb", "Hanami.configure do", "Hanami.configure do\nmount Ping, at: '/ping'"
|
||||
|
||||
generate "action web home#index --url=/"
|
||||
generate "action web books#create --url=/books --method=POST"
|
||||
|
||||
generate "action admin home#index --url=/"
|
||||
|
||||
hanami "routes"
|
||||
|
||||
expect(out).to eq "Name Method Path Action \n\n /ping Ping \n\n Name Method Path Action \n\n GET, HEAD /admin Admin::Controllers::Home::Index\n\n Name Method Path Action \n\n GET, HEAD / Web::Controllers::Home::Index \n POST /books Web::Controllers::Books::Create"
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami routes
|
||||
|
||||
Usage:
|
||||
hanami routes
|
||||
|
||||
Description:
|
||||
Prints routes
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami routes --help', output
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,626 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "json"
|
||||
|
||||
RSpec.describe "hanami server", type: :integration do
|
||||
context "without routes" do
|
||||
it "shows welcome page" do
|
||||
with_project do
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
|
||||
expect(page).to have_content("The web, with simplicity.")
|
||||
expect(page).to have_content("Hanami is Open Source Software for MVC web development with Ruby.")
|
||||
expect(page).to have_content("bundle exec hanami generate action web 'home#index' --url=/")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "shows welcome page for generated app" do
|
||||
with_project do
|
||||
generate "app admin"
|
||||
|
||||
server do
|
||||
visit "/admin"
|
||||
|
||||
expect(page).to have_content("bundle exec hanami generate action admin 'home#index' --url=/")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "with routes" do
|
||||
it "serves action" do
|
||||
with_project do
|
||||
server do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
visit "/"
|
||||
expect(page).to have_title("Web")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "serves static asset" do
|
||||
with_project do
|
||||
server do
|
||||
write "apps/web/assets/javascripts/app.js", <<~EOF
|
||||
console.log('test');
|
||||
EOF
|
||||
visit "/assets/app.js"
|
||||
expect(page).to have_content("console.log('test');")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "serves contents from database" do
|
||||
with_project do
|
||||
setup_model
|
||||
console do |input, _, _|
|
||||
input.puts("BookRepository.new.create(title: 'Learn Hanami')")
|
||||
input.puts("exit")
|
||||
end
|
||||
|
||||
generate "action web books#show --url=/books/:id"
|
||||
rewrite "apps/web/controllers/books/show.rb", <<~EOF
|
||||
module Web::Controllers::Books
|
||||
class Show
|
||||
include Web::Action
|
||||
expose :book
|
||||
|
||||
def call(params)
|
||||
@book = BookRepository.new.find(params[:id]) or halt(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
rewrite "apps/web/templates/books/show.html.erb", <<~EOF
|
||||
<h1><%= book.title %></h1>
|
||||
EOF
|
||||
|
||||
server do
|
||||
visit "/books/1"
|
||||
expect(page).to have_content("Learn Hanami")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "logging" do
|
||||
let(:log) { "log/development.log" }
|
||||
let(:project) { "bookshelf" }
|
||||
|
||||
context "when enabled" do
|
||||
it "logs GET requests" do
|
||||
with_project(project) do
|
||||
touch log
|
||||
replace "config/environment.rb", "logger level: :debug", %(logger level: :debug, stream: "#{log}")
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
end
|
||||
|
||||
content = contents(log)
|
||||
expect(content).to include("[#{project}] [INFO]")
|
||||
expect(content).to match(%r{HTTP/1.1 GET 200 (.*) /})
|
||||
end
|
||||
end
|
||||
|
||||
it "logs GET requests with query string" do
|
||||
with_project(project) do
|
||||
touch log
|
||||
replace "config/environment.rb", "logger level: :debug", %(logger level: :debug, stream: "#{log}")
|
||||
|
||||
run_cmd "hanami generate action web home#index --url=/", []
|
||||
|
||||
server do
|
||||
visit "/?ping=pong"
|
||||
expect(page).to have_title("Web")
|
||||
end
|
||||
|
||||
content = contents(log)
|
||||
expect(content).to include("[#{project}] [INFO]")
|
||||
expect(content).to match(%({"ping"=>"pong"}))
|
||||
end
|
||||
end
|
||||
|
||||
it "logs non-GET requests with payload" do
|
||||
with_project(project) do
|
||||
touch log
|
||||
replace "config/environment.rb", "logger level: :debug", %(logger level: :debug, stream: "#{log}")
|
||||
|
||||
run_cmd "hanami generate action web books#create --method=POST", []
|
||||
|
||||
server do
|
||||
post "/books", book: { title: "Functions" }
|
||||
end
|
||||
|
||||
content = contents(log)
|
||||
expect(content).to include("[#{project}] [INFO]")
|
||||
expect(content).to match(%({"book"=>{"title"=>"Functions"}}))
|
||||
end
|
||||
end
|
||||
|
||||
it "logs non-GET requests from body parsers" do
|
||||
with_project(project) do
|
||||
touch log
|
||||
replace "config/environment.rb", "logger level: :debug", %(logger level: :debug, stream: "#{log}")
|
||||
inject_line_after "config/environment.rb", "Hanami.configure", "require 'hanami/middleware/body_parser'\nmiddleware.use Hanami::Middleware::BodyParser, :json"
|
||||
|
||||
run_cmd "hanami generate action web books#create --method=POST", []
|
||||
inject_line_after "apps/web/controllers/books/create.rb", "call", 'Hanami.logger.debug(request.env["CONTENT_TYPE"]);self.body = %({"status":"OK"})'
|
||||
|
||||
server do
|
||||
post "/books", book: { title: "Why we sleep" }
|
||||
post "/books", JSON.generate(book: { title: "Parsers" }), "CONTENT_TYPE" => "app/json", "HTTP_ACCEPT" => "app/json"
|
||||
post "/books", JSON.generate(%w[this is cool]), "CONTENT_TYPE" => "app/json", "HTTP_ACCEPT" => "app/json"
|
||||
end
|
||||
|
||||
content = contents(log)
|
||||
expect(content).to include("[#{project}] [INFO]")
|
||||
expect(content).to include("POST 200")
|
||||
|
||||
expect(content).to include("app/x-www-form-urlencoded")
|
||||
expect(content).to include(%({"book"=>{"title"=>"Why we sleep"}}))
|
||||
|
||||
expect(content).to include("app/json")
|
||||
expect(content).to include(%({"book"=>{"title"=>"Parsers"}}))
|
||||
expect(content).to include(%({"_"=>["this", "is", "cool"]}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when not enabled" do
|
||||
it "does not log request" do
|
||||
with_project(project) do
|
||||
replace "config/environment.rb", "logger level: :debug", ""
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
end
|
||||
|
||||
expect(log).to_not be_an_existing_file
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "--host" do
|
||||
it "starts on given host" do
|
||||
with_project do
|
||||
server(host: "127.0.0.1") do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xit "fails when missing" do
|
||||
with_project do
|
||||
server(host: nil)
|
||||
|
||||
expect(exitstatus).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "--port" do
|
||||
it "starts on given port" do
|
||||
with_project do
|
||||
server(port: 1982) do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xit "fails when missing" do
|
||||
with_project do
|
||||
server(port: nil)
|
||||
|
||||
expect(exitstatus).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "environment" do
|
||||
it "starts with given environment" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = Hanami.env
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
RSpec::Support::Env["HANAMI_ENV"] = env = "production"
|
||||
RSpec::Support::Env["DATABASE_URL"] = "sqlite://#{Pathname.new('db').join('bookshelf.sqlite')}"
|
||||
RSpec::Support::Env["SMTP_HOST"] = "localhost"
|
||||
RSpec::Support::Env["SMTP_PORT"] = "25"
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_content(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xit "fails when missing" do
|
||||
with_project do
|
||||
server(environment: nil)
|
||||
|
||||
expect(exitstatus).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "puma" do
|
||||
it "starts" do
|
||||
with_project("bookshelf_server_puma", server: :puma) do
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "unicorn" do
|
||||
it "starts" do
|
||||
with_project("bookshelf_server_unicorn", server: :unicorn) do
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "code reloading" do
|
||||
it "reloads templates code" do
|
||||
with_project do
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
rewrite "apps/web/templates/home/index.html.erb", <<~EOF
|
||||
<h1>Hello, World!</h1>
|
||||
EOF
|
||||
|
||||
visit "/"
|
||||
expect(page).to have_title("Web")
|
||||
expect(page).to have_content("Hello, World!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "reloads view" do
|
||||
with_project do
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
rewrite "apps/web/views/home/index.rb", <<~EOF
|
||||
module Web::Views::Home
|
||||
class Index
|
||||
include Web::View
|
||||
|
||||
def greeting
|
||||
"Ciao!"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
rewrite "apps/web/templates/home/index.html.erb", <<~EOF
|
||||
<%= greeting %>
|
||||
EOF
|
||||
|
||||
visit "/"
|
||||
expect(page).to have_title("Web")
|
||||
expect(page).to have_content("Ciao!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "reloads action" do
|
||||
with_project do
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = "Hi!"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
visit "/"
|
||||
expect(page).to have_content("Hi!")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "reloads model" do
|
||||
project_name = "bookshelf"
|
||||
|
||||
with_project(project_name) do
|
||||
# STEP 1: prepare the database and the repository
|
||||
generate_model "user"
|
||||
generate_migration "create_users", <<~EOF
|
||||
Hanami::Model.migration do
|
||||
change do
|
||||
create_table :users do
|
||||
primary_key :id
|
||||
column :name, String
|
||||
end
|
||||
|
||||
execute "INSERT INTO users (name) VALUES('L')"
|
||||
execute "INSERT INTO users (name) VALUES('MG')"
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
rewrite "lib/#{project_name}/repositories/user_repository.rb", <<~EOF
|
||||
class UserRepository < Hanami::Repository
|
||||
def listing
|
||||
all
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
hanami "db prepare"
|
||||
|
||||
# STEP 2: generate the action
|
||||
generate "action web users#index --url=/users"
|
||||
|
||||
rewrite "apps/web/controllers/users/index.rb", <<~EOF
|
||||
module Web::Controllers::Users
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = UserRepository.new.listing.map(&:name).join(", ")
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
# STEP 3: visit the page
|
||||
visit "/users"
|
||||
|
||||
expect(page).to have_content("L, MG")
|
||||
|
||||
# STEP 4: change the repository, then visit the page again
|
||||
rewrite "lib/#{project_name}/repositories/user_repository.rb", <<~EOF
|
||||
class UserRepository < Hanami::Repository
|
||||
def listing
|
||||
all.reverse
|
||||
end
|
||||
end
|
||||
EOF
|
||||
visit "/users"
|
||||
|
||||
expect(page).to have_content("MG, L")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
xit "reloads asset" do
|
||||
with_project do
|
||||
server do
|
||||
write "apps/web/assets/stylesheets/style.css", <<~EOF
|
||||
body { background-color: #fff; }
|
||||
EOF
|
||||
|
||||
visit "/assets/style.css"
|
||||
expect(page).to have_content("#fff")
|
||||
|
||||
rewrite "apps/web/assets/stylesheets/style.css", <<~EOF
|
||||
body { background-color: #333; }
|
||||
EOF
|
||||
|
||||
visit "/assets/style.css"
|
||||
expect(page).to have_content("#333")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "without code reloading" do
|
||||
it "doesn't reload code" do
|
||||
with_project do
|
||||
server("no-code-reloading" => nil) do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
visit "/"
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "without hanami model" do
|
||||
it "uses custom model domain" do
|
||||
project_without_hanami_model("bookshelf", gems: ["dry-struct"]) do
|
||||
write "lib/entities/access_token.rb", <<~EOF
|
||||
require 'dry-struct'
|
||||
require 'securerandom'
|
||||
|
||||
module Types
|
||||
include Dry::Types.module
|
||||
end
|
||||
|
||||
class AccessToken < Dry::Struct
|
||||
attribute :id, Types::String.default { SecureRandom.uuid }
|
||||
attribute :secret, Types::String
|
||||
attribute :digest, Types::String
|
||||
end
|
||||
EOF
|
||||
generate "action web access_tokens#show --url=/access_tokens/:id"
|
||||
rewrite "apps/web/controllers/access_tokens/show.rb", <<~EOF
|
||||
module Web::Controllers::AccessTokens
|
||||
class Show
|
||||
include Web::Action
|
||||
expose :access_token
|
||||
|
||||
def call(params)
|
||||
@access_token = AccessToken.new(id: '1', secret: 'shh', digest: 'abc')
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
rewrite "apps/web/templates/access_tokens/show.html.erb", <<~EOF
|
||||
<h1><%= access_token.secret %></h1>
|
||||
EOF
|
||||
|
||||
server do
|
||||
visit "/access_tokens/1"
|
||||
visit "/access_tokens/1" # forces code reloading
|
||||
expect(page).to have_content("shh")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "without mailer" do
|
||||
it "returns page" do
|
||||
with_project do
|
||||
remove_block "config/environment.rb", "mailer do"
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<-OUT
|
||||
Command:
|
||||
hanami server
|
||||
|
||||
Usage:
|
||||
hanami server
|
||||
|
||||
Description:
|
||||
Start Hanami server (only for development)
|
||||
|
||||
Options:
|
||||
--server=VALUE # Force a server engine (eg, webrick, puma, thin, etc..)
|
||||
--host=VALUE # The host address to bind to
|
||||
--port=VALUE, -p VALUE # The port to run the server on
|
||||
--debug=VALUE # Turn on debug output
|
||||
--warn=VALUE # Turn on warnings
|
||||
--daemonize=VALUE # Daemonize the server
|
||||
--pid=VALUE # Path to write a pid file after daemonize
|
||||
--[no-]code-reloading # Code reloading, default: true
|
||||
--help, -h # Print this help
|
||||
|
||||
Examples:
|
||||
hanami server # Basic usage (it uses the bundled server engine)
|
||||
hanami server --server=webrick # Force `webrick` server engine
|
||||
hanami server --host=0.0.0.0 # Bind to a host
|
||||
hanami server --port=2306 # Bind to a port
|
||||
hanami server --no-code-reloading # Disable code reloading
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami server --help', output
|
||||
end
|
||||
end
|
||||
|
||||
context "with HANAMI_APPS ENV variable" do
|
||||
it "loads only specific app" do
|
||||
with_project do
|
||||
generate "app admin"
|
||||
|
||||
remove_line "config/environment.rb", "require_relative '../apps/admin/app'"
|
||||
remove_line "config/environment.rb", "mount Admin::App"
|
||||
|
||||
remove_line "config/environment.rb", "require_relative '../apps/web/app'"
|
||||
remove_line "config/environment.rb", "mount Web::App"
|
||||
|
||||
inject_line_after "config/environment.rb", "Hanami.configure", <<-EOL
|
||||
if Hanami.app?(:admin)
|
||||
require_relative '../apps/admin/app'
|
||||
mount Admin::App, at: '/admin'
|
||||
end
|
||||
|
||||
if Hanami.app?(:web)
|
||||
require_relative '../apps/web/app'
|
||||
mount Web::App, at: '/'
|
||||
end
|
||||
EOL
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = "app: web"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
generate "action admin home#index --url=/"
|
||||
|
||||
rewrite "apps/admin/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Admin::Action
|
||||
|
||||
def call(params)
|
||||
self.body = "app: admin"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
RSpec::Support::Env["HANAMI_APPS"] = "web"
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
expect(page).to have_content("app: web")
|
||||
|
||||
visit "/admin"
|
||||
expect(page).to have_content("Not Found")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,85 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "hanami version", type: :integration do
|
||||
context "within a project" do
|
||||
it "prints current version" do
|
||||
with_project do
|
||||
run_cmd 'hanami version', "v#{Hanami::VERSION}"
|
||||
end
|
||||
end
|
||||
|
||||
it "prints current version with v alias" do
|
||||
with_project do
|
||||
run_cmd 'hanami v', "v#{Hanami::VERSION}"
|
||||
end
|
||||
end
|
||||
|
||||
it "prints current version with -v alias" do
|
||||
with_project do
|
||||
run_cmd 'hanami -v', "v#{Hanami::VERSION}"
|
||||
end
|
||||
end
|
||||
|
||||
it "prints current version with --version alias" do
|
||||
with_project do
|
||||
run_cmd 'hanami --version', "v#{Hanami::VERSION}"
|
||||
end
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
with_project do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami version
|
||||
|
||||
Usage:
|
||||
hanami version
|
||||
|
||||
Description:
|
||||
Print Hanami version
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami version --help', output
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "outside of a project" do
|
||||
it 'prints current version' do
|
||||
run_cmd 'hanami version', "v#{Hanami::VERSION}"
|
||||
end
|
||||
|
||||
it 'prints current version with v alias' do
|
||||
run_cmd 'hanami v', "v#{Hanami::VERSION}"
|
||||
end
|
||||
|
||||
it 'prints current version with -v alias' do
|
||||
run_cmd 'hanami -v', "v#{Hanami::VERSION}"
|
||||
end
|
||||
|
||||
it 'prints current version with --version alias' do
|
||||
run_cmd 'hanami --version', "v#{Hanami::VERSION}"
|
||||
end
|
||||
|
||||
it "prints help message" do
|
||||
output = <<~OUT
|
||||
Command:
|
||||
hanami version
|
||||
|
||||
Usage:
|
||||
hanami version
|
||||
|
||||
Description:
|
||||
Print Hanami version
|
||||
|
||||
Options:
|
||||
--help, -h # Print this help
|
||||
OUT
|
||||
|
||||
run_cmd 'hanami version --help', output
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Early Hints", type: :integration do
|
||||
it "pushes assets" do
|
||||
skip "curl not installed" if which("curl").nil?
|
||||
|
||||
with_project("bookshelf", server: :puma) do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
write "apps/web/assets/javascripts/app.css", <<~EOF
|
||||
body { margin: 0; }
|
||||
EOF
|
||||
|
||||
inject_line_after "apps/web/templates/app.html.erb", /favicon/, %(<%= stylesheet "app" %>)
|
||||
inject_line_after "config/environment.rb", /mount/, "early_hints true"
|
||||
|
||||
write "config/puma.rb", <<~EOF
|
||||
early_hints true
|
||||
EOF
|
||||
|
||||
port = RSpec::Support::RandomPort.call
|
||||
server(port: port) do
|
||||
sleep 2
|
||||
|
||||
# The Ruby HTTP client that we use for testing (excon), fails to connect to the server.
|
||||
# It's very likely that it assumes that for each request, the server will return only one response.
|
||||
# But in case of Early Hints (103) the returned response are multiple: `n` Early Hints (103) + OK (200).
|
||||
#
|
||||
# For this reason we fall back to cURL for this test.
|
||||
system_exec("curl -i -v http://localhost:#{port}/")
|
||||
expect(out).to include("Link: </assets/app.css>; rel=preload; as=style")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,244 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "handle exceptions", type: :integration do
|
||||
it "doesn't handle exceptions in development mode" do
|
||||
with_project do
|
||||
generate_action
|
||||
|
||||
server do
|
||||
get "/books/1"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't handle exceptions in test mode" do
|
||||
with_project do
|
||||
generate_action
|
||||
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "test"
|
||||
|
||||
server do
|
||||
get "/books/1"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when handles exceptions in production mode" do
|
||||
it "it returns the expected status" do
|
||||
with_project do
|
||||
generate_action
|
||||
setup_production_env
|
||||
|
||||
server do
|
||||
get "/books/1"
|
||||
|
||||
expect(last_response.status).to eq(400)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "and an exception is raised from a template" do
|
||||
it "it returns a 500 and it renders a custom template if it exists" do
|
||||
with_project do
|
||||
generate "action web books#show --url=/books/:id"
|
||||
|
||||
rewrite "apps/web/templates/books/show.html.erb", <<~EOF
|
||||
<%= raise ArgumentError.new("oh nooooo") %>
|
||||
EOF
|
||||
|
||||
write "apps/web/templates/500.html.erb", <<~EOF
|
||||
This is a custom template for 500 error
|
||||
EOF
|
||||
|
||||
setup_production_env
|
||||
|
||||
server do
|
||||
get "/books/1"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
expect(last_response.body).to eq("This is a custom template for 500 error\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "it returns a 500 and it renders the default template if custom template doesn't exist" do
|
||||
with_project do
|
||||
generate "action web books#show --url=/books/:id"
|
||||
|
||||
rewrite "apps/web/templates/books/show.html.erb", <<~EOF
|
||||
<%= raise ArgumentError.new("oh nooooo") %>
|
||||
EOF
|
||||
|
||||
setup_production_env
|
||||
|
||||
server do
|
||||
get "/books/1"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
expect(last_response.body).to include("<h2>500 - Internal Server Error</h2>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "it returns a 500 and renders backtrace error if an exception is raised from 500 custom template" do
|
||||
with_project do
|
||||
generate "action web books#show --url=/books/:id"
|
||||
|
||||
rewrite "apps/web/templates/books/show.html.erb", <<~EOF
|
||||
<%= raise ArgumentError.new("oh nooooo") %>
|
||||
EOF
|
||||
|
||||
write "apps/web/templates/500.html.erb", <<~EOF
|
||||
<%= raise ArgumentError.new("Error from custom template") %>
|
||||
This is a custom template for 500 error
|
||||
EOF
|
||||
|
||||
setup_production_env
|
||||
|
||||
server do
|
||||
get "/books/1"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
expect(last_response.body).to_not include("This is a custom template for 500 error")
|
||||
expect(last_response.body).to include("Error from custom template")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "and an exception is raised from a view" do
|
||||
it "it returns a 500 and it renders a custom template if it exists" do
|
||||
with_project do
|
||||
generate "action web books#show --url=/books/:id"
|
||||
|
||||
rewrite "apps/web/views/books/show.rb", <<~EOF
|
||||
module Web::Views::Books
|
||||
class Show
|
||||
include Web::View
|
||||
|
||||
def header
|
||||
raise ArgumentError.new("oh nooooo")
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
rewrite "apps/web/templates/books/show.html.erb", <<~EOF
|
||||
<%= header %>
|
||||
EOF
|
||||
|
||||
write "apps/web/templates/500.html.erb", <<~EOF
|
||||
This is a custom template for 500 error
|
||||
EOF
|
||||
|
||||
setup_production_env
|
||||
|
||||
server do
|
||||
get "/books/1"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
expect(last_response.body).to eq("This is a custom template for 500 error\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "it returns a 500 and it renders the default template if custom template doesn't exist" do
|
||||
with_project do
|
||||
generate "action web books#show --url=/books/:id"
|
||||
|
||||
rewrite "apps/web/views/books/show.rb", <<~EOF
|
||||
module Web::Views::Books
|
||||
class Show
|
||||
include Web::View
|
||||
|
||||
def header
|
||||
raise ArgumentError.new("oh nooooo")
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
rewrite "apps/web/templates/books/show.html.erb", <<~EOF
|
||||
<%= header %>
|
||||
EOF
|
||||
|
||||
setup_production_env
|
||||
|
||||
server do
|
||||
get "/books/1"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
expect(last_response.body).to include("<h2>500 - Internal Server Error</h2>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "it returns a 500 and renders backtrace error if an exception is raised from 500 custom template" do
|
||||
with_project do
|
||||
generate "action web books#show --url=/books/:id"
|
||||
|
||||
rewrite "apps/web/views/books/show.rb", <<~EOF
|
||||
module Web::Views::Books
|
||||
class Show
|
||||
include Web::View
|
||||
|
||||
def header
|
||||
raise ArgumentError.new("oh nooooo")
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
rewrite "apps/web/templates/books/show.html.erb", <<~EOF
|
||||
<%= header %>
|
||||
EOF
|
||||
|
||||
write "apps/web/templates/500.html.erb", <<~EOF
|
||||
<%= raise ArgumentError.new("Error from custom template") %>
|
||||
This is a custom template for 500 error
|
||||
EOF
|
||||
|
||||
setup_production_env
|
||||
|
||||
server do
|
||||
get "/books/1"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
expect(last_response.body).to_not include("This is a custom template for 500 error")
|
||||
expect(last_response.body).to include("Error from custom template")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_action
|
||||
generate "action web books#show --url=/books/:id"
|
||||
|
||||
rewrite "apps/web/controllers/books/show.rb", <<~EOF
|
||||
module Web::Controllers::Books
|
||||
class Show
|
||||
include Web::Action
|
||||
handle_exception ArgumentError => 400
|
||||
|
||||
def call(params)
|
||||
raise ArgumentError.new("oh nooooo")
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
end
|
||||
|
||||
def setup_production_env
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "production"
|
||||
RSpec::Support::Env["DATABASE_URL"] = "sqlite://#{Pathname.new('db').join('bookshelf.sqlite')}"
|
||||
RSpec::Support::Env["SMTP_HOST"] = "localhost"
|
||||
RSpec::Support::Env["SMTP_PORT"] = "25"
|
||||
end
|
||||
end
|
|
@ -1,89 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "HTTP HEAD", type: :integration do
|
||||
it "returns empty body for HEAD requests" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
server do
|
||||
head "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.body).to eq("")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "returns empty body for HEAD requests when body is set by the action" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = "Hello"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
head "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.body).to eq("")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "returns empty body for HEAD requests when body is set by the view" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
rewrite "apps/web/views/home/index.rb", <<~EOF
|
||||
module Web::Views::Home
|
||||
class Index
|
||||
include Web::View
|
||||
|
||||
def render
|
||||
"World"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
head "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.body).to eq("")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "returns empty body for HEAD requests with send file" do
|
||||
with_project do
|
||||
write "public/static.txt", "Plain text file"
|
||||
generate "action web home#index --url=/"
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
send_file "static.txt"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
head "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.body).to eq("")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "HTTP headers", type: :integration do
|
||||
it "returns HTTP headers" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers.keys).to eq(
|
||||
%w[
|
||||
X-Frame-Options X-Content-Type-Options X-Xss-Protection
|
||||
Content-Security-Policy Content-Type Content-Length
|
||||
Server Date Connection
|
||||
]
|
||||
)
|
||||
|
||||
expect(last_response.headers["X-Frame-Options"]).to eq("DENY")
|
||||
expect(last_response.headers["X-Content-Type-Options"]).to eq("nosniff")
|
||||
expect(last_response.headers["X-Xss-Protection"]).to eq("1; mode=block")
|
||||
expect(last_response.headers["Content-Security-Policy"]).to eq("form-action 'self'; frame-ancestors 'self'; base-uri 'self'; default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self' https: data:; style-src 'self' 'unsafe-inline' https:; font-src 'self'; object-src 'none'; plugin-types app/pdf; child-src 'self'; frame-src 'self'; media-src 'self'")
|
||||
expect(last_response.headers["Content-Type"]).to eq("text/html; charset=utf-8")
|
||||
expect(Integer(last_response.headers["Content-Length"])).to be > 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,32 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Mailer", type: :integration do
|
||||
it "use a mailer" do
|
||||
with_project do
|
||||
generate "mailer welcome"
|
||||
write "lib/bookshelf/mailers/default_user.rb", <<~EOF
|
||||
module Mailers
|
||||
module DefaultUser
|
||||
def user_name
|
||||
"Alfonso"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
replace "config/environment.rb", "delivery :test", <<-EOF
|
||||
delivery :test
|
||||
|
||||
prepare do
|
||||
include Mailers::DefaultUser
|
||||
end
|
||||
EOF
|
||||
|
||||
console do |input, _, _|
|
||||
input.puts("Mailers::Welcome.new.user_name")
|
||||
end
|
||||
|
||||
expect(out).to include("Alfonso")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,81 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Project middleware", type: :integration do
|
||||
it "mounts Rack middleware" do
|
||||
with_project do
|
||||
generate_middleware
|
||||
|
||||
unshift "config/environment.rb", 'require "rack/etag"'
|
||||
unshift "config/environment.rb", 'require_relative "./middleware/runtime"'
|
||||
unshift "config/environment.rb", 'require_relative "./middleware/custom"'
|
||||
|
||||
inject_line_after "config/environment.rb", "Hanami.configure", <<~EOL
|
||||
middleware.use Middleware::Runtime
|
||||
middleware.use Middleware::Custom, "OK"
|
||||
middleware.use Rack::ETag
|
||||
EOL
|
||||
|
||||
generate "action web home#index --url=/"
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = "OK"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
|
||||
expect(last_response.headers["X-Runtime"]).to eq("1ms")
|
||||
expect(last_response.headers["X-Custom"]).to eq("OK")
|
||||
expect(last_response.headers["ETag"]).to_not be_nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_middleware # rubocop:disable Metrics/MethodLength
|
||||
write "config/middleware/runtime.rb", <<~EOF
|
||||
module Middleware
|
||||
class Runtime
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status, headers, body = @app.call(env)
|
||||
headers["X-Runtime"] = "1ms"
|
||||
|
||||
[status, headers, body]
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
write "config/middleware/custom.rb", <<~EOF
|
||||
module Middleware
|
||||
class Custom
|
||||
def initialize(app, value)
|
||||
@app = app
|
||||
@value = value
|
||||
end
|
||||
|
||||
def call(env)
|
||||
status, headers, body = @app.call(env)
|
||||
headers["X-Custom"] = @value
|
||||
|
||||
[status, headers, body]
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
end
|
||||
end
|
|
@ -1,88 +0,0 @@
|
|||
require "resolv-replace"
|
||||
require "net/http"
|
||||
require "uri"
|
||||
|
||||
RSpec.describe "mount apps", type: :integration do
|
||||
before do
|
||||
stub_dns_hosts("127.0.0.1 #{host} www.#{host} #{subdomain} localhost")
|
||||
end
|
||||
|
||||
let(:host) { "bookshelf.test" }
|
||||
let(:subdomain) { "beta.#{host}" }
|
||||
|
||||
context "with apps mounted with path" do
|
||||
it "shows welcome page" do
|
||||
with_project do
|
||||
generate_host_middleware
|
||||
generate "app admin"
|
||||
generate "app beta"
|
||||
|
||||
replace "config/environment.rb", "Beta::App", %( mount Beta::App, at: "/", host: "#{subdomain}")
|
||||
|
||||
server do
|
||||
# Web
|
||||
visit "/"
|
||||
expect(page).to have_content("bundle exec hanami generate action web 'home#index' --url=/")
|
||||
|
||||
# Admin
|
||||
visit "/admin"
|
||||
expect(page).to have_content("bundle exec hanami generate action admin 'home#index' --url=/")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when apps mounted with host: option" do
|
||||
it "shows welcome page" do
|
||||
with_project do
|
||||
generate_host_middleware
|
||||
generate "app admin"
|
||||
generate "app beta"
|
||||
|
||||
replace "config/environment.rb", "Beta::App", %( mount Beta::App, at: "/", host: "#{subdomain}")
|
||||
|
||||
port = RSpec::Support::RandomPort.call
|
||||
server(port: port) do
|
||||
# Beta
|
||||
response = raw_http_request("http://#{subdomain}:#{port}")
|
||||
expect(response.body).to include("bundle exec hanami generate action beta 'home#index' --url=/")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_host_middleware
|
||||
unshift "config/environment.rb", 'require_relative "./middleware/host"'
|
||||
|
||||
inject_line_after "config/environment.rb", "Hanami.configure", <<-EOL
|
||||
middleware.use Middleware::Host
|
||||
EOL
|
||||
|
||||
write "config/middleware/host.rb", <<-EOF
|
||||
require "uri"
|
||||
|
||||
module Middleware
|
||||
class Host
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
host = URI.parse(env["REQUEST_URI"]).host
|
||||
env["SERVER_NAME"] = host
|
||||
env["HTTP_HOST"] = host
|
||||
env["HTTP_X_FORWARDED_HOST"] = host
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
end
|
||||
|
||||
def raw_http_request(uri)
|
||||
Net::HTTP.get_response(URI.parse(uri))
|
||||
end
|
||||
end
|
|
@ -1,40 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Project initializers", type: :integration do
|
||||
it "mounts Rack middleware" do
|
||||
with_project("project_initializers", gems: ["i18n"]) do
|
||||
write "config/locales/en.yml", <<~EOF
|
||||
en:
|
||||
greeting: "Welcome stranger"
|
||||
EOF
|
||||
|
||||
write "config/initializers/i18n.rb", <<~EOF
|
||||
require 'i18n'
|
||||
I18n.load_path = Dir['config/locales/*.yml']
|
||||
I18n.backend.load_translations
|
||||
EOF
|
||||
|
||||
generate "action web home#index --url=/"
|
||||
rewrite "apps/web/views/home/index.rb", <<~EOF
|
||||
module Web::Views::Home
|
||||
class Index
|
||||
include Web::View
|
||||
|
||||
def greeting
|
||||
I18n.t(:greeting)
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
rewrite "apps/web/templates/home/index.html.erb", <<~EOF
|
||||
<h1><%= greeting%></h1>
|
||||
EOF
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.body).to include("Welcome stranger")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "rackup", type: :integration do
|
||||
it "serves contents from database" do
|
||||
with_project do
|
||||
setup_model
|
||||
console do |input, _, _|
|
||||
input.puts("BookRepository.new.create(title: 'Learn Hanami')")
|
||||
input.puts("exit")
|
||||
end
|
||||
|
||||
generate "action web books#show --url=/books/:id"
|
||||
rewrite "apps/web/controllers/books/show.rb", <<~EOF
|
||||
module Web::Controllers::Books
|
||||
class Show
|
||||
include Web::Action
|
||||
expose :book
|
||||
|
||||
def call(params)
|
||||
@book = BookRepository.new.find(params[:id]) or halt(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
rewrite "apps/web/templates/books/show.html.erb", <<~EOF
|
||||
<h1><%= book.title %></h1>
|
||||
EOF
|
||||
|
||||
rackup do
|
||||
visit "/books/1"
|
||||
expect(page).to have_content("Learn Hanami")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,67 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Rake: default task", type: :integration do
|
||||
context "with Minitest" do
|
||||
xit "runs tests" do
|
||||
with_project("bookshelf", test: "minitest") do
|
||||
setup_model
|
||||
|
||||
prepare_development_database
|
||||
generate_development_data
|
||||
|
||||
prepare_test_database
|
||||
|
||||
write "spec/bookshelf/repositories/book_repository_spec.rb", <<~EOF
|
||||
require 'spec_helper'
|
||||
|
||||
describe BookRepository do
|
||||
before do
|
||||
BookRepository.new.clear
|
||||
end
|
||||
|
||||
it 'finds all the records' do
|
||||
BookRepository.new.all.to_a.must_equal []
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
bundle_exec "rake"
|
||||
expect(out).to include("2 runs, 3 assertions, 0 failures, 0 errors, 0 skips")
|
||||
|
||||
assert_development_data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prepare_development_database
|
||||
prepare_database
|
||||
end
|
||||
|
||||
def prepare_test_database
|
||||
prepare_database env: "test"
|
||||
end
|
||||
|
||||
def generate_development_data
|
||||
migrate
|
||||
|
||||
console do |input, _, _|
|
||||
input.puts("BookRepository.new.create(title: 'Learn Hanami')")
|
||||
input.puts("exit")
|
||||
end
|
||||
end
|
||||
|
||||
def assert_development_data
|
||||
console do |input, _, _|
|
||||
input.puts("BookRepository.new.all.to_a.count")
|
||||
input.puts("exit")
|
||||
end
|
||||
|
||||
expect(out).to include("\n1")
|
||||
end
|
||||
|
||||
def prepare_database(env: nil)
|
||||
hanami "db prepare", env: env
|
||||
end
|
||||
end
|
|
@ -1,69 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Rake: default task", type: :integration do
|
||||
context "with RSpec" do
|
||||
it "runs tests" do
|
||||
with_project("bookshelf", test: "rspec") do
|
||||
setup_model
|
||||
|
||||
prepare_development_database
|
||||
generate_development_data
|
||||
|
||||
prepare_test_database
|
||||
|
||||
generate "mailer bookshelf"
|
||||
|
||||
write "spec/bookshelf/repositories/book_repository_spec.rb", <<~EOF
|
||||
RSpec.describe BookRepository do
|
||||
before do
|
||||
described_class.new.clear
|
||||
end
|
||||
|
||||
it 'finds all the records' do
|
||||
expect(described_class.new.all.to_a).to eq([])
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
bundle_exec "rake"
|
||||
|
||||
# The default mailer_spec fails on purpose so you set the correct delivery information.
|
||||
expect(out).to include("3 examples, 1 failure")
|
||||
|
||||
assert_development_data
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prepare_development_database
|
||||
prepare_database
|
||||
end
|
||||
|
||||
def prepare_test_database
|
||||
prepare_database env: "test"
|
||||
end
|
||||
|
||||
def generate_development_data
|
||||
migrate
|
||||
|
||||
console do |input, _, _|
|
||||
input.puts("BookRepository.new.create(title: 'Learn Hanami')")
|
||||
input.puts("exit")
|
||||
end
|
||||
end
|
||||
|
||||
def assert_development_data
|
||||
console do |input, _, _|
|
||||
input.puts("BookRepository.new.all.to_a.count")
|
||||
input.puts("exit")
|
||||
end
|
||||
|
||||
expect(out).to include("\n1")
|
||||
end
|
||||
|
||||
def prepare_database(env: nil)
|
||||
hanami "db prepare", env: env
|
||||
end
|
||||
end
|
|
@ -1,61 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Routing helpers", type: :integration do
|
||||
it "uses routing helpers within action" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
generate "action web books#index --url=/books"
|
||||
|
||||
# Add `as:` option, so it can be used by the routing helper
|
||||
replace "apps/web/config/routes.rb", "/books", "get '/books', to: 'books#index', as: :books"
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
redirect_to routes.books_url
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(current_path).to eq("/books")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "uses routing helpers within view" do
|
||||
with_project do
|
||||
generate "action web books#index --url=/books"
|
||||
generate "action web books#show --url=/books/:id"
|
||||
|
||||
# Add `as:` option, so it can be used by the routing helper
|
||||
replace "apps/web/config/routes.rb", "/books/:id", "get '/books/:id', to: 'books#show', as: :book"
|
||||
rewrite "apps/web/views/books/index.rb", <<~EOF
|
||||
module Web::Views::Books
|
||||
class Index
|
||||
include Web::View
|
||||
|
||||
def featured_book_path
|
||||
routes.path(:book, id: 23)
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
rewrite "apps/web/templates/books/index.html.erb", <<~EOF
|
||||
<h1>Books</h1>
|
||||
<h2><a href="<%= featured_book_path %>">Featured Book</a></h2>
|
||||
EOF
|
||||
|
||||
server do
|
||||
visit "/books"
|
||||
|
||||
expect(page.body).to include(%(<a href="/books/23">Featured Book</a>))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Content-Security-Policy header", type: :integration do
|
||||
it "returns default value" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers["Content-Security-Policy"]).to eq("form-action 'self'; frame-ancestors 'self'; base-uri 'self'; default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self' https: data:; style-src 'self' 'unsafe-inline' https:; font-src 'self'; object-src 'none'; plugin-types app/pdf; child-src 'self'; frame-src 'self'; media-src 'self'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "returns custom value" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
replace "apps/web/app.rb", "script-src 'self';", "script-src 'self' https://code.jquery.com;"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers["Content-Security-Policy"]).to eq("form-action 'self'; frame-ancestors 'self'; base-uri 'self'; default-src 'none'; script-src 'self' https://code.jquery.com; connect-src 'self'; img-src 'self' https: data:; style-src 'self' 'unsafe-inline' https:; font-src 'self'; object-src 'none'; plugin-types app/pdf; child-src 'self'; frame-src 'self'; media-src 'self'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't send header if setting is removed" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
replace "apps/web/app.rb", "security.content_security_policy %{", "%{"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers).to_not have_key("Content-Security-Policy")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,42 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "CSRF protection", type: :integration do
|
||||
it "protects POST endpoints from invalid token" do
|
||||
with_project do
|
||||
generate "action web books#create --url=/books --method=POST"
|
||||
replace "apps/web/app.rb", "# sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']", "sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']"
|
||||
|
||||
server do
|
||||
post "/books", title: "TDD", _csrf_token: "invalid"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "protects PATCH endpoints from invalid token" do
|
||||
with_project do
|
||||
generate "action web books#update --url=/books/:id --method=PATCH"
|
||||
replace "apps/web/app.rb", "# sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']", "sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']"
|
||||
|
||||
server do
|
||||
patch "/books/1", title: "Foo", _csrf_token: "invalid"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "protects DELETE endpoints from invalid token" do
|
||||
with_project do
|
||||
generate "action web books#destroy --url=/books/:id --method=DELETE"
|
||||
replace "apps/web/app.rb", "# sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']", "sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']"
|
||||
|
||||
server do
|
||||
delete "/books/1", _csrf_token: "invalid"
|
||||
|
||||
expect(last_response.status).to eq(500)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "force SSL", type: :integration do
|
||||
it "forces SSL" do
|
||||
project = "bookshelf_force_ssl"
|
||||
|
||||
with_project(project, server: :puma) do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
inject_line_after "apps/web/app.rb", "configure do", "force_ssl true"
|
||||
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "production"
|
||||
RSpec::Support::Env["DATABASE_URL"] = "sqlite://#{Pathname.new('db').join('bookshelf.sqlite')}"
|
||||
RSpec::Support::Env["SMTP_HOST"] = "localhost"
|
||||
RSpec::Support::Env["SMTP_PORT"] = "25"
|
||||
|
||||
# key = Pathname.new(__dir__).join("..", "fixtures", "openssl", "server.key").realpath
|
||||
# cert = Pathname.new(__dir__).join("..", "fixtures", "openssl", "server.crt").realpath
|
||||
|
||||
# bundle_exec "puma -b 'ssl://127.0.0.1:2300?key=#{key}&cert=#{cert}'" do
|
||||
server do
|
||||
# FIXME: I know, it's lame how I solved this problem, but I can't get Excon to do SSL handshake
|
||||
expect do
|
||||
get "/"
|
||||
end.to raise_error(Excon::Error::Socket)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "X-Content-Type-Options header", type: :integration do
|
||||
it "returns default value" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers["X-Content-Type-Options"]).to eq("nosniff")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "returns custom value" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
replace "apps/web/app.rb", "security.x_content_type_options 'nosniff'", "security.x_content_type_options 'foo'"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers["X-Content-Type-Options"]).to eq("foo")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't send header if setting is removed" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
replace "apps/web/app.rb", "security.x_content_type_options 'nosniff'", ""
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers).to_not have_key("X-Content-Type-Options")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "X-Frame-Options header", type: :integration do
|
||||
it "returns default value" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers["X-Frame-Options"]).to eq("DENY")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "returns custom value" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
replace "apps/web/app.rb", "security.x_frame_options 'DENY'", "security.x_frame_options 'ALLOW-FROM https://example.test/'"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers["X-Frame-Options"]).to eq("ALLOW-FROM https://example.test/")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't send header if setting is removed" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
replace "apps/web/app.rb", "security.x_frame_options 'DENY'", ""
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers).to_not have_key("X-Frame-Options")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "X-XSS-Protection header", type: :integration do
|
||||
it "returns default value" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers["X-XSS-Protection"]).to eq("1; mode=block")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "returns custom value" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
replace "apps/web/app.rb", "security.x_xss_protection '1; mode=block'", "security.x_xss_protection '0'"
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers["X-XSS-Protection"]).to eq("0")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't send header if setting is removed" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
replace "apps/web/app.rb", "security.x_xss_protection '1; mode=block'", ""
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.headers).to_not have_key("X-XSS-Protection")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,51 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Send file", type: :integration do
|
||||
it "sends file from the public directory" do
|
||||
with_project do
|
||||
write "public/static.txt", "Static file"
|
||||
generate "action web home#index --url=/"
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
send_file "static.txt"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.body).to include("Static file")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "doesn't send file outside of public directory" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
send_file __FILE__
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(404)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,247 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Sessions", type: :integration do
|
||||
it "shows welcome page" do
|
||||
with_project do
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(page).to have_title("Hanami | The web, with simplicity")
|
||||
|
||||
expect(page).to have_content("The web, with simplicity.")
|
||||
expect(page).to have_content("Hanami is Open Source Software for MVC web development with Ruby.")
|
||||
expect(page).to have_content("bundle exec hanami generate action web 'home#index' --url=/")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "is empty by default" do
|
||||
with_project do
|
||||
prepare
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
|
||||
expect(current_path).to eq("/")
|
||||
expect(page).to have_content("Sign in")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "preserves data across requests" do
|
||||
with_project do
|
||||
prepare
|
||||
|
||||
server do
|
||||
visit "/"
|
||||
expect(current_path).to eq("/")
|
||||
|
||||
click_link("Sign in")
|
||||
expect(current_path).to eq("/signin")
|
||||
|
||||
within "form#signin-form" do
|
||||
click_button "Sign in"
|
||||
end
|
||||
|
||||
visit "/" # Without this, Capybara losts the session.
|
||||
|
||||
expect(current_path).to eq("/")
|
||||
expect(page).to have_content("Welcome, Luca")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "clears the session" do
|
||||
with_project do
|
||||
prepare
|
||||
|
||||
server do
|
||||
given_signedin_user
|
||||
|
||||
click_link "Sign out"
|
||||
|
||||
expect(current_path).to eq("/")
|
||||
expect(page).to_not have_content("Welcome, Luca")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when sessions aren't enabled" do
|
||||
it "raises error when trying to use `session'" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = session[:foo]
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
server do
|
||||
visit "/"
|
||||
expect(page).to have_content("To use `session', please enable sessions for the current app.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "raises error when trying to use `flash'" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.body = flash[:notice]
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
server do
|
||||
visit "/"
|
||||
expect(page).to have_content("To use `flash', please enable sessions for the current app.")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def prepare
|
||||
# Enable sessions
|
||||
replace "apps/web/app.rb", "# sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']", "sessions :cookie, secret: ENV['WEB_SESSIONS_SECRET']"
|
||||
|
||||
generate_user
|
||||
generate_actions
|
||||
end
|
||||
|
||||
def generate_user # rubocop:disable Metrics/MethodLength
|
||||
generate_model "user"
|
||||
generate_migration "create_users", <<~EOF
|
||||
Hanami::Model.migration do
|
||||
change do
|
||||
create_table :users do
|
||||
primary_key :id
|
||||
column :name, String
|
||||
end
|
||||
|
||||
execute "INSERT INTO users (name) VALUES('Luca')"
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
hanami "db prepare"
|
||||
end
|
||||
|
||||
def generate_actions
|
||||
generate_home
|
||||
generate_signin
|
||||
generate_signout
|
||||
end
|
||||
|
||||
def generate_home # rubocop:disable Metrics/MethodLength
|
||||
generate "action web home#index --url=/"
|
||||
replace "apps/web/config/routes.rb", "home#index", 'root to: "home#index"'
|
||||
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
expose :current_user
|
||||
|
||||
def call(params)
|
||||
@current_user = UserRepository.new.find(session[:user_id])
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
rewrite "apps/web/templates/home/index.html.erb", <<~EOF
|
||||
<h1>Bookshelf</h1>
|
||||
|
||||
<% if current_user.nil? %>
|
||||
<%= link_to "Sign in", "/signin" %>
|
||||
<% else %>
|
||||
Welcome, <%= current_user.name %>
|
||||
<%= link_to "Sign out", "/signout" %>
|
||||
<% end %>
|
||||
EOF
|
||||
end
|
||||
|
||||
def generate_signin
|
||||
generate_signin_new_action
|
||||
generate_signin_create_action
|
||||
end
|
||||
|
||||
def generate_signin_new_action
|
||||
generate "action web sessions#new --url=/signin --method=GET"
|
||||
rewrite "apps/web/templates/sessions/new.html.erb", <<~EOF
|
||||
<h1>Sign in</h1>
|
||||
|
||||
<%=
|
||||
form_for :signin, "/signin", id: "signin-form" do
|
||||
div do
|
||||
button "Sign in"
|
||||
end
|
||||
end
|
||||
%>
|
||||
EOF
|
||||
end
|
||||
|
||||
def generate_signin_create_action
|
||||
generate "action web sessions#create --url=/signin --method=POST"
|
||||
rewrite "apps/web/controllers/sessions/create.rb", <<~EOF
|
||||
module Web::Controllers::Sessions
|
||||
class Create
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
session[:user_id] = UserRepository.new.first.id
|
||||
redirect_to routes.root_url
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
end
|
||||
|
||||
def generate_signout
|
||||
generate "action web sessions#destroy --url=/signout --method=GET"
|
||||
|
||||
rewrite "apps/web/controllers/sessions/destroy.rb", <<~EOF
|
||||
module Web::Controllers::Sessions
|
||||
class Destroy
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
session[:user_id] = nil
|
||||
redirect_to routes.root_url
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
end
|
||||
|
||||
def given_signedin_user # rubocop:disable Metrics/AbcSize
|
||||
visit "/"
|
||||
expect(current_path).to eq("/")
|
||||
|
||||
click_link("Sign in")
|
||||
expect(current_path).to eq("/signin")
|
||||
|
||||
within "form#signin-form" do
|
||||
click_button "Sign in"
|
||||
end
|
||||
|
||||
visit "/" # Without this, Capybara losts the session.
|
||||
|
||||
expect(current_path).to eq("/")
|
||||
expect(page).to have_content("Welcome, Luca")
|
||||
end
|
||||
end
|
|
@ -1,21 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Static middleware", type: :integration do
|
||||
it "serves a public file" do
|
||||
with_project do
|
||||
write "public/static.txt", "Static file"
|
||||
|
||||
RSpec::Support::Env["HANAMI_ENV"] = "production"
|
||||
RSpec::Support::Env["DATABASE_URL"] = "sqlite://#{Pathname.new('db').join('bookshelf.sqlite')}"
|
||||
RSpec::Support::Env["SERVE_STATIC_ASSETS"] = "true"
|
||||
RSpec::Support::Env["SMTP_HOST"] = "localhost"
|
||||
RSpec::Support::Env["SMTP_PORT"] = "25"
|
||||
|
||||
server do
|
||||
visit "/static.txt"
|
||||
|
||||
expect(page.body).to include("Static file")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,41 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Streaming", type: :integration do
|
||||
xit "streams the body" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
|
||||
# Require Rack::Chunked
|
||||
unshift "apps/web/app.rb", "require 'rack/chunked'"
|
||||
|
||||
# Mount middleware
|
||||
replace "apps/web/app.rb", "# middleware.use", "middleware.use ::Rack::Chunked"
|
||||
replace "apps/web/app.rb", "controller.prepare do", "controller.format text: 'text/plain'\ncontroller.prepare do"
|
||||
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
self.format = :text
|
||||
self.body = Enumerator.new do |y|
|
||||
%w(one two three).each { |s| y << s }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
get "/", {}, "HTTP_VERSION" => "HTTP/1.1"
|
||||
|
||||
expect(last_response.headers).to_not have_key("Content-Length")
|
||||
expect(last_response.headers["Transfer-Encoding"]).to eq("chunked")
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.body).to eq("3\r\none\r\n3\r\ntwo\r\n5\r\nthree\r\n0\r\n\r\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,52 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe "Unsafe send file", type: :integration do
|
||||
it "sends file from the public directory" do
|
||||
with_project do
|
||||
write "public/static.txt", "Static file"
|
||||
generate "action web home#index --url=/"
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
unsafe_send_file "public/static.txt"
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.body).to include("Static file")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it "sends file outside of the public directory" do
|
||||
with_project do
|
||||
generate "action web home#index --url=/"
|
||||
rewrite "apps/web/controllers/home/index.rb", <<~EOF
|
||||
module Web::Controllers::Home
|
||||
class Index
|
||||
include Web::Action
|
||||
|
||||
def call(params)
|
||||
unsafe_send_file __FILE__
|
||||
end
|
||||
end
|
||||
end
|
||||
EOF
|
||||
|
||||
server do
|
||||
get "/"
|
||||
|
||||
expect(last_response.status).to eq(200)
|
||||
expect(last_response.body).to include("Web::Controllers::Home")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue