Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add current_page? helper for pages. #1074

Merged
merged 5 commits into from
Apr 2, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions spec/lucky/url_helpers_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
require "../spec_helper"

include ContextHelper

describe Lucky::UrlHelpers do
describe "#current_page?" do
context "given a string" do
it "tests if a path matches the request path or not" do
view_for("/").current_page?("/").should be_true
view_for("/action").current_page?("/gum").should be_false
view_for("/action").current_page?("/action").should be_true
view_for("/action").current_page?("/action/").should be_true
view_for("/action/").current_page?("/action").should be_true
view_for("/action/").current_page?("/action/").should be_true
end

it "tests if the path of a url matches request path or not" do
view_for("/")
.current_page?("https://example.com/")
.should be_true
view_for("/action")
.current_page?("https://example.com/action")
.should be_true
view_for("/action", host_with_port: "example.io")
.current_page?("https://example.com/action")
.should be_false
view_for("/action", host_with_port: "example.com:3000")
.current_page?("https://example.com/action")
.should be_false
view_for("/action", host_with_port: "example.com:3000")
.current_page?("https://example.com:3000/action")
.should be_true
view_for("/action", host_with_port: "example.com:3000")
.current_page?("http://example.com:3000/action")
.should be_true
end

it "only tests positive for get and head requests" do
view_for("/get", "GET").current_page?("/get").should be_true
view_for("/head", "HEAD").current_page?("/head").should be_true
view_for("/post", "POST").current_page?("/post").should be_false
view_for("/put", "PUT").current_page?("/put").should be_false
view_for("/patch", "PATCH").current_page?("/patch").should be_false
view_for("/delete", "DELETE").current_page?("/delete").should be_false
end
wout marked this conversation as resolved.
Show resolved Hide resolved

it "ignores query parameters by default" do
view_for("/action?order=desc&page=1").current_page?("/action")
.should be_true
view_for("/action").current_page?("/action?order=desc&page=1")
.should be_true
view_for("/action?order=desc&page=1").current_page?("/action/123")
.should be_false
end

it "checks query params if explicitly required" do
view_for("/action?order=desc&page=1")
.current_page?("/action?order=desc&page=1", check_query_params: true)
.should be_true
view_for("/action")
.current_page?("/action", check_query_params: true)
.should be_true
view_for("/action")
.current_page?("/action?order=desc&page=1", check_query_params: true)
.should be_false
view_for("/action?order=desc&page=1")
.current_page?("/action", check_query_params: true)
.should be_false
end
end

context "given a browser action" do
it "tests if the path matches or not" do
view_for("/pages/123").current_page?(Pages::Show.with(123))
.should be_true
view_for("/pages/123").current_page?(Pages::Show.with(12))
.should be_false
view_for("/pages").current_page?(Pages::Index)
.should be_true
wout marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
end

private def view_for(
path : String,
method : String = "GET",
host_with_port : String = "example.com"
)
request = HTTP::Request.new(method, path)
request.headers["Host"] = host_with_port
TestPage.new(build_context(path: path, request: request))
end
wout marked this conversation as resolved.
Show resolved Hide resolved

private class TestPage
include Lucky::HTMLPage
include Lucky::UrlHelpers
end

class Pages::Index < TestAction
get "/pages" do
plain_text "I'm just a list of pages"
end
end

class Pages::Show < TestAction
get "/pages/:id" do
plain_text "I'm just a page"
end
end
18 changes: 15 additions & 3 deletions spec/support/context_helper.cr
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
module ContextHelper
extend self

private def build_request(method = "GET", body = "", content_type = "", fixed_length : Bool = false) : HTTP::Request
private def build_request(
method = "GET",
body = "",
content_type = "",
fixed_length : Bool = false
) : HTTP::Request
headers = HTTP::Headers.new
headers.add("Content-Type", content_type)
if fixed_length
Expand All @@ -10,7 +15,10 @@ module ContextHelper
HTTP::Request.new(method, "/", body: body, headers: headers)
end

def build_context(path = "/", request : HTTP::Request? = nil) : HTTP::Server::Context
def build_context(
path = "/",
request : HTTP::Request? = nil
) : HTTP::Server::Context
build_context_with_io(IO::Memory.new, path: path, request: request)
end

Expand All @@ -26,7 +34,11 @@ module ContextHelper
)
end

private def build_context_with_io(io : IO, path = "/", request = nil) : HTTP::Server::Context
private def build_context_with_io(
io : IO,
path = "/",
request = nil
) : HTTP::Server::Context
request = request || HTTP::Request.new("GET", path)
response = HTTP::Server::Response.new(io)
HTTP::Server::Context.new request, response
Expand Down
75 changes: 75 additions & 0 deletions src/lucky/page_helpers/url_helpers.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
module Lucky::UrlHelpers
# Tests if the given path matches the current request path.
#
# ```
# # Let's say we are visiting https://example.com/shop/products?order=desc&page=1
# current_page?("/shop/checkout")
# # => false
# current_page?("/shop/products")
# # => true
# current_page?("/shop/products/")
# # => true
# current_page?("/shop/products?order=desc&page=1")
# # => true
# current_page?("/shop/products", check_query_params: true)
# # => false
# current_page?("/shop/products?order=desc&page=1", check_query_params: true)
# # => true
# current_page?("https://example.com/shop/products")
# # => true
# current_page?("https://example.io/shop/products")
# # => false
# current_page?("https://example.com/shop/products", check_query_params: true)
# # => false
# current_page?("https://example.com/shop/products?order=desc&page=1")
# # => true
# ```
def current_page?(
value : String,
check_query_params : Bool = false
)
request = @context.request

return false unless {"GET", "HEAD"}.includes?(request.method)

uri = URI.parse(value)

if check_query_params
resource = request.resource
path = uri.full_path
else
resource = URI.parse(request.resource).path
path = uri.path
end

unless path == '/'
path = path.chomp('/')
resource = resource.chomp('/')
end

if value.match(/^\w+:\/\//)
host_with_port = uri.port ? "#{uri.host}:#{uri.port}" : uri.host
"#{host_with_port}#{path}" == "#{request.host_with_port}#{resource}"
else
path == resource
end
end

# Tests if the given path matches the current request path.
#
# ```
# # Visiting https://example.com/pages/123
# current_page?(Pages::Show.with(123))
# # => true
# current_page?(Posts::Show.with(123))
# # => false
# # Visiting https://example.com/pages
# current_page?(Pages::Index)
# # => true
# current_page?(Blog::Index)
# # => false
# ```
def current_page?(action : Lucky::Action.class | Lucky::RouteHelper)
current_page?(action.path)
wout marked this conversation as resolved.
Show resolved Hide resolved
end
end