2019-11-02 21:32:58 +07:00
|
|
|
package suites
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-11-19 08:50:34 +07:00
|
|
|
"encoding/json"
|
2019-11-25 03:27:59 +07:00
|
|
|
"errors"
|
2019-11-02 21:32:58 +07:00
|
|
|
"fmt"
|
2020-11-19 08:50:34 +07:00
|
|
|
"io/ioutil"
|
2019-11-02 21:32:58 +07:00
|
|
|
"os"
|
|
|
|
"strings"
|
2019-11-25 03:27:59 +07:00
|
|
|
"testing"
|
2020-11-19 08:50:34 +07:00
|
|
|
"time"
|
2019-11-02 21:32:58 +07:00
|
|
|
|
2020-12-16 08:47:31 +07:00
|
|
|
log "github.com/sirupsen/logrus"
|
2019-11-25 03:27:59 +07:00
|
|
|
"github.com/stretchr/testify/require"
|
2019-11-02 21:32:58 +07:00
|
|
|
"github.com/tebeka/selenium"
|
|
|
|
"github.com/tebeka/selenium/chrome"
|
|
|
|
)
|
|
|
|
|
|
|
|
// WebDriverSession binding a selenium service and a webdriver.
|
|
|
|
type WebDriverSession struct {
|
|
|
|
service *selenium.Service
|
|
|
|
WebDriver selenium.WebDriver
|
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// StartWebDriverWithProxy create a selenium session.
|
2019-11-25 03:27:59 +07:00
|
|
|
func StartWebDriverWithProxy(proxy string, port int) (*WebDriverSession, error) {
|
2020-06-19 18:25:41 +07:00
|
|
|
driverPath := os.Getenv("CHROMEDRIVER_PATH")
|
|
|
|
if driverPath == "" {
|
|
|
|
driverPath = "/usr/bin/chromedriver"
|
|
|
|
}
|
|
|
|
|
|
|
|
service, err := selenium.NewChromeDriverService(driverPath, port)
|
2019-11-02 21:32:58 +07:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-03-05 06:25:52 +07:00
|
|
|
browserPath := os.Getenv("BROWSER_PATH")
|
|
|
|
if browserPath == "" {
|
|
|
|
browserPath = "/usr/bin/chromium-browser"
|
|
|
|
}
|
|
|
|
|
2019-11-02 21:32:58 +07:00
|
|
|
chromeCaps := chrome.Capabilities{
|
2020-03-05 06:25:52 +07:00
|
|
|
Path: browserPath,
|
2019-11-02 21:32:58 +07:00
|
|
|
}
|
|
|
|
|
2020-01-18 04:06:33 +07:00
|
|
|
chromeCaps.Args = append(chromeCaps.Args, "--ignore-certificate-errors")
|
|
|
|
|
2019-11-02 21:32:58 +07:00
|
|
|
if os.Getenv("HEADLESS") != "" {
|
|
|
|
chromeCaps.Args = append(chromeCaps.Args, "--headless")
|
2019-12-27 18:07:53 +07:00
|
|
|
chromeCaps.Args = append(chromeCaps.Args, "--no-sandbox")
|
2019-11-02 21:32:58 +07:00
|
|
|
}
|
|
|
|
|
2019-11-25 03:27:59 +07:00
|
|
|
if proxy != "" {
|
|
|
|
chromeCaps.Args = append(chromeCaps.Args, fmt.Sprintf("--proxy-server=%s", proxy))
|
|
|
|
}
|
|
|
|
|
2019-11-02 21:32:58 +07:00
|
|
|
caps := selenium.Capabilities{}
|
|
|
|
caps.AddChrome(chromeCaps)
|
|
|
|
|
|
|
|
wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port))
|
|
|
|
if err != nil {
|
2020-12-16 08:47:31 +07:00
|
|
|
_ = service.Stop()
|
|
|
|
|
|
|
|
log.Fatal(err)
|
2019-11-02 21:32:58 +07:00
|
|
|
}
|
|
|
|
|
|
|
|
return &WebDriverSession{
|
|
|
|
service: service,
|
|
|
|
WebDriver: wd,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// StartWebDriver create a selenium session.
|
2019-11-25 03:27:59 +07:00
|
|
|
func StartWebDriver() (*WebDriverSession, error) {
|
2020-11-28 07:06:42 +07:00
|
|
|
return StartWebDriverWithProxy("", GetWebDriverPort())
|
2019-11-25 03:27:59 +07:00
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// Stop stop the selenium session.
|
2019-11-02 21:32:58 +07:00
|
|
|
func (wds *WebDriverSession) Stop() error {
|
2020-11-19 08:50:34 +07:00
|
|
|
var coverage map[string]interface{}
|
2019-11-02 21:32:58 +07:00
|
|
|
|
2020-11-19 08:50:34 +07:00
|
|
|
coverageDir := "../../web/.nyc_output"
|
|
|
|
time := time.Now()
|
|
|
|
|
|
|
|
resp, err := wds.WebDriver.ExecuteScriptRaw("return JSON.stringify(window.__coverage__)", nil)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(resp, &coverage)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
coverageData := fmt.Sprintf("%s", coverage["value"])
|
|
|
|
|
|
|
|
_ = os.MkdirAll(coverageDir, 0775)
|
|
|
|
|
|
|
|
err = ioutil.WriteFile(fmt.Sprintf("%s/coverage-%d.json", coverageDir, time.Unix()), []byte(coverageData), 0664) //nolint:gosec
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
err = wds.WebDriver.Quit()
|
2019-11-02 21:32:58 +07:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return wds.service.Stop()
|
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// WithWebdriver run some actions against a webdriver.
|
2019-11-02 21:32:58 +07:00
|
|
|
func WithWebdriver(fn func(webdriver selenium.WebDriver) error) error {
|
|
|
|
wds, err := StartWebDriver()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-04-22 10:33:14 +07:00
|
|
|
defer wds.Stop() //nolint:errcheck // TODO: Legacy code, consider refactoring time permitting.
|
2019-11-02 21:32:58 +07:00
|
|
|
|
|
|
|
return fn(wds.WebDriver)
|
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// Wait wait until condition holds true.
|
2019-11-25 03:27:59 +07:00
|
|
|
func (wds *WebDriverSession) Wait(ctx context.Context, condition selenium.Condition) error {
|
|
|
|
done := make(chan error, 1)
|
2020-05-06 02:35:32 +07:00
|
|
|
|
2019-11-25 03:27:59 +07:00
|
|
|
go func() {
|
|
|
|
done <- wds.WebDriver.Wait(condition)
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-ctx.Done():
|
|
|
|
return errors.New("waiting timeout reached")
|
|
|
|
case err := <-done:
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wds *WebDriverSession) waitElementLocated(ctx context.Context, t *testing.T, by, value string) selenium.WebElement {
|
2019-11-02 21:32:58 +07:00
|
|
|
var el selenium.WebElement
|
2020-05-06 02:35:32 +07:00
|
|
|
|
2019-11-25 03:27:59 +07:00
|
|
|
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
2019-11-02 21:32:58 +07:00
|
|
|
var err error
|
|
|
|
el, err = driver.FindElement(by, value)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if strings.Contains(err.Error(), "no such element") {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return el != nil, nil
|
|
|
|
})
|
|
|
|
|
2019-11-25 03:27:59 +07:00
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, el)
|
2020-05-06 02:35:32 +07:00
|
|
|
|
2019-11-25 03:27:59 +07:00
|
|
|
return el
|
|
|
|
}
|
|
|
|
|
|
|
|
func (wds *WebDriverSession) waitElementsLocated(ctx context.Context, t *testing.T, by, value string) []selenium.WebElement {
|
|
|
|
var el []selenium.WebElement
|
2020-05-06 02:35:32 +07:00
|
|
|
|
2019-11-25 03:27:59 +07:00
|
|
|
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
|
|
|
var err error
|
|
|
|
el, err = driver.FindElements(by, value)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if strings.Contains(err.Error(), "no such element") {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return el != nil, nil
|
|
|
|
})
|
|
|
|
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.NotNil(t, el)
|
2020-05-06 02:35:32 +07:00
|
|
|
|
2019-11-02 21:32:58 +07:00
|
|
|
return el
|
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// WaitElementLocatedByID wait an element is located by id.
|
2019-11-25 03:27:59 +07:00
|
|
|
func (wds *WebDriverSession) WaitElementLocatedByID(ctx context.Context, t *testing.T, id string) selenium.WebElement {
|
|
|
|
return wds.waitElementLocated(ctx, t, selenium.ByID, id)
|
2019-11-02 21:32:58 +07:00
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// WaitElementLocatedByTagName wait an element is located by tag name.
|
2019-11-25 03:27:59 +07:00
|
|
|
func (wds *WebDriverSession) WaitElementLocatedByTagName(ctx context.Context, t *testing.T, tagName string) selenium.WebElement {
|
|
|
|
return wds.waitElementLocated(ctx, t, selenium.ByTagName, tagName)
|
2019-11-02 21:32:58 +07:00
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// WaitElementLocatedByClassName wait an element is located by class name.
|
2019-11-25 03:27:59 +07:00
|
|
|
func (wds *WebDriverSession) WaitElementLocatedByClassName(ctx context.Context, t *testing.T, className string) selenium.WebElement {
|
|
|
|
return wds.waitElementLocated(ctx, t, selenium.ByClassName, className)
|
2019-11-02 21:32:58 +07:00
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// WaitElementLocatedByLinkText wait an element is located by link text.
|
2019-11-25 03:27:59 +07:00
|
|
|
func (wds *WebDriverSession) WaitElementLocatedByLinkText(ctx context.Context, t *testing.T, linkText string) selenium.WebElement {
|
|
|
|
return wds.waitElementLocated(ctx, t, selenium.ByLinkText, linkText)
|
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// WaitElementLocatedByCSSSelector wait an element is located by class name.
|
2019-11-25 03:27:59 +07:00
|
|
|
func (wds *WebDriverSession) WaitElementLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) selenium.WebElement {
|
|
|
|
return wds.waitElementLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
|
|
|
|
}
|
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// WaitElementsLocatedByCSSSelector wait an element is located by CSS selector.
|
2019-11-25 03:27:59 +07:00
|
|
|
func (wds *WebDriverSession) WaitElementsLocatedByCSSSelector(ctx context.Context, t *testing.T, cssSelector string) []selenium.WebElement {
|
|
|
|
return wds.waitElementsLocated(ctx, t, selenium.ByCSSSelector, cssSelector)
|
|
|
|
}
|
2019-11-02 21:32:58 +07:00
|
|
|
|
2020-05-02 12:06:39 +07:00
|
|
|
// WaitElementTextContains wait the text of an element contains a pattern.
|
2019-11-25 03:27:59 +07:00
|
|
|
func (wds *WebDriverSession) WaitElementTextContains(ctx context.Context, t *testing.T, element selenium.WebElement, pattern string) {
|
|
|
|
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
2019-11-02 21:32:58 +07:00
|
|
|
text, err := element.Text()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Contains(text, pattern), nil
|
|
|
|
})
|
2019-11-25 03:27:59 +07:00
|
|
|
require.NoError(t, err)
|
2019-11-02 21:32:58 +07:00
|
|
|
}
|
2021-05-05 05:06:05 +07:00
|
|
|
|
|
|
|
func (wds *WebDriverSession) waitBodyContains(ctx context.Context, t *testing.T, pattern string) {
|
|
|
|
err := wds.Wait(ctx, func(driver selenium.WebDriver) (bool, error) {
|
|
|
|
text, err := wds.WaitElementLocatedByTagName(ctx, t, "body").Text()
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return strings.Contains(text, pattern), nil
|
|
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
|
|
}
|