cassette_explorer 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 565db288bd62dcbd44e712bd0e5f6f84af54e656
4
+ data.tar.gz: 7ee23f6229a282aa669f4f82d769605050eb57e0
5
+ SHA512:
6
+ metadata.gz: 74d975de6175f17c9367567b575373bad68d8d67de142514b0ed5ffd0f89c3de02718c7495b11afd4d81a2508a308bb55db5446783f31b721c6a67a9fbec0bf9
7
+ data.tar.gz: ac84df6d4aace1d43583ceac36dcbea757933888f1fafc9301d5dbc0f6e11815267b08fbeb72dd9c4eadcb0ca56e77c0fd8f6d6fd5724aeb091fabb79fc52f06
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cassette_explorer.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,42 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: "bundle exec rspec" do
28
+ require "guard/rspec/dsl"
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+
31
+ # Feel free to open issues for suggestions and improvements
32
+
33
+ # RSpec files
34
+ rspec = dsl.rspec
35
+ watch(rspec.spec_helper) { rspec.spec_dir }
36
+ watch(rspec.spec_support) { rspec.spec_dir }
37
+ watch(rspec.spec_files)
38
+
39
+ # Ruby files
40
+ ruby = dsl.ruby
41
+ dsl.watch_spec_files_for(ruby.lib_files)
42
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Zequez
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # CassetteExplorer
2
+
3
+ CasetteExplorer is a command line utility that allow you to mount a webserver to load the
4
+ [VCR](https://github.com/vcr/vcr) cassettes in the browser.
5
+
6
+ ## Installation
7
+
8
+ ```
9
+ $ gem install cassette_explorer
10
+ ```
11
+
12
+ ## Usage
13
+
14
+ ```
15
+ $ cassette_explorer -h
16
+ Usage: cassette_explorer /path/to/vcr/cassettes/directory [options]
17
+ -r, --no-replace-urls Replace relative URLs with absolute URLs. Default: true
18
+ -f, --load-in-iframe Default the views to load in an iframe (it can be toggled from the page). Default: false
19
+ -w, --watch Watches the cassettes for changes. Default: true
20
+ -p, --port PORT Port to mount the server
21
+ -h, --help Prints this help
22
+ ```
23
+
24
+ ## TODO
25
+
26
+ - Write better tests
27
+ - Make the view a folder tree
28
+
29
+ ## Contributing
30
+
31
+ 1. Fork it ( https://github.com/Zequez/cassette_explorer/fork )
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create a new Pull Request
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ THIS_FILE = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
4
+ begin
5
+ require File.dirname(THIS_FILE) + '/../lib/cassette_explorer'
6
+ rescue LoadError
7
+ require 'cassette_explorer'
8
+ end
9
+
10
+ server = CassetteExplorer::Server.from_command_line(ARGV)
11
+ if server
12
+ server.run
13
+ end
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cassette_explorer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'cassette_explorer'
8
+ spec.version = CassetteExplorer::VERSION
9
+ spec.authors = ['Zequez']
10
+ spec.email = ['zequez@gmail.com']
11
+ spec.summary = %q{VCR Cassette Explorer}
12
+ spec.description = %q{Mounts a webserver to serve VCR cassettes for visualization on your browser}
13
+ spec.homepage = 'http://github.com/Zequez/cassette_explorer'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = 'cassette_explorer'
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'listen', '~> 3.0'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.7'
24
+ spec.add_development_dependency 'rake', '~> 10.0'
25
+ spec.add_development_dependency 'rspec', '~> 3.3'
26
+ spec.add_development_dependency 'guard', '~> 2.13'
27
+ spec.add_development_dependency 'guard-rspec', '~> 4.6'
28
+ spec.add_development_dependency 'webmock', '~> 1.21'
29
+ spec.add_development_dependency 'vcr', '~> 2.9'
30
+ spec.add_development_dependency 'typhoeus', '~> 0.7'
31
+ spec.add_development_dependency 'nokogiri', '~> 1.6'
32
+ spec.add_development_dependency 'capybara', '~> 2.4'
33
+ spec.add_development_dependency 'selenium-webdriver', '~> 2.46'
34
+ spec.add_development_dependency 'poltergeist', '~> 1.6'
35
+ end
@@ -0,0 +1,20 @@
1
+ dir = File.dirname(__FILE__)
2
+ $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
3
+
4
+ require 'webrick'
5
+ require 'yaml'
6
+ require 'erb'
7
+ require 'listen'
8
+ require 'optparse'
9
+ require 'ostruct'
10
+ require 'base64'
11
+
12
+ require "cassette_explorer/version"
13
+ require "cassette_explorer/page"
14
+ require "cassette_explorer/reader"
15
+ require "cassette_explorer/template"
16
+ require "cassette_explorer/server"
17
+
18
+ module CassetteExplorer
19
+ ROOT = Pathname.new(__dir__) + '..'
20
+ end
@@ -0,0 +1,54 @@
1
+ module CassetteExplorer
2
+ class Page
3
+ attr_reader :request
4
+
5
+ def initialize(request)
6
+ @request = request
7
+ end
8
+
9
+ def method
10
+ @request['request']['method'].upcase
11
+ end
12
+
13
+ def status
14
+ @request['response']['status']['code']
15
+ end
16
+
17
+ def title
18
+ @title ||= html_raw.scan(/<title>([^<]+)<\/title>/).flatten.first
19
+ end
20
+
21
+ def html(relative_urls = false)
22
+ if relative_urls
23
+ html_with_relative_urls
24
+ else
25
+ html_raw
26
+ end
27
+ end
28
+
29
+ def url
30
+ @request['request']['uri']
31
+ end
32
+
33
+ def uri
34
+ @uri ||= URI.parse(url)
35
+ end
36
+
37
+ def html_raw
38
+ body = @request['response']['body']
39
+
40
+ if body['base64_string']
41
+ html = Base64.decode64 body['base64_string']
42
+ else
43
+ html = body['string']
44
+ end
45
+ end
46
+
47
+ def html_with_relative_urls
48
+ base_path = uri.scheme + '://' + uri.host + uri.path.sub(/[^\/]+$/, '')
49
+ html_raw
50
+ .gsub('href="./', "href=\"#{base_path}")
51
+ .gsub('src="./', "src=\"#{base_path}")
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,32 @@
1
+ module CassetteExplorer
2
+ class Reader
3
+ def initialize(path)
4
+ @path = File.expand_path(path)
5
+ end
6
+
7
+ def read
8
+
9
+ files = Dir["#{@path}/**/**"]
10
+
11
+ sections = {}
12
+
13
+ files.reject{ |f| File.directory? f }.each do |file|
14
+ section_name = section_name_from_file_path(file)
15
+ sections[section_name] ||= []
16
+
17
+ data = YAML.load_file(file)
18
+ if data['http_interactions']
19
+ data['http_interactions'].each do |request|
20
+ sections[section_name].push Page.new(request)
21
+ end
22
+ end
23
+ end
24
+
25
+ sections
26
+ end
27
+
28
+ def section_name_from_file_path(file_path)
29
+ file_path.gsub(@path, '')
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,105 @@
1
+ module CassetteExplorer
2
+ class Server
3
+ def self.from_command_line(argv)
4
+ options = {}
5
+
6
+ parser = OptionParser.new do |opts|
7
+ opts.banner = 'Usage: cassette_explorer /path/to/vcr/cassettes/directory [options]'
8
+
9
+ opts.on('-r', '--no-replace-urls', 'Replace relative URLs with absolute URLs. Default: true') do |v|
10
+ options[:relative_urls] = !v
11
+ end
12
+
13
+ opts.on(
14
+ '-f',
15
+ '--load-in-iframe',
16
+ 'Default the views to load in an iframe (it can be toggled from the page). Default: false'
17
+ ) do |v|
18
+ options[:load_in_iframe] = v
19
+ end
20
+
21
+ opts.on('-w', '--watch', 'Watches the cassettes for changes. Default: true') do |v|
22
+ options[:watch] = v
23
+ end
24
+
25
+ opts.on('-p', '--port PORT', 'Port to mount the server') do |v|
26
+ options[:port] = v
27
+ end
28
+
29
+ opts.on('-h', '--help', 'Prints this help') do |v|
30
+ options[:help] = v
31
+ end
32
+ end
33
+
34
+ begin
35
+ parser.parse!(argv)
36
+ raise OptionParser::InvalidOption if argv.size != 1
37
+ options[:path] = argv.pop
38
+ rescue OptionParser::InvalidOption
39
+ options[:help] = true
40
+ end
41
+
42
+ if options[:help]
43
+ puts parser.help
44
+ else
45
+ new options
46
+ end
47
+ end
48
+
49
+ def initialize(options = {})
50
+ @options = {
51
+ port: 2332,
52
+ path: './spec/fixtures/vcr_cassettes/',
53
+ template: __dir__ + '/templates/index.erb',
54
+ relative_urls: true,
55
+ watch: true,
56
+ load_in_iframe: false
57
+ }.merge(options)
58
+
59
+ @reader = Reader.new(@options[:path])
60
+ end
61
+
62
+ def run(blocking = true)
63
+ @sections = @reader.read
64
+
65
+ listener = Listen.to(@options[:path]) do
66
+ puts 'Change detected, reloading cassettes'
67
+ @sections = @reader.read
68
+ end
69
+
70
+ listener.start # doesn't block
71
+
72
+ server = WEBrick::HTTPServer.new Port: @options[:port]
73
+ server.mount_proc '/' do |req, res|
74
+ url = req.query['url']
75
+ file = req.query['file']
76
+ data = req.query['data']
77
+
78
+ if file and url
79
+ section = @sections[file]
80
+ page = section.detect{ |p| p.url == url }
81
+
82
+ if data
83
+ res.body = YAML::dump(page.request)
84
+ else
85
+ res.body = page.html(@options[:relative_urls])
86
+ end
87
+ else
88
+ res.body = render_index(@sections)
89
+ end
90
+ end
91
+
92
+ if blocking
93
+ server.start
94
+ else
95
+ Thread.new do
96
+ server.start
97
+ end
98
+ end
99
+ end
100
+
101
+ def render_index(data)
102
+ Template.new(files: data, load_in_iframe: @options[:load_in_iframe]).render(@options[:template])
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,14 @@
1
+ module CassetteExplorer
2
+ class Template < OpenStruct
3
+ include ERB::Util
4
+
5
+ def render(template_file)
6
+ template = File.read template_file
7
+ ERB.new(template).result(binding)
8
+ end
9
+
10
+ def page_path(file, page)
11
+ "?file=#{u file}&url=#{u page.url}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,87 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <title>List of pages</title>
4
+ <style>
5
+ body {
6
+ margin: 0;
7
+ font-family: monospace;
8
+ font-size: 16px;
9
+ line-height: 24px;
10
+ }
11
+
12
+ .file {
13
+ color: grey;
14
+ }
15
+
16
+ .method {
17
+ font-weight: bold;
18
+ }
19
+
20
+ .method.get {
21
+ color: green;
22
+ }
23
+
24
+ .method.post {
25
+ color: red;
26
+ }
27
+
28
+ .link {
29
+
30
+ }
31
+
32
+ .status {
33
+ font-weight: bold;
34
+ color: red;
35
+ }
36
+
37
+ .status.status-200 {
38
+ color: green;
39
+ }
40
+
41
+ .iframe-toggle-container {
42
+ position: absolute;
43
+ top: 0;
44
+ right: 0;
45
+ }
46
+
47
+ iframe {
48
+ width: 100%;
49
+ height: 1000px;
50
+ border: 0;
51
+ }
52
+ </style>
53
+ <body>
54
+ <ul>
55
+ <% files.each_pair do |file, pages| %>
56
+ <li class="file"><%= file %></li>
57
+ <ul>
58
+ <% pages.each do |page| %>
59
+ <li>
60
+ <a class="interaction-link" href="<%=page_path file, page%>&data">Data</a>
61
+ <span class="method <%= page.method.downcase %>"><%= page.method %></span>
62
+ <a class="link" href="<%=page_path file, page%>"><%= page.url %></a>
63
+ <span class="status status-<%= page.status %>">=> <%= page.status %></span>
64
+ <span class="title"><%= page.title and %Q{"#{page.title}"} %></span>
65
+ </li>
66
+ <% end %>
67
+ </ul>
68
+ <% end %>
69
+ </ul>
70
+ <label class="iframe-toggle-container">
71
+ Load in iframe
72
+ <input type="checkbox" id="iframe-toggle" <%=load_in_iframe ? 'checked' : ''%>/>
73
+ </label>
74
+ <iframe src=""></iframe>
75
+
76
+ <script>
77
+ var iframe = document.getElementsByTagName('iframe')[0];
78
+ var iframeToggle = document.getElementById('iframe-toggle');
79
+ document.body.addEventListener('click', function(ev){
80
+ if (iframeToggle.checked && ev.target.tagName == 'A') {
81
+ iframe.src = ev.target.href;
82
+ ev.preventDefault();
83
+ }
84
+ });
85
+ </script>
86
+ </body>
87
+ </html>