Last active
November 21, 2017 21:16
-
-
Save paulcsmith/c5da6e8888d1a50f13fb6d9acc37a6a9 to your computer and use it in GitHub Desktop.
Testing Lucky apps
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# spec/support/app_conn.cr | |
require "http/client" | |
class AppConn | |
getter! response | |
@response : HTTP::Client::Response? = nil | |
module Matchers | |
def contain_html(html : String) | |
ContainHtmlExpectation.new(html) | |
end | |
def redirect_to(path : String) | |
RedirectedToExpectation.new(path) | |
end | |
end | |
struct RedirectedToExpectation | |
def initialize(@expected_path : String) | |
end | |
def match(conn) | |
conn.response.status_code == 302 && | |
conn.response.headers.fetch("Location") == @expected_path | |
end | |
def failure_message(conn) | |
"Expected to redirect to #{@expected_path}, but redirected to #{conn.response.headers.fetch("Location")}" | |
end | |
def negative_failure_message(conn) | |
"Expected not to redirect to #{@expected_path}, but redirected to #{conn.response.headers.fetch("Location")}" | |
end | |
end | |
struct ContainHtmlExpectation | |
def initialize(@expected_html : String) | |
end | |
def match(conn) | |
conn.response.status_code == 200 && | |
HTML.unescape(conn.response.body).includes? @expected_html | |
end | |
def failure_message(conn) | |
<<-TEXT | |
Expected response to have status 200 with html: #{@expected_html} | |
Instead got: | |
Status: #{conn.response.status_code} | |
HTML: #{conn.response.body} | |
TEXT | |
end | |
def negative_failure_message(conn) | |
<<-TEXT | |
Expected not to have response with status 200 and html: #{@expected_html} | |
TEXT | |
end | |
end | |
def visit(path) | |
request = HTTP::Request.new("GET", path) | |
@response = process_request request | |
end | |
private def process_request(request) | |
io = IO::Memory.new | |
response = HTTP::Server::Response.new(io) | |
context = HTTP::Server::Context.new(request, response) | |
middlewares.call context | |
response.close | |
io.rewind | |
HTTP::Client::Response.from_io(io, decompress: false) | |
end | |
private def middlewares | |
HTTP::Server.build_middleware AppHandlers::HANDLERS | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# These are all the handlers you'd find in src/server.cr | |
# This is out of date. Use the handlers defined in your src/server.cr | |
# Remember to require this in app.cr | |
# This could go in src/app_handlers.cr | |
class AppHandlers | |
HANDLERS = [ | |
HTTP::ErrorHandler.new, | |
HTTP::LogHandler.new, | |
LuckyWeb::RouteHandler.new, | |
HTTP::StaticFileHandler.new("./public", false), | |
] | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This allows you to use Forms as factories. Kind of like FactoryGirl | |
abstract class BaseBox | |
getter form | |
macro form(form_class) | |
@form = {{ form_class }}.new | |
end | |
macro inherited | |
form {{ @type.name.gsub(/Box/, "::BaseForm").id }} | |
end | |
macro method_missing(call) | |
form._{{ call.name }}.value = {{ call.args.first }} | |
self | |
end | |
def save | |
form.save | |
end | |
def self.save_pair | |
new.save | |
new.save | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# spec/support/database_cleaner.cr | |
class DatabaseCleaner | |
def self.truncate | |
PostgreSQLAdapter.new.truncate | |
end | |
end | |
class PostgreSQLAdapter | |
def cascade | |
"CASCADE" | |
end | |
def restart_identity | |
"RESTART IDENTITY" | |
end | |
def truncate | |
return if table_names.empty? | |
statement = ("TRUNCATE TABLE #{table_names.map { |name| name }.join(", ")} #{restart_identity} #{cascade};") | |
LuckyRecord::Repo.run do |db| | |
db.exec statement | |
end | |
end | |
def table_names | |
tables_with_schema(excluding: "schema_migrations") | |
end | |
def tables_with_schema(excluding : String) | |
select_rows <<-SQL | |
SELECT table_name | |
FROM information_schema.tables | |
WHERE table_schema='public' | |
AND table_type='BASE TABLE' | |
AND table_name != '#{excluding}'; | |
SQL | |
end | |
def select_rows(statement) | |
rows = [] of String | |
LuckyRecord::Repo.run do |db| | |
db.query statement do |rs| | |
rs.each do | |
rows << rs.read(String) | |
end | |
end | |
end | |
rows | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "../../../spec_helper" | |
describe Admin::Users::Index do | |
it "shows a list of users" do | |
users = UserBox.save_pair | |
conn = AppConn.new | |
conn.visit Admin::Users::Index.path | |
UserQuery.new.each do |user| | |
conn.should contain_html(user.email) | |
end | |
end | |
end | |
# or | |
require "../../spec_helper" | |
describe Home::Index do | |
it "redirects to the landing page" do | |
conn = AppConn.new | |
conn.visit "/" | |
conn.should redirect_to(LandingPage::Index.path) | |
end | |
end | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "./app" | |
require "colorize" | |
server = HTTP::Server.new("0.0.0.0", 8080, AppHandlers::HANDLERS) | |
puts "Listening on #{ServerSettings.url}...".colorize(:green) | |
server.listen |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "spec" | |
require "../src/app" | |
require "./support/database_cleaner" | |
require "./support/**" | |
# This is important | |
include AppConn::Matchers | |
Spec.before_each do | |
DatabaseCleaner.truncate | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class UserBox < BaseBox | |
def initialize | |
email "paul" | |
name "paul@thoughtbot.com" | |
encrypted_password "fake_password" | |
end | |
def yoda | |
name "yoda" | |
email "yoda@starwars.com" | |
end | |
def set_name(value) | |
name value | |
email "#{value}@thoughtbot.com" | |
end | |
end | |
# You could add custom methods too | |
UserBox.new.yoda.save | |
# or | |
UserBox.new.set_name("Rey").save |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment