Skip to content

Commit eeb9e8f

Browse files
authored
Merge pull request #12 from browserbase/anirudh/add-playwright-e2e
Port over e2e tests from core
2 parents 3f523dc + 6b95755 commit eeb9e8f

18 files changed

+1958
-30
lines changed

Diff for: .gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@ dist
1515
codegen.log
1616
Brewfile.lock.json
1717
screenshot.png
18-
openapi.v1.yaml
18+
**/.DS_Store

Diff for: examples/e2e/test_playwright.py

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import os
2+
from typing import Generator
3+
4+
import pytest
5+
from dotenv import load_dotenv
6+
from playwright.sync_api import Playwright, sync_playwright
7+
8+
from browserbase import Browserbase
9+
10+
from .. import (
11+
BROWSERBASE_API_KEY,
12+
playwright_basic,
13+
playwright_proxy,
14+
playwright_upload,
15+
playwright_captcha,
16+
playwright_contexts,
17+
playwright_downloads,
18+
)
19+
20+
bb = Browserbase(api_key=BROWSERBASE_API_KEY)
21+
load_dotenv()
22+
23+
CI = os.getenv("CI", "false").lower() == "true"
24+
25+
26+
@pytest.fixture(scope="session")
27+
def playwright() -> Generator[Playwright, None, None]:
28+
with sync_playwright() as p:
29+
yield p
30+
31+
32+
def test_playwright_basic(playwright: Playwright) -> None:
33+
playwright_basic.run(playwright)
34+
35+
36+
@pytest.mark.skipif(True, reason="Flaky and fails often")
37+
def test_playwright_captcha(playwright: Playwright) -> None:
38+
playwright_captcha.run(playwright)
39+
40+
41+
def test_playwright_contexts(playwright: Playwright) -> None:
42+
playwright_contexts.run(playwright)
43+
44+
45+
def test_playwright_downloads(playwright: Playwright) -> None:
46+
playwright_downloads.run(playwright)
47+
48+
49+
def test_playwright_proxy_enable_via_create_session(playwright: Playwright) -> None:
50+
playwright_proxy.run_enable_via_create_session(playwright)
51+
52+
53+
def test_playwright_proxy_enable_via_querystring(playwright: Playwright) -> None:
54+
playwright_proxy.run_enable_via_querystring_with_created_session(playwright)
55+
56+
57+
@pytest.mark.skipif(CI, reason="Flaky and fails on CI")
58+
def test_playwright_proxy_geolocation_country(playwright: Playwright) -> None:
59+
playwright_proxy.run_geolocation_country(playwright)
60+
61+
62+
@pytest.mark.skipif(CI, reason="Flaky and fails on CI")
63+
def test_playwright_proxy_geolocation_state(playwright: Playwright) -> None:
64+
playwright_proxy.run_geolocation_state(playwright)
65+
66+
67+
@pytest.mark.skipif(CI, reason="Flaky and fails on CI")
68+
def test_playwright_proxy_geolocation_american_city(playwright: Playwright) -> None:
69+
playwright_proxy.run_geolocation_american_city(playwright)
70+
71+
72+
@pytest.mark.skipif(CI, reason="Flaky and fails on CI")
73+
def test_playwright_proxy_geolocation_non_american_city(playwright: Playwright) -> None:
74+
playwright_proxy.run_geolocation_non_american_city(playwright)
75+
76+
77+
def test_playwright_upload(playwright: Playwright) -> None:
78+
playwright_upload.run(playwright)

Diff for: examples/e2e/test_playwright_basic.py

-21
This file was deleted.

Diff for: examples/packages/extensions/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.zip
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<html>
2+
<body>
3+
<h1>Hello Extensions</h1>
4+
</body>
5+
</html>
5.81 KB
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"manifest_version": 3,
3+
"name": "Browserbase Extension Test",
4+
"description": "Test extension for browserbase",
5+
"version": "1.0",
6+
"action": {
7+
"default_popup": "hello.html"
8+
},
9+
"content_scripts": [
10+
{
11+
"matches": ["https://www.browserbase.com/*"],
12+
"js": ["scripts/content.js"]
13+
}
14+
],
15+
"web_accessible_resources": [
16+
{
17+
"resources": ["images/logo.png"],
18+
"matches": ["https://www.browserbase.com/*"]
19+
}
20+
]
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
const imageUrl = chrome.runtime.getURL("images/logo.png");
2+
window
3+
.fetch(imageUrl)
4+
.then((response) => {
5+
if (response.ok) {
6+
console.log("browserbase test extension image loaded");
7+
}
8+
})
9+
.catch((error) => {
10+
console.log(error);
11+
});

