From 9fce2249c7782d0a886b998868840815c143a8dc Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 28 May 2025 16:27:22 +0800 Subject: [PATCH 1/3] add location write and dynamic script cases --- public/dynamic_scripts/index.html | 9 +++++++ public/dynamic_scripts/script.js | 3 +++ public/location_write/index1.html | 5 ++++ public/location_write/index2.html | 5 ++++ public/location_write/index3.html | 5 ++++ public/location_write/index4.html | 4 +++ puppeteer/dynamic_scripts.js | 45 +++++++++++++++++++++++++++++++ puppeteer/location_write.js | 39 +++++++++++++++++++++++++++ runner/main.go | 2 ++ 9 files changed, 117 insertions(+) create mode 100644 public/dynamic_scripts/index.html create mode 100644 public/dynamic_scripts/script.js create mode 100644 public/location_write/index1.html create mode 100644 public/location_write/index2.html create mode 100644 public/location_write/index3.html create mode 100644 public/location_write/index4.html create mode 100644 puppeteer/dynamic_scripts.js create mode 100644 puppeteer/location_write.js diff --git a/public/dynamic_scripts/index.html b/public/dynamic_scripts/index.html new file mode 100644 index 0000000..34f905a --- /dev/null +++ b/public/dynamic_scripts/index.html @@ -0,0 +1,9 @@ + + + +
+ diff --git a/public/dynamic_scripts/script.js b/public/dynamic_scripts/script.js new file mode 100644 index 0000000..e4df4a2 --- /dev/null +++ b/public/dynamic_scripts/script.js @@ -0,0 +1,3 @@ +(() => { + document.getElementById('product').innerText = 'Keemun'; +})(); diff --git a/public/location_write/index1.html b/public/location_write/index1.html new file mode 100644 index 0000000..a1eaec5 --- /dev/null +++ b/public/location_write/index1.html @@ -0,0 +1,5 @@ + + +
1
+ + diff --git a/public/location_write/index2.html b/public/location_write/index2.html new file mode 100644 index 0000000..332bf8e --- /dev/null +++ b/public/location_write/index2.html @@ -0,0 +1,5 @@ + + +
2
+ + diff --git a/public/location_write/index3.html b/public/location_write/index3.html new file mode 100644 index 0000000..fbcd22e --- /dev/null +++ b/public/location_write/index3.html @@ -0,0 +1,5 @@ + + +
3
+ + diff --git a/public/location_write/index4.html b/public/location_write/index4.html new file mode 100644 index 0000000..357d334 --- /dev/null +++ b/public/location_write/index4.html @@ -0,0 +1,4 @@ + + +
4
+ diff --git a/puppeteer/dynamic_scripts.js b/puppeteer/dynamic_scripts.js new file mode 100644 index 0000000..28c105d --- /dev/null +++ b/puppeteer/dynamic_scripts.js @@ -0,0 +1,45 @@ +// Copyright 2023-2024 Lightpanda (Selecy SAS) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +'use scrict' + +import puppeteer from 'puppeteer-core'; + +const browserAddress = process.env.BROWSER_ADDRESS ? process.env.BROWSER_ADDRESS : 'ws://127.0.0.1:9222'; +const baseURL = process.env.URL ? process.env.URL : 'http://127.0.0.1:1234' + +// use browserWSEndpoint to pass the Lightpanda's CDP server address. +const browser = await puppeteer.connect({ + browserWSEndpoint: browserAddress, +}); + +// The rest of your script remains the same. +const context = await browser.createBrowserContext(); +const page = await context.newPage(); + +await page.goto(baseURL + '/dynamic_scripts/index.html');; + +await page.waitForFunction(() => { + const products = document.querySelector('#product'); + return products.textContent.length > 0; +}); + +const product = await page.evaluate(() => { return document.querySelector('#product').textContent; }); +if (product !== 'Keemun') { + console.log(res); + throw new Error("invalid product"); +} + +await page.close(); +await context.close(); +await browser.disconnect(); diff --git a/puppeteer/location_write.js b/puppeteer/location_write.js new file mode 100644 index 0000000..4f803ea --- /dev/null +++ b/puppeteer/location_write.js @@ -0,0 +1,39 @@ +// Copyright 2023-2024 Lightpanda (Selecy SAS) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +'use scrict' + +import puppeteer from 'puppeteer-core'; + +const browserAddress = process.env.BROWSER_ADDRESS ? process.env.BROWSER_ADDRESS : 'ws://127.0.0.1:9222'; +const baseURL = process.env.URL ? process.env.URL : 'http://127.0.0.1:1234' + +// use browserWSEndpoint to pass the Lightpanda's CDP server address. +const browser = await puppeteer.connect({ + browserWSEndpoint: browserAddress, +}); + +// The rest of your script remains the same. +const context = await browser.createBrowserContext(); +const page = await context.newPage(); + +await page.goto(baseURL + '/location_write/index1.html');; +await page.waitForFunction(() => { + const p = document.querySelector('#page'); + return p.textContent == '4'; + +}, {timeout: 2000}); + +await page.close(); +await context.close(); +await browser.disconnect(); diff --git a/runner/main.go b/runner/main.go index b006f4c..439e116 100644 --- a/runner/main.go +++ b/runner/main.go @@ -102,6 +102,8 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) error { {Bin: "node", Args: []string{"puppeteer/links.js"}, Env: []string{"URL=http://127.0.0.1:1234/campfire-commerce/"}}, {Bin: "node", Args: []string{"puppeteer/click.js"}}, {Bin: "node", Args: []string{"puppeteer/wait_for_network.js"}}, + {Bin: "node", Args: []string{"puppeteer/dynamic_scripts.js"}}, + {Bin: "node", Args: []string{"puppeteer/location_write.js"}}, {Bin: "node", Args: []string{"playwright/connect.js"}}, {Bin: "node", Args: []string{"playwright/cdp.js"}, Env: []string{"RUNS=2"}}, {Bin: "node", Args: []string{"playwright/click.js"}}, From c1a4b8e27cde925a206b9d041df620a3dc39c30c Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Wed, 28 May 2025 17:46:57 +0800 Subject: [PATCH 2/3] add timeout to dynamic_script waitForFunction --- puppeteer/dynamic_scripts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/puppeteer/dynamic_scripts.js b/puppeteer/dynamic_scripts.js index 28c105d..3c5b891 100644 --- a/puppeteer/dynamic_scripts.js +++ b/puppeteer/dynamic_scripts.js @@ -32,7 +32,7 @@ await page.goto(baseURL + '/dynamic_scripts/index.html');; await page.waitForFunction(() => { const products = document.querySelector('#product'); return products.textContent.length > 0; -}); +}, {timeout: 1000}); const product = await page.evaluate(() => { return document.querySelector('#product').textContent; }); if (product !== 'Keemun') { From 2c0055c59428e3ba096f07e03cd5cfb7e895be10 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Thu, 29 May 2025 13:45:06 +0800 Subject: [PATCH 3/3] Add case for form submission (via the JS submit() function call) This tests the code in https://github.com/lightpanda-io/browser/pull/723 --- public/form/get.html | 10 ++++++ public/form/post.html | 10 ++++++ puppeteer/form.js | 72 +++++++++++++++++++++++++++++++++++++++++++ runner/main.go | 31 +++++++++++++++++-- 4 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 public/form/get.html create mode 100644 public/form/post.html create mode 100644 puppeteer/form.js diff --git a/public/form/get.html b/public/form/get.html new file mode 100644 index 0000000..c51dcb7 --- /dev/null +++ b/public/form/get.html @@ -0,0 +1,10 @@ + + +
+ + + + +
+ + diff --git a/public/form/post.html b/public/form/post.html new file mode 100644 index 0000000..75abf2b --- /dev/null +++ b/public/form/post.html @@ -0,0 +1,10 @@ + + +
+ + + + +
+ + diff --git a/puppeteer/form.js b/puppeteer/form.js new file mode 100644 index 0000000..0404982 --- /dev/null +++ b/puppeteer/form.js @@ -0,0 +1,72 @@ +// Copyright 2023-2024 Lightpanda (Selecy SAS) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +'use scrict' + +import puppeteer from 'puppeteer-core'; + +const browserAddress = process.env.BROWSER_ADDRESS ? process.env.BROWSER_ADDRESS : 'ws://127.0.0.1:9222'; +const baseURL = process.env.URL ? process.env.URL : 'http://127.0.0.1:1234' + +// use browserWSEndpoint to pass the Lightpanda's CDP server address. +const browser = await puppeteer.connect({ + browserWSEndpoint: browserAddress, +}); + +// The rest of your script remains the same. +const context = await browser.createBrowserContext(); +const page = await context.newPage(); + +await testForm(page, '/form/get.html', { + method: 'GET', + body: '', + query: 'h1=v1&h3=v3&favorite+drink=tea', +}); + +await testForm(page, '/form/post.html', { + method: 'POST', + body: 'h1=v1&h3=v3&favorite+drink=tea', + query: '', +}); + + +await context.close(); +await browser.disconnect(); + + +async function testForm(page, url, expected) { + await page.goto(baseURL + url);; + + await page.waitForFunction(() => { + const p = document.querySelector('#method'); + return p.textContent != ''; + }, {timeout: 4000}); + + const method = await page.evaluate(() => { return document.querySelector('#method').textContent; }); + if (method !== expected.method) { + console.log(method); + throw new Error("invalid method"); + } + + const body = await page.evaluate(() => { return document.querySelector('#body').textContent; }); + if (body !== expected.body) { + console.log(body); + throw new Error("invalid body"); + } + + const query = await page.evaluate(() => { return document.querySelector('#query').textContent; }); + if (query !== expected.query) { + console.log(query); + throw new Error("invalid query"); + } +} diff --git a/runner/main.go b/runner/main.go index 439e116..ed1f725 100644 --- a/runner/main.go +++ b/runner/main.go @@ -104,6 +104,7 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) error { {Bin: "node", Args: []string{"puppeteer/wait_for_network.js"}}, {Bin: "node", Args: []string{"puppeteer/dynamic_scripts.js"}}, {Bin: "node", Args: []string{"puppeteer/location_write.js"}}, + {Bin: "node", Args: []string{"puppeteer/form.js"}}, {Bin: "node", Args: []string{"playwright/connect.js"}}, {Bin: "node", Args: []string{"playwright/cdp.js"}, Env: []string{"RUNS=2"}}, {Bin: "node", Args: []string{"playwright/click.js"}}, @@ -163,11 +164,11 @@ func runtest(ctx context.Context, t Test) error { // run the local http server func runhttp(ctx context.Context, addr, dir string) error { - handler := http.FileServer(http.Dir(dir)) + fs := http.FileServer(http.Dir(dir)) srv := &http.Server{ Addr: addr, - Handler: handler, + Handler: Handler{fs: fs}, BaseContext: func(net.Listener) context.Context { return ctx }, @@ -203,3 +204,29 @@ func env(key, dflt string) string { return val } + +type Handler struct { + fs http.Handler +} + +func (h Handler) ServeHTTP(res http.ResponseWriter, req *http.Request) { + switch req.URL.Path { + case "/form/submit": + defer req.Body.Close() + body, err := io.ReadAll(req.Body) + if err != nil { + panic(err) + } + + res.Header().Add("Content-Type", "text/html") + res.Write([]byte("
  • ")) + res.Write([]byte(req.Method)) + res.Write([]byte("
  • ")) + res.Write(body) + res.Write([]byte("
  • ")) + res.Write([]byte(req.URL.RawQuery)) + res.Write([]byte("
")) + default: + h.fs.ServeHTTP(res, req) + } +}