From 26a0687a783efcec2a3418cf3fc6eaa485704af6 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Wed, 9 Nov 2022 17:38:48 +0000 Subject: [PATCH 01/16] Refactor lifecycle tests ping handler We're deduplicating the ping handler to make it simpler to read the tests. --- tests/lifecycle_wait_test.go | 126 ++++++++--------------------------- 1 file changed, 29 insertions(+), 97 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index d6fb0632f..15ce33926 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -36,17 +36,7 @@ func TestLifecycleWaitForLoadStateLoad(t *testing.T) { http.Redirect(w, r, tb.staticURL("wait_for_nav_lifecycle.html"), http.StatusMovedPermanently) }) - var counter int64 - var counterMu sync.Mutex - tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { - counterMu.Lock() - defer counterMu.Unlock() - - time.Sleep(time.Millisecond * 100) - - counter++ - fmt.Fprintf(w, "pong %d", counter) - }) + withPingHandler(t, tb, time.Millisecond*100, nil) tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintf(w, ` @@ -90,17 +80,7 @@ func TestLifecycleWaitForLoadStateDOMContentLoaded(t *testing.T) { http.Redirect(w, r, tb.staticURL("wait_for_nav_lifecycle.html"), http.StatusMovedPermanently) }) - var counter int64 - var counterMu sync.Mutex - tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { - counterMu.Lock() - defer counterMu.Unlock() - - time.Sleep(time.Millisecond * 100) - - counter++ - fmt.Fprintf(w, "pong %d", counter) - }) + withPingHandler(t, tb, time.Millisecond*100, nil) tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintf(w, ` @@ -144,15 +124,7 @@ func TestLifecycleWaitForLoadStateNetworkIdle(t *testing.T) { http.Redirect(w, r, tb.staticURL("wait_for_nav_lifecycle.html"), http.StatusMovedPermanently) }) - var counter int64 - var counterMu sync.Mutex - tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { - counterMu.Lock() - defer counterMu.Unlock() - - counter++ - fmt.Fprintf(w, "pong %d", counter) - }) + withPingHandler(t, tb, 0, nil) tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintf(w, ` @@ -193,17 +165,7 @@ func TestLifecycleWaitForLoadStateDOMContentLoadedThenNetworkIdle(t *testing.T) http.Redirect(w, r, tb.staticURL("wait_for_nav_lifecycle.html"), http.StatusMovedPermanently) }) - var counter int64 - var counterMu sync.Mutex - tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { - counterMu.Lock() - defer counterMu.Unlock() - - time.Sleep(time.Millisecond * 100) - - counter++ - fmt.Fprintf(w, "pong %d", counter) - }) + withPingHandler(t, tb, time.Millisecond*100, nil) tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintf(w, ` @@ -232,17 +194,7 @@ func TestLifecycleReloadLoad(t *testing.T) { http.Redirect(w, r, tb.staticURL("reload_lifecycle.html"), http.StatusMovedPermanently) }) - var counter int64 - var counterMu sync.Mutex - tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { - counterMu.Lock() - defer counterMu.Unlock() - - time.Sleep(time.Millisecond * 100) - - counter++ - fmt.Fprintf(w, "pong %d", counter) - }) + withPingHandler(t, tb, time.Millisecond*100, nil) tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintf(w, ` @@ -282,17 +234,7 @@ func TestLifecycleReloadDOMContentLoaded(t *testing.T) { http.Redirect(w, r, tb.staticURL("reload_lifecycle.html"), http.StatusMovedPermanently) }) - var counter int64 - var counterMu sync.Mutex - tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { - counterMu.Lock() - defer counterMu.Unlock() - - time.Sleep(time.Millisecond * 100) - - counter++ - fmt.Fprintf(w, "pong %d", counter) - }) + withPingHandler(t, tb, time.Millisecond*100, nil) tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintf(w, ` @@ -334,15 +276,7 @@ func TestLifecycleReloadNetworkIdle(t *testing.T) { http.Redirect(w, r, tb.staticURL("reload_lifecycle.html"), http.StatusMovedPermanently) }) - var counter int64 - var counterMu sync.Mutex - tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { - counterMu.Lock() - defer counterMu.Unlock() - - counter++ - fmt.Fprintf(w, "pong %d", counter) - }) + withPingHandler(t, tb, 0, nil) tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintf(w, ` @@ -415,20 +349,8 @@ func TestLifecycleNetworkIdle(t *testing.T) { http.Redirect(w, r, tb.staticURL("prolonged_network_idle.html"), http.StatusMovedPermanently) }) - var counter int64 ch := make(chan bool) - var counterMu sync.Mutex - tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { - <-ch - - counterMu.Lock() - defer counterMu.Unlock() - - time.Sleep(time.Millisecond * 50) - - counter++ - fmt.Fprintf(w, "pong %d", counter) - }) + withPingHandler(t, tb, time.Millisecond*50, ch) tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { fmt.Fprintf(w, ` @@ -456,17 +378,7 @@ func TestLifecycleNetworkIdle(t *testing.T) { http.Redirect(w, r, tb.staticURL("prolonged_network_idle_10.html"), http.StatusMovedPermanently) }) - var counterMu sync.Mutex - var counter int64 - tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { - counterMu.Lock() - defer counterMu.Unlock() - - time.Sleep(time.Millisecond * 50) - - counter++ - fmt.Fprintf(w, "pong %d", counter) - }) + withPingHandler(t, tb, time.Millisecond*50, nil) assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() { result := p.TextContent("#pingRequestText", nil) @@ -475,6 +387,26 @@ func TestLifecycleNetworkIdle(t *testing.T) { }) } +func withPingHandler(t *testing.T, tb *testBrowser, slow time.Duration, ch chan bool) { + t.Helper() + + var counter int64 + var counterMu sync.Mutex + tb.withHandler("/ping", func(w http.ResponseWriter, _ *http.Request) { + if ch != nil { + <-ch + } + + counterMu.Lock() + defer counterMu.Unlock() + + time.Sleep(slow) + + counter++ + fmt.Fprintf(w, "pong %d", counter) + }) +} + func assertHome( t *testing.T, tb *testBrowser, From 616400ffd1d48f0bfe0bc1d727d20ee9fa93d8f1 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Wed, 9 Nov 2022 17:53:25 +0000 Subject: [PATCH 02/16] Refactor lifecycle test ping.js handler Deduplicate the ping.js handler in the lifecycle tests. --- tests/lifecycle_wait_test.go | 98 ++++++++++++------------------------ 1 file changed, 31 insertions(+), 67 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 15ce33926..2c97d9934 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -37,13 +37,7 @@ func TestLifecycleWaitForLoadStateLoad(t *testing.T) { }) withPingHandler(t, tb, time.Millisecond*100, nil) - - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, ` - var pingJSTextOutput = document.getElementById("pingJSText"); - pingJSTextOutput.innerText = "ping.js loaded from server"; - `) - }) + withPingJSHandler(t, tb, false, nil) waitUntil := common.LifecycleEventLoad assertHome(t, tb, p, waitUntil, func() { @@ -81,15 +75,7 @@ func TestLifecycleWaitForLoadStateDOMContentLoaded(t *testing.T) { }) withPingHandler(t, tb, time.Millisecond*100, nil) - - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, ` - await new Promise(resolve => setTimeout(resolve, 1000)); - - var pingJSTextOutput = document.getElementById("pingJSText"); - pingJSTextOutput.innerText = "ping.js loaded from server"; - `) - }) + withPingJSHandler(t, tb, true, nil) waitUntil := common.LifecycleEventDOMContentLoad assertHome(t, tb, p, waitUntil, func() { @@ -125,13 +111,7 @@ func TestLifecycleWaitForLoadStateNetworkIdle(t *testing.T) { }) withPingHandler(t, tb, 0, nil) - - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, ` - var pingJSTextOutput = document.getElementById("pingJSText"); - pingJSTextOutput.innerText = "ping.js loaded from server"; - `) - }) + withPingJSHandler(t, tb, false, nil) waitUntil := common.LifecycleEventNetworkIdle assertHome(t, tb, p, waitUntil, func() { @@ -166,13 +146,7 @@ func TestLifecycleWaitForLoadStateDOMContentLoadedThenNetworkIdle(t *testing.T) }) withPingHandler(t, tb, time.Millisecond*100, nil) - - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, ` - var pingJSTextOutput = document.getElementById("pingJSText"); - pingJSTextOutput.innerText = "ping.js loaded from server"; - `) - }) + withPingJSHandler(t, tb, false, nil) assertHome(t, tb, p, common.LifecycleEventDOMContentLoad, func() { p.WaitForLoadState(common.LifecycleEventNetworkIdle.String(), nil) @@ -195,13 +169,7 @@ func TestLifecycleReloadLoad(t *testing.T) { }) withPingHandler(t, tb, time.Millisecond*100, nil) - - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, ` - var pingJSTextOutput = document.getElementById("pingJSText"); - pingJSTextOutput.innerText = "ping.js loaded from server"; - `) - }) + withPingJSHandler(t, tb, false, nil) waitUntil := common.LifecycleEventLoad assertHome(t, tb, p, waitUntil, func() { @@ -235,15 +203,7 @@ func TestLifecycleReloadDOMContentLoaded(t *testing.T) { }) withPingHandler(t, tb, time.Millisecond*100, nil) - - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, ` - await new Promise(resolve => setTimeout(resolve, 1000)); - - var pingJSTextOutput = document.getElementById("pingJSText"); - pingJSTextOutput.innerText = "ping.js loaded from server"; - `) - }) + withPingJSHandler(t, tb, true, nil) waitUntil := common.LifecycleEventDOMContentLoad assertHome(t, tb, p, waitUntil, func() { @@ -277,13 +237,7 @@ func TestLifecycleReloadNetworkIdle(t *testing.T) { }) withPingHandler(t, tb, 0, nil) - - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, ` - var pingJSTextOutput = document.getElementById("pingJSText"); - pingJSTextOutput.innerText = "ping.js loaded from server"; - `) - }) + withPingJSHandler(t, tb, false, nil) waitUntil := common.LifecycleEventNetworkIdle assertHome(t, tb, p, waitUntil, func() { @@ -327,12 +281,7 @@ func TestLifecycleNetworkIdle(t *testing.T) { `) }) - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, ` - var pingJSTextOutput = document.getElementById("pingJSText"); - pingJSTextOutput.innerText = "ping.js loaded from server"; - `) - }) + withPingJSHandler(t, tb, false, nil) assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() { result := p.TextContent("#pingJSText", nil) @@ -351,14 +300,7 @@ func TestLifecycleNetworkIdle(t *testing.T) { ch := make(chan bool) withPingHandler(t, tb, time.Millisecond*50, ch) - - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - fmt.Fprintf(w, ` - var pingJSTextOutput = document.getElementById("pingJSText"); - pingJSTextOutput.innerText = "ping.js loaded from server"; - `) - close(ch) - }) + withPingJSHandler(t, tb, false, ch) assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() { result := p.TextContent("#pingRequestText", nil) @@ -407,6 +349,28 @@ func withPingHandler(t *testing.T, tb *testBrowser, slow time.Duration, ch chan }) } +func withPingJSHandler(t *testing.T, tb *testBrowser, slow bool, ch chan bool) { + t.Helper() + + tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { + script := ` + var pingJSTextOutput = document.getElementById("pingJSText"); + pingJSTextOutput.innerText = "ping.js loaded from server"; + ` + if slow { + script = ` + await new Promise(resolve => setTimeout(resolve, 1000)); + + ` + script + } + fmt.Fprint(w, script) + + if ch != nil { + close(ch) + } + }) +} + func assertHome( t *testing.T, tb *testBrowser, From 038e596a0b17bf52caaba64ec86afcc33289e933 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Wed, 9 Nov 2022 18:01:13 +0000 Subject: [PATCH 03/16] Refactor lifecycle tests home handler Deduplicate the home handler in the lifecycle tests. --- tests/lifecycle_wait_test.go | 64 ++++++++++++++++++++---------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 2c97d9934..fe3f4ef25 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -14,6 +14,25 @@ import ( "github.com/grafana/xk6-browser/common" ) +// General guidelines on lifecycle events: +// +// - load: This lifecycle event is emitted by the browser once: +// 1. The HTML is loaded; +// 2. The async scripts have loaded; +// It does not wait for the other network requests to +// complete. +// +// - domcontentloaded: This lifecycle event is emitted by the +// browser once: +// 1. The HTML is loaded; +// It does not wait for the async scripts or +// the other network requests to complete. +// +// - networkidle: This lifecycle event is emitted by the browser once: +// 1. The HTML is loaded; +// 2. The async scripts have loaded; +// 3. All other network requests have completed; + func TestLifecycleWaitForLoadStateLoad(t *testing.T) { // Test description // @@ -27,15 +46,12 @@ func TestLifecycleWaitForLoadStateLoad(t *testing.T) { // (which is when load is fired). We also want // to ensure that the load event is stored // internally, and we don't block on WaitForLoadState. - t.Parallel() tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, tb.staticURL("wait_for_nav_lifecycle.html"), http.StatusMovedPermanently) - }) + withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") withPingHandler(t, tb, time.Millisecond*100, nil) withPingJSHandler(t, tb, false, nil) @@ -70,10 +86,8 @@ func TestLifecycleWaitForLoadStateDOMContentLoaded(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, tb.staticURL("wait_for_nav_lifecycle.html"), http.StatusMovedPermanently) - }) + withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") withPingHandler(t, tb, time.Millisecond*100, nil) withPingJSHandler(t, tb, true, nil) @@ -106,10 +120,8 @@ func TestLifecycleWaitForLoadStateNetworkIdle(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, tb.staticURL("wait_for_nav_lifecycle.html"), http.StatusMovedPermanently) - }) + withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") withPingHandler(t, tb, 0, nil) withPingJSHandler(t, tb, false, nil) @@ -141,10 +153,8 @@ func TestLifecycleWaitForLoadStateDOMContentLoadedThenNetworkIdle(t *testing.T) tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, tb.staticURL("wait_for_nav_lifecycle.html"), http.StatusMovedPermanently) - }) + withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") withPingHandler(t, tb, time.Millisecond*100, nil) withPingJSHandler(t, tb, false, nil) @@ -164,10 +174,8 @@ func TestLifecycleReloadLoad(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, tb.staticURL("reload_lifecycle.html"), http.StatusMovedPermanently) - }) + withHomeHandler(t, tb, "reload_lifecycle.html") withPingHandler(t, tb, time.Millisecond*100, nil) withPingJSHandler(t, tb, false, nil) @@ -198,10 +206,8 @@ func TestLifecycleReloadDOMContentLoaded(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, tb.staticURL("reload_lifecycle.html"), http.StatusMovedPermanently) - }) + withHomeHandler(t, tb, "reload_lifecycle.html") withPingHandler(t, tb, time.Millisecond*100, nil) withPingJSHandler(t, tb, true, nil) @@ -232,10 +238,8 @@ func TestLifecycleReloadNetworkIdle(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, tb.staticURL("reload_lifecycle.html"), http.StatusMovedPermanently) - }) + withHomeHandler(t, tb, "reload_lifecycle.html") withPingHandler(t, tb, 0, nil) withPingJSHandler(t, tb, false, nil) @@ -294,10 +298,8 @@ func TestLifecycleNetworkIdle(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, tb.staticURL("prolonged_network_idle.html"), http.StatusMovedPermanently) - }) + withHomeHandler(t, tb, "prolonged_network_idle.html") ch := make(chan bool) withPingHandler(t, tb, time.Millisecond*50, ch) withPingJSHandler(t, tb, false, ch) @@ -316,10 +318,8 @@ func TestLifecycleNetworkIdle(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, tb.staticURL("prolonged_network_idle_10.html"), http.StatusMovedPermanently) - }) + withHomeHandler(t, tb, "prolonged_network_idle_10.html") withPingHandler(t, tb, time.Millisecond*50, nil) assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() { @@ -329,6 +329,14 @@ func TestLifecycleNetworkIdle(t *testing.T) { }) } +func withHomeHandler(t *testing.T, tb *testBrowser, htmlFile string) { + t.Helper() + + tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, tb.staticURL(htmlFile), http.StatusMovedPermanently) + }) +} + func withPingHandler(t *testing.T, tb *testBrowser, slow time.Duration, ch chan bool) { t.Helper() From 9837fa55909547e3b0ba3efffc05d69dce942d26 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Thu, 10 Nov 2022 11:45:56 +0000 Subject: [PATCH 04/16] Refactor WaitForLoadState lifecycle tests Deduplicate them and convert them to a table test. --- tests/lifecycle_wait_test.go | 238 +++++++++++++++++------------------ 1 file changed, 113 insertions(+), 125 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index fe3f4ef25..7a7e33c6a 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -33,140 +33,128 @@ import ( // 2. The async scripts have loaded; // 3. All other network requests have completed; -func TestLifecycleWaitForLoadStateLoad(t *testing.T) { - // Test description - // - // 1. goto /home and wait for the load lifecycle event. - // 2. use WaitForLoadState with load to ensure that load - // lifecycle event has already fired. - // - // Success criteria: We don't wait for all network requests to - // complete, but we are interested in waiting - // for all async scripts to have fully loaded - // (which is when load is fired). We also want - // to ensure that the load event is stored - // internally, and we don't block on WaitForLoadState. +func TestLifecycleWaitForLoadState(t *testing.T) { t.Parallel() - tb := newTestBrowser(t, withFileServer()) - p := tb.NewPage(nil) - - withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") - withPingHandler(t, tb, time.Millisecond*100, nil) - withPingJSHandler(t, tb, false, nil) - - waitUntil := common.LifecycleEventLoad - assertHome(t, tb, p, waitUntil, func() { - result := p.TextContent("#pingRequestText", nil) - assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) - - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "ping.js loaded from server", result) - - // This shouldn't block and return after calling hasLifecycleEventFired. - p.WaitForLoadState(waitUntil.String(), nil) - }) -} - -func TestLifecycleWaitForLoadStateDOMContentLoaded(t *testing.T) { - // Test description - // - // 1. goto /home and wait for the domcontentloaded lifecycle event. - // 2. use WaitForLoadState with domcontentloaded to ensure that - // domcontentloaded lifecycle event has already fired. - // - // Success criteria: We don't wait for all network requests or the - // async scripts to complete, and we're only - // interested in the html file being loaded. We - // also want to ensure that the domcontentloaded - // event is stored internally, and we don't block - // on WaitForLoadState. - - t.Parallel() - - tb := newTestBrowser(t, withFileServer()) - p := tb.NewPage(nil) - - withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") - withPingHandler(t, tb, time.Millisecond*100, nil) - withPingJSHandler(t, tb, true, nil) - - waitUntil := common.LifecycleEventDOMContentLoad - assertHome(t, tb, p, waitUntil, func() { - result := p.TextContent("#pingRequestText", nil) - assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) - - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "Waiting...", result) - - // This shouldn't block and return after calling hasLifecycleEventFired. - p.WaitForLoadState(waitUntil.String(), nil) - }) -} - -func TestLifecycleWaitForLoadStateNetworkIdle(t *testing.T) { - // Test description - // - // 1. goto /home and wait for the networkidle lifecycle event. - // 2. use WaitForLoadState with networkidle to ensure that - // networkidle lifecycle event has already fired. - // - // Success criteria: We wait for all network requests and async - // scripts to complete. We also want to ensure - // that the networkidle event is stored internally, - // and we don't block on WaitForLoadState. - - t.Parallel() - - tb := newTestBrowser(t, withFileServer()) - p := tb.NewPage(nil) - - withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") - withPingHandler(t, tb, 0, nil) - withPingJSHandler(t, tb, false, nil) - - waitUntil := common.LifecycleEventNetworkIdle - assertHome(t, tb, p, waitUntil, func() { - result := p.TextContent("#pingRequestText", nil) - assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) - - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "ping.js loaded from server", result) - - // This shouldn't block and return after calling hasLifecycleEventFired. - p.WaitForLoadState(waitUntil.String(), nil) - }) -} + tests := []struct { + name string + pingSlowness time.Duration + pingJSSlow bool + waitUntil common.LifecycleEvent + pingRequestTextAssert func(result string) + pingJSTextAssert func(result string) + assertFunc func(p api.Page) + }{ + { + // Test description + // + // 1. goto /home and wait for the load lifecycle event. + // 2. use WaitForLoadState with load. + // + // Success criteria: We want to ensure that the load event is persisted in + // memory, and we don't block on WaitForLoadState. + name: "load", + pingSlowness: time.Millisecond * 100, + pingJSSlow: false, + waitUntil: common.LifecycleEventLoad, + pingRequestTextAssert: func(result string) { + assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + { + // Test description + // + // 1. goto /home and wait for the domcontentloaded lifecycle event. + // 2. use WaitForLoadState with domcontentloaded. + // + // Success criteria: We want to ensure that the domcontentloaded event is + // persisted in memory, and we don't block on WaitForLoadState. + name: "domcontentloaded", + pingSlowness: time.Millisecond * 100, + pingJSSlow: true, + waitUntil: common.LifecycleEventDOMContentLoad, + pingRequestTextAssert: func(result string) { + assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "Waiting...", result) + }, + }, + { + // Test description + // + // 1. goto /home and wait for the networkidle lifecycle event. + // 2. use WaitForLoadState with networkidle. + // + // Success criteria: We want to ensure that the networkidle event is + // persisted in memory, and we don't block on WaitForLoadState. + name: "networkidle", + pingSlowness: 0, + pingJSSlow: false, + waitUntil: common.LifecycleEventNetworkIdle, + pingRequestTextAssert: func(result string) { + assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + { + // Test description + // + // 1. goto /home and wait for the domcontentloaded lifecycle event. + // 2. use WaitForLoadState with networkidle. + // + // Success criteria: We want to quickly move to calling WaitForLoadState + // so that we wait until networkidle is received from + // the browser. So not relying on the persisted state in memory. + name: "domcontentloaded then networkidle", + pingSlowness: time.Millisecond * 100, + pingJSSlow: false, + waitUntil: common.LifecycleEventDOMContentLoad, + assertFunc: func(p api.Page) { + p.WaitForLoadState(common.LifecycleEventNetworkIdle.String(), nil) + + result := p.TextContent("#pingRequestText", nil) + assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) + + result = p.TextContent("#pingJSText", nil) + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + } -func TestLifecycleWaitForLoadStateDOMContentLoadedThenNetworkIdle(t *testing.T) { - // Test description - // - // 1. goto /home and wait for the domcontentloaded lifecycle event. - // 2. use WaitForLoadState with networkidle to now wait for the - // lifecycle event from the browser. - // - // Success criteria: We want to quickly move to calling WaitForLoadState - // so that we block until a networkidle lifecycle - // event is received from the browser. + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() - t.Parallel() + tb := newTestBrowser(t, withFileServer()) + p := tb.NewPage(nil) - tb := newTestBrowser(t, withFileServer()) - p := tb.NewPage(nil) + withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") + withPingHandler(t, tb, tt.pingSlowness, nil) + withPingJSHandler(t, tb, tt.pingJSSlow, nil) - withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") - withPingHandler(t, tb, time.Millisecond*100, nil) - withPingJSHandler(t, tb, false, nil) + if tt.assertFunc != nil { + assertHome(t, tb, p, tt.waitUntil, func() { tt.assertFunc(p) }) + return + } - assertHome(t, tb, p, common.LifecycleEventDOMContentLoad, func() { - p.WaitForLoadState(common.LifecycleEventNetworkIdle.String(), nil) + assertHome(t, tb, p, tt.waitUntil, func() { + result := p.TextContent("#pingRequestText", nil) + tt.pingRequestTextAssert(result) - result := p.TextContent("#pingRequestText", nil) - assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) + result = p.TextContent("#pingJSText", nil) + tt.pingJSTextAssert(result) - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "ping.js loaded from server", result) - }) + // This shouldn't block and return after calling hasLifecycleEventFired. + p.WaitForLoadState(tt.waitUntil.String(), nil) + }) + }) + } } func TestLifecycleReloadLoad(t *testing.T) { From b5d61b6e78846bdb96eaf39daf7c533f281a1053 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Thu, 10 Nov 2022 15:54:59 +0000 Subject: [PATCH 05/16] Refactor reload lifecycle lifecycle test Deduplicate them and convert them to a table test. --- tests/lifecycle_wait_test.go | 174 ++++++++++++++++++----------------- 1 file changed, 91 insertions(+), 83 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 7a7e33c6a..8e7f8c700 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -157,100 +157,108 @@ func TestLifecycleWaitForLoadState(t *testing.T) { } } -func TestLifecycleReloadLoad(t *testing.T) { +func TestLifecycleReload(t *testing.T) { t.Parallel() - tb := newTestBrowser(t, withFileServer()) - p := tb.NewPage(nil) - - withHomeHandler(t, tb, "reload_lifecycle.html") - withPingHandler(t, tb, time.Millisecond*100, nil) - withPingJSHandler(t, tb, false, nil) - - waitUntil := common.LifecycleEventLoad - assertHome(t, tb, p, waitUntil, func() { - result := p.TextContent("#pingRequestText", nil) - assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) - - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "ping.js loaded from server", result) - - opts := tb.toGojaValue(common.PageReloadOptions{ - WaitUntil: waitUntil, - Timeout: 30 * time.Second, - }) - p.Reload(opts) - - result = p.TextContent("#pingRequestText", nil) - assert.NotEqualValues(t, "Waiting... pong 20 - for loop complete", result) - - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "ping.js loaded from server", result) - }) -} - -func TestLifecycleReloadDOMContentLoaded(t *testing.T) { - t.Parallel() - - tb := newTestBrowser(t, withFileServer()) - p := tb.NewPage(nil) - - withHomeHandler(t, tb, "reload_lifecycle.html") - withPingHandler(t, tb, time.Millisecond*100, nil) - withPingJSHandler(t, tb, true, nil) - - waitUntil := common.LifecycleEventDOMContentLoad - assertHome(t, tb, p, waitUntil, func() { - result := p.TextContent("#pingRequestText", nil) - assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) - - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "Waiting...", result) - - opts := tb.toGojaValue(common.PageReloadOptions{ - WaitUntil: waitUntil, - Timeout: 30 * time.Second, - }) - p.Reload(opts) + tests := []struct { + name string + pingSlowness time.Duration + pingJSSlow bool + waitUntil common.LifecycleEvent + pingRequestTextAssert func(result string, pingCount int) + pingJSTextAssert func(result string) + }{ + { + // Test description + // + // 1. goto /home and wait for the load lifecycle event. + // 2. reload the page and wait with for the load lifecycle event. + // + // Success criteria: The resulting page after reload is the same as + // the initial navigation with goto. + name: "load", + pingSlowness: time.Millisecond * 100, + pingJSSlow: false, + waitUntil: common.LifecycleEventLoad, + pingRequestTextAssert: func(result string, pingCount int) { + assert.NotEqualValues(t, fmt.Sprintf("Waiting... pong %d - for loop complete", pingCount), result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + { + // Test description + // + // 1. goto /home and wait for the domcontentloaded lifecycle event. + // 2. reload the page and wait with for the domcontentloaded lifecycle event. + // + // Success criteria: The resulting page after reload is the same as + // the initial navigation with goto. + name: "domcontentloaded", + pingSlowness: time.Millisecond * 100, + pingJSSlow: true, + waitUntil: common.LifecycleEventDOMContentLoad, + pingRequestTextAssert: func(result string, pingCount int) { + assert.NotEqualValues(t, fmt.Sprintf("Waiting... pong %d - for loop complete", pingCount), result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "Waiting...", result) + }, + }, + { + // Test description + // + // 1. goto /home and wait for the networkidle lifecycle event. + // 2. reload the page and wait with for the networkidle lifecycle event. + // + // Success criteria: The resulting page after reload is the same as + // the initial navigation with goto. + name: "networkidle", + pingSlowness: 0, + pingJSSlow: false, + waitUntil: common.LifecycleEventNetworkIdle, + pingRequestTextAssert: func(result string, pingCount int) { + assert.EqualValues(t, fmt.Sprintf("Waiting... pong %d - for loop complete", pingCount), result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + } - result = p.TextContent("#pingRequestText", nil) - assert.NotEqualValues(t, "Waiting... pong 20 - for loop complete", result) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "Waiting...", result) - }) -} + tb := newTestBrowser(t, withFileServer()) + p := tb.NewPage(nil) -func TestLifecycleReloadNetworkIdle(t *testing.T) { - t.Parallel() + withHomeHandler(t, tb, "reload_lifecycle.html") + withPingHandler(t, tb, tt.pingSlowness, nil) + withPingJSHandler(t, tb, tt.pingJSSlow, nil) - tb := newTestBrowser(t, withFileServer()) - p := tb.NewPage(nil) + assertHome(t, tb, p, tt.waitUntil, func() { + result := p.TextContent("#pingRequestText", nil) + tt.pingRequestTextAssert(result, 10) - withHomeHandler(t, tb, "reload_lifecycle.html") - withPingHandler(t, tb, 0, nil) - withPingJSHandler(t, tb, false, nil) + result = p.TextContent("#pingJSText", nil) + tt.pingJSTextAssert(result) - waitUntil := common.LifecycleEventNetworkIdle - assertHome(t, tb, p, waitUntil, func() { - result := p.TextContent("#pingRequestText", nil) - assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) + opts := tb.toGojaValue(common.PageReloadOptions{ + WaitUntil: tt.waitUntil, + Timeout: 30 * time.Second, + }) + p.Reload(opts) - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "ping.js loaded from server", result) + result = p.TextContent("#pingRequestText", nil) + tt.pingRequestTextAssert(result, 20) - opts := tb.toGojaValue(common.PageReloadOptions{ - WaitUntil: waitUntil, - Timeout: 30 * time.Second, + result = p.TextContent("#pingJSText", nil) + tt.pingJSTextAssert(result) + }) }) - p.Reload(opts) - - result = p.TextContent("#pingRequestText", nil) - assert.EqualValues(t, "Waiting... pong 20 - for loop complete", result) - - result = p.TextContent("#pingJSText", nil) - assert.EqualValues(t, "ping.js loaded from server", result) - }) + } } func TestLifecycleNetworkIdle(t *testing.T) { From a5d283faa1c6587823e51c9a731b8bd4bf4b9341 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Thu, 10 Nov 2022 16:14:20 +0000 Subject: [PATCH 06/16] Refactor comment WaitForLoadState test --- tests/lifecycle_wait_test.go | 60 ++++++++++-------------------------- 1 file changed, 17 insertions(+), 43 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 8e7f8c700..d0f2203fa 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -36,6 +36,14 @@ import ( func TestLifecycleWaitForLoadState(t *testing.T) { t.Parallel() + // Test description + // + // 1. goto /home and wait for the specified lifecycle event. + // 2. use WaitForLoadState with the same specified lifecycle event. + // + // Success criteria: We want to ensure that the specified event is persisted in + // memory, and we don't block on WaitForLoadState. + tests := []struct { name string pingSlowness time.Duration @@ -46,13 +54,6 @@ func TestLifecycleWaitForLoadState(t *testing.T) { assertFunc func(p api.Page) }{ { - // Test description - // - // 1. goto /home and wait for the load lifecycle event. - // 2. use WaitForLoadState with load. - // - // Success criteria: We want to ensure that the load event is persisted in - // memory, and we don't block on WaitForLoadState. name: "load", pingSlowness: time.Millisecond * 100, pingJSSlow: false, @@ -65,13 +66,6 @@ func TestLifecycleWaitForLoadState(t *testing.T) { }, }, { - // Test description - // - // 1. goto /home and wait for the domcontentloaded lifecycle event. - // 2. use WaitForLoadState with domcontentloaded. - // - // Success criteria: We want to ensure that the domcontentloaded event is - // persisted in memory, and we don't block on WaitForLoadState. name: "domcontentloaded", pingSlowness: time.Millisecond * 100, pingJSSlow: true, @@ -84,13 +78,6 @@ func TestLifecycleWaitForLoadState(t *testing.T) { }, }, { - // Test description - // - // 1. goto /home and wait for the networkidle lifecycle event. - // 2. use WaitForLoadState with networkidle. - // - // Success criteria: We want to ensure that the networkidle event is - // persisted in memory, and we don't block on WaitForLoadState. name: "networkidle", pingSlowness: 0, pingJSSlow: false, @@ -110,7 +97,7 @@ func TestLifecycleWaitForLoadState(t *testing.T) { // // Success criteria: We want to quickly move to calling WaitForLoadState // so that we wait until networkidle is received from - // the browser. So not relying on the persisted state in memory. + // the browser -- not relying on the persisted state in memory. name: "domcontentloaded then networkidle", pingSlowness: time.Millisecond * 100, pingJSSlow: false, @@ -160,6 +147,14 @@ func TestLifecycleWaitForLoadState(t *testing.T) { func TestLifecycleReload(t *testing.T) { t.Parallel() + // Test description + // + // 1. goto /home and wait for the specified lifecycle event. + // 2. reload the page and wait for the specified lifecycle event. + // + // Success criteria: The resulting page after reload is the same as + // the initial navigation with goto. + tests := []struct { name string pingSlowness time.Duration @@ -169,13 +164,6 @@ func TestLifecycleReload(t *testing.T) { pingJSTextAssert func(result string) }{ { - // Test description - // - // 1. goto /home and wait for the load lifecycle event. - // 2. reload the page and wait with for the load lifecycle event. - // - // Success criteria: The resulting page after reload is the same as - // the initial navigation with goto. name: "load", pingSlowness: time.Millisecond * 100, pingJSSlow: false, @@ -188,13 +176,6 @@ func TestLifecycleReload(t *testing.T) { }, }, { - // Test description - // - // 1. goto /home and wait for the domcontentloaded lifecycle event. - // 2. reload the page and wait with for the domcontentloaded lifecycle event. - // - // Success criteria: The resulting page after reload is the same as - // the initial navigation with goto. name: "domcontentloaded", pingSlowness: time.Millisecond * 100, pingJSSlow: true, @@ -207,13 +188,6 @@ func TestLifecycleReload(t *testing.T) { }, }, { - // Test description - // - // 1. goto /home and wait for the networkidle lifecycle event. - // 2. reload the page and wait with for the networkidle lifecycle event. - // - // Success criteria: The resulting page after reload is the same as - // the initial navigation with goto. name: "networkidle", pingSlowness: 0, pingJSSlow: false, From 760acfa2ff75e80fcb61d76b8240b4670d49505e Mon Sep 17 00:00:00 2001 From: ankur22 Date: Thu, 10 Nov 2022 16:23:27 +0000 Subject: [PATCH 07/16] Add tests for goto lifecycle events --- tests/lifecycle_wait_test.go | 68 +++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index d0f2203fa..65017a039 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -235,7 +235,73 @@ func TestLifecycleReload(t *testing.T) { } } -func TestLifecycleNetworkIdle(t *testing.T) { +func TestLifecycleGoto(t *testing.T) { + t.Parallel() + + // Test description + // + // 1. goto /home and wait for the specified lifecycle event. + // + // Success criteria: The web page is in the expected state once we receive + // the specified lifecycle event from the browser. + + tests := []struct { + name string + pingSlowness time.Duration + pingJSSlow bool + waitUntil common.LifecycleEvent + pingRequestTextAssert func(result string) + pingJSTextAssert func(result string) + }{ + { + name: "load", + pingSlowness: time.Millisecond * 100, + pingJSSlow: false, + waitUntil: common.LifecycleEventLoad, + pingRequestTextAssert: func(result string) { + assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + { + name: "domcontentloaded", + pingSlowness: time.Millisecond * 100, + pingJSSlow: true, + waitUntil: common.LifecycleEventDOMContentLoad, + pingRequestTextAssert: func(result string) { + assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "Waiting...", result) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + tb := newTestBrowser(t, withFileServer()) + p := tb.NewPage(nil) + + withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") + withPingHandler(t, tb, tt.pingSlowness, nil) + withPingJSHandler(t, tb, tt.pingJSSlow, nil) + + assertHome(t, tb, p, tt.waitUntil, func() { + result := p.TextContent("#pingRequestText", nil) + tt.pingRequestTextAssert(result) + + result = p.TextContent("#pingJSText", nil) + tt.pingJSTextAssert(result) + }) + }) + } +} + +func TestLifecycleGotoNetworkIdle(t *testing.T) { t.Parallel() t.Run("doesn't timeout waiting for networkIdle", func(t *testing.T) { From fa3066c51ff94951a848da1fbb308880e5b07735 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Thu, 10 Nov 2022 16:46:37 +0000 Subject: [PATCH 08/16] Add goto lifecycle with subframe tests These tests ensure that we receive the subframe events before the lifecycle events for the main/root frame. This will ensure that the waitUntil only unblocks once all frames are ready. --- tests/lifecycle_wait_test.go | 113 +++++++++++++++++++++++++ tests/static/lifecycle_main_frame.html | 12 +++ tests/static/lifecycle_subframe.html | 37 ++++++++ 3 files changed, 162 insertions(+) create mode 100644 tests/static/lifecycle_main_frame.html create mode 100644 tests/static/lifecycle_subframe.html diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 65017a039..d7587fbf8 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -235,6 +235,86 @@ func TestLifecycleReload(t *testing.T) { } } +func TestLifecycleGotoWithSubFrame(t *testing.T) { + t.Parallel() + + // Test description + // + // 1. goto /home (with iframe to /sub) and wait for the specified lifecycle event. + // + // Success criteria: The web page (all frames) is in the expected state + // once we receive the specified lifecycle event from + // the browser. + + tests := []struct { + name string + pingSlowness time.Duration + pingJSSlow bool + waitUntil common.LifecycleEvent + pingRequestTextAssert func(result string) + pingJSTextAssert func(result string) + }{ + { + name: "load", + pingSlowness: time.Millisecond * 100, + pingJSSlow: false, + waitUntil: common.LifecycleEventLoad, + pingRequestTextAssert: func(result string) { + assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + { + name: "domcontentloaded", + pingSlowness: time.Millisecond * 100, + pingJSSlow: true, + waitUntil: common.LifecycleEventDOMContentLoad, + pingRequestTextAssert: func(result string) { + assert.NotEqualValues(t, "Waiting... pong 10 - for loop complete", result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "Waiting...", result) + }, + }, + { + name: "networkidle", + pingSlowness: 0, + pingJSSlow: false, + waitUntil: common.LifecycleEventNetworkIdle, + pingRequestTextAssert: func(result string) { + assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + tb := newTestBrowser(t, withFileServer()) + p := tb.NewPage(nil) + + withHomeHandler(t, tb, "lifecycle_main_frame.html") + withSubHandler(t, tb, "lifecycle_subframe.html") + withPingHandler(t, tb, tt.pingSlowness, nil) + withPingJSSubFrameHandler(t, tb, tt.pingJSSlow, nil) + + assertHome(t, tb, p, tt.waitUntil, func() { + result := p.TextContent("#subFramePingRequestText", nil) + tt.pingRequestTextAssert(result) + + result = p.TextContent("#subFramePingJSText", nil) + tt.pingJSTextAssert(result) + }) + }) + } +} + func TestLifecycleGoto(t *testing.T) { t.Parallel() @@ -373,6 +453,14 @@ func withHomeHandler(t *testing.T, tb *testBrowser, htmlFile string) { }) } +func withSubHandler(t *testing.T, tb *testBrowser, htmlFile string) { + t.Helper() + + tb.withHandler("/sub", func(w http.ResponseWriter, r *http.Request) { + http.Redirect(w, r, tb.staticURL(htmlFile), http.StatusMovedPermanently) + }) +} + func withPingHandler(t *testing.T, tb *testBrowser, slow time.Duration, ch chan bool) { t.Helper() @@ -415,6 +503,31 @@ func withPingJSHandler(t *testing.T, tb *testBrowser, slow bool, ch chan bool) { }) } +func withPingJSSubFrameHandler(t *testing.T, tb *testBrowser, slow bool, ch chan bool) { + t.Helper() + + tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { + script := ` + var pingJSTextOutput = document.getElementById("pingJSText"); + var parentOutputServerMsg = window.parent.document.getElementById('subFramePingJSText'); + + pingJSTextOutput.innerText = "ping.js loaded from server"; + parentOutputServerMsg.innerText = pingJSTextOutput.innerText; + ` + if slow { + script = ` + await new Promise(resolve => setTimeout(resolve, 1000)); + + ` + script + } + fmt.Fprint(w, script) + + if ch != nil { + close(ch) + } + }) +} + func assertHome( t *testing.T, tb *testBrowser, diff --git a/tests/static/lifecycle_main_frame.html b/tests/static/lifecycle_main_frame.html new file mode 100644 index 000000000..8141c1cb2 --- /dev/null +++ b/tests/static/lifecycle_main_frame.html @@ -0,0 +1,12 @@ + + + + + +
main
+
Waiting...
+
Waiting...
+ + + + \ No newline at end of file diff --git a/tests/static/lifecycle_subframe.html b/tests/static/lifecycle_subframe.html new file mode 100644 index 000000000..99e9b9b59 --- /dev/null +++ b/tests/static/lifecycle_subframe.html @@ -0,0 +1,37 @@ + + + + + +
Waiting...
+
Waiting...
+ + + + + + \ No newline at end of file From 2be58546501c453d84942f3776a1aa312604568f Mon Sep 17 00:00:00 2001 From: ankur22 Date: Thu, 10 Nov 2022 17:29:43 +0000 Subject: [PATCH 09/16] Refactor the lifecycle tests This is in preparation for the WaitForNavigation lifecycle tests. --- tests/lifecycle_wait_test.go | 63 ++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index d7587fbf8..d95d7fb0a 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -126,11 +126,14 @@ func TestLifecycleWaitForLoadState(t *testing.T) { withPingJSHandler(t, tb, tt.pingJSSlow, nil) if tt.assertFunc != nil { - assertHome(t, tb, p, tt.waitUntil, func() { tt.assertFunc(p) }) + assertHome(t, tb, p, tt.waitUntil, func() testPromise { + tt.assertFunc(p) + return testPromise{} + }, nil) return } - assertHome(t, tb, p, tt.waitUntil, func() { + assertHome(t, tb, p, tt.waitUntil, func() testPromise { result := p.TextContent("#pingRequestText", nil) tt.pingRequestTextAssert(result) @@ -139,7 +142,9 @@ func TestLifecycleWaitForLoadState(t *testing.T) { // This shouldn't block and return after calling hasLifecycleEventFired. p.WaitForLoadState(tt.waitUntil.String(), nil) - }) + + return testPromise{} + }, nil) }) } } @@ -212,7 +217,7 @@ func TestLifecycleReload(t *testing.T) { withPingHandler(t, tb, tt.pingSlowness, nil) withPingJSHandler(t, tb, tt.pingJSSlow, nil) - assertHome(t, tb, p, tt.waitUntil, func() { + assertHome(t, tb, p, tt.waitUntil, func() testPromise { result := p.TextContent("#pingRequestText", nil) tt.pingRequestTextAssert(result, 10) @@ -230,7 +235,9 @@ func TestLifecycleReload(t *testing.T) { result = p.TextContent("#pingJSText", nil) tt.pingJSTextAssert(result) - }) + + return testPromise{} + }, nil) }) } } @@ -304,13 +311,15 @@ func TestLifecycleGotoWithSubFrame(t *testing.T) { withPingHandler(t, tb, tt.pingSlowness, nil) withPingJSSubFrameHandler(t, tb, tt.pingJSSlow, nil) - assertHome(t, tb, p, tt.waitUntil, func() { + assertHome(t, tb, p, tt.waitUntil, func() testPromise { result := p.TextContent("#subFramePingRequestText", nil) tt.pingRequestTextAssert(result) result = p.TextContent("#subFramePingJSText", nil) tt.pingJSTextAssert(result) - }) + + return testPromise{} + }, nil) }) } } @@ -370,13 +379,15 @@ func TestLifecycleGoto(t *testing.T) { withPingHandler(t, tb, tt.pingSlowness, nil) withPingJSHandler(t, tb, tt.pingJSSlow, nil) - assertHome(t, tb, p, tt.waitUntil, func() { + assertHome(t, tb, p, tt.waitUntil, func() testPromise { result := p.TextContent("#pingRequestText", nil) tt.pingRequestTextAssert(result) result = p.TextContent("#pingJSText", nil) tt.pingJSTextAssert(result) - }) + + return testPromise{} + }, nil) }) } } @@ -403,10 +414,12 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { withPingJSHandler(t, tb, false, nil) - assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() { + assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() testPromise { result := p.TextContent("#pingJSText", nil) assert.EqualValues(t, "ping.js loaded from server", result) - }) + + return testPromise{} + }, nil) }) t.Run("doesn't unblock wait for networkIdle too early", func(t *testing.T) { @@ -420,13 +433,15 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { withPingHandler(t, tb, time.Millisecond*50, ch) withPingJSHandler(t, tb, false, ch) - assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() { + assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() testPromise { result := p.TextContent("#pingRequestText", nil) assert.EqualValues(t, "Waiting... pong 4 - for loop complete", result) result = p.TextContent("#pingJSText", nil) assert.EqualValues(t, "ping.js loaded from server", result) - }) + + return testPromise{} + }, nil) }) t.Run("doesn't unblock wait on networkIdle early when load and domcontentloaded complete at once", func(t *testing.T) { @@ -438,10 +453,12 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { withHomeHandler(t, tb, "prolonged_network_idle_10.html") withPingHandler(t, tb, time.Millisecond*50, nil) - assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() { + assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() testPromise { result := p.TextContent("#pingRequestText", nil) assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) - }) + + return testPromise{} + }, nil) }) } @@ -530,10 +547,9 @@ func withPingJSSubFrameHandler(t *testing.T, tb *testBrowser, slow bool, ch chan func assertHome( t *testing.T, - tb *testBrowser, - p api.Page, + tb *testBrowser, p api.Page, waitUntil common.LifecycleEvent, - check func(), + check func() testPromise, secondCheck func(), ) { t.Helper() @@ -543,15 +559,20 @@ func assertHome( WaitUntil: waitUntil, Timeout: 30 * time.Second, }) - tb.promise(p.Goto(tb.URL("/home"), opts)).then( - func() { - check() + prm := tb.promise(p.Goto(tb.URL("/home"), opts)).then( + func() testPromise { resolved = true + return check() }, func() { rejected = true }, ) + if secondCheck != nil { + prm.then(func() { + secondCheck() + }) + } return nil }) From e21c08cca81866b8a8d15272a11cf4ba4acf4b20 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Thu, 10 Nov 2022 17:58:08 +0000 Subject: [PATCH 10/16] Add WaitForNavigation lifecycle tests --- tests/lifecycle_wait_test.go | 105 +++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index d95d7fb0a..255114d04 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -14,6 +14,111 @@ import ( "github.com/grafana/xk6-browser/common" ) +func TestLifecycleWaitForNavigation(t *testing.T) { + // Test description + // + // 1. goto /home and wait for the specified lifecycle event. + // 2. click on a link that navigates to a page, and wait on + // the specified lifecycle event. + // + // Success criteria: The click will perform a navigation away + // from the current page, it should wait for + // the specified lifecycle event and the result + // of the page should match the original nav. + + t.Parallel() + + tests := []struct { + name string + pingSlowness time.Duration + pingJSSlow bool + waitUntil common.LifecycleEvent + pingRequestTextAssert func(result string, pingCount int) + pingJSTextAssert func(result string) + assertFunc func(p api.Page) + }{ + { + name: "load", + pingSlowness: time.Millisecond * 100, + pingJSSlow: false, + waitUntil: common.LifecycleEventLoad, + pingRequestTextAssert: func(result string, pingCount int) { + assert.NotEqualValues(t, fmt.Sprintf("Waiting... pong %d - for loop complete", pingCount), result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + { + name: "domcontentloaded", + pingSlowness: time.Millisecond * 100, + pingJSSlow: true, + waitUntil: common.LifecycleEventDOMContentLoad, + pingRequestTextAssert: func(result string, pingCount int) { + assert.NotEqualValues(t, fmt.Sprintf("Waiting... pong %d - for loop complete", pingCount), result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "Waiting...", result) + }, + }, + { + name: "networkidle", + pingSlowness: 0, + pingJSSlow: false, + waitUntil: common.LifecycleEventNetworkIdle, + pingRequestTextAssert: func(result string, pingCount int) { + assert.EqualValues(t, fmt.Sprintf("Waiting... pong %d - for loop complete", pingCount), result) + }, + pingJSTextAssert: func(result string) { + assert.EqualValues(t, "ping.js loaded from server", result) + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + tb := newTestBrowser(t, withFileServer()) + p := tb.NewPage(nil) + + withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") + withPingHandler(t, tb, tt.pingSlowness, nil) + withPingJSHandler(t, tb, tt.pingJSSlow, nil) + + if tt.assertFunc != nil { + assertHome(t, tb, p, tt.waitUntil, func() testPromise { + tt.assertFunc(p) + return testPromise{} + }, nil) + return + } + + assertHome(t, tb, p, tt.waitUntil, func() testPromise { + result := p.TextContent("#pingRequestText", nil) + tt.pingRequestTextAssert(result, 10) + + result = p.TextContent("#pingJSText", nil) + tt.pingJSTextAssert(result) + + waitForNav := p.WaitForNavigation(tb.toGojaValue(&common.FrameWaitForNavigationOptions{ + Timeout: 30000, + WaitUntil: tt.waitUntil, + })) + click := p.Click("#homeLink", nil) + + return tb.promiseAll(waitForNav, click) + }, func() { + result := p.TextContent("#pingRequestText", nil) + tt.pingRequestTextAssert(result, 20) + + result = p.TextContent("#pingJSText", nil) + tt.pingJSTextAssert(result) + }) + }) + } +} + // General guidelines on lifecycle events: // // - load: This lifecycle event is emitted by the browser once: From 40fbc7d1a33b429a030e22b9697d9c679d634eff Mon Sep 17 00:00:00 2001 From: ankur22 Date: Fri, 11 Nov 2022 10:28:14 +0000 Subject: [PATCH 11/16] Add a WaitForNavigation timeout test This test locks in the behaviour which highlights that it will timeout if the function call is made after the navigation has ended. --- tests/lifecycle_wait_test.go | 92 +++++++++++++++++++++++++++++------- 1 file changed, 74 insertions(+), 18 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 255114d04..5849165b6 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -14,6 +14,25 @@ import ( "github.com/grafana/xk6-browser/common" ) +// General guidelines on lifecycle events: +// +// - load: This lifecycle event is emitted by the browser once: +// 1. The HTML is loaded; +// 2. The async scripts have loaded; +// It does not wait for the other network requests to +// complete. +// +// - domcontentloaded: This lifecycle event is emitted by the +// browser once: +// 1. The HTML is loaded; +// It does not wait for the async scripts or +// the other network requests to complete. +// +// - networkidle: This lifecycle event is emitted by the browser once: +// 1. The HTML is loaded; +// 2. The async scripts have loaded; +// 3. All other network requests have completed; + func TestLifecycleWaitForNavigation(t *testing.T) { // Test description // @@ -119,24 +138,61 @@ func TestLifecycleWaitForNavigation(t *testing.T) { } } -// General guidelines on lifecycle events: -// -// - load: This lifecycle event is emitted by the browser once: -// 1. The HTML is loaded; -// 2. The async scripts have loaded; -// It does not wait for the other network requests to -// complete. -// -// - domcontentloaded: This lifecycle event is emitted by the -// browser once: -// 1. The HTML is loaded; -// It does not wait for the async scripts or -// the other network requests to complete. -// -// - networkidle: This lifecycle event is emitted by the browser once: -// 1. The HTML is loaded; -// 2. The async scripts have loaded; -// 3. All other network requests have completed; +func TestLifecycleWaitForNavigationTimeout(t *testing.T) { + t.Parallel() + + // Test description + // + // 1. goto /home and wait for the networkidle lifecycle event. + // 2. use WaitForNavigation with networkidle. + // + // Success criteria: Time out reached after navigation completed and + // wait for lifecycle event set, to signify that + // WaitForNavigation must be set before we navigate + // to a new page. + + tb := newTestBrowser(t, withFileServer()) + p := tb.NewPage(nil) + + withHomeHandler(t, tb, "prolonged_network_idle_10.html") + withPingHandler(t, tb, 0, nil) + + waitUntil := common.LifecycleEventNetworkIdle + var resolved, rejected bool + err := tb.await(func() error { + opts := tb.toGojaValue(common.FrameGotoOptions{ + WaitUntil: waitUntil, + Timeout: 30 * time.Second, + }) + prm := tb.promise(p.Goto(tb.URL("/home"), opts)).then( + func() testPromise { + result := p.TextContent("#pingRequestText", nil) + assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) + + waitForNav := p.WaitForNavigation(tb.toGojaValue(&common.FrameWaitForNavigationOptions{ + Timeout: 1000, + WaitUntil: waitUntil, + })) + + return tb.promise(waitForNav) + }, + ) + prm.then( + func() { + resolved = true + }, + func() { + rejected = true + }, + ) + + return nil + }) + require.NoError(t, err) + + assert.False(t, resolved) + assert.True(t, rejected) +} func TestLifecycleWaitForLoadState(t *testing.T) { t.Parallel() From 31365bdbef5b4bfc35d7f14a95e2f5e801f2ee28 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Fri, 11 Nov 2022 10:41:29 +0000 Subject: [PATCH 12/16] Refactor lifecycle helper method --- tests/lifecycle_wait_test.go | 42 +++++++++++------------------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 5849165b6..aa3483a7a 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -103,7 +103,7 @@ func TestLifecycleWaitForNavigation(t *testing.T) { withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") withPingHandler(t, tb, tt.pingSlowness, nil) - withPingJSHandler(t, tb, tt.pingJSSlow, nil) + withPingJSHandler(t, tb, tt.pingJSSlow, nil, false) if tt.assertFunc != nil { assertHome(t, tb, p, tt.waitUntil, func() testPromise { @@ -284,7 +284,7 @@ func TestLifecycleWaitForLoadState(t *testing.T) { withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") withPingHandler(t, tb, tt.pingSlowness, nil) - withPingJSHandler(t, tb, tt.pingJSSlow, nil) + withPingJSHandler(t, tb, tt.pingJSSlow, nil, false) if tt.assertFunc != nil { assertHome(t, tb, p, tt.waitUntil, func() testPromise { @@ -376,7 +376,7 @@ func TestLifecycleReload(t *testing.T) { withHomeHandler(t, tb, "reload_lifecycle.html") withPingHandler(t, tb, tt.pingSlowness, nil) - withPingJSHandler(t, tb, tt.pingJSSlow, nil) + withPingJSHandler(t, tb, tt.pingJSSlow, nil, false) assertHome(t, tb, p, tt.waitUntil, func() testPromise { result := p.TextContent("#pingRequestText", nil) @@ -470,7 +470,7 @@ func TestLifecycleGotoWithSubFrame(t *testing.T) { withHomeHandler(t, tb, "lifecycle_main_frame.html") withSubHandler(t, tb, "lifecycle_subframe.html") withPingHandler(t, tb, tt.pingSlowness, nil) - withPingJSSubFrameHandler(t, tb, tt.pingJSSlow, nil) + withPingJSHandler(t, tb, tt.pingJSSlow, nil, true) assertHome(t, tb, p, tt.waitUntil, func() testPromise { result := p.TextContent("#subFramePingRequestText", nil) @@ -538,7 +538,7 @@ func TestLifecycleGoto(t *testing.T) { withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") withPingHandler(t, tb, tt.pingSlowness, nil) - withPingJSHandler(t, tb, tt.pingJSSlow, nil) + withPingJSHandler(t, tb, tt.pingJSSlow, nil, false) assertHome(t, tb, p, tt.waitUntil, func() testPromise { result := p.TextContent("#pingRequestText", nil) @@ -573,7 +573,7 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { `) }) - withPingJSHandler(t, tb, false, nil) + withPingJSHandler(t, tb, false, nil, false) assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() testPromise { result := p.TextContent("#pingJSText", nil) @@ -592,7 +592,7 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { withHomeHandler(t, tb, "prolonged_network_idle.html") ch := make(chan bool) withPingHandler(t, tb, time.Millisecond*50, ch) - withPingJSHandler(t, tb, false, ch) + withPingJSHandler(t, tb, false, ch, false) assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() testPromise { result := p.TextContent("#pingRequestText", nil) @@ -659,7 +659,7 @@ func withPingHandler(t *testing.T, tb *testBrowser, slow time.Duration, ch chan }) } -func withPingJSHandler(t *testing.T, tb *testBrowser, slow bool, ch chan bool) { +func withPingJSHandler(t *testing.T, tb *testBrowser, slow bool, ch chan bool, withSubFrame bool) { t.Helper() tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { @@ -667,31 +667,13 @@ func withPingJSHandler(t *testing.T, tb *testBrowser, slow bool, ch chan bool) { var pingJSTextOutput = document.getElementById("pingJSText"); pingJSTextOutput.innerText = "ping.js loaded from server"; ` - if slow { - script = ` - await new Promise(resolve => setTimeout(resolve, 1000)); + if withSubFrame { + script += ` - ` + script - } - fmt.Fprint(w, script) - - if ch != nil { - close(ch) - } - }) -} - -func withPingJSSubFrameHandler(t *testing.T, tb *testBrowser, slow bool, ch chan bool) { - t.Helper() - - tb.withHandler("/ping.js", func(w http.ResponseWriter, _ *http.Request) { - script := ` - var pingJSTextOutput = document.getElementById("pingJSText"); var parentOutputServerMsg = window.parent.document.getElementById('subFramePingJSText'); - - pingJSTextOutput.innerText = "ping.js loaded from server"; parentOutputServerMsg.innerText = pingJSTextOutput.innerText; - ` + ` + } if slow { script = ` await new Promise(resolve => setTimeout(resolve, 1000)); From 7f17113c4aa6e3c24d4e3754536254cdf90eac3c Mon Sep 17 00:00:00 2001 From: ankur22 Date: Fri, 11 Nov 2022 15:40:32 +0000 Subject: [PATCH 13/16] Reformat duration for consistency Resolves: https://github.com/grafana/xk6-browser/pull/647#discussion_r1020172118 --- tests/lifecycle_wait_test.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index aa3483a7a..981879dc1 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -58,7 +58,7 @@ func TestLifecycleWaitForNavigation(t *testing.T) { }{ { name: "load", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: false, waitUntil: common.LifecycleEventLoad, pingRequestTextAssert: func(result string, pingCount int) { @@ -70,7 +70,7 @@ func TestLifecycleWaitForNavigation(t *testing.T) { }, { name: "domcontentloaded", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: true, waitUntil: common.LifecycleEventDOMContentLoad, pingRequestTextAssert: func(result string, pingCount int) { @@ -216,7 +216,7 @@ func TestLifecycleWaitForLoadState(t *testing.T) { }{ { name: "load", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: false, waitUntil: common.LifecycleEventLoad, pingRequestTextAssert: func(result string) { @@ -228,7 +228,7 @@ func TestLifecycleWaitForLoadState(t *testing.T) { }, { name: "domcontentloaded", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: true, waitUntil: common.LifecycleEventDOMContentLoad, pingRequestTextAssert: func(result string) { @@ -260,7 +260,7 @@ func TestLifecycleWaitForLoadState(t *testing.T) { // so that we wait until networkidle is received from // the browser -- not relying on the persisted state in memory. name: "domcontentloaded then networkidle", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: false, waitUntil: common.LifecycleEventDOMContentLoad, assertFunc: func(p api.Page) { @@ -331,7 +331,7 @@ func TestLifecycleReload(t *testing.T) { }{ { name: "load", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: false, waitUntil: common.LifecycleEventLoad, pingRequestTextAssert: func(result string, pingCount int) { @@ -343,7 +343,7 @@ func TestLifecycleReload(t *testing.T) { }, { name: "domcontentloaded", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: true, waitUntil: common.LifecycleEventDOMContentLoad, pingRequestTextAssert: func(result string, pingCount int) { @@ -424,7 +424,7 @@ func TestLifecycleGotoWithSubFrame(t *testing.T) { }{ { name: "load", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: false, waitUntil: common.LifecycleEventLoad, pingRequestTextAssert: func(result string) { @@ -436,7 +436,7 @@ func TestLifecycleGotoWithSubFrame(t *testing.T) { }, { name: "domcontentloaded", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: true, waitUntil: common.LifecycleEventDOMContentLoad, pingRequestTextAssert: func(result string) { @@ -505,7 +505,7 @@ func TestLifecycleGoto(t *testing.T) { }{ { name: "load", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: false, waitUntil: common.LifecycleEventLoad, pingRequestTextAssert: func(result string) { @@ -517,7 +517,7 @@ func TestLifecycleGoto(t *testing.T) { }, { name: "domcontentloaded", - pingSlowness: time.Millisecond * 100, + pingSlowness: 100 * time.Millisecond, pingJSSlow: true, waitUntil: common.LifecycleEventDOMContentLoad, pingRequestTextAssert: func(result string) { @@ -591,7 +591,7 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { withHomeHandler(t, tb, "prolonged_network_idle.html") ch := make(chan bool) - withPingHandler(t, tb, time.Millisecond*50, ch) + withPingHandler(t, tb, 50*time.Millisecond, ch) withPingJSHandler(t, tb, false, ch, false) assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() testPromise { @@ -612,7 +612,7 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { p := tb.NewPage(nil) withHomeHandler(t, tb, "prolonged_network_idle_10.html") - withPingHandler(t, tb, time.Millisecond*50, nil) + withPingHandler(t, tb, 50*time.Millisecond, nil) assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() testPromise { result := p.TextContent("#pingRequestText", nil) From 6791e5b14c589f69ac387b0908f05598d2e19b62 Mon Sep 17 00:00:00 2001 From: Ankur Date: Fri, 11 Nov 2022 15:44:12 +0000 Subject: [PATCH 14/16] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: İnanç Gümüş --- tests/lifecycle_wait_test.go | 133 ++++++++++++++++++++--------------- 1 file changed, 76 insertions(+), 57 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 981879dc1..82b58a182 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -16,34 +16,46 @@ import ( // General guidelines on lifecycle events: // -// - load: This lifecycle event is emitted by the browser once: -// 1. The HTML is loaded; -// 2. The async scripts have loaded; -// It does not wait for the other network requests to -// complete. +// load // -// - domcontentloaded: This lifecycle event is emitted by the -// browser once: -// 1. The HTML is loaded; -// It does not wait for the async scripts or -// the other network requests to complete. +// The load event is fired when the initial HTML document has been completely +// loaded. It does not wait for the other network requests to complete. // -// - networkidle: This lifecycle event is emitted by the browser once: -// 1. The HTML is loaded; -// 2. The async scripts have loaded; -// 3. All other network requests have completed; +// Emitted by the browser once: +// 1. The HTML is loaded. +// 2. The async scripts have loaded. +// +// domcontentloaded +// +// The DOMContentLoaded event is fired when the initial HTML document has been +// completely loaded and parsed. It does not wait for the async scripts or the +// other network requests to complete. +// +// Emitted by the browser once: +// 1. The HTML is loaded. +// +// networkidle +// +// The networkidle event is fired when there are no network connections for at +// least 500ms. +// +// Emitted by the browser once: +// 1. The HTML is loaded. +// 2. The async scripts have loaded. +// 3. All other network requests have completed. func TestLifecycleWaitForNavigation(t *testing.T) { // Test description // - // 1. goto /home and wait for the specified lifecycle event. - // 2. click on a link that navigates to a page, and wait on + // Steps: + // 1. goto /home and wait for the specified lifecycle event. + // 2. click on a link that navigates to a page, and wait on // the specified lifecycle event. // - // Success criteria: The click will perform a navigation away - // from the current page, it should wait for - // the specified lifecycle event and the result - // of the page should match the original nav. + // Success criteria: + // The click will perform a navigation away from the current page, + // it should wait for the specified lifecycle event and the result + // of the page should match the original nav. t.Parallel() @@ -143,13 +155,15 @@ func TestLifecycleWaitForNavigationTimeout(t *testing.T) { // Test description // - // 1. goto /home and wait for the networkidle lifecycle event. - // 2. use WaitForNavigation with networkidle. + // Steps: + // 1. goto /home and wait for the networkidle lifecycle event. + // 2. use WaitForNavigation with networkidle. // - // Success criteria: Time out reached after navigation completed and - // wait for lifecycle event set, to signify that - // WaitForNavigation must be set before we navigate - // to a new page. + // Success criteria: + // Time out reached after navigation completed and + // wait for lifecycle event set, to signify that + // WaitForNavigation must be set before we navigate + // to a new page. tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) @@ -177,14 +191,11 @@ func TestLifecycleWaitForNavigationTimeout(t *testing.T) { return tb.promise(waitForNav) }, ) - prm.then( - func() { - resolved = true - }, - func() { - rejected = true - }, - ) + prm.then(func() { + resolved = true + }, func() { + rejected = true + }) return nil }) @@ -199,11 +210,13 @@ func TestLifecycleWaitForLoadState(t *testing.T) { // Test description // - // 1. goto /home and wait for the specified lifecycle event. - // 2. use WaitForLoadState with the same specified lifecycle event. + // Steps: + // 1. goto /home and wait for the specified lifecycle event. + // 2. use WaitForLoadState with the same specified lifecycle event. // - // Success criteria: We want to ensure that the specified event is persisted in - // memory, and we don't block on WaitForLoadState. + // Success criteria: + // We want to ensure that the specified event is persisted in + // memory, and we don't block on WaitForLoadState. tests := []struct { name string @@ -253,12 +266,14 @@ func TestLifecycleWaitForLoadState(t *testing.T) { { // Test description // - // 1. goto /home and wait for the domcontentloaded lifecycle event. - // 2. use WaitForLoadState with networkidle. + // Steps: + // 1. goto /home and wait for the domcontentloaded lifecycle event. + // 2. use WaitForLoadState with networkidle. // - // Success criteria: We want to quickly move to calling WaitForLoadState - // so that we wait until networkidle is received from - // the browser -- not relying on the persisted state in memory. + // Success criteria: + // We want to quickly move to calling WaitForLoadState + // so that we wait until networkidle is received from + // the browser -- not relying on the persisted state in memory. name: "domcontentloaded then networkidle", pingSlowness: 100 * time.Millisecond, pingJSSlow: false, @@ -315,11 +330,13 @@ func TestLifecycleReload(t *testing.T) { // Test description // - // 1. goto /home and wait for the specified lifecycle event. - // 2. reload the page and wait for the specified lifecycle event. + // Steps: + // 1. goto /home and wait for the specified lifecycle event. + // 2. reload the page and wait for the specified lifecycle event. // - // Success criteria: The resulting page after reload is the same as - // the initial navigation with goto. + // Success criteria: + // The resulting page after reload is the same as + // the initial navigation with goto. tests := []struct { name string @@ -408,11 +425,13 @@ func TestLifecycleGotoWithSubFrame(t *testing.T) { // Test description // - // 1. goto /home (with iframe to /sub) and wait for the specified lifecycle event. + // Steps: + // 1. goto /home (with iframe to /sub) and wait for the specified lifecycle event. // - // Success criteria: The web page (all frames) is in the expected state - // once we receive the specified lifecycle event from - // the browser. + // Success criteria: + // The web page (all frames) is in the expected state + // once we receive the specified lifecycle event from + // the browser. tests := []struct { name string @@ -490,10 +509,12 @@ func TestLifecycleGoto(t *testing.T) { // Test description // - // 1. goto /home and wait for the specified lifecycle event. + // Steps: + // 1. goto /home and wait for the specified lifecycle event. // - // Success criteria: The web page is in the expected state once we receive - // the specified lifecycle event from the browser. + // Success criteria: + // The web page is in the expected state once we receive + // the specified lifecycle event from the browser. tests := []struct { name string @@ -712,9 +733,7 @@ func assertHome( }, ) if secondCheck != nil { - prm.then(func() { - secondCheck() - }) + prm.then(secondCheck) } return nil From 43956c67f704e3465405a10fb8fe1f953656de48 Mon Sep 17 00:00:00 2001 From: ankur22 Date: Fri, 11 Nov 2022 16:54:38 +0000 Subject: [PATCH 15/16] Refactor static lifecycle html file Consolidates as much of the test functionality into one html file. --- tests/lifecycle_wait_test.go | 16 +++++----- ...lifecycle_subframe.html => lifecycle.html} | 7 ++++- ...idle_10.html => lifecycle_no_ping_js.html} | 0 tests/static/prolonged_network_idle.html | 30 ------------------ tests/static/reload_lifecycle.html | 30 ------------------ tests/static/wait_for_nav_lifecycle.html | 31 ------------------- 6 files changed, 14 insertions(+), 100 deletions(-) rename tests/static/{lifecycle_subframe.html => lifecycle.html} (80%) rename tests/static/{prolonged_network_idle_10.html => lifecycle_no_ping_js.html} (100%) delete mode 100644 tests/static/prolonged_network_idle.html delete mode 100644 tests/static/reload_lifecycle.html delete mode 100644 tests/static/wait_for_nav_lifecycle.html diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 82b58a182..97774283f 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -113,7 +113,7 @@ func TestLifecycleWaitForNavigation(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") + withHomeHandler(t, tb, "lifecycle.html?pingCount=10") withPingHandler(t, tb, tt.pingSlowness, nil) withPingJSHandler(t, tb, tt.pingJSSlow, nil, false) @@ -168,7 +168,7 @@ func TestLifecycleWaitForNavigationTimeout(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - withHomeHandler(t, tb, "prolonged_network_idle_10.html") + withHomeHandler(t, tb, "lifecycle_no_ping_js.html") withPingHandler(t, tb, 0, nil) waitUntil := common.LifecycleEventNetworkIdle @@ -297,7 +297,7 @@ func TestLifecycleWaitForLoadState(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") + withHomeHandler(t, tb, "lifecycle.html?pingCount=10") withPingHandler(t, tb, tt.pingSlowness, nil) withPingJSHandler(t, tb, tt.pingJSSlow, nil, false) @@ -391,7 +391,7 @@ func TestLifecycleReload(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - withHomeHandler(t, tb, "reload_lifecycle.html") + withHomeHandler(t, tb, "lifecycle.html?pingCount=10") withPingHandler(t, tb, tt.pingSlowness, nil) withPingJSHandler(t, tb, tt.pingJSSlow, nil, false) @@ -487,7 +487,7 @@ func TestLifecycleGotoWithSubFrame(t *testing.T) { p := tb.NewPage(nil) withHomeHandler(t, tb, "lifecycle_main_frame.html") - withSubHandler(t, tb, "lifecycle_subframe.html") + withSubHandler(t, tb, "lifecycle.html?pingCount=10") withPingHandler(t, tb, tt.pingSlowness, nil) withPingJSHandler(t, tb, tt.pingJSSlow, nil, true) @@ -557,7 +557,7 @@ func TestLifecycleGoto(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - withHomeHandler(t, tb, "wait_for_nav_lifecycle.html") + withHomeHandler(t, tb, "lifecycle.html?pingCount=10") withPingHandler(t, tb, tt.pingSlowness, nil) withPingJSHandler(t, tb, tt.pingJSSlow, nil, false) @@ -610,7 +610,7 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - withHomeHandler(t, tb, "prolonged_network_idle.html") + withHomeHandler(t, tb, "lifecycle.html?pingCount=4") ch := make(chan bool) withPingHandler(t, tb, 50*time.Millisecond, ch) withPingJSHandler(t, tb, false, ch, false) @@ -632,7 +632,7 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { tb := newTestBrowser(t, withFileServer()) p := tb.NewPage(nil) - withHomeHandler(t, tb, "prolonged_network_idle_10.html") + withHomeHandler(t, tb, "lifecycle_no_ping_js.html") withPingHandler(t, tb, 50*time.Millisecond, nil) assertHome(t, tb, p, common.LifecycleEventNetworkIdle, func() testPromise { diff --git a/tests/static/lifecycle_subframe.html b/tests/static/lifecycle.html similarity index 80% rename from tests/static/lifecycle_subframe.html rename to tests/static/lifecycle.html index 99e9b9b59..c3db753bb 100644 --- a/tests/static/lifecycle_subframe.html +++ b/tests/static/lifecycle.html @@ -3,6 +3,7 @@ + Home
Waiting...
Waiting...
@@ -19,7 +20,11 @@ }) async function pingRequestText() { - for (var i = 0; i < 10; i++) { + const queryString = window.location.search; + const urlParams = new URLSearchParams(queryString); + const pingCount = urlParams.get('pingCount') + + for (var i = 0; i < pingCount; i++) { await fetch('/ping') .then(response => response.text()) .then((data) => { diff --git a/tests/static/prolonged_network_idle_10.html b/tests/static/lifecycle_no_ping_js.html similarity index 100% rename from tests/static/prolonged_network_idle_10.html rename to tests/static/lifecycle_no_ping_js.html diff --git a/tests/static/prolonged_network_idle.html b/tests/static/prolonged_network_idle.html deleted file mode 100644 index 9bfade531..000000000 --- a/tests/static/prolonged_network_idle.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - -
Waiting...
-
Waiting...
- - - - - - \ No newline at end of file diff --git a/tests/static/reload_lifecycle.html b/tests/static/reload_lifecycle.html deleted file mode 100644 index bc0349abc..000000000 --- a/tests/static/reload_lifecycle.html +++ /dev/null @@ -1,30 +0,0 @@ - - - - - -
Waiting...
-
Waiting...
- - - - - - \ No newline at end of file diff --git a/tests/static/wait_for_nav_lifecycle.html b/tests/static/wait_for_nav_lifecycle.html deleted file mode 100644 index d2fd9ad39..000000000 --- a/tests/static/wait_for_nav_lifecycle.html +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - Home -
Waiting...
-
Waiting...
- - - - - - \ No newline at end of file From 215000dcc6f01c983ae240b8480436db458c59ff Mon Sep 17 00:00:00 2001 From: ankur22 Date: Fri, 11 Nov 2022 17:12:20 +0000 Subject: [PATCH 16/16] Refactor timeout lifecycle test into table test Resolves: https://github.com/grafana/xk6-browser/pull/647#discussion_r1020197128 --- tests/lifecycle_wait_test.go | 118 +++++++++++++++-------------------- 1 file changed, 49 insertions(+), 69 deletions(-) diff --git a/tests/lifecycle_wait_test.go b/tests/lifecycle_wait_test.go index 97774283f..e685a3da8 100644 --- a/tests/lifecycle_wait_test.go +++ b/tests/lifecycle_wait_test.go @@ -66,7 +66,8 @@ func TestLifecycleWaitForNavigation(t *testing.T) { waitUntil common.LifecycleEvent pingRequestTextAssert func(result string, pingCount int) pingJSTextAssert func(result string) - assertFunc func(p api.Page) + assertFunc func(tb *testBrowser, p api.Page) testPromise + wantError string }{ { name: "load", @@ -104,6 +105,35 @@ func TestLifecycleWaitForNavigation(t *testing.T) { assert.EqualValues(t, "ping.js loaded from server", result) }, }, + { + // Test description + // + // Steps: + // 1. goto /home and wait for the specified lifecycle event. + // 2. call WaitForNavigation without clicking on the link. + // the specified lifecycle event. + // + // Success criteria: + // We want this test to timeout since the navigation has + // completed, a new one hasn't started but we "accidentally" + // call WaitForNavigation. + name: "timeout", + pingSlowness: 0, + pingJSSlow: false, + waitUntil: common.LifecycleEventNetworkIdle, + assertFunc: func(tb *testBrowser, p api.Page) testPromise { + result := p.TextContent("#pingRequestText", nil) + assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) + + waitForNav := p.WaitForNavigation(tb.toGojaValue(&common.FrameWaitForNavigationOptions{ + Timeout: 1000, + WaitUntil: common.LifecycleEventNetworkIdle, + })) + + return tb.promise(waitForNav) + }, + wantError: "Uncaught (in promise) waiting for navigation: timed out after 1s", + }, } for _, tt := range tests { @@ -119,9 +149,8 @@ func TestLifecycleWaitForNavigation(t *testing.T) { if tt.assertFunc != nil { assertHome(t, tb, p, tt.waitUntil, func() testPromise { - tt.assertFunc(p) - return testPromise{} - }, nil) + return tt.assertFunc(tb, p) + }, nil, tt.wantError) return } @@ -145,66 +174,11 @@ func TestLifecycleWaitForNavigation(t *testing.T) { result = p.TextContent("#pingJSText", nil) tt.pingJSTextAssert(result) - }) + }, "") }) } } -func TestLifecycleWaitForNavigationTimeout(t *testing.T) { - t.Parallel() - - // Test description - // - // Steps: - // 1. goto /home and wait for the networkidle lifecycle event. - // 2. use WaitForNavigation with networkidle. - // - // Success criteria: - // Time out reached after navigation completed and - // wait for lifecycle event set, to signify that - // WaitForNavigation must be set before we navigate - // to a new page. - - tb := newTestBrowser(t, withFileServer()) - p := tb.NewPage(nil) - - withHomeHandler(t, tb, "lifecycle_no_ping_js.html") - withPingHandler(t, tb, 0, nil) - - waitUntil := common.LifecycleEventNetworkIdle - var resolved, rejected bool - err := tb.await(func() error { - opts := tb.toGojaValue(common.FrameGotoOptions{ - WaitUntil: waitUntil, - Timeout: 30 * time.Second, - }) - prm := tb.promise(p.Goto(tb.URL("/home"), opts)).then( - func() testPromise { - result := p.TextContent("#pingRequestText", nil) - assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) - - waitForNav := p.WaitForNavigation(tb.toGojaValue(&common.FrameWaitForNavigationOptions{ - Timeout: 1000, - WaitUntil: waitUntil, - })) - - return tb.promise(waitForNav) - }, - ) - prm.then(func() { - resolved = true - }, func() { - rejected = true - }) - - return nil - }) - require.NoError(t, err) - - assert.False(t, resolved) - assert.True(t, rejected) -} - func TestLifecycleWaitForLoadState(t *testing.T) { t.Parallel() @@ -305,7 +279,7 @@ func TestLifecycleWaitForLoadState(t *testing.T) { assertHome(t, tb, p, tt.waitUntil, func() testPromise { tt.assertFunc(p) return testPromise{} - }, nil) + }, nil, "") return } @@ -320,7 +294,7 @@ func TestLifecycleWaitForLoadState(t *testing.T) { p.WaitForLoadState(tt.waitUntil.String(), nil) return testPromise{} - }, nil) + }, nil, "") }) } } @@ -415,7 +389,7 @@ func TestLifecycleReload(t *testing.T) { tt.pingJSTextAssert(result) return testPromise{} - }, nil) + }, nil, "") }) } } @@ -499,7 +473,7 @@ func TestLifecycleGotoWithSubFrame(t *testing.T) { tt.pingJSTextAssert(result) return testPromise{} - }, nil) + }, nil, "") }) } } @@ -569,7 +543,7 @@ func TestLifecycleGoto(t *testing.T) { tt.pingJSTextAssert(result) return testPromise{} - }, nil) + }, nil, "") }) } } @@ -601,7 +575,7 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { assert.EqualValues(t, "ping.js loaded from server", result) return testPromise{} - }, nil) + }, nil, "") }) t.Run("doesn't unblock wait for networkIdle too early", func(t *testing.T) { @@ -623,7 +597,7 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { assert.EqualValues(t, "ping.js loaded from server", result) return testPromise{} - }, nil) + }, nil, "") }) t.Run("doesn't unblock wait on networkIdle early when load and domcontentloaded complete at once", func(t *testing.T) { @@ -640,7 +614,7 @@ func TestLifecycleGotoNetworkIdle(t *testing.T) { assert.EqualValues(t, "Waiting... pong 10 - for loop complete", result) return testPromise{} - }, nil) + }, nil, "") }) } @@ -713,7 +687,7 @@ func assertHome( t *testing.T, tb *testBrowser, p api.Page, waitUntil common.LifecycleEvent, - check func() testPromise, secondCheck func(), + check func() testPromise, secondCheck func(), wantError string, ) { t.Helper() @@ -738,6 +712,12 @@ func assertHome( return nil }) + + if wantError != "" { + require.EqualError(t, err, wantError) + return + } + require.NoError(t, err) assert.True(t, resolved)