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

Feature/#229 wait no element (#249)

* Added possibility to wait for an element or a class absence
This commit is contained in:
Tim Voronov 2019-03-06 21:52:41 -05:00 committed by GitHub
parent 5188f9e71b
commit d0caef8be7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 385 additions and 102 deletions

View File

@ -54,6 +54,7 @@ jobs:
script: script:
- make cover - make cover
- stage: e2e - stage: e2e
go: stable
before_script: before_script:
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 --disable-setuid-sandbox --no-sandbox about:blank & - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 --disable-setuid-sandbox --no-sandbox about:blank &
script: script:

View File

@ -1,12 +1,14 @@
package main package main
import ( import (
"context"
"flag" "flag"
"fmt" "fmt"
"github.com/MontFerret/ferret/e2e/runner" "github.com/MontFerret/ferret/e2e/runner"
"github.com/MontFerret/ferret/e2e/server" "github.com/MontFerret/ferret/e2e/server"
"github.com/rs/zerolog" "github.com/rs/zerolog"
"os" "os"
"os/signal"
"path/filepath" "path/filepath"
"regexp" "regexp"
) )
@ -101,7 +103,18 @@ func main() {
Filter: filterR, Filter: filterR,
}) })
err := r.Run() ctx, cancel := context.WithCancel(context.Background())
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func() {
for {
<-c
cancel()
}
}()
err := r.Run(ctx)
if err != nil { if err != nil {
os.Exit(1) os.Exit(1)

View File

@ -0,0 +1,42 @@
import random from "../../../utils/random.js";
const e = React.createElement;
function render(id) {
return e("span", { id: `${id}-content` }, ["Hello world"]);
}
export default class AppearableComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
element: props.appear === true ? null : render(props.id)
};
}
handleClick() {
setTimeout(() => {
this.setState({
element: this.props.appear === true ? render(this.props.id) : null
})
}, random())
}
render() {
const btnId = `${this.props.id}-btn`;
return e("div", {className: "card"}, [
e("div", { className: "card-header"}, [
e("button", {
id: btnId,
className: "btn btn-primary",
onClick: this.handleClick.bind(this)
}, [
this.props.title || "Toggle class"
])
]),
e("div", { className: "card-body"}, this.state.element)
]);
}
}

View File

