ruby_pocket 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|