Compare commits
101 Commits
d3ab78b17d
...
2023-10-15
| Author | SHA1 | Date | |
|---|---|---|---|
| 5631ae386f | |||
| c31c556df5 | |||
| 5fae0c4fa9 | |||
| 144731062b | |||
| 72f3e03edd | |||
| b08693e866 | |||
| ae2360a480 | |||
| 213265a5d0 | |||
| dfb6b23e70 | |||
| c81a7a6160 | |||
| bbb710a5e1 | |||
| 7eaa8eacf8 | |||
| d78e361c29 | |||
| 1ae6c02472 | |||
| 11206854ad | |||
| 33089e6fbd | |||
| 5cec47efcc | |||
| cabb4daded | |||
| 1e61a76ded | |||
| ac3e9f14cd | |||
| 5ee9351362 | |||
| 2964e06639 | |||
| d1a0fc0222 | |||
| 4a82174526 | |||
| 3ae20255f6 | |||
| f69c53bf29 | |||
| 7d6c098047 | |||
| 3421e18f21 | |||
| 0dec27a01c | |||
| c45cf6ab51 | |||
| e50667b392 | |||
| d4db64f404 | |||
| 05858078d1 | |||
| f75f0378bf | |||
| c3d2fb32e2 | |||
| 1fe30fc7e7 | |||
| 43cc151138 | |||
| 1fee4f276d | |||
| 0b17f36fde | |||
| aca92fdae1 | |||
| c7bf62062a | |||
| b853ca6fe4 | |||
| d5919fc82a | |||
| e2395989f4 | |||
| e93868a483 | |||
| 7f6fc24872 | |||
| 776ec0d30d | |||
| 6861740d00 | |||
| 77e72d18e1 | |||
| 1792d94082 | |||
| 16d17bcadb | |||
| f5d1361d59 | |||
| c4932dd6cc | |||
| 031a30ed5f | |||
| cc1fe1c3b7 | |||
| 72e1cee9d5 | |||
| a90ff753b1 | |||
| b6010cd431 | |||
| 33bfb23405 | |||
| ad4f8492d4 | |||
| e6cc0959a4 | |||
| 5bce31baf2 | |||
| c7fee27623 | |||
| f8499a79c2 | |||
| b97ee3f3bb | |||
| beb9b0c14a | |||
| c874426f2e | |||
| c77cf2cb78 | |||
| 02e95ae916 | |||
| 0f0838ae5a | |||
| 47e5a093b4 | |||
| a0ba8ba9f3 | |||
| c0a0548662 | |||
| 2448c93fa7 | |||
| e720761106 | |||
| 450dc26df7 | |||
| 7a32edb6e3 | |||
| 6160174c5d | |||
| d972cc2c3b | |||
| 6795508d3e | |||
| b5af621ee2 | |||
| a4fbdeabe3 | |||
| 3a71e58e22 | |||
| ef9cef8915 | |||
| ee5a6e673b | |||
| 721ce33adb | |||
| e1d0c6162d | |||
| 435eeb9601 | |||
| 95eb1fea4c | |||
| 03eafcdf4c | |||
| 38f92d83c3 | |||
| 6c40ae6311 | |||
| 5cc79ed54d | |||
| b70b0846a1 | |||
| de61253db6 | |||
| c93ac10288 | |||
| 14f5f51d1e | |||
| 79b12ac4b7 | |||
| 8430e828e9 | |||
| 9ca0e9ca48 | |||
| 67e99df83e |
@@ -2,7 +2,6 @@
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
gem "falcon", "~> 0.42.3"
|
||||
|
||||
gem "roda", "~> 3.72"
|
||||
|
||||
@@ -27,3 +26,7 @@ group :test do
|
||||
gem "simplecov"
|
||||
|
||||
end
|
||||
|
||||
gem "puma", "~> 6.4"
|
||||
|
||||
gem "rackup", "~> 2.1"
|
||||
|
||||
+9
-64
@@ -3,30 +3,7 @@ GEM
|
||||
specs:
|
||||
addressable (2.8.5)
|
||||
public_suffix (>= 2.0.2, < 6.0)
|
||||
async (2.6.4)
|
||||
console (~> 1.10)
|
||||
fiber-annotation
|
||||
io-event (~> 1.1)
|
||||
timers (~> 4.1)
|
||||
async-container (0.16.12)
|
||||
async
|
||||
async-io
|
||||
async-http (0.60.2)
|
||||
async (>= 1.25)
|
||||
async-io (>= 1.28)
|
||||
async-pool (>= 0.2)
|
||||
protocol-http (~> 0.24.0)
|
||||
protocol-http1 (~> 0.15.0)
|
||||
protocol-http2 (~> 0.15.0)
|
||||
traces (>= 0.10.0)
|
||||
async-http-cache (0.4.3)
|
||||
async-http (~> 0.56)
|
||||
async-io (1.36.0)
|
||||
async
|
||||
async-pool (0.4.0)
|
||||
async (>= 1.25)
|
||||
bigdecimal (3.1.4)
|
||||
build-environment (1.13.0)
|
||||
builder (3.2.4)
|
||||
capybara (3.39.2)
|
||||
addressable
|
||||
@@ -37,9 +14,6 @@ GEM
|
||||
rack-test (>= 0.6.3)
|
||||
regexp_parser (>= 1.5, < 3.0)
|
||||
xpath (~> 3.2)
|
||||
console (1.23.2)
|
||||
fiber-annotation
|
||||
fiber-local
|
||||
cucumber (9.0.2)
|
||||
builder (~> 3.2, >= 3.2.4)
|
||||
cucumber-ci-environment (~> 9.2, >= 9.2.0)
|
||||
@@ -67,51 +41,25 @@ GEM
|
||||
diff-lcs (1.5.0)
|
||||
docile (1.4.0)
|
||||
erubi (1.12.0)
|
||||
falcon (0.42.3)
|
||||
async
|
||||
async-container (~> 0.16.0)
|
||||
async-http (~> 0.57)
|
||||
async-http-cache (~> 0.4.0)
|
||||
async-io (~> 1.22)
|
||||
build-environment (~> 1.13)
|
||||
bundler
|
||||
localhost (~> 1.1)
|
||||
openssl (~> 3.0)
|
||||
process-metrics (~> 0.2.0)
|
||||
protocol-rack (~> 0.1)
|
||||
samovar (~> 2.1)
|
||||
ffi (1.16.2)
|
||||
fiber-annotation (0.2.0)
|
||||
fiber-local (1.0.0)
|
||||
io-event (1.3.2)
|
||||
localhost (1.1.10)
|
||||
mapping (1.1.1)
|
||||
matrix (0.4.2)
|
||||
mini_mime (1.1.5)
|
||||
mini_portile2 (2.8.4)
|
||||
multi_test (1.1.0)
|
||||
nio4r (2.5.9)
|
||||
nokogiri (1.15.4)
|
||||
mini_portile2 (~> 2.8.2)
|
||||
racc (~> 1.4)
|
||||
openssl (3.2.0)
|
||||
process-metrics (0.2.1)
|
||||
console (~> 1.8)
|
||||
samovar (~> 2.1)
|
||||
protocol-hpack (1.4.2)
|
||||
protocol-http (0.24.7)
|
||||
protocol-http1 (0.15.1)
|
||||
protocol-http (~> 0.22)
|
||||
protocol-http2 (0.15.1)
|
||||
protocol-hpack (~> 1.4)
|
||||
protocol-http (~> 0.18)
|
||||
protocol-rack (0.2.6)
|
||||
protocol-http (~> 0.23)
|
||||
rack (>= 1.0)
|
||||
public_suffix (5.0.3)
|
||||
puma (6.4.0)
|
||||
nio4r (~> 2.0)
|
||||
racc (1.7.1)
|
||||
rack (3.0.8)
|
||||
rack-test (2.1.0)
|
||||
rack (>= 1.3)
|
||||
rackup (2.1.0)
|
||||
rack (>= 3)
|
||||
webrick (~> 1.8)
|
||||
regexp_parser (2.8.1)
|
||||
rexml (3.2.6)
|
||||
roda (3.72.0)
|
||||
@@ -130,9 +78,6 @@ GEM
|
||||
rspec-support (~> 3.12.0)
|
||||
rspec-support (3.12.1)
|
||||
rubyzip (2.3.2)
|
||||
samovar (2.2.0)
|
||||
console (~> 1.0)
|
||||
mapping (~> 1.0)
|
||||
selenium-webdriver (4.13.1)
|
||||
rexml (~> 3.2, >= 3.2.5)
|
||||
rubyzip (>= 1.2.2, < 3.0)
|
||||
@@ -150,8 +95,7 @@ GEM
|
||||
sys-uname (1.2.3)
|
||||
ffi (~> 1.1)
|
||||
tilt (2.3.0)
|
||||
timers (4.3.5)
|
||||
traces (0.11.1)
|
||||
webrick (1.8.1)
|
||||
websocket (1.2.10)
|
||||
xpath (3.2.0)
|
||||
nokogiri (~> 1.8)
|
||||
@@ -163,7 +107,8 @@ DEPENDENCIES
|
||||
capybara (~> 3.39)
|
||||
cucumber (~> 9.0)
|
||||
erubi (~> 1.12)
|
||||
falcon (~> 0.42.3)
|
||||
puma (~> 6.4)
|
||||
rackup (~> 2.1)
|
||||
roda (~> 3.72)
|
||||
rspec (~> 3.12)
|
||||
selenium-webdriver (~> 4.13)
|
||||
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
environment {
|
||||
APP_SESSION_SECRET = ''
|
||||
DB_NAME = 'url_shortener.db'
|
||||
}
|
||||
stages {
|
||||
stage('Init') {
|
||||
steps {
|
||||
sh 'rbenv local 3.2.2'
|
||||
script {
|
||||
env.APP_SESSION_SECRET = sh(script: 'ruby secret.rb', returnStdout: true)
|
||||
}
|
||||
sh 'echo "ENV[\\\"APP_SESSION_SECRET\\\"] ||= $(ruby secret.rb)" > .env.rb'
|
||||
sh 'echo "ENV[\\\"DB_NAME\\\"] ||= \\\"${DB_NAME}\\\"" >> .env.rb'
|
||||
sh 'cat .env.rb'
|
||||
}
|
||||
}
|
||||
stage('Build dependencies') {
|
||||
steps {
|
||||
sh 'bundle install'
|
||||
sh 'sequel -m db/migrations sqlite://db/${DB_NAME}'
|
||||
}
|
||||
}
|
||||
stage('Run tests') {
|
||||
steps {
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
sh 'cucumber features --format html --out cucumber.html'
|
||||
}
|
||||
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
|
||||
sh 'rspec spec --format html --out spec.html'
|
||||
}
|
||||
}
|
||||
}
|
||||
stage('Report results') {
|
||||
steps {
|
||||
archive(includes: 'pkg/*.gem')
|
||||
publishHTML (target: [
|
||||
allowMissing: false,
|
||||
alwaysLinkToLastBuild: false,
|
||||
keepAll: true,
|
||||
reportDir: '.',
|
||||
reportFiles: 'cucumber.html, spec.html, coverage/index.html',
|
||||
reportName: 'Test Results',
|
||||
reportTitles: 'Cucumber Results, RSpec Results, Test Coverage'])
|
||||
}
|
||||
}
|
||||
}
|
||||
post {
|
||||
success {
|
||||
mattermostSend channel: 'git-messages', color: 'good', message: "[${JOB_NAME}](${JOB_URL}) [#${BUILD_NUMBER}](${BUILD_URL}) ([Gitea](${GIT_URL}))", text: 'Build Finished Successfully'
|
||||
}
|
||||
unstable {
|
||||
mattermostSend channel: 'git-messages', color: 'warning', message: "[${JOB_NAME}](${JOB_URL}) [#${BUILD_NUMBER}](${BUILD_URL}) ([Gitea](${GIT_URL}))", text: "Build Unstable"
|
||||
}
|
||||
failure {
|
||||
mattermostSend channel: 'git-messages', color: 'danger', message: "[${JOB_NAME}](${JOB_URL}) [#${BUILD_NUMBER}](${BUILD_URL}) ([Gitea](${GIT_URL}))", text: "Build Failed"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,8 @@ Roda, Falcon, Sequel, and SQLite
|
||||
|
||||
the point of this project is to quickly build something and work on continuous deployment while making small refinements to the functional pieces.
|
||||
|
||||
the only outside piece of software that this project relies on is sqlite3.
|
||||
the outside pieces of software that this project relies on are sqlite3 and pkgconf.
|
||||
|
||||
|
||||
|
||||
first you have to install the dependencies:
|
||||
|
||||
@@ -2,11 +2,14 @@ require 'roda'
|
||||
require 'securerandom'
|
||||
require 'json'
|
||||
require 'sequel'
|
||||
require 'open-uri'
|
||||
|
||||
class App < Roda
|
||||
plugin :sessions, secret: ENV.delete('APP_SESSION_SECRET')
|
||||
plugin :render, escape: true
|
||||
plugin :flash
|
||||
plugin :json_parser
|
||||
plugin :request_headers
|
||||
|
||||
DB = Sequel.sqlite("db/#{ENV['DB_NAME']}")
|
||||
links = DB[:links]
|
||||
@@ -18,24 +21,83 @@ class App < Roda
|
||||
end
|
||||
|
||||
r.get String do | url_code |
|
||||
link = links.filter(:code => url_code).first[:url]
|
||||
r.redirect link
|
||||
link = links.filter(:code => url_code)
|
||||
r.redirect link.first[:url] unless link.first.nil?
|
||||
@message = "Link #{url_code} doesn't exist"
|
||||
response.status = 404
|
||||
view :home
|
||||
end
|
||||
|
||||
r.post "create" do
|
||||
url = r.params['url']
|
||||
if url.empty?
|
||||
if url.nil? or url.empty?
|
||||
flash['message'] = "Please enter a valid URL";
|
||||
r.redirect '/'
|
||||
end
|
||||
if nil == links.filter(:url => url).first
|
||||
|
||||
begin
|
||||
OpenURI.open_uri(url)
|
||||
rescue URI::BadURIError
|
||||
flash['message'] = "Invalid URL"
|
||||
r.redirect '/'
|
||||
rescue OpenURI::HTTPError
|
||||
flash['message'] = "URL not found"
|
||||
r.redirect '/'
|
||||
rescue SocketError => e
|
||||
flash['message'] = "URL does not resolve"
|
||||
r.redirect '/'
|
||||
end
|
||||
|
||||
if links.filter(:url => url).first.nil?
|
||||
code = SecureRandom.urlsafe_base64 4
|
||||
links.insert(url: url, code: code)
|
||||
@message = "Link created"
|
||||
end
|
||||
code = links.filter(:url => url).first[:code]
|
||||
@message ||= "Link exists"
|
||||
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + links.filter(:url => url).first[:code]
|
||||
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + code
|
||||
view :create
|
||||
end
|
||||
r.on "links" do
|
||||
r.post do
|
||||
if 'application/json' != r.headers['CONTENT_TYPE']
|
||||
response.status = 400
|
||||
return {message: "not a valid json request"}.to_json
|
||||
end
|
||||
|
||||
url = r.params['url']
|
||||
if url.nil?
|
||||
response.status = 400
|
||||
return {message: "missing url parameter"}.to_json
|
||||
end
|
||||
|
||||
if url.empty?
|
||||
response.status = 400
|
||||
return {message: "invalid url parameter"}.to_json
|
||||
end
|
||||
|
||||
begin
|
||||
OpenURI.open_uri(url)
|
||||
rescue URI::BadURIError
|
||||
response.status = 400
|
||||
return {message: "invalid url parameter"}.to_json
|
||||
rescue OpenURI::HTTPError
|
||||
response.status = 400
|
||||
return {message: "url not found"}.to_json
|
||||
rescue SocketError => e
|
||||
response.status = 400
|
||||
return {message: "url does not resolve"}.to_json
|
||||
end
|
||||
|
||||
if links.filter(:url => url).first.nil?
|
||||
code = SecureRandom.urlsafe_base64 4
|
||||
links.insert(url: url, code: code)
|
||||
end
|
||||
|
||||
code = links.filter(:url => url).first[:code]
|
||||
@new_link = 'http://' + request.env['HTTP_HOST'] + '/' + code
|
||||
return {url: url, code: code, link: @new_link}.to_json
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -10,32 +10,3 @@ Feature: Homepage
|
||||
And I should see the message "Enter a URL"
|
||||
And I should see a form field "url"
|
||||
And I should see a "Submit" button
|
||||
|
||||
Scenario: Submitting the form without entering a URL
|
||||
Given I visit the "/" page
|
||||
When I click the "Submit" button
|
||||
Then I should be on "/" page
|
||||
And I should see the message "Please enter a valid URL"
|
||||
|
||||
@db-test
|
||||
Scenario: Submitting the form with a correct URL
|
||||
Given I visit the "/" page
|
||||
When I type "http://google.com" in the "url" field
|
||||
And I click the "Submit" button
|
||||
Then I should be on "/create" page
|
||||
And I should see the message "Link created"
|
||||
|
||||
@db-test
|
||||
Scenario: Submitting the form with an existing URL
|
||||
Given I visit the "/" page
|
||||
And A link already exists with the url "http://google.com"
|
||||
When I type "http://google.com" in the "url" field
|
||||
And I click the "Submit" button
|
||||
Then I should be on "/create" page
|
||||
And I should see the message "Link exists"
|
||||
|
||||
@db-test
|
||||
Scenario: Accessing the URL based on the shortcode
|
||||
Given A link already exists with the url "https://google.com" and code "aaaaaa"
|
||||
When I visit the "/aaaaaa" location
|
||||
Then I should be redirected to "https://google.com"
|
||||
|
||||
@@ -3,6 +3,7 @@ Before('@db-test') do
|
||||
@links = Sequel.sqlite("db/#{ENV['DB_NAME']}")[:links]
|
||||
end
|
||||
|
||||
|
||||
# GIVEN
|
||||
|
||||
Given('I visit the {string} page') do |string|
|
||||
@@ -17,6 +18,7 @@ Given('A link already exists with the url {string} and code {string}') do |url,
|
||||
@links.insert(url: url, code: code)
|
||||
end
|
||||
|
||||
|
||||
# WHEN
|
||||
|
||||
When('I click the {string} button') do |string|
|
||||
@@ -31,6 +33,7 @@ When('I visit the {string} location') do |string|
|
||||
visit string
|
||||
end
|
||||
|
||||
|
||||
# THEN
|
||||
|
||||
Then('I should see text {string}') do |string|
|
||||
@@ -58,6 +61,12 @@ Then('I should be redirected to {string}') do |string|
|
||||
location = actual.index(string)
|
||||
location.should equal(0)
|
||||
end
|
||||
|
||||
Then('The status code should be {int}') do |code|
|
||||
page.status_code.should eq(code)
|
||||
end
|
||||
|
||||
|
||||
# AFTER
|
||||
|
||||
After('@db-test') do
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# features/submit.feature
|
||||
|
||||
Feature: Submit
|
||||
|
||||
Submitting URL's on the homepage
|
||||
|
||||
Scenario: Submitting the form without entering a URL
|
||||
Given I visit the "/" page
|
||||
When I click the "Submit" button
|
||||
Then I should be on "/" page
|
||||
And I should see the message "Please enter a valid URL"
|
||||
|
||||
@db-test
|
||||
Scenario: Submitting the form with a correct URL
|
||||
Given I visit the "/" page
|
||||
When I type "http://google.com" in the "url" field
|
||||
And I click the "Submit" button
|
||||
Then I should be on "/create" page
|
||||
And I should see the message "Link created"
|
||||
|
||||
@db-test
|
||||
Scenario: Submitting the form with an existing URL
|
||||
Given I visit the "/" page
|
||||
And A link already exists with the url "http://google.com"
|
||||
When I type "http://google.com" in the "url" field
|
||||
And I click the "Submit" button
|
||||
Then I should be on "/create" page
|
||||
And I should see the message "Link exists"
|
||||
|
||||
@db-test
|
||||
Scenario: Submitting the form with a URL that is 404
|
||||
Given I visit the "/" page
|
||||
When I type "http://google.com/example" in the "url" field
|
||||
And I click the "Submit" button
|
||||
Then I should be on "/" page
|
||||
And I should see the message "URL not found"
|
||||
|
||||
@db-test
|
||||
Scenario: Submitting the form with a URL that does not resolve
|
||||
Given I visit the "/" page
|
||||
When I type "http://bad.tld" in the "url" field
|
||||
And I click the "Submit" button
|
||||
Then I should be on "/" page
|
||||
And I should see the message "URL does not resolve"
|
||||
|
||||
@db-test
|
||||
Scenario: Submitting the form with an invalid URL
|
||||
Given I visit the "/" page
|
||||
When I type "not-an-url" in the "url" field
|
||||
And I click the "Submit" button
|
||||
Then I should be on "/" page
|
||||
And I should see the message "Invalid URL"
|
||||
@@ -0,0 +1,19 @@
|
||||
# features/visit.feature
|
||||
|
||||
Feature: Visit
|
||||
|
||||
Accessing a URL from it's link code
|
||||
|
||||
@db-test
|
||||
Scenario: Accessing the URL based on the shortcode
|
||||
Given A link already exists with the url "https://google.com" and code "aaaaaa"
|
||||
When I visit the "/aaaaaa" location
|
||||
Then I should be redirected to "https://google.com"
|
||||
|
||||
Scenario: Accessing a nonexistent URL code
|
||||
Given I visit the "/aaaaaa" location
|
||||
Then I should be on "/aaaaaa" page
|
||||
And The status code should be 404
|
||||
And I should see the message "Link aaaaaa doesn't exist"
|
||||
And I should see a form field "url"
|
||||
And I should see a "Submit" button
|
||||
@@ -0,0 +1,2 @@
|
||||
require 'securerandom'
|
||||
puts SecureRandom.base64(64).inspect()
|
||||
@@ -0,0 +1,101 @@
|
||||
require 'simplecov'
|
||||
SimpleCov.start
|
||||
|
||||
require_relative '../.env'
|
||||
ENV["DB_NAME"] = "test_#{ENV["DB_NAME"]}"
|
||||
require_relative '../app'
|
||||
require 'rubygems'
|
||||
require 'roda'
|
||||
require 'sequel'
|
||||
require 'rspec'
|
||||
require 'rack/test'
|
||||
|
||||
# DB initialization
|
||||
Sequel.extension :migration
|
||||
Sequel.sqlite("db/#{ENV['DB_NAME']}") do |db|
|
||||
Sequel::Migrator.apply(db, "db/migrations")
|
||||
end
|
||||
|
||||
def app
|
||||
App
|
||||
end
|
||||
|
||||
describe "Submit API request to create new link" do
|
||||
include Rack::Test::Methods
|
||||
before :each do
|
||||
@links = Sequel.sqlite("db/#{ENV['DB_NAME']}")[:links]
|
||||
end
|
||||
after :each do
|
||||
@links.delete
|
||||
end
|
||||
it "should return link data in json format when a valid url is submitted" do
|
||||
data = {
|
||||
url: 'http://google.com'
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
expect(last_response).to be_ok
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['url']).to eq(data[:url])
|
||||
expect(response_json['code']).not_to eq(nil)
|
||||
expect(response_json['link']).to include(response_json['code'])
|
||||
end
|
||||
|
||||
it "should return with a 400 status and 'invalid url parameter' message when an empty url is submitted" do
|
||||
data = {
|
||||
url: ''
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('invalid url parameter')
|
||||
end
|
||||
|
||||
it "should return with a 400 status and 'missing url parameter' message when an empty url is submitted" do
|
||||
data = {
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('missing url parameter')
|
||||
end
|
||||
|
||||
it "should return with a 400 status and 'invalid url parameter' message when an invalid url is submitted" do
|
||||
data = {
|
||||
url: 'not-an-url'
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('invalid url parameter')
|
||||
end
|
||||
|
||||
it "should return with a 400 status and 'url not found' message when a 404 url is submitted" do
|
||||
data = {
|
||||
url: 'http://google.com/example'
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('url not found')
|
||||
end
|
||||
|
||||
it "should return with a 400 status and 'url not found' message when a URL with no DNS is submitted" do
|
||||
data = {
|
||||
url: 'http://bad.tld'
|
||||
}
|
||||
post('/links', data.to_json, "CONTENT_TYPE" => "application/json")
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('url does not resolve')
|
||||
end
|
||||
|
||||
it "should return with a 400 status and 'not a valid json request' message when a request is made with the wrong content type header" do
|
||||
data = {
|
||||
url: 'http://google.com'
|
||||
}
|
||||
post('/links', data.to_json)
|
||||
expect(last_response.status).to eq(400)
|
||||
response_json = JSON.parse(last_response.body)
|
||||
expect(response_json['message']).to eq('not a valid json request')
|
||||
end
|
||||
end
|
||||
+1
-1
@@ -1 +1 @@
|
||||
<h3 style="overflow-wrap: break-word; word-wrap: break-word;"><a href="<%= @new_link %>" target="_blank"><%= @new_link %></a></h3>
|
||||
<h3 style="overflow-wrap: break-word; word-wrap: break-word; margin-top: 3rem; line-height: 2rem;"><a href="<%= @new_link %>" target="_blank"><%= @new_link %></a></h3>
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<form method="POST" action="/create">
|
||||
<div style="margin-bottom: 1rem;">
|
||||
<input type="url"
|
||||
name="url"
|
||||
pattern="https?://.+"
|
||||
placeholder="http://www.example.com/"
|
||||
autocomplete="off"
|
||||
required />
|
||||
</div>
|
||||
<div>
|
||||
<button type="submit">Submit</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
+35
-19
@@ -1,6 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title><%= @page_title || "URL Shortener" %></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" href="data:,">
|
||||
<style>
|
||||
:root {
|
||||
@@ -16,16 +17,44 @@
|
||||
|
||||
body {
|
||||
background: var(--white);
|
||||
background-image: linear-gradient(to bottom right, var(--lightest-blue), var(--white), var(--lightest-blue));
|
||||
background-image: linear-gradient(to bottom right, var(--lightest-blue), var(--white), var(--white), var(--white), var(--lightest-blue));
|
||||
font-family: Helvetica, sans-serif;
|
||||
color: var(--black);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 5rem 0 0;
|
||||
border: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
main {
|
||||
width: 30rem;
|
||||
max-width: calc(100% - 1rem);
|
||||
box-sizing: border-box;
|
||||
border: 5px solid var(--medium-blue);
|
||||
border-radius: 20px;
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
text-align: center;
|
||||
min-height: 260px;
|
||||
box-shadow: 3px 2px 10px var(--shadow);
|
||||
background: var(--white);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input, button {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 1rem;
|
||||
border-radius: 5px;
|
||||
border: 3px solid var(--dark-blue);
|
||||
margin-right: 1rem;
|
||||
outline: none;
|
||||
transition: all .25s ease-in-out;
|
||||
-moz-transition: all .25s ease-in-out;
|
||||
-webkit-transition: all .25s ease-in-out;
|
||||
}
|
||||
|
||||
input:focus, input:hover {
|
||||
@@ -36,12 +65,12 @@
|
||||
color: var(--white);
|
||||
padding: 1rem;
|
||||
border-radius: 5px;
|
||||
border: 2px solid var(--dark-blue);
|
||||
border: 2px solid var(--darker-blue);
|
||||
background: var(--dark-blue);
|
||||
font-weight: bold;
|
||||
transition: all .25s ease-in-out;
|
||||
-moz-transition: all .25s ease-in-out;
|
||||
-webkit-transition: all .25s ease-in-out;
|
||||
transition: background .25s ease-in-out;
|
||||
-moz-transition: background .25s ease-in-out;
|
||||
-webkit-transition: background .25s ease-in-out;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
@@ -66,19 +95,6 @@
|
||||
color: var(--darker-blue);
|
||||
border-bottom: 4px solid var(--darker-blue);
|
||||
}
|
||||
main {
|
||||
max-width: 30rem;
|
||||
min-width: 18rem;
|
||||
border: 5px solid var(--medium-blue);
|
||||
border-radius: 20px;
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
margin-top: 5rem;
|
||||
text-align: center;
|
||||
min-height: 200px;
|
||||
box-shadow: 3px 2px 10px var(--shadow);
|
||||
background: var(--white);
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user