Skip to content
This repository has been archived by the owner on Jan 30, 2025. It is now read-only.

Remove the timeout option from isVisible and isHidden #1111

Merged
merged 9 commits into from
Nov 28, 2023
12 changes: 6 additions & 6 deletions common/element_handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,12 +382,12 @@ func (h *ElementHandle) isEnabled(apiCtx context.Context, timeout time.Duration)
return h.waitForElementState(apiCtx, []string{"enabled"}, timeout)
}

func (h *ElementHandle) isHidden(apiCtx context.Context, timeout time.Duration) (bool, error) {
return h.waitForElementState(apiCtx, []string{"hidden"}, timeout)
func (h *ElementHandle) isHidden(apiCtx context.Context) (bool, error) {
return h.waitForElementState(apiCtx, []string{"hidden"}, 0)
}

func (h *ElementHandle) isVisible(apiCtx context.Context, timeout time.Duration) (bool, error) {
return h.waitForElementState(apiCtx, []string{"visible"}, timeout)
func (h *ElementHandle) isVisible(apiCtx context.Context) (bool, error) {
return h.waitForElementState(apiCtx, []string{"visible"}, 0)
}

func (h *ElementHandle) offsetPosition(apiCtx context.Context, offset *Position) (*Position, error) {
Expand Down Expand Up @@ -949,7 +949,7 @@ func (h *ElementHandle) IsEnabled() bool {

// IsHidden checks if the element is hidden.
func (h *ElementHandle) IsHidden() bool {
result, err := h.isHidden(h.ctx, 0)
result, err := h.isHidden(h.ctx)
if err != nil && !errors.Is(err, ErrTimedOut) { // We don't care anout timeout errors here!
k6ext.Panic(h.ctx, "checking element is hidden: %w", err)
}
Expand All @@ -958,7 +958,7 @@ func (h *ElementHandle) IsHidden() bool {

// IsVisible checks if the element is visible.
func (h *ElementHandle) IsVisible() bool {
result, err := h.isVisible(h.ctx, 0)
result, err := h.isVisible(h.ctx)
if err != nil && !errors.Is(err, ErrTimedOut) { // We don't care anout timeout errors here!
k6ext.Panic(h.ctx, "checking element is visible: %w", err)
}
Expand Down
12 changes: 6 additions & 6 deletions common/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -1245,7 +1245,7 @@ func (f *Frame) isDisabled(selector string, opts *FrameIsDisabledOptions) (bool,
func (f *Frame) IsHidden(selector string, opts goja.Value) (bool, error) {
f.log.Debugf("Frame:IsHidden", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)

popts := NewFrameIsHiddenOptions(f.defaultTimeout())
popts := NewFrameIsHiddenOptions()
if err := popts.Parse(f.ctx, opts); err != nil {
return false, fmt.Errorf("parsing is hidden options: %w", err)
}
Expand All @@ -1259,8 +1259,8 @@ func (f *Frame) IsHidden(selector string, opts goja.Value) (bool, error) {

func (f *Frame) isHidden(selector string, opts *FrameIsHiddenOptions) (bool, error) {
isHidden := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
v, err := handle.isHidden(apiCtx, 0) // Zero timeout when checking state
if errors.Is(err, ErrTimedOut) { // We don't care about timeout errors here!
v, err := handle.isHidden(apiCtx) // Zero timeout when checking state
if errors.Is(err, ErrTimedOut) { // We don't care about timeout errors here!
return v, nil
}
return v, err
Expand All @@ -1278,7 +1278,7 @@ func (f *Frame) isHidden(selector string, opts *FrameIsHiddenOptions) (bool, err
func (f *Frame) IsVisible(selector string, opts goja.Value) (bool, error) {
f.log.Debugf("Frame:IsVisible", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)

popts := NewFrameIsVisibleOptions(f.defaultTimeout())
popts := NewFrameIsVisibleOptions()
if err := popts.Parse(f.ctx, opts); err != nil {
return false, fmt.Errorf("parsing is visible options: %w", err)
}
Expand All @@ -1292,8 +1292,8 @@ func (f *Frame) IsVisible(selector string, opts goja.Value) (bool, error) {

func (f *Frame) isVisible(selector string, opts *FrameIsVisibleOptions) (bool, error) {
isVisible := func(apiCtx context.Context, handle *ElementHandle) (any, error) {
v, err := handle.isVisible(apiCtx, 0) // Zero timeout when checking state
if errors.Is(err, ErrTimedOut) { // We don't care about timeout errors here!
v, err := handle.isVisible(apiCtx) // Zero timeout when checking state
if errors.Is(err, ErrTimedOut) { // We don't care about timeout errors here!
return v, nil
}
return v, err
Expand Down
119 changes: 33 additions & 86 deletions common/frame_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ type FrameIsEnabledOptions struct {
}

type FrameIsHiddenOptions struct {
FrameBaseOptions
Strict bool `json:"strict"`
}

type FrameIsVisibleOptions struct {
FrameBaseOptions
Strict bool `json:"strict"`
}

type FramePressOptions struct {
Expand Down Expand Up @@ -223,19 +223,10 @@ func NewFrameCheckOptions(defaultTimeout time.Duration) *FrameCheckOptions {
}

func (o *FrameCheckOptions) Parse(ctx context.Context, opts goja.Value) error {
rt := k6ext.Runtime(ctx)
if err := o.ElementHandleBasePointerOptions.Parse(ctx, opts); err != nil {
return err
}
if opts != nil && !goja.IsUndefined(opts) && !goja.IsNull(opts) {
opts := opts.ToObject(rt)
for _, k := range opts.Keys() {
switch k {
case "strict":
o.Strict = opts.Get(k).ToBoolean()
}
}
}
o.Strict = parseStrict(ctx, opts)
return nil
}

Expand All @@ -247,19 +238,10 @@ func NewFrameClickOptions(defaultTimeout time.Duration) *FrameClickOptions {
}

func (o *FrameClickOptions) Parse(ctx context.Context, opts goja.Value) error {
rt := k6ext.Runtime(ctx)
if err := o.ElementHandleClickOptions.Parse(ctx, opts); err != nil {
return err
}
if opts != nil && !goja.IsUndefined(opts) && !goja.IsNull(opts) {
opts := opts.ToObject(rt)
for _, k := range opts.Keys() {
switch k {
case "strict":
o.Strict = opts.Get(k).ToBoolean()
}
}
}
o.Strict = parseStrict(ctx, opts)
return nil
}

Expand All @@ -271,19 +253,10 @@ func NewFrameDblClickOptions(defaultTimeout time.Duration) *FrameDblclickOptions
}

func (o *FrameDblclickOptions) Parse(ctx context.Context, opts goja.Value) error {
rt := k6ext.Runtime(ctx)
if err := o.ElementHandleDblclickOptions.Parse(ctx, opts); err != nil {
return err
}
if opts != nil && !goja.IsUndefined(opts) && !goja.IsNull(opts) {
opts := opts.ToObject(rt)
for _, k := range opts.Keys() {
switch k {
case "strict":
o.Strict = opts.Get(k).ToBoolean()
}
}
}
o.Strict = parseStrict(ctx, opts)
return nil
}

Expand All @@ -295,19 +268,10 @@ func NewFrameFillOptions(defaultTimeout time.Duration) *FrameFillOptions {
}

func (o *FrameFillOptions) Parse(ctx context.Context, opts goja.Value) error {
rt := k6ext.Runtime(ctx)
if err := o.ElementHandleBaseOptions.Parse(ctx, opts); err != nil {
return err
}
if opts != nil && !goja.IsUndefined(opts) && !goja.IsNull(opts) {
opts := opts.ToObject(rt)
for _, k := range opts.Keys() {
switch k {
case "strict":
o.Strict = opts.Get(k).ToBoolean()
}
}
}
o.Strict = parseStrict(ctx, opts)
return nil
}

Expand Down Expand Up @@ -348,19 +312,10 @@ func NewFrameHoverOptions(defaultTimeout time.Duration) *FrameHoverOptions {
}

func (o *FrameHoverOptions) Parse(ctx context.Context, opts goja.Value) error {
rt := k6ext.Runtime(ctx)
if err := o.ElementHandleHoverOptions.Parse(ctx, opts); err != nil {
return err
}
if opts != nil && !goja.IsUndefined(opts) && !goja.IsNull(opts) {
opts := opts.ToObject(rt)
for _, k := range opts.Keys() {
switch k {
case "strict":
o.Strict = opts.Get(k).ToBoolean()
}
}
}
o.Strict = parseStrict(ctx, opts)
return nil
}

Expand Down Expand Up @@ -455,29 +410,23 @@ func (o *FrameIsEnabledOptions) Parse(ctx context.Context, opts goja.Value) erro
return nil
}

func NewFrameIsHiddenOptions(defaultTimeout time.Duration) *FrameIsHiddenOptions {
return &FrameIsHiddenOptions{
FrameBaseOptions: *NewFrameBaseOptions(defaultTimeout),
}
// NewFrameIsHiddenOptions creates and returns a new instance of FrameIsHiddenOptions.
func NewFrameIsHiddenOptions() *FrameIsHiddenOptions {
return &FrameIsHiddenOptions{}
}

func (o *FrameIsHiddenOptions) Parse(ctx context.Context, opts goja.Value) error {
if err := o.FrameBaseOptions.Parse(ctx, opts); err != nil {
return err
}
o.Strict = parseStrict(ctx, opts)
return nil
}

func NewFrameIsVisibleOptions(defaultTimeout time.Duration) *FrameIsVisibleOptions {
return &FrameIsVisibleOptions{
FrameBaseOptions: *NewFrameBaseOptions(defaultTimeout),
}
// NewFrameIsVisibleOptions creates and returns a new instance of FrameIsVisibleOptions.
func NewFrameIsVisibleOptions() *FrameIsVisibleOptions {
return &FrameIsVisibleOptions{}
}

func (o *FrameIsVisibleOptions) Parse(ctx context.Context, opts goja.Value) error {
if err := o.FrameBaseOptions.Parse(ctx, opts); err != nil {
return err
}
o.Strict = parseStrict(ctx, opts)
return nil
}

Expand All @@ -502,19 +451,10 @@ func NewFrameSelectOptionOptions(defaultTimeout time.Duration) *FrameSelectOptio
}

func (o *FrameSelectOptionOptions) Parse(ctx context.Context, opts goja.Value) error {
rt := k6ext.Runtime(ctx)
if err := o.ElementHandleBaseOptions.Parse(ctx, opts); err != nil {
return err
}
if opts != nil && !goja.IsUndefined(opts) && !goja.IsNull(opts) {
opts := opts.ToObject(rt)
for _, k := range opts.Keys() {
switch k {
case "strict":
o.Strict = opts.Get(k).ToBoolean()
}
}
}
o.Strict = parseStrict(ctx, opts)
return nil
}

Expand Down Expand Up @@ -611,19 +551,10 @@ func NewFrameUncheckOptions(defaultTimeout time.Duration) *FrameUncheckOptions {
}

func (o *FrameUncheckOptions) Parse(ctx context.Context, opts goja.Value) error {
rt := k6ext.Runtime(ctx)
if err := o.ElementHandleBasePointerOptions.Parse(ctx, opts); err != nil {
return err
}
if opts != nil && !goja.IsUndefined(opts) && !goja.IsNull(opts) {
opts := opts.ToObject(rt)
for _, k := range opts.Keys() {
switch k {
case "strict":
o.Strict = opts.Get(k).ToBoolean()
}
}
}
o.Strict = parseStrict(ctx, opts)
return nil
}

Expand Down Expand Up @@ -761,3 +692,19 @@ func NewFrameDispatchEventOptions(defaultTimeout time.Duration) *FrameDispatchEv
FrameBaseOptions: NewFrameBaseOptions(defaultTimeout),
}
}

func parseStrict(ctx context.Context, opts goja.Value) bool {
var strict bool

rt := k6ext.Runtime(ctx)
if opts != nil && !goja.IsUndefined(opts) && !goja.IsNull(opts) {
opts := opts.ToObject(rt)
for _, k := range opts.Keys() {
if k == "strict" {
strict = opts.Get(k).ToBoolean()
}
}
}

return strict
}
38 changes: 8 additions & 30 deletions common/locator.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,52 +233,30 @@ func (l *Locator) isDisabled(opts *FrameIsDisabledOptions) (bool, error) {

// IsVisible returns true if the element matches the locator's
// selector and is visible. Otherwise, returns false.
func (l *Locator) IsVisible(opts goja.Value) bool {
l.log.Debugf("Locator:IsVisible", "fid:%s furl:%q sel:%q opts:%+v", l.frame.ID(), l.frame.URL(), l.selector, opts)
func (l *Locator) IsVisible() (bool, error) {
l.log.Debugf("Locator:IsVisible", "fid:%s furl:%q sel:%q", l.frame.ID(), l.frame.URL(), l.selector)

copts := NewFrameIsVisibleOptions(l.frame.defaultTimeout())
if err := copts.Parse(l.ctx, opts); err != nil {
k6ext.Panic(l.ctx, "parsing is visible options: %w", err)
}
visible, err := l.isVisible(copts)
visible, err := l.frame.isVisible(l.selector, &FrameIsVisibleOptions{Strict: true})
if err != nil {
k6ext.Panic(l.ctx, "checking is %q visible: %w", l.selector, err)
return false, fmt.Errorf("checking is %q visible: %w", l.selector, err)
}

return visible
}

// isVisible is like IsVisible but takes parsed options and does not
// throw an error.
func (l *Locator) isVisible(opts *FrameIsVisibleOptions) (bool, error) {
opts.Strict = true
return l.frame.isVisible(l.selector, opts)
return visible, nil
}

// IsHidden returns true if the element matches the locator's
// selector and is hidden. Otherwise, returns false.
func (l *Locator) IsHidden(opts goja.Value) (bool, error) {
l.log.Debugf("Locator:IsHidden", "fid:%s furl:%q sel:%q opts:%+v", l.frame.ID(), l.frame.URL(), l.selector, opts)
func (l *Locator) IsHidden() (bool, error) {
l.log.Debugf("Locator:IsHidden", "fid:%s furl:%q sel:%q", l.frame.ID(), l.frame.URL(), l.selector)

copts := NewFrameIsHiddenOptions(l.frame.defaultTimeout())
if err := copts.Parse(l.ctx, opts); err != nil {
return false, fmt.Errorf("parsing is hidden options: %w", err)
}
hidden, err := l.isHidden(copts)
hidden, err := l.frame.isHidden(l.selector, &FrameIsHiddenOptions{Strict: true})
if err != nil {
return false, fmt.Errorf("checking is %q hidden: %w", l.selector, err)
}

return hidden, nil
}

// isHidden is like IsHidden but takes parsed options and does not
// throw an error.
func (l *Locator) isHidden(opts *FrameIsHiddenOptions) (bool, error) {
opts.Strict = true
return l.frame.isHidden(l.selector, opts)
}

// Fill out the element using locator's selector with strict mode on.
func (l *Locator) Fill(value string, opts goja.Value) {
l.log.Debugf(
Expand Down
4 changes: 2 additions & 2 deletions tests/locator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ func TestLocatorElementState(t *testing.T) {
{
"hidden",
`() => document.getElementById('inputText').style.visibility = 'hidden'`,
func(l *common.Locator) bool { resp, _ := l.IsHidden(nil); return !resp },
func(l *common.Locator) bool { resp, _ := l.IsHidden(); return !resp },
},
{
"readOnly",
Expand All @@ -354,7 +354,7 @@ func TestLocatorElementState(t *testing.T) {
{
"visible",
`() => document.getElementById('inputText').style.visibility = 'hidden'`,
func(l *common.Locator) bool { return l.IsVisible(nil) },
func(l *common.Locator) bool { resp, _ := l.IsVisible(); return resp },
},
}

Expand Down
8 changes: 2 additions & 6 deletions tests/page_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1270,9 +1270,7 @@ func TestPageIsVisible(t *testing.T) {
name: "first_div",
selector: "div",
options: common.FrameIsVisibleOptions{
FrameBaseOptions: common.FrameBaseOptions{
Strict: true,
},
Strict: true,
},
wantErr: "error:strictmodeviolation",
},
Expand Down Expand Up @@ -1337,9 +1335,7 @@ func TestPageIsHidden(t *testing.T) {
name: "first_div",
selector: "div",
options: common.FrameIsVisibleOptions{
FrameBaseOptions: common.FrameBaseOptions{
Strict: true,
},
Strict: true,
},
wantErr: "error:strictmodeviolation",
},
Expand Down