github.com/bazelbuild/rules_webtesting@v0.2.0/go/screenshotter/screenshotter.go (about) 1 // Copyright 2017 Google Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package screenshotter provides a client-side API for accessing the google/screenshot Web Test Launcher endpoint. 16 package screenshotter 17 18 import ( 19 "bytes" 20 "encoding/base64" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "image" 25 "image/png" 26 "io/ioutil" 27 "log" 28 "net/http" 29 "os" 30 "strings" 31 32 "github.com/tebeka/selenium" 33 ) 34 35 // Screenshotter allows configurable screenshotting. 36 type Screenshotter interface { 37 // Of returns a copy of Screenshotter configured to crop the screenshot using the given WebElement. 38 Of(selenium.WebElement) Screenshotter 39 // Excluding returns a copy of Screenshotter configured to exclude the given WebElement by covering it with a black box. 40 Excluding(selenium.WebElement) Screenshotter 41 // TakeScreenshot returns the screenshot. 42 TakeScreenshot() (Screenshot, error) 43 } 44 45 type screenshotter struct { 46 url string 47 Element selenium.WebElement 48 Exclude []selenium.WebElement 49 } 50 51 // Screenshot is a PNG-encoded screenshot. 52 type Screenshot []byte 53 54 // New creates a new Screenshotter. It looks up the address of the WebDriver remote end in 55 // the WEB_TEST_WEBDRIVER_SERVER environment variable, and use driver to get the session id 56 // that will be used for screenshotting. 57 func New(driver selenium.WebDriver) (Screenshotter, error) { 58 address, ok := os.LookupEnv("WEB_TEST_WEBDRIVER_SERVER") 59 if !ok { 60 return nil, errors.New(`environment variable "WEB_TEST_WEBDRIVER_SERVER" not set`) 61 } 62 return &screenshotter{ 63 url: fmt.Sprintf("%s/session/%s/google/screenshot", strings.TrimSuffix(address, "/"), driver.SessionID()), 64 }, nil 65 } 66 67 // Of returns a copy of Screenshotter configured to crop the screenshot using the given WebElement. 68 func (s *screenshotter) Of(e selenium.WebElement) Screenshotter { 69 log.Printf("e: %+v", e) 70 return &screenshotter{ 71 url: s.url, 72 Element: e, 73 Exclude: s.Exclude, 74 } 75 } 76 77 // Excluding returns a copy of Screenshotter configured to exclude the given WebElement by covering it with a black box. 78 func (s *screenshotter) Excluding(e selenium.WebElement) Screenshotter { 79 return &screenshotter{ 80 url: s.url, 81 Element: s.Element, 82 Exclude: append([]selenium.WebElement{e}, s.Exclude...), 83 } 84 } 85 86 // TakeScreenshot returns the screenshot. 87 func (s *screenshotter) TakeScreenshot() (Screenshot, error) { 88 body, err := json.Marshal(s) 89 if err != nil { 90 return nil, err 91 } 92 93 resp, err := http.Post(s.url, "application/json; charset=utf-8", bytes.NewReader(body)) 94 if err != nil { 95 return nil, err 96 } 97 b, _ := ioutil.ReadAll(resp.Body) 98 resp.Body.Close() 99 result := struct { 100 Status int `json:"status"` 101 Value interface{} `json:"value"` 102 Error string `json:"error"` 103 Message string `json:"message"` 104 }{} 105 106 if err := json.Unmarshal(b, &result); err != nil { 107 return nil, fmt.Errorf("error %v decoding json %q", err, b) 108 } 109 110 if result.Status != 0 || result.Error != "" { 111 msg := result.Message 112 if msg == "" { 113 msg, _ = result.Value.(string) 114 115 } 116 if msg == "" { 117 msg = fmt.Sprintf("server returned status %d error %q", result.Status, result.Error) 118 } 119 return nil, errors.New(msg) 120 } 121 122 v, ok := result.Value.(string) 123 if !ok { 124 return nil, fmt.Errorf("expected value to be string, but was %T", result.Value) 125 } 126 127 return base64.StdEncoding.DecodeString(v) 128 } 129 130 // AsImage converts s into an Image object. 131 func (s Screenshot) AsImage() (image.Image, error) { 132 return png.Decode(bytes.NewReader(s)) 133 }