vizreview 0.1.0 → 1.0.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9669457c4a826133190d6f8658e0289745cd93bda9c6faa1260bc7f0ef2fad02
4
- data.tar.gz: b0005bbf4a2c71face463b967cc8a1bef84eb2bf138d3af23fc280cae4b4c1e7
3
+ metadata.gz: 5894a98186be334a2c6c510e8bc98ac5352b1fcf8c281e07fdbf5ca38a170e0a
4
+ data.tar.gz: d2168cd6f07e24c38674b4b35b409cf5cdec807bfabebaa78354fe5297dff4dc
5
5
  SHA512:
6
- metadata.gz: 1d9da4a0b5034495db28d9f67e06c0221d48c82c1fd50160dc197b1f8f1df0f15204a093e6f7709156250cf5a310fb2267a185293a48d36b70d2bd7895343409
7
- data.tar.gz: abd2ad062619ad0477fdd344d425ab27044d8f542b41f59c265bf8f8a105b58d7eeb85ba8822d89763e47d970626aa6f2687379f57eadd570c0d262af1a59a46
6
+ metadata.gz: 5a0404d5568b744c10acefb88420a0a7b67525b069858859322a83f365caa172dadfca5d08aa592b1661d8b3d2a2a152774e3ee1b7c15471ded40f5dbd6e9fb3
7
+ data.tar.gz: 78aa4f7a9181384fbc9e5b470511ce6eaa149da73f06dac13751ac8cb210eda7620fcff9850c791254d9cbbe64ecca880e72e66261104e839239ec4f374c6666
data/Gemfile CHANGED
@@ -1,6 +1,6 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
4
 
5
5
  # Specify your gem's dependencies in vizreview.gemspec
6
6
  gemspec
