Skip to content

Commit c5d6377

Browse files
authored
feat: added support for CDPSession (#128)
1 parent de638a4 commit c5d6377

10 files changed

+183
-1
lines changed

Diff for: browser.go

+13
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ func (b *browserImpl) NewPage(options ...BrowserNewContextOptions) (Page, error)
5252
return page, nil
5353
}
5454

55+
func (b *browserImpl) NewBrowserCDPSession() (CDPSession, error) {
56+
channel, err := b.channel.Send("crNewBrowserCDPSession", map[string]interface{}{
57+
"sdkLanguage": "javascript",
58+
})
59+
if err != nil {
60+
return nil, fmt.Errorf("could not send message: %w", err)
61+
}
62+
63+
cdpSession := fromChannel(channel).(*cdpSessionImpl)
64+
65+
return cdpSession, nil
66+
}
67+
5568
func (b *browserImpl) Contexts() []BrowserContext {
5669
b.Lock()
5770
defer b.Unlock()

Diff for: browser_context.go

+14
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,20 @@ func (b *browserContextImpl) Browser() Browser {
4646
return b.browser
4747
}
4848

49+
func (b *browserContextImpl) NewCDPSession(page Page) (CDPSession, error) {
50+
channel, err := b.channel.Send("crNewCDPSession", map[string]interface{}{
51+
"sdkLanguage": "javascript",
52+
"page": page.(*pageImpl).channel,
53+
})
54+
if err != nil {
55+
return nil, fmt.Errorf("could not send message: %w", err)
56+
}
57+
58+
cdpSession := fromChannel(channel).(*cdpSessionImpl)
59+
60+
return cdpSession, nil
61+
}
62+
4963
func (b *browserContextImpl) NewPage(options ...BrowserNewPageOptions) (Page, error) {
5064
if b.ownedPage != nil {
5165
return nil, errors.New("Please use browser.NewContext()")

Diff for: cdp_session.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package playwright
2+
3+
type cdpSessionImpl struct {
4+
channelOwner
5+
}
6+
7+
func (c *cdpSessionImpl) Detach() error {
8+
_, err := c.channel.Send("detach")
9+
return err
10+
}
11+
12+
func (c *cdpSessionImpl) Send(method string, params map[string]interface{}) (interface{}, error) {
13+
result, err := c.channel.Send("send", map[string]interface{}{
14+
"method": method,
15+
"params": params,
16+
})
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
return result, err
22+
}
23+
24+
func (c *cdpSessionImpl) onEvent(params map[string]interface{}) {
25+
c.Emit(params["method"].(string), params["params"])
26+
}
27+
28+
func newCDPSession(parent *channelOwner, objectType string, guid string, initializer map[string]interface{}) *cdpSessionImpl {
29+
bt := &cdpSessionImpl{}
30+
31+
bt.createChannelOwner(bt, parent, objectType, guid, initializer)
32+
33+
bt.channel.On("event", func(params map[string]interface{}) {
34+
bt.onEvent(params)
35+
})
36+
37+
return bt
38+
}

Diff for: generated_interfaces.go

+18
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,27 @@ type Browser interface {
2424
// testing frameworks should explicitly create Browser.newContext() followed by the
2525
// BrowserContext.newPage() to control their exact life times.
2626
NewPage(options ...BrowserNewContextOptions) (Page, error)
27+
NewBrowserCDPSession() (CDPSession, error)
2728
// Returns the browser version.
2829
Version() string
2930
}
3031

32+
// The `CDPSession` instances are used to talk raw Chrome Devtools Protocol:
33+
// - protocol methods can be called with `session.send` method.
34+
// - protocol events can be subscribed to with `session.on` method.
35+
// Useful links:
36+
// - Documentation on DevTools Protocol can be found here:
37+
// [DevTools Protocol Viewer](https://chromedevtools.github.io/devtools-protocol/).
38+
// - Getting Started with DevTools Protocol:
39+
// https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md
40+
type CDPSession interface {
41+
EventEmitter
42+
// Detaches the CDPSession from the target. Once detached, the CDPSession object won't emit any events and can't be used to
43+
// send messages.
44+
Detach() error
45+
Send(method string, params map[string]interface{}) (interface{}, error)
46+
}
47+
3148
// BrowserContexts provide a way to operate multiple independent browser sessions.
3249
// If a page opens another page, e.g. with a `window.open` call, the popup will belong to the parent page's browser
3350
// context.
@@ -79,6 +96,7 @@ type BrowserContext interface {
7996
// Grants specified permissions to the browser context. Only grants corresponding permissions to the given origin if
8097
// specified.
8198
GrantPermissions(permissions []string, options ...BrowserContextGrantPermissionsOptions) error
99+
NewCDPSession(page Page) (CDPSession, error)
82100
// Creates a new page in the browser context.
83101
NewPage(options ...BrowserNewPageOptions) (Page, error)
84102
// Returns all open pages in the context.

Diff for: objectFactory.go

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ func createObjectFactory(parent *channelOwner, objectType string, guid string, i
1212
return newBrowserType(parent, objectType, guid, initializer)
1313
case "BrowserContext":
1414
return newBrowserContext(parent, objectType, guid, initializer)
15+
case "CDPSession":
16+
return newCDPSession(parent, objectType, guid, initializer)
1517
case "ConsoleMessage":
1618
return newConsoleMessage(parent, objectType, guid, initializer)
1719
case "Dialog":

Diff for: scripts/data/interfaces.json

+21
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,28 @@
2929
"options ...BrowserNewContextOptions",
3030
"(Page, error)"
3131
],
32+
"NewBrowserCDPSession": [
33+
null,
34+
"(CDPSession, error)"
35+
],
3236
"Version": [
3337
null,
3438
"string"
3539
]
3640
},
41+
"CDPSession": {
42+
"extends": [
43+
"EventEmitter"
44+
],
45+
"Detach": [
46+
null,
47+
"error"
48+
],
49+
"Send": [
50+
"method string, params map[string]interface{}",
51+
"(interface{}, error)"
52+
]
53+
},
3754
"BrowserContext": {
3855
"extends": [
3956
"EventEmitter"
@@ -82,6 +99,10 @@
8299
"permissions []string, options ...BrowserContextGrantPermissionsOptions",
83100
"error"
84101
],
102+
"NewCDPSession": [
103+
"page Page",
104+
"(CDPSession, error)"
105+
],
85106
"NewPage": [
86107
"options ...BrowserNewPageOptions",
87108
"(Page, error)"

Diff for: scripts/validate-interfaces.js

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ const api = getAPIDocs()
55

66
const IGNORE_CLASSES = [
77
"Selectors",
8-
"CDPSession",
98
"Logger",
109
"BrowserServer",
1110
"Accessibility",

Diff for: tests/browser_context_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,18 @@ func TestBrowserContextSetExtraHTTPHeaders(t *testing.T) {
7777
<-intercepted
7878
}
7979

80+
func TestBrowserContextNewCDPSession(t *testing.T) {
81+
BeforeEach(t)
82+
defer AfterEach(t)
83+
cdpSession, err := page.Context().NewCDPSession(page)
84+
if isChromium {
85+
require.NoError(t, err)
86+
require.NoError(t, cdpSession.Detach())
87+
} else {
88+
require.Error(t, err)
89+
}
90+
}
91+
8092
func TestBrowserContextSetGeolocation(t *testing.T) {
8193
BeforeEach(t)
8294
defer AfterEach(t)

Diff for: tests/browser_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ func TestBrowserShouldErrorUponSecondCreateNewPage(t *testing.T) {
100100
require.NoError(t, page.Close())
101101
}
102102

103+
func TestNewBrowserCDPSession(t *testing.T) {
104+
BeforeEach(t)
105+
defer AfterEach(t)
106+
cdpSession, err := browser.NewBrowserCDPSession()
107+
if isChromium {
108+
require.NoError(t, err)
109+
require.NoError(t, cdpSession.Detach())
110+
} else {
111+
require.Error(t, err)
112+
}
113+
}
114+
103115
func TestBrowserClose(t *testing.T) {
104116
pw, err := playwright.Run()
105117
require.NoError(t, err)

Diff for: tests/cdp_session_test.go

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package playwright_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
)
8+
9+
func TestCDPSessionSend(t *testing.T) {
10+
BeforeEach(t)
11+
defer AfterEach(t)
12+
cdpSession, err := browser.NewBrowserCDPSession()
13+
if isChromium {
14+
require.NoError(t, err)
15+
result, err := cdpSession.Send("Target.getTargets", nil)
16+
require.NoError(t, err)
17+
targetInfos := result.(map[string]interface{})["targetInfos"].([]interface{})
18+
require.Equal(t, 1, len(targetInfos))
19+
} else {
20+
require.Error(t, err)
21+
}
22+
}
23+
24+
func TestCDPSessionOn(t *testing.T) {
25+
BeforeEach(t)
26+
defer AfterEach(t)
27+
cdpSession, err := page.Context().NewCDPSession(page)
28+
if isChromium {
29+
require.NoError(t, err)
30+
_, err = cdpSession.Send("Console.enable", nil)
31+
require.NoError(t, err)
32+
cdpSession.On("Console.messageAdded", func(params map[string]interface{}) {
33+
require.NotNil(t, params)
34+
})
35+
_, err = page.Evaluate(`console.log("hello")`)
36+
require.NoError(t, err)
37+
require.NoError(t, cdpSession.Detach())
38+
} else {
39+
require.Error(t, err)
40+
}
41+
}
42+
43+
func TestCDPSessionDetach(t *testing.T) {
44+
BeforeEach(t)
45+
defer AfterEach(t)
46+
cdpSession, err := browser.NewBrowserCDPSession()
47+
if isChromium {
48+
require.NoError(t, err)
49+
require.NoError(t, cdpSession.Detach())
50+
} else {
51+
require.Error(t, err)
52+
}
53+
}

0 commit comments

Comments
 (0)