storexplore 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data/.gitignore +26 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Guardfile +9 -0
- data/LICENSE +165 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/lib/storexplore/api.rb +63 -0
- data/lib/storexplore/api_builder.rb +68 -0
- data/lib/storexplore/array_utils.rb +36 -0
- data/lib/storexplore/browsing_error.rb +26 -0
- data/lib/storexplore/digger.rb +35 -0
- data/lib/storexplore/hash_utils.rb +56 -0
- data/lib/storexplore/null_digger.rb +30 -0
- data/lib/storexplore/testing/api_shared_examples.rb +140 -0
- data/lib/storexplore/testing/configuration.rb +56 -0
- data/lib/storexplore/testing/dummy_data.rb +67 -0
- data/lib/storexplore/testing/dummy_store.rb +195 -0
- data/lib/storexplore/testing/dummy_store_api.rb +54 -0
- data/lib/storexplore/testing/dummy_store_constants.rb +31 -0
- data/lib/storexplore/testing/dummy_store_generator.rb +65 -0
- data/lib/storexplore/testing/matchers/have_unique_matcher.rb +74 -0
- data/lib/storexplore/testing/matchers/mostly_matcher.rb +45 -0
- data/lib/storexplore/testing.rb +30 -0
- data/lib/storexplore/uri_utils.rb +38 -0
- data/lib/storexplore/version.rb +24 -0
- data/lib/storexplore/walker.rb +84 -0
- data/lib/storexplore/walker_page.rb +142 -0
- data/lib/storexplore/walker_page_error.rb +25 -0
- data/lib/storexplore.rb +34 -0
- data/spec/lib/storexplore/api_builder_spec.rb +99 -0
- data/spec/lib/storexplore/api_spec.rb +44 -0
- data/spec/lib/storexplore/digger_spec.rb +53 -0
- data/spec/lib/storexplore/store_walker_page_spec_fixture.html +21 -0
- data/spec/lib/storexplore/testing/dummy_store_api_spec.rb +120 -0
- data/spec/lib/storexplore/uri_utils_spec.rb +51 -0
- data/spec/lib/storexplore/walker_page_spec.rb +120 -0
- data/spec/lib/storexplore/walker_spec.rb +97 -0
- data/spec/spec_helper.rb +28 -0
- data/storexplore.gemspec +27 -0
- data.tar.gz.sig +0 -0
- metadata +187 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# api_shared_examples.rb
|
4
|
+
#
|
5
|
+
# Copyright (c) 2010, 2011, 2012, 2013 by Philippe Bourgau. All rights reserved.
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License as published by the Free Software Foundation; either
|
10
|
+
# version 3.0 of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This library is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public
|
18
|
+
# License along with this library; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
20
|
+
# MA 02110-1301 USA
|
21
|
+
|
22
|
+
module Storexplore
|
23
|
+
module Testing
|
24
|
+
|
25
|
+
module ApiSpecMacros
|
26
|
+
|
27
|
+
def self.included(base)
|
28
|
+
base.send :extend, ClassMethods
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
|
33
|
+
def it_should_behave_like_any_store_items_api
|
34
|
+
|
35
|
+
before :all do
|
36
|
+
@range = 0..1
|
37
|
+
|
38
|
+
generate_store
|
39
|
+
explore_store
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should have many item categories" do
|
43
|
+
# TODO remove .first(3) once rspec handles lazy enums
|
44
|
+
expect(@store.categories.first(3)).to have_at_least(3).items
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should have many items" do
|
48
|
+
expect(sample_items).to have_at_least(3).items
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should have item categories with different names" do
|
52
|
+
categories_attributes = sample_categories.map { |cat| cat.attributes }
|
53
|
+
expect(categories_attributes).to mostly have_unique(:name).in(categories_attributes)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should have items with different names" do
|
57
|
+
expect(sample_items_attributes).to mostly have_unique(:name).in(sample_items_attributes)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should have parseable item category attributes" do
|
61
|
+
expect(parseable_categories_attributes).to mostly have_key(:name)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should have some valid item attributes" do
|
65
|
+
expect(sample_items_attributes).not_to be_empty
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should have items with a price" do
|
69
|
+
expect(sample_items_attributes).to all_ { have_key(:price) }
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should mostly have items with an image" do
|
73
|
+
expect(sample_items_attributes).to mostly have_key(:image)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should mostly have items with a brand" do
|
77
|
+
expect(sample_items_attributes).to mostly have_key(:brand)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should have items with unique remote id" do
|
81
|
+
expect(sample_items_attributes).to all_ { have_unique(:remote_id).in(sample_items_attributes) }
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should have items with unique uris" do
|
85
|
+
expect(valid_sample_items).to mostly have_unique(:uri).in(valid_sample_items)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def generate_store
|
91
|
+
# by default, the store already exists
|
92
|
+
end
|
93
|
+
|
94
|
+
def explore_store
|
95
|
+
@sample_categories = dig([@store], :categories)
|
96
|
+
@all_sample_categories, @sample_items = dig_deep(@sample_categories)
|
97
|
+
@valid_sample_items = valid_items(@sample_items)
|
98
|
+
@sample_items_attributes = (valid_sample_items.map &:attributes).uniq
|
99
|
+
@parseable_categories_attributes = all_sample_categories.map do |category|
|
100
|
+
category.attributes rescue {}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
attr_accessor :sample_categories, :all_sample_categories, :sample_items, :valid_sample_items, :sample_items_attributes, :parseable_categories_attributes
|
105
|
+
|
106
|
+
def dig(categories, message)
|
107
|
+
categories.map { |cat| cat.send(message).to_a[@range] }.flatten
|
108
|
+
end
|
109
|
+
|
110
|
+
def dig_deep(categories)
|
111
|
+
all_categories = [categories]
|
112
|
+
|
113
|
+
while (items = dig(categories, :items)).empty?
|
114
|
+
categories = dig(categories, :categories)
|
115
|
+
all_categories << categories
|
116
|
+
end
|
117
|
+
|
118
|
+
[all_categories.flatten, items]
|
119
|
+
end
|
120
|
+
|
121
|
+
def valid_items(items)
|
122
|
+
result = []
|
123
|
+
sample_items.each do |item|
|
124
|
+
begin
|
125
|
+
item.attributes
|
126
|
+
result.push(item)
|
127
|
+
rescue BrowsingError => e
|
128
|
+
Testing.logger.debug e.message
|
129
|
+
end
|
130
|
+
end
|
131
|
+
result
|
132
|
+
end
|
133
|
+
|
134
|
+
def logger
|
135
|
+
Testing.config.logger
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# configuration.rb
|
4
|
+
#
|
5
|
+
# Copyright (c) 2010, 2011, 2012, 2013 by Philippe Bourgau. All rights reserved.
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License as published by the Free Software Foundation; either
|
10
|
+
# version 3.0 of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This library is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public
|
18
|
+
# License along with this library; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
20
|
+
# MA 02110-1301 USA
|
21
|
+
|
22
|
+
require 'logger'
|
23
|
+
|
24
|
+
module Storexplore
|
25
|
+
module Testing
|
26
|
+
|
27
|
+
def self.config
|
28
|
+
@config ||= Configuration.new
|
29
|
+
yield @config if block_given?
|
30
|
+
@config
|
31
|
+
end
|
32
|
+
|
33
|
+
class Configuration
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
@logger = Logger.new(STDOUT)
|
37
|
+
@logger.level = Logger::INFO
|
38
|
+
end
|
39
|
+
|
40
|
+
# Generation directory where the dummy stores will be generated.
|
41
|
+
# A sub folder with name DummyStore::NAME will be created there to hold all generated dummy stores,
|
42
|
+
# the content of this directory will be deleted when the DummyStore.wipeout method is called
|
43
|
+
def dummy_store_generation_dir
|
44
|
+
raise StandardError.new('You need to configure a dummy store generation directory with Storexplore::Testing.config.dummy_store_generation_dir=') if @generation_dir.nil?
|
45
|
+
@generation_dir
|
46
|
+
end
|
47
|
+
def dummy_store_generation_dir=(generation_dir)
|
48
|
+
@generation_dir = generation_dir
|
49
|
+
end
|
50
|
+
|
51
|
+
# Logger for custom test messages
|
52
|
+
attr_accessor :logger
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# dummy_data.rb
|
4
|
+
#
|
5
|
+
# Copyright (c) 2012, 2013 by Philippe Bourgau. All rights reserved.
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License as published by the Free Software Foundation; either
|
10
|
+
# version 3.0 of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This library is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public
|
18
|
+
# License along with this library; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
20
|
+
# MA 02110-1301 USA
|
21
|
+
|
22
|
+
module Storexplore
|
23
|
+
module Testing
|
24
|
+
|
25
|
+
class DummyData
|
26
|
+
|
27
|
+
def self.name(kind)
|
28
|
+
"#{kind.capitalize}-#{new_int}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.attributes(name, options)
|
32
|
+
{
|
33
|
+
name: name,
|
34
|
+
brand: brand(name),
|
35
|
+
image: image(name),
|
36
|
+
remote_id: remote_id,
|
37
|
+
price: price(name)
|
38
|
+
}.merge(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def self.brand(name)
|
44
|
+
"#{name} Inc."
|
45
|
+
end
|
46
|
+
def self.image(name)
|
47
|
+
"http://www.photofabric.com/#{name}"
|
48
|
+
end
|
49
|
+
def self.remote_id
|
50
|
+
new_int.to_s
|
51
|
+
end
|
52
|
+
def self.price(name)
|
53
|
+
hash = name.hash.abs
|
54
|
+
digits = Math.log(hash, 10).round
|
55
|
+
(hash/10.0**(digits-2)).round(2)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.new_int
|
59
|
+
@last_int ||= 0
|
60
|
+
result = @last_int
|
61
|
+
@last_int = @last_int + 1
|
62
|
+
result
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# dummy_store.rb
|
4
|
+
#
|
5
|
+
# Copyright (c) 2012, 2013 by Philippe Bourgau. All rights reserved.
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License as published by the Free Software Foundation; either
|
10
|
+
# version 3.0 of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This library is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public
|
18
|
+
# License along with this library; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
20
|
+
# MA 02110-1301 USA
|
21
|
+
|
22
|
+
require "fileutils"
|
23
|
+
|
24
|
+
module Storexplore
|
25
|
+
module Testing
|
26
|
+
|
27
|
+
class DummyStore
|
28
|
+
|
29
|
+
def self.open(store_name)
|
30
|
+
new(root_path(store_name), store_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.uri(store_name)
|
34
|
+
"file://#{root_path(store_name)}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.wipe_out
|
38
|
+
FileUtils.rm_rf(root_dir)
|
39
|
+
end
|
40
|
+
def self.wipe_out_store(store_name)
|
41
|
+
FileUtils.rm_rf(root_path(store_name))
|
42
|
+
end
|
43
|
+
|
44
|
+
def uri
|
45
|
+
"file://#{@path}"
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :name
|
49
|
+
|
50
|
+
def categories
|
51
|
+
_name, categories, _items, _attributes = read
|
52
|
+
categories.map do |category_name|
|
53
|
+
DummyStore.new("#{absolute_category_dir(category_name)}/index.html", category_name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
def category(category_name)
|
57
|
+
short_category_name = short_name(category_name)
|
58
|
+
add([short_category_name], [], {})
|
59
|
+
DummyStore.new("#{absolute_category_dir(short_category_name)}/index.html", category_name)
|
60
|
+
end
|
61
|
+
def remove_category(category_name)
|
62
|
+
short_category_name = short_name(category_name)
|
63
|
+
remove([short_category_name], [], [])
|
64
|
+
FileUtils.rm_rf(absolute_category_dir(short_category_name))
|
65
|
+
end
|
66
|
+
|
67
|
+
def items
|
68
|
+
_name, _categories, items, _attributes = read
|
69
|
+
items.map do |item_name|
|
70
|
+
DummyStore.new(absolute_item_file(item_name), item_name)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
def item(item_name)
|
74
|
+
short_item_name = short_name(item_name)
|
75
|
+
add([], [short_item_name], {})
|
76
|
+
DummyStore.new(absolute_item_file(short_item_name), item_name)
|
77
|
+
end
|
78
|
+
def remove_item(item_name)
|
79
|
+
short_item_name = short_name(item_name)
|
80
|
+
remove([], [short_item_name], [])
|
81
|
+
FileUtils.rm_rf(absolute_item_file(short_item_name))
|
82
|
+
end
|
83
|
+
|
84
|
+
def attributes(*args)
|
85
|
+
return add_attributes(args[0]) if args.size == 1
|
86
|
+
|
87
|
+
_name, _categories, _items, attributes = read
|
88
|
+
HashUtils.internalize_keys(attributes)
|
89
|
+
end
|
90
|
+
def add_attributes(values)
|
91
|
+
add([], [], HashUtils.stringify_keys(values))
|
92
|
+
end
|
93
|
+
def remove_attributes(*attribute_names)
|
94
|
+
remove([], [], ArrayUtils.stringify(attribute_names))
|
95
|
+
end
|
96
|
+
|
97
|
+
def generate(count = 1)
|
98
|
+
DummyStoreGenerator.new([self], count)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def self.root_dir
|
104
|
+
File.join(Testing.config.dummy_store_generation_dir, DummyStoreConstants::NAME)
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.root_path(store_name)
|
108
|
+
"#{root_dir}/#{store_name}/index.html"
|
109
|
+
end
|
110
|
+
|
111
|
+
def initialize(path, name)
|
112
|
+
@path = path
|
113
|
+
@name = name
|
114
|
+
if !File.exists?(path)
|
115
|
+
write(name, [], [], {})
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def short_name(full_name)
|
120
|
+
full_name[0..20]
|
121
|
+
end
|
122
|
+
|
123
|
+
def absolute_category_dir(category_name)
|
124
|
+
"#{File.dirname(@path)}/#{relative_category_dir(category_name)}"
|
125
|
+
end
|
126
|
+
def relative_category_dir(category_name)
|
127
|
+
category_name
|
128
|
+
end
|
129
|
+
|
130
|
+
def absolute_item_file(item_name)
|
131
|
+
"#{File.dirname(@path)}/#{relative_item_file(item_name)}"
|
132
|
+
end
|
133
|
+
def relative_item_file(item_name)
|
134
|
+
"#{item_name}.html"
|
135
|
+
end
|
136
|
+
|
137
|
+
def add(extra_categories, extra_items, extra_attributes)
|
138
|
+
name, categories, items, attributes = read
|
139
|
+
|
140
|
+
if !ArrayUtils.contains?(categories, extra_categories) || !ArrayUtils.contains?(items, extra_items) || !HashUtils.contains?(attributes,extra_attributes)
|
141
|
+
write(name, categories + extra_categories, items + extra_items, attributes.merge(extra_attributes))
|
142
|
+
end
|
143
|
+
end
|
144
|
+
def remove(wrong_categories, wrong_items, wrong_attributes)
|
145
|
+
name, categories, items, attributes = read
|
146
|
+
write(name, categories - wrong_categories, items - wrong_items, HashUtils.without(attributes,wrong_attributes))
|
147
|
+
end
|
148
|
+
|
149
|
+
def write(name, categories, items, attributes)
|
150
|
+
FileUtils.mkdir_p(File.dirname(@path))
|
151
|
+
IO.write(@path, content(name, categories, items, attributes))
|
152
|
+
end
|
153
|
+
|
154
|
+
def content(name, categories, items, attributes)
|
155
|
+
(["<!DOCTYPE html>",
|
156
|
+
"<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" /></head>",
|
157
|
+
"<body><h1>#{name}</h1><div id=\"categories\"><h2>Categories</h2><ul>"] +
|
158
|
+
categories.map {|cat| "<li><a class=\"category\" href=\"#{relative_category_dir(cat)}/index.html\">#{cat}</a></li>" } +
|
159
|
+
['</ul></div><div id="items"><h2>Items</h2><ul>']+
|
160
|
+
items.map {|item| "<li><a class=\"item\" href=\"#{relative_item_file(item)}\">#{item}</a></li>" } +
|
161
|
+
['</ul></div><div id="attributes"><h2>Attributes</h2><ul>']+
|
162
|
+
attributes.map {|key, value| "<li><span id=\"#{key}\">#{value}</span></li>" } +
|
163
|
+
["</ul></div></body></html>"]).join("\n")
|
164
|
+
end
|
165
|
+
|
166
|
+
def read
|
167
|
+
parse(IO.readlines(@path))
|
168
|
+
end
|
169
|
+
|
170
|
+
def parse(lines)
|
171
|
+
name = ""
|
172
|
+
categories = []
|
173
|
+
items = []
|
174
|
+
attributes = {}
|
175
|
+
lines.each do |line|
|
176
|
+
name_match = /<h1>([^<]+)<\/h1>/.match(line)
|
177
|
+
sub_match = /<li><a class=\"([^\"]+)\" href=\"[^\"]+.html\">([^<]+)<\/a><\/li>/.match(line)
|
178
|
+
attr_match = /<li><span id=\"([^\"]+)\">([^<]+)<\/span><\/li>/.match(line)
|
179
|
+
|
180
|
+
if !!name_match
|
181
|
+
name = name_match[1]
|
182
|
+
elsif !!sub_match
|
183
|
+
case sub_match[1]
|
184
|
+
when "category" then categories << sub_match[2]
|
185
|
+
when "item" then items << sub_match[2]
|
186
|
+
end
|
187
|
+
elsif !!attr_match
|
188
|
+
attributes[attr_match[1]] = attr_match[2]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
[name, categories, items, attributes]
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# dummy_store_api.rb
|
4
|
+
#
|
5
|
+
# Copyright (c) 2012, 2013 by Philippe Bourgau. All rights reserved.
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License as published by the Free Software Foundation; either
|
10
|
+
# version 3.0 of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This library is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public
|
18
|
+
# License along with this library; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
20
|
+
# MA 02110-1301 USA
|
21
|
+
|
22
|
+
require_relative 'dummy_store_constants'
|
23
|
+
|
24
|
+
module Storexplore
|
25
|
+
module Testing
|
26
|
+
|
27
|
+
Storexplore::define_api DummyStoreConstants::NAME do
|
28
|
+
|
29
|
+
categories 'a.category' do
|
30
|
+
attributes do
|
31
|
+
{ :name => page.get_one("h1").content }
|
32
|
+
end
|
33
|
+
|
34
|
+
categories 'a.category' do
|
35
|
+
attributes do
|
36
|
+
{ :name => page.get_one("h1").content }
|
37
|
+
end
|
38
|
+
|
39
|
+
items 'a.item' do
|
40
|
+
attributes do
|
41
|
+
{
|
42
|
+
:name => page.get_one('h1').content,
|
43
|
+
:brand => page.get_one('#brand').content,
|
44
|
+
:price => page.get_one('#price').content.to_f,
|
45
|
+
:image => page.get_one('#image').content,
|
46
|
+
:remote_id => page.get_one('#remote_id').content
|
47
|
+
}
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# dummy_store_constants.rb
|
4
|
+
#
|
5
|
+
# Copyright (c) 2013 by Philippe Bourgau. All rights reserved.
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License as published by the Free Software Foundation; either
|
10
|
+
# version 3.0 of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This library is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public
|
18
|
+
# License along with this library; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
20
|
+
# MA 02110-1301 USA
|
21
|
+
|
22
|
+
module Storexplore
|
23
|
+
module Testing
|
24
|
+
|
25
|
+
class DummyStoreConstants
|
26
|
+
|
27
|
+
NAME = 'dummy-store'
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# dummy_store_generator.rb
|
4
|
+
#
|
5
|
+
# Copyright (c) 2012, 2013 by Philippe Bourgau. All rights reserved.
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License as published by the Free Software Foundation; either
|
10
|
+
# version 3.0 of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This library is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public
|
18
|
+
# License along with this library; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
20
|
+
# MA 02110-1301 USA
|
21
|
+
|
22
|
+
module Storexplore
|
23
|
+
module Testing
|
24
|
+
|
25
|
+
class DummyStoreGenerator
|
26
|
+
def initialize(pages, count = 1)
|
27
|
+
@pages = pages
|
28
|
+
@count = count
|
29
|
+
end
|
30
|
+
|
31
|
+
def and(count)
|
32
|
+
@count = count
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def categories
|
37
|
+
dispatch(:category)
|
38
|
+
end
|
39
|
+
alias_method :category, :categories
|
40
|
+
|
41
|
+
def items
|
42
|
+
dispatch(:item).attributes
|
43
|
+
end
|
44
|
+
alias_method :item, :items
|
45
|
+
|
46
|
+
def attributes(options = {})
|
47
|
+
@pages.map do |page|
|
48
|
+
attributes = DummyData.attributes(page.name, options)
|
49
|
+
page.attributes(HashUtils.without(attributes, [:name]))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def dispatch(message)
|
56
|
+
sub_pages = @pages.map do |page|
|
57
|
+
@count.times.map do
|
58
|
+
page.send(message, DummyData.name(message))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
DummyStoreGenerator.new(sub_pages.flatten)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
#
|
3
|
+
# have_unique_matcher.rb
|
4
|
+
#
|
5
|
+
# Copyright (c) 2010, 2011, 2013 by Philippe Bourgau. All rights reserved.
|
6
|
+
#
|
7
|
+
# This library is free software; you can redistribute it and/or
|
8
|
+
# modify it under the terms of the GNU Lesser General Public
|
9
|
+
# License as published by the Free Software Foundation; either
|
10
|
+
# version 3.0 of the License, or (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This library is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
15
|
+
# Lesser General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU Lesser General Public
|
18
|
+
# License along with this library; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
20
|
+
# MA 02110-1301 USA
|
21
|
+
|
22
|
+
|
23
|
+
# Matcher to verify that a hash's key is unique in a collection of other hashes
|
24
|
+
# a full class is required to implement the in method
|
25
|
+
# Use like: expect(hash).to have_unique(:id).in(hashes)
|
26
|
+
class HaveUnique
|
27
|
+
|
28
|
+
def initialize(key)
|
29
|
+
@key = key
|
30
|
+
end
|
31
|
+
|
32
|
+
def in(collection)
|
33
|
+
@collection = collection
|
34
|
+
@index = Hash.new(0)
|
35
|
+
collection.each do |item|
|
36
|
+
@index[value(item)] += 1
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def matches?(actual)
|
42
|
+
@actual = actual
|
43
|
+
@index[value(actual)] == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
def failure_message_for_should
|
47
|
+
"expected #{value_expression(actual)} (=#{value(actual)}) to be unique in #{@collection}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def description
|
51
|
+
"expected an hash or object with a unique #{@key} in #{@collection}"
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def value(actual)
|
56
|
+
if actual.instance_of?(Hash)
|
57
|
+
actual[@key]
|
58
|
+
else
|
59
|
+
actual.send(@key)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
def value_expression(actual)
|
63
|
+
if actual.instance_of?(Hash)
|
64
|
+
"#{actual}[#{@key}]"
|
65
|
+
else
|
66
|
+
"#{actual}.#{@key}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def have_unique(key)
|
72
|
+
HaveUnique.new(key)
|
73
|
+
end
|
74
|
+
|