ruby_pocket 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +191 -0
- data/Rakefile +51 -0
- data/config/config.rb +14 -0
- data/config/database.rb +14 -0
- data/db/migrations/001_create_tags.rb +15 -0
- data/db/migrations/002_create_favorites.rb +17 -0
- data/db/migrations/003_create_favorites_tags.rb +20 -0
- data/db/pocket_development.db +0 -0
- data/db/pocket_test.db +0 -0
- data/lib/ruby_pocket/all.rb +5 -0
- data/lib/ruby_pocket/cli/add_action.rb +12 -0
- data/lib/ruby_pocket/cli/delete_action.rb +16 -0
- data/lib/ruby_pocket/cli/list_action.rb +39 -0
- data/lib/ruby_pocket/cli/open_action.rb +12 -0
- data/lib/ruby_pocket/cli/options.rb +23 -0
- data/lib/ruby_pocket/cli.rb +8 -0
- data/lib/ruby_pocket/environment.rb +11 -0
- data/lib/ruby_pocket/favorite.rb +36 -0
- data/lib/ruby_pocket/favorite_creator.rb +62 -0
- data/lib/ruby_pocket/favorite_query.rb +34 -0
- data/lib/ruby_pocket/tag.rb +32 -0
- data/lib/ruby_pocket/version.rb +3 -0
- data/lib/ruby_pocket/web_page.rb +33 -0
- data/lib/ruby_pocket.rb +37 -0
- data/test/ruby_pocket/cli/options_test.rb +49 -0
- data/test/ruby_pocket/favorite_creator_test.rb +80 -0
- data/test/ruby_pocket/favorite_query_test.rb +98 -0
- data/test/ruby_pocket/favorite_test.rb +76 -0
- data/test/ruby_pocket/features/add_test.rb +40 -0
- data/test/ruby_pocket/features/delete_test.rb +45 -0
- data/test/ruby_pocket/features/list_test.rb +75 -0
- data/test/ruby_pocket/tag_test.rb +53 -0
- data/test/ruby_pocket/web_page_test.rb +68 -0
- data/test/support/add_feature_mocker.rb +37 -0
- data/test/support/custom_assertions.rb +9 -0
- data/test/support/database_test_case.rb +11 -0
- data/test/support/default_test_case.rb +9 -0
- data/test/support/favorite_factory.rb +14 -0
- data/test/support/feature_assertions.rb +22 -0
- data/test/support/feature_test_case.rb +32 -0
- data/test/support/webmock.rb +3 -0
- data/test/test_helper.rb +19 -0
- metadata +305 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module RubyPocket
|
2
|
+
HttpError = Class.new RubyPocketError
|
3
|
+
|
4
|
+
class WebPage
|
5
|
+
class << self
|
6
|
+
alias :for :new
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(url, scrapper = nil)
|
10
|
+
fetch_page url, scrapper
|
11
|
+
end
|
12
|
+
|
13
|
+
def title
|
14
|
+
@page.title
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def fetch_page(url, scrapper)
|
20
|
+
@page = (scrapper || default_scrapper).get(url)
|
21
|
+
rescue SocketError => e
|
22
|
+
raise HttpError, 'Got a socket error'
|
23
|
+
rescue Mechanize::ResponseCodeError => e
|
24
|
+
raise HttpError, "#{e.response_code} response code"
|
25
|
+
end
|
26
|
+
|
27
|
+
def default_scrapper
|
28
|
+
require 'mechanize'
|
29
|
+
|
30
|
+
Mechanize.new
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/ruby_pocket.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'etc'
|
2
|
+
require 'ruby_pocket/version'
|
3
|
+
require 'ruby_pocket/environment'
|
4
|
+
|
5
|
+
module RubyPocket
|
6
|
+
RubyPocketError = Class.new StandardError
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_accessor :environment
|
10
|
+
|
11
|
+
def environment=(environment)
|
12
|
+
@environment = Environment.new(environment)
|
13
|
+
end
|
14
|
+
|
15
|
+
def environment
|
16
|
+
self.environment = 'PRODUCTION' unless @environment
|
17
|
+
@environment
|
18
|
+
end
|
19
|
+
|
20
|
+
def setup_data_dir
|
21
|
+
return unless environment.production?
|
22
|
+
return if Dir.exist?(data_dir)
|
23
|
+
|
24
|
+
Dir.mkdir data_dir
|
25
|
+
end
|
26
|
+
|
27
|
+
def data_dir
|
28
|
+
@data_dir ||= File.join(home_dir, '.ruby_pocket')
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def home_dir
|
34
|
+
Etc.getpwuid.dir
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'ruby_pocket/cli/options'
|
3
|
+
|
4
|
+
module RubyPocket
|
5
|
+
module Cli
|
6
|
+
class OptionsTest < DefaultTestCase
|
7
|
+
def test_has_an_action
|
8
|
+
options = Options.new
|
9
|
+
options.action = :add
|
10
|
+
|
11
|
+
assert_equal :add, options.action
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_cant_assign_two_subsequent_actions
|
15
|
+
options = Options.new
|
16
|
+
options.action = :add
|
17
|
+
|
18
|
+
error = assert_raises(ArgumentError) do
|
19
|
+
options.action = :list
|
20
|
+
end
|
21
|
+
|
22
|
+
assert_match %r(list and add), error.message
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_validate_fails_when_has_no_action
|
26
|
+
options = Options.new
|
27
|
+
|
28
|
+
error = assert_raises(ArgumentError) do
|
29
|
+
options.validate!
|
30
|
+
end
|
31
|
+
|
32
|
+
assert_match %r(supply an action), error.message
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_validate_succeeds_when_has_an_action
|
36
|
+
options = Options.new
|
37
|
+
options.action = :add
|
38
|
+
|
39
|
+
assert_nil options.validate!
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_has_a_values_hash
|
43
|
+
options = Options.new
|
44
|
+
|
45
|
+
assert_equal Hash.new, options.values
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'minitest/mock'
|
3
|
+
require 'ruby_pocket/favorite_creator'
|
4
|
+
|
5
|
+
module RubyPocket
|
6
|
+
class FavoriteCreatorTest < DatabaseTestCase
|
7
|
+
def test_does_not_inject_a_persisted_favorite
|
8
|
+
persisted_favorite = Favorite.create url: 'http://www.rubytapas.com'
|
9
|
+
creator = build_favorite_creator({})
|
10
|
+
|
11
|
+
error = assert_raises StandardError do
|
12
|
+
creator.favorite = persisted_favorite
|
13
|
+
end
|
14
|
+
|
15
|
+
assert_match %r(new favorite), error.message
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_when_no_name_present_in_values_fetches_web_page_from_url
|
19
|
+
url = 'http://www.rubytapas.com'
|
20
|
+
|
21
|
+
values = { name: nil, url: url }
|
22
|
+
favorite = Favorite.new
|
23
|
+
web_page = web_page_mock(url, page_title: 'Ruby Tapas')
|
24
|
+
|
25
|
+
creator = build_favorite_creator(values, favorite, web_page)
|
26
|
+
creator.save
|
27
|
+
|
28
|
+
assert web_page.verify
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_when_no_name_present_in_values_fetches_name_from_web_pages_title
|
32
|
+
url = 'http://www.rubytapas.com'
|
33
|
+
|
34
|
+
values = { name: nil, url: url }
|
35
|
+
favorite = Favorite.new
|
36
|
+
web_page = web_page_mock(url, page_title: 'Ruby Tapas')
|
37
|
+
|
38
|
+
creator = build_favorite_creator(values, favorite, web_page)
|
39
|
+
creator.save
|
40
|
+
|
41
|
+
assert_equal 'Ruby Tapas', favorite.name
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_does_not_save_favorite_when_values_are_invalid
|
45
|
+
favorite = Favorite.new
|
46
|
+
creator = build_favorite_creator({}, favorite)
|
47
|
+
|
48
|
+
error = assert_raises ValidationError do
|
49
|
+
assert_not creator.save
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_equal 'url is not present', error.message
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_saves_favorite_when_values_are_valid
|
56
|
+
favorite = Favorite.new
|
57
|
+
params = { name: 'RubyTapas', url: 'http://www.rubytapas.com' }
|
58
|
+
creator = build_favorite_creator(params, favorite)
|
59
|
+
|
60
|
+
assert creator.save
|
61
|
+
assert_not favorite.new?
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def build_favorite_creator(values, favorite = nil, web_page = nil)
|
67
|
+
FavoriteCreator.new(values).tap do |creator|
|
68
|
+
creator.favorite = favorite if favorite
|
69
|
+
creator.web_page = web_page if web_page
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def web_page_mock(url, page_title: 'Ruby Tapas')
|
74
|
+
contents = OpenStruct.new(title: page_title)
|
75
|
+
|
76
|
+
web_page_class = Minitest::Mock.new
|
77
|
+
web_page_class.expect :for, contents, [url]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'support/favorite_factory'
|
3
|
+
require 'ruby_pocket/favorite_query'
|
4
|
+
|
5
|
+
module RubyPocket
|
6
|
+
class FavoriteQueryTest < DatabaseTestCase
|
7
|
+
include FavoriteFactory
|
8
|
+
|
9
|
+
def test_finds_all_favorites
|
10
|
+
ruby_flow = create_favorite(url: 'http://www.rubyflow.com')
|
11
|
+
ruby_tapas = create_favorite(url: 'http://www.rubytapas.com')
|
12
|
+
|
13
|
+
favorites = FavoriteQuery.new.all
|
14
|
+
|
15
|
+
assert_equal [ruby_flow, ruby_tapas], favorites
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_when_no_tag_names_finds_all_favorites
|
19
|
+
ruby_flow = create_favorite(url: 'http://www.rubyflow.com')
|
20
|
+
ruby_tapas = create_favorite(url: 'http://www.rubytapas.com')
|
21
|
+
|
22
|
+
favorites = FavoriteQuery.new.where(tag_names: nil).all
|
23
|
+
|
24
|
+
assert_equal [ruby_flow, ruby_tapas], favorites
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_finds_favorite_by_tag_name
|
28
|
+
create_favorite(
|
29
|
+
url: 'http://www.rubyflow.com',
|
30
|
+
tag_names: %w(ruby)
|
31
|
+
)
|
32
|
+
microservices = create_favorite(
|
33
|
+
url: 'http://martinfowler.com/microservices',
|
34
|
+
tag_names: %w(microservices)
|
35
|
+
)
|
36
|
+
|
37
|
+
favorites = FavoriteQuery
|
38
|
+
.new
|
39
|
+
.where(tag_names: %w[microservices])
|
40
|
+
.all
|
41
|
+
|
42
|
+
assert_equal [microservices], favorites
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_finds_favorite_by_more_than_one_tag_name
|
46
|
+
create_favorite(
|
47
|
+
url: 'http://www.rubyflow.com',
|
48
|
+
tag_names: %w(ruby)
|
49
|
+
)
|
50
|
+
create_favorite(
|
51
|
+
url: 'http://railscasts.com',
|
52
|
+
tag_names: %w(screencasts)
|
53
|
+
)
|
54
|
+
ruby_tapas = create_favorite(
|
55
|
+
url: 'http://www.rubytapas.com',
|
56
|
+
tag_names: %w(ruby screencasts)
|
57
|
+
)
|
58
|
+
|
59
|
+
favorites = FavoriteQuery
|
60
|
+
.new
|
61
|
+
.where(tag_names: %w[ruby screencasts])
|
62
|
+
.all
|
63
|
+
|
64
|
+
assert_equal [ruby_tapas], favorites
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_finds_favorite_by_more_than_one_tag_name_with_chained_syntax
|
68
|
+
create_favorite(
|
69
|
+
url: 'http://www.rubyflow.com',
|
70
|
+
tag_names: %w(ruby)
|
71
|
+
)
|
72
|
+
create_favorite(
|
73
|
+
url: 'http://railscasts.com',
|
74
|
+
tag_names: %w(screencasts)
|
75
|
+
)
|
76
|
+
ruby_tapas = create_favorite(
|
77
|
+
url: 'http://www.rubytapas.com',
|
78
|
+
tag_names: %w(ruby screencasts)
|
79
|
+
)
|
80
|
+
|
81
|
+
favorites = FavoriteQuery
|
82
|
+
.new
|
83
|
+
.where(tag_names: %w[ruby])
|
84
|
+
.where(tag_names: %w[screencasts])
|
85
|
+
.all
|
86
|
+
|
87
|
+
assert_equal [ruby_tapas], favorites
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_fails_when_tag_name_does_not_exist
|
91
|
+
error = assert_raises ArgumentError do
|
92
|
+
FavoriteQuery.new.where(tag_names: %w[microservices]).all
|
93
|
+
end
|
94
|
+
|
95
|
+
assert_match %r(not found), error.message
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'support/favorite_factory'
|
3
|
+
require 'ruby_pocket/favorite'
|
4
|
+
|
5
|
+
module RubyPocket
|
6
|
+
class FavoriteTest < DatabaseTestCase
|
7
|
+
include FavoriteFactory
|
8
|
+
|
9
|
+
def test_persists_a_favorite
|
10
|
+
favorite = create_favorite
|
11
|
+
|
12
|
+
assert_not favorite.new?
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_url_is_unique
|
16
|
+
create_favorite
|
17
|
+
|
18
|
+
error = assert_raises(Sequel::ValidationFailed) do
|
19
|
+
create_favorite
|
20
|
+
end
|
21
|
+
|
22
|
+
assert_match(/url is already taken/, error.message)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_name_is_trimmed
|
26
|
+
values = build_values(name: ' Ruby is Cool ')
|
27
|
+
favorite = create_favorite(values)
|
28
|
+
|
29
|
+
assert_equal 'Ruby is Cool', favorite.name
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_favorite_is_persisted_correctly
|
33
|
+
values = build_values
|
34
|
+
favorite = create_favorite
|
35
|
+
|
36
|
+
assert_subset values, favorite.values
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_persists_a_favorite_with_tags
|
40
|
+
values = build_values(tag_names: %w(ruby cool))
|
41
|
+
favorite = create_favorite(values)
|
42
|
+
|
43
|
+
assert_not favorite.new?
|
44
|
+
assert_equal %w(ruby cool), favorite.tags.map(&:name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_persists_a_favorite_without_tags
|
48
|
+
favorite = create_favorite
|
49
|
+
|
50
|
+
assert_empty favorite.tags
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_tags_with_equal_names_are_not_duplicated
|
54
|
+
ruby_flow_values = build_values(
|
55
|
+
url: 'http://www.rubyflow.com',
|
56
|
+
tag_names: %w(Ruby)
|
57
|
+
)
|
58
|
+
ruby_flow = create_favorite(ruby_flow_values)
|
59
|
+
|
60
|
+
ruby_tapas_values = build_values(
|
61
|
+
url: 'http://www.rubytapas.com',
|
62
|
+
tag_names: %w(ruby)
|
63
|
+
)
|
64
|
+
ruby_tapas = create_favorite(ruby_tapas_values)
|
65
|
+
|
66
|
+
assert_equal ruby_flow.tags, ruby_tapas.tags
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_favorites_with_tags_are_deleted_without_errors
|
70
|
+
values = build_values(tag_names: %w(one two))
|
71
|
+
favorite = create_favorite(values)
|
72
|
+
|
73
|
+
assert favorite.destroy
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'ruby_pocket/favorite'
|
3
|
+
|
4
|
+
module RubyPocket
|
5
|
+
module Cli
|
6
|
+
class AddTest < FeatureTestCase
|
7
|
+
def test_adds_a_favorite
|
8
|
+
url = 'http://www.rubytapas.com'
|
9
|
+
title = 'Ruby Tapas'
|
10
|
+
|
11
|
+
output = run_command_web_mocked("pocket -a #{url}", url, title)
|
12
|
+
|
13
|
+
assert_match %r(Ruby Tapas.+created), output.stdout
|
14
|
+
assert Favorite.find(name: 'Ruby Tapas')
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_adds_a_favorite_with_name_and_tags
|
18
|
+
url = 'http://www.fancyname.com'
|
19
|
+
|
20
|
+
output = run_command("pocket -a #{url} -n 'Fancy Name' -t ruby")
|
21
|
+
added_favorite = Favorite.find(name: 'Fancy Name')
|
22
|
+
|
23
|
+
assert_match %r(Fancy Name.+created), output.stdout
|
24
|
+
assert added_favorite
|
25
|
+
assert_equal %w(ruby), added_favorite.tags.map(&:name)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def run_command_web_mocked(command, url, title)
|
31
|
+
run_command(
|
32
|
+
command,
|
33
|
+
MOCK_FEATURE: 'add',
|
34
|
+
WEB_MOCK_URL: url,
|
35
|
+
WEB_MOCK_PAGE_TITLE: title
|
36
|
+
)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'support/favorite_factory'
|
3
|
+
require 'ruby_pocket/favorite'
|
4
|
+
|
5
|
+
module RubyPocket
|
6
|
+
module Cli
|
7
|
+
class DeleteTest < FeatureTestCase
|
8
|
+
include FavoriteFactory
|
9
|
+
|
10
|
+
def test_deletes_one_favorite_by_id
|
11
|
+
favorite = create_favorite
|
12
|
+
|
13
|
+
output = run_command("pocket -d #{favorite.id}")
|
14
|
+
|
15
|
+
assert_match %r(#{favorite.name}.+deleted), output.stdout
|
16
|
+
assert_raises(Sequel::Error) { favorite.reload }
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_deletes_two_favorites_by_id
|
20
|
+
favorite_one = create_favorite
|
21
|
+
favorite_two = create_favorite(
|
22
|
+
name: 'Ruby',
|
23
|
+
url: 'http://www.ruby-lang.org'
|
24
|
+
)
|
25
|
+
|
26
|
+
output = run_command("pocket -d #{favorite_one.id},#{favorite_two.id}")
|
27
|
+
|
28
|
+
assert_match %r(#{favorite_one.name}.+deleted), output.stdout
|
29
|
+
assert_match %r(#{favorite_two.name}.+deleted), output.stdout
|
30
|
+
assert_raises(Sequel::Error) { favorite_one.reload }
|
31
|
+
assert_raises(Sequel::Error) { favorite_two.reload }
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_does_not_delete_favorite_when_not_found
|
35
|
+
favorite = create_favorite
|
36
|
+
|
37
|
+
output = run_command("pocket -d #{favorite.id},1000")
|
38
|
+
|
39
|
+
assert_match %r(#{favorite.name}.+deleted), output.stdout
|
40
|
+
assert_match %r(1000 not found), output.stdout
|
41
|
+
assert_raises(Sequel::Error) { favorite.reload }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'support/favorite_factory'
|
3
|
+
require 'ruby_pocket/favorite'
|
4
|
+
|
5
|
+
module RubyPocket
|
6
|
+
module Cli
|
7
|
+
class ListTest < FeatureTestCase
|
8
|
+
include FavoriteFactory
|
9
|
+
|
10
|
+
def test_lists_available_favorites_with_id_name_and_tags
|
11
|
+
values = build_values(tag_names: %w(ruby))
|
12
|
+
favorite = create_favorite(values)
|
13
|
+
|
14
|
+
output = run_command('pocket -l')
|
15
|
+
|
16
|
+
assert_shows_favorite favorite, output
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_shows_message_when_has_no_favorites
|
20
|
+
output = run_command('pocket -l')
|
21
|
+
|
22
|
+
assert_match %r(is empty), output.stdout
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_searches_for_favorites_by_tags
|
26
|
+
values = {
|
27
|
+
name: 'Ruby Tapas',
|
28
|
+
url: 'http://www.rubytapas.com',
|
29
|
+
tag_names: %w(ruby)
|
30
|
+
}
|
31
|
+
ruby_tapas_favorite = create_favorite(values)
|
32
|
+
|
33
|
+
values = {
|
34
|
+
name: 'Martin Fowler - Microservices article',
|
35
|
+
url: 'http://martinfowler.com/articles/microservices.html',
|
36
|
+
tag_names: %w(microservices)
|
37
|
+
}
|
38
|
+
microservices_favorite = create_favorite(values)
|
39
|
+
|
40
|
+
output = run_command('pocket -l -t microservices')
|
41
|
+
|
42
|
+
assert_not_shows_favorite ruby_tapas_favorite, output
|
43
|
+
assert_shows_favorite microservices_favorite, output
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_searches_for_favorites_by_more_than_one_tag
|
47
|
+
values = {
|
48
|
+
name: 'Ruby Tapas',
|
49
|
+
url: 'http://www.rubytapas.com',
|
50
|
+
tag_names: %w(ruby screencasts)
|
51
|
+
}
|
52
|
+
ruby_tapas_favorite = create_favorite(values)
|
53
|
+
|
54
|
+
values = {
|
55
|
+
name: 'Ruby Flow',
|
56
|
+
url: 'http://www.rubyflow.com',
|
57
|
+
tag_names: %w(ruby aggregator)
|
58
|
+
}
|
59
|
+
ruby_flow_favorite = create_favorite(values)
|
60
|
+
|
61
|
+
output = run_command('pocket -l -t ruby,screencasts')
|
62
|
+
|
63
|
+
assert_shows_favorite ruby_tapas_favorite, output
|
64
|
+
assert_not_shows_favorite ruby_flow_favorite, output
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_shows_an_error_when_tag_not_found
|
68
|
+
output = run_command('pocket -l -t microservices')
|
69
|
+
|
70
|
+
assert_match %r(Tag.+not found), output.stderr
|
71
|
+
assert_empty output.stdout
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'ruby_pocket/tag'
|
3
|
+
|
4
|
+
module RubyPocket
|
5
|
+
class TagTest < DatabaseTestCase
|
6
|
+
def test_tag_name_is_unique
|
7
|
+
Tag.create name: 'ruby'
|
8
|
+
|
9
|
+
error = assert_raises Sequel::ValidationFailed do
|
10
|
+
Tag.create name: 'ruby'
|
11
|
+
end
|
12
|
+
|
13
|
+
assert_match 'name is already taken', error.message
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_trims_white_space_from_tag_name
|
17
|
+
tag = Tag.create(name: ' Ruby ')
|
18
|
+
|
19
|
+
assert_equal 'ruby', tag.name
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_downcases_tag_name
|
23
|
+
tag = Tag.create(name: 'Ruby')
|
24
|
+
|
25
|
+
assert_equal 'ruby', tag.name
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_puts_dashes_between_words_on_tag_name
|
29
|
+
tag = Tag.create(name: 'Precious Ruby')
|
30
|
+
|
31
|
+
assert_equal 'precious-ruby', tag.name
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_replaces_non_letter_characters_with_dashes_on_tag_name
|
35
|
+
names = ['Precious_Ruby', 'Cool_!#>Precious%$#%%Ruby']
|
36
|
+
tags = names.map { |name| Tag.create(name: name) }
|
37
|
+
|
38
|
+
assert_equal %w(precious-ruby cool-precious-ruby), tags.map(&:name)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_normalizes_dashes_of_the_tag_name
|
42
|
+
tag = Tag.create(name: 'Precious__Ruby')
|
43
|
+
|
44
|
+
assert_equal 'precious-ruby', tag.name
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_normalizes_spaces_between_words_of_tag_name
|
48
|
+
tag = Tag.create(name: 'Cool Precious Ruby')
|
49
|
+
|
50
|
+
assert_equal 'cool-precious-ruby', tag.name
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'support/webmock'
|
3
|
+
require 'ruby_pocket/web_page'
|
4
|
+
|
5
|
+
module RubyPocket
|
6
|
+
class WebPageTest < DefaultTestCase
|
7
|
+
def test_calls_the_scrapper_with_the_url
|
8
|
+
url = 'http://www.rubytapas.com'
|
9
|
+
scrapper = scrapper_mock(url)
|
10
|
+
WebPage.for(url, scrapper)
|
11
|
+
|
12
|
+
assert scrapper.verify
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_fetches_the_title_from_the_web_page
|
16
|
+
url = 'http://www.rubytapas.com'
|
17
|
+
scrapper = scrapper_mock(url, 'Ruby Tapas')
|
18
|
+
web_page = WebPage.for(url, scrapper)
|
19
|
+
|
20
|
+
assert_equal 'Ruby Tapas', web_page.title
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_throws_http_error_when_url_not_found
|
24
|
+
url = 'http://google.com/does_not_exist'
|
25
|
+
stub_request(:any, url).to_return status: 404
|
26
|
+
|
27
|
+
error = assert_raises(HttpError) do
|
28
|
+
WebPage.for url
|
29
|
+
end
|
30
|
+
|
31
|
+
assert_match %r(404), error.message
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_throws_http_error_when_internal_server_error
|
35
|
+
url = 'http://throwmea500.com'
|
36
|
+
stub_request(:any, url).to_return status: 500
|
37
|
+
|
38
|
+
error = assert_raises(HttpError) do
|
39
|
+
WebPage.for url
|
40
|
+
end
|
41
|
+
|
42
|
+
assert_match %r(500), error.message
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_throws_http_error_url_does_not_exist
|
46
|
+
url = 'http://inexistenturl.com'
|
47
|
+
stub_request(:any, url).to_raise SocketError
|
48
|
+
|
49
|
+
error = assert_raises(HttpError) do
|
50
|
+
WebPage.for url
|
51
|
+
end
|
52
|
+
|
53
|
+
assert_match %r(socket error), error.message
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def scrapper_mock(url, title = nil)
|
59
|
+
title ||= 'Ruby Tapas'
|
60
|
+
|
61
|
+
page = OpenStruct.new(title: title)
|
62
|
+
|
63
|
+
scrapper = Minitest::Mock.new
|
64
|
+
scrapper.expect :get, page, [url]
|
65
|
+
scrapper
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require_relative 'webmock'
|
2
|
+
|
3
|
+
class AddFeatureMocker
|
4
|
+
include WebMock::API
|
5
|
+
|
6
|
+
def run
|
7
|
+
return unless url && page_title
|
8
|
+
|
9
|
+
stub_request(:any, url).to_return(
|
10
|
+
body: html_body,
|
11
|
+
status: 200,
|
12
|
+
headers: { 'Content-Type': 'text/html' }
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def url
|
17
|
+
ENV['WEB_MOCK_URL']
|
18
|
+
end
|
19
|
+
|
20
|
+
def page_title
|
21
|
+
ENV['WEB_MOCK_PAGE_TITLE']
|
22
|
+
end
|
23
|
+
|
24
|
+
def html_body
|
25
|
+
<<-HTML
|
26
|
+
<!DOCTYPE html>
|
27
|
+
<html>
|
28
|
+
<head>
|
29
|
+
<title>#{page_title}</title>
|
30
|
+
</head>
|
31
|
+
|
32
|
+
<body>
|
33
|
+
</body>
|
34
|
+
</html>
|
35
|
+
HTML
|
36
|
+
end
|
37
|
+
end
|