Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# text files must be lf for golden file tests to work
*.txt eol=lf
*.json eol=lf
3 changes: 2 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:
branches: [ master ]
jobs:
build:
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -51,4 +52,4 @@ jobs:
- name: Build package
run: python build_package.py
- name: Test
run: pytest --browser=${{ matrix.browser }}
run: pytest -vv --browser=${{ matrix.browser }}
7 changes: 4 additions & 3 deletions playwright/serializers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from typing import List, Union
from playwright.helper import FilePayload
from os import path
import os
import mimetypes
import base64

from playwright.helper import FilePayload


def normalize_file_payloads(
files: Union[str, FilePayload, List[Union[str, FilePayload]]]
Expand All @@ -18,7 +19,7 @@ def normalize_file_payloads(
if isinstance(item, str):
with open(item, mode="rb") as fd:
file: FilePayload = {
"name": path.basename(item),
"name": os.path.basename(item),
"mimeType": mimetypes.guess_type(item)[0]
or "application/octet-stream",
"buffer": base64.b64encode(fd.read()).decode(),
Expand Down
2 changes: 2 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
markers =
skip_browser
only_browser
skip_platform
only_platform
[mypy]
ignore_missing_imports = True
[flake8]
Expand Down
3 changes: 3 additions & 0 deletions tests/assets/one-style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
background-color: pink;
}
2 changes: 2 additions & 0 deletions tests/assets/one-style.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<link rel='stylesheet' href='./one-style.css'>
<div>hello, world!</div>
Binary file added tests/assets/pptr.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions tests/assets/serviceworkers/empty/sw.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<script>
window.registrationPromise = navigator.serviceWorker.register('sw.js');
</script>
Empty file.
3 changes: 3 additions & 0 deletions tests/assets/serviceworkers/fetch/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
body {
background-color: pink;
}
5 changes: 5 additions & 0 deletions tests/assets/serviceworkers/fetch/sw.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<link rel="stylesheet" href="./style.css">
<script>
window.registrationPromise = navigator.serviceWorker.register('sw.js');
window.activationPromise = new Promise(resolve => navigator.serviceWorker.oncontrollerchange = resolve);
</script>
7 changes: 7 additions & 0 deletions tests/assets/serviceworkers/fetch/sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
self.addEventListener('fetch', event => {
event.respondWith(fetch(event.request));
});

self.addEventListener('activate', event => {
event.waitUntil(clients.claim());
});
12 changes: 12 additions & 0 deletions tests/assets/serviceworkers/fetchdummy/sw.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script>
window.registrationPromise = navigator.serviceWorker.register('sw.js');
window.activationPromise = new Promise(resolve => navigator.serviceWorker.oncontrollerchange = resolve);

async function fetchDummy(name) {
const response = await fetch(name);
if (!response.ok)
return 'FAILURE: ' + response.statusText;
const text = await response.text();
return text;
}
</script>
15 changes: 15 additions & 0 deletions tests/assets/serviceworkers/fetchdummy/sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
self.addEventListener('fetch', event => {
if (event.request.url.endsWith('.html') || event.request.url.includes('passthrough')) {
event.respondWith(fetch(event.request));
return;
}
const slash = event.request.url.lastIndexOf('/');
const name = event.request.url.substring(slash + 1);
const blob = new Blob(["responseFromServiceWorker:" + name], {type : 'text/css'});
const response = new Response(blob, { "status" : 200 , "statusText" : "OK" });
event.respondWith(response);
});

self.addEventListener('activate', event => {
event.waitUntil(clients.claim());
});
1 change: 1 addition & 0 deletions tests/assets/simple.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"foo": "bar"}
58 changes: 46 additions & 12 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@

import asyncio
import pytest
import playwright
import sys

import playwright
from .server import server as server_object
from .utils import utils as utils_object

Expand Down Expand Up @@ -60,6 +61,7 @@ async def context(browser):
context = await browser.newContext()
yield context
await context.close()
assert len(browser.contexts) == 0


@pytest.fixture
Expand Down Expand Up @@ -112,23 +114,55 @@ def is_chromium(browser_name):
return browser_name == "chromium"


@pytest.fixture(autouse=True)
def skip_by_browser(request, browser_name):
skip_browsers_names = []
@pytest.fixture(scope="session")
def is_win(browser_name):
return sys.platform == "win32"


@pytest.fixture(scope="session")
def is_linux(browser_name):
return sys.platform == "linux"


@pytest.fixture(scope="session")
def is_mac(browser_name):
return sys.platform == "darwin"


def _get_skiplist(request, values, value_name):
skipped_values = []
# Allowlist
only_browser_marker = request.node.get_closest_marker("only_browser")
if only_browser_marker:
skip_browsers_names = ["chromium", "firefox", "webkit"]
skip_browsers_names.remove(only_browser_marker.args[0])
only_marker = request.node.get_closest_marker(f"only_{value_name}")
if only_marker:
skipped_values = values
skipped_values.remove(only_marker.args[0])

# Denylist
skip_browser_marker = request.node.get_closest_marker("skip_browser")
if skip_browser_marker:
skip_browsers_names.append(skip_browser_marker.args[0])
skip_marker = request.node.get_closest_marker(f"skip_{value_name}")
if skip_marker:
skipped_values.append(skip_marker.args[0])

return skipped_values


@pytest.fixture(autouse=True)
def skip_by_browser(request, browser_name):
skip_browsers_names = _get_skiplist(
request, ["chromium", "firefox", "webkit"], "browser"
)

if browser_name in skip_browsers_names:
pytest.skip("skipped on this platform: {}".format(browser_name))
pytest.skip("skipped for this browser: {}".format(browser_name))


@pytest.fixture(autouse=True)
def skip_by_platform(request):
skip_platform_names = _get_skiplist(
request, ["win32", "linux", "darwin"], "platform"
)

if sys.platform in skip_platform_names:
pytest.skip("skipped on this platform: {}".format(sys.platform))


def pytest_addoption(parser):
Expand Down
95 changes: 60 additions & 35 deletions tests/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,15 @@

import asyncio
from contextlib import closing

from http import HTTPStatus
import os
import socket
import threading
import binascii
import gzip
import mimetypes

from twisted.internet import reactor
from twisted.web.static import File
from twisted.web import server as web_server, resource
from twisted.web._responses import UNAUTHORIZED
from twisted.web import http


def find_free_port():
Expand All @@ -33,36 +32,30 @@ def find_free_port():
return s.getsockname()[1]


class NoResponseResource:
@staticmethod
def render(request):
return b""


class UnauthorizedResource(resource.ErrorPage):
def __init__(self, message="Sorry, resource is unauthorized."):
resource.ErrorPage.__init__(
self, UNAUTHORIZED, "Unauthorized Resource", message
)


class Server:
def __init__(self):
self.PORT = find_free_port()
self.EMPTY_PAGE = f"http://localhost:{self.PORT}/empty.html"
self.PREFIX = f"http://localhost:{self.PORT}"
self.CROSS_PROCESS_PREFIX = f"http://127.0.0.1:{self.PORT}"

def __repr__(self) -> str:
return self.PREFIX

def start(self):
request_subscribers = {}
auth = {}
routes = {}
gzip_routes = set()
self.request_subscribers = request_subscribers
self.auth = auth
self.routes = routes
self.gzip_routes = gzip_routes
static_path = os.path.join(os.path.dirname(__file__), "assets")

class CustomFileServer(File):
def getChild(self, path, request):
class TestServerHTTPHandler(http.Request):
def process(self):
request = self
uri_path = request.uri.decode()
if request_subscribers.get(uri_path):
request_subscribers[uri_path].set_result(request)
Expand All @@ -74,25 +67,44 @@ def getChild(self, path, request):
)
creds_correct = False
if authorization_header:
creds = binascii.a2b_base64(
authorization_header[0].split(" ")[1].encode() + b"==="
).decode()
creds_correct = ":".join(auth.get(uri_path)) == creds
creds_correct = auth.get(uri_path) == (
request.getUser(),
request.getPassword(),
)
if not (authorization_header or creds_correct):
request.responseHeaders.addRawHeader(
b"www-authenticate", b'Basic realm="Secure Area"'
request.setHeader(
b"www-authenticate", 'Basic realm="Secure Area"'
)
return UnauthorizedResource()
request.setResponseCode(HTTPStatus.UNAUTHORIZED)
request.finish()
return
if routes.get(uri_path):
routes[uri_path](request)
return NoResponseResource()

return super().getChild(path, request)

static_path = os.path.join(os.path.dirname(__file__), "assets")
resource = CustomFileServer(static_path)
site = web_server.Site(resource)
reactor.listenTCP(self.PORT, site)
return
file_content = None
try:
file_content = open(
os.path.join(static_path, uri_path[1:]), "rb"
).read()
except (FileNotFoundError, IsADirectoryError):
request.setResponseCode(HTTPStatus.NOT_FOUND)
if file_content:
request.setHeader("Content-Type", mimetypes.guess_type(uri_path)[0])
if uri_path in gzip_routes:
request.setHeader("Content-Encoding", "gzip")
request.write(gzip.compress(file_content))
else:
request.write(file_content)
self.setResponseCode(HTTPStatus.OK)
self.finish()

class MyHttp(http.HTTPChannel):
requestFactory = TestServerHTTPHandler

class MyHttpFactory(http.HTTPFactory):
protocol = MyHttp

reactor.listenTCP(self.PORT, MyHttpFactory())
self.thread = threading.Thread(
target=lambda: reactor.run(installSignalHandlers=0)
)
Expand All @@ -114,9 +126,22 @@ def set_auth(self, path: str, username: str, password: str):
def reset(self):
self.request_subscribers.clear()
self.auth.clear()
self.gzip_routes.clear()
self.routes.clear()

def set_route(self, path, callback):
self.routes[path] = callback

def enable_gzip(self, path):
self.gzip_routes.add(path)

def set_redirect(self, from_, to):
def handle_redirect(request):
request.setResponseCode(HTTPStatus.FOUND)
request.setHeader("location", to)
request.finish()

self.set_route(from_, handle_redirect)


server = Server()
11 changes: 8 additions & 3 deletions tests/test_add_init_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import os

from playwright import Error
from os import path


async def test_add_init_script_evaluate_before_anything_else_on_the_page(page):
Expand All @@ -24,7 +25,9 @@ async def test_add_init_script_evaluate_before_anything_else_on_the_page(page):

async def test_add_init_script_work_with_a_path(page):
await page.addInitScript(
path=path.join(path.dirname(path.abspath(__file__)), "assets/injectedfile.js")
path=os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets/injectedfile.js"
)
)
await page.goto("data:text/html,<script>window.result = window.injected</script>")
assert await page.evaluate("window.result") == 123
Expand Down Expand Up @@ -57,7 +60,9 @@ async def test_add_init_script_work_with_browser_context_scripts_with_a_path(
page, context
):
await context.addInitScript(
path=path.join(path.dirname(path.abspath(__file__)), "assets/injectedfile.js")
path=os.path.join(
os.path.dirname(os.path.abspath(__file__)), "assets/injectedfile.js"
)
)
page = await context.newPage()
await page.goto("data:text/html,<script>window.result = window.injected</script>")
Expand Down
Loading