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

Commit 97ef8a7

Browse files
author
Ivan Mirić
authored
Merge pull request #299 from grafana/fix/innertext-innerhtml
Fix `{Page,Frame}.{innerHTML,innerText,textContent}` panic
2 parents 0d82dd4 + 65ab3c2 commit 97ef8a7

File tree

2 files changed

+164
-15
lines changed

2 files changed

+164
-15
lines changed

common/frame.go

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -975,6 +975,7 @@ func (f *Frame) Hover(selector string, opts goja.Value) {
975975
applySlowMo(f.ctx)
976976
}
977977

978+
// InnerHTML returns the innerHTML attribute of the element located by selector.
978979
func (f *Frame) InnerHTML(selector string, opts goja.Value) string {
979980
f.log.Debugf("Frame:InnerHTML", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)
980981

@@ -995,13 +996,24 @@ func (f *Frame) InnerHTML(selector string, opts goja.Value) string {
995996
}
996997

997998
applySlowMo(f.ctx)
998-
return value.(string)
999+
1000+
if value == nil {
1001+
return ""
1002+
}
1003+
1004+
val, ok := value.(goja.Value)
1005+
if !ok {
1006+
k6Throw(f.ctx, "unexpected innerHTML value type: %T", value)
1007+
}
1008+
1009+
return val.ToString().String()
9991010
}
10001011

1012+
// InnerText returns the innerText attribute of the element located by selector.
10011013
func (f *Frame) InnerText(selector string, opts goja.Value) string {
10021014
f.log.Debugf("Frame:InnerText", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)
10031015

1004-
parsedOpts := NewFrameInnerHTMLOptions(f.defaultTimeout())
1016+
parsedOpts := NewFrameInnerTextOptions(f.defaultTimeout())
10051017
if err := parsedOpts.Parse(f.ctx, opts); err != nil {
10061018
k6Throw(f.ctx, "%w", err)
10071019
}
@@ -1018,7 +1030,17 @@ func (f *Frame) InnerText(selector string, opts goja.Value) string {
10181030
}
10191031

10201032
applySlowMo(f.ctx)
1021-
return value.(string)
1033+
1034+
if value == nil {
1035+
return ""
1036+
}
1037+
1038+
val, ok := value.(goja.Value)
1039+
if !ok {
1040+
k6Throw(f.ctx, "unexpected innerText value type: %T", value)
1041+
}
1042+
1043+
return val.ToString().String()
10221044
}
10231045