@ -1,3 +1,5 @@
import random from "../../../utils/random.js";
const e = React.createElement; const e = React.createElement;
export default class ClickableComponent extends React.PureComponent { export default class ClickableComponent extends React.PureComponent {
@ -5,7 +7,7 @@ export default class ClickableComponent extends React.PureComponent {
super(props); super(props);
this.state = { this.state = {
clicked: false show: props.show === true
}; };
} }
@ -13,12 +15,12 @@ export default class ClickableComponent extends React.PureComponent {
let timeout = 500; let timeout = 500;
if (this.props.randomTimeout) { if (this.props.randomTimeout) {
timeout = Math.ceil(Math.random() * 1000 * 10); timeout = random();
} }
setTimeout(() => { setTimeout(() => {
this.setState({ this.setState({
clicked: !this.state.clicked show: !this.state.show
}) })
}, timeout) }, timeout)
} }
@ -28,7 +30,7 @@ export default class ClickableComponent extends React.PureComponent {
const contentId = `${this.props.id}-content`; const contentId = `${this.props.id}-content`;
const classNames = ["alert"]; const classNames = ["alert"];
if (this.state.clicked) { if (this.state.show === true) {
classNames.push("alert-success"); classNames.push("alert-success");
} }
@ -39,7 +41,7 @@ export default class ClickableComponent extends React.PureComponent {
className: "btn btn-primary", className: "btn btn-primary",
onClick: this.handleClick.bind(this) onClick: this.handleClick.bind(this)
}, [ }, [
"Toggle class" this.props.title || "Toggle class"
]) ])
]), ]),
e("div", { className: "card-body"}, [ e("div", { className: "card-body"}, [

View File

@ -1,19 +1,60 @@
import Hoverable from "./hoverable.js"; import Hoverable from "./hoverable.js";
import Clickable from "./clickable.js"; import Clickable from "./clickable.js";
import Appearable from "./appearable.js";
const e = React.createElement; const e = React.createElement;
export default class EventsPage extends React.Component { export default class EventsPage extends React.Component {
render() { render() {
return e("div", { className: "row", id: "page-events" }, [ return e("div", { id: "page-events" }, [
e("div", { className: "col-lg-4"}, [ e("div", { className: "row" }, [
e(Hoverable), e("div", { className: "col-lg-4"}, [
e(Hoverable),
]),
e("div", { className: "col-lg-4"}, [
e(Clickable, {
id: "wait-class",
title: "Add class"
})
]),
e("div", { className: "col-lg-4"}, [
e(Clickable, {
id: "wait-class-random",
title: "Add class 2",
randomTimeout: true
})
])
]), ]),
e("div", { className: "col-lg-4"}, [ e("div", { className: "row" }, [
e(Clickable, { id: "wait-class" }) e("div", { className: "col-lg-4"}, [
]), e(Clickable, {
e("div", { className: "col-lg-4"}, [ id: "wait-no-class",
e(Clickable, { id: "wait-class-random", randomTimeout: true }) title: "Remove class",
show: true
})
]),
e("div", { className: "col-lg-4"}, [
e(Clickable, {
id: "wait-no-class-random",
title: "Remove class 2",
show: true,
randomTimeout: true
})
]),
e("div", { className: "col-lg-4"}, [
e(Appearable, {
id: "wait-element",
appear: true,
title: "Appearable"
})
]),
e("div", { className: "col-lg-4"}, [
e(Appearable, {
id: "wait-no-element",
appear: false,
title: "Disappearable"
})
])
]) ])
]) ])
} }

View File

@ -0,0 +1,13 @@
export default function random(min = 1000, max = 5000) {
const val = Math.random() * 1000 * 10;
if (val < min) {
return min;
}
if (val > max) {
return max;
}
return val;
}

View File

@ -52,9 +52,7 @@ func New(logger zerolog.Logger, settings Settings) *Runner {
} }
} }
func (r *Runner) Run() error { func (r *Runner) Run(ctx context.Context) error {
ctx := context.Background()
ctx = drivers.WithContext( ctx = drivers.WithContext(
ctx, ctx,
cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress)), cdp.NewDriver(cdp.WithAddress(r.settings.CDPAddress)),

View File

@ -3,7 +3,7 @@ LET doc = DOCUMENT(url, true)
WAIT_ELEMENT(doc, "#page-events") WAIT_ELEMENT(doc, "#page-events")
CLICK_ALL(doc, ".clickable button") CLICK_ALL(doc, "#wait-class-btn, #wait-class-random-btn")
WAIT_CLASS_ALL(doc, ".clickable .card-body div", "alert-success", 10000) WAIT_CLASS_ALL(doc, "#wait-class-content, #wait-class-random-content", "alert-success", 10000)
RETURN "" RETURN ""

View File

@ -0,0 +1,13 @@
LET url = @dynamic + "?redirect=/events"
LET doc = DOCUMENT(url, true)
LET pageSelector = "#page-events"
LET elemSelector = "#wait-element-content"
LET btnSelector = "#wait-element-btn"
WAIT_ELEMENT(doc, pageSelector)
CLICK(doc, btnSelector)
WAIT_ELEMENT(doc, elemSelector, 10000)
RETURN ELEMENT_EXISTS(doc, elemSelector) ? "" : "element not found"

View File

@ -0,0 +1,9 @@
LET url = @dynamic + "?redirect=/events"
LET doc = DOCUMENT(url, true)
WAIT_ELEMENT(doc, "#page-events")
CLICK_ALL(doc, "#wait-no-class-btn, #wait-no-class-random-btn")
WAIT_NO_CLASS_ALL(doc, "#wait-no-class-content, #wait-no-class-random-content", "alert-success", 10000)
RETURN ""

View File

@ -0,0 +1,14 @@
LET url = @dynamic + "?redirect=/events"
LET doc = DOCUMENT(url, true)
WAIT_ELEMENT(doc, "#page-events")
// with fixed timeout
CLICK(doc, "#wait-no-class-btn")
WAIT_NO_CLASS(doc, "#wait-no-class-content", "alert-success")
// with random timeout
CLICK(doc, "#wait-no-class-random-btn")
WAIT_NO_CLASS(doc, "#wait-no-class-random-content", "alert-success", 10000)
RETURN ""

View File

@ -0,0 +1,13 @@
LET url = @dynamic + "?redirect=/events"
LET doc = DOCUMENT(url, true)
LET pageSelector = "#page-events"
LET elemSelector = "#wait-no-element-content"
LET btnSelector = "#wait-no-element-btn"
WAIT_ELEMENT(doc, pageSelector)
CLICK(doc, btnSelector)
WAIT_NO_ELEMENT(doc, elemSelector, 10000)
RETURN ELEMENT_EXISTS(doc, elemSelector) ? "element should not be found" : ""

View File

@ -0,0 +1,20 @@
LET url = @dynamic + "?redirect=/events"
LET doc = DOCUMENT(url, true)
WAIT_ELEMENT(doc, "#page-events")
// with fixed timeout
LET b1 = ELEMENT(doc, "#wait-no-class-btn")
LET c1 = ELEMENT(doc, "#wait-no-class-content")
CLICK(b1)
WAIT_NO_CLASS(c1, "alert-success")
// with random timeout
LET b2 = ELEMENT(doc, "#wait-no-class-random-btn")
LET c2 = ELEMENT(doc, "#wait-no-class-random-content")
CLICK(b2)
WAIT_NO_CLASS(c2, "alert-success", 10000)
RETURN ""

View File

@ -526,44 +526,23 @@ func (doc *HTMLDocument) MoveMouseByXY(ctx context.Context, x, y values.Float) e
) )
} }
func (doc *HTMLDocument) WaitForSelector(ctx context.Context, selector values.String) error { func (doc *HTMLDocument) WaitForElement(ctx context.Context, selector values.String, when drivers.WaitEvent) error {
task := events.NewEvalWaitTask( task := events.NewEvalWaitTask(
doc.client, doc.client,
fmt.Sprintf(` fmt.Sprintf(
var el = document.querySelector(%s); `
if (el != null) { var el = document.querySelector(%s);
return true;
} if (el %s null) {
// null means we need to repeat return true;
return null; }
`, eval.ParamString(selector.String())),
events.DefaultPolling, // null means we need to repeat
) return null;
`,
_, err := task.Run(ctx)
return err
}
func (doc *HTMLDocument) WaitForClassBySelector(ctx context.Context, selector, class values.String) error {
task := events.NewEvalWaitTask(
doc.client,
fmt.Sprintf(`
var el = document.querySelector(%s);
if (el == null) {
return false;
}
var className = %s;
var found = el.className.split(' ').find(i => i === className);
if (found != null) {
return true;
}
// null means we need to repeat
return null;
`,
eval.ParamString(selector.String()), eval.ParamString(selector.String()),
eval.ParamString(class.String()), waitEventToEqOperator(when),
), ),
events.DefaultPolling, events.DefaultPolling,
) )
@ -573,22 +552,58 @@ func (doc *HTMLDocument) WaitForClassBySelector(ctx context.Context, selector, c
return err return err
} }
func (doc *HTMLDocument) WaitForClassBySelectorAll(ctx context.Context, selector, class values.String) error { func (doc *HTMLDocument) WaitForClassBySelector(ctx context.Context, selector, class values.String, when drivers.WaitEvent) error {
task := events.NewEvalWaitTask(
doc.client,
fmt.Sprintf(`
var el = document.querySelector(%s);
if (el == null) {
return false;
}
var className = %s;
var found = el.className.split(' ').find(i => i === className);
if (found %s null) {
return true;
}
// null means we need to repeat
return null;
`,
eval.ParamString(selector.String()),
eval.ParamString(class.String()),
waitEventToEqOperator(when),
),
events.DefaultPolling,
)
_, err := task.Run(ctx)
return err
}
func (doc *HTMLDocument) WaitForClassBySelectorAll(ctx context.Context, selector, class values.String, when drivers.WaitEvent) error {
task := events.NewEvalWaitTask( task := events.NewEvalWaitTask(
doc.client, doc.client,
fmt.Sprintf(` fmt.Sprintf(`
var elements = document.querySelectorAll(%s); var elements = document.querySelectorAll(%s);
if (elements == null || elements.length === 0) { if (elements == null || elements.length === 0) {
return false; return false;
} }
var className = %s; var className = %s;
var foundCount = 0; var foundCount = 0;
elements.forEach((el) => { elements.forEach((el) => {
var found = el.className.split(' ').find(i => i === className); var found = el.className.split(' ').find(i => i === className);
if (found != null) { if (found %s null) {
foundCount++; foundCount++;
} }
}); });
if (foundCount === elements.length) { if (foundCount === elements.length) {
return true; return true;
} }
@ -598,6 +613,7 @@ func (doc *HTMLDocument) WaitForClassBySelectorAll(ctx context.Context, selector
`, `,
eval.ParamString(selector.String()), eval.ParamString(selector.String()),
eval.ParamString(class.String()), eval.ParamString(class.String()),
waitEventToEqOperator(when),
), ),
events.DefaultPolling, events.DefaultPolling,
) )

View File

@ -695,7 +695,7 @@ func (el *HTMLElement) ExistsBySelector(ctx context.Context, selector values.Str
return values.True return values.True
} }
func (el *HTMLElement) WaitForClass(ctx context.Context, class values.String) error { func (el *HTMLElement) WaitForClass(ctx context.Context, class values.String, when drivers.WaitEvent) error {
task := events.NewWaitTask( task := events.NewWaitTask(
func(ctx2 context.Context) (core.Value, error) { func(ctx2 context.Context) (core.Value, error) {
current := el.GetAttribute(ctx2, "class") current := el.GetAttribute(ctx2, "class")
@ -708,9 +708,28 @@ func (el *HTMLElement) WaitForClass(ctx context.Context, class values.String) er
classStr := string(class) classStr := string(class)
classes := strings.Split(string(str), " ") classes := strings.Split(string(str), " ")
for _, c := range classes { if when != drivers.WaitEventAbsence {
if c == classStr { for _, c := range classes {
return values.True, nil if c == classStr {
// The value does not really matter if it's not None
// None indicates that operation needs to be repeated
return values.True, nil
}
}
} else {
var found values.Boolean
for _, c := range classes {
if c == classStr {
found = values.True
break
}
}
if found == values.False {
// The value does not really matter if it's not None
// None indicates that operation needs to be repeated
return values.False, nil
} }
} }

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"context" "context"
"errors" "errors"
"github.com/MontFerret/ferret/pkg/drivers"
"math" "math"
"strings" "strings"
@ -402,3 +403,11 @@ func createEventBroker(client *cdp.Client) (*events.EventBroker, error) {
return broker, nil return broker, nil
} }
func waitEventToEqOperator(when drivers.WaitEvent) string {
if when == drivers.WaitEventAbsence {
return "=="
}
return "!="
}

View File

@ -225,15 +225,15 @@ func (doc *HTMLDocument) WaitForNavigation(_ context.Context) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) WaitForSelector(_ context.Context, _ values.String) error { func (doc *HTMLDocument) WaitForElement(_ context.Context, _ values.String, _ drivers.WaitEvent) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) WaitForClassBySelector(_ context.Context, _, _ values.String) error { func (doc *HTMLDocument) WaitForClassBySelector(_ context.Context, _, _ values.String, _ drivers.WaitEvent) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (doc *HTMLDocument) WaitForClassBySelectorAll(_ context.Context, _, _ values.String) error { func (doc *HTMLDocument) WaitForClassBySelectorAll(_ context.Context, _, _ values.String, _ drivers.WaitEvent) error {
return core.ErrNotSupported return core.ErrNotSupported
} }

View File

@ -309,7 +309,7 @@ func (nd *HTMLElement) Hover(_ context.Context) error {
return core.ErrNotSupported return core.ErrNotSupported
} }
func (nd *HTMLElement) WaitForClass(_ context.Context, _ values.String) error { func (nd *HTMLElement) WaitForClass(_ context.Context, _ values.String, _ drivers.WaitEvent) error {
return core.ErrNotSupported return core.ErrNotSupported
} }

View File

@ -10,6 +10,9 @@ import (
) )
type ( type (
// WaitEvent is an enum that represents what event is needed to wait for
WaitEvent int
// Node is an interface from which a number of DOM API object types inherit. // Node is an interface from which a number of DOM API object types inherit.
// It allows those types to be treated similarly; // It allows those types to be treated similarly;
// for example, inheriting the same set of methods, or being tested in the same way. // for example, inheriting the same set of methods, or being tested in the same way.
@ -74,7 +77,7 @@ type (
Hover(ctx context.Context) error Hover(ctx context.Context) error
WaitForClass(ctx context.Context, class values.String) error WaitForClass(ctx context.Context, class values.String, when WaitEvent) error
} }
// The Document interface represents any web page loaded in the browser // The Document interface represents any web page loaded in the browser
@ -120,10 +123,18 @@ type (
WaitForNavigation(ctx context.Context) error WaitForNavigation(ctx context.Context) error
WaitForSelector(ctx context.Context, selector values.String) error WaitForElement(ctx context.Context, selector values.String, when WaitEvent) error
WaitForClassBySelector(ctx context.Context, selector, class values.String) error WaitForClassBySelector(ctx context.Context, selector, class values.String, when WaitEvent) error
WaitForClassBySelectorAll(ctx context.Context, selector, class values.String) error WaitForClassBySelectorAll(ctx context.Context, selector, class values.String, when WaitEvent) error
} }
) )
const (
// Event indicating to wait for value to appear
WaitEventPresence = 0
// Event indicating to wait for value to disappear
WaitEventAbsence = 1
)

View File

@ -14,36 +14,39 @@ const defaultTimeout = 5000
func NewLib() map[string]core.Function { func NewLib() map[string]core.Function {
return map[string]core.Function{ return map[string]core.Function{
"CLICK": Click, "CLICK": Click,
"CLICK_ALL": ClickAll, "CLICK_ALL": ClickAll,
"DOCUMENT": Document, "DOCUMENT": Document,
"DOWNLOAD": Download, "DOWNLOAD": Download,
"ELEMENT": Element, "ELEMENT": Element,
"ELEMENT_EXISTS": ElementExists, "ELEMENT_EXISTS": ElementExists,
"ELEMENTS": Elements, "ELEMENTS": Elements,
"ELEMENTS_COUNT": ElementsCount, "ELEMENTS_COUNT": ElementsCount,
"HOVER": Hover, "HOVER": Hover,
"INNER_HTML": InnerHTML, "INNER_HTML": InnerHTML,
"INNER_HTML_ALL": InnerHTMLAll, "INNER_HTML_ALL": InnerHTMLAll,
"INNER_TEXT": InnerText, "INNER_TEXT": InnerText,
"INNER_TEXT_ALL": InnerTextAll, "INNER_TEXT_ALL": InnerTextAll,
"INPUT": Input, "INPUT": Input,
"MOUSE": MouseMoveXY, "MOUSE": MouseMoveXY,
"NAVIGATE": Navigate, "NAVIGATE": Navigate,
"NAVIGATE_BACK": NavigateBack, "NAVIGATE_BACK": NavigateBack,
"NAVIGATE_FORWARD": NavigateForward, "NAVIGATE_FORWARD": NavigateForward,
"PAGINATION": Pagination, "PAGINATION": Pagination,
"PDF": PDF, "PDF": PDF,
"SCREENSHOT": Screenshot, "SCREENSHOT": Screenshot,
"SCROLL": ScrollXY, "SCROLL": ScrollXY,
"SCROLL_BOTTOM": ScrollBottom, "SCROLL_BOTTOM": ScrollBottom,
"SCROLL_ELEMENT": ScrollInto, "SCROLL_ELEMENT": ScrollInto,
"SCROLL_TOP": ScrollTop, "SCROLL_TOP": ScrollTop,
"SELECT": Select, "SELECT": Select,
"WAIT_ELEMENT": WaitElement, "WAIT_ELEMENT": WaitElement,
"WAIT_CLASS": WaitClass, "WAIT_NO_ELEMENT": WaitNoElement,
"WAIT_CLASS_ALL": WaitClassAll, "WAIT_CLASS": WaitClass,
"WAIT_NAVIGATION": WaitNavigation, "WAIT_NO_CLASS": WaitNoClass,
"WAIT_CLASS_ALL": WaitClassAll,
"WAIT_NO_CLASS_ALL": WaitNoClassAll,
"WAIT_NAVIGATION": WaitNavigation,
} }
} }

View File

@ -18,6 +18,23 @@ import (
// @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(ctx context.Context, args ...core.Value) (core.Value, error) { func WaitClass(ctx context.Context, args ...core.Value) (core.Value, error) {
return waitClassWhen(ctx, args, drivers.WaitEventPresence)
}
// WaitClass waits for a class to disappear on a given element.
// Stops the execution until the navigation ends or operation times out.
// @param docOrEl (HTMLDocument|HTMLElement) - Target document or element.
// @param selectorOrClass (String) - If document is passed, this param must represent an element selector.
// Otherwise target class.
// @param classOrTimeout (String|Int, optional) - If document is passed, this param must represent target class name.
// Otherwise timeout.
// @param timeout (Int, optional) - If document is passed, this param must represent timeout.
// Otherwise not passed.
func WaitNoClass(ctx context.Context, args ...core.Value) (core.Value, error) {
return waitClassWhen(ctx, args, drivers.WaitEventAbsence)
}
func waitClassWhen(ctx context.Context, args []core.Value, when drivers.WaitEvent) (core.Value, error) {
err := core.ValidateArgs(args, 2, 4) err := core.ValidateArgs(args, 2, 4)
if err != nil { if err != nil {
@ -74,7 +91,7 @@ func WaitClass(ctx context.Context, args ...core.Value) (core.Value, error) {
ctx, fn := waitTimeout(ctx, timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn() defer fn()
return values.None, doc.WaitForClassBySelector(ctx, selector, class) return values.None, doc.WaitForClassBySelector(ctx, selector, class, when)
} }
el := arg1.(drivers.HTMLElement) el := arg1.(drivers.HTMLElement)
@ -93,5 +110,5 @@ func WaitClass(ctx context.Context, args ...core.Value) (core.Value, error) {
ctx, fn := waitTimeout(ctx, timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn() defer fn()
return values.None, el.WaitForClass(ctx, class) return values.None, el.WaitForClass(ctx, class, when)
} }

View File

@ -2,6 +2,7 @@ package html
import ( import (
"context" "context"
"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"
@ -14,6 +15,20 @@ import (
// @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(ctx context.Context, args ...core.Value) (core.Value, error) { func WaitClassAll(ctx context.Context, args ...core.Value) (core.Value, error) {
return waitClassAllWhen(ctx, args, drivers.WaitEventPresence)
}
// WaitClassAll waits for a class to disappear on all matched elements.
// Stops the execution until the navigation ends or operation times out.
// @param doc (HTMLDocument) - Parent document.
// @param selector (String) - String of CSS selector.
// @param class (String) - String of target CSS class.
// @param timeout (Int, optional) - Optional timeout.
func WaitNoClassAll(ctx context.Context, args ...core.Value) (core.Value, error) {
return waitClassAllWhen(ctx, args, drivers.WaitEventAbsence)
}
func waitClassAllWhen(ctx context.Context, args []core.Value, when drivers.WaitEvent) (core.Value, error) {
err := core.ValidateArgs(args, 3, 4) err := core.ValidateArgs(args, 3, 4)
if err != nil { if err != nil {
@ -57,5 +72,5 @@ func WaitClassAll(ctx context.Context, args ...core.Value) (core.Value, error) {
ctx, fn := waitTimeout(ctx, timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn() defer fn()
return values.None, doc.WaitForClassBySelectorAll(ctx, selector, class) return values.None, doc.WaitForClassBySelectorAll(ctx, selector, class, when)
} }

View File

@ -2,6 +2,7 @@ package html
import ( import (
"context" "context"
"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"
@ -13,6 +14,19 @@ import (
// @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(ctx context.Context, args ...core.Value) (core.Value, error) { func WaitElement(ctx context.Context, args ...core.Value) (core.Value, error) {
return waitElementWhen(ctx, args, drivers.WaitEventPresence)
}
// WaitNoElements waits for element to disappear in the DOM.
// Stops the execution until it does not find an element or operation times out.
// @param doc (HTMLDocument) - Driver HTMLDocument.
// @param selector (String) - Target element's selector.
// @param timeout (Int, optional) - Optional timeout. Default 5000 ms.
func WaitNoElement(ctx context.Context, args ...core.Value) (core.Value, error) {
return waitElementWhen(ctx, args, drivers.WaitEventAbsence)
}
func waitElementWhen(ctx context.Context, args []core.Value, when drivers.WaitEvent) (core.Value, error) {
err := core.ValidateArgs(args, 2, 3) err := core.ValidateArgs(args, 2, 3)
if err != nil { if err != nil {
@ -41,5 +55,5 @@ func WaitElement(ctx context.Context, args ...core.Value) (core.Value, error) {
ctx, fn := waitTimeout(ctx, timeout) ctx, fn := waitTimeout(ctx, timeout)
defer fn() defer fn()
return values.None, doc.WaitForSelector(ctx, values.NewString(selector)) return values.None, doc.WaitForElement(ctx, values.NewString(selector), when)
} }