specs_watcher 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.
@@ -0,0 +1,169 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: http://www.specsonline.com/cgi-bin/search?Sortby=Name&inclass=Liquors&keyword=yamazaki&noask=noask&origin=&pageid=search&pricefrom=&pricethru=&region=&showmax=1000&size=&subclass=&submit=Click%20to%20Show%20More&webclass=Liquors
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ User-Agent:
11
+ - Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_0) AppleWebKit/537.36 (KHTML,
12
+ like Gecko) Chrome/38.0.2125.122 Safari/537.36
13
+ Dnt:
14
+ - '1'
15
+ Accept-Language:
16
+ - en-US,en;q=0.8
17
+ Accept:
18
+ - text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
19
+ Referer:
20
+ - http://www.specsonline.com/cgi-bin/search?keyword=&inclass=Liquors&webclass=Liquors&subclass=130&origin=&region=&size=&Sortby=Name&pricefrom=&pricethru=&
21
+ Connection:
22
+ - keep-alive
23
+ response:
24
+ status:
25
+ code: 200
26
+ message: OK
27
+ headers:
28
+ Date:
29
+ - Sun, 23 Nov 2014 18:25:19 GMT
30
+ Content-Type:
31
+ - text/html
32
+ Transfer-Encoding:
33
+ - chunked
34
+ Connection:
35
+ - keep-alive
36
+ Keep-Alive:
37
+ - timeout=5
38
+ Vary:
39
+ - Accept-Encoding
40
+ Server:
41
+ - ''
42
+ Content-Encoding:
43
+ - gzip
44
+ body:
45
+ encoding: UTF-8
46
+ string: "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 3.0//EN\" \"html.dtd\">\n<HTML>\n<HEAD>\n<META
47
+ HTTP-EQUIV=\"Set-Cookie\" CONTENT=\"specsonline=11416767336204.128.208.187;
48
+ expires=Saturday, 31-Dec-50 23:59:59 GMT; path=/\">\n<TITLE>cart </TITLE>\n<script
49
+ language=\"JavaScript\">\nvar cartwin;\n\nfunction ordwin( form ) \n{\n\tvar
50
+ url = form;\n\n\tsmwin = window.open(url,'orderhist','width=600,height=640,alwaysRaised=yes,directories=no,location=no,menubar=no,status=no,scrollbars=yes,toolbar=no,resizable=no,left=350,top=150,screenX=350,screenY=150');\n\tsmwin.focus();\n\treturn
51
+ false;\n}\n\nfunction smallwin( form ) \n{\n\tvar url = '/cgi-bin/pleasewait?Form='+form\n\n\tsmwin
52
+ = window.open(url,'SpecsOnline','width=500,height=440,alwaysRaised=yes,directories=no,location=no,menubar=no,status=no,scrollbars=yes,toolbar=no,resizable=no,left=150,top=150,screenX=150,screenY=150');\n\tsmwin.focus();\n\treturn
53
+ false;\n}\n\nfunction rladdcart( upc ) \n{\n\tvar url = \"/cgi-bin/rlshowcart?upc=\"+upc\n\tvar
54
+ doreg = ''\n/*\n\tdoreg = loadreg();\n\tif ( doreg == 'register' )\n url
55
+ = '/cgi-bin/cartreg?upc='+upc;\t\n*/\n\tcartwin = window.open(url,\"ShoppingCart\",'width=600,height=480,alwaysRaised=yes,directories=no,location=no,menubar=no,status=no,scrollbars=yes,toolbar=no,resizable=no,left=150,top=150,screenX=150,screenY=150');\n\tcartwin.focus();\n\treturn
56
+ false;\n}\nfunction addcart( upc ) \n{\n\tvar url = \"/cgi-bin/showcart?upc=\"+upc\n\tvar
57
+ doreg = ''\n/*\n\tdoreg = loadreg();\n\tif ( doreg == 'register' )\n url
58
+ = '/cgi-bin/cartreg?upc='+upc;\t\n*/\n\tcartwin = window.open(url,'ShoppingCart','width=600,height=480,alwaysRaised=yes,directories=no,location=no,menubar=no,status=no,scrollbars=yes,toolbar=no,resizable=no,left=150,top=150,screenX=150,screenY=150');\n\tcartwin.focus();\n\treturn
59
+ false;\n}\n\nfunction showsyn ( upc ) \n{\n\tvar url = '/cgi-bin/pleasewait?Form=showsyn&upc='+upc\n\tsynwin
60
+ = window.open(url,'Synopsis','width=500,height=200,directories=no,location=no,menubar=no,status=no,scrollbars=yes,toolbar=no,resizable=no,left=150,top=150,screenX=150,screenY=150');\n\tsynwin.focus();\n\treturn
61
+ false;\n}\n\nfunction showpic ( upc ) \n{\n\tvar url = '/cgi-bin/pleasewait?Form=showpic&upc='+upc\n\tpicwin
62
+ = window.open(url,'Picture','title=no,width=200,height=200,directories=no,location=no,menubar=no,status=no,scrollbars=no,toolbar=no,resizable=yes,left=250,top=250,screenX=250,screenY=250');\n\tpicwin.focus();\n\treturn
63
+ false;\n}\n\nfunction showpic2( upc )\n{\n\tvar x, y;\n\tvar divname = 'PicDiv';\n\tvar
64
+ imgsrc1 = '/prodpics/'+upc+'.jpg';\n\tvar imgsrc2 = '/images/close.jpg';\n/*\n\tx
65
+ = 200;\n\ty = 0;\n\tdocument.getElementById(divname).style.left = x;\n\tdocument.getElementById(divname).style.top
66
+ = y;\n*/\n\tdocument.getElementById(divname).innerHTML = \"<img id=PicImage
67
+ src=\\\"\" + imgsrc1 + \"\\\" onclick=hidepic() >\" + \"<img id=PicClose src=\\\"\"
68
+ + imgsrc2 + \"\\\" onclick=hidepic() >\";\n\tdocument.getElementById(divname).style.display=
69
+ \"block\";\n\treturn false;\n}\n\nfunction hidekey()\n{\n\tvar divname = 'KeyDiv';\n\tdocument.getElementById(divname).innerHTML
70
+ = \"\";\n\tdocument.getElementById(divname).style.display = \"none\";\n\treturn
71
+ false;\n}\n\nfunction hidepic()\n{\n\tvar divname = 'PicDiv';\n\tdocument.getElementById(divname).innerHTML
72
+ = \"\";\n\tdocument.getElementById(divname).style.display = \"none\";\n\treturn
73
+ false;\n}\n\nfunction showkey ( upc ) \n{\n\tvar url = '/cgi-bin/pleasewait?Form=showkey&upc='+upc\n\tskeywin
74
+ = window.open(url,'KeyInfo','width=500,height=200,directories=no,location=no,menubar=no,status=no,scrollbars=yes,toolbar=no,resizable=no,left=150,top=250,screenX=150,screenY=250');\n\tskeywin.focus();\n\treturn
75
+ false;\n}\n\nfunction showkey2( upc )\n{\nvar url = '/cgi-bin/pleasewait?Form=showkey&upc='+upc\n\tvar
76
+ x, y;\n\tvar divname = 'KeyDiv';\n\tvar imgsrc1 = '/prodpics/'+upc+'.jpg';\n\tvar
77
+ imgsrc2 = '/images/close.jpg';\n\tx = 125;\n\ty = 100;\n\tdocument.getElementById(divname).style.left
78
+ = x;\n\tdocument.getElementById(divname).style.top = y;\n\tdocument.getElementById(divname).innerHTML
79
+ = \"<iframe width=500 height=200 id=KeyURL src=\\\"\"+url+\"\\\" onclick=hidekey()
80
+ >\";\n\tdocument.getElementById(divname).style.display= \"block\";\n\treturn
81
+ false;\n}\n\t\nfunction showavail ( upc ) \n{\n\tvar url = '/cgi-bin/pleasewait?Form=showavail&upc='+upc\n\tskeywin
82
+ = window.open(url,'KeyInfo','width=540,height=500,directories=no,location=no,menubar=no,status=no,scrollbars=yes,toolbar=no,resizable=no,left=250,top=150,screenX=250,screenY=150');\n\tskeywin.focus();\n\treturn
83
+ false;\n}\n\nfunction showavail2 ( upc ) \n{\n\tvar url = '/cgi-bin/showavail&upc='+upc\n\tvar
84
+ x, y;\n\tvar divname = 'KeyDiv';\n\tx = 125;\n\ty = 100;\n\tdocument.getElementById(divname).style.left
85
+ = x;\n\tdocument.getElementById(divname).style.top = y;\n\tdocument.getElementById(divname).innerHTML
86
+ = \"<iframe width=500 height=400 id=AvlURL src=\\\"\"+url+\"\\\" onmouseleave=hidekey()
87
+ >\";\n\tdocument.getElementById(divname).style.display= \"block\";\n\treturn
88
+ false;\n}\n\n</script>\n</HEAD>\n<BODY BGCOLOR=\"f0f0f0\" FBACKGROUND=\"/gifs/graybunny.gif\">\n<BODY
89
+ BGCOLOR=\"ffffff\" >\n\n <script>\n <!--\n // Copyright (c) 1996-1997 Tomer
90
+ Shiran. All rights reserved.\n // Permission given to use the script provided
91
+ that this notice remains as is.\n // Additional scripts can be found at http://www.geocities.com/~yehuda/\n\n
92
+ // Boolean variable specified if alert should be displayed if cookie exceeds
93
+ 4KB\n var caution = false\n\n // name - name of the cookie\n // value - value
94
+ of the cookie\n // [expires] - expiration date of the cookie (defaults to
95
+ end of current session)\n // [path] - path for which the cookie is valid (defaults
96
+ to path of calling document)\n // [domain] - domain for which the cookie is
97
+ valid (defaults to domain of calling document)\n // [secure] - Boolean value
98
+ indicating if the cookie transmission requires a secure transmission\n //
99
+ * an argument defaults when it is assigned null as a placeholder\n // * a
100
+ null placeholder is not required for trailing omitted arguments\n // * expires.toGMTString()\n
101
+ function setCookie(name, value, expires, path, domain, secure) {\n var curCookie
102
+ = name + \"=\" + escape(value) +\n// *; expires=Saturday, 31-Dec-50 23:59:59
103
+ GMT; path=/\"\n ((expires) ? \"; expires=\" + expires.toGMTString() : \"\")
104
+ +\n ((path) ? \"; path=\" + path : \"/\") +\n ((domain) ? \"; domain=\" +
105
+ domain : \"\") +\n ((secure) ? \"; secure\" : \"0\")\n //*if (!caution ||
106
+ (name + \"=\" + escape(value)).length <= 4000)\n document.cookie = curCookie\n
107
+ //*else\n //*if (confirm(\"Cookie exceeds 4KB and will be cut!\"))\n //*document.cookie
108
+ = curCookie\n }\n\n // \"; expires=Thu, 02 Aug 2008 19:56:50 GMT\" +\n //
109
+ name - name of the desired cookie\n // * return string containing value of
110
+ specified cookie or null if cookie does not exist\n function getCookie(name)
111
+ {\n var prefix = name + \"=\"\n var cookieStartIndex = document.cookie.indexOf(prefix)\n
112
+ if (cookieStartIndex == -1)\n return null\n var cookieEndIndex = document.cookie.indexOf(\";\",
113
+ cookieStartIndex + prefix.length)\n if (cookieEndIndex == -1)\n cookieEndIndex
114
+ = document.cookie.length\n return unescape(document.cookie.substring(cookieStartIndex
115
+ + prefix.length, cookieEndIndex))\n }\n\n // name - name of the cookie\n //
116
+ [path] - path of the cookie (must be same as path used to create cookie)\n
117
+ // [domain] - domain of the cookie (must be same as domain used to create
118
+ cookie)\n // * path and domain default if assigned null or omitted if no explicit
119
+ argument proceeds\n function deleteCookie(name, path, domain) {\n if (getCookie(name))
120
+ {\n document.cookie = name + \"=\" + \n ((path) ? \"; path=\" + path : \"/\")
121
+ +\n ((domain) ? \"; domain=\" + domain : \"\") +\n \"; expires=Thu, 01-Jan-70
122
+ 00:00:01 GMT\"\n }\n }\n\n // date - any instance of the Date object\n //
123
+ * you should hand all instances of the Date object to this function for \"repairs\"\n
124
+ // * this function is taken from Chapter 14, \"Time and Date in JavaScript\",
125
+ in \"Learn Advanced JavaScript Programming\"\n function fixDate(date) {\n
126
+ var base = new Date(0)\n var skew = base.getTime()\n if (skew > 0)\n date.setTime(date.getTime()
127
+ - skew)\n }\n \n var now = new Date()\n fixDate(now)\n now.setTime(now.getTime()
128
+ + 31 * 24 * 60 * 60 * 1000)\n var name = getCookie(\"specsonline\")\n if (!name){\n
129
+ \ name = '1'+now.getTime()\n //* document.write( '<META HTTP-EQUIV=\"Set-Cookie\"
130
+ CONTENT=\"specsonline='+nane+' ; expires=Saturday, 31-Dec-50 23:59:59 GMT;
131
+ path=/\">' )\n //*setCookie(\"specsonline\", name, now)\n}\n //document.write(\"id:
132
+ \" + name )\n //-->\n </script>\n<div id=PicDiv>\n</div>\n<div id=KeyDiv>\n</div>\n<font
133
+ FACE=\"Arial, helvetica\" SIZE=\"+0\" COLOR=\"#000000\" >\n</ul>\n<FORM action=/cgi-bin/search
134
+ >\n<b><center> 2 items in current selection. </center></b>\n</FORM>\n<hr>\nReceive
135
+ an Additional 5% Discount for Cash Payment. Prices and Vintages subject to
136
+ change.\n<BR><font color=f00000>All Availability and Pricing is at the Smith
137
+ street location. Availability and prices may vary by location.</font>\n</CENTER>\n<p>\n<B>\n<STYLE
138
+ TYPE=\"text/css\">\n{font-family: Arial;}\ninput {font-family: Arial;font-size:
139
+ 9;}\nselect {font-family: Arial;font-size: 9;}\ntd {font-family: Arial; font-size:
140
+ 11;}\n.resize {\nwidth: 120px;\nheight : auto;\n}\n\n.resize {\nwidth: auto;\nheight
141
+ : 100px;\n}\n#KeyDiv {\ndisplay: none;\nposition: fixed;\nborder:4px solid
142
+ grey;\nbackground-color: grey;\nz-index:1;\n}\n#KeyURL {\nborder:10 solid
143
+ white;\nposition: relative;\nfloat: left;\n}\n#PicDiv {\ndisplay: none;\nposition:
144
+ fixed;\ntop:50px;\nleft:150px;\nMax-Height:400px;\nz-index:2;\n}\n#PicImage
145
+ {\nborder:24px solid white;\nposition: relative;\nMax-Height:400px;\nfloat:
146
+ left;\n}\n#PicClose {\nposition: relative;\nfloat: left;\nopacity: .5;\nleft:
147
+ -75px;\ntop: 5px;\nz-index:2;\n}\n</STYLE>\n<TABLE WIDTH=98% CELLPADDING=0
148
+ CELLSPACING=0><TR>\n<TR><TD COLSPAN=8><HR></TD></TR>\n<TR VALIGN=TOP><TD><IMG
149
+ SRC=/prodpics/NOPIC.jpg WIDTH=100 onmouseover=hidepic()></TD><TD WIDTH=50%>SUNTORY
150
+ YAMAZAKI 12YR [SCOTLAND]<br><br><CENTER><FONT style='font-family:Trebuchet
151
+ MS;font-size:10;color:b0b0b0;font-weight:bold;' ></FONT></CENTER></TD><TD
152
+ WIDTH=50 ALIGN=LEFT> 750ML</TD><TD WIDTH=60 ALIGN=LEFT>Each<br>Case [12]</TD><TD
153
+ WIDTH=40 ALIGN=RIGHT> 60.99<br> 658.74</TD></TD><TD ALIGN=RIGHT WIDTH=40
154
+ VALIGN=TOP><IMG SRC=/gifs/blank.gif></TD><TD WIDTH=80><A NAME=1 HREF=# onclick=\"return
155
+ false\"><IMG SRC=/gifs/limited.png BORDER=0></A><br><br><br><A NAME=1 HREF=#
156
+ onclick=\"showavail('008885700161');return false\"><IMG SRC=/gifs/checkavail.png
157
+ BORDER=0></A></TD></TR>\n<TR><TD COLSPAN=8><HR></TD></TR>\n<TR VALIGN=TOP><TD><IMG
158
+ SRC=/prodpics/NOPIC.jpg WIDTH=100 onmouseover=hidepic()></TD><TD WIDTH=50%>SUNTORY
159
+ YAMAZAKI 18YR 6/CS [JAPAN]<br><br><CENTER><FONT style='font-family:Trebuchet
160
+ MS;font-size:10;color:b0b0b0;font-weight:bold;' ></FONT></CENTER></TD><TD
161
+ WIDTH=50 ALIGN=LEFT> 750ML</TD><TD WIDTH=60 ALIGN=LEFT>Each<br>Case [6]</TD><TD
162
+ WIDTH=40 ALIGN=RIGHT> 216.83<br>1164.57</TD></TD><TD ALIGN=RIGHT WIDTH=40
163
+ VALIGN=TOP><IMG SRC=/gifs/blank.gif></TD><TD WIDTH=80><A NAME=2 HREF=# onclick=\"return
164
+ false\"><IMG SRC=/gifs/limited.png BORDER=0></A><br><br><br><A NAME=2 HREF=#
165
+ onclick=\"showavail('088857001623');return false\"><IMG SRC=/gifs/checkavail.png
166
+ BORDER=0></A></TD></TR>\n</TABLE>\n</B>\n</ul>\n"
167
+ http_version:
168
+ recorded_at: Sun, 23 Nov 2014 18:25:20 GMT
169
+ recorded_with: VCR 2.9.3
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpecsWatcher::Availability do
4
+ subject(:availability) { SpecsWatcher::Availability }
5
+
6
+ describe '.search' do
7
+ context 'default options' do
8
+ it 'returns a list of results with the correct attributes' do
9
+ VCR.use_cassette('availability_search_default_options') do
10
+ results = availability.search(upc: '085457000211', zip: '77008')
11
+ first_location = results[:locations].first
12
+
13
+ expect(results[:title]).to match(/smooth ambler/i)
14
+ expect(first_location[:store_name]).to_not be_empty
15
+ expect(first_location[:availability]).to_not be_empty
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,144 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpecsWatcher::CLI do
4
+ describe '#search' do
5
+ context 'without arguments' do
6
+ it 'returns the first 1000 results from the inventory' do
7
+ VCR.use_cassette('searcher_search_default_options') do
8
+ content = capture(:stdout) { SpecsWatcher::CLI.start(%w[search]) }
9
+ expect(content.lines.count).to eql(1001)
10
+ end
11
+ end
12
+ end
13
+
14
+ context "'s' alias" do
15
+ it 'returns the first 1000 results from the inventory' do
16
+ VCR.use_cassette('searcher_search_default_options') do
17
+ content = capture(:stdout) { SpecsWatcher::CLI.start(%w[s]) }
18
+ expect(content.lines.count).to eql(1001)
19
+ end
20
+ end
21
+ end
22
+
23
+ context "option :format" do
24
+ context 'default format' do
25
+ it 'returns a table' do
26
+ VCR.use_cassette('cli_search_keyword_balvenie') do
27
+ content = capture(:stdout) { SpecsWatcher::CLI.start(%w[search balvenie]) }
28
+ expect(content.lines[1]).to eql("BALVENIE MALT * 12YR SINGLE BARREL 6/CS [SCOTLAND] 72.41 750ML 386.65 Case [6] 008366487306\n")
29
+ end
30
+ end
31
+ end
32
+
33
+ context ':json format' do
34
+ it 'prints valid json' do
35
+ VCR.use_cassette('cli_search_keyword_balvenie') do
36
+ content = capture(:stdout) { SpecsWatcher::CLI.start(%w[search balvenie --format json]) }
37
+ expect(JSON.parse(content).first).to eql({
38
+ "title" => "BALVENIE MALT * 12YR SINGLE BARREL 6/CS [SCOTLAND]",
39
+ "price" => 72.41,
40
+ "size" => "750ML",
41
+ "case_price" => 386.65,
42
+ "case_size" => "Case [6]",
43
+ "description" => "",
44
+ "image" => "http://www.specsonline.com/prodpics/008366487306.jpg",
45
+ "upc" => "008366487306"
46
+ })
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ describe 'keyword' do
53
+ context 'a bogus keyword' do
54
+ it 'returns no results' do
55
+ VCR.use_cassette('cli_keyword_bogus') do
56
+ content = capture(:stdout) { SpecsWatcher::CLI.start(%w[search foobar]) }
57
+ expect(content).to match(/No Results/)
58
+ end
59
+ end
60
+ end
61
+
62
+ context "searching for 'balvenie'" do
63
+ it "returns results for 'balvenie'" do
64
+ VCR.use_cassette('cli_search_keyword_balvenie') do
65
+ content = capture(:stdout) { SpecsWatcher::CLI.start(%w[search balvenie]) }
66
+ expect(content.lines[1]).to match(/balvenie/i)
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ describe 'option :category' do
73
+ shared_examples_for 'category bourbon' do
74
+ it 'returns the full list of bourbons' do
75
+ VCR.use_cassette('searcher_search_bourbon') do
76
+ content = capture(:stdout) { SpecsWatcher::CLI.start(command) }
77
+ expect(content.lines[1]).to match(/8 feathers/i)
78
+ end
79
+ end
80
+ end
81
+
82
+ context '--category bourbon' do
83
+ let(:command) { %w[search --category bourbon] }
84
+ it_behaves_like 'category bourbon'
85
+ end
86
+
87
+ context '-c bourbon' do
88
+ let(:command) { %w[search -c bourbon] }
89
+ it_behaves_like 'category bourbon'
90
+ end
91
+
92
+ context 'non-existent category' do
93
+ it 'prints a useful error' do
94
+ content = capture(:stdout) { SpecsWatcher::CLI.start(%w[search -c bubbles]) }
95
+ expect(content.lines.first).to match(/'bubbles' is not a valid category/i)
96
+ end
97
+ end
98
+ end
99
+
100
+ describe 'option :verbose' do
101
+ shared_examples_for 'verbose' do
102
+ it 'returns additional columns' do
103
+ VCR.use_cassette('searcher_search_default_options') do
104
+ content = capture(:stdout) { SpecsWatcher::CLI.start(command) }
105
+ expect(content.lines.first).to match(/description/i)
106
+ expect(content.lines.first).to match(/image/i)
107
+ end
108
+ end
109
+ end
110
+
111
+ context '--verbose' do
112
+ let(:command) { %w[search --verbose] }
113
+ it_behaves_like 'verbose'
114
+ end
115
+
116
+ context '-v' do
117
+ let(:command) { %w[search -v] }
118
+ it_behaves_like 'verbose'
119
+ end
120
+ end
121
+ end
122
+
123
+ describe '#availability' do
124
+ shared_examples_for 'availability' do
125
+ it 'returns a list of locations' do
126
+ VCR.use_cassette('cli_availability') do
127
+ content = capture(:stdout) { SpecsWatcher::CLI.start(command) }
128
+ expect(content.lines[1]).to match(/Store name/i)
129
+ expect(content.lines[1]).to match(/Availability/i)
130
+ end
131
+ end
132
+ end
133
+
134
+ context 'availability' do
135
+ let(:command) { %w[availability 78751 085457000211] }
136
+ it_behaves_like 'availability'
137
+ end
138
+
139
+ context "'a' alias" do
140
+ let(:command) { %w[a 78751 085457000211] }
141
+ it_behaves_like 'availability'
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe SpecsWatcher::Searcher do
4
+ subject(:searcher) { SpecsWatcher::Searcher }
5
+
6
+ describe '.search' do
7
+ context 'default search' do
8
+ it 'returns the first 1000 results from the inventory' do
9
+ VCR.use_cassette('searcher_search_default_options') do
10
+ expect(searcher.search.count).to eql(1000)
11
+ end
12
+ end
13
+
14
+ it 'items have the correct attributes' do
15
+ VCR.use_cassette('searcher_search_default_options') do
16
+ results = searcher.search
17
+ first = results.first
18
+ expect(first[:title]).not_to be_empty
19
+ expect(first[:price]).to be_a Float
20
+ expect(first[:size]).not_to be_empty
21
+ expect(first[:case_price]).to be_a Float
22
+ expect(first[:case_size]).not_to be_empty
23
+ expect(first[:upc]).not_to be_empty
24
+ expect(first).not_to have_key(:description)
25
+ expect(first).not_to have_key(:image)
26
+ end
27
+ end
28
+
29
+ context 'option :verbose' do
30
+ it 'returns results with additional attributes' do
31
+ VCR.use_cassette('searcher_search_default_options') do
32
+ results = searcher.search(verbose: true)
33
+ expect(results.first).to have_key(:description)
34
+ expect(results.first).to have_key(:image)
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'searching for bourbon' do
41
+ it 'returns the full list of bourbons' do
42
+ VCR.use_cassette('searcher_search_bourbon') do
43
+ results = searcher.search(category: 'bourbon')
44
+ expect(results.first[:title]).to match(/8 feathers/i)
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'searching for "yamazaki"' do
50
+ it 'returns results matching the keyword' do
51
+ VCR.use_cassette('seracher_search_keyword_yamazaki') do
52
+ results = searcher.search(keyword: 'yamazaki')
53
+ expect(results.count).to eql(2)
54
+ results.each do |r|
55
+ expect(r[:title]).to match(/yamazaki/i)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,114 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause this
4
+ # file to always be loaded, without a need to explicitly require it in any files.
5
+ #
6
+ # Given that it is always loaded, you are encouraged to keep this file as
7
+ # light-weight as possible. Requiring heavyweight dependencies from this file
8
+ # will add to the boot time of your test suite on EVERY test run, even for an
9
+ # individual file that may not need all of that loaded. Instead, consider making
10
+ # a separate helper file that requires the additional dependencies and performs
11
+ # the additional setup, and require it from the spec files that actually need it.
12
+ #
13
+ # The `.rspec` file also contains a few flags that are not defaults but that
14
+ # users commonly want.
15
+ #
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+
18
+ require 'specs_watcher'
19
+ require 'vcr'
20
+ require 'pry'
21
+
22
+ VCR.configure do |c|
23
+ c.cassette_library_dir = 'spec/fixtures/vcr_cassettes'
24
+ c.hook_into :webmock
25
+ c.allow_http_connections_when_no_cassette = true
26
+ end
27
+
28
+ RSpec.configure do |config|
29
+ # rspec-expectations config goes here. You can use an alternate
30
+ # assertion/expectation library such as wrong or the stdlib/minitest
31
+ # assertions if you prefer.
32
+ config.expect_with :rspec do |expectations|
33
+ # This option will default to `true` in RSpec 4. It makes the `description`
34
+ # and `failure_message` of custom matchers include text for helper methods
35
+ # defined using `chain`, e.g.:
36
+ # be_bigger_than(2).and_smaller_than(4).description
37
+ # # => "be bigger than 2 and smaller than 4"
38
+ # ...rather than:
39
+ # # => "be bigger than 2"
40
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
41
+ end
42
+
43
+ # rspec-mocks config goes here. You can use an alternate test double
44
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
45
+ config.mock_with :rspec do |mocks|
46
+ # Prevents you from mocking or stubbing a method that does not exist on
47
+ # a real object. This is generally recommended, and will default to
48
+ # `true` in RSpec 4.
49
+ mocks.verify_partial_doubles = true
50
+ end
51
+
52
+ # The settings below are suggested to provide a good initial experience
53
+ # with RSpec, but feel free to customize to your heart's content.
54
+
55
+ # These two settings work together to allow you to limit a spec run
56
+ # to individual examples or groups you care about by tagging them with
57
+ # `:focus` metadata. When nothing is tagged with `:focus`, all examples
58
+ # get run.
59
+ config.filter_run :focus
60
+ config.run_all_when_everything_filtered = true
61
+
62
+ # Limits the available syntax to the non-monkey patched syntax that is recommended.
63
+ # For more details, see:
64
+ # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
65
+ # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66
+ # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching
67
+ # config.disable_monkey_patching!
68
+
69
+ # This setting enables warnings. It's recommended, but in some cases may
70
+ # be too noisy due to issues in dependencies.
71
+ # config.warnings = true
72
+
73
+ # Many RSpec users commonly either run the entire suite or an individual
74
+ # file, and it's useful to allow more verbose output when running an
75
+ # individual spec file.
76
+ if config.files_to_run.one?
77
+ # Use the documentation formatter for detailed output,
78
+ # unless a formatter has already been configured
79
+ # (e.g. via a command-line flag).
80
+ config.default_formatter = 'doc'
81
+ end
82
+
83
+ # Print the 10 slowest examples and example groups at the
84
+ # end of the spec run, to help surface which specs are running
85
+ # particularly slow.
86
+ # config.profile_examples = 10
87
+
88
+ # Run specs in random order to surface order dependencies. If you find an
89
+ # order dependency and want to debug it, you can fix the order by providing
90
+ # the seed, which is printed after each run.
91
+ # --seed 1234
92
+ config.order = :random
93
+
94
+ # Seed global randomization in this process using the `--seed` CLI option.
95
+ # Setting this allows you to use `--seed` to deterministically reproduce
96
+ # test failures related to randomization by passing the same `--seed` value
97
+ # as the one that triggered the failure.
98
+ Kernel.srand config.seed
99
+
100
+ # Capture stdout
101
+ # https://github.com/erikhuda/thor/blob/3241f2fbf1172b6182723b073fd4b390200660e9/spec/helper.rb#L51
102
+ def capture(stream)
103
+ begin
104
+ stream = stream.to_s
105
+ eval "$#{stream} = StringIO.new"
106
+ yield
107
+ result = eval("$#{stream}").string
108
+ ensure
109
+ eval("$#{stream} = #{stream.upcase}")
110
+ end
111
+
112
+ result
113
+ end
114
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'specs_watcher/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "specs_watcher"
8
+ spec.version = SpecsWatcher::VERSION
9
+ spec.authors = ["Aaron Ortbals"]
10
+ spec.email = ["aaron.ortbals@gmail.com"]
11
+ spec.summary = %q{A useful CLI for searching Spec's online inventory}
12
+ spec.homepage = "https://vault.aaronortbals.com/aortbals/specs_watcher"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency "typhoeus", "~> 0.6"
21
+ spec.add_dependency "nokogiri", "~> 1.6"
22
+ spec.add_dependency "thor", "~> 0.19"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "rspec", "~> 3.1"
27
+ spec.add_development_dependency "vcr", "~> 2.9"
28
+ spec.add_development_dependency 'webmock', '~> 1.20'
29
+ spec.add_development_dependency 'pry', '~> 0'
30
+ end