|
9 | 9 | "io"
|
10 | 10 | "net"
|
11 | 11 | "net/http"
|
| 12 | + "net/http/httptest" |
12 | 13 | "runtime"
|
13 | 14 | "slices"
|
14 | 15 | "strconv"
|
@@ -3093,3 +3094,227 @@ func TestPageWaitForResponse(t *testing.T) {
|
3093 | 3094 | require.ErrorContains(t, err, "waiting for response")
|
3094 | 3095 | })
|
3095 | 3096 | }
|
| 3097 | + |
| 3098 | +// TestClickInNestedFramesCORS tests clicking on buttons within nested frames |
| 3099 | +// which are from different origins. At the end of the test the counter in |
| 3100 | +// each frame should be "1". |
| 3101 | +func TestClickInNestedFramesCORS(t *testing.T) { |
| 3102 | + t.Parallel() |
| 3103 | + |
| 3104 | + // Origin C: innermost frame with counter button and nested same-origin iframe |
| 3105 | + originCHTML := `<!DOCTYPE html> |
| 3106 | + <html> |
| 3107 | + <head></head> |
| 3108 | + <body> |
| 3109 | + <p>Counter: <span id="count">0</span></p> |
| 3110 | + <button id="increment">Increment Counter</button> |
| 3111 | + <iframe id="frameD" src="/innerC" width="300" height="100"></iframe> |
| 3112 | + <script> |
| 3113 | + let count = 0; |
| 3114 | + document.getElementById('increment').addEventListener('click', () => { |
| 3115 | + count++; |
| 3116 | + document.getElementById('count').textContent = count; |
| 3117 | + }); |
| 3118 | + </script> |
| 3119 | + </body> |
| 3120 | + </html>` |
| 3121 | + |
| 3122 | + // Nested same-origin frame content served at /innerC on origin C |
| 3123 | + innerCHTML := `<!DOCTYPE html> |
| 3124 | + <html> |
| 3125 | + <head></head> |
| 3126 | + <body> |
| 3127 | + <p>Counter D: <span id="countD">0</span></p> |
| 3128 | + <button id="incrementD">Increment Counter D</button> |
| 3129 | + <script> |
| 3130 | + let countD = 0; |
| 3131 | + document.getElementById('incrementD').addEventListener('click', () => { |
| 3132 | + countD++; |
| 3133 | + document.getElementById('countD').textContent = countD; |
| 3134 | + }); |
| 3135 | + </script> |
| 3136 | + </body> |
| 3137 | + </html>` |
| 3138 | + |
| 3139 | + // Nested same-origin frame content served at /innerA on origin A |
| 3140 | + innerAHTML := `<!DOCTYPE html> |
| 3141 | + <html> |
| 3142 | + <head></head> |
| 3143 | + <body> |
| 3144 | + <p>Counter A2: <span id="countA2">0</span></p> |
| 3145 | + <button id="incrementA2">Increment Counter A2</button> |
| 3146 | + <script> |
| 3147 | + let countA2 = 0; |
| 3148 | + document.getElementById('incrementA2').addEventListener('click', () => { |
| 3149 | + countA2++; |
| 3150 | + document.getElementById('countA2').textContent = countA2; |
| 3151 | + }); |
| 3152 | + </script> |
| 3153 | + </body> |
| 3154 | + </html>` |
| 3155 | + |
| 3156 | + // Server for origin C |
| 3157 | + muxC := http.NewServeMux() |
| 3158 | + muxC.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
| 3159 | + w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 3160 | + _, err := w.Write([]byte(originCHTML)) |
| 3161 | + require.NoError(t, err) |
| 3162 | + }) |
| 3163 | + muxC.HandleFunc("/innerC", func(w http.ResponseWriter, r *http.Request) { |
| 3164 | + w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 3165 | + _, err := w.Write([]byte(innerCHTML)) |
| 3166 | + require.NoError(t, err) |
| 3167 | + }) |
| 3168 | + srvC := httptest.NewServer(muxC) |
| 3169 | + defer srvC.Close() |
| 3170 | + |
| 3171 | + // Origin B: intermediate frame embedding origin C + own counter (with dynamic C URL) |
| 3172 | + originBHTML := fmt.Sprintf(`<!DOCTYPE html> |
| 3173 | + <html> |
| 3174 | + <head></head> |
| 3175 | + <body> |
| 3176 | + <p>Counter B: <span id="countB">0</span></p> |
| 3177 | + <button id="incrementB">Increment Counter B</button> |
| 3178 | + <iframe id="frameC" src="%s" width="400" height="200"></iframe> |
| 3179 | + <script> |
| 3180 | + let countB = 0; |
| 3181 | + document.getElementById('incrementB').addEventListener('click', () => { |
| 3182 | + countB++; |
| 3183 | + document.getElementById('countB').textContent = countB; |
| 3184 | + }); |
| 3185 | + </script> |
| 3186 | + </body> |
| 3187 | + </html>`, srvC.URL) |
| 3188 | + |
| 3189 | + // Server for origin B |
| 3190 | + muxB := http.NewServeMux() |
| 3191 | + muxB.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
| 3192 | + w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 3193 | + _, err := w.Write([]byte(originBHTML)) |
| 3194 | + require.NoError(t, err) |
| 3195 | + }) |
| 3196 | + srvB := httptest.NewServer(muxB) |
| 3197 | + defer srvB.Close() |
| 3198 | + |
| 3199 | + // Origin A: main page embedding origin B and same-origin frame A (with dynamic B URL) |
| 3200 | + originAHTML := fmt.Sprintf(`<!DOCTYPE html> |
| 3201 | + <html> |
| 3202 | + <head></head> |
| 3203 | + <body> |
| 3204 | + <p>Counter A: <span id="countA">0</span></p> |
| 3205 | + <button id="incrementA">Increment Counter A</button> |
| 3206 | + <iframe id="frameA" src="/innerA" width="300" height="150" style="display: block; margin: 10px auto;"></iframe> |
| 3207 | + <iframe id="frameB" src="%s" width="450" height="300" style="display: block; margin: 10px auto;"></iframe> |
| 3208 | + <script> |
| 3209 | + let countA = 0; |
| 3210 | + document.getElementById('incrementA').addEventListener('click', () => { |
| 3211 | + countA++; |
| 3212 | + document.getElementById('countA').textContent = countA; |
| 3213 | + }); |
| 3214 | + </script> |
| 3215 | + </body> |
| 3216 | + </html>`, srvB.URL) |
| 3217 | + |
| 3218 | + // Server for origin A |
| 3219 | + muxA := http.NewServeMux() |
| 3220 | + muxA.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { |
| 3221 | + w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 3222 | + _, err := w.Write([]byte(originAHTML)) |
| 3223 | + require.NoError(t, err) |
| 3224 | + }) |
| 3225 | + muxA.HandleFunc("/innerA", func(w http.ResponseWriter, r *http.Request) { |
| 3226 | + w.Header().Set("Content-Type", "text/html; charset=utf-8") |
| 3227 | + _, err := w.Write([]byte(innerAHTML)) |
| 3228 | + require.NoError(t, err) |
| 3229 | + }) |
| 3230 | + srvA := httptest.NewServer(muxA) |
| 3231 | + defer srvA.Close() |
| 3232 | + |
| 3233 | + // Use srvA.URL as the entry point in the rest of the test (navigate, click, etc.). |
| 3234 | + page := newTestBrowser(t).NewPage(nil) |
| 3235 | + |
| 3236 | + // Navigate to the page that srvA is serving. |
| 3237 | + opts := &common.FrameGotoOptions{ |
| 3238 | + Timeout: common.DefaultTimeout, |
| 3239 | + } |
| 3240 | + _, err := page.Goto(srvA.URL, opts) |
| 3241 | + require.NoError(t, err) |
| 3242 | + |
| 3243 | + var ( |
| 3244 | + clickOpts = common.NewFrameClickOptions(page.Timeout()) |
| 3245 | + expectedCount = "1" |
| 3246 | + ) |
| 3247 | + |
| 3248 | + // First click on the main frame. |
| 3249 | + err = page.Locator("#incrementA", nil).Click(clickOpts) |
| 3250 | + require.NoError(t, err) |
| 3251 | + |
| 3252 | + countA, ok, err := page.Locator("#countA", nil).TextContent(nil) |
| 3253 | + require.NoError(t, err) |
| 3254 | + assert.True(t, ok) |
| 3255 | + assert.Equal(t, expectedCount, countA) |
| 3256 | + |
| 3257 | + // Now get the first nested frame. |
| 3258 | + frameA, err := page.Query("#frameA") |
| 3259 | + require.NoError(t, err) |
| 3260 | + |
| 3261 | + frameAContent, err := frameA.ContentFrame() |
| 3262 | + require.NoError(t, err) |
| 3263 | + |
| 3264 | + // Click on the second nested frame. |
| 3265 | + err = frameAContent.Locator("#incrementA2", nil).Click(clickOpts) |
| 3266 | + require.NoError(t, err) |
| 3267 | + |
| 3268 | + countA2, ok, err := frameAContent.Locator("#countA2", nil).TextContent(nil) |
| 3269 | + require.NoError(t, err) |
| 3270 | + assert.True(t, ok) |
| 3271 | + assert.Equal(t, expectedCount, countA2) |
| 3272 | + |
| 3273 | + // Now get the second nested frame. |
| 3274 | + frameB, err := page.Query("#frameB") |
| 3275 | + require.NoError(t, err) |
| 3276 | + |
| 3277 | + frameBContent, err := frameB.ContentFrame() |
| 3278 | + require.NoError(t, err) |
| 3279 | + |
| 3280 | + // Click on the third nested frame. |
| 3281 | + err = frameBContent.Locator("#incrementB", nil).Click(clickOpts) |
| 3282 | + require.NoError(t, err) |
| 3283 | + |
| 3284 | + countB, ok, err := frameBContent.Locator("#countB", nil).TextContent(nil) |
| 3285 | + require.NoError(t, err) |
| 3286 | + assert.True(t, ok) |
| 3287 | + assert.Equal(t, expectedCount, countB) |
| 3288 | + |
| 3289 | + // Now get the third nested frame. |
| 3290 | + frameC, err := frameBContent.Query("#frameC", false) |
| 3291 | + require.NoError(t, err) |
| 3292 | + |
| 3293 | + frameCContent, err := frameC.ContentFrame() |
| 3294 | + require.NoError(t, err) |
| 3295 | + |
| 3296 | + // Click on the fourth nested frame. |
| 3297 | + err = frameCContent.Locator("#increment", nil).Click(clickOpts) |
| 3298 | + require.NoError(t, err) |
| 3299 | + |
| 3300 | + count, ok, err := frameCContent.Locator("#count", nil).TextContent(nil) |
| 3301 | + require.NoError(t, err) |
| 3302 | + assert.True(t, ok) |
| 3303 | + assert.Equal(t, expectedCount, count) |
| 3304 | + |
| 3305 | + // Now get the fourth nested frame. |
| 3306 | + frameD, err := frameCContent.Query("#frameD", false) |
| 3307 | + require.NoError(t, err) |
| 3308 | + |
| 3309 | + frameDContent, err := frameD.ContentFrame() |
| 3310 | + require.NoError(t, err) |
| 3311 | + |
| 3312 | + // Click on the fifth nested frame. |
| 3313 | + err = frameDContent.Locator("#incrementD", nil).Click(clickOpts) |
| 3314 | + require.NoError(t, err) |
| 3315 | + |
| 3316 | + countD, ok, err := frameDContent.Locator("#countD", nil).TextContent(nil) |
| 3317 | + require.NoError(t, err) |
| 3318 | + assert.True(t, ok) |
| 3319 | + assert.Equal(t, expectedCount, countD) |
| 3320 | +} |
0 commit comments