Skip to content

Commit

Permalink
initial autogcd push
Browse files Browse the repository at this point in the history
define the structure of autogcd
  • Loading branch information
wirepair committed Sep 9, 2015
1 parent bda6ea0 commit 5ac9a96
Show file tree
Hide file tree
Showing 8 changed files with 663 additions and 0 deletions.
137 changes: 137 additions & 0 deletions autogcd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package autogcd

import (
"github.com/wirepair/gcd"
"sync"
)

type AutoGcd struct {
debugger *gcd.Gcd
settings *Settings
tabLock *sync.RWMutex
tabs map[string]*Tab
}

func NewAutoGcd(settings *Settings) *AutoGcd {
auto := &AutoGcd{settings: settings}
auto.tabLock = &sync.RWMutex{}
auto.tabs = make(map[string]*Tab)
auto.debugger = gcd.NewChromeDebugger()

if len(settings.extensions) > 0 {
auto.debugger.AddFlags(settings.extensions)
}

if len(settings.flags) > 0 {
auto.debugger.AddFlags(settings.flags)
}

if settings.timeout > 0 {
auto.debugger.SetTimeout(settings.timeout)
}

if settings.chromeHost != "" {
auto.debugger.SetHost(settings.chromeHost)
}

return auto
}

func (auto *AutoGcd) Start() error {
var tabs []*gcd.ChromeTarget
var err error
auto.debugger.StartProcess(auto.settings.chromePath, auto.settings.userDir, auto.settings.chromePort)

tabs, err = auto.debugger.GetTargets()
if err != nil {
return err
}

auto.tabLock.Lock()
for _, tab := range tabs {
auto.tabs[tab.Target.Id] = NewTab(tab)
}
auto.tabLock.Unlock()
return nil
}

// Returns the first "visual" tab.
func (auto *AutoGcd) GetTab() (*Tab, error) {
auto.tabLock.RLock()
defer auto.tabLock.RUnlock()
for _, tab := range auto.tabs {
if tab.Target.Type == "page" {
return tab, nil
}
}
return nil, &InvalidTabErr{Message: "no Page tab types found"}
}

// Returns a safe copy of tabs
func (auto *AutoGcd) GetAllTabs() map[string]*Tab {
auto.tabLock.RLock()
defer auto.tabLock.RUnlock()
tabs := make(map[string]*Tab)
for id, tab := range auto.tabs {
tabs[id] = tab
}
return tabs
}

// Activate the tab in the chrome UI
func (auto *AutoGcd) ActivateTab(tab *Tab) error {
return auto.debugger.ActivateTab(tab.ChromeTarget)
}

// Activate the tab in the chrome UI, by tab id
func (auto *AutoGcd) ActivateTabById(id string) error {
tab, err := auto.tabById(id)
if err != nil {
return err
}
return auto.ActivateTab(tab)
}

// Creates a new tab
func (auto *AutoGcd) NewTab() (*Tab, error) {
target, err := auto.debugger.NewTab()
if err != nil {
return nil, &InvalidTabErr{Message: "unable to create tab: " + err.Error()}
}
auto.tabLock.Lock()
defer auto.tabLock.Unlock()

tab := NewTab(target)
auto.tabs[target.Target.Id] = tab
return tab, nil
}

func (auto *AutoGcd) CloseTab(tab *Tab) error {
if err := auto.debugger.CloseTab(tab.ChromeTarget); err != nil {
return err
}
auto.tabLock.Lock()
defer auto.tabLock.Unlock()

delete(auto.tabs, tab.Target.Id)
return nil
}

func (auto *AutoGcd) CloseTabById(id string) error {
tab, err := auto.tabById(id)
if err != nil {
return err
}
auto.CloseTab(tab)
return nil
}

func (auto *AutoGcd) tabById(id string) (*Tab, error) {
auto.tabLock.RLock()
tab := auto.tabs[id]
auto.tabLock.RUnlock()
if tab == nil {
return nil, &InvalidTabErr{"unknown tab id " + id}
}
return tab, nil
}
109 changes: 109 additions & 0 deletions autogcd_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package autogcd

import (
"flag"
"os"
"testing"
)

var (
testPath string
testDir string
testPort string
)