10241046
func (f *Frame) InputValue(selector string, opts goja.Value) string {
@@ -1403,6 +1425,7 @@ func (f *Frame) Tap(selector string, opts goja.Value) {
14031425
applySlowMo(f.ctx)
14041426
}
14051427

1428+
// TextContent returns the textContent attribute of the element located by selector.
14061429
func (f *Frame) TextContent(selector string, opts goja.Value) string {
14071430
f.log.Debugf("Frame:TextContent", "fid:%s furl:%q sel:%q", f.ID(), f.URL(), selector)
14081431

@@ -1423,7 +1446,17 @@ func (f *Frame) TextContent(selector string, opts goja.Value) string {
14231446
}
14241447

14251448
applySlowMo(f.ctx)
1426-
return value.(string)
1449+
1450+
if value == nil {
1451+
return ""
1452+
}
1453+
1454+
val, ok := value.(goja.Value)
1455+
if !ok {
1456+
k6Throw(f.ctx, "unexpected textContent value type: %T", value)
1457+
}
1458+
1459+
return val.ToString().String()
14271460
}
14281461

14291462
func (f *Frame) Title() string {

tests/page_test.go

Lines changed: 127 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ type emulateMediaOpts struct {
4141
ReducedMotion string `js:"reducedMotion"`
4242
}
4343

44+
type jsFrameBaseOpts struct {
45+
Timeout string
46+
Strict bool
47+
}
48+
49+
const sampleHTML = `<div><b>Test</b><ol><li><i>One</i></li></ol></div>`
50+
4451
func TestPageEmulateMedia(t *testing.T) {
4552
tb := newTestBrowser(t)
4653
p := tb.NewPage(nil)
@@ -106,18 +113,8 @@ func TestPageEvaluate(t *testing.T) {
106113
t.Run(tc.name, func(t *testing.T) {
107114
t.Parallel()
108115

109-
// Not using assert.Panics* because we want to match on an error substring.
110116
defer func() {
111-
r := recover()
112-
require.NotNil(t, r)
113-
require.IsType(t, &goja.Object{}, r)
114-
gotObj, _ := r.(*goja.Object)
115-
got := gotObj.Export()
116-
expErr := fmt.Errorf("%w", errors.New(tc.errMsg))
117-
require.IsType(t, expErr, got)
118-
gotErr, ok := got.(error)
119-
require.True(t, ok)
120-
assert.Contains(t, gotErr.Error(), expErr.Error())
117+
assertPanicErrorContains(t, recover(), tc.errMsg)
121118
}()
122119

123120
tb := newTestBrowser(t)
@@ -182,6 +179,111 @@ func TestPageGotoWaitUntilDOMContentLoaded(t *testing.T) {
182179
assert.EqualValues(t, "DOMContentLoaded", actual[0], `expected "DOMContentLoaded" event to have fired`)
183180
}
184181

182+
func TestPageInnerHTML(t *testing.T) {
183+
t.Parallel()
184+
185+
t.Run("ok", func(t *testing.T) {
186+
t.Parallel()
187+
188+
p := newTestBrowser(t).NewPage(nil)
189+
p.SetContent(sampleHTML, nil)
190+
assert.Equal(t, `<b>Test</b><ol><li><i>One</i></li></ol>`, p.InnerHTML("div", nil))
191+
})
192+
193+
t.Run("err_empty_selector", func(t *testing.T) {
194+
t.Parallel()
195+
196+
defer func() {
197+
assertPanicErrorContains(t, recover(), "The provided selector is empty")
198+
}()
199+
200+
p := newTestBrowser(t).NewPage(nil)
201+
p.InnerHTML("", nil)
202+
t.Error("did not panic")
203+
})
204+
205+
t.Run("err_wrong_selector", func(t *testing.T) {
206+
t.Parallel()
207+
208+
tb := newTestBrowser(t)
209+
p := tb.NewPage(nil)
210+
p.SetContent(sampleHTML, nil)
211+
assert.Equal(t, "", p.InnerHTML("p", tb.rt.ToValue(jsFrameBaseOpts{
212+
Timeout: "100",
213+
})))
214+
})
215+
}
216+
217+
func TestPageInnerText(t *testing.T) {
218+
t.Parallel()
219+
220+
t.Run("ok", func(t *testing.T) {
221+
t.Parallel()
222+
223+
p := newTestBrowser(t).NewPage(nil)
224+
p.SetContent(sampleHTML, nil)
225+
assert.Equal(t, "Test\nOne", p.InnerText("div", nil))
226+
})
227+
228+
t.Run("err_empty_selector", func(t *testing.T) {
229+
t.Parallel()
230+
231+
defer func() {
232+
assertPanicErrorContains(t, recover(), "The provided selector is empty")
233+
}()
234+
235+
p := newTestBrowser(t).NewPage(nil)
236+
p.InnerText("", nil)
237+
t.Error("did not panic")
238+
})
239+
240+
t.Run("err_wrong_selector", func(t *testing.T) {
241+
t.Parallel()
242+
243+
tb := newTestBrowser(t)
244+
p := tb.NewPage(nil)
245+
p.SetContent(sampleHTML, nil)
246+
assert.Equal(t, "", p.InnerText("p", tb.rt.ToValue(jsFrameBaseOpts{
247+
Timeout: "100",
248+
})))
249+
})
250+
}
251+
252+
func TestPageTextContent(t *testing.T) {
253+
t.Parallel()
254+
255+
t.Run("ok", func(t *testing.T) {
256+
t.Parallel()
257+
258+
p := newTestBrowser(t).NewPage(nil)
259+
p.SetContent(sampleHTML, nil)
260+
assert.Equal(t, "TestOne", p.TextContent("div", nil))
261+
})
262+
263+
t.Run("err_empty_selector", func(t *testing.T) {
264+
t.Parallel()
265+
266+
defer func() {
267+
assertPanicErrorContains(t, recover(), "The provided selector is empty")
268+
}()
269+
270+
p := newTestBrowser(t).NewPage(nil)
271+
p.TextContent("", nil)
272+
t.Error("did not panic")
273+
})
274+
275+
t.Run("err_wrong_selector", func(t *testing.T) {
276+
t.Parallel()
277+
278+
tb := newTestBrowser(t)
279+
p := tb.NewPage(nil)
280+
p.SetContent(sampleHTML, nil)
281+
assert.Equal(t, "", p.TextContent("p", tb.rt.ToValue(jsFrameBaseOpts{
282+
Timeout: "100",
283+
})))
284+
})
285+
}
286+
185287
func TestPageInputValue(t *testing.T) {
186288
p := newTestBrowser(t).NewPage(nil)
187289

@@ -535,3 +637,17 @@ func TestPageWaitForNavigationShouldNotPanic(t *testing.T) {
535637
<-ctx.Done()
536638
require.NotPanics(t, func() { p.WaitForNavigation(nil) })
537639
}
640+
641+
func assertPanicErrorContains(t *testing.T, err interface{}, expErrMsg string) {
642+
t.Helper()
643+
644+
require.NotNil(t, err)
645+
require.IsType(t, &goja.Object{}, err)
646+
gotObj, _ := err.(*goja.Object)
647+
got := gotObj.Export()
648+
expErr := fmt.Errorf("%w", errors.New(expErrMsg))
649+
require.IsType(t, expErr, got)
650+
gotErr, ok := got.(error)
651+
require.True(t, ok)
652+
assert.Contains(t, gotErr.Error(), expErr.Error())
653+
}

0 commit comments

Comments
 (0)