storexplore 0.0.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
- 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
|
+
|