func init() {
flag.StringVar(&testPath, "chrome", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe", "path to Xvfb")
flag.StringVar(&testDir, "dir", "C:\\temp\\", "user directory")
flag.StringVar(&testPort, "port", "9222", "Debugger port")
}

func TestMain(m *testing.M) {
flag.Parse()
ret := m.Run()
testCleanUp()
os.Exit(ret)
}

func testCleanUp() {

}

func TestStart(t *testing.T) {
s := NewSettings(testPath, testDir, testPort)
s.SetStartTimeout(15)
s.SetChromeHost("localhost")
auto := NewAutoGcd(s)
if err := auto.Start(); err != nil {
t.Fatalf("failed to start chrome: %s\n", err)
}
auto.debugger.ExitProcess()
}

func TestGetTab(t *testing.T) {
var err error
var tab *Tab
auto := testDefaultStartup(t)
tab, err = auto.GetTab()
if err != nil {
t.Fatalf("Error getting tab: %s\n", err)
}

if tab.Target.Type != "page" {
t.Fatalf("Got tab but wasn't of type Page")
}
auto.debugger.ExitProcess()
}

func TestNewTab(t *testing.T) {
var err error
//var newTab *Tab
auto := testDefaultStartup(t)
tabLen := len(auto.tabs)
_, err = auto.NewTab()
if err != nil {
t.Fatalf("error creating new tab: %s\n", err)
}

if tabLen+1 != len(auto.tabs) {
t.Fatalf("error created new tab but not reflected in our map")
}

auto.debugger.ExitProcess()
}

func TestCloseTab(t *testing.T) {
var err error
var newTab *Tab
auto := testDefaultStartup(t)
tabLen := len(auto.tabs)

newTab, err = auto.NewTab()
if err != nil {
t.Fatalf("error creating new tab: %s\n", err)
}

if tabLen+1 != len(auto.tabs) {
t.Fatalf("error created new tab but not reflected in our map")
}

err = auto.CloseTab(newTab)
if err != nil {
t.Fatalf("error closing tab")
}

if _, err := auto.tabById(newTab.Target.Id); err == nil {
t.Fatalf("error closed tab still in our map")
}

auto.debugger.ExitProcess()
}

func testDefaultStartup(t *testing.T) *AutoGcd {
s := NewSettings(testPath, testDir, testPort)
auto := NewAutoGcd(s)
if err := auto.Start(); err != nil {
t.Fatalf("failed to start chrome: %s\n", err)
}
return auto
}
69 changes: 69 additions & 0 deletions element.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package autogcd

import (
"github.com/wirepair/gcd/gcdprotogen/types"
)

type InvalidDimensionsErr struct {
Message string
}

func (e *InvalidDimensionsErr) Error() string {
return "invalid dimensions " + e.Message
}

type Element struct {
tab *Tab // reference to the containing tab
id *types.ChromeDOMNodeId // nodeId in chrome
}

func newElement(tab *Tab, id int) *Element {
nodeId := types.ChromeDOMNodeId(id)
e := &Element{}
e.tab = tab
e.id = &nodeId
return e
}

func (e *Element) Click() error {
var x int
var y int

points, err := e.Dimensions()
if err != nil {
return err
}

x, y, err = centroid(points)
if err != nil {
return err
}
// click the centroid of the element.
return e.tab.Click(x, y)
}

func (e *Element) Dimensions() ([]float64, error) {
var points []float64
box, err := e.tab.DOM().GetBoxModel(e.id)
if err != nil {
return nil, err
}
points = *box.Content
return points, nil
}

// finds the centroid of an arbitrary number of points.
// Assumes points[i] = x, points[i+1] = y.
func centroid(points []float64) (int, int, error) {
pointLen := len(points)
if pointLen%2 != 0 {
return -1, -1, &InvalidDimensionsErr{"number of points are not divisible by two"}
}
x := 0
y := 0
for i := 0; i < pointLen; i = i + 2 {
x += int(points[i])
y += int(points[i+1])
}
return x / pointLen, y / pointLen, nil
}
Empty file added element_test.go
Empty file.
42 changes: 42 additions & 0 deletions settings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package autogcd

import (
"fmt"
"time"
)

type Settings struct {
timeout time.Duration
chromePath string
chromeHost string
chromePort string
userDir string
extensions []string
flags []string
}

func NewSettings(chromePath, userDir, chromePort string) *Settings {
s := &Settings{}
s.chromePath = chromePath
s.chromePort = chromePort
s.userDir = userDir
s.extensions = make([]string, 0)
s.flags = make([]string, 0)
return s
}

func (s *Settings) SetChromeHost(host string) {
s.chromeHost = host
}

func (s *Settings) SetStartTimeout(timeout time.Duration) {
s.timeout = timeout
}

func (s *Settings) AddStartupFlags(flags []string) {
s.flags = append(s.flags, flags...)
}

func (s *Settings) AddExtension(path string) {
s.extensions = append(s.extensions, fmt.Sprintf("--load-extension=%s", path))
}
Loading

0 comments on commit 5ac9a96

Please sign in to comment.