Skip to content

Commit

Permalink
Async browser.newPage
Browse files Browse the repository at this point in the history
To run everything race-free, we run all JS code on the event loop.
  • Loading branch information
inancgumus committed May 8, 2024
1 parent 7a82924 commit a3b0093
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 102 deletions.
28 changes: 15 additions & 13 deletions browser/browser_mapping.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,21 +69,23 @@ func mapBrowser(vu moduleVU) mapping { //nolint:funlen,cyclop
}
return b.Version(), nil
},
"newPage": func(opts goja.Value) (mapping, error) {
b, err := vu.browser()
if err != nil {
return nil, err
}
page, err := b.NewPage(opts)
if err != nil {
return nil, err //nolint:wrapcheck
}
"newPage": func(opts goja.Value) *goja.Promise {
return k6ext.Promise(vu.Context(), func() (any, error) {
b, err := vu.browser()
if err != nil {
return nil, err
}
page, err := b.NewPage(opts)
if err != nil {
return nil, err //nolint:wrapcheck
}

if err := initBrowserContext(b.Context(), vu.testRunID); err != nil {
return nil, err
}
if err := initBrowserContext(b.Context(), vu.testRunID); err != nil {
return nil, err
}

return mapPage(vu, page), nil
return mapPage(vu, page), nil
})
},
}
}
Expand Down
40 changes: 22 additions & 18 deletions tests/browser_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -657,28 +657,31 @@ func TestK6Object(t *testing.T) {

// First test with browser.newPage
got, err := vu.TestRT.RunOnEventLoop(`
const p = browser.newPage();
p.goto("about:blank");
const o = p.evaluate(() => window.k6);
JSON.stringify(o);
(async function() {
const p = await browser.newPage();
p.goto("about:blank");
const o = p.evaluate(() => window.k6);
return JSON.stringify(o);
})();
`)
require.NoError(t, err)
assert.Equal(t, tt.want, got.String())
p, ok := got.Export().(*goja.Promise)
require.Truef(t, ok, "got: %T, want *goja.Promise", got.Export())
assert.Equal(t, tt.want, p.Result().String())

// Now test with browser.newContext
got, err = vu.TestRT.RunOnEventLoop(`
const test = async function() {
(async function() {
await browser.closeContext();
const c = await browser.newContext();
const p2 = c.newPage();
const p2 = await c.newPage();
p2.goto("about:blank");
const o2 = p2.evaluate(() => window.k6);
return JSON.stringify(o2);
}
test();
})();
`)
require.NoError(t, err)
p, ok := got.Export().(*goja.Promise)
p, ok = got.Export().(*goja.Promise)
require.Truef(t, ok, "got: %T, want *goja.Promise", got.Export())
assert.Equal(t, tt.want, p.Result().String())
})
Expand All @@ -704,17 +707,18 @@ func TestNewTab(t *testing.T) {
mux.Handle(path, http.StripPrefix(path, fs))

// Start the iteration
_, rt, _, cleanUp := startIteration(t, env.ConstLookup(env.K6TestRunID, "12345"))
vu, _, _, cleanUp := startIteration(t, env.ConstLookup(env.K6TestRunID, "12345"))
defer cleanUp()

// Run the test script
_, err := rt.RunString(fmt.Sprintf(`
const p = browser.newPage()
p.goto("%s/%s/ping.html")
const p2 = browser.context().newPage()
p2.goto("%s/%s/ping.html")
`, s.URL, testBrowserStaticDir, s.URL, testBrowserStaticDir))
_, err := vu.TestRT.RunOnEventLoop(fmt.Sprintf(`
(async function() {
const p = await browser.newPage()
p.goto("%s/%s/ping.html")
const p2 = browser.context().newPage()
p2.goto("%s/%s/ping.html")
})()`, s.URL, testBrowserStaticDir, s.URL, testBrowserStaticDir))
require.NoError(t, err)
}