Diff for: examples/packages/logo.png

5.81 KB
Loading

Diff for: examples/playwright_basic.py

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,20 @@
11
from playwright.sync_api import Playwright, sync_playwright
22

33
from examples import (
4-
BROWSERBASE_API_KEY,
54
BROWSERBASE_PROJECT_ID,
6-
BROWSERBASE_CONNECT_URL,
75
bb,
86
)
97

108

11-
def run(playwright: Playwright):
9+
def run(playwright: Playwright) -> None:
1210
# Create a session on Browserbase
1311
session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID)
1412
assert session.id is not None
1513
assert session.status == "RUNNING", f"Session status is {session.status}"
1614

1715
# Connect to the remote session
18-
connect_url = (
19-
f"{BROWSERBASE_CONNECT_URL}?sessionId={session.id}&apiKey={BROWSERBASE_API_KEY}"
20-
)
2116
chromium = playwright.chromium
22-
browser = chromium.connect_over_cdp(connect_url)
17+
browser = chromium.connect_over_cdp(session.connect_url)
2318
context = browser.contexts[0]
2419
page = context.pages[0]
2520

Diff for: examples/playwright_captcha.py

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from playwright.sync_api import Playwright, ConsoleMessage, sync_playwright
2+
3+
from examples import (
4+
BROWSERBASE_API_KEY,
5+
BROWSERBASE_PROJECT_ID,
6+
BROWSERBASE_CONNECT_URL,
7+
bb,
8+
)
9+
10+
DEFAULT_CAPTCHA_URL = "https://www.google.com/recaptcha/api2/demo"
11+
OVERRIDE_TIMEOUT = 60000 # 60 seconds, adjust as needed
12+
13+
14+
def run(playwright: Playwright) -> None:
15+
# Create a session on Browserbase
16+
session = bb.sessions.create(project_id=BROWSERBASE_PROJECT_ID)
17+
assert session.id is not None
18+
assert session.status == "RUNNING", f"Session status is {session.status}"
19+
20+
# Connect to the remote session
21+
connect_url = (
22+
f"{BROWSERBASE_CONNECT_URL}?sessionId={session.id}&apiKey={BROWSERBASE_API_KEY}"
23+
)
24+
chromium = playwright.chromium
25+
browser = chromium.connect_over_cdp(connect_url)
26+
context = browser.contexts[0]
27+
page = context.pages[0]
28+
29+
captcha_solving_started = False
30+
captcha_solving_finished = False
31+
32+
# Browserbase logs messages to the console to indicate when captcha solving has started and finished
33+
# We can track these messages to know when the captcha solving has started and finished
34+
def handle_console(msg: ConsoleMessage) -> None:
35+
nonlocal captcha_solving_started, captcha_solving_finished
36+
if msg.text == "browserbase-solving-started":
37+
captcha_solving_started = True
38+
page.evaluate("window.captchaSolvingFinished = false;")
39+
elif msg.text == "browserbase-solving-finished":
40+
captcha_solving_finished = True
41+
page.evaluate("window.captchaSolvingFinished = true;")
42+
43+
page.on("console", handle_console)
44+
45+
page.goto(DEFAULT_CAPTCHA_URL, wait_until="networkidle")
46+
page.wait_for_function(
47+
"() => window.captchaSolvingFinished === true", timeout=OVERRIDE_TIMEOUT
48+
)
49+
50+
assert captcha_solving_started, "Captcha solving did not start"
51+
assert captcha_solving_finished, "Captcha solving did not finish"
52+
53+
page.close()
54+
browser.close()
55+
print("Done!")
56+
57+
58+
if __name__ == "__main__":
59+
with sync_playwright() as playwright:
60+
run(playwright)

Diff for: examples/playwright_contexts.py

