1
0
mirror of https://github.com/MontFerret/ferret.git synced 2025-01-26 03:51:57 +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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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 {
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 {
return err
@ -83,7 +96,7 @@ func (r *Runner) Run() error {
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)
if err != nil {
@ -126,13 +139,30 @@ func (r *Runner) runQueries(dir string) ([]Result, error) {
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
}
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()
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(
ctx,
runtime.WithLog(zerolog.ConsoleWriter{Out: os.Stdout}),
@ -207,20 +223,9 @@ func (r *Runner) report(results []Result) Summary {
for _, res := range results {
if res.err != nil {
r.logger.Error().
Timestamp().
Err(res.err).
Str("file", res.name).
Dur("time", res.duration).
Msg("Test failed")
failed++
} else {
r.logger.Info().
Timestamp().
Str("file", res.name).
Dur("time", res.duration).
Msg("Test passed")
passed++
}

View File

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

View File

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

View File

@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/mafredri/cdp"
@ -18,13 +19,13 @@ func ParamString(param string) string {
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.
NewEvaluateArgs(PrepareEval(exp)).
SetReturnByValue(ret).
SetAwaitPromise(async)
out, err := client.Runtime.Evaluate(context.Background(), args)
out, err := client.Runtime.Evaluate(ctx, args)
if err != nil {
return values.None, err

View File

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

View File

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

View File

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

View File

@ -287,10 +287,6 @@ func createChildrenArray(nodes []dom.Node) []*HTMLElementIdentity {
return children
}
func contextWithTimeout() (context.Context, context.CancelFunc) {
return context.WithTimeout(context.Background(), DefaultTimeout)
}
func waitForLoadEvent(ctx context.Context, client *cdp.Client) error {
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":
return doc.GetURL(), nil
case "body":
return doc.QuerySelector("body"), nil
return doc.QuerySelector(ctx, "body"), nil
case "head":
return doc.QuerySelector("head"), nil
return doc.QuerySelector(ctx, "head"), nil
default:
return GetInNode(ctx, doc.DocumentElement(), path)
}
@ -46,13 +46,13 @@ func GetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value
switch segment {
case "innerText":
return el.InnerText(), nil
return el.InnerText(ctx), nil
case "innerHTML":
return el.InnerHTML(), nil
return el.InnerHTML(ctx), nil
case "value":
return el.GetValue(), nil
return el.GetValue(ctx), nil
case "attributes":
return el.GetAttributes(), nil
return el.GetAttributes(ctx), nil
default:
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 {
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:])
@ -89,7 +89,7 @@ func GetInNode(ctx context.Context, node drivers.HTMLNode, path []core.Value) (c
case "nodeName":
return node.NodeName(), nil
case "children":
return node.GetChildNodes(), nil
return node.GetChildNodes(ctx), nil
case "length":
return node.Length(), nil
default:

View File

@ -23,10 +23,10 @@ func NewIterator(
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 {
idx := iterator.pos
val := iterator.node.GetChildNode(idx)
val := iterator.node.GetChildNode(ctx, idx)
iterator.pos++

View File

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

View File

@ -21,7 +21,7 @@ func SetInDocument(ctx context.Context, doc drivers.HTMLDocument, path []core.Va
switch segment {
case "url", "URL":
return doc.SetURL(values.NewString(value.String()))
return doc.SetURL(ctx, values.NewString(value.String()))
default:
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 {
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)
@ -56,7 +56,7 @@ func SetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value
obj := value.(*values.Object)
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
})
@ -67,7 +67,7 @@ func SetInElement(ctx context.Context, el drivers.HTMLElement, path []core.Value
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 (
"context"
"io"
"time"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
)
const DefaultTimeout = time.Second * 30
type (
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"
}
func (doc *HTMLDocument) GetChildNodes() core.Value {
return doc.element.GetChildNodes()
func (doc *HTMLDocument) GetChildNodes(ctx context.Context) core.Value {
return doc.element.GetChildNodes(ctx)
}
func (doc *HTMLDocument) GetChildNode(idx values.Int) core.Value {
return doc.element.GetChildNode(idx)
func (doc *HTMLDocument) GetChildNode(ctx context.Context, idx values.Int) core.Value {
return doc.element.GetChildNode(ctx, idx)
}
func (doc *HTMLDocument) QuerySelector(selector values.String) core.Value {
return doc.element.QuerySelector(selector)
func (doc *HTMLDocument) QuerySelector(ctx context.Context, selector values.String) core.Value {
return doc.element.QuerySelector(ctx, selector)
}
func (doc *HTMLDocument) QuerySelectorAll(selector values.String) core.Value {
return doc.element.QuerySelectorAll(selector)
func (doc *HTMLDocument) QuerySelectorAll(ctx context.Context, selector values.String) core.Value {
return doc.element.QuerySelectorAll(ctx, selector)
}
func (doc *HTMLDocument) CountBySelector(selector values.String) values.Int {
return doc.element.CountBySelector(selector)
func (doc *HTMLDocument) CountBySelector(ctx context.Context, selector values.String) values.Int {
return doc.element.CountBySelector(ctx, selector)
}
func (doc *HTMLDocument) ExistsBySelector(selector values.String) values.Boolean {
return doc.element.ExistsBySelector(selector)
func (doc *HTMLDocument) ExistsBySelector(ctx context.Context, selector values.String) values.Boolean {
return doc.element.ExistsBySelector(ctx, selector)
}
func (doc *HTMLDocument) DocumentElement() drivers.HTMLElement {
@ -157,75 +157,75 @@ func (doc *HTMLDocument) GetURL() core.Value {
return doc.url
}
func (doc *HTMLDocument) SetURL(_ values.String) error {
func (doc *HTMLDocument) SetURL(_ context.Context, _ values.String) error {
return core.ErrInvalidOperation
}
func (doc *HTMLDocument) Navigate(_ values.String, _ values.Int) error {
func (doc *HTMLDocument) Navigate(_ context.Context, _ values.String) error {
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
}
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
}
func (doc *HTMLDocument) ClickBySelector(_ values.String) (values.Boolean, error) {
func (doc *HTMLDocument) ClickBySelector(_ context.Context, _ values.String) (values.Boolean, error) {
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
}
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
}
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
}
func (doc *HTMLDocument) HoverBySelector(_ values.String) error {
func (doc *HTMLDocument) HoverBySelector(_ context.Context, _ values.String) error {
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
}
func (doc *HTMLDocument) CaptureScreenshot(_ drivers.ScreenshotParams) (values.Binary, error) {
func (doc *HTMLDocument) CaptureScreenshot(_ context.Context, _ drivers.ScreenshotParams) (values.Binary, error) {
return nil, core.ErrNotSupported
}
func (doc *HTMLDocument) ScrollTop() error {
func (doc *HTMLDocument) ScrollTop(_ context.Context) error {
return core.ErrNotSupported
}
func (doc *HTMLDocument) ScrollBottom() error {
func (doc *HTMLDocument) ScrollBottom(_ context.Context) error {
return core.ErrNotSupported
}
func (doc *HTMLDocument) ScrollBySelector(_ values.String) error {
func (doc *HTMLDocument) ScrollBySelector(_ context.Context, _ values.String) error {
return core.ErrNotSupported
}
func (doc *HTMLDocument) WaitForNavigation(_ values.Int) error {
func (doc *HTMLDocument) WaitForNavigation(_ context.Context) error {
return core.ErrNotSupported
}
func (doc *HTMLDocument) WaitForSelector(_ values.String, _ values.Int) error {
func (doc *HTMLDocument) WaitForSelector(_ context.Context, _ values.String) error {
return core.ErrNotSupported
}
func (doc *HTMLDocument) WaitForClassBySelector(_, _ values.String, _ values.Int) error {
func (doc *HTMLDocument) WaitForClassBySelector(_ context.Context, _, _ values.String) error {
return core.ErrNotSupported
}
func (doc *HTMLDocument) WaitForClassBySelectorAll(_, _ values.String, _ values.Int) error {
func (doc *HTMLDocument) WaitForClassBySelectorAll(_ context.Context, _, _ values.String) error {
return core.ErrNotSupported
}

View File

@ -27,7 +27,7 @@ func NewHTMLElement(node *goquery.Selection) (drivers.HTMLElement, 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 {
@ -35,7 +35,7 @@ func (nd *HTMLElement) Type() core.Type {
}
func (nd *HTMLElement) String() string {
return nd.InnerHTML().String()
return nd.InnerHTML(context.Background()).String()
}
func (nd *HTMLElement) Compare(other core.Value) int64 {
@ -43,7 +43,10 @@ func (nd *HTMLElement) Compare(other core.Value) int64 {
case drivers.HTMLElementType:
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:
return drivers.Compare(nd.Type(), other.Type())
}
@ -101,7 +104,7 @@ func (nd *HTMLElement) Length() values.Int {
return nd.children.Length()
}
func (nd *HTMLElement) GetValue() core.Value {
func (nd *HTMLElement) GetValue(_ context.Context) core.Value {
val, ok := nd.selection.Attr("value")
if ok {
@ -111,17 +114,17 @@ func (nd *HTMLElement) GetValue() core.Value {
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())
return nil
}
func (nd *HTMLElement) InnerText() values.String {
func (nd *HTMLElement) InnerText(_ context.Context) values.String {
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()
if err != nil {
@ -131,7 +134,7 @@ func (nd *HTMLElement) InnerHTML() values.String {
return values.NewString(h)
}
func (nd *HTMLElement) GetAttributes() core.Value {
func (nd *HTMLElement) GetAttributes(_ context.Context) core.Value {
if nd.attrs == nil {
nd.attrs = nd.parseAttrs()
}
@ -139,7 +142,7 @@ func (nd *HTMLElement) GetAttributes() core.Value {
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())
if ok {
@ -149,13 +152,13 @@ func (nd *HTMLElement) GetAttribute(name values.String) core.Value {
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))
return nil
}
func (nd *HTMLElement) GetChildNodes() core.Value {
func (nd *HTMLElement) GetChildNodes(_ context.Context) core.Value {
if nd.children == nil {
nd.children = nd.parseChildren()
}
@ -163,7 +166,7 @@ func (nd *HTMLElement) GetChildNodes() core.Value {
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 {
nd.children = nd.parseChildren()
}
@ -171,7 +174,7 @@ func (nd *HTMLElement) GetChildNode(idx values.Int) core.Value {
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())
if selection == nil {
@ -187,7 +190,7 @@ func (nd *HTMLElement) QuerySelector(selector values.String) core.Value {
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())
if selection == nil {
@ -207,7 +210,7 @@ func (nd *HTMLElement) QuerySelectorAll(selector values.String) core.Value {
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())
str, err := selection.Html()
@ -220,7 +223,7 @@ func (nd *HTMLElement) InnerHTMLBySelector(selector values.String) values.String
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())
arr := values.NewArray(selection.Length())
@ -236,13 +239,13 @@ func (nd *HTMLElement) InnerHTMLBySelectorAll(selector values.String) *values.Ar
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())
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())
arr := values.NewArray(selection.Length())
@ -253,7 +256,7 @@ func (nd *HTMLElement) InnerTextBySelectorAll(selector values.String) *values.Ar
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())
if selection == nil {
@ -263,7 +266,7 @@ func (nd *HTMLElement) CountBySelector(selector values.String) values.Int {
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())
if selection == nil {
@ -285,27 +288,27 @@ func (nd *HTMLElement) Iterate(_ context.Context) (core.Iterator, error) {
return common.NewIterator(nd)
}
func (nd *HTMLElement) Click() (values.Boolean, error) {
func (nd *HTMLElement) Click(_ context.Context) (values.Boolean, error) {
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
}
func (nd *HTMLElement) Select(_ *values.Array) (*values.Array, error) {
func (nd *HTMLElement) Select(_ context.Context, _ *values.Array) (*values.Array, error) {
return nil, core.ErrNotSupported
}
func (nd *HTMLElement) ScrollIntoView() error {
func (nd *HTMLElement) ScrollIntoView(_ context.Context) error {
return core.ErrNotSupported
}
func (nd *HTMLElement) Hover() error {
func (nd *HTMLElement) Hover(_ context.Context) error {
return core.ErrNotSupported
}
func (nd *HTMLElement) WaitForClass(_ values.String, _ values.Int) error {
func (nd *HTMLElement) WaitForClass(_ context.Context, _ values.String) error {
return core.ErrNotSupported
}

View File

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

View File

@ -1,6 +1,7 @@
package drivers
import (
"context"
"io"
"github.com/MontFerret/ferret/pkg/runtime/collections"
@ -24,56 +25,56 @@ type (
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 interface {
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
@ -85,40 +86,40 @@ type (
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
// @param source (Document | Element) - Event source.
// @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)
if err != nil {
@ -25,7 +25,7 @@ func Click(_ context.Context, args ...core.Value) (core.Value, error) {
return values.False, err
}
return el.Click()
return el.Click(ctx)
}
// CLICK(doc, selector)
@ -37,5 +37,5 @@ func Click(_ context.Context, args ...core.Value) (core.Value, error) {
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 selector (String) - Selector.
// @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)
if err != nil {
@ -34,5 +34,5 @@ func ClickAll(_ context.Context, args ...core.Value) (core.Value, error) {
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 selector (String) - CSS selector.
// @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)
if err != nil {
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) {

View File

@ -11,12 +11,12 @@ import (
// @param docOrEl (HTMLDocument|HTMLNode) - Parent document or element.
// @param selector (String) - CSS 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)
if err != nil {
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 selector (String) - CSS selector.
// @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)
if err != nil {
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 selector (String) - 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)
if err != nil {
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.
// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element.
// @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)
if err != nil {
@ -38,11 +38,11 @@ func Hover(_ context.Context, args ...core.Value) (core.Value, error) {
doc := args[0].(drivers.HTMLDocument)
selector := args[1].(values.String)
return values.None, doc.HoverBySelector(selector)
return values.None, doc.HoverBySelector(ctx, selector)
}
// Element
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 selector (String, optional) - String of CSS selector.
// @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)
if err != nil {
@ -33,7 +33,7 @@ func InnerHTML(_ context.Context, args ...core.Value) (core.Value, error) {
}
if len(args) == 1 {
return el.InnerHTML(), nil
return el.InnerHTML(ctx), nil
}
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)
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 selector (String) - String of CSS selector.
// @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)
if err != nil {
@ -40,5 +40,5 @@ func InnerHTMLAll(_ context.Context, args ...core.Value) (core.Value, error) {
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 selector (String, optional) - String of CSS selector.
// @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)
if err != nil {
@ -26,7 +26,7 @@ func InnerText(_ context.Context, args ...core.Value) (core.Value, error) {
}
if len(args) == 1 {
return el.InnerText(), nil
return el.InnerText(ctx), nil
}
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)
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 selector (String) - String of CSS selector.
// @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)
if err != nil {
@ -40,5 +40,5 @@ func InnerTextAll(_ context.Context, args ...core.Value) (core.Value, error) {
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 delay (Int, optional) - Waits delay milliseconds between keystrokes
// @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)
if err != nil {
@ -54,7 +54,7 @@ func Input(_ context.Context, args ...core.Value) (core.Value, error) {
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)
@ -72,7 +72,7 @@ func Input(_ context.Context, args ...core.Value) (core.Value, error) {
delay = arg3.(values.Int)
}
err = el.Input(args[1], delay)
err = el.Input(ctx, args[1], delay)
if err != nil {
return values.False, err

View File

@ -2,11 +2,11 @@ package html
import (
"context"
"github.com/MontFerret/ferret/pkg/drivers"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
"time"
)
const defaultTimeout = 5000
@ -67,6 +67,13 @@ func ValidateDocument(ctx context.Context, value core.Value) (core.Value, error)
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) {
vt := value.Type()

View File

@ -2,7 +2,6 @@ package html
import (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -14,7 +13,7 @@ import (
// @param doc (Document) - Target document.
// @param url (String) - Target url to navigate.
// @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)
if err != nil {
@ -39,5 +38,8 @@ func Navigate(_ context.Context, args ...core.Value) (core.Value, error) {
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 (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"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 timeout (Int, optional) - Optional timeout. Default is 5000.
// @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)
if err != nil {
@ -51,5 +50,8 @@ func NavigateBack(_ context.Context, args ...core.Value) (core.Value, error) {
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 (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"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 timeout (Int, optional) - Optional timeout. Default is 5000.
// @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)
if err != nil {
@ -51,5 +50,8 @@ func NavigateForward(_ context.Context, args ...core.Value) (core.Value, error)
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
}
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++
if i.pos == 0 {
return values.ZeroInt, values.ZeroInt, nil
}
clicked, err := i.document.ClickBySelector(i.selector)
clicked, err := i.document.ClickBySelector(ctx, i.selector)
if err != nil {
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 {
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 {
return values.None, err

View File

@ -10,7 +10,7 @@ import (
// ScrollTop scrolls the document's window to its bottom.
// @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)
if err != nil {
@ -29,5 +29,5 @@ func ScrollBottom(_ context.Context, args ...core.Value) (core.Value, error) {
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.
// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element.
// @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)
if err != nil {
@ -36,7 +36,7 @@ func ScrollInto(_ context.Context, args ...core.Value) (core.Value, error) {
doc := args[0].(drivers.HTMLDocument)
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)
@ -48,5 +48,5 @@ func ScrollInto(_ context.Context, args ...core.Value) (core.Value, error) {
// Element
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.
// @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)
if err != nil {
@ -29,5 +29,5 @@ func ScrollTop(_ context.Context, args ...core.Value) (core.Value, error) {
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 value (Array<String) - Target value. Optional.
// @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)
if err != nil {
@ -46,7 +46,7 @@ func Select(_ context.Context, args ...core.Value) (core.Value, error) {
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)
@ -58,5 +58,5 @@ func Select(_ context.Context, args ...core.Value) (core.Value, error) {
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 (
"context"
"github.com/MontFerret/ferret/pkg/drivers"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
@ -18,7 +17,7 @@ import (
// Otherwise timeout.
// @param timeout (Int, optional) - If document is passed, this param must represent timeout.
// 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)
if err != nil {
@ -72,7 +71,10 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) {
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)
@ -88,5 +90,8 @@ func WaitClass(_ context.Context, args ...core.Value) (core.Value, error) {
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 (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -14,7 +13,7 @@ import (
// @param selector (String) - String of CSS selector.
// @param class (String) - String of target CSS class.
// @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)
if err != nil {
@ -55,5 +54,8 @@ func WaitClassAll(_ context.Context, args ...core.Value) (core.Value, error) {
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 (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -13,7 +12,7 @@ import (
// @param doc (HTMLDocument) - Driver HTMLDocument.
// @param selector (String) - Target element's selector.
// @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)
if err != nil {
@ -39,5 +38,8 @@ func WaitElement(_ context.Context, args ...core.Value) (core.Value, error) {
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 (
"context"
"github.com/MontFerret/ferret/pkg/runtime/core"
"github.com/MontFerret/ferret/pkg/runtime/values"
"github.com/MontFerret/ferret/pkg/runtime/values/types"
@ -12,7 +11,7 @@ import (
// Stops the execution until the navigation ends or operation times out.
// @param doc (HTMLDocument) - Driver HTMLDocument.
// @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)
if err != nil {
@ -37,5 +36,8 @@ func WaitNavigation(_ context.Context, args ...core.Value) (core.Value, error) {
timeout = args[1].(values.Int)
}
return values.None, doc.WaitForNavigation(timeout)
ctx, fn := waitTimeout(ctx, timeout)
defer fn()
return values.None, doc.WaitForNavigation(ctx)
}