Skip to content

Commit e51b45d

Browse files
committed
Add a test for page.on('response')
1 parent c1d22de commit e51b45d

File tree

1 file changed

+347
-0
lines changed

1 file changed

+347
-0
lines changed

internal/js/modules/k6/browser/tests/page_test.go

+347
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"regexp"
1212
"runtime"
1313
"strconv"
14+
"strings"
1415
"sync/atomic"
1516
"testing"
1617
"time"
@@ -2475,3 +2476,349 @@ type request struct {
24752476
Size map[string]int `json:"size"`
24762477
URL string `json:"url"`
24772478
}
2479+
2480+
type response struct {
2481+
AllHeaders map[string]string `json:"allHeaders"`
2482+
Body string `json:"body"`
2483+
FrameURL string `json:"frameUrl"`
2484+
AcceptLanguageHeader string `json:"acceptLanguageHeader"`
2485+
AcceptLanguageHeaders []string `json:"acceptLanguageHeaders"`
2486+
Headers map[string]string `json:"headers"`
2487+
HeadersArray []map[string]string `json:"headersArray"`
2488+
JSON string `json:"json"`
2489+
OK bool `json:"ok"`
2490+
RequestURL string `json:"requestUrl"`
2491+
SecurityDetails map[string]string `json:"securityDetails"`
2492+
ServerAddr map[string]any `json:"serverAddr"`
2493+
Size map[string]int `json:"size"`
2494+
Status int64 `json:"status"`
2495+
StatusText string `json:"statusText"`
2496+
URL string `json:"url"`
2497+
Text string `json:"text"`
2498+
}
2499+
2500+
func TestPageOnResponse(t *testing.T) {
2501+
t.Parallel()
2502+
2503+
// Start and setup a webserver to test the page.on('request') handler.
2504+
tb := newTestBrowser(t, withHTTPServer())
2505+
defer tb.Browser.Close()
2506+
2507+
tb.withHandler("/home", func(w http.ResponseWriter, r *http.Request) {
2508+
_, err := fmt.Fprintf(w, `<!DOCTYPE html>
2509+
<html>
2510+
<head>
2511+
<link rel="stylesheet" href="/style.css">
2512+
</head>
2513+
<body>
2514+
<script>fetch('/api', {
2515+
method: 'POST',
2516+
headers: {
2517+
'Content-Type': 'application/json'
2518+
},
2519+
body: JSON.stringify({name: 'tester'})
2520+
})</script>
2521+
</body>
2522+
</html>`)
2523+
require.NoError(t, err)
2524+
})
2525+
tb.withHandler("/api", func(w http.ResponseWriter, r *http.Request) {
2526+
body, err := io.ReadAll(r.Body)
2527+
require.NoError(t, err)
2528+
defer require.NoError(t, r.Body.Close())
2529+
2530+
var data struct {
2531+
Name string `json:"name"`
2532+
}
2533+
err = json.Unmarshal(body, &data)
2534+
require.NoError(t, err)
2535+
2536+
_, err = fmt.Fprintf(w, `{"message": "Hello %s!"}`, data.Name)
2537+
require.NoError(t, err)
2538+
})
2539+
tb.withHandler("/style.css", func(w http.ResponseWriter, r *http.Request) {
2540+
w.Header().Set("Content-Type", "text/css")
2541+
_, err := fmt.Fprintf(w, `body { background-color: #f0f0f0; }`)
2542+
require.NoError(t, err)
2543+
})
2544+
2545+
// Start and setup a k6 iteration to test the page.on('request') handler.
2546+
vu, _, _, cleanUp := startIteration(t)
2547+
defer cleanUp()
2548+
2549+
// Some of the business logic is in the mapping layer unfortunately.
2550+
// To test everything is wried up correctly, we're required to work
2551+
// with RunPromise.
2552+
//
2553+
// The code below is the JavaScript code that is executed in the k6 iteration.
2554+
// It will wait for all requests to be captured in returnValue, before returning.
2555+
gv, err := vu.RunAsync(t, `
2556+
const context = await browser.newContext({locale: 'en-US', userAgent: 'some-user-agent'});
2557+
const page = await context.newPage();
2558+
2559+
var returnValue = [];
2560+
page.on('response', async (response) => {
2561+
// We need to check if the response is JSON before calling json()
2562+
const headers = response.headers();
2563+
var json = null;
2564+
if (headers["content-type"] === "application/json") {
2565+
json = await response.json();
2566+
}
2567+
2568+
returnValue.push({
2569+
allHeaders: await response.allHeaders(),
2570+
body: await response.body() ? String.fromCharCode.apply(null, new Uint8Array(await response.body())) : null,
2571+
frameUrl: response.frame().url(),
2572+
acceptLanguageHeader: await response.headerValue('Accept-Language'),
2573+
acceptLanguageHeaders: await response.headerValues('Accept-Language'),
2574+
headers: response.headers(),
2575+
headersArray: await response.headersArray(),
2576+
json: json,
2577+
ok: response.ok(),
2578+
requestUrl: response.request().url(),
2579+
securityDetails: await response.securityDetails(),
2580+
serverAddr: await response.serverAddr(),
2581+
size: await response.size(),
2582+
status: response.status(),
2583+
statusText: response.statusText(),
2584+
url: response.url(),
2585+
text: await response.text()
2586+
});
2587+
})
2588+
2589+
await page.goto('%s', {waitUntil: 'networkidle'});
2590+
2591+
await page.close();
2592+
2593+
return JSON.stringify(returnValue, null, 2);
2594+
`, tb.url("/home"))
2595+
assert.NoError(t, err)
2596+
2597+
got := k6test.ToPromise(t, gv)
2598+
2599+
// Convert the result to a string and then to a slice of requests.
2600+
var responses []response
2601+
err = json.Unmarshal([]byte(got.Result().String()), &responses)
2602+
require.NoError(t, err)
2603+
2604+
for i := range responses {
2605+
// Normalize any port numbers in the string values to :8080
2606+
if responses[i].URL != "" {
2607+
responses[i].URL = regexp.MustCompile(`:\d+`).ReplaceAllString(responses[i].URL, ":8080")
2608+
}
2609+
if responses[i].FrameURL != "" {
2610+
responses[i].FrameURL = regexp.MustCompile(`:\d+`).ReplaceAllString(responses[i].FrameURL, ":8080")
2611+
}
2612+
for k, v := range responses[i].AllHeaders {
2613+
responses[i].AllHeaders[k] = regexp.MustCompile(`:\d+`).ReplaceAllString(v, ":8080")
2614+
2615+
// Normalize the date
2616+
if strings.Contains(strings.ToLower(k), "date") {
2617+
responses[i].AllHeaders[k] = "Wed, 29 Jan 2025 09:00:00 GMT"
2618+
}
2619+
}
2620+
for k, v := range responses[i].Headers {
2621+
responses[i].Headers[k] = regexp.MustCompile(`:\d+`).ReplaceAllString(v, ":8080")
2622+
2623+
// Normalize the date
2624+
if strings.Contains(strings.ToLower(k), "date") {
2625+
responses[i].Headers[k] = "Wed, 29 Jan 2025 09:00:00 GMT"
2626+
}
2627+
}
2628+
for k, header := range responses[i].HeadersArray {
2629+
if header["value"] != "" {
2630+
responses[i].HeadersArray[k]["value"] = regexp.MustCompile(`:\d+`).ReplaceAllString(header["value"], ":8080")
2631+
}
2632+
// Normalize the date
2633+
if strings.Contains(strings.ToLower(header["name"]), "date") {
2634+
responses[i].HeadersArray[k]["value"] = "Wed, 29 Jan 2025 09:00:00 GMT"
2635+
}
2636+
}
2637+
for k := range responses[i].ServerAddr {
2638+
if k == "port" {
2639+
responses[i].ServerAddr[k] = 8080
2640+
}
2641+
}
2642+
if responses[i].RequestURL != "" {
2643+
responses[i].RequestURL = regexp.MustCompile(`:\d+`).ReplaceAllString(responses[i].RequestURL, ":8080")
2644+
}
2645+
}
2646+
2647+
expected := []response{
2648+
{
2649+
AllHeaders: map[string]string{
2650+
"content-length": "286",
2651+
"content-type": "text/html; charset=utf-8",
2652+
"date": "Wed, 29 Jan 2025 09:00:00 GMT",
2653+
},
2654+
Body: "<!DOCTYPE html>\n<html>\n<head>\n <link rel=\"stylesheet\" href=\"/style.css\">\n</head>\n<body>\n <script>fetch('/api', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({name: 'tester'})\n })</script>\n</body>\n</html>",
2655+
FrameURL: "http://127.0.0.1:8080/home",
2656+
AcceptLanguageHeader: "",
2657+
AcceptLanguageHeaders: []string{""},
2658+
Headers: map[string]string{
2659+
"Content-Length": "286",
2660+
"Content-Type": "text/html; charset=utf-8",
2661+
"Date": "Wed, 29 Jan 2025 09:00:00 GMT",
2662+
},
2663+
HeadersArray: []map[string]string{
2664+
{"name": "Content-Length", "value": "286"},
2665+
{"name": "Content-Type", "value": "text/html; charset=utf-8"},
2666+
{"name": "Date", "value": "Wed, 29 Jan 2025 09:00:00 GMT"},
2667+
},
2668+
JSON: "",
2669+
OK: true,
2670+
RequestURL: "http://127.0.0.1:8080/home",
2671+
SecurityDetails: map[string]string(nil),
2672+
ServerAddr: map[string]interface{}{"ip_address": "127.0.0.1", "port": 8080},
2673+
Size: map[string]int{"body": 286, "headers": 117},
2674+
Status: 200,
2675+
StatusText: "OK",
2676+
URL: "http://127.0.0.1:8080/home",
2677+
Text: "<!DOCTYPE html>\n<html>\n<head>\n <link rel=\"stylesheet\" href=\"/style.css\">\n</head>\n<body>\n <script>fetch('/api', {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json'\n },\n body: JSON.stringify({name: 'tester'})\n })</script>\n</body>\n</html>",
2678+
},
2679+
{
2680+
AllHeaders: map[string]string{
2681+
"content-length": "35",
2682+
"content-type": "text/css",
2683+
"date": "Wed, 29 Jan 2025 09:00:00 GMT",
2684+
},
2685+
Body: "body { background-color: #f0f0f0; }",
2686+
FrameURL: "http://127.0.0.1:8080/home",
2687+
AcceptLanguageHeader: "",
2688+
AcceptLanguageHeaders: []string{""},
2689+
Headers: map[string]string{
2690+
"Content-Length": "35",
2691+
"Content-Type": "text/css",
2692+
"Date": "Wed, 29 Jan 2025 09:00:00 GMT",
2693+
},
2694+
HeadersArray: []map[string]string{
2695+
{"name": "Date", "value": "Wed, 29 Jan 2025 09:00:00 GMT"},
2696+
{"name": "Content-Type", "value": "text/css"},
2697+
{"name": "Content-Length", "value": "35"},
2698+
},
2699+
JSON: "",
2700+
OK: true,
2701+
RequestURL: "http://127.0.0.1:8080/style.css",
2702+
SecurityDetails: map[string]string(nil),
2703+
ServerAddr: map[string]interface{}{"ip_address": "127.0.0.1", "port": 8080},
2704+
Size: map[string]int{"body": 35, "headers": 100},
2705+
Status: 200,
2706+
StatusText: "OK",
2707+
URL: "http://127.0.0.1:8080/style.css",
2708+
Text: "body { background-color: #f0f0f0; }",
2709+
},
2710+
{
2711+
AllHeaders: map[string]string{
2712+
"access-control-allow-credentials": "true",
2713+
"access-control-allow-origin": "*",
2714+
"content-length": "10",
2715+
"content-type": "text/plain; charset=utf-8",
2716+
"date": "Wed, 29 Jan 2025 09:00:00 GMT",
2717+
"x-content-type-options": "nosniff",
2718+
},
2719+
Body: "Not Found\n",
2720+
FrameURL: "http://127.0.0.1:8080/home",
2721+
AcceptLanguageHeader: "",
2722+
AcceptLanguageHeaders: []string{""},
2723+
Headers: map[string]string{
2724+
"Access-Control-Allow-Credentials": "true",
2725+
"Access-Control-Allow-Origin": "*",
2726+
"Content-Length": "10",
2727+
"Content-Type": "text/plain; charset=utf-8",
2728+
"Date": "Wed, 29 Jan 2025 09:00:00 GMT",
2729+
"X-Content-Type-Options": "nosniff",
2730+
},
2731+
HeadersArray: []map[string]string{
2732+
{"name": "Date", "value": "Wed, 29 Jan 2025 09:00:00 GMT"},
2733+
{"name": "Content-Type", "value": "text/plain; charset=utf-8"},
2734+
{"name": "Access-Control-Allow-Credentials", "value": "true"},
2735+
{"name": "X-Content-Type-Options", "value": "nosniff"},
2736+
{"name": "Access-Control-Allow-Origin", "value": "*"},
2737+
{"name": "Content-Length", "value": "10"},
2738+
},
2739+
JSON: "",
2740+
OK: false,
2741+
RequestURL: "http://127.0.0.1:8080/favicon.ico",
2742+
SecurityDetails: map[string]string(nil),
2743+
ServerAddr: map[string]interface{}{"ip_address": "127.0.0.1", "port": 8080},
2744+
Size: map[string]int{"body": 10, "headers": 229},
2745+
Status: 404,
2746+
StatusText: "Not Found",
2747+
URL: "http://127.0.0.1:8080/favicon.ico",
2748+
Text: "Not Found\n",
2749+
},
2750+
{
2751+
AllHeaders: map[string]string{
2752+
"content-length": "28",
2753+
"content-type": "text/plain; charset=utf-8",
2754+
"date": "Wed, 29 Jan 2025 09:00:00 GMT",
2755+
},
2756+
Body: "",
2757+
FrameURL: "http://127.0.0.1:8080/home",
2758+
AcceptLanguageHeader: "",
2759+
AcceptLanguageHeaders: []string{""},
2760+
Headers: map[string]string{
2761+
"Content-Length": "28",
2762+
"Content-Type": "text/plain; charset=utf-8",
2763+
"Date": "Wed, 29 Jan 2025 09:00:00 GMT",
2764+
},
2765+
HeadersArray: []map[string]string{
2766+
{"name": "Date", "value": "Wed, 29 Jan 2025 09:00:00 GMT"},
2767+
{"name": "Content-Type", "value": "text/plain; charset=utf-8"},
2768+
{"name": "Content-Length", "value": "28"},
2769+
},
2770+
JSON: "",
2771+
OK: true,
2772+
RequestURL: "http://127.0.0.1:8080/api",
2773+
SecurityDetails: map[string]string(nil),
2774+
ServerAddr: map[string]interface{}{"ip_address": "127.0.0.1", "port": 8080},
2775+
Size: map[string]int{"body": 0, "headers": 117},
2776+
Status: 200,
2777+
StatusText: "OK",
2778+
URL: "http://127.0.0.1:8080/api",
2779+
Text: "",
2780+
},
2781+
}
2782+
2783+
// Compare each request one by one for better test failure visibility
2784+
for _, resp := range responses {
2785+
i := -1
2786+
for j, e := range expected {
2787+
if resp.RequestURL == e.RequestURL {
2788+
i = j
2789+
break
2790+
}
2791+
}
2792+
assert.NotEqual(t, -1, i, "failed to find expected response with request URL %s", resp.RequestURL)
2793+
2794+
assert.Equal(t, expected[i].AllHeaders, resp.AllHeaders, "AllHeaders mismatch")
2795+
assert.Equal(t, expected[i].Body, resp.Body, "Body mismatch")
2796+
assert.Equal(t, expected[i].FrameURL, resp.FrameURL, "FrameUrl mismatch")
2797+
assert.Equal(t, expected[i].AcceptLanguageHeader, resp.AcceptLanguageHeader, "AcceptLanguageHeader mismatch")
2798+
assert.Equal(t, expected[i].AcceptLanguageHeaders, resp.AcceptLanguageHeaders, "AcceptLanguageHeaders mismatch")
2799+
assert.Equal(t, expected[i].Headers, resp.Headers, "Headers mismatch")
2800+
assert.Equal(t, expected[i].JSON, resp.JSON, "JSON mismatch")
2801+
assert.Equal(t, expected[i].OK, resp.OK, "OK mismatch")
2802+
assert.Equal(t, expected[i].RequestURL, resp.RequestURL, "RequestURL mismatch")
2803+
assert.Equal(t, expected[i].SecurityDetails, resp.SecurityDetails, "SecurityDetails mismatch")
2804+
assert.Equal(t, expected[i].ServerAddr, resp.ServerAddr, "ServerAddr mismatch")
2805+
assert.Equal(t, expected[i].Size, resp.Size, "Size mismatch")
2806+
assert.Equal(t, expected[i].Status, resp.Status, "Status mismatch")
2807+
assert.Equal(t, expected[i].StatusText, resp.StatusText, "StatusText mismatch")
2808+
assert.Equal(t, expected[i].URL, resp.URL, "URL mismatch")
2809+
assert.Equal(t, expected[i].Text, resp.Text, "Text mismatch")
2810+
2811+
// Compare HeadersArray elements one by one
2812+
assert.Equal(t, len(expected[i].HeadersArray), len(resp.HeadersArray), "HeadersArray length mismatch")
2813+
for _, expectedHeader := range expected[i].HeadersArray {
2814+
found := false
2815+
for _, actualHeader := range resp.HeadersArray {
2816+
if expectedHeader["name"] == actualHeader["name"] && expectedHeader["value"] == actualHeader["value"] {
2817+
found = true
2818+
break
2819+
}
2820+
}
2821+
assert.True(t, found, fmt.Sprintf("Expected header {name: %s, value: %s} not found in actual headers", expectedHeader["name"], expectedHeader["value"]))
2822+
}
2823+
}
2824+
}

0 commit comments

Comments
 (0)