+128
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import time
2+
from typing import Optional
3+
4+
from playwright.sync_api import Cookie, Browser, Playwright, sync_playwright
5+
6+
from examples import (
7+
BROWSERBASE_API_KEY,
8+
BROWSERBASE_PROJECT_ID,
9+
BROWSERBASE_CONNECT_URL,
10+
bb,
11+
)
12+
from browserbase.types.session_create_params import (
13+
BrowserSettings,
14+
BrowserSettingsContext,
15+
)
16+
17+
CONTEXT_TEST_URL = "https://www.browserbase.com"
18+
SECOND = 1000
19+
20+
21+
def add_hour(date: float) -> int:
22+
return int((date + 3600) * 1000) // SECOND
23+
24+
25+
def find_cookie(browser: Browser, name: str) -> Optional[Cookie]:
26+
default_context = browser.contexts[0]
27+
cookies = default_context.cookies()
28+
return next((cookie for cookie in cookies if cookie.get("name") == name), None)
29+
30+
31+
def run(playwright: Playwright) -> None:
32+
context_id = None
33+
session_id = None
34+
test_cookie_name = None
35+
test_cookie_value = None
36+
37+
# Step 1: Creates a context
38+
context = bb.contexts.create(project_id=BROWSERBASE_PROJECT_ID)
39+
assert context.id is not None
40+
context_id = context.id
41+
42+
uploaded_context = bb.contexts.retrieve(id=context_id)
43+
assert uploaded_context.id == context_id
44+
45+
# Step 2: Creates a session with the context
46+
session = bb.sessions.create(
47+
project_id=BROWSERBASE_PROJECT_ID,
48+
browser_settings=BrowserSettings(
49+
context=BrowserSettingsContext(id=context_id, persist=True),
50+
),
51+
)
52+
53+
assert (
54+
session.context_id == context_id
55+
), f"Session context_id is {session.context_id}, expected {context_id}"
56+
session_id = session.id
57+
58+
# Step 3: Populates and persists the context
59+
print(f"Populating context {context_id} during session {session_id}")
60+
connect_url = (
61+
f"{BROWSERBASE_CONNECT_URL}?sessionId={session_id}&apiKey={BROWSERBASE_API_KEY}"
62+
)
63+
browser = playwright.chromium.connect_over_cdp(connect_url)
64+
page = browser.contexts[0].pages[0]
65+
66+
page.goto(CONTEXT_TEST_URL, wait_until="domcontentloaded")
67+
68+
now = time.time()
69+
test_cookie_name = f"bb_{int(now * 1000)}"
70+
test_cookie_value = time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime(now))
71+
browser.contexts[0].add_cookies(
72+
[
73+
{
74+
"domain": ".browserbase.com",
75+
"expires": add_hour(now),
76+
"name": test_cookie_name,
77+
"path": "/",
78+
"value": test_cookie_value,
79+
}
80+
]
81+
)
82+
83+
assert find_cookie(browser, test_cookie_name) is not None
84+
85+
page.goto("https://www.google.com", wait_until="domcontentloaded")
86+
page.go_back()
87+
88+
assert find_cookie(browser, test_cookie_name) is not None
89+
90+
page.close()
91+
browser.close()
92+
93+
time.sleep(5)
94+
95+
# Step 4: Creates another session with the same context
96+
session = bb.sessions.create(
97+
project_id=BROWSERBASE_PROJECT_ID,
98+
browser_settings=BrowserSettings(
99+
context=BrowserSettingsContext(id=context_id, persist=True)
100+
),
101+
)
102+
assert (
103+
session.context_id == context_id
104+
), f"Session context_id is {session.context_id}, expected {context_id}"
105+
session_id = session.id
106+
107+
# Step 5: Uses context to find previous state
108+
print(f"Reusing context {context_id} during session {session_id}")
109+
connect_url = (
110+
f"{BROWSERBASE_CONNECT_URL}?sessionId={session_id}&apiKey={BROWSERBASE_API_KEY}"
111+
)
112+
browser = playwright.chromium.connect_over_cdp(connect_url)
113+
page = browser.contexts[0].pages[0]
114+
115+
page.goto(CONTEXT_TEST_URL, wait_until="domcontentloaded")
116+
117+
found_cookie = find_cookie(browser, test_cookie_name)
118+
print(found_cookie)
119+
assert found_cookie is not None
120+
assert found_cookie.get("value") == test_cookie_value
121+
122+
page.close()
123+
browser.close()
124+
125+
126+
if __name__ == "__main__":
127+
with sync_playwright() as playwright:
128+
run(playwright)

0 commit comments

Comments
 (0)