Skip to content

Commit 7df1b61

Browse files
authored
tests: added popup tests (#39)
1 parent ec84441 commit 7df1b61

File tree

7 files changed

+571
-17
lines changed

7 files changed

+571
-17
lines changed

tests/assets/popup/popup.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Popup</title>
5+
<script>
6+
window.initialUserAgent = navigator.userAgent;
7+
</script>
8+
</head>
9+
<body>
10+
I am a popup
11+
</body>
12+
</html>

tests/assets/popup/window-open.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Popup test</title>
5+
</head>
6+
<body>
7+
<script>
8+
window.open('./popup.html');
9+
</script>
10+
</body>
11+
</html>

tests/assets/title.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<title>Woof-Woof</title>

tests/conftest.py

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,8 @@
1313
# limitations under the License.
1414

1515
import asyncio
16-
import os
1716
import pytest
1817
import playwright
19-
import threading
20-
import time
21-
22-
from twisted.internet import reactor
23-
from twisted.web.static import File
24-
from twisted.web import server as web_server, resource
2518

2619
from .server import server as server_object
2720
from .utils import utils as utils_object
@@ -46,8 +39,10 @@ def event_loop():
4639

4740

4841
@pytest.fixture(scope="session")
49-
async def browser(browser_name):
50-
browser = await playwright.browser_types[browser_name].launch()
42+
async def browser(browser_name, pytestconfig):
43+
browser = await playwright.browser_types[browser_name].launch(
44+
headless=not pytestconfig.getoption("--headful")
45+
)
5146
yield browser
5247
await browser.close()
5348

@@ -78,15 +73,15 @@ def utils():
7873

7974
@pytest.fixture(autouse=True, scope="session")
8075
async def start_http_server():
81-
static_path = os.path.join(os.path.dirname(__file__), "assets")
82-
resource = File(static_path)
83-
site = web_server.Site(resource)
84-
reactor.listenTCP(server_object.PORT, site)
85-
t = threading.Thread(target=lambda: reactor.run(installSignalHandlers=0))
86-
t.start()
76+
server_object.start()
8777
yield
88-
reactor.stop()
89-
t.join()
78+
server_object.stop()
79+
80+
81+
@pytest.fixture(autouse=True)
82+
async def after_each_hook():
83+
yield
84+
server_object.reset()
9085

9186

9287
@pytest.fixture(scope="session")
@@ -136,3 +131,9 @@ def pytest_addoption(parser):
136131
default=[],
137132
help="Browsers which should be used. By default on all the browsers.",
138133
)
134+
parser.addoption(
135+
"--headful",
136+
action="store_true",
137+
default=False,
138+
help="Run tests in headful mode.",
139+
)

tests/server.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,19 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import asyncio
1516
from contextlib import closing
1617

1718
import os
1819
import socket
20+
import threading
21+
import os
22+
import binascii
23+
24+
from twisted.internet import reactor
25+
from twisted.web.static import File
26+
from twisted.web import server as web_server, resource
27+
from twisted.web._responses import UNAUTHORIZED
1928

2029

2130
def find_free_port():
@@ -25,12 +34,76 @@ def find_free_port():
2534
return s.getsockname()[1]
2635

2736

37+
class UnauthorizedResource(resource.ErrorPage):
38+
def __init__(self, message="Sorry, resource is unauthorized."):
39+
resource.ErrorPage.__init__(
40+
self, UNAUTHORIZED, "Unauthorized Resource", message
41+
)
42+
43+
2844
class Server:
2945
def __init__(self):
3046
self.PORT = find_free_port()
3147
self.EMPTY_PAGE = f"http://localhost:{self.PORT}/empty.html"
3248
self.PREFIX = f"http://localhost:{self.PORT}"
3349
self.CROSS_PROCESS_PREFIX = f"http://127.0.0.1:{self.PORT}"
3450

51+
def start(self):
52+
request_subscribers = {}
53+
auth = {}
54+
self.request_subscribers = request_subscribers
55+
self.auth = auth
56+
57+
class CustomFileServer(File):
58+
def getChild(self, path, request):
59+
uri_path = request.uri.decode()
60+
if request_subscribers.get(uri_path):
61+
request_subscribers[uri_path].set_result(request)
62+
request_subscribers.pop(uri_path)
63+
64+
if auth.get(uri_path):
65+
authorization_header = request.requestHeaders.getRawHeaders(
66+
"authorization"
67+
)
68+
creds_correct = False
69+
if authorization_header:
70+
creds = binascii.a2b_base64(
71+
authorization_header[0].split(" ")[1].encode() + b"==="
72+
).decode()
73+
creds_correct = ":".join(auth.get(uri_path)) == creds
74+
if not (authorization_header or creds_correct):
75+
request.responseHeaders.addRawHeader(
76+
b"www-authenticate", b'Basic realm="Secure Area"'
77+
)
78+
return UnauthorizedResource()
79+
80+
return super().getChild(path, request)
81+
82+
static_path = os.path.join(os.path.dirname(__file__), "assets")
83+
resource = CustomFileServer(static_path)
84+
site = web_server.Site(resource)
85+
reactor.listenTCP(self.PORT, site)
86+
self.thread = threading.Thread(
87+
target=lambda: reactor.run(installSignalHandlers=0)
88+
)
89+
self.thread.start()
90+
91+
def stop(self):
92+
reactor.stop()
93+
self.thread.join()
94+
95+
async def wait_for_request(self, path):
96+
future = asyncio.Future()
97+
self.request_subscribers[path] = future
98+
future.add_done_callback(lambda f: self.request_subscribers.pop(path, None))
99+
return await future
100+
101+
def set_auth(self, path: str, username: str, password: str):
102+
self.auth[path] = (username, password)
103+
104+
def reset(self):
105+
self.request_subscribers.clear()
106+
self.auth.clear()
107+
35108

36109
server = Server()

tests/test_input.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
# limitations under the License.
1414

1515
import asyncio
16+
from asyncio.futures import Future
1617
import os
1718

1819
from playwright.page import Page
20+
from playwright.file_chooser import FileChooser
1921

2022
FILE_TO_UPLOAD = os.path.join(
2123
os.path.dirname(os.path.realpath(__file__)), "assets/file-to-upload.txt"

0 commit comments

Comments
 (0)