Skip to content

Commit 1803876

Browse files
committed
add request interception test for chromedp
1 parent b1108f5 commit 1803876

File tree

2 files changed

+174
-0
lines changed

2 files changed

+174
-0
lines changed

chromedp/ri/main.go

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
// Copyright 2023-2025 Lightpanda (Selecy SAS)
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
package main
15+
16+
import (
17+
"context"
18+
"errors"
19+
"flag"
20+
"fmt"
21+
"io"
22+
"log"
23+
"log/slog"
24+
"os"
25+
"strings"
26+
27+
"github.com/chromedp/cdproto/cdp"
28+
"github.com/chromedp/cdproto/fetch"
29+
"github.com/chromedp/chromedp"
30+
)
31+
32+
const (
33+
exitOK = 0
34+
exitFail = 1
35+
)
36+
37+
// main starts interruptable context and runs the program.
38+
func main() {
39+
ctx, cancel := context.WithCancel(context.Background())
40+
defer cancel()
41+
42+
err := run(ctx, os.Args, os.Stdout, os.Stderr)
43+
if err != nil {
44+
fmt.Fprintln(os.Stderr, err.Error())
45+
os.Exit(exitFail)
46+
}
47+
48+
os.Exit(exitOK)
49+
}
50+
51+
const (
52+
CdpWSDefault = "ws://127.0.0.1:9222"
53+
)
54+
55+
func run(ctx context.Context, args []string, stdout, stderr io.Writer) error {
56+
// declare runtime flag parameters.
57+
flags := flag.NewFlagSet(args[0], flag.ExitOnError)
58+
flags.SetOutput(stderr)
59+
60+
var (
61+
verbose = flags.Bool("verbose", false, "enable debug log level")
62+
cdpws = flags.String("cdp", env("CDPCLI_WS", CdpWSDefault), "cdp ws to connect")
63+
)
64+
65+
// usage func declaration.
66+
exec := args[0]
67+
flags.Usage = func() {
68+
fmt.Fprintf(stderr, "usage: %s <url>]\n", exec)
69+
fmt.Fprintf(stderr, "chromedp fetch an url and intercept requests.\n")
70+
fmt.Fprintf(stderr, "\nCommand line options:\n")
71+
flags.PrintDefaults()
72+
fmt.Fprintf(stderr, "\nEnvironment vars:\n")
73+
fmt.Fprintf(stderr, "\tCDPCLI_WS\tdefault %s\n", CdpWSDefault)
74+
}
75+
if err := flags.Parse(args[1:]); err != nil {
76+
return err
77+
}
78+
79+
if *verbose {
80+
slog.SetLogLoggerLevel(slog.LevelDebug)
81+
}
82+
83+
args = flags.Args()
84+
if len(args) != 1 {
85+
return errors.New("url is required")
86+
}
87+
url := args[0]
88+
89+
ctx, cancel := chromedp.NewRemoteAllocator(ctx,
90+
*cdpws, chromedp.NoModifyURL,
91+
)
92+
defer cancel()
93+
94+
// build context options
95+
var opts []chromedp.ContextOption
96+
if *verbose {
97+
opts = append(opts, chromedp.WithDebugf(log.Printf))
98+
}
99+
100+
ctx, cancel = chromedp.NewContext(ctx, opts...)
101+
defer cancel()
102+
103+
// ensure the first tab is created
104+
if err := chromedp.Run(ctx); err != nil {
105+
return fmt.Errorf("new tab: %w", err)
106+
}
107+
108+
chromedp.ListenTarget(ctx, func(ev any) {
109+
switch ev := ev.(type) {
110+
case *fetch.EventRequestPaused:
111+
go func() {
112+
url := ev.Request.URL
113+
fmt.Println(ev.RequestID, url)
114+
115+
// alter the response with a new body
116+
if strings.HasSuffix(url, "/reviews.json") {
117+
_ = chromedp.Run(ctx,
118+
fetch.FulfillRequest(ev.RequestID, 200).WithBody(`["alter review"]`),
119+
)
120+
return
121+
}
122+
123+
// by default let the request running.
124+
_ = chromedp.Run(ctx, fetch.ContinueRequest(ev.RequestID))
125+
}()
126+
}
127+
})
128+
129+
if err := chromedp.Run(ctx,
130+
fetch.Enable().WithPatterns(nil),
131+
); err != nil {
132+
log.Fatal(err)
133+
}
134+
135+
err := chromedp.Run(ctx, chromedp.Navigate(url))
136+
if err != nil {
137+
return fmt.Errorf("navigate %s: %w", url, err)
138+
}
139+
140+
var a []*cdp.Node
141+
if err := chromedp.Run(ctx, chromedp.Nodes(`#product-reviews h4`, &a, chromedp.Populate(1, false))); err != nil {
142+
return fmt.Errorf("get reviews: %w", err)
143+
}
144+
145+
reviews := make([]string, 0, len(a))
146+
for _, aa := range a {
147+
if aa.ChildNodeCount != 1 {
148+
return errors.New("invalid childnodes")
149+
}
150+
reviews = append(reviews, aa.Children[0].NodeValue)
151+
}
152+
153+
fmt.Fprintf(os.Stdout, "%v", reviews)
154+
155+
if len(reviews) != 1 {
156+
return errors.New("invalid reviews number")
157+
}
158+
if reviews[0] != "alter review" {
159+
return errors.New("invalid reviews title")
160+
}
161+
162+
return nil
163+
}
164+
165+
// env returns the env value corresponding to the key or the default string.
166+
func env(key, dflt string) string {
167+
val, ok := os.LookupEnv(key)
168+
if !ok {
169+
return dflt
170+
}
171+
172+
return val
173+
}

runner/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ func run(ctx context.Context, args []string, stdout, stderr io.Writer) error {
116116
{Bin: "go", Args: []string{"run", "fetch/main.go", "http://127.0.0.1:1234/campfire-commerce/"}, Dir: "chromedp"},
117117
{Bin: "go", Args: []string{"run", "links/main.go", "http://127.0.0.1:1234/campfire-commerce/"}, Dir: "chromedp"},
118118
{Bin: "go", Args: []string{"run", "click/main.go", "http://127.0.0.1:1234/"}, Dir: "chromedp"},
119+
{Bin: "go", Args: []string{"run", "ri/main.go", "http://127.0.0.1:1234/campfire-commerce/"}, Dir: "chromedp"},
119120
} {
120121
if *verbose {
121122
t.Stderr = stderr

0 commit comments

Comments
 (0)