Expand Down
8 changes: 5 additions & 3 deletions tests/browser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,9 +232,11 @@ func TestBrowserCrashErr(t *testing.T) {

rt := vu.Runtime()
require.NoError(t, rt.Set("browser", jsMod.Browser))
_, err := rt.RunString(`
const p = browser.newPage();
p.close();
_, err := vu.TestRT.RunOnEventLoop(`
(async function() {
const p = await browser.newPage();
p.close();
})();
`)
assert.ErrorContains(t, err, "launching browser: Invalid devtools server port")
}
Expand Down
8 changes: 5 additions & 3 deletions tests/browser_type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,11 @@ func TestBrowserTypeLaunchToConnect(t *testing.T) {

rt := vu.Runtime()
require.NoError(t, rt.Set("browser", jsMod.Browser))
_, err := rt.RunString(`
const p = browser.newPage();
p.close();
_, err := vu.TestRT.RunOnEventLoop(`
(async function() {
const p = await browser.newPage();
p.close();
})();
`)
require.NoError(t, err)

Expand Down
148 changes: 84 additions & 64 deletions tests/page_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,23 +216,32 @@ func TestPageEvaluateMapping(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

_, rt, _, cleanUp := startIteration(t)
vu, _, _, cleanUp := startIteration(t)
defer cleanUp()

// Test script as non string input
got, err := rt.RunString(fmt.Sprintf(`
const p = browser.newPage()
p.evaluate(%s)
got, err := vu.TestRT.RunOnEventLoop(fmt.Sprintf(`
let p;
(async function() {
p = await browser.newPage()
return p.evaluate(%s)
})();
`, tt.script))
assert.NoError(t, err)
assert.Equal(t, rt.ToValue(tt.want), got)
p, ok := got.Export().(*goja.Promise)
require.Truef(t, ok, "got: %T, want *goja.Promise", got.Export())
assert.Equal(t, vu.ToGojaValue(tt.want), p.Result())

// Test script as string input
got, err = rt.RunString(fmt.Sprintf(`
p.evaluate("%s")
got, err = vu.TestRT.RunOnEventLoop(fmt.Sprintf(`
(async function() {
return p.evaluate("%s")
})();
`, tt.script))
assert.NoError(t, err)
assert.Equal(t, rt.ToValue(tt.want), got)
p, ok = got.Export().(*goja.Promise)
require.Truef(t, ok, "got: %T, want *goja.Promise", got.Export())
assert.Equal(t, vu.ToGojaValue(tt.want), p.Result())
})
}
}
Expand Down Expand Up @@ -262,18 +271,21 @@ func TestPageEvaluateMappingError(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

_, rt, _, cleanUp := startIteration(t)
vu, _, _, cleanUp := startIteration(t)
defer cleanUp()

// Test script as non string input
_, err := rt.RunString(fmt.Sprintf(`
const p = browser.newPage()
p.evaluate(%s)
_, err := vu.TestRT.RunOnEventLoop(fmt.Sprintf(`
let p;
(async function() {
p = await browser.newPage()
p.evaluate(%s)
})();
`, tt.script))
assert.ErrorContains(t, err, tt.wantErr)

// Test script as string input
_, err = rt.RunString(fmt.Sprintf(`
_, err = vu.TestRT.RunOnEventLoop(fmt.Sprintf(`
p.evaluate("%s")
`, tt.script))
assert.ErrorContains(t, err, tt.wantErr)
Expand Down Expand Up @@ -648,21 +660,19 @@ func TestPageWaitForFunction(t *testing.T) {
// waitForFunction.
script := `
var page;
const test = async function() {
page = browser.newPage();
(async function() {
page = await browser.newPage();
let resp = await page.waitForFunction(%s, %s, %s);
log('ok: '+resp);
}
test();
`
})();`

t.Run("ok_func_raf_default", func(t *testing.T) {
t.Parallel()

vu, rt, log, cleanUp := startIteration(t)
vu, _, log, cleanUp := startIteration(t)
defer cleanUp()

_, err := rt.RunString(`fn = () => {
_, err := vu.TestRT.RunOnEventLoop(`fn = () => {
if (typeof window._cnt == 'undefined') window._cnt = 0;
if (window._cnt >= 50) return true;
window._cnt++;
Expand All @@ -681,7 +691,7 @@ func TestPageWaitForFunction(t *testing.T) {
vu, rt, log, cleanUp := startIteration(t)
defer cleanUp()

_, err := rt.RunString(`fn = arg => {
_, err := vu.TestRT.RunOnEventLoop(`fn = arg => {
window._arg = arg;
return true;
}`)
Expand All @@ -692,7 +702,7 @@ func TestPageWaitForFunction(t *testing.T) {
require.NoError(t, err)
assert.Contains(t, *log, "ok: null")

argEval, err := rt.RunString(`page.evaluate(() => window._arg);`)
argEval, err := vu.TestRT.RunOnEventLoop(`page.evaluate(() => window._arg);`)
require.NoError(t, err)

var gotArg string
Expand All @@ -706,7 +716,7 @@ func TestPageWaitForFunction(t *testing.T) {
vu, rt, log, cleanUp := startIteration(t)
defer cleanUp()

_, err := rt.RunString(`fn = (...args) => {
_, err := vu.TestRT.RunOnEventLoop(`fn = (...args) => {
window._args = args;
return true;
}`)
Expand All @@ -720,7 +730,7 @@ func TestPageWaitForFunction(t *testing.T) {
require.NoError(t, err)
assert.Contains(t, *log, "ok: null")

argEval, err := rt.RunString(`page.evaluate(() => window._args);`)
argEval, err := vu.TestRT.RunOnEventLoop(`page.evaluate(() => window._args);`)
require.NoError(t, err)

var gotArgs []int
Expand All @@ -734,7 +744,7 @@ func TestPageWaitForFunction(t *testing.T) {
vu, _, _, cleanUp := startIteration(t)
defer cleanUp()

_, err := vu.TestRT.RunOnEventLoop(fmt.Sprintf(script, "false", "{ polling: 'raf', timeout: 500, }", "null"))
_, err := vu.TestRT.RunOnEventLoop(fmt.Sprintf(script, "false", "{ polling: 'raf', timeout: 500 }", "null"))
require.ErrorContains(t, err, "timed out after 500ms")
})

Expand All @@ -754,18 +764,22 @@ func TestPageWaitForFunction(t *testing.T) {
t.Run("ok_expr_poll_interval", func(t *testing.T) {
t.Parallel()

vu, rt, log, cleanUp := startIteration(t)
vu, _, log, cleanUp := startIteration(t)
defer cleanUp()

_, err := rt.RunString(`
const page = browser.newPage();
page.evaluate(() => {
setTimeout(() => {
const el = document.createElement('h1');
el.innerHTML = 'Hello';
document.body.appendChild(el);
}, 1000);
});`)
_, err := vu.TestRT.RunOnEventLoop(`
let page;
(async function() {
page = await browser.newPage();
page.evaluate(() => {
setTimeout(() => {
const el = document.createElement('h1');
el.innerHTML = 'Hello';
document.body.appendChild(el);
}, 1000);
});
})();`)
require.NoError(t, err)

script := `
Expand All @@ -788,30 +802,33 @@ func TestPageWaitForFunction(t *testing.T) {
t.Run("ok_func_poll_mutation", func(t *testing.T) {
t.Parallel()

vu, rt, log, cleanUp := startIteration(t)
vu, _, log, cleanUp := startIteration(t)
defer cleanUp()

_, err := rt.RunString(`
fn = () => document.querySelector('h1') !== null
const page = browser.newPage();
page.evaluate(() => {
console.log('calling setTimeout...');
setTimeout(() => {
console.log('creating element...');
const el = document.createElement('h1');
el.innerHTML = 'Hello';
document.body.appendChild(el);
}, 1000);
})`)
_, err := vu.TestRT.RunOnEventLoop(`
let page;
(async function() {
fn = () => document.querySelector('h1') !== null
page = await browser.newPage();
page.evaluate(() => {
console.log('calling setTimeout...');
setTimeout(() => {
console.log('creating element...');
const el = document.createElement('h1');
el.innerHTML = 'Hello';
document.body.appendChild(el);
}, 1000);
})
})();`)
require.NoError(t, err)

script := `
const test = async function() {
(async function() {
let resp = await page.waitForFunction(%s, %s, %s);
log('ok: '+resp);
}
test();`
})()`

s := fmt.Sprintf(script, "fn", "{ polling: 'mutation', timeout: 2000, }", "null")
_, err = vu.TestRT.RunOnEventLoop(s)
Expand Down Expand Up @@ -1679,23 +1696,26 @@ func TestShadowDOMAndDocumentFragment(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

_, rt, _, cleanUp := startIteration(t)
vu, _, _, cleanUp := startIteration(t)
defer cleanUp()

got, err := rt.RunString(fmt.Sprintf(`
const p = browser.newPage()
p.goto("%s/%s/shadow_and_doc_frag.html")
got, err := vu.TestRT.RunOnEventLoop(fmt.Sprintf(`
(async function() {
const p = await browser.newPage()
p.goto("%s/%s/shadow_and_doc_frag.html")
const s = p.locator('%s')
s.waitFor({
timeout: 1000,
state: 'attached',
});
const s = p.locator('%s')
s.waitFor({
timeout: 1000,
state: 'attached',
});
s.innerText();
`, s.URL, testBrowserStaticDir, tt.selector))
return s.innerText();
})()`, s.URL, testBrowserStaticDir, tt.selector))
assert.NoError(t, err)
assert.Equal(t, tt.want, got.String())
p, ok := got.Export().(*goja.Promise)
require.Truef(t, ok, "got: %T, want *goja.Promise", got.Export())
assert.Equal(t, tt.want, p.Result().String())
})
}
}
Loading

0 comments on commit a3b0093

Please sign in to comment.