1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-06-23 00:28:10 +02:00

Added Context to HTML methods (#235)

* Added Context to HTML methods

* Fixed unit tests

* Updated timeout

* Fixed WAIT_CLASS timeout
This commit is contained in:
Tim Voronov
2019-02-20 21:24:05 -05:00
committed by GitHub
parent 34c8c02258
commit 6e15846d0f
45 changed files with 415 additions and 446 deletions

View File

@ -53,7 +53,20 @@ func New(logger zerolog.Logger, settings Settings) *Runner {
} }
func (r *Runner) Run() error { func (r *Runner) Run() error {
results, err := r.runQueries(r.settings.Dir) ctx := context.Background()
ctx = drivers.WithContext(
ctx,
cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress)),
)
ctx = drivers.WithContext(
ctx,
http.NewDriver(),
drivers.AsDefault(),
)
results, err := r.runQueries(ctx, r.settings.Dir)
if err != nil { if err != nil {
return err return err
@ -83,7 +96,7 @@ func (r *Runner) Run() error {
return nil return nil
} }
func (r *Runner) runQueries(dir string) ([]Result, error) { func (r *Runner) runQueries(ctx context.Context, dir string) ([]Result, error) {
files, err := ioutil.ReadDir(dir) files, err := ioutil.ReadDir(dir)
if err != nil { if err != nil {
@ -126,13 +139,30 @@ func (r *Runner) runQueries(dir string) ([]Result, error) {
continue continue
} }
results = append(results, r.runQuery(c, fName, string(b))) r.logger.Info().Timestamp().Str("name", fName).Msg("Running test")
result := r.runQuery(ctx, c, fName, string(b))
if result.err == nil {
r.logger.Info().
Timestamp().
Str("file", result.name).
Msg("Test passed")
} else {
r.logger.Error().
Timestamp().
Err(result.err).
Str("file", result.name).
Msg("Test failed")
}
results = append(results, result)
} }
return results, nil return results, nil
} }
func (r *Runner) runQuery(c *compiler.FqlCompiler, name, script string) Result { func (r *Runner) runQuery(ctx context.Context, c *compiler.FqlCompiler, name, script string) Result {
start := time.Now() start := time.Now()
p, err := c.Compile(script) p, err := c.Compile(script)
@ -145,20 +175,6 @@ func (r *Runner) runQuery(c *compiler.FqlCompiler, name, script string) Result {
} }
} }
ctx := context.Background()
ctx = drivers.WithContext(
ctx,
cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress)),
)
ctx = drivers.WithContext(
ctx,
http.NewDriver(),
drivers.AsDefault(),
)
r.logger.Info().Timestamp().Str("name", name).Msg("Running test")
out, err := p.Run( out, err := p.Run(
ctx, ctx,
runtime.WithLog(zerolog.ConsoleWriter{Out: os.Stdout}), runtime.WithLog(zerolog.ConsoleWriter{Out: os.Stdout}),
@ -207,20 +223,9 @@ func (r *Runner) report(results []Result) Summary {
for _, res := range results { for _, res := range results {
if res.err != nil { if res.err != nil {
r.logger.Error().
Timestamp().
Err(res.err).
Str("file", res.name).
Dur("time", res.duration).
Msg("Test failed")
failed++ failed++
} else { } else {
r.logger.Info().
Timestamp().
Str("file", res.name).
Dur("time", res.duration).
Msg("Test passed")
passed++ passed++
} }

View File

@ -273,32 +273,32 @@ func (doc *HTMLDocument) Length() values.Int {
return doc.element.Length() return doc.element.Length()
} }
func (doc *HTMLDocument) GetChildNodes() core.Value { func (doc *HTMLDocument) GetChildNodes(ctx context.Context) core.Value {
doc.Lock() doc.Lock()
defer doc.Unlock() defer doc.Unlock()
return doc.element.GetChildNodes() return doc.element.GetChildNodes(ctx)
} }
func (doc *HTMLDocument) GetChildNode(idx values.Int) core.Value { func (doc *HTMLDocument) GetChildNode(ctx context.Context, idx values.Int) core.Value {
doc.Lock() doc.Lock()
defer doc.Unlock() defer doc.Unlock()
return doc.element.GetChildNode(idx) return doc.element.GetChildNode(ctx, idx)
} }
func (doc *HTMLDocument) QuerySelector(selector values.String) core.Value { func (doc *HTMLDocument) QuerySelector(ctx context.Context, selector values.String) core.Value {
doc.Lock() doc.Lock()
defer doc.Unlock() defer doc.Unlock()
return doc.element.QuerySelector(selector) return doc.element.QuerySelector(ctx, selector)
} }
func (doc *HTMLDocument) QuerySelectorAll(selector values.String) core.Value { func (doc *HTMLDocument) QuerySelectorAll(ctx context.Context, selector values.String) core.Value {
doc.Lock() doc.Lock()
defer doc.Unlock() defer doc.Unlock()
return doc.element.QuerySelectorAll(selector) return doc.element.QuerySelectorAll(ctx, selector)
} }
func (doc *HTMLDocument) DocumentElement() drivers.HTMLElement { func (doc *HTMLDocument) DocumentElement() drivers.HTMLElement {
@ -315,26 +315,27 @@ func (doc *HTMLDocument) GetURL() core.Value {
return doc.url return doc.url
} }
func (doc *HTMLDocument) SetURL(url values.String) error { func (doc *HTMLDocument) SetURL(ctx context.Context, url values.String) error {
return doc.Navigate(url, values.Int(DefaultTimeout)) return doc.Navigate(ctx, url)
} }
func (doc *HTMLDocument) CountBySelector(selector values.String) values.Int { func (doc *HTMLDocument) CountBySelector(ctx context.Context, selector values.String) values.Int {
doc.Lock() doc.Lock()
defer doc.Unlock() defer doc.Unlock()
return doc.element.CountBySelector(selector) return doc.element.CountBySelector(ctx, selector)
} }
func (doc *HTMLDocument) ExistsBySelector(selector values.String) values.Boolean { func (doc *HTMLDocument) ExistsBySelector(ctx context.Context, selector values.String) values.Boolean {
doc.Lock() doc.Lock()
defer doc.Unlock() defer doc.Unlock()
return doc.element.ExistsBySelector(selector) return doc.element.ExistsBySelector(ctx, selector)
} }
func (doc *HTMLDocument) ClickBySelector(selector values.String) (values.Boolean, error) { func (doc *HTMLDocument) ClickBySelector(ctx context.Context, selector values.String) (values.Boolean, error) {
res, err := eval.Eval( res, err := eval.Eval(
ctx,
doc.client, doc.client,
fmt.Sprintf(` fmt.Sprintf(`
var el = document.querySelector(%s); var el = document.querySelector(%s);
@ -360,8 +361,9 @@ func (doc *HTMLDocument) ClickBySelector(selector values.String) (values.Boolean
return values.False, nil return values.False, nil
} }
func (doc *HTMLDocument) ClickBySelectorAll(selector values.String) (values.Boolean, error) { func (doc *HTMLDocument) ClickBySelectorAll(ctx context.Context, selector values.String) (values.Boolean, error) {
res, err := eval.Eval( res, err := eval.Eval(
ctx,
doc.client, doc.client,
fmt.Sprintf(` fmt.Sprintf(`
var elements = document.querySelectorAll(%s); var elements = document.querySelectorAll(%s);
@ -389,12 +391,11 @@ func (doc *HTMLDocument) ClickBySelectorAll(selector values.String) (values.Bool
return values.False, nil return values.False, nil
} }
func (doc *HTMLDocument) InputBySelector(selector values.String, value core.Value, delay values.Int) (values.Boolean, error) { func (doc *HTMLDocument) InputBySelector(ctx context.Context, selector values.String, value core.Value, delay values.Int) (values.Boolean, error) {
ctx := context.Background()
valStr := value.String() valStr := value.String()
res, err := eval.Eval( res, err := eval.Eval(
ctx,
doc.client, doc.client,
fmt.Sprintf(` fmt.Sprintf(`
var el = document.querySelector(%s); var el = document.querySelector(%s);
@ -423,9 +424,11 @@ func (doc *HTMLDocument) InputBySelector(selector values.String, value core.Valu
for _, ch := range valStr { for _, ch := range valStr {
for _, ev := range []string{"keyDown", "keyUp"} { for _, ev := range []string{"keyDown", "keyUp"} {
ke := input.NewDispatchKeyEventArgs(ev).SetText(string(ch)) ke := input.NewDispatchKeyEventArgs(ev).SetText(string(ch))
if err := doc.client.Input.DispatchKeyEvent(ctx, ke); err != nil { if err := doc.client.Input.DispatchKeyEvent(ctx, ke); err != nil {
return values.False, err return values.False, err
} }
time.Sleep(delayMs * time.Millisecond) time.Sleep(delayMs * time.Millisecond)
} }
} }
@ -433,8 +436,9 @@ func (doc *HTMLDocument) InputBySelector(selector values.String, value core.Valu
return values.True, nil return values.True, nil
} }
func (doc *HTMLDocument) SelectBySelector(selector values.String, value *values.Array) (*values.Array, error) { func (doc *HTMLDocument) SelectBySelector(ctx context.Context, selector values.String, value *values.Array) (*values.Array, error) {
res, err := eval.Eval( res, err := eval.Eval(
ctx,
doc.client, doc.client,
fmt.Sprintf(` fmt.Sprintf(`
var element = document.querySelector(%s); var element = document.querySelector(%s);
@ -479,11 +483,8 @@ func (doc *HTMLDocument) SelectBySelector(selector values.String, value *values.
return nil, core.TypeError(types.Array, res.Type()) return nil, core.TypeError(types.Array, res.Type())
} }
func (doc *HTMLDocument) HoverBySelector(selector values.String) error { func (doc *HTMLDocument) HoverBySelector(ctx context.Context, selector values.String) error {
ctx, cancel := contextWithTimeout() err := doc.ScrollBySelector(ctx, selector)
defer cancel()
err := doc.ScrollBySelector(selector)
if err != nil { if err != nil {
return err return err
@ -518,7 +519,7 @@ func (doc *HTMLDocument) HoverBySelector(selector values.String) error {
) )
} }
func (doc *HTMLDocument) WaitForSelector(selector values.String, timeout values.Int) error { func (doc *HTMLDocument) WaitForSelector(ctx context.Context, selector values.String) error {
task := events.NewEvalWaitTask( task := events.NewEvalWaitTask(
doc.client, doc.client,
fmt.Sprintf(` fmt.Sprintf(`
@ -529,16 +530,15 @@ func (doc *HTMLDocument) WaitForSelector(selector values.String, timeout values.
// null means we need to repeat // null means we need to repeat
return null; return null;
`, eval.ParamString(selector.String())), `, eval.ParamString(selector.String())),
time.Millisecond*time.Duration(timeout),
events.DefaultPolling, events.DefaultPolling,
) )
_, err := task.Run() _, err := task.Run(ctx)
return err return err
} }
func (doc *HTMLDocument) WaitForClassBySelector(selector, class values.String, timeout values.Int) error { func (doc *HTMLDocument) WaitForClassBySelector(ctx context.Context, selector, class values.String) error {
task := events.NewEvalWaitTask( task := events.NewEvalWaitTask(
doc.client, doc.client,
fmt.Sprintf(` fmt.Sprintf(`
@ -558,16 +558,15 @@ func (doc *HTMLDocument) WaitForClassBySelector(selector, class values.String, t
eval.ParamString(selector.String()), eval.ParamString(selector.String()),
eval.ParamString(class.String()), eval.ParamString(class.String()),
), ),
time.Millisecond*time.Duration(timeout),
events.DefaultPolling, events.DefaultPolling,
) )
_, err := task.Run() _, err := task.Run(ctx)
return err return err
} }
func (doc *HTMLDocument) WaitForClassBySelectorAll(selector, class values.String, timeout values.Int) error { func (doc *HTMLDocument) WaitForClassBySelectorAll(ctx context.Context, selector, class values.String) error {
task := events.NewEvalWaitTask( task := events.NewEvalWaitTask(
doc.client, doc.client,
fmt.Sprintf(` fmt.Sprintf(`
@ -593,23 +592,17 @@ func (doc *HTMLDocument) WaitForClassBySelectorAll(selector, class values.String
eval.ParamString(selector.String()), eval.ParamString(selector.String()),
eval.ParamString(class.String()), eval.ParamString(class.String()),
), ),
time.Millisecond*time.Duration(timeout),
events.DefaultPolling, events.DefaultPolling,
) )
_, err := task.Run() _, err := task.Run(ctx)
return err return err
} }
func (doc *HTMLDocument) WaitForNavigation(timeout values.Int) error { func (doc *HTMLDocument) WaitForNavigation(ctx context.Context) error {
// do not wait
if timeout == 0 {
return nil
}
onEvent := make(chan struct{}) onEvent := make(chan struct{})
listener := func(_ interface{}) { listener := func(_ context.Context, _ interface{}) {
close(onEvent) close(onEvent)
} }
@ -620,17 +613,16 @@ func (doc *HTMLDocument) WaitForNavigation(timeout values.Int) error {
select { select {
case <-onEvent: case <-onEvent:
return nil return nil
case <-time.After(time.Millisecond * time.Duration(timeout)): case <-ctx.Done():
return core.ErrTimeout return core.ErrTimeout
} }
} }
func (doc *HTMLDocument) Navigate(url values.String, timeout values.Int) error { func (doc *HTMLDocument) Navigate(ctx context.Context, url values.String) error {
if url == "" { if url == "" {
url = BlankPageURL url = BlankPageURL
} }
ctx := context.Background()
repl, err := doc.client.Page.Navigate(ctx, page.NewNavigateArgs(url.String())) repl, err := doc.client.Page.Navigate(ctx, page.NewNavigateArgs(url.String()))
if err != nil { if err != nil {
@ -641,11 +633,10 @@ func (doc *HTMLDocument) Navigate(url values.String, timeout values.Int) error {
return errors.New(*repl.ErrorText) return errors.New(*repl.ErrorText)
} }
return doc.WaitForNavigation(timeout) return doc.WaitForNavigation(ctx)
} }
func (doc *HTMLDocument) NavigateBack(skip values.Int, timeout values.Int) (values.Boolean, error) { func (doc *HTMLDocument) NavigateBack(ctx context.Context, skip values.Int) (values.Boolean, error) {
ctx := context.Background()
history, err := doc.client.Page.GetNavigationHistory(ctx) history, err := doc.client.Page.GetNavigationHistory(ctx)
if err != nil { if err != nil {
@ -675,7 +666,7 @@ func (doc *HTMLDocument) NavigateBack(skip values.Int, timeout values.Int) (valu
return values.False, err return values.False, err
} }
err = doc.WaitForNavigation(timeout) err = doc.WaitForNavigation(ctx)
if err != nil { if err != nil {
return values.False, err return values.False, err
@ -684,8 +675,7 @@ func (doc *HTMLDocument) NavigateBack(skip values.Int, timeout values.Int) (valu
return values.True, nil return values.True, nil
} }
func (doc *HTMLDocument) NavigateForward(skip values.Int, timeout values.Int) (values.Boolean, error) { func (doc *HTMLDocument) NavigateForward(ctx context.Context, skip values.Int) (values.Boolean, error) {
ctx := context.Background()
history, err := doc.client.Page.GetNavigationHistory(ctx) history, err := doc.client.Page.GetNavigationHistory(ctx)
if err != nil { if err != nil {
@ -718,7 +708,7 @@ func (doc *HTMLDocument) NavigateForward(skip values.Int, timeout values.Int) (v
return values.False, err return values.False, err
} }
err = doc.WaitForNavigation(timeout) err = doc.WaitForNavigation(ctx)
if err != nil { if err != nil {
return values.False, err return values.False, err
@ -727,9 +717,7 @@ func (doc *HTMLDocument) NavigateForward(skip values.Int, timeout values.Int) (v
return values.True, nil return values.True, nil
} }
func (doc *HTMLDocument) PrintToPDF(params drivers.PDFParams) (values.Binary, error) { func (doc *HTMLDocument) PrintToPDF(ctx context.Context, params drivers.PDFParams) (values.Binary, error) {
ctx := context.Background()
args := page.NewPrintToPDFArgs() args := page.NewPrintToPDFArgs()
args. args.
SetLandscape(bool(params.Landscape)). SetLandscape(bool(params.Landscape)).
@ -787,8 +775,7 @@ func (doc *HTMLDocument) PrintToPDF(params drivers.PDFParams) (values.Binary, er
return values.NewBinary(reply.Data), nil return values.NewBinary(reply.Data), nil
} }
func (doc *HTMLDocument) CaptureScreenshot(params drivers.ScreenshotParams) (values.Binary, error) { func (doc *HTMLDocument) CaptureScreenshot(ctx context.Context, params drivers.ScreenshotParams) (values.Binary, error) {
ctx := context.Background()
metrics, err := doc.client.Page.GetLayoutMetrics(ctx) metrics, err := doc.client.Page.GetLayoutMetrics(ctx)
if params.Format == drivers.ScreenshotFormatJPEG && params.Quality < 0 && params.Quality > 100 { if params.Format == drivers.ScreenshotFormatJPEG && params.Quality < 0 && params.Quality > 100 {
@ -836,8 +823,8 @@ func (doc *HTMLDocument) CaptureScreenshot(params drivers.ScreenshotParams) (val
return values.NewBinary(reply.Data), nil return values.NewBinary(reply.Data), nil
} }
func (doc *HTMLDocument) ScrollTop() error { func (doc *HTMLDocument) ScrollTop(ctx context.Context) error {
_, err := eval.Eval(doc.client, ` _, err := eval.Eval(ctx, doc.client, `
window.scrollTo({ window.scrollTo({
left: 0, left: 0,
top: 0, top: 0,
@ -848,8 +835,8 @@ func (doc *HTMLDocument) ScrollTop() error {
return err return err
} }
func (doc *HTMLDocument) ScrollBottom() error { func (doc *HTMLDocument) ScrollBottom(ctx context.Context) error {
_, err := eval.Eval(doc.client, ` _, err := eval.Eval(ctx, doc.client, `
window.scrollTo({ window.scrollTo({
left: 0, left: 0,
top: window.document.body.scrollHeight, top: window.document.body.scrollHeight,
@ -860,8 +847,8 @@ func (doc *HTMLDocument) ScrollBottom() error {
return err return err
} }
func (doc *HTMLDocument) ScrollBySelector(selector values.String) error { func (doc *HTMLDocument) ScrollBySelector(ctx context.Context, selector values.String) error {
_, err := eval.Eval(doc.client, fmt.Sprintf(` _, err := eval.Eval(ctx, doc.client, fmt.Sprintf(`
var el = document.querySelector(%s); var el = document.querySelector(%s);
if (el == null) { if (el == null) {
throw new Error("element not found"); throw new Error("element not found");
@ -876,13 +863,10 @@ func (doc *HTMLDocument) ScrollBySelector(selector values.String) error {
return err return err
} }
func (doc *HTMLDocument) handlePageLoad(_ interface{}) { func (doc *HTMLDocument) handlePageLoad(ctx context.Context, _ interface{}) {
doc.Lock() doc.Lock()
defer doc.Unlock() defer doc.Unlock()
ctx, cancel := contextWithTimeout()
defer cancel()
node, err := getRootElement(ctx, doc.client) node, err := getRootElement(ctx, doc.client)
if err != nil { if err != nil {
@ -924,7 +908,7 @@ func (doc *HTMLDocument) handlePageLoad(_ interface{}) {
} }
} }
func (doc *HTMLDocument) handleError(val interface{}) { func (doc *HTMLDocument) handleError(_ context.Context, val interface{}) {
err, ok := val.(error) err, ok := val.(error)
if !ok { if !ok {

View File

@ -25,8 +25,6 @@ import (
"github.com/rs/zerolog" "github.com/rs/zerolog"
) )
const DefaultTimeout = time.Second * 30
var emptyNodeID = dom.NodeID(0) var emptyNodeID = dom.NodeID(0)
var emptyBackendID = dom.BackendNodeID(0) var emptyBackendID = dom.BackendNodeID(0)
@ -201,7 +199,7 @@ func (el *HTMLElement) Type() core.Type {
} }
func (el *HTMLElement) MarshalJSON() ([]byte, error) { func (el *HTMLElement) MarshalJSON() ([]byte, error) {
val, err := el.innerText.Read() val, err := el.innerText.Read(context.Background())
if err != nil { if err != nil {
return nil, err return nil, err
@ -211,7 +209,7 @@ func (el *HTMLElement) MarshalJSON() ([]byte, error) {
} }
func (el *HTMLElement) String() string { func (el *HTMLElement) String() string {
return el.InnerHTML().String() return el.InnerHTML(context.Background()).String()
} }
func (el *HTMLElement) Compare(other core.Value) int64 { func (el *HTMLElement) Compare(other core.Value) int64 {
@ -219,7 +217,9 @@ func (el *HTMLElement) Compare(other core.Value) int64 {
case drivers.HTMLElementType: case drivers.HTMLElementType:
other := other.(drivers.HTMLElement) other := other.(drivers.HTMLElement)
return el.InnerHTML().Compare(other.InnerHTML()) ctx := context.Background()
return el.InnerHTML(ctx).Compare(other.InnerHTML(ctx))
default: default:
return drivers.Compare(el.Type(), other.Type()) return drivers.Compare(el.Type(), other.Type())
} }
@ -258,14 +258,11 @@ func (el *HTMLElement) SetIn(ctx context.Context, path []core.Value, value core.
return common.SetInElement(ctx, el, path, value) return common.SetInElement(ctx, el, path, value)
} }
func (el *HTMLElement) GetValue() core.Value { func (el *HTMLElement) GetValue(ctx context.Context) core.Value {
if !el.IsConnected() { if !el.IsConnected() {
return el.value return el.value
} }
ctx, cancel := contextWithTimeout()
defer cancel()
val, err := eval.Property(ctx, el.client, el.id.objectID, "value") val, err := eval.Property(ctx, el.client, el.id.objectID, "value")
if err != nil { if err != nil {
@ -279,15 +276,12 @@ func (el *HTMLElement) GetValue() core.Value {
return val return val
} }
func (el *HTMLElement) SetValue(value core.Value) error { func (el *HTMLElement) SetValue(ctx context.Context, value core.Value) error {
if !el.IsConnected() { if !el.IsConnected() {
// TODO: Return an error // TODO: Return an error
return nil return nil
} }
ctx, cancel := contextWithTimeout()
defer cancel()
return el.client.DOM.SetNodeValue(ctx, dom.NewSetNodeValueArgs(el.id.nodeID, value.String())) return el.client.DOM.SetNodeValue(ctx, dom.NewSetNodeValueArgs(el.id.nodeID, value.String()))
} }
@ -303,8 +297,8 @@ func (el *HTMLElement) Length() values.Int {
return values.NewInt(len(el.children)) return values.NewInt(len(el.children))
} }
func (el *HTMLElement) GetAttributes() core.Value { func (el *HTMLElement) GetAttributes(ctx context.Context) core.Value {
val, err := el.attributes.Read() val, err := el.attributes.Read(ctx)
if err != nil { if err != nil {
return values.None return values.None
@ -314,8 +308,8 @@ func (el *HTMLElement) GetAttributes() core.Value {
return val.Copy() return val.Copy()
} }
func (el *HTMLElement) GetAttribute(name values.String) core.Value { func (el *HTMLElement) GetAttribute(ctx context.Context, name values.String) core.Value {
attrs, err := el.attributes.Read() attrs, err := el.attributes.Read(ctx)
if err != nil { if err != nil {
return values.None return values.None
@ -330,15 +324,15 @@ func (el *HTMLElement) GetAttribute(name values.String) core.Value {
return val return val
} }
func (el *HTMLElement) SetAttribute(name, value values.String) error { func (el *HTMLElement) SetAttribute(ctx context.Context, name, value values.String) error {
return el.client.DOM.SetAttributeValue( return el.client.DOM.SetAttributeValue(
context.Background(), ctx,
dom.NewSetAttributeValueArgs(el.id.nodeID, string(name), string(value)), dom.NewSetAttributeValueArgs(el.id.nodeID, string(name), string(value)),
) )
} }
func (el *HTMLElement) GetChildNodes() core.Value { func (el *HTMLElement) GetChildNodes(ctx context.Context) core.Value {
val, err := el.loadedChildren.Read() val, err := el.loadedChildren.Read(ctx)
if err != nil { if err != nil {
return values.NewArray(0) return values.NewArray(0)
@ -347,9 +341,8 @@ func (el *HTMLElement) GetChildNodes() core.Value {
return val return val
} }
func (el *HTMLElement) GetChildNode(idx values.Int) core.Value { func (el *HTMLElement) GetChildNode(ctx context.Context, idx values.Int) core.Value {
// TODO: Add lazy loading val, err := el.loadedChildren.Read(ctx)
val, err := el.loadedChildren.Read()
if err != nil { if err != nil {
return values.None return values.None
@ -358,14 +351,11 @@ func (el *HTMLElement) GetChildNode(idx values.Int) core.Value {
return val.(*values.Array).Get(idx) return val.(*values.Array).Get(idx)
} }
func (el *HTMLElement) QuerySelector(selector values.String) core.Value { func (el *HTMLElement) QuerySelector(ctx context.Context, selector values.String) core.Value {
if !el.IsConnected() { if !el.IsConnected() {
return values.None return values.None
} }
ctx, cancel := contextWithTimeout()
defer cancel()
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId? // TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
selectorArgs := dom.NewQuerySelectorArgs(el.id.nodeID, selector.String()) selectorArgs := dom.NewQuerySelectorArgs(el.id.nodeID, selector.String())
found, err := el.client.DOM.QuerySelector(ctx, selectorArgs) found, err := el.client.DOM.QuerySelector(ctx, selectorArgs)
@ -399,14 +389,11 @@ func (el *HTMLElement) QuerySelector(selector values.String) core.Value {
return res return res
} }
func (el *HTMLElement) QuerySelectorAll(selector values.String) core.Value { func (el *HTMLElement) QuerySelectorAll(ctx context.Context, selector values.String) core.Value {
if !el.IsConnected() { if !el.IsConnected() {
return values.NewArray(0) return values.NewArray(0)
} }
ctx, cancel := contextWithTimeout()
defer cancel()
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId? // TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
selectorArgs := dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String()) selectorArgs := dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String())
res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs) res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs)
@ -455,8 +442,8 @@ func (el *HTMLElement) QuerySelectorAll(selector values.String) core.Value {
return arr return arr
} }
func (el *HTMLElement) InnerText() values.String { func (el *HTMLElement) InnerText(ctx context.Context) values.String {
val, err := el.innerText.Read() val, err := el.innerText.Read(ctx)
if err != nil { if err != nil {
return values.EmptyString return values.EmptyString
@ -469,14 +456,11 @@ func (el *HTMLElement) InnerText() values.String {
return val.(values.String) return val.(values.String)
} }
func (el *HTMLElement) InnerTextBySelector(selector values.String) values.String { func (el *HTMLElement) InnerTextBySelector(ctx context.Context, selector values.String) values.String {
if !el.IsConnected() { if !el.IsConnected() {
return values.EmptyString return values.EmptyString
} }
ctx, cancel := contextWithTimeout()
defer cancel()
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId? // TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
found, err := el.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(el.id.nodeID, selector.String())) found, err := el.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(el.id.nodeID, selector.String()))
@ -534,10 +518,7 @@ func (el *HTMLElement) InnerTextBySelector(selector values.String) values.String
return values.NewString(text.String()) return values.NewString(text.String())
} }
func (el *HTMLElement) InnerTextBySelectorAll(selector values.String) *values.Array { func (el *HTMLElement) InnerTextBySelectorAll(ctx context.Context, selector values.String) *values.Array {
ctx, cancel := contextWithTimeout()
defer cancel()
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId? // TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
res, err := el.client.DOM.QuerySelectorAll(ctx, dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String())) res, err := el.client.DOM.QuerySelectorAll(ctx, dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String()))
@ -595,21 +576,18 @@ func (el *HTMLElement) InnerTextBySelectorAll(selector values.String) *values.Ar
return arr return arr
} }
func (el *HTMLElement) InnerHTML() values.String { func (el *HTMLElement) InnerHTML(_ context.Context) values.String {
el.mu.Lock() el.mu.Lock()
defer el.mu.Unlock() defer el.mu.Unlock()
return el.innerHTML return el.innerHTML
} }
func (el *HTMLElement) InnerHTMLBySelector(selector values.String) values.String { func (el *HTMLElement) InnerHTMLBySelector(ctx context.Context, selector values.String) values.String {
if !el.IsConnected() { if !el.IsConnected() {
return values.EmptyString return values.EmptyString
} }
ctx, cancel := contextWithTimeout()
defer cancel()
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId? // TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
found, err := el.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(el.id.nodeID, selector.String())) found, err := el.client.DOM.QuerySelector(ctx, dom.NewQuerySelectorArgs(el.id.nodeID, selector.String()))
@ -636,10 +614,7 @@ func (el *HTMLElement) InnerHTMLBySelector(selector values.String) values.String
return text return text
} }
func (el *HTMLElement) InnerHTMLBySelectorAll(selector values.String) *values.Array { func (el *HTMLElement) InnerHTMLBySelectorAll(ctx context.Context, selector values.String) *values.Array {
ctx, cancel := contextWithTimeout()
defer cancel()
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId? // TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
selectorArgs := dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String()) selectorArgs := dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String())
res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs) res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs)
@ -674,14 +649,11 @@ func (el *HTMLElement) InnerHTMLBySelectorAll(selector values.String) *values.Ar
return arr return arr
} }
func (el *HTMLElement) CountBySelector(selector values.String) values.Int { func (el *HTMLElement) CountBySelector(ctx context.Context, selector values.String) values.Int {
if !el.IsConnected() { if !el.IsConnected() {
return values.ZeroInt return values.ZeroInt
} }
ctx, cancel := contextWithTimeout()
defer cancel()
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId? // TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
selectorArgs := dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String()) selectorArgs := dom.NewQuerySelectorAllArgs(el.id.nodeID, selector.String())
res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs) res, err := el.client.DOM.QuerySelectorAll(ctx, selectorArgs)
@ -697,14 +669,11 @@ func (el *HTMLElement) CountBySelector(selector values.String) values.Int {
return values.NewInt(len(res.NodeIDs)) return values.NewInt(len(res.NodeIDs))
} }
func (el *HTMLElement) ExistsBySelector(selector values.String) values.Boolean { func (el *HTMLElement) ExistsBySelector(ctx context.Context, selector values.String) values.Boolean {
if !el.IsConnected() { if !el.IsConnected() {
return values.False return values.False
} }
ctx, cancel := contextWithTimeout()
defer cancel()
// TODO: Can we use RemoteObjectID or BackendID instead of NodeId? // TODO: Can we use RemoteObjectID or BackendID instead of NodeId?
selectorArgs := dom.NewQuerySelectorArgs(el.id.nodeID, selector.String()) selectorArgs := dom.NewQuerySelectorArgs(el.id.nodeID, selector.String())
res, err := el.client.DOM.QuerySelector(ctx, selectorArgs) res, err := el.client.DOM.QuerySelector(ctx, selectorArgs)
@ -724,10 +693,10 @@ func (el *HTMLElement) ExistsBySelector(selector values.String) values.Boolean {
return values.True return values.True
} }
func (el *HTMLElement) WaitForClass(class values.String, timeout values.Int) error { func (el *HTMLElement) WaitForClass(ctx context.Context, class values.String) error {
task := events.NewWaitTask( task := events.NewWaitTask(
func() (core.Value, error) { func(ctx2 context.Context) (core.Value, error) {
current := el.GetAttribute("class") current := el.GetAttribute(ctx2, "class")
if current.Type() != types.String { if current.Type() != types.String {
return values.None, nil return values.None, nil
@ -745,27 +714,19 @@ func (el *HTMLElement) WaitForClass(class values.String, timeout values.Int) err
return values.None, nil return values.None, nil
}, },
time.Millisecond*time.Duration(timeout),
events.DefaultPolling, events.DefaultPolling,
) )
_, err := task.Run() _, err := task.Run(ctx)
return err return err
} }
func (el *HTMLElement) Click() (values.Boolean, error) { func (el *HTMLElement) Click(ctx context.Context) (values.Boolean, error) {
ctx, cancel := contextWithTimeout()
defer cancel()
return events.DispatchEvent(ctx, el.client, el.id.objectID, "click") return events.DispatchEvent(ctx, el.client, el.id.objectID, "click")
} }
func (el *HTMLElement) Input(value core.Value, delay values.Int) error { func (el *HTMLElement) Input(ctx context.Context, value core.Value, delay values.Int) error {
ctx, cancel := contextWithTimeout()
defer cancel()
if err := el.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(el.id.objectID)); err != nil { if err := el.client.DOM.Focus(ctx, dom.NewFocusArgs().SetObjectID(el.id.objectID)); err != nil {
el.logError(err).Msg("failed to focus") el.logError(err).Msg("failed to focus")
@ -795,7 +756,7 @@ func (el *HTMLElement) Input(value core.Value, delay values.Int) error {
return nil return nil
} }
func (el *HTMLElement) Select(value *values.Array) (*values.Array, error) { func (el *HTMLElement) Select(ctx context.Context, value *values.Array) (*values.Array, error) {
var attrID = "data-ferret-select" var attrID = "data-ferret-select"
if el.NodeName() != "SELECT" { if el.NodeName() != "SELECT" {
@ -808,9 +769,6 @@ func (el *HTMLElement) Select(value *values.Array) (*values.Array, error) {
return nil, err return nil, err
} }
ctx, cancel := contextWithTimeout()
defer cancel()
err = el.client.DOM.SetAttributeValue(ctx, dom.NewSetAttributeValueArgs(el.id.nodeID, attrID, id.String())) err = el.client.DOM.SetAttributeValue(ctx, dom.NewSetAttributeValueArgs(el.id.nodeID, attrID, id.String()))
if err != nil { if err != nil {
@ -818,6 +776,7 @@ func (el *HTMLElement) Select(value *values.Array) (*values.Array, error) {
} }
res, err := eval.Eval( res, err := eval.Eval(
ctx,
el.client, el.client,
fmt.Sprintf(` fmt.Sprintf(`
var el = document.querySelector('[%s="%s"]'); var el = document.querySelector('[%s="%s"]');
@ -865,7 +824,7 @@ func (el *HTMLElement) Select(value *values.Array) (*values.Array, error) {
return nil, core.TypeError(types.Array, res.Type()) return nil, core.TypeError(types.Array, res.Type())
} }
func (el *HTMLElement) ScrollIntoView() error { func (el *HTMLElement) ScrollIntoView(ctx context.Context) error {
var attrID = "data-ferret-scroll" var attrID = "data-ferret-scroll"
id, err := uuid.NewV4() id, err := uuid.NewV4()
@ -874,45 +833,43 @@ func (el *HTMLElement) ScrollIntoView() error {
return err return err
} }
ctx, cancel := contextWithTimeout()
defer cancel()
err = el.client.DOM.SetAttributeValue(ctx, dom.NewSetAttributeValueArgs(el.id.nodeID, attrID, id.String())) err = el.client.DOM.SetAttributeValue(ctx, dom.NewSetAttributeValueArgs(el.id.nodeID, attrID, id.String()))
if err != nil { if err != nil {
return err return err
} }
_, err = eval.Eval(el.client, fmt.Sprintf(` _, err = eval.Eval(
var el = document.querySelector('[%s="%s"]'); ctx,
if (el == null) { el.client,
throw new Error('element not found'); fmt.Sprintf(`
} var el = document.querySelector('[%s="%s"]');
el.scrollIntoView({ if (el == null) {
behavior: 'instant', throw new Error('element not found');
inline: 'center', }
block: 'center'
}); el.scrollIntoView({
`, behavior: 'instant',
attrID, inline: 'center',
id.String(), block: 'center'
), false, false) });
`,
attrID,
id.String(),
), false, false)
el.client.DOM.RemoveAttribute(ctx, dom.NewRemoveAttributeArgs(el.id.nodeID, attrID)) el.client.DOM.RemoveAttribute(ctx, dom.NewRemoveAttributeArgs(el.id.nodeID, attrID))
return err return err
} }
func (el *HTMLElement) Hover() error { func (el *HTMLElement) Hover(ctx context.Context) error {
err := el.ScrollIntoView() err := el.ScrollIntoView(ctx)
if err != nil { if err != nil {
return err return err
} }
ctx, cancel := contextWithTimeout()
defer cancel()
q, err := getClickablePoint(ctx, el.client, el.id) q, err := getClickablePoint(ctx, el.client, el.id)
if err != nil { if err != nil {
@ -932,11 +889,8 @@ func (el *HTMLElement) IsConnected() values.Boolean {
return el.connected return el.connected
} }
func (el *HTMLElement) loadInnerText() (core.Value, error) { func (el *HTMLElement) loadInnerText(ctx context.Context) (core.Value, error) {
if el.IsConnected() { if el.IsConnected() {
ctx, cancel := contextWithTimeout()
defer cancel()
text, err := loadInnerText(ctx, el.client, el.id) text, err := loadInnerText(ctx, el.client, el.id)
if err == nil { if err == nil {
@ -948,7 +902,7 @@ func (el *HTMLElement) loadInnerText() (core.Value, error) {
// and just parse cached innerHTML // and just parse cached innerHTML
} }
h := el.InnerHTML() h := el.InnerHTML(ctx)
if h == values.EmptyString { if h == values.EmptyString {
return h, nil return h, nil
@ -965,18 +919,15 @@ func (el *HTMLElement) loadInnerText() (core.Value, error) {
return parsed, nil return parsed, nil
} }
func (el *HTMLElement) loadAttrs() (core.Value, error) { func (el *HTMLElement) loadAttrs(_ context.Context) (core.Value, error) {
return parseAttrs(el.rawAttrs), nil return parseAttrs(el.rawAttrs), nil
} }
func (el *HTMLElement) loadChildren() (core.Value, error) { func (el *HTMLElement) loadChildren(ctx context.Context) (core.Value, error) {
if !el.IsConnected() { if !el.IsConnected() {
return values.NewArray(0), nil return values.NewArray(0), nil
} }
ctx, cancel := contextWithTimeout()
defer cancel()
loaded := values.NewArray(len(el.children)) loaded := values.NewArray(len(el.children))
for _, childID := range el.children { for _, childID := range el.children {
@ -1001,11 +952,11 @@ func (el *HTMLElement) loadChildren() (core.Value, error) {
return loaded, nil return loaded, nil
} }
func (el *HTMLElement) handlePageReload(_ interface{}) { func (el *HTMLElement) handlePageReload(_ context.Context, _ interface{}) {
el.Close() el.Close()
} }
func (el *HTMLElement) handleAttrModified(message interface{}) { func (el *HTMLElement) handleAttrModified(ctx context.Context, message interface{}) {
reply, ok := message.(*dom.AttributeModifiedReply) reply, ok := message.(*dom.AttributeModifiedReply)
// well.... // well....
@ -1018,7 +969,7 @@ func (el *HTMLElement) handleAttrModified(message interface{}) {
return return
} }
el.attributes.Write(func(v core.Value, err error) { el.attributes.Write(ctx, func(v core.Value, err error) {
if err != nil { if err != nil {
el.logError(err).Msg("failed to update element") el.logError(err).Msg("failed to update element")
@ -1035,7 +986,7 @@ func (el *HTMLElement) handleAttrModified(message interface{}) {
}) })
} }
func (el *HTMLElement) handleAttrRemoved(message interface{}) { func (el *HTMLElement) handleAttrRemoved(ctx context.Context, message interface{}) {
reply, ok := message.(*dom.AttributeRemovedReply) reply, ok := message.(*dom.AttributeRemovedReply)
// well.... // well....
@ -1054,7 +1005,7 @@ func (el *HTMLElement) handleAttrRemoved(message interface{}) {
return return
} }
el.attributes.Write(func(v core.Value, err error) { el.attributes.Write(ctx, func(v core.Value, err error) {
if err != nil { if err != nil {
el.logError(err).Msg("failed to update element") el.logError(err).Msg("failed to update element")
@ -1071,7 +1022,7 @@ func (el *HTMLElement) handleAttrRemoved(message interface{}) {
}) })
} }
func (el *HTMLElement) handleChildrenCountChanged(message interface{}) { func (el *HTMLElement) handleChildrenCountChanged(ctx context.Context, message interface{}) {
reply, ok := message.(*dom.ChildNodeCountUpdatedReply) reply, ok := message.(*dom.ChildNodeCountUpdatedReply)
if !ok { if !ok {
@ -1082,9 +1033,6 @@ func (el *HTMLElement) handleChildrenCountChanged(message interface{}) {
return return
} }
ctx, cancel := contextWithTimeout()
defer cancel()
node, err := el.client.DOM.DescribeNode( node, err := el.client.DOM.DescribeNode(
ctx, ctx,
dom.NewDescribeNodeArgs().SetObjectID(el.id.objectID), dom.NewDescribeNodeArgs().SetObjectID(el.id.objectID),
@ -1102,7 +1050,7 @@ func (el *HTMLElement) handleChildrenCountChanged(message interface{}) {
el.children = createChildrenArray(node.Node.Children) el.children = createChildrenArray(node.Node.Children)
} }
func (el *HTMLElement) handleChildInserted(message interface{}) { func (el *HTMLElement) handleChildInserted(ctx context.Context, message interface{}) {
reply, ok := message.(*dom.ChildNodeInsertedReply) reply, ok := message.(*dom.ChildNodeInsertedReply)
if !ok { if !ok {
@ -1143,10 +1091,7 @@ func (el *HTMLElement) handleChildInserted(message interface{}) {
return return
} }
el.loadedChildren.Write(func(v core.Value, err error) { el.loadedChildren.Write(ctx, func(v core.Value, err error) {
ctx, cancel := contextWithTimeout()
defer cancel()
loadedArr := v.(*values.Array) loadedArr := v.(*values.Array)
loadedEl, err := LoadElement(ctx, el.logger, el.client, el.events, nextID, emptyBackendID) loadedEl, err := LoadElement(ctx, el.logger, el.client, el.events, nextID, emptyBackendID)
@ -1171,7 +1116,7 @@ func (el *HTMLElement) handleChildInserted(message interface{}) {
}) })
} }
func (el *HTMLElement) handleChildRemoved(message interface{}) { func (el *HTMLElement) handleChildRemoved(ctx context.Context, message interface{}) {
reply, ok := message.(*dom.ChildNodeRemovedReply) reply, ok := message.(*dom.ChildNodeRemovedReply)
if !ok { if !ok {
@ -1206,7 +1151,7 @@ func (el *HTMLElement) handleChildRemoved(message interface{}) {
return return
} }
el.loadedChildren.Write(func(v core.Value, err error) { el.loadedChildren.Write(ctx, func(v core.Value, err error) {
if err != nil { if err != nil {
el.logger.Error(). el.logger.Error().
Timestamp(). Timestamp().
@ -1217,9 +1162,6 @@ func (el *HTMLElement) handleChildRemoved(message interface{}) {
return return
} }
ctx, cancel := contextWithTimeout()
defer cancel()
loadedArr := v.(*values.Array) loadedArr := v.(*values.Array)
loadedArr.RemoveAt(values.NewInt(targetIDx)) loadedArr.RemoveAt(values.NewInt(targetIDx))

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/mafredri/cdp" "github.com/mafredri/cdp"
@ -18,13 +19,13 @@ func ParamString(param string) string {
return "`" + param + "`" return "`" + param + "`"
} }
func Eval(client *cdp.Client, exp string, ret bool, async bool) (core.Value, error) { func Eval(ctx context.Context, client *cdp.Client, exp string, ret bool, async bool) (core.Value, error) {
args := runtime. args := runtime.
NewEvaluateArgs(PrepareEval(exp)). NewEvaluateArgs(PrepareEval(exp)).
SetReturnByValue(ret). SetReturnByValue(ret).
SetAwaitPromise(async) SetAwaitPromise(async)
out, err := client.Runtime.Evaluate(context.Background(), args) out, err := client.Runtime.Evaluate(ctx, args)
if err != nil { if err != nil {
return values.None, err return values.None, err

View File

@ -2,17 +2,19 @@ package events
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/drivers"
"reflect"
"sync"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/mafredri/cdp/protocol/dom" "github.com/mafredri/cdp/protocol/dom"
"github.com/mafredri/cdp/protocol/page" "github.com/mafredri/cdp/protocol/page"
"reflect"
"sync"
) )
type ( type (
Event int Event int
EventListener func(message interface{}) EventListener func(ctx context.Context, message interface{})
EventBroker struct { EventBroker struct {
mu sync.Mutex mu sync.Mutex
@ -275,7 +277,11 @@ func (broker *EventBroker) emit(ctx context.Context, event Event, message interf
case <-ctx.Done(): case <-ctx.Done():
return return
default: default:
listener(message) ctx2, fn := drivers.WithDefaultTimeout(ctx)
listener(ctx2, message)
fn()
} }
} }
} }

View File

@ -1,6 +1,7 @@
package events_test package events_test
import ( import (
"context"
"github.com/MontFerret/ferret/pkg/drivers/cdp/events" "github.com/MontFerret/ferret/pkg/drivers/cdp/events"
"github.com/mafredri/cdp/protocol/dom" "github.com/mafredri/cdp/protocol/dom"
"github.com/mafredri/cdp/protocol/page" "github.com/mafredri/cdp/protocol/page"
@ -198,7 +199,7 @@ func TestEventBroker(t *testing.T) {
b := NewTestEventBroker() b := NewTestEventBroker()
StressTest(func() error { StressTest(func() error {
b.AddEventListener(events.EventLoad, func(message interface{}) {}) b.AddEventListener(events.EventLoad, func(ctx context.Context, message interface{}) {})
return nil return nil
}, 500) }, 500)
@ -210,7 +211,7 @@ func TestEventBroker(t *testing.T) {
defer b.Stop() defer b.Stop()
StressTest(func() error { StressTest(func() error {
b.AddEventListener(events.EventLoad, func(message interface{}) {}) b.AddEventListener(events.EventLoad, func(ctx context.Context, message interface{}) {})
return nil return nil
}, 500) }, 500)
@ -222,7 +223,7 @@ func TestEventBroker(t *testing.T) {
b := NewTestEventBroker() b := NewTestEventBroker()
StressTest(func() error { StressTest(func() error {
listener := func(message interface{}) {} listener := func(ctx context.Context, message interface{}) {}
b.AddEventListener(events.EventLoad, listener) b.AddEventListener(events.EventLoad, listener)
b.RemoveEventListener(events.EventLoad, listener) b.RemoveEventListener(events.EventLoad, listener)
@ -239,7 +240,7 @@ func TestEventBroker(t *testing.T) {
defer b.Stop() defer b.Stop()
StressTest(func() error { StressTest(func() error {
listener := func(message interface{}) {} listener := func(ctx context.Context, message interface{}) {}
b.AddEventListener(events.EventLoad, listener) b.AddEventListener(events.EventLoad, listener)
@ -267,7 +268,7 @@ func TestEventBroker(t *testing.T) {
var listener events.EventListener var listener events.EventListener
listener = func(message interface{}) { listener = func(ctx context.Context, message interface{}) {
counter += 1 counter += 1
b.RemoveEventListener(events.EventLoad, listener) b.RemoveEventListener(events.EventLoad, listener)
@ -296,7 +297,7 @@ func TestEventBroker(t *testing.T) {
var counter int64 var counter int64
b.AddEventListener(events.EventLoad, func(message interface{}) { b.AddEventListener(events.EventLoad, func(ctx context.Context, message interface{}) {
atomic.AddInt64(&counter, 1) atomic.AddInt64(&counter, 1)
b.Stop() b.Stop()
}) })

View File

@ -1,6 +1,7 @@
package events package events
import ( import (
"context"
"github.com/MontFerret/ferret/pkg/drivers/cdp/eval" "github.com/MontFerret/ferret/pkg/drivers/cdp/eval"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
@ -9,10 +10,10 @@ import (
) )
type ( type (
Function func() (core.Value, error) Function func(ctx context.Context) (core.Value, error)
WaitTask struct { WaitTask struct {
fun Function fun Function
timeout time.Duration
polling time.Duration polling time.Duration
} }
) )
@ -21,39 +22,31 @@ const DefaultPolling = time.Millisecond * time.Duration(200)
func NewWaitTask( func NewWaitTask(
fun Function, fun Function,
timeout time.Duration,
polling time.Duration, polling time.Duration,
) *WaitTask { ) *WaitTask {
return &WaitTask{ return &WaitTask{
fun, fun,
timeout,
polling, polling,
} }
} }
func (task *WaitTask) Run() (core.Value, error) { func (task *WaitTask) Run(ctx context.Context) (core.Value, error) {
timer := time.NewTimer(task.timeout)
for { for {
select { select {
case <-timer.C: case <-ctx.Done():
return values.None, core.ErrTimeout return values.None, core.ErrTimeout
default: default:
out, err := task.fun() out, err := task.fun(ctx)
// expression failed // expression failed
// terminating // terminating
if err != nil { if err != nil {
timer.Stop()
return values.None, err return values.None, err
} }
// output is not empty // output is not empty
// terminating // terminating
if out != values.None { if out != values.None {
timer.Stop()
return out, nil return out, nil
} }
@ -66,19 +59,18 @@ func (task *WaitTask) Run() (core.Value, error) {
func NewEvalWaitTask( func NewEvalWaitTask(
client *cdp.Client, client *cdp.Client,
predicate string, predicate string,
timeout time.Duration,
polling time.Duration, polling time.Duration,
) *WaitTask { ) *WaitTask {
return NewWaitTask( return NewWaitTask(
func() (core.Value, error) { func(ctx context.Context) (core.Value, error) {
return eval.Eval( return eval.Eval(
ctx,
client, client,
predicate, predicate,
true, true,
false, false,
) )
}, },
timeout,
polling, polling,
) )
} }

View File

@ -287,10 +287,6 @@ func createChildrenArray(nodes []dom.Node) []*HTMLElementIdentity {
return children return children
} }
func contextWithTimeout() (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), DefaultTimeout)
}
func waitForLoadEvent(ctx context.Context, client *cdp.Client) error { func waitForLoadEvent(ctx context.Context, client *cdp.Client) error {
loadEventFired, err := client.Page.LoadEventFired(ctx) loadEventFired, err := client.Page.LoadEventFired(ctx)

View File

@ -23,9 +23,9 @@ func GetInDocument(ctx context.Context, doc drivers.HTMLDocument, path []core.Va
case "url", "URL": case "url", "URL":
return doc.GetURL(), nil return doc.GetURL(), nil
case "body": case "body":
return doc.QuerySelector("body"), nil return doc.QuerySelector(ctx, "body"), nil
case "head": case "head":
return doc.QuerySelector("head"), nil return doc.QuerySelector(ctx, "head"), nil
default: default:
return GetInNode(ctx, doc.DocumentElement(), path) return GetInNode(ctx, doc.DocumentElement(), path)
} }
@ -46,13 +46,13 @@ func GetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value
switch segment { switch segment {
case "innerText": case "innerText":
return el.InnerText(), nil return el.InnerText(ctx), nil
case "innerHTML": case "innerHTML":
return el.InnerHTML(), nil return el.InnerHTML(ctx), nil
case "value": case "value":
return el.GetValue(), nil return el.GetValue(ctx), nil
case "attributes": case "attributes":
return el.GetAttributes(), nil return el.GetAttributes(ctx), nil
default: default:
return GetInNode(ctx, el, path) return GetInNode(ctx, el, path)
} }
@ -74,7 +74,7 @@ func GetInNode(ctx context.Context, node drivers.HTMLNode, path []core.Value) (c
if nt == drivers.HTMLElementType || nt == drivers.HTMLDocumentType { if nt == drivers.HTMLElementType || nt == drivers.HTMLDocumentType {
re := node.(drivers.HTMLNode) re := node.(drivers.HTMLNode)
return re.GetChildNode(segment.(values.Int)), nil return re.GetChildNode(ctx, segment.(values.Int)), nil
} }
return values.GetIn(ctx, node, path[0:]) return values.GetIn(ctx, node, path[0:])
@ -89,7 +89,7 @@ func GetInNode(ctx context.Context, node drivers.HTMLNode, path []core.Value) (c
case "nodeName": case "nodeName":
return node.NodeName(), nil return node.NodeName(), nil
case "children": case "children":
return node.GetChildNodes(), nil return node.GetChildNodes(ctx), nil
case "length": case "length":
return node.Length(), nil return node.Length(), nil
default: default:

View File

@ -23,10 +23,10 @@ func NewIterator(
return &Iterator{node, 0}, nil return &Iterator{node, 0}, nil
} }
func (iterator *Iterator) Next(_ context.Context) (value core.Value, key core.Value, err error) { func (iterator *Iterator) Next(ctx context.Context) (value core.Value, key core.Value, err error) {
if iterator.node.Length() > iterator.pos { if iterator.node.Length() > iterator.pos {
idx := iterator.pos idx := iterator.pos
val := iterator.node.GetChildNode(idx) val := iterator.node.GetChildNode(ctx, idx)
iterator.pos++ iterator.pos++

View File

@ -1,6 +1,7 @@
package common package common
import ( import (
"context"
"sync" "sync"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
@ -8,7 +9,7 @@ import (
) )
type ( type (
LazyFactory func() (core.Value, error) LazyFactory func(ctx context.Context) (core.Value, error)
LazyValue struct { LazyValue struct {
sync.Mutex sync.Mutex
@ -40,12 +41,12 @@ func (lv *LazyValue) Ready() bool {
// Read returns an underlying value. // Read returns an underlying value.
// Not thread safe. Should not mutated. // Not thread safe. Should not mutated.
// @returns (Value) - Underlying value if successfully loaded, otherwise error // @returns (Value) - Underlying value if successfully loaded, otherwise error
func (lv *LazyValue) Read() (core.Value, error) { func (lv *LazyValue) Read(ctx context.Context) (core.Value, error) {
lv.Lock() lv.Lock()
defer lv.Unlock() defer lv.Unlock()
if !lv.ready { if !lv.ready {
lv.load() lv.load(ctx)
} }
return lv.value, lv.err return lv.value, lv.err
@ -54,12 +55,12 @@ func (lv *LazyValue) Read() (core.Value, error) {
// Write safely mutates an underlying value. // Write safely mutates an underlying value.
// Loads a value if it's not ready. // Loads a value if it's not ready.
// Thread safe. // Thread safe.
func (lv *LazyValue) Write(writer func(v core.Value, err error)) { func (lv *LazyValue) Write(ctx context.Context, writer func(v core.Value, err error)) {
lv.Lock() lv.Lock()
defer lv.Unlock() defer lv.Unlock()
if !lv.ready { if !lv.ready {
lv.load() lv.load(ctx)
} }
writer(lv.value, lv.err) writer(lv.value, lv.err)
@ -76,8 +77,8 @@ func (lv *LazyValue) Reset() {
lv.err = nil lv.err = nil
} }
func (lv *LazyValue) load() { func (lv *LazyValue) load(ctx context.Context) {
val, err := lv.factory() val, err := lv.factory(ctx)
if err == nil { if err == nil {
lv.value = val lv.value = val

View File

@ -21,7 +21,7 @@ func SetInDocument(ctx context.Context, doc drivers.HTMLDocument, path []core.Va
switch segment { switch segment {
case "url", "URL": case "url", "URL":
return doc.SetURL(values.NewString(value.String())) return doc.SetURL(ctx, values.NewString(value.String()))
default: default:
return SetInNode(ctx, doc, path, value) return SetInNode(ctx, doc, path, value)
} }
@ -45,7 +45,7 @@ func SetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value
if len(path) > 1 { if len(path) > 1 {
attrName := path[1] attrName := path[1]
return el.SetAttribute(values.NewString(attrName.String()), values.NewString(value.String())) return el.SetAttribute(ctx, values.NewString(attrName.String()), values.NewString(value.String()))
} }
err := core.ValidateType(value, types.Object) err := core.ValidateType(value, types.Object)
@ -56,7 +56,7 @@ func SetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value
obj := value.(*values.Object) obj := value.(*values.Object)
obj.ForEach(func(value core.Value, key string) bool { obj.ForEach(func(value core.Value, key string) bool {
err = el.SetAttribute(values.NewString(key), values.NewString(value.String())) err = el.SetAttribute(ctx, values.NewString(key), values.NewString(value.String()))
return err == nil return err == nil
}) })
@ -67,7 +67,7 @@ func SetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value
return core.Error(ErrInvalidPath, PathToString(path[1:])) return core.Error(ErrInvalidPath, PathToString(path[1:]))
} }
return el.SetValue(value) return el.SetValue(ctx, value)
} }
} }

View File

@ -3,11 +3,14 @@ package drivers
import ( import (
"context" "context"
"io" "io"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
) )
const DefaultTimeout = time.Second * 30
type ( type (
ctxKey struct{} ctxKey struct{}

9
pkg/drivers/helpers.go Normal file
View File

@ -0,0 +1,9 @@
package drivers
import (
"context"
)
func WithDefaultTimeout(ctx context.Context) (context.Context, context.CancelFunc) {
return context.WithTimeout(ctx, DefaultTimeout)
}

View File

@ -125,28 +125,28 @@ func (doc *HTMLDocument) NodeName() values.String {
return "#document" return "#document"
} }
func (doc *HTMLDocument) GetChildNodes() core.Value { func (doc *HTMLDocument) GetChildNodes(ctx context.Context) core.Value {
return doc.element.GetChildNodes() return doc.element.GetChildNodes(ctx)
} }
func (doc *HTMLDocument) GetChildNode(idx values.Int) core.Value { func (doc *HTMLDocument) GetChildNode(ctx context.Context, idx values.Int) core.Value {
return doc.element.GetChildNode(idx) return doc.element.GetChildNode(ctx, idx)
} }
func (doc *HTMLDocument) QuerySelector(selector values.String) core.Value { func (doc *HTMLDocument) QuerySelector(ctx context.Context, selector values.String) core.Value {
return doc.element.QuerySelector(selector) return doc.element.QuerySelector(ctx, selector)
} }
func (doc *HTMLDocument) QuerySelectorAll(selector values.String) core.Value { func (doc *HTMLDocument) QuerySelectorAll(ctx context.Context, selector values.String) core.Value {
return doc.element.QuerySelectorAll(selector) return doc.element.QuerySelectorAll(ctx, selector)
} }
func (doc *HTMLDocument) CountBySelector(selector values.String) values.Int { func (doc *HTMLDocument) CountBySelector(ctx context.Context, selector values.String) values.Int {
return doc.element.CountBySelector(selector) return doc.element.CountBySelector(ctx, selector)
} }
func (doc *HTMLDocument) ExistsBySelector(selector values.String) values.Boolean { func (doc *HTMLDocument) ExistsBySelector(ctx context.Context, selector values.String) values.Boolean {
return doc.element.ExistsBySelector(selector) return doc.element.ExistsBySelector(ctx, selector)
} }
func (doc *HTMLDocument) DocumentElement() drivers.HTMLElement { func (doc *HTMLDocument) DocumentElement() drivers.HTMLElement {
@ -157,75 +157,75 @@ func (doc *HTMLDocument) GetURL() core.Value {
return doc.url return doc.url
} }
func (doc *HTMLDocument) SetURL(_ values.String) error { func (doc *HTMLDocument) SetURL(_ context.Context, _ values.String) error {
return core.ErrInvalidOperation return core.ErrInvalidOperation
} }
func (doc *HTMLDocument) Navigate(_ values.String, _ values.Int) error { func (doc *HTMLDocument) Navigate(_ context.Context, _ values.String) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) NavigateBack(_ values.Int, _ values.Int) (values.Boolean, error) { func (doc *HTMLDocument) NavigateBack(_ context.Context, _ values.Int) (values.Boolean, error) {
return false, core.ErrNotSupported return false, core.ErrNotSupported
} }
func (doc *HTMLDocument) NavigateForward(_ values.Int, _ values.Int) (values.Boolean, error) { func (doc *HTMLDocument) NavigateForward(_ context.Context, _ values.Int) (values.Boolean, error) {
return false, core.ErrNotSupported return false, core.ErrNotSupported
} }
func (doc *HTMLDocument) ClickBySelector(_ values.String) (values.Boolean, error) { func (doc *HTMLDocument) ClickBySelector(_ context.Context, _ values.String) (values.Boolean, error) {
return false, core.ErrNotSupported return false, core.ErrNotSupported
} }
func (doc *HTMLDocument) ClickBySelectorAll(_ values.String) (values.Boolean, error) { func (doc *HTMLDocument) ClickBySelectorAll(_ context.Context, _ values.String) (values.Boolean, error) {
return false, core.ErrNotSupported return false, core.ErrNotSupported
} }
func (doc *HTMLDocument) InputBySelector(_ values.String, _ core.Value, _ values.Int) (values.Boolean, error) { func (doc *HTMLDocument) InputBySelector(_ context.Context, _ values.String, _ core.Value, _ values.Int) (values.Boolean, error) {
return false, core.ErrNotSupported return false, core.ErrNotSupported
} }
func (doc *HTMLDocument) SelectBySelector(_ values.String, _ *values.Array) (*values.Array, error) { func (doc *HTMLDocument) SelectBySelector(_ context.Context, _ values.String, _ *values.Array) (*values.Array, error) {
return nil, core.ErrNotSupported return nil, core.ErrNotSupported
} }
func (doc *HTMLDocument) HoverBySelector(_ values.String) error { func (doc *HTMLDocument) HoverBySelector(_ context.Context, _ values.String) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) PrintToPDF(_ drivers.PDFParams) (values.Binary, error) { func (doc *HTMLDocument) PrintToPDF(_ context.Context, _ drivers.PDFParams) (values.Binary, error) {
return nil, core.ErrNotSupported return nil, core.ErrNotSupported
} }
func (doc *HTMLDocument) CaptureScreenshot(_ drivers.ScreenshotParams) (values.Binary, error) { func (doc *HTMLDocument) CaptureScreenshot(_ context.Context, _ drivers.ScreenshotParams) (values.Binary, error) {
return nil, core.ErrNotSupported return nil, core.ErrNotSupported
} }
func (doc *HTMLDocument) ScrollTop() error { func (doc *HTMLDocument) ScrollTop(_ context.Context) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) ScrollBottom() error { func (doc *HTMLDocument) ScrollBottom(_ context.Context) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) ScrollBySelector(_ values.String) error { func (doc *HTMLDocument) ScrollBySelector(_ context.Context, _ values.String) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) WaitForNavigation(_ values.Int) error { func (doc *HTMLDocument) WaitForNavigation(_ context.Context) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) WaitForSelector(_ values.String, _ values.Int) error { func (doc *HTMLDocument) WaitForSelector(_ context.Context, _ values.String) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) WaitForClassBySelector(_, _ values.String, _ values.Int) error { func (doc *HTMLDocument) WaitForClassBySelector(_ context.Context, _, _ values.String) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) WaitForClassBySelectorAll(_, _ values.String, _ values.Int) error { func (doc *HTMLDocument) WaitForClassBySelectorAll(_ context.Context, _, _ values.String) error {
return core.ErrNotSupported return core.ErrNotSupported
} }

View File

@ -27,7 +27,7 @@ func NewHTMLElement(node *goquery.Selection) (drivers.HTMLElement, error) {
} }
func (nd *HTMLElement) MarshalJSON() ([]byte, error) { func (nd *HTMLElement) MarshalJSON() ([]byte, error) {
return json.Marshal(nd.InnerText().String()) return json.Marshal(nd.InnerText(context.Background()).String())
} }
func (nd *HTMLElement) Type() core.Type { func (nd *HTMLElement) Type() core.Type {
@ -35,7 +35,7 @@ func (nd *HTMLElement) Type() core.Type {
} }
func (nd *HTMLElement) String() string { func (nd *HTMLElement) String() string {
return nd.InnerHTML().String() return nd.InnerHTML(context.Background()).String()
} }
func (nd *HTMLElement) Compare(other core.Value) int64 { func (nd *HTMLElement) Compare(other core.Value) int64 {
@ -43,7 +43,10 @@ func (nd *HTMLElement) Compare(other core.Value) int64 {
case drivers.HTMLElementType: case drivers.HTMLElementType:
other := other.(drivers.HTMLElement) other := other.(drivers.HTMLElement)
return nd.InnerHTML().Compare(other.InnerHTML()) ctx, fn := drivers.WithDefaultTimeout(context.Background())
defer fn()
return nd.InnerHTML(ctx).Compare(other.InnerHTML(ctx))
default: default:
return drivers.Compare(nd.Type(), other.Type()) return drivers.Compare(nd.Type(), other.Type())
} }
@ -101,7 +104,7 @@ func (nd *HTMLElement) Length() values.Int {
return nd.children.Length() return nd.children.Length()
} }
func (nd *HTMLElement) GetValue() core.Value { func (nd *HTMLElement) GetValue(_ context.Context) core.Value {
val, ok := nd.selection.Attr("value") val, ok := nd.selection.Attr("value")
if ok { if ok {
@ -111,17 +114,17 @@ func (nd *HTMLElement) GetValue() core.Value {
return values.EmptyString return values.EmptyString
} }
func (nd *HTMLElement) SetValue(value core.Value) error { func (nd *HTMLElement) SetValue(_ context.Context, value core.Value) error {
nd.selection.SetAttr("value", value.String()) nd.selection.SetAttr("value", value.String())
return nil return nil
} }
func (nd *HTMLElement) InnerText() values.String { func (nd *HTMLElement) InnerText(_ context.Context) values.String {
return values.NewString(nd.selection.Text()) return values.NewString(nd.selection.Text())
} }
func (nd *HTMLElement) InnerHTML() values.String { func (nd *HTMLElement) InnerHTML(_ context.Context) values.String {
h, err := nd.selection.Html() h, err := nd.selection.Html()
if err != nil { if err != nil {
@ -131,7 +134,7 @@ func (nd *HTMLElement) InnerHTML() values.String {
return values.NewString(h) return values.NewString(h)
} }
func (nd *HTMLElement) GetAttributes() core.Value { func (nd *HTMLElement) GetAttributes(_ context.Context) core.Value {
if nd.attrs == nil { if nd.attrs == nil {
nd.attrs = nd.parseAttrs() nd.attrs = nd.parseAttrs()
} }
@ -139,7 +142,7 @@ func (nd *HTMLElement) GetAttributes() core.Value {
return nd.attrs return nd.attrs
} }
func (nd *HTMLElement) GetAttribute(name values.String) core.Value { func (nd *HTMLElement) GetAttribute(_ context.Context, name values.String) core.Value {
v, ok := nd.selection.Attr(name.String()) v, ok := nd.selection.Attr(name.String())
if ok { if ok {
@ -149,13 +152,13 @@ func (nd *HTMLElement) GetAttribute(name values.String) core.Value {
return values.None return values.None
} }
func (nd *HTMLElement) SetAttribute(name, value values.String) error { func (nd *HTMLElement) SetAttribute(_ context.Context, name, value values.String) error {
nd.selection.SetAttr(string(name), string(value)) nd.selection.SetAttr(string(name), string(value))
return nil return nil
} }
func (nd *HTMLElement) GetChildNodes() core.Value { func (nd *HTMLElement) GetChildNodes(_ context.Context) core.Value {
if nd.children == nil { if nd.children == nil {
nd.children = nd.parseChildren() nd.children = nd.parseChildren()
} }
@ -163,7 +166,7 @@ func (nd *HTMLElement) GetChildNodes() core.Value {
return nd.children return nd.children
} }
func (nd *HTMLElement) GetChildNode(idx values.Int) core.Value { func (nd *HTMLElement) GetChildNode(_ context.Context, idx values.Int) core.Value {
if nd.children == nil { if nd.children == nil {
nd.children = nd.parseChildren() nd.children = nd.parseChildren()
} }
@ -171,7 +174,7 @@ func (nd *HTMLElement) GetChildNode(idx values.Int) core.Value {
return nd.children.Get(idx) return nd.children.Get(idx)
} }
func (nd *HTMLElement) QuerySelector(selector values.String) core.Value { func (nd *HTMLElement) QuerySelector(_ context.Context, selector values.String) core.Value {
selection := nd.selection.Find(selector.String()) selection := nd.selection.Find(selector.String())
if selection == nil { if selection == nil {
@ -187,7 +190,7 @@ func (nd *HTMLElement) QuerySelector(selector values.String) core.Value {
return res return res
} }
func (nd *HTMLElement) QuerySelectorAll(selector values.String) core.Value { func (nd *HTMLElement) QuerySelectorAll(_ context.Context, selector values.String) core.Value {
selection := nd.selection.Find(selector.String()) selection := nd.selection.Find(selector.String())
if selection == nil { if selection == nil {
@ -207,7 +210,7 @@ func (nd *HTMLElement) QuerySelectorAll(selector values.String) core.Value {
return arr return arr
} }
func (nd *HTMLElement) InnerHTMLBySelector(selector values.String) values.String { func (nd *HTMLElement) InnerHTMLBySelector(_ context.Context, selector values.String) values.String {
selection := nd.selection.Find(selector.String()) selection := nd.selection.Find(selector.String())
str, err := selection.Html() str, err := selection.Html()
@ -220,7 +223,7 @@ func (nd *HTMLElement) InnerHTMLBySelector(selector values.String) values.String
return values.NewString(str) return values.NewString(str)
} }
func (nd *HTMLElement) InnerHTMLBySelectorAll(selector values.String) *values.Array { func (nd *HTMLElement) InnerHTMLBySelectorAll(_ context.Context, selector values.String) *values.Array {
selection := nd.selection.Find(selector.String()) selection := nd.selection.Find(selector.String())
arr := values.NewArray(selection.Length()) arr := values.NewArray(selection.Length())
@ -236,13 +239,13 @@ func (nd *HTMLElement) InnerHTMLBySelectorAll(selector values.String) *values.Ar
return arr return arr
} }
func (nd *HTMLElement) InnerTextBySelector(selector values.String) values.String { func (nd *HTMLElement) InnerTextBySelector(_ context.Context, selector values.String) values.String {
selection := nd.selection.Find(selector.String()) selection := nd.selection.Find(selector.String())
return values.NewString(selection.Text()) return values.NewString(selection.Text())
} }
func (nd *HTMLElement) InnerTextBySelectorAll(selector values.String) *values.Array { func (nd *HTMLElement) InnerTextBySelectorAll(_ context.Context, selector values.String) *values.Array {
selection := nd.selection.Find(selector.String()) selection := nd.selection.Find(selector.String())
arr := values.NewArray(selection.Length()) arr := values.NewArray(selection.Length())
@ -253,7 +256,7 @@ func (nd *HTMLElement) InnerTextBySelectorAll(selector values.String) *values.Ar
return arr return arr
} }
func (nd *HTMLElement) CountBySelector(selector values.String) values.Int { func (nd *HTMLElement) CountBySelector(_ context.Context, selector values.String) values.Int {
selection := nd.selection.Find(selector.String()) selection := nd.selection.Find(selector.String())
if selection == nil { if selection == nil {
@ -263,7 +266,7 @@ func (nd *HTMLElement) CountBySelector(selector values.String) values.Int {
return values.NewInt(selection.Size()) return values.NewInt(selection.Size())
} }
func (nd *HTMLElement) ExistsBySelector(selector values.String) values.Boolean { func (nd *HTMLElement) ExistsBySelector(_ context.Context, selector values.String) values.Boolean {
selection := nd.selection.Closest(selector.String()) selection := nd.selection.Closest(selector.String())
if selection == nil { if selection == nil {
@ -285,27 +288,27 @@ func (nd *HTMLElement) Iterate(_ context.Context) (core.Iterator, error) {
return common.NewIterator(nd) return common.NewIterator(nd)
} }
func (nd *HTMLElement) Click() (values.Boolean, error) { func (nd *HTMLElement) Click(_ context.Context) (values.Boolean, error) {
return false, core.ErrNotSupported return false, core.ErrNotSupported
} }
func (nd *HTMLElement) Input(_ core.Value, _ values.Int) error { func (nd *HTMLElement) Input(_ context.Context, _ core.Value, _ values.Int) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (nd *HTMLElement) Select(_ *values.Array) (*values.Array, error) { func (nd *HTMLElement) Select(_ context.Context, _ *values.Array) (*values.Array, error) {
return nil, core.ErrNotSupported return nil, core.ErrNotSupported
} }
func (nd *HTMLElement) ScrollIntoView() error { func (nd *HTMLElement) ScrollIntoView(_ context.Context) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (nd *HTMLElement) Hover() error { func (nd *HTMLElement) Hover(_ context.Context) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (nd *HTMLElement) WaitForClass(_ values.String, _ values.Int) error { func (nd *HTMLElement) WaitForClass(_ context.Context, _ values.String) error {
return core.ErrNotSupported return core.ErrNotSupported
} }

View File

@ -2,6 +2,7 @@ package http_test
import ( import (
"bytes" "bytes"
"context"
"github.com/MontFerret/ferret/pkg/drivers" "github.com/MontFerret/ferret/pkg/drivers"
"github.com/MontFerret/ferret/pkg/drivers/http" "github.com/MontFerret/ferret/pkg/drivers/http"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
@ -321,7 +322,7 @@ func TestElement(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
v := el.GetValue() v := el.GetValue(context.Background())
So(v, ShouldEqual, "find") So(v, ShouldEqual, "find")
}) })
@ -348,7 +349,7 @@ func TestElement(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
v := el.InnerText() v := el.InnerText(context.Background())
So(v, ShouldEqual, "Ferret") So(v, ShouldEqual, "Ferret")
}) })
@ -375,7 +376,7 @@ func TestElement(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
v := el.InnerHTML() v := el.InnerHTML(context.Background())
So(v, ShouldEqual, "<h2>Ferret</h2>") So(v, ShouldEqual, "<h2>Ferret</h2>")
}) })
@ -391,7 +392,7 @@ func TestElement(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
found := el.QuerySelector(values.NewString("body .card-img-top:nth-child(1)")) found := el.QuerySelector(context.Background(), values.NewString("body .card-img-top:nth-child(1)"))
So(found, ShouldNotEqual, values.None) So(found, ShouldNotEqual, values.None)
@ -413,7 +414,7 @@ func TestElement(t *testing.T) {
So(err, ShouldBeNil) So(err, ShouldBeNil)
v := el.CountBySelector(values.NewString("head meta")) v := el.CountBySelector(context.Background(), values.NewString("head meta"))
So(v, ShouldEqual, 4) So(v, ShouldEqual, 4)
}) })

View File

@ -1,6 +1,7 @@
package drivers package drivers
import ( import (
"context"
"io" "io"
"github.com/MontFerret/ferret/pkg/runtime/collections" "github.com/MontFerret/ferret/pkg/runtime/collections"
@ -24,56 +25,56 @@ type (
NodeName() values.String NodeName() values.String
GetChildNodes() core.Value GetChildNodes(ctx context.Context) core.Value
GetChildNode(idx values.Int) core.Value GetChildNode(ctx context.Context, idx values.Int) core.Value
QuerySelector(selector values.String) core.Value QuerySelector(ctx context.Context, selector values.String) core.Value
QuerySelectorAll(selector values.String) core.Value QuerySelectorAll(ctx context.Context, selector values.String) core.Value
CountBySelector(selector values.String) values.Int CountBySelector(ctx context.Context, selector values.String) values.Int
ExistsBySelector(selector values.String) values.Boolean ExistsBySelector(ctx context.Context, selector values.String) values.Boolean
} }
// HTMLElement is the most general base interface which most objects in a Document implement. // HTMLElement is the most general base interface which most objects in a Document implement.
HTMLElement interface { HTMLElement interface {
HTMLNode HTMLNode
InnerText() values.String InnerText(ctx context.Context) values.String
InnerHTML() values.String InnerHTML(ctx context.Context) values.String
GetValue() core.Value GetValue(ctx context.Context) core.Value
SetValue(value core.Value) error SetValue(ctx context.Context, value core.Value) error
GetAttributes() core.Value GetAttributes(ctx context.Context) core.Value
GetAttribute(name values.String) core.Value GetAttribute(ctx context.Context, name values.String) core.Value
SetAttribute(name, value values.String) error SetAttribute(ctx context.Context, name, value values.String) error
InnerHTMLBySelector(selector values.String) values.String InnerHTMLBySelector(ctx context.Context, selector values.String) values.String
InnerHTMLBySelectorAll(selector values.String) *values.Array InnerHTMLBySelectorAll(ctx context.Context, selector values.String) *values.Array
InnerTextBySelector(selector values.String) values.String InnerTextBySelector(ctx context.Context, selector values.String) values.String
InnerTextBySelectorAll(selector values.String) *values.Array InnerTextBySelectorAll(ctx context.Context, selector values.String) *values.Array
Click() (values.Boolean, error) Click(ctx context.Context) (values.Boolean, error)
Input(value core.Value, delay values.Int) error Input(ctx context.Context, value core.Value, delay values.Int) error
Select(value *values.Array) (*values.Array, error) Select(ctx context.Context, value *values.Array) (*values.Array, error)
ScrollIntoView() error ScrollIntoView(ctx context.Context) error
Hover() error Hover(ctx context.Context) error
WaitForClass(class values.String, timeout values.Int) error WaitForClass(ctx context.Context, class values.String) error
} }
// The Document interface represents any web page loaded in the browser // The Document interface represents any web page loaded in the browser
@ -85,40 +86,40 @@ type (
GetURL() core.Value GetURL() core.Value
SetURL(url values.String) error SetURL(ctx context.Context, url values.String) error
Navigate(url values.String, timeout values.Int) error Navigate(ctx context.Context, url values.String) error
NavigateBack(skip values.Int, timeout values.Int) (values.Boolean, error) NavigateBack(ctx context.Context, skip values.Int) (values.Boolean, error)
NavigateForward(skip values.Int, timeout values.Int) (values.Boolean, error) NavigateForward(ctx context.Context, skip values.Int) (values.Boolean, error)
ClickBySelector(selector values.String) (values.Boolean, error) ClickBySelector(ctx context.Context, selector values.String) (values.Boolean, error)
ClickBySelectorAll(selector values.String) (values.Boolean, error) ClickBySelectorAll(ctx context.Context, selector values.String) (values.Boolean, error)
InputBySelector(selector values.String, value core.Value, delay values.Int) (values.Boolean, error) InputBySelector(ctx context.Context, selector values.String, value core.Value, delay values.Int) (values.Boolean, error)
SelectBySelector(selector values.String, value *values.Array) (*values.Array, error) SelectBySelector(ctx context.Context, selector values.String, value *values.Array) (*values.Array, error)
HoverBySelector(selector values.String) error HoverBySelector(ctx context.Context, selector values.String) error
PrintToPDF(params PDFParams) (values.Binary, error) PrintToPDF(ctx context.Context, params PDFParams) (values.Binary, error)
CaptureScreenshot(params ScreenshotParams) (values.Binary, error) CaptureScreenshot(ctx context.Context, params ScreenshotParams) (values.Binary, error)
ScrollTop() error ScrollTop(ctx context.Context) error
ScrollBottom() error ScrollBottom(ctx context.Context) error
ScrollBySelector(selector values.String) error ScrollBySelector(ctx context.Context, selector values.String) error
WaitForNavigation(timeout values.Int) error WaitForNavigation(ctx context.Context) error
WaitForSelector(selector values.String, timeout values.Int) error WaitForSelector(ctx context.Context, selector values.String) error
WaitForClassBySelector(selector, class values.String, timeout values.Int) error WaitForClassBySelector(ctx context.Context, selector, class values.String) error
WaitForClassBySelectorAll(selector, class values.String, timeout values.Int) error WaitForClassBySelectorAll(ctx context.Context, selector, class values.String) error
} }
) )

View File

@ -10,7 +10,7 @@ import (
// Click dispatches click event on a given element // Click dispatches click event on a given element
// @param source (Document | Element) - Event source. // @param source (Document | Element) - Event source.
// @param selector (String, optional) - Optional selector. Only used when a document instance is passed. // @param selector (String, optional) - Optional selector. Only used when a document instance is passed.
func Click(_ context.Context, args ...core.Value) (core.Value, error) { func Click(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 2) err := core.ValidateArgs(args, 1, 2)
if err != nil { if err != nil {
@ -25,7 +25,7 @@ func Click(_ context.Context, args ...core.Value) (core.Value, error) {
return values.False, err return values.False, err
} }
return el.Click() return el.Click(ctx)
} }
// CLICK(doc, selector) // CLICK(doc, selector)
@ -37,5 +37,5 @@ func Click(_ context.Context, args ...core.Value) (core.Value, error) {
selector := args[1].String() selector := args[1].String()
return doc.ClickBySelector(values.NewString(selector)) return doc.ClickBySelector(ctx, values.NewString(selector))
} }

View File

@ -12,7 +12,7 @@ import (
// @param source (Document) - Document. // @param source (Document) - Document.
// @param selector (String) - Selector. // @param selector (String) - Selector.
// @returns (Boolean) - Returns true if matched at least one element. // @returns (Boolean) - Returns true if matched at least one element.
func ClickAll(_ context.Context, args ...core.Value) (core.Value, error) { func ClickAll(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 2) err := core.ValidateArgs(args, 2, 2)
if err != nil { if err != nil {
@ -34,5 +34,5 @@ func ClickAll(_ context.Context, args ...core.Value) (core.Value, error) {
return values.None, err return values.None, err
} }
return doc.ClickBySelectorAll(values.NewString(selector)) return doc.ClickBySelectorAll(ctx, values.NewString(selector))
} }

View File

@ -14,14 +14,14 @@ import (
// @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element. // @param docOrEl (HTMLDocument|HTMLElement) - Parent document or element.
// @param selector (String) - CSS selector. // @param selector (String) - CSS selector.
// @returns (HTMLElement | None) - Returns an HTMLElement if found, otherwise NONE. // @returns (HTMLElement | None) - Returns an HTMLElement if found, otherwise NONE.
func Element(_ context.Context, args ...core.Value) (core.Value, error) { func Element(ctx context.Context, args ...core.Value) (core.Value, error) {
el, selector, err := queryArgs(args) el, selector, err := queryArgs(args)
if err != nil { if err != nil {
return values.None, err return values.None, err
} }
return el.QuerySelector(selector), nil return el.QuerySelector(ctx, selector), nil
} }
func queryArgs(args []core.Value) (drivers.HTMLNode, values.String, error) { func queryArgs(args []core.Value) (drivers.HTMLNode, values.String, error) {

View File

@ -11,12 +11,12 @@ import (
// @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element. // @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element.
// @param selector (String) - CSS selector. // @param selector (String) - CSS selector.
// @returns (Boolean) - A boolean value indicating whether there is an element matched by selector. // @returns (Boolean) - A boolean value indicating whether there is an element matched by selector.
func ElementExists(_ context.Context, args ...core.Value) (core.Value, error) { func ElementExists(ctx context.Context, args ...core.Value) (core.Value, error) {
el, selector, err := queryArgs(args) el, selector, err := queryArgs(args)
if err != nil { if err != nil {
return values.None, err return values.None, err
} }
return el.ExistsBySelector(selector), nil return el.ExistsBySelector(ctx, selector), nil
} }

View File

@ -12,12 +12,12 @@ import (
// @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element. // @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element.
// @param selector (String) - CSS selector. // @param selector (String) - CSS selector.
// @returns (Array) - Returns an array of found HTML element. // @returns (Array) - Returns an array of found HTML element.
func Elements(_ context.Context, args ...core.Value) (core.Value, error) { func Elements(ctx context.Context, args ...core.Value) (core.Value, error) {
el, selector, err := queryArgs(args) el, selector, err := queryArgs(args)
if err != nil { if err != nil {
return values.None, err return values.None, err
} }
return el.QuerySelectorAll(selector), nil return el.QuerySelectorAll(ctx, selector), nil
} }

View File

@ -12,12 +12,12 @@ import (
// @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element. // @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element.
// @param selector (String) - CSS selector. // @param selector (String) - CSS selector.
// @returns (Int) - A number of found HTML elements by a given CSS selector. // @returns (Int) - A number of found HTML elements by a given CSS selector.
func ElementsCount(_ context.Context, args ...core.Value) (core.Value, error) { func ElementsCount(ctx context.Context, args ...core.Value) (core.Value, error) {
el, selector, err := queryArgs(args) el, selector, err := queryArgs(args)
if err != nil { if err != nil {
return values.None, err return values.None, err
} }
return el.CountBySelector(selector), nil return el.CountBySelector(ctx, selector), nil
} }

View File

@ -13,7 +13,7 @@ import (
// If there's no element matching selector, the method returns an error. // If there's no element matching selector, the method returns an error.
// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element. // @param docOrEl (HTMLDocument|HTMLElement) - Target document or element.
// @param selector (String, options) - If document is passed, this param must represent an element selector. // @param selector (String, options) - If document is passed, this param must represent an element selector.
func Hover(_ context.Context, args ...core.Value) (core.Value, error) { func Hover(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 2) err := core.ValidateArgs(args, 1, 2)
if err != nil { if err != nil {
@ -38,11 +38,11 @@ func Hover(_ context.Context, args ...core.Value) (core.Value, error) {
doc := args[0].(drivers.HTMLDocument) doc := args[0].(drivers.HTMLDocument)
selector := args[1].(values.String) selector := args[1].(values.String)
return values.None, doc.HoverBySelector(selector) return values.None, doc.HoverBySelector(ctx, selector)
} }
// Element // Element
el := args[0].(drivers.HTMLElement) el := args[0].(drivers.HTMLElement)
return values.None, el.Hover() return values.None, el.Hover(ctx)
} }

View File

@ -13,7 +13,7 @@ import (
// @param doc (Document|Element) - Parent document or element. // @param doc (Document|Element) - Parent document or element.
// @param selector (String, optional) - String of CSS selector. // @param selector (String, optional) - String of CSS selector.
// @returns (String) - Inner HTML string if an element found, otherwise empty string. // @returns (String) - Inner HTML string if an element found, otherwise empty string.
func InnerHTML(_ context.Context, args ...core.Value) (core.Value, error) { func InnerHTML(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 2) err := core.ValidateArgs(args, 1, 2)
if err != nil { if err != nil {
@ -33,7 +33,7 @@ func InnerHTML(_ context.Context, args ...core.Value) (core.Value, error) {
} }
if len(args) == 1 { if len(args) == 1 {
return el.InnerHTML(), nil return el.InnerHTML(ctx), nil
} }
err = core.ValidateType(args[1], types.String) err = core.ValidateType(args[1], types.String)
@ -44,5 +44,5 @@ func InnerHTML(_ context.Context, args ...core.Value) (core.Value, error) {
selector := args[1].(values.String) selector := args[1].(values.String)
return el.InnerHTMLBySelector(selector), nil return el.InnerHTMLBySelector(ctx, selector), nil
} }

View File

@ -13,7 +13,7 @@ import (
// @param doc (HTMLDocument|HTMLElement) - Parent document or element. // @param doc (HTMLDocument|HTMLElement) - Parent document or element.
// @param selector (String) - String of CSS selector. // @param selector (String) - String of CSS selector.
// @returns (String) - An array of inner HTML strings if any element found, otherwise empty array. // @returns (String) - An array of inner HTML strings if any element found, otherwise empty array.
func InnerHTMLAll(_ context.Context, args ...core.Value) (core.Value, error) { func InnerHTMLAll(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 2) err := core.ValidateArgs(args, 2, 2)
if err != nil { if err != nil {
@ -40,5 +40,5 @@ func InnerHTMLAll(_ context.Context, args ...core.Value) (core.Value, error) {
selector := args[1].(values.String) selector := args[1].(values.String)
return el.InnerHTMLBySelectorAll(selector), nil return el.InnerHTMLBySelectorAll(ctx, selector), nil
} }

View File

@ -12,7 +12,7 @@ import (
// @param doc (HTMLDocument|HTMLElement) - Parent document or element. // @param doc (HTMLDocument|HTMLElement) - Parent document or element.
// @param selector (String, optional) - String of CSS selector. // @param selector (String, optional) - String of CSS selector.
// @returns (String) - Inner text if an element found, otherwise empty string. // @returns (String) - Inner text if an element found, otherwise empty string.
func InnerText(_ context.Context, args ...core.Value) (core.Value, error) { func InnerText(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 2) err := core.ValidateArgs(args, 1, 2)
if err != nil { if err != nil {
@ -26,7 +26,7 @@ func InnerText(_ context.Context, args ...core.Value) (core.Value, error) {
} }
if len(args) == 1 { if len(args) == 1 {
return el.InnerText(), nil return el.InnerText(ctx), nil
} }
err = core.ValidateType(args[1], types.String) err = core.ValidateType(args[1], types.String)
@ -37,5 +37,5 @@ func InnerText(_ context.Context, args ...core.Value) (core.Value, error) {
selector := args[1].(values.String) selector := args[1].(values.String)
return el.InnerTextBySelector(selector), nil return el.InnerTextBySelector(ctx, selector), nil
} }

View File

@ -13,7 +13,7 @@ import (
// @param doc (HTMLDocument|HTMLElement) - Parent document or element. // @param doc (HTMLDocument|HTMLElement) - Parent document or element.
// @param selector (String) - String of CSS selector. // @param selector (String) - String of CSS selector.
// @returns (String) - An array of inner text if any element found, otherwise empty array. // @returns (String) - An array of inner text if any element found, otherwise empty array.
func InnerTextAll(_ context.Context, args ...core.Value) (core.Value, error) { func InnerTextAll(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 2) err := core.ValidateArgs(args, 2, 2)
if err != nil { if err != nil {
@ -40,5 +40,5 @@ func InnerTextAll(_ context.Context, args ...core.Value) (core.Value, error) {
selector := args[1].(values.String) selector := args[1].(values.String)
return el.InnerTextBySelectorAll(selector), nil return el.InnerTextBySelectorAll(ctx, selector), nil
} }

View File

@ -15,7 +15,7 @@ import (
// @param value (String) - Target value. // @param value (String) - Target value.
// @param delay (Int, optional) - Waits delay milliseconds between keystrokes // @param delay (Int, optional) - Waits delay milliseconds between keystrokes
// @returns (Boolean) - Returns true if an element was found. // @returns (Boolean) - Returns true if an element was found.
func Input(_ context.Context, args ...core.Value) (core.Value, error) { func Input(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 4) err := core.ValidateArgs(args, 2, 4)
if err != nil { if err != nil {
@ -54,7 +54,7 @@ func Input(_ context.Context, args ...core.Value) (core.Value, error) {
delay = arg4.(values.Int) delay = arg4.(values.Int)
} }
return doc.InputBySelector(arg2.(values.String), args[2], delay) return doc.InputBySelector(ctx, arg2.(values.String), args[2], delay)
} }
el := arg1.(drivers.HTMLElement) el := arg1.(drivers.HTMLElement)
@ -72,7 +72,7 @@ func Input(_ context.Context, args ...core.Value) (core.Value, error) {
delay = arg3.(values.Int) delay = arg3.(values.Int)
} }
err = el.Input(args[1], delay) err = el.Input(ctx, args[1], delay)
if err != nil { if err != nil {
return values.False, err return values.False, err

View File

@ -2,11 +2,11 @@ package html
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/drivers" "github.com/MontFerret/ferret/pkg/drivers"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types" "github.com/MontFerret/ferret/pkg/runtime/values/types"
"time"
) )
const defaultTimeout = 5000 const defaultTimeout = 5000
@ -67,6 +67,13 @@ func ValidateDocument(ctx context.Context, value core.Value) (core.Value, error)
return doc, nil return doc, nil
} }
func waitTimeout(ctx context.Context, value values.Int) (context.Context, context.CancelFunc) {
return context.WithTimeout(
ctx,
time.Duration(value)*time.Millisecond,
)
}
func resolveElement(value core.Value) (drivers.HTMLElement, error) { func resolveElement(value core.Value) (drivers.HTMLElement, error) {
vt := value.Type() vt := value.Type()

View File

@ -2,7 +2,6 @@ package html
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types" "github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -14,7 +13,7 @@ import (
// @param doc (Document) - Target document. // @param doc (Document) - Target document.
// @param url (String) - Target url to navigate. // @param url (String) - Target url to navigate.
// @param timeout (Int, optional) - Optional timeout. Default is 5000. // @param timeout (Int, optional) - Optional timeout. Default is 5000.
func Navigate(_ context.Context, args ...core.Value) (core.Value, error) { func Navigate(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 3) err := core.ValidateArgs(args, 2, 3)
if err != nil { if err != nil {
@ -39,5 +38,8 @@ func Navigate(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[2].(values.Int) timeout = args[2].(values.Int)
} }
return values.None, doc.Navigate(args[1].(values.String), timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn()
return values.None, doc.Navigate(ctx, args[1].(values.String))
} }

View File

@ -2,7 +2,6 @@ package html
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types" "github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -15,7 +14,7 @@ import (
// @param entry (Int, optional) - Optional value indicating how many pages to skip. Default 1. // @param entry (Int, optional) - Optional value indicating how many pages to skip. Default 1.
// @param timeout (Int, optional) - Optional timeout. Default is 5000. // @param timeout (Int, optional) - Optional timeout. Default is 5000.
// @returns (Boolean) - Returns TRUE if history exists and the operation succeeded, otherwise FALSE. // @returns (Boolean) - Returns TRUE if history exists and the operation succeeded, otherwise FALSE.
func NavigateBack(_ context.Context, args ...core.Value) (core.Value, error) { func NavigateBack(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 3) err := core.ValidateArgs(args, 1, 3)
if err != nil { if err != nil {
@ -51,5 +50,8 @@ func NavigateBack(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[2].(values.Int) timeout = args[2].(values.Int)
} }
return doc.NavigateBack(skip, timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn()
return doc.NavigateBack(ctx, skip)
} }

View File

@ -2,7 +2,6 @@ package html
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types" "github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -15,7 +14,7 @@ import (
// @param entry (Int, optional) - Optional value indicating how many pages to skip. Default 1. // @param entry (Int, optional) - Optional value indicating how many pages to skip. Default 1.
// @param timeout (Int, optional) - Optional timeout. Default is 5000. // @param timeout (Int, optional) - Optional timeout. Default is 5000.
// @returns (Boolean) - Returns TRUE if history exists and the operation succeeded, otherwise FALSE. // @returns (Boolean) - Returns TRUE if history exists and the operation succeeded, otherwise FALSE.
func NavigateForward(_ context.Context, args ...core.Value) (core.Value, error) { func NavigateForward(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 3) err := core.ValidateArgs(args, 1, 3)
if err != nil { if err != nil {
@ -51,5 +50,8 @@ func NavigateForward(_ context.Context, args ...core.Value) (core.Value, error)
timeout = args[2].(values.Int) timeout = args[2].(values.Int)
} }
return doc.NavigateForward(skip, timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn()
return doc.NavigateForward(ctx, skip)
} }

View File

@ -85,14 +85,14 @@ func (p *Paging) Iterate(_ context.Context) (core.Iterator, error) {
return &PagingIterator{p.document, p.selector, -1}, nil return &PagingIterator{p.document, p.selector, -1}, nil
} }
func (i *PagingIterator) Next(_ context.Context) (core.Value, core.Value, error) { func (i *PagingIterator) Next(ctx context.Context) (core.Value, core.Value, error) {
i.pos++ i.pos++
if i.pos == 0 { if i.pos == 0 {
return values.ZeroInt, values.ZeroInt, nil return values.ZeroInt, values.ZeroInt, nil
} }
clicked, err := i.document.ClickBySelector(i.selector) clicked, err := i.document.ClickBySelector(ctx, i.selector)
if err != nil { if err != nil {
return values.None, values.None, err return values.None, values.None, err

View File

@ -292,7 +292,7 @@ func PDF(ctx context.Context, args ...core.Value) (core.Value, error) {
} }
} }
pdf, err := doc.PrintToPDF(pdfParams) pdf, err := doc.PrintToPDF(ctx, pdfParams)
if err != nil { if err != nil {
return values.None, err return values.None, err

View File

@ -155,7 +155,7 @@ func Screenshot(ctx context.Context, args ...core.Value) (core.Value, error) {
} }
} }
scr, err := doc.CaptureScreenshot(screenshotParams) scr, err := doc.CaptureScreenshot(ctx, screenshotParams)
if err != nil { if err != nil {
return values.None, err return values.None, err

View File

@ -10,7 +10,7 @@ import (
// ScrollTop scrolls the document's window to its bottom. // ScrollTop scrolls the document's window to its bottom.
// @param doc (HTMLDocument) - Target document. // @param doc (HTMLDocument) - Target document.
func ScrollBottom(_ context.Context, args ...core.Value) (core.Value, error) { func ScrollBottom(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 1) err := core.ValidateArgs(args, 1, 1)
if err != nil { if err != nil {
@ -29,5 +29,5 @@ func ScrollBottom(_ context.Context, args ...core.Value) (core.Value, error) {
return values.None, err return values.None, err
} }
return values.None, doc.ScrollBottom() return values.None, doc.ScrollBottom(ctx)
} }

View File

@ -12,7 +12,7 @@ import (
// ScrollInto scrolls an element on. // ScrollInto scrolls an element on.
// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element. // @param docOrEl (HTMLDocument|HTMLElement) - Target document or element.
// @param selector (String, options) - If document is passed, this param must represent an element selector. // @param selector (String, options) - If document is passed, this param must represent an element selector.
func ScrollInto(_ context.Context, args ...core.Value) (core.Value, error) { func ScrollInto(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 2) err := core.ValidateArgs(args, 1, 2)
if err != nil { if err != nil {
@ -36,7 +36,7 @@ func ScrollInto(_ context.Context, args ...core.Value) (core.Value, error) {
doc := args[0].(drivers.HTMLDocument) doc := args[0].(drivers.HTMLDocument)
selector := args[1].(values.String) selector := args[1].(values.String)
return values.None, doc.ScrollBySelector(selector) return values.None, doc.ScrollBySelector(ctx, selector)
} }
err = core.ValidateType(args[0], drivers.HTMLElementType) err = core.ValidateType(args[0], drivers.HTMLElementType)
@ -48,5 +48,5 @@ func ScrollInto(_ context.Context, args ...core.Value) (core.Value, error) {
// Element // Element
el := args[0].(drivers.HTMLElement) el := args[0].(drivers.HTMLElement)
return values.None, el.ScrollIntoView() return values.None, el.ScrollIntoView(ctx)
} }

View File

@ -10,7 +10,7 @@ import (
// ScrollTop scrolls the document's window to its top. // ScrollTop scrolls the document's window to its top.
// @param doc (HTMLDocument) - Target document. // @param doc (HTMLDocument) - Target document.
func ScrollTop(_ context.Context, args ...core.Value) (core.Value, error) { func ScrollTop(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 1) err := core.ValidateArgs(args, 1, 1)
if err != nil { if err != nil {
@ -29,5 +29,5 @@ func ScrollTop(_ context.Context, args ...core.Value) (core.Value, error) {
return values.None, err return values.None, err
} }
return values.None, doc.ScrollTop() return values.None, doc.ScrollTop(ctx)
} }

View File

@ -14,7 +14,7 @@ import (
// @param valueOrSelector (String | Array<String>) - Selector or a an array of strings as a value. // @param valueOrSelector (String | Array<String>) - Selector or a an array of strings as a value.
// @param value (Array<String) - Target value. Optional. // @param value (Array<String) - Target value. Optional.
// @returns (Array<String>) - Returns an array of selected values. // @returns (Array<String>) - Returns an array of selected values.
func Select(_ context.Context, args ...core.Value) (core.Value, error) { func Select(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 4) err := core.ValidateArgs(args, 2, 4)
if err != nil { if err != nil {
@ -46,7 +46,7 @@ func Select(_ context.Context, args ...core.Value) (core.Value, error) {
return values.False, err return values.False, err
} }
return doc.SelectBySelector(arg2.(values.String), arg3.(*values.Array)) return doc.SelectBySelector(ctx, arg2.(values.String), arg3.(*values.Array))
} }
el := arg1.(drivers.HTMLElement) el := arg1.(drivers.HTMLElement)
@ -58,5 +58,5 @@ func Select(_ context.Context, args ...core.Value) (core.Value, error) {
return values.False, err return values.False, err
} }
return el.Select(arg2.(*values.Array)) return el.Select(ctx, arg2.(*values.Array))
} }

View File

@ -2,7 +2,6 @@ package html
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/drivers" "github.com/MontFerret/ferret/pkg/drivers"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
@ -18,7 +17,7 @@ import (
// Otherwise timeout. // Otherwise timeout.
// @param timeout (Int, optional) - If document is passed, this param must represent timeout. // @param timeout (Int, optional) - If document is passed, this param must represent timeout.
// Otherwise not passed. // Otherwise not passed.
func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) { func WaitClass(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 4) err := core.ValidateArgs(args, 2, 4)
if err != nil { if err != nil {
@ -72,7 +71,10 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[3].(values.Int) timeout = args[3].(values.Int)
} }
return values.None, doc.WaitForClassBySelector(selector, class, timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn()
return values.None, doc.WaitForClassBySelector(ctx, selector, class)
} }
el := arg1.(drivers.HTMLElement) el := arg1.(drivers.HTMLElement)
@ -88,5 +90,8 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[2].(values.Int) timeout = args[2].(values.Int)
} }
return values.None, el.WaitForClass(class, timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn()
return values.None, el.WaitForClass(ctx, class)
} }

View File

@ -2,7 +2,6 @@ package html
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types" "github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -14,7 +13,7 @@ import (
// @param selector (String) - String of CSS selector. // @param selector (String) - String of CSS selector.
// @param class (String) - String of target CSS class. // @param class (String) - String of target CSS class.
// @param timeout (Int, optional) - Optional timeout. // @param timeout (Int, optional) - Optional timeout.
func WaitClassAll(_ context.Context, args ...core.Value) (core.Value, error) { func WaitClassAll(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 3, 4) err := core.ValidateArgs(args, 3, 4)
if err != nil { if err != nil {
@ -55,5 +54,8 @@ func WaitClassAll(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[3].(values.Int) timeout = args[3].(values.Int)
} }
return values.None, doc.WaitForClassBySelectorAll(selector, class, timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn()
return values.None, doc.WaitForClassBySelectorAll(ctx, selector, class)
} }

View File

@ -2,7 +2,6 @@ package html
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types" "github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -13,7 +12,7 @@ import (
// @param doc (HTMLDocument) - Driver HTMLDocument. // @param doc (HTMLDocument) - Driver HTMLDocument.
// @param selector (String) - Target element's selector. // @param selector (String) - Target element's selector.
// @param timeout (Int, optional) - Optional timeout. Default 5000 ms. // @param timeout (Int, optional) - Optional timeout. Default 5000 ms.
func WaitElement(_ context.Context, args ...core.Value) (core.Value, error) { func WaitElement(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 2, 3) err := core.ValidateArgs(args, 2, 3)
if err != nil { if err != nil {
@ -39,5 +38,8 @@ func WaitElement(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[2].(values.Int) timeout = args[2].(values.Int)
} }
return values.None, doc.WaitForSelector(values.NewString(selector), timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn()
return values.None, doc.WaitForSelector(ctx, values.NewString(selector))
} }

View File

@ -2,7 +2,6 @@ package html
import ( import (
"context" "context"
"github.com/MontFerret/ferret/pkg/runtime/core" "github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values" "github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types" "github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -12,7 +11,7 @@ import (
// Stops the execution until the navigation ends or operation times out. // Stops the execution until the navigation ends or operation times out.
// @param doc (HTMLDocument) - Driver HTMLDocument. // @param doc (HTMLDocument) - Driver HTMLDocument.
// @param timeout (Int, optional) - Optional timeout. Default 5000 ms. // @param timeout (Int, optional) - Optional timeout. Default 5000 ms.
func WaitNavigation(_ context.Context, args ...core.Value) (core.Value, error) { func WaitNavigation(ctx context.Context, args ...core.Value) (core.Value, error) {
err := core.ValidateArgs(args, 1, 2) err := core.ValidateArgs(args, 1, 2)
if err != nil { if err != nil {
@ -37,5 +36,8 @@ func WaitNavigation(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[1].(values.Int) timeout = args[1].(values.Int)
} }
return values.None, doc.WaitForNavigation(timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn()
return values.None, doc.WaitForNavigation(ctx)
} }