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

Commit

Permalink
Implement async goto
Browse files Browse the repository at this point in the history
Updates #428
  • Loading branch information
mstoykov committed Oct 12, 2022
1 parent 31c29c9 commit c62f9a4
Show file tree
Hide file tree
Showing 27 changed files with 468 additions and 327 deletions.
2 changes: 1 addition & 1 deletion api/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type Frame interface {
Focus(selector string, opts goja.Value)
FrameElement() ElementHandle
GetAttribute(selector string, name string, opts goja.Value) goja.Value
Goto(url string, opts goja.Value) Response
Goto(url string, opts goja.Value) *goja.Promise
Hover(selector string, opts goja.Value)
InnerHTML(selector string, opts goja.Value) string
InnerText(selector string, opts goja.Value) string
Expand Down
2 changes: 1 addition & 1 deletion api/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ type Page interface {
GetAttribute(selector string, name string, opts goja.Value) goja.Value
GoBack(opts goja.Value) Response
GoForward(opts goja.Value) Response
Goto(url string, opts goja.Value) Response
Goto(url string, opts goja.Value) *goja.Promise
Hover(selector string, opts goja.Value)
InnerHTML(selector string, opts goja.Value) string
InnerText(selector string, opts goja.Value) string
Expand Down
18 changes: 14 additions & 4 deletions common/frame.go
Original file line number Diff line number Diff line change
Expand Up @@ -1054,10 +1054,20 @@ func (f *Frame) getAttribute(selector, name string, opts *FrameBaseOptions) (goj
}

// Goto will navigate the frame to the specified URL and return a HTTP response object.
func (f *Frame) Goto(url string, opts goja.Value) api.Response {
resp := f.manager.NavigateFrame(f, url, opts)
applySlowMo(f.ctx)
return resp
func (f *Frame) Goto(url string, opts goja.Value) *goja.Promise {
netMgr := f.manager.page.mainFrameSession.getNetworkManager()
defaultReferer := netMgr.extraHTTPHeaders["referer"]
parsedOpts := NewFrameGotoOptions(
defaultReferer, time.Duration(f.manager.timeoutSettings.navigationTimeout())*time.Second)
if err := parsedOpts.Parse(f.ctx, opts); err != nil {
k6ext.Panic(f.ctx, "parsing frame navigation options to %q: %w", url, err)
return nil
}
return k6ext.Promise(f.ctx, func() (interface{}, error) {
resp, err := f.manager.NavigateFrame(f, url, parsedOpts)
applySlowMo(f.ctx)
return resp, err
})
}

// Hover moves the pointer over the first element that matches the selector.
Expand Down
30 changes: 12 additions & 18 deletions common/frame_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import (
"fmt"
"sync"
"sync/atomic"
"time"

"github.com/grafana/xk6-browser/api"
"github.com/grafana/xk6-browser/k6ext"
Expand All @@ -36,7 +35,6 @@ import (

"github.com/chromedp/cdproto/cdp"
"github.com/chromedp/cdproto/network"
"github.com/dop251/goja"
)

// FrameManager manages all frames in a page and their life-cycles, it's a purely internal component.
Expand Down Expand Up @@ -569,7 +567,8 @@ func (m *FrameManager) setMainFrame(f *Frame) {
}

// NavigateFrame will navigate specified frame to specified URL.
func (m *FrameManager) NavigateFrame(frame *Frame, url string, opts goja.Value) api.Response {
//nolint:funlen,cyclop
func (m *FrameManager) NavigateFrame(frame *Frame, url string, parsedOpts *FrameGotoOptions) (api.Response, error) {
var (
fmid = m.ID()
fid = frame.ID()
Expand All @@ -580,13 +579,6 @@ func (m *FrameManager) NavigateFrame(frame *Frame, url string, opts goja.Value)
defer m.logger.Debugf("FrameManager:NavigateFrame:return",
"fmid:%d fid:%v furl:%s url:%s", fmid, fid, furl, url)

netMgr := m.page.mainFrameSession.getNetworkManager()
defaultReferer := netMgr.extraHTTPHeaders["referer"]
parsedOpts := NewFrameGotoOptions(defaultReferer, time.Duration(m.timeoutSettings.navigationTimeout())*time.Second)
if err := parsedOpts.Parse(m.ctx, opts); err != nil {
k6ext.Panic(m.ctx, "parsing frame navigation options to %q: %v", url, err)
}

timeoutCtx, timeoutCancelFn := context.WithTimeout(m.ctx, parsedOpts.Timeout)
defer timeoutCancelFn()

Expand Down Expand Up @@ -620,35 +612,38 @@ func (m *FrameManager) NavigateFrame(frame *Frame, url string, opts goja.Value)

// Attaching an iframe to an existing page doesn't seem to trigger a "Target.attachedToTarget" event
// from the browser even when "Target.setAutoAttach" is true. If this is the case fallback to the

// main frame's session.
fs = frame.page.mainFrameSession
}
newDocumentID, err := fs.navigateFrame(frame, url, parsedOpts.Referer)
if err != nil {
k6ext.Panic(m.ctx, "navigating to %q: %v", url, err)
return nil, fmt.Errorf("navigating to %q: %w", url, err)
}

if newDocumentID == "" {
// It's a navigation within the same document (e.g. via anchor links or
// the History API), so don't wait for a response nor any lifecycle
// events.
return nil
return nil, nil
}

// unblock the waiter goroutine
newDocIDCh <- newDocumentID

handleTimeoutError := func(err error) {
wrapTimeoutError := func(err error) error {
if errors.Is(err, context.DeadlineExceeded) {
err = &k6ext.UserFriendlyError{
Err: err,
Timeout: parsedOpts.Timeout,
}
k6ext.Panic(m.ctx, "navigating to %q: %w", url, err)
return fmt.Errorf("navigating to %q: %w", url, err)
}
m.logger.Debugf("FrameManager:NavigateFrame",
"fmid:%d fid:%v furl:%s url:%s timeoutCtx done: %v",
fmid, fid, furl, url, err)

return err // TODO maybe wrap this as well?
}

var resp *Response
Expand All @@ -664,17 +659,16 @@ func (m *FrameManager) NavigateFrame(frame *Frame, url string, opts goja.Value)
}
}
case <-timeoutCtx.Done():
handleTimeoutError(timeoutCtx.Err())
return nil
return nil, wrapTimeoutError(timeoutCtx.Err())
}

select {
case <-lifecycleEvtCh:
case <-timeoutCtx.Done():
handleTimeoutError(timeoutCtx.Err())
return nil, wrapTimeoutError(timeoutCtx.Err())
}

return resp
return resp, nil
}

// Page returns the page that this frame manager belongs to.
Expand Down
2 changes: 1 addition & 1 deletion common/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ func (p *Page) GoForward(opts goja.Value) api.Response {
}

// Goto will navigate the page to the specified URL and return a HTTP response object.
func (p *Page) Goto(url string, opts goja.Value) api.Response {
func (p *Page) Goto(url string, opts goja.Value) *goja.Promise {
p.logger.Debugf("Page:Goto", "sid:%v url:%q", p.sessionID(), url)

return p.MainFrame().Goto(url, opts)
Expand Down
14 changes: 7 additions & 7 deletions examples/browser_args.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export default function() {
const context = browser.newContext();
const page = context.newPage();

const res = page.goto('http://test.k6.io/', { waitUntil: 'load' });
page.goto('http://test.k6.io/', { waitUntil: 'load' }).then((res) => {
check(res, {
'null response': r => r === null,
});

check(res, {
'null response': r => r === null,
});

page.close();
browser.close();
page.close();
browser.close();
})
}
2 changes: 1 addition & 1 deletion examples/browser_on.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function() {
'should be disconnected on event': !browser.isConnected(),
});
return handlerCalled;
// The promise reject/failure handler
// The promise reject/failure handler
}, (val) => {
console.error(`promise rejected: ${val}`);
});
Expand Down
22 changes: 11 additions & 11 deletions examples/colorscheme.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,17 +23,17 @@ export default function() {
page.goto(
'https://googlechromelabs.github.io/dark-mode-toggle/demo/',
{ waitUntil: 'load' },
);
).then(() => {
const colorScheme = page.evaluate(() => {
return {
isDarkColorScheme: window.matchMedia('(prefers-color-scheme: dark)').matches
};
});
check(colorScheme, {
'isDarkColorScheme': cs => cs.isDarkColorScheme
});

const colorScheme = page.evaluate(() => {
return {
isDarkColorScheme: window.matchMedia('(prefers-color-scheme: dark)').matches
};
page.close();
browser.close();
});
check(colorScheme, {
'isDarkColorScheme': cs => cs.isDarkColorScheme
});

page.close();
browser.close();
}
42 changes: 21 additions & 21 deletions examples/device_emulation.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,26 @@ export default function() {
const context = browser.newContext(options);
const page = context.newPage();

page.goto('https://k6.io/', { waitUntil: 'networkidle' });

const dimensions = page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});

check(dimensions, {
'width': d => d.width === device.viewport.width,
'height': d => d.height === device.viewport.height,
'scale': d => d.deviceScaleFactor === device.deviceScaleFactor,
page.goto('https://k6.io/', { waitUntil: 'networkidle' }).then(() => {
const dimensions = page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});

check(dimensions, {
'width': d => d.width === device.viewport.width,
'height': d => d.height === device.viewport.height,
'scale': d => d.deviceScaleFactor === device.deviceScaleFactor,
});

if (!__ENV.XK6_HEADLESS) {
sleep(10);
}

page.close();
browser.close();
});

if (!__ENV.XK6_HEADLESS) {
sleep(10);
}

page.close();
browser.close();
}
35 changes: 18 additions & 17 deletions examples/evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,24 @@ export default function() {
const context = browser.newContext();
const page = context.newPage();

page.goto('https://test.k6.io/', { waitUntil: 'load' });
const dimensions = page.evaluate(() => {
const obj = {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
console.log(obj); // tests #120
return obj;
});
page.goto('https://test.k6.io/', { waitUntil: 'load' }).then(() => {
const dimensions = page.evaluate(() => {
const obj = {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
console.log(obj); // tests #120
return obj;
});

check(dimensions, {
'width': d => d.width === 1265,
'height': d => d.height === 720,
'scale': d => d.deviceScaleFactor === 1,
});
check(dimensions, {
'width': d => d.width === 1265,
'height': d => d.height === 720,
'scale': d => d.deviceScaleFactor === 1,
});

page.close();
browser.close();
page.close();
browser.close();
});
}
12 changes: 7 additions & 5 deletions examples/fillform.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ export default function() {
const page = context.newPage();

// Goto front page, find login link and click it
page.goto('https://test.k6.io/', { waitUntil: 'networkidle' });
Promise.all([
page.waitForNavigation(),
page.locator('a[href="/my_messages.php"]').click(),
]).then(() => {
page.goto('https://test.k6.io/', { waitUntil: 'networkidle' }).then(() => {
return Promise.all([
page.waitForNavigation(),
page.locator('a[href="/my_messages.php"]').click(),
])

}).then(() => {
// Enter login credentials and login
page.locator('input[name="login"]').type('admin');
page.locator('input[name="password"]').type('123');
Expand Down
15 changes: 8 additions & 7 deletions examples/getattribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ export default function() {

page.goto('https://googlechromelabs.github.io/dark-mode-toggle/demo/', {
waitUntil: 'load',
});
let el = page.$('#dark-mode-toggle-3')
check(el, {
"GetAttribute('mode')": e => e.getAttribute('mode') == 'light',
});
}).then(() => {
let el = page.$('#dark-mode-toggle-3')
check(el, {
"GetAttribute('mode')": e => e.getAttribute('mode') == 'light',
});

page.close();
browser.close();
page.close();
browser.close();
});
}
13 changes: 7 additions & 6 deletions examples/grant_permission.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ export default function() {
const context = browser.newContext({
permissions: ["camera", "microphone"],
});

const page = context.newPage();
page.goto('http://whatsmyuseragent.org/');
page.screenshot({ path: `example-chromium.png` });
page.goto('http://whatsmyuseragent.org/').then(() => {
page.screenshot({ path: `example-chromium.png` });

page.close();
browser.close();
}
page.close();
browser.close();
})
}
12 changes: 6 additions & 6 deletions examples/hosts.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ export default function() {
const context = browser.newContext();
const page = context.newPage();

const res = page.goto('http://test.k6.io/', { waitUntil: 'load' });
page.goto('http://test.k6.io/', { waitUntil: 'load' }).then((res) => {
check(res, {
'null response': r => r === null,
});

check(res, {
'null response': r => r === null,
page.close();
browser.close();
});

page.close();
browser.close();
}
Loading

0 comments on commit c62f9a4

Please sign in to comment.