@@ -0,0 +1,43 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ vizreview (1.0.0)
5
+ chunky_png (~> 1.3.7)
6
+ typhoeus (~> 1.3)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ chunky_png (1.3.10)
12
+ diff-lcs (1.3)
13
+ ethon (0.11.0)
14
+ ffi (>= 1.3.0)
15
+ ffi (1.9.25)
16
+ rake (10.5.0)
17
+ rspec (3.8.0)
18
+ rspec-core (~> 3.8.0)
19
+ rspec-expectations (~> 3.8.0)
20
+ rspec-mocks (~> 3.8.0)
21
+ rspec-core (3.8.0)
22
+ rspec-support (~> 3.8.0)
23
+ rspec-expectations (3.8.2)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.8.0)
26
+ rspec-mocks (3.8.0)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.8.0)
29
+ rspec-support (3.8.0)
30
+ typhoeus (1.3.0)
31
+ ethon (>= 0.9.0)
32
+
33
+ PLATFORMS
34
+ ruby
35
+
36
+ DEPENDENCIES
37
+ bundler (~> 1.16)
38
+ rake (~> 10.0)
39
+ rspec (~> 3.0)
40
+ vizreview!
41
+
42
+ BUNDLED WITH
43
+ 1.16.2
data/LICENSE ADDED
@@ -0,0 +1,32 @@
1
+ SOFTWARE DEVELOPMENT KIT (SDK) LICENSE AGREEMENT
2
+ DEVELOPMENT KIT (SDK) LICENSE AGREEMENT
3
+
4
+ This Software License Agreement (the "Agreement") governs use of the SDK by you, and/or the entity on whose behalf you are downloading the SDK ("you"). BY DOWNLOADING, INSTALLING, OR OTHERWISE ACCESSING OR USING THE SDK, YOU AGREE THAT YOU HAVE READ, UNDERSTOOD, AND AGREE TO BE BOUND BY THE AGREEMENT. IF YOU DO NOT AGREE, YOU MAY NOT USE THE SDK. Accordingly, you and VizReview acknowledge and agree as follows:
5
+
6
+ 1. LIMITED LICENSE. Subject to your complete and ongoing compliance with all the terms and conditions set forth in this Agreement, including without limitation all license limitations and restrictions set forth herein, VizReview grants you the following limited, non-exclusive, non-transferable, non-sublicensable, revocable licenses to:
7
+
8
+ a. use, and (where applicable) authorize your employees to use, the Documentation internally solely in connection with developing your own branded applications that interoperate with the Service ("Applications");
9
+
10
+ b. incorporate unmodified Libraries into your Application, solely for the purpose of enabling interoperability with the Service, solely in accordance with all applicable Documentation and applicable terms, and subject to you obtaining and maintaining an API key from VizReview, to distribute Libraries so incorporated in your compliant Application to end users in executable form (except and solely to the extent that the Libraries are written in a language that is traditionally delivered for runtime interpretation in source code form); and
11
+
12
+ c. use, modify, and redistribute the Sample Code pursuant to the applicable open source license, as identified in the headers or associated Documentation.
13
+
14
+ 2. RESTRICTIONS. By accessing or using the SDK, you represent, warrant, and covenant that (a) you are a person or business entity engaged in the development of software applications, and (b) in the case of a business entity, you have the full power and authority to bind such entity to the terms of this Agreement. References to "you" herein shall refer to you, and/or the entity on whose behalf you are using the SDK, and all individual users of the SDK on behalf of such entity. You acknowledge that the foregoing license does not include any right to (i) redistribute, sell, lease, license, or modify any portion of the SDK, or (ii) distribute, deploy, or otherwise utilize Applications on a public, production, commercial, or other similar purpose other than internal use for evaluation and the development of non-public, experimental Applications (any other public, production, commercial, or similar use requires a separate agreement with VizReview), or (iii) use or implement any undocumented feature or API, or use any documented feature or API other than in accordance with applicable documentation. You may not reproduce, distribute, publicly display, or publicly perform any part of the SDK, except as provided herein or in the applicable open source license. Except if, and solely to the extent that, such a restriction is not authorized herein, or impermissible under applicable law or applicable Third Party Software (defined below) license terms, you may not (y) decompile, reverse engineer, or otherwise access or attempt to access the source code for the SDK not made available to you in source code form, or make or attempt to make any modification to the SDK; or (z) remove, obscure, interfere with or circumvent any feature of the SDK, including without limitation any copyright or other intellectual property notices, security, or access control mechanism. You may not use the SDK for any purpose other than interoperating with the Service in a manner for which the SDK and Service are expressly designed. If you are prohibited under applicable law from using the SDK, you may not use them, and you will comply with all applicable laws and regulations (including without limitation laws and regulations related to export controls) in connection with your use of the SDK. Without limiting the generality of the foregoing, you represent and warrant that the SDK will not be shipped, transferred or exported into any country or used in any manner prohibited by the United States Export Administration Act or any other export laws, restrictions or regulations (collectively the "Export Laws"). In addition, if the SDK is identified as export controlled items under the Export Laws, you represent and warrant that you are not a citizen, or otherwise located within, an embargoed nation (including without limitation Crimea, Cuba, Iran, North Korea, Sudan, or Syria and that you are not otherwise prohibited under the Export Laws from receiving the SDK. You may not use the SDK for any purpose, or use the SDK in the development of any Application that is for the purpose of lifesaving, emergency response, or otherwise for deployment in any circumstance in which failure would be likely to lead to property damage, environmental damage, personal injury, or death. ANY USE IN VIOLATION OF THE FOREGOING LIMITATIONS AND RESTRICTIONS IS STRICTLY PROHIBITED, AND UNLICENSED.
15
+
16
+ 3. RESERVATION OF RIGHTS. The SDK is owned by VizReview and licensed, not sold, to you. The SDK, content, visual interfaces, interactive features, information, graphics, design, compilation, computer code, products, services, and all other elements of the SDK and related documentation (the "VizReview Materials"), are protected by copyright, trade dress, patent, and trademark laws of the United States and other jurisdictions, international conventions, and all other relevant intellectual property and proprietary rights, and applicable laws. As between you and VizReview, all VizReview Materials, including intellectual property rights therein and thereto, are the sole and exclusive property of VizReview or its subsidiaries or affiliated companies and/or its third-party licensors. You may not to sell, license, distribute, copy, modify, publicly perform or display, transmit, publish, edit, adapt, create derivative works from, or make any use of the VizReview Materials except as expressly authorized hereunder. VizReview reserves all rights not expressly granted in this Agreement. You do not acquire any right, title or interest to the VizReview Materials, whether by implication, estoppel, or otherwise, except for the limited rights set forth in this Agreement.
17
+
18
+ 4. CONFIDENTIALITY. The SDK (including as embodied in or utilized by any Application) is the confidential and proprietary information of VizReview, and you may not, during the term or thereafter, disclose them to any third party, or to use them for any purpose other than as expressly provided herein, without a separate written agreement with VizReview authorizing you to do so.
19
+
20
+ 5. FEEDBACK. If you provide VizReview with any comments, bug reports, feedback, enhancements, or modifications proposed or suggested by you for the SDK or the Service ("Feedback"), such Feedback is provided on a non-confidential basis (notwithstanding any notice to the contrary you may include in any accompanying communication), and VizReview shall have the right to use such Feedback at its discretion, including, but not limited to the incorporation of such suggested changes into the VizReview Materials, SDK or Service. You hereby grant VizReview a perpetual, irrevocable, transferable, sublicensable, nonexclusive license under all rights necessary to so incorporate and use your Feedback for any purpose, including to make and sell products and services.
21
+
22
+ 6. THIRD PARTY SOFTWARE. The SDK consists of a package of components, including certain third party software ("Third Party Software") that are provided by their authors under separate license terms (the "Third Party Terms"), as described in more detail in the SDK. Your use of the Third Party Software in conjunction with the SDK in a manner consistent with this Agreement is permitted, however, you may have broader rights under the applicable Third Party Terms and nothing in this Agreement is intended to impose further restrictions on your use of the Third Party Software.
23
+
24
+ 7. TERM AND TERMINATION. This Agreement will remain in effect until terminated. The Agreement, and your rights and licenses hereunder, will terminate immediately upon your breach of the Agreement. You may terminate the Agreement by uninstalling and ceasing all use of the SDK. VizReview may terminate this Agreement at any time for any reason, including without limitation any actual or suspected misuse or abuse by you of the SDK or any violation of this Agreement. Following any termination of this Agreement, you must immediately uninstall and cease use of the SDK, and destroy all copies. Sections 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, and 11 shall survive any termination of this Agreement.
25
+
26
+ 8. WARRANTY DISCLAIMER AND LIMITATION OF LIABILITY. THE SDK AND ALL ASSOCIATED DOCUMENTATION, LIBRARIES, AND SAMPLE CODE ARE PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTY OF ANY KIND. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, VizReview DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OR CONDITIONS OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, TITLE, QUALITY, RESULTS, AND NON-INFRINGEMENT. VizReview EXPRESSLY DISCLAIMS ANY WARRANTIES OF ANY KIND WITH RESPECT TO THE ACCURACY OR FUNCTIONALITY OF THE SDK, AND WITH RESPECT TO THE ACCURACY, VALIDITY, OR COMPLETENESS OF ANY INFORMATION OR FEATURES AVAILABLE THROUGH THE SDK, OR THE QUALITY OR CONSISTENCY OF THE SDK OR RESULTS OBTAINED THROUGH ITS USE. UNDER NO CIRCUMSTANCES WILL VizReview BE LIABLE FOR ANY CONSEQUENTIAL, SPECIAL, INDIRECT, INCIDENTAL OR PUNITIVE DAMAGES WHATSOEVER ARISING OUT OF THE USE OR INABILITY TO USE THE SDK, EVEN IF VizReview HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, AND NOTWITHSTANDING ANY FAILURE OF ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. IN NO EVENT WILL VizReview'S AGGREGATE LIABILITY FOR DAMAGES ARISING OUT OF THIS AGREEMENT OR THE TERMS EXCEED THE GREATER OF AMOUNTS PAID BY YOU FOR THE SDK, IF ANY, OR $50USD. SOME JURISDICTIONS DO NOT ALLOW LIMITATIONS ON IMPLIED WARRANTIES OR THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, SO THE ABOVE LIMITATIONS MAY NOT APPLY TO YOU. IN SUCH AN EVENT THE ABOVE LIMITATIONS AND EXCLUSIONS WILL BE ENFORCED TO THE MAXIMUM EXTENT PERMITTED UNDER APPLICABLE LAW.
27
+
28
+ 9. INDEMNITY. You agree to indemnify, defend and hold VizReview and its affiliates, officers, directors, suppliers, licensors, and other customers harmless from and against any and all liability and costs, including reasonable attorneys' fees incurred by such parties, in connection with or arising out of your Applications, your use or misuse of the SDK, or your violation of this Agreement, any applicable law or regulation.
29
+
30
+ 10. GOVERNING LAW; VENUE. Any claim relating to the SDK or Service shall be governed by the laws of California, without regard to conflict of laws provisions. Disputes arising under this shall be resolved in, and subject to the sole and exclusive jurisdiction of the state and federal courts located in the Northern District of California.
31
+
32
+ 11. MISCELLANEOUS. This Agreement is the entire agreement between you and VizReview, and supersedes any and all prior agreements, negotiations, or other communications between you and VizReview, whether oral or written, with respect to the subject matter hereof, and, except as expressly provided herein, cannot be modified except in writing signed by both parties. In the event that any provision of this Agreement is held to be invalid or unenforceable, then: (a) such provision shall be deemed reformed to the extent strictly necessary to render such provision valid and enforceable, or if not capable of such reformation shall be deemed severed from this Agreement; and (b) the validity and enforceability of all of the other provisions hereof, shall in no way be affected or impaired thereby. You may not assign this Agreement without the prior written consent of VizReview, whether expressly or by operation of law, including in connection with a merger or change of control, and any such attempted assignment shall be void and of no effect. VizReview may assign this Agreement without restriction and without any notice to you. Subject to the foregoing, this Agreement shall be binding on the parties and their respective successors and permitted assigns. You acknowledge and understand that if VizReview is unable to provide the SDK as a result of a force majeure event VizReview will not be in breach of this Agreement. A force majeure event means any event beyond the control of VizReview. The failure to exercise, or delay in exercising, a right, power or remedy provided in this Agreement or by law shall not constitute a waiver of that right, power or remedy. VizReview's waiver of any obligation or breach of this Agreement shall not operate as a waiver of any other obligation or subsequent breach of the Agreement. The English language version of this Agreement shall be the official and controlling version, and any translation provided is solely for convenience. The SDK is a "Commercial Item" as that term is defined at 48 C.F.R. 2.101, consisting of "Commercial Computer Software" and "Commercial Computer Software Documentation." If and to the extent the SDK is supplied to or purchased by or on behalf of a United States government entity or an entity licensing the SDK for or on behalf of a United States government entity, the SDK is licensed (a) only as a Commercial Item and (b) with only those rights as are granted to all other end users pursuant to the terms and conditions of this agreement.
@@ -1,5 +1,16 @@
1
1
  require "vizreview/version"
2
+ require 'vizreview/image'
3
+ require 'vizreview/rectangle'
4
+ require 'vizreview/screenshot_provider'
5
+ require 'vizreview/browser'
6
+ require 'vizreview/configuration'
7
+ require 'vizreview/service'
8
+ require 'vizreview/logger'
9
+ require 'vizreview/client'
10
+ require 'vizreview/snapshot'
11
+ require 'vizreview/utils/git_utils'
12
+ require 'vizreview/version_info_providers/git_provider'
13
+ require 'vizreview/version_info_providers/provider_detector'
2
14
 
3
- module Vizreview
4
- # Your code goes here...
15
+ module VizReview
5
16
  end
@@ -0,0 +1,2 @@
1
+ require_relative './browser/page_manager'
2
+ require_relative './browser/info_provider'
@@ -0,0 +1,39 @@
1
+ module VizReview
2
+ module Browser
3
+ class InfoProvider
4
+ def initialize(driver)
5
+ @driver = driver
6
+ end
7
+
8
+ def page_rect
9
+ metrics = driver.execute_script(JS_GET_PAGE_METRICS).with_indifferent_access
10
+ max_width = [metrics[:scroll_width], metrics[:body_scroll_width]].max
11
+ max_height = [metrics[:body_client_height], metrics[:body_scroll_height]].max
12
+ return Rectangle.new(0, 0, max_width, max_height)
13
+ end
14
+
15
+ def pixel_ratio
16
+ driver.execute_script('return window.devicePixelRatio')
17
+ end
18
+
19
+ def browser_name
20
+ driver.browser.browser
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :driver
26
+
27
+ JS_GET_PAGE_METRICS = <<-JS.freeze
28
+ return {
29
+ scroll_width: document.documentElement.scrollWidth,
30
+ body_scroll_width: document.body.scrollWidth,
31
+ client_height: document.documentElement.clientHeight,
32
+ body_client_height: document.body.clientHeight,
33
+ scroll_height: document.documentElement.scrollHeight,
34
+ body_scroll_height: document.body.scrollHeight
35
+ };
36
+ JS
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,49 @@
1
+ module VizReview
2
+ module Browser
3
+ class PageManager
4
+ def initialize(driver)
5
+ @driver = driver
6
+ end
7
+
8
+ def screenshot
9
+ # currOverflow = scrollOverflow
10
+ # scrollOverflow = 'hidden' if hideScrollbars
11
+ img = driver.browser.screenshot_as(:png)
12
+ # scrollOverflow = currOverflow
13
+ Image.from_string(img)
14
+ end
15
+
16
+ def scroll_to(x, y)
17
+ driver.execute_script("window.scrollTo(#{x}, #{y})")
18
+ end
19
+
20
+ def hide_scrollbars
21
+ res = overflow = scroll_overflow
22
+ driver.execute_script('document.querySelector("html").style.overflow = "hidden";')
23
+ res = yield if block_given?
24
+ driver.execute_script("document.querySelector(\"html\").style.overflow = \"#{overflow}\";")
25
+ res
26
+ end
27
+
28
+ def apply_viewport(viewport)
29
+ curr_size = driver.browser.manage.window.size
30
+ return yield(curr_size.width, curr_size.height) if viewport.nil?
31
+ driver.browser.manage.window.resize_to(
32
+ viewport[:width] || curr_size.width, viewport[:height] || curr_size.height
33
+ )
34
+ new_size = driver.browser.manage.window.size
35
+ res = yield(new_size.width, new_size.height)
36
+ driver.browser.manage.window.resize_to(curr_size.width, curr_size.height)
37
+ res
38
+ end
39
+
40
+ private
41
+
42
+ attr_reader :driver
43
+
44
+ def scroll_overflow
45
+ driver.execute_script('return document.querySelector("html").style.overflow;')
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,68 @@
1
+ module VizReview
2
+ class Client
3
+ API_URL = 'https://app.vizreview.io'
4
+
5
+ class << self
6
+ attr_reader :config, :conn, :version_service, :version_id, :uploader,
7
+ :snapshot_service, :version_info_provider
8
+
9
+ def start
10
+ Logger.prefix = 'VizReview - '
11
+ @config = default_config
12
+ yield(config) if block_given?
13
+ Logger.level = config.log_level
14
+ raise 'Token not provided in VizReview::Client.start block' if config.token.nil?
15
+ auth = Service::Auth.new(config.token)
16
+ @conn = Service::APIConnection.new(url: config.api_url, auth: auth)
17
+ @version_info_provider = VersionInfoProviders::ProviderDetector.retrieve
18
+ @version_service = Service::Version.new(conn)
19
+ @uploader = Service::ParallelUploader.new(pool_size: 4)
20
+ @snapshot_service = Service::Snapshot.new(conn)
21
+ initiate_version!
22
+ end
23
+
24
+ def finish
25
+ raise 'VizReview client has not been started.' if version_id.nil?
26
+ uploader&.stop
27
+ version_service&.complete(version_id)
28
+ end
29
+
30
+ def capture!(driver, name:, viewport: nil)
31
+ raise 'VizReview client has not been started.' if version_id.nil?
32
+ viewport = config.default_viewport if viewport.nil?
33
+ viewport = [viewport] unless viewport.is_a?(Array)
34
+ driver = driver.driver if driver.respond_to?(:driver)
35
+ info_provider = Browser::InfoProvider.new(driver)
36
+ page_manager = Browser::PageManager.new(driver)
37
+ screenshot_provider = ScreenshotProvider.new(page_manager, info_provider)
38
+
39
+ # For each viewport, take a screenshot
40
+ snapshots = []
41
+ viewport.each do |vp|
42
+ snapshots << screenshot_provider.snap!(full: true, viewport: vp)
43
+ end
44
+
45
+ snapshots.each { |snapshot| upload_snapshot(name, snapshot) }
46
+ end
47
+
48
+ private
49
+
50
+ def initiate_version!
51
+ branch = version_info_provider.branch
52
+ version = version_service.create(branch)
53
+ @version_id = JSON.parse(version.body)['id']
54
+ end
55
+
56
+ def upload_snapshot(name, snapshot)
57
+ tmp = snapshot.image.save_temp
58
+ result = snapshot_service.create(version_id, snapshot: snapshot, name: name)
59
+ upload_url = JSON.parse(result.body)['uploadUrl']
60
+ uploader.upload(upload_url, tmp.path)
61
+ end
62
+
63
+ def default_config
64
+ Configuration.new(api_url: API_URL)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,11 @@
1
+ module VizReview
2
+ class Configuration
3
+ attr_accessor :api_url, :token, :log_level, :default_viewport
4
+
5
+ def initialize(api_url:)
6
+ @api_url = api_url
7
+ @log_level = :fatal
8
+ @default_viewport = nil
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ require 'chunky_png'
2
+
3
+ module VizReview
4
+ class Image
5
+ class << self
6
+ def from_string(img_str)
7
+ stream = ::ChunkyPNG::Datastream.from_string(img_str)
8
+ image = ::ChunkyPNG::Image.from_datastream(stream)
9
+ new(image)
10
+ end
11
+
12
+ def create(width, height)
13
+ new(::ChunkyPNG::Image.new(width, height))
14
+ end
15
+ end
16
+
17
+ def initialize(image)
18
+ @image = image
19
+ end
20
+
21
+ def draw!(image, x ,y)
22
+ self.image.replace!(image.image, x, y)
23
+ end
24
+
25
+ def save_temp
26
+ tmp = Tempfile.new
27
+ image.save(tmp.path)
28
+ tmp
29
+ end
30
+
31
+ def to_blob
32
+ image.to_datastream.to_blob
33
+ end
34
+
35
+ def save(path)
36
+ image.save(path)
37
+ end
38
+
39
+ def width
40
+ image.width
41
+ end
42
+
43
+ def height
44
+ image.height
45
+ end
46
+
47
+ protected
48
+
49
+ attr_reader :image
50
+ end
51
+ end
@@ -0,0 +1,39 @@
1
+ module VizReview
2
+ class Logger
3
+ class << self
4
+ LOG_LEVELS = [:debug, :info, :warn, :fatal]
5
+
6
+ @level = :fatal
7
+
8
+ def level=(level)
9
+ raise ArgumentError.new('Invalid log level') unless level.to_sym.in?(LOG_LEVELS)
10
+ @level = level.to_sym
11
+ end
12
+
13
+ def log(msg)
14
+ puts "#{prefix}#{msg}".strip
15
+ end
16
+
17
+ def debug(msg)
18
+ return unless log?(:debug)
19
+ log("[DEBUG] #{msg}")
20
+ end
21
+
22
+ def prefix=(prefix)
23
+ @prefix = prefix
24
+ end
25
+
26
+ def prefix
27
+ @prefix || ''
28
+ end
29
+
30
+ private
31
+
32
+ def log?(level)
33
+ curr_index = LOG_LEVELS.index(@level)
34
+ level_index = LOG_LEVELS.index(level)
35
+ level_index >= curr_index
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ module VizReview
2
+ class Rectangle
3
+ OVERLAP = 4
4
+
5
+ attr_reader :x, :y, :width, :height
6
+
7
+ def initialize(x, y, width, height)
8
+ @x = x
9
+ @y = y
10
+ @width = width
11
+ @height = height
12
+ end
13
+
14
+ def subdivide(sub_width, sub_height)
15
+ rects = []
16
+ top = x
17
+ while (top < height)
18
+ bottom = [top + sub_height, height].min
19
+ left = y
20
+ while (left < width)
21
+ right = [left + sub_width, width].min
22
+ rects << Rectangle.new(
23
+ right - sub_width,
24
+ bottom - sub_height,
25
+ sub_width,
26
+ sub_height,
27
+ )
28
+ left += sub_width
29
+ end
30
+ top += sub_height
31
+ end
32
+ rects
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,46 @@
1
+ module VizReview
2
+ class ScreenshotProvider
3
+ def initialize(page, info_provider)
4
+ @page = page
5
+ @info_provider = info_provider
6
+ end
7
+
8
+ def snap!(full: true, viewport: nil)
9
+ page.hide_scrollbars do
10
+ page.apply_viewport(viewport) do |width, height|
11
+ img = full ? full_snap! : page.screenshot
12
+ Snapshot.new(
13
+ image: img,
14
+ viewport: { width: width, height: height },
15
+ browser: {
16
+ name: info_provider.browser_name,
17
+ version: nil,
18
+ },
19
+ )
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def full_snap!
27
+ ratio = info_provider.pixel_ratio
28
+ rect = info_provider.page_rect
29
+
30
+ img = page.screenshot
31
+ sub_width = (img.width / ratio).round
32
+ sub_height = (img.height / ratio).round
33
+
34
+ base = Image.create(rect.width * ratio, rect.height * ratio)
35
+
36
+ rect.subdivide(sub_width, sub_height).each do |subrect|
37
+ page.scroll_to(subrect.x, subrect.y)
38
+ img = page.screenshot
39
+ base.draw!(img, subrect.x * ratio, subrect.y * ratio)
40
+ end
41
+ base
42
+ end
43
+
44
+ attr_reader :page, :info_provider
45
+ end
46
+ end
@@ -0,0 +1,12 @@
1
+ require_relative './service/connection'
2
+ require_relative './service/api_connection'
3
+ require_relative './service/auth'
4
+ require_relative './service/parallel_uploader'
5
+ require_relative './service/google_uploader'
6
+ require_relative './service/version'
7
+ require_relative './service/snapshot'
8
+
9
+ module VizReview
10
+ module Service
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ module VizReview
2
+ module Service
3
+ class APIConnection < Connection
4
+ def initialize(url:, auth:)
5
+ super(auth: auth)
6
+ @url = url
7
+ end
8
+
9
+ def post(path, body: nil, headers: {})
10
+ super("#{url}#{path}", body: body, headers: headers)
11
+ end
12
+
13
+ def put(path, body: nil, headers: {})
14
+ super("#{url}#{path}", body: body, headers: headers)
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :url
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module VizReview
2
+ module Service
3
+ class Auth
4
+ def initialize(token)
5
+ @token = token
6
+ end
7
+
8
+ def headers
9
+ { 'Authorization' => "Bearer #{token}" }
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :token
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,34 @@
1
+ require 'typhoeus'
2
+ require 'json'
3
+
4
+ module VizReview
5
+ module Service
6
+ class Connection
7
+ def initialize(auth: nil)
8
+ @auth = auth
9
+ end
10
+
11
+ def post(url, body: nil, headers: {}, verbose: false)
12
+ Logger.debug("POST #{url} - #{{ body: body.to_s[0..255], headers: headers }}")
13
+ res = Typhoeus.post(url, body: body, headers: headers.merge(auth_headers), verbose: verbose)
14
+ Logger.debug("Response (POST #{url}): { response_code: #{res.response_code}, body: #{res.body} }")
15
+ res
16
+ end
17
+
18
+ def put(url, body: nil, headers: {}, verbose: false)
19
+ Logger.debug("PUT #{url} - #{{ body: body.to_s[0..255], headers: headers }}")
20
+ res = Typhoeus.put(url, body: body, headers: headers.merge(auth_headers), verbose: verbose)
21
+ Logger.debug("Response (PUT #{url}): { response_code: #{res.response_code}, body: #{res.body} }")
22
+ res
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :auth
28
+
29
+ def auth_headers
30
+ auth&.headers || {}
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'thread'
2
+ require 'digest'
3
+
4
+ module VizReview
5
+ module Service
6
+ class GoogleUploader
7
+ def initialize(conn = Connection.new)
8
+ @conn = conn
9
+ end
10
+
11
+ def upload(url, image_path)
12
+ image_file = File.open(image_path, 'rb')
13
+ image = image_file.read
14
+ md5 = Digest::MD5.base64digest(image)
15
+
16
+ Logger.debug("GoogleUploader: Uploading image #{url} (md5: #{md5}, file_size: #{image_file.size})")
17
+ res = conn.put(url, body: image, headers: {
18
+ 'Content-Type' => 'image/png',
19
+ 'Content-MD5' => md5,
20
+ 'Content-Length' => image_file.size.to_s,
21
+ })
22
+ if res.response_code == 200
23
+ Logger.debug("GoogleUploader: Uploaded image #{url}")
24
+ else
25
+ Logger.debug("GoogleUploader: Failed to upload image #{url} (#{res.response_code}) #{res.body}")
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :conn
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,52 @@
1
+ require 'thread'
2
+ require 'uri'
3
+
4
+ module VizReview
5
+ module Service
6
+ class ParallelUploader
7
+ DEFAULT_POOL_SIZE = 4
8
+ EXIT_CODE = 1
9
+
10
+ def initialize(pool_size: DEFAULT_POOL_SIZE)
11
+ @pool_size = pool_size
12
+ @queue = Queue.new
13
+ @workers = start_pool
14
+ end
15
+
16
+ def upload(url, image_path)
17
+ queue.push({ url: url, image_path: image_path })
18
+ end
19
+
20
+ def stop
21
+ Logger.debug("ParallelUploader: Stopping uploader pool...")
22
+ pool_size.times { queue.push(EXIT_CODE) }
23
+ @workers.each { |worker| worker.join }
24
+ Logger.debug("ParallelUploader: Uploader pool completely stopped")
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :pool_size, :workers, :queue
30
+
31
+ def start_pool
32
+ Logger.debug("ParallelUploader: Starting uploader pool (pool_size: #{pool_size})")
33
+ (1..pool_size).map do |index|
34
+ Logger.debug("ParallelUploader: Starting uploader thread ##{index}...")
35
+ Thread.new do
36
+ Logger.debug("ParallelUploader: Uploader thread ##{index} started")
37
+ begin
38
+ uploader = GoogleUploader.new
39
+ while (data = @queue.pop(false))
40
+ Logger.debug("ParallelUploader: Uploader thread ##{index} received #{data}")
41
+ break if data == EXIT_CODE
42
+ uploader.upload(data[:url], data[:image_path])
43
+ end
44
+ rescue ThreadError
45
+ end
46
+ Logger.debug("ParallelUploader: Uploader thread ##{index} exiting...")
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ require 'digest'
2
+
3
+ module VizReview
4
+ module Service
5
+ class Snapshot
6
+ def initialize(conn)
7
+ @conn = conn
8
+ end
9
+
10
+ def create(version_id, snapshot:, name:)
11
+ md5 = Digest::MD5.base64digest(snapshot.image.to_blob)
12
+ conn.post('/snapshots/', body: {
13
+ versionId: version_id,
14
+ name: name,
15
+ width: snapshot.image.width,
16
+ height: snapshot.image.height,
17
+ viewport: snapshot.viewport,
18
+ browser: snapshot.browser,
19
+ contentType: 'image/png',
20
+ contentMd5: md5
21
+ })
22
+ end
23
+
24
+ def complete(id)
25
+ conn.put("/versions/#{id}/complete", body: { branch: branch })
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :conn
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,23 @@
1
+ module VizReview
2
+ module Service
3
+ class Version
4
+ def initialize(conn)
5
+ @conn = conn
6
+ end
7
+
8
+ def create(branch)
9
+ Logger.debug("Creating new version from branch #{branch}...")
10
+ conn.post('/versions/', body: { branch: branch })
11
+ end
12
+
13
+ def complete(id)
14
+ Logger.debug("Marking version #{id} as complete...")
15
+ conn.put("/versions/#{id}/complete")
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :conn
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ module VizReview
2
+ class Snapshot
3
+ attr_reader :image, :browser, :viewport
4
+
5
+ def initialize(image:, browser:, viewport:)
6
+ @image = image
7
+ @browser = browser
8
+ @viewport = viewport
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module VizReview
2
+ module Utils
3
+ class GitUtils
4
+ class << self
5
+ def installed?
6
+ res = `git rev-parse --git-dir 2> /dev/null`
7
+ !res.empty?
8
+ rescue
9
+ false
10
+ end
11
+
12
+ def branch
13
+ branch = `git symbolic-ref HEAD 2>/dev/null`
14
+ return nil if branch.empty?
15
+ branch.strip.gsub(/^refs\/heads\//, "")
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -1,3 +1,3 @@
1
1
  module Vizreview
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -0,0 +1,13 @@
1
+ module VizReview
2
+ module VersionInfoProviders
3
+ class GitProvider
4
+ def initialize
5
+ raise 'Git must be initialized in the project' unless Utils::GitUtils.installed?
6
+ end
7
+
8
+ def branch
9
+ Utils::GitUtils.branch
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,11 @@
1
+ module VizReview
2
+ module VersionInfoProviders
3
+ class ProviderDetector
4
+ class << self
5
+ def retrieve
6
+ GitProvider.new
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -24,4 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "bundler", "~> 1.16"
25
25
  spec.add_development_dependency "rake", "~> 10.0"
26
26
  spec.add_development_dependency "rspec", "~> 3.0"
27
+
28
+ spec.add_dependency "chunky_png", "~> 1.3.7"
29
+ spec.add_dependency "typhoeus", "~> 1.3"
27
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vizreview
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Felipe Correia
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-10-31 00:00:00.000000000 Z
11
+ date: 2018-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,34 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: chunky_png
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.7
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.3.7
69
+ - !ruby/object:Gem::Dependency
70
+ name: typhoeus
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.3'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.3'
55
83
  description:
56
84
  email:
57
85
  - info@vizreview.io
@@ -64,12 +92,35 @@ files:
64
92
  - ".travis.yml"
65
93
  - CODE_OF_CONDUCT.md
66
94
  - Gemfile
95
+ - Gemfile.lock
96
+ - LICENSE
67
97
  - README.md
68
98
  - Rakefile
69
99
  - bin/console
70
100
  - bin/setup
71
101
  - lib/vizreview.rb
102
+ - lib/vizreview/browser.rb
103
+ - lib/vizreview/browser/info_provider.rb
104
+ - lib/vizreview/browser/page_manager.rb
105
+ - lib/vizreview/client.rb
106
+ - lib/vizreview/configuration.rb
107
+ - lib/vizreview/image.rb
108
+ - lib/vizreview/logger.rb
109
+ - lib/vizreview/rectangle.rb
110
+ - lib/vizreview/screenshot_provider.rb
111
+ - lib/vizreview/service.rb
112
+ - lib/vizreview/service/api_connection.rb
113
+ - lib/vizreview/service/auth.rb
114
+ - lib/vizreview/service/connection.rb
115
+ - lib/vizreview/service/google_uploader.rb
116
+ - lib/vizreview/service/parallel_uploader.rb
117
+ - lib/vizreview/service/snapshot.rb
118
+ - lib/vizreview/service/version.rb
119
+ - lib/vizreview/snapshot.rb
120
+ - lib/vizreview/utils/git_utils.rb
72
121
  - lib/vizreview/version.rb
122
+ - lib/vizreview/version_info_providers/git_provider.rb
123
+ - lib/vizreview/version_info_providers/provider_detector.rb
73
124
  - vizreview.gemspec
74
125
  homepage: http://vizreview.io
75
126
  licenses: []