├── .gitignore ├── README.md ├── nix ├── chromium-playwright.nix ├── firefox-playwright.nix └── playwright-browsers.nix ├── poetry.lock ├── poetry.toml ├── pyproject.toml ├── run_tests.sh ├── shell.nix └── src ├── conftest.py └── test_playwright_nix.py /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Using playwright with nix-provisioned browsers 2 | 3 | The playwright docs pretty clearly state that the releases are tied to their 4 | specific browser versions, so this approach will fetch, patch and wrap them. 5 | 6 | This repo also showcases how to wire things up for pytest. 7 | 8 | ## Running the test 9 | 10 | `nix-shell --run 'poetry install && poetry run pytest'` or `./run_tests.sh` which does exactly 11 | that. 12 | 13 | For playing around, running `nix-shell` followed by `poetry shell` is probably nicer. 14 | 15 | Try passing `--headed` or `--browser=firefox` to `pytest`. 16 | 17 | ## Future work 18 | 19 | This is using a pretty blunt method to get the `rpath`, `interpreter` and wrapper: We install the 20 | binary browsers from nixpkgs and take stuff from there. This has a few problems: 21 | 22 | - It's inefficient. There's no real need to install nixpgks chrome to do this 23 | - It's brittle. If nixpkgs changes how their wrappers work, some playwright browser versions might 24 | stop working. 25 | 26 | The main reason why I chose this approach is that I do not understand how nixpkgs generates the 27 | wrappers and I don't want to think too much about the specific dependencies. It could 28 | theoretically be advantageous to patchelf and generate the wrappers ourselves, so if you want to 29 | follow that idea, I'd love to see where that goes. 30 | 31 | ## Acknowledgements 32 | 33 | I could not have done this without [this work by ludios](https://github.com/ludios/nixos-playwright) 34 | -------------------------------------------------------------------------------- /nix/chromium-playwright.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { }, rev }: 2 | 3 | let 4 | inherit (pkgs) fetchzip; 5 | inherit (pkgs.stdenv) mkDerivation; 6 | 7 | inherit (pkgs.stdenv.hostPlatform) system; 8 | selectSystem = attrs: 9 | attrs.${system} or (throw "Unsupported system: ${system}"); 10 | 11 | suffix = selectSystem { 12 | # Not sure how other system compatibility is, needs trial & error 13 | x86_64-linux = "linux"; 14 | aarch64-linux = "linux-arm64"; 15 | # x86_64-darwin = "mac"; 16 | # aarch64-darwin = "mac-arm64"; 17 | }; 18 | sha256 = { 19 | # Fill in on demand 20 | "1005" = selectSystem { 21 | x86_64-linux = "sha256-HfsvjiFKjlYQJqug2hj0z3CVp1o0TMyIw4Kybr82kbo="; 22 | }; 23 | }.${rev}; 24 | 25 | upstream_chromium = fetchzip { 26 | url = 27 | "https://playwright.azureedge.net/builds/chromium/${rev}/chromium-${suffix}.zip"; 28 | inherit sha256; 29 | stripRoot = true; 30 | }; 31 | in mkDerivation { 32 | name = "chromium-playwright"; 33 | version = rev; 34 | src = upstream_chromium; 35 | 36 | nativeBuildInputs = [ pkgs.patchelf ]; 37 | 38 | installPhase = '' 39 | mkdir $out 40 | 41 | cp -r $src/* $out/ 42 | 43 | # patchelf the binary 44 | wrapper="${pkgs.google-chrome-dev}/bin/google-chrome-unstable" 45 | wrapper_2="$(cat "$wrapper" | grep '^exec ' | grep -o -P '/nix/store/[^"]+' | head -n 1)" 46 | binary="$(dirname "$wrapper_2")/chrome" 47 | interpreter="$(patchelf --print-interpreter "$binary")" 48 | rpath="$(patchelf --print-rpath "$binary")" 49 | 50 | chmod u+w $out/chrome 51 | stat $out/chrome 52 | patchelf --set-interpreter "$interpreter" $out/chrome 53 | patchelf --set-rpath "$rpath" $out/chrome 54 | chmod u-w $out/chrome 55 | 56 | # create the wrapper script 57 | mv $out/chrome $out/chrome.bin 58 | cat "$wrapper" | grep -vE '^exec ' > $out/chrome 59 | echo "exec \"$out/chrome.bin\" \"\$@\"" >> $out/chrome 60 | chmod a+x $out/chrome 61 | ''; 62 | } 63 | -------------------------------------------------------------------------------- /nix/firefox-playwright.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { }, rev }: 2 | 3 | let 4 | inherit (pkgs) fetchzip; 5 | inherit (pkgs.stdenv) mkDerivation; 6 | 7 | inherit (pkgs.stdenv.hostPlatform) system; 8 | selectSystem = attrs: 9 | attrs.${system} or (throw "Unsupported system: ${system}"); 10 | 11 | suffix = selectSystem { 12 | # Not sure how other system compatibility is, needs trial & error 13 | x86_64-linux = "ubuntu-20.04"; 14 | # aarch64-linux = "ubuntu-20.04-arm64"; 15 | # x86_64-darwin = "mac"; 16 | # aarch64-darwin = "mac-arm64"; 17 | }; 18 | sha256 = { 19 | "1323" = selectSystem { 20 | x86_64-linux = "sha256-N9qGlC+d3M+vEGQYREWeOjmyvOccoJXhR4mM+VZ8NjI="; 21 | }; 22 | }.${rev}; 23 | 24 | upstream_firefox = fetchzip { 25 | url = 26 | "https://playwright.azureedge.net/builds/firefox/${rev}/firefox-${suffix}.zip"; 27 | inherit sha256; 28 | stripRoot = true; 29 | }; 30 | in mkDerivation { 31 | name = "firefox-playwright"; 32 | version = rev; 33 | src = upstream_firefox; 34 | 35 | nativeBuildInputs = [ pkgs.patchelf ]; 36 | 37 | installPhase = '' 38 | mkdir $out 39 | 40 | cp -r $src/* $out/ 41 | 42 | # patchelf the binary 43 | wrapper="${pkgs.firefox-bin}/bin/firefox" 44 | binary="$(readlink -f $(<"$wrapper" grep '^exec ' | grep -o -P '/nix/store/[^"]+' | head -n 1))" 45 | 46 | interpreter="$(patchelf --print-interpreter "$binary")" 47 | rpath="$(patchelf --print-rpath "$binary")" 48 | 49 | find $out -executable -type f | while read i; do 50 | chmod u+w "$i" 51 | [[ $i == *.so ]] || patchelf --set-interpreter "$interpreter" "$i" 52 | patchelf --set-rpath "$rpath" "$i" 53 | chmod u-w "$i" 54 | done 55 | 56 | 57 | # create the wrapper script 58 | rm $out/firefox 59 | <"$wrapper" grep -vE '^exec ' > $out/firefox 60 | echo "exec \"$out/firefox-bin\" \"\$@\"" >> $out/firefox 61 | chmod a+x $out/firefox 62 | ''; 63 | } 64 | -------------------------------------------------------------------------------- /nix/playwright-browsers.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import { }, playwright_version, data_sha256 }: 2 | 3 | let 4 | inherit (builtins) fetchurl map fromJSON readFile listToAttrs; 5 | 6 | browser_revs = let 7 | file = fetchurl { 8 | url = 9 | "https://raw.githubusercontent.com/microsoft/playwright/v${playwright_version}/packages/playwright-core/browsers.json"; 10 | sha256 = data_sha256; 11 | }; 12 | raw_data = fromJSON (readFile file); 13 | in listToAttrs (map ({ name, revision, ... }: { 14 | inherit name; 15 | value = revision; 16 | }) raw_data.browsers); 17 | 18 | chromium-playwright = import ./chromium-playwright.nix { 19 | inherit pkgs; 20 | rev = browser_revs.chromium; 21 | }; 22 | firefox-playwright = import ./firefox-playwright.nix { 23 | inherit pkgs; 24 | rev = browser_revs.firefox; 25 | }; 26 | in { 27 | chromium = "${chromium-playwright}/chrome"; 28 | firefox = "${firefox-playwright}/firefox"; 29 | } 30 | 31 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "atomicwrites" 3 | version = "1.4.0" 4 | description = "Atomic file writes." 5 | category = "dev" 6 | optional = false 7 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 8 | 9 | [[package]] 10 | name = "attrs" 11 | version = "21.4.0" 12 | description = "Classes Without Boilerplate" 13 | category = "dev" 14 | optional = false 15 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 16 | 17 | [package.extras] 18 | dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] 19 | docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] 20 | tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] 21 | tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] 22 | 23 | [[package]] 24 | name = "certifi" 25 | version = "2022.5.18.1" 26 | description = "Python package for providing Mozilla's CA Bundle." 27 | category = "dev" 28 | optional = false 29 | python-versions = ">=3.6" 30 | 31 | [[package]] 32 | name = "charset-normalizer" 33 | version = "2.0.12" 34 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 35 | category = "dev" 36 | optional = false 37 | python-versions = ">=3.5.0" 38 | 39 | [package.extras] 40 | unicode_backport = ["unicodedata2"] 41 | 42 | [[package]] 43 | name = "colorama" 44 | version = "0.4.4" 45 | description = "Cross-platform colored terminal text." 46 | category = "dev" 47 | optional = false 48 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 49 | 50 | [[package]] 51 | name = "greenlet" 52 | version = "1.1.2" 53 | description = "Lightweight in-process concurrent programming" 54 | category = "dev" 55 | optional = false 56 | python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" 57 | 58 | [package.extras] 59 | docs = ["sphinx"] 60 | 61 | [[package]] 62 | name = "idna" 63 | version = "3.3" 64 | description = "Internationalized Domain Names in Applications (IDNA)" 65 | category = "dev" 66 | optional = false 67 | python-versions = ">=3.5" 68 | 69 | [[package]] 70 | name = "iniconfig" 71 | version = "1.1.1" 72 | description = "iniconfig: brain-dead simple config-ini parsing" 73 | category = "dev" 74 | optional = false 75 | python-versions = "*" 76 | 77 | [[package]] 78 | name = "packaging" 79 | version = "21.3" 80 | description = "Core utilities for Python packages" 81 | category = "dev" 82 | optional = false 83 | python-versions = ">=3.6" 84 | 85 | [package.dependencies] 86 | pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" 87 | 88 | [[package]] 89 | name = "playwright" 90 | version = "1.22.0" 91 | description = "A high-level API to automate web browsers" 92 | category = "dev" 93 | optional = false 94 | python-versions = ">=3.7" 95 | 96 | [package.dependencies] 97 | greenlet = "1.1.2" 98 | pyee = "8.1.0" 99 | websockets = "10.1" 100 | 101 | [[package]] 102 | name = "pluggy" 103 | version = "1.0.0" 104 | description = "plugin and hook calling mechanisms for python" 105 | category = "dev" 106 | optional = false 107 | python-versions = ">=3.6" 108 | 109 | [package.extras] 110 | dev = ["pre-commit", "tox"] 111 | testing = ["pytest", "pytest-benchmark"] 112 | 113 | [[package]] 114 | name = "py" 115 | version = "1.11.0" 116 | description = "library with cross-python path, ini-parsing, io, code, log facilities" 117 | category = "dev" 118 | optional = false 119 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 120 | 121 | [[package]] 122 | name = "pyee" 123 | version = "8.1.0" 124 | description = "A port of node.js's EventEmitter to python." 125 | category = "dev" 126 | optional = false 127 | python-versions = "*" 128 | 129 | [[package]] 130 | name = "pyparsing" 131 | version = "3.0.9" 132 | description = "pyparsing module - Classes and methods to define and execute parsing grammars" 133 | category = "dev" 134 | optional = false 135 | python-versions = ">=3.6.8" 136 | 137 | [package.extras] 138 | diagrams = ["railroad-diagrams", "jinja2"] 139 | 140 | [[package]] 141 | name = "pytest" 142 | version = "7.1.2" 143 | description = "pytest: simple powerful testing with Python" 144 | category = "dev" 145 | optional = false 146 | python-versions = ">=3.7" 147 | 148 | [package.dependencies] 149 | atomicwrites = {version = ">=1.0", markers = "sys_platform == \"win32\""} 150 | attrs = ">=19.2.0" 151 | colorama = {version = "*", markers = "sys_platform == \"win32\""} 152 | iniconfig = "*" 153 | packaging = "*" 154 | pluggy = ">=0.12,<2.0" 155 | py = ">=1.8.2" 156 | tomli = ">=1.0.0" 157 | 158 | [package.extras] 159 | testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "xmlschema"] 160 | 161 | [[package]] 162 | name = "pytest-base-url" 163 | version = "2.0.0" 164 | description = "pytest plugin for URL based testing" 165 | category = "dev" 166 | optional = false 167 | python-versions = ">=3.7,<4.0" 168 | 169 | [package.dependencies] 170 | pytest = ">=3.0.0,<8.0.0" 171 | requests = ">=2.9" 172 | 173 | [[package]] 174 | name = "pytest-playwright" 175 | version = "0.3.0" 176 | description = "A pytest wrapper with fixtures for Playwright to automate web browsers" 177 | category = "dev" 178 | optional = false 179 | python-versions = ">=3.7" 180 | 181 | [package.dependencies] 182 | playwright = ">=1.18" 183 | pytest = "*" 184 | pytest-base-url = "*" 185 | python-slugify = "*" 186 | 187 | [[package]] 188 | name = "python-slugify" 189 | version = "6.1.2" 190 | description = "A Python slugify application that also handles Unicode" 191 | category = "dev" 192 | optional = false 193 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 194 | 195 | [package.dependencies] 196 | text-unidecode = ">=1.3" 197 | 198 | [package.extras] 199 | unidecode = ["Unidecode (>=1.1.1)"] 200 | 201 | [[package]] 202 | name = "requests" 203 | version = "2.27.1" 204 | description = "Python HTTP for Humans." 205 | category = "dev" 206 | optional = false 207 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 208 | 209 | [package.dependencies] 210 | certifi = ">=2017.4.17" 211 | charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} 212 | idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} 213 | urllib3 = ">=1.21.1,<1.27" 214 | 215 | [package.extras] 216 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 217 | use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] 218 | 219 | [[package]] 220 | name = "text-unidecode" 221 | version = "1.3" 222 | description = "The most basic Text::Unidecode port" 223 | category = "dev" 224 | optional = false 225 | python-versions = "*" 226 | 227 | [[package]] 228 | name = "tomli" 229 | version = "2.0.1" 230 | description = "A lil' TOML parser" 231 | category = "dev" 232 | optional = false 233 | python-versions = ">=3.7" 234 | 235 | [[package]] 236 | name = "urllib3" 237 | version = "1.26.9" 238 | description = "HTTP library with thread-safe connection pooling, file post, and more." 239 | category = "dev" 240 | optional = false 241 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 242 | 243 | [package.extras] 244 | brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] 245 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 246 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 247 | 248 | [[package]] 249 | name = "websockets" 250 | version = "10.1" 251 | description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" 252 | category = "dev" 253 | optional = false 254 | python-versions = ">=3.7" 255 | 256 | [metadata] 257 | lock-version = "1.1" 258 | python-versions = "^3.9" 259 | content-hash = "90c3aa9ba6ff1cca12ac6915ccfa0188c19553a3612ed1282915c73e25fee373" 260 | 261 | [metadata.files] 262 | atomicwrites = [ 263 | {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"}, 264 | {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"}, 265 | ] 266 | attrs = [ 267 | {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, 268 | {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, 269 | ] 270 | certifi = [ 271 | {file = "certifi-2022.5.18.1-py3-none-any.whl", hash = "sha256:f1d53542ee8cbedbe2118b5686372fb33c297fcd6379b050cca0ef13a597382a"}, 272 | {file = "certifi-2022.5.18.1.tar.gz", hash = "sha256:9c5705e395cd70084351dd8ad5c41e65655e08ce46f2ec9cf6c2c08390f71eb7"}, 273 | ] 274 | charset-normalizer = [ 275 | {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, 276 | {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, 277 | ] 278 | colorama = [ 279 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 280 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 281 | ] 282 | greenlet = [ 283 | {file = "greenlet-1.1.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:58df5c2a0e293bf665a51f8a100d3e9956febfbf1d9aaf8c0677cf70218910c6"}, 284 | {file = "greenlet-1.1.2-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:aec52725173bd3a7b56fe91bc56eccb26fbdff1386ef123abb63c84c5b43b63a"}, 285 | {file = "greenlet-1.1.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:833e1551925ed51e6b44c800e71e77dacd7e49181fdc9ac9a0bf3714d515785d"}, 286 | {file = "greenlet-1.1.2-cp27-cp27m-win32.whl", hash = "sha256:aa5b467f15e78b82257319aebc78dd2915e4c1436c3c0d1ad6f53e47ba6e2713"}, 287 | {file = "greenlet-1.1.2-cp27-cp27m-win_amd64.whl", hash = "sha256:40b951f601af999a8bf2ce8c71e8aaa4e8c6f78ff8afae7b808aae2dc50d4c40"}, 288 | {file = "greenlet-1.1.2-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:95e69877983ea39b7303570fa6760f81a3eec23d0e3ab2021b7144b94d06202d"}, 289 | {file = "greenlet-1.1.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:356b3576ad078c89a6107caa9c50cc14e98e3a6c4874a37c3e0273e4baf33de8"}, 290 | {file = "greenlet-1.1.2-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8639cadfda96737427330a094476d4c7a56ac03de7265622fcf4cfe57c8ae18d"}, 291 | {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:97e5306482182170ade15c4b0d8386ded995a07d7cc2ca8f27958d34d6736497"}, 292 | {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e6a36bb9474218c7a5b27ae476035497a6990e21d04c279884eb10d9b290f1b1"}, 293 | {file = "greenlet-1.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:abb7a75ed8b968f3061327c433a0fbd17b729947b400747c334a9c29a9af6c58"}, 294 | {file = "greenlet-1.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b336501a05e13b616ef81ce329c0e09ac5ed8c732d9ba7e3e983fcc1a9e86965"}, 295 | {file = "greenlet-1.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:14d4f3cd4e8b524ae9b8aa567858beed70c392fdec26dbdb0a8a418392e71708"}, 296 | {file = "greenlet-1.1.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:17ff94e7a83aa8671a25bf5b59326ec26da379ace2ebc4411d690d80a7fbcf23"}, 297 | {file = "greenlet-1.1.2-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9f3cba480d3deb69f6ee2c1825060177a22c7826431458c697df88e6aeb3caee"}, 298 | {file = "greenlet-1.1.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:fa877ca7f6b48054f847b61d6fa7bed5cebb663ebc55e018fda12db09dcc664c"}, 299 | {file = "greenlet-1.1.2-cp35-cp35m-win32.whl", hash = "sha256:7cbd7574ce8e138bda9df4efc6bf2ab8572c9aff640d8ecfece1b006b68da963"}, 300 | {file = "greenlet-1.1.2-cp35-cp35m-win_amd64.whl", hash = "sha256:903bbd302a2378f984aef528f76d4c9b1748f318fe1294961c072bdc7f2ffa3e"}, 301 | {file = "greenlet-1.1.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:049fe7579230e44daef03a259faa24511d10ebfa44f69411d99e6a184fe68073"}, 302 | {file = "greenlet-1.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:dd0b1e9e891f69e7675ba5c92e28b90eaa045f6ab134ffe70b52e948aa175b3c"}, 303 | {file = "greenlet-1.1.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:7418b6bfc7fe3331541b84bb2141c9baf1ec7132a7ecd9f375912eca810e714e"}, 304 | {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f9d29ca8a77117315101425ec7ec2a47a22ccf59f5593378fc4077ac5b754fce"}, 305 | {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21915eb821a6b3d9d8eefdaf57d6c345b970ad722f856cd71739493ce003ad08"}, 306 | {file = "greenlet-1.1.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eff9d20417ff9dcb0d25e2defc2574d10b491bf2e693b4e491914738b7908168"}, 307 | {file = "greenlet-1.1.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b8c008de9d0daba7b6666aa5bbfdc23dcd78cafc33997c9b7741ff6353bafb7f"}, 308 | {file = "greenlet-1.1.2-cp36-cp36m-win32.whl", hash = "sha256:32ca72bbc673adbcfecb935bb3fb1b74e663d10a4b241aaa2f5a75fe1d1f90aa"}, 309 | {file = "greenlet-1.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:f0214eb2a23b85528310dad848ad2ac58e735612929c8072f6093f3585fd342d"}, 310 | {file = "greenlet-1.1.2-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:b92e29e58bef6d9cfd340c72b04d74c4b4e9f70c9fa7c78b674d1fec18896dc4"}, 311 | {file = "greenlet-1.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fdcec0b8399108577ec290f55551d926d9a1fa6cad45882093a7a07ac5ec147b"}, 312 | {file = "greenlet-1.1.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:93f81b134a165cc17123626ab8da2e30c0455441d4ab5576eed73a64c025b25c"}, 313 | {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e12bdc622676ce47ae9abbf455c189e442afdde8818d9da983085df6312e7a1"}, 314 | {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c790abda465726cfb8bb08bd4ca9a5d0a7bd77c7ac1ca1b839ad823b948ea28"}, 315 | {file = "greenlet-1.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f276df9830dba7a333544bd41070e8175762a7ac20350786b322b714b0e654f5"}, 316 | {file = "greenlet-1.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c5d5b35f789a030ebb95bff352f1d27a93d81069f2adb3182d99882e095cefe"}, 317 | {file = "greenlet-1.1.2-cp37-cp37m-win32.whl", hash = "sha256:64e6175c2e53195278d7388c454e0b30997573f3f4bd63697f88d855f7a6a1fc"}, 318 | {file = "greenlet-1.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:b11548073a2213d950c3f671aa88e6f83cda6e2fb97a8b6317b1b5b33d850e06"}, 319 | {file = "greenlet-1.1.2-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:9633b3034d3d901f0a46b7939f8c4d64427dfba6bbc5a36b1a67364cf148a1b0"}, 320 | {file = "greenlet-1.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:eb6ea6da4c787111adf40f697b4e58732ee0942b5d3bd8f435277643329ba627"}, 321 | {file = "greenlet-1.1.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:f3acda1924472472ddd60c29e5b9db0cec629fbe3c5c5accb74d6d6d14773478"}, 322 | {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e859fcb4cbe93504ea18008d1df98dee4f7766db66c435e4882ab35cf70cac43"}, 323 | {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:00e44c8afdbe5467e4f7b5851be223be68adb4272f44696ee71fe46b7036a711"}, 324 | {file = "greenlet-1.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec8c433b3ab0419100bd45b47c9c8551248a5aee30ca5e9d399a0b57ac04651b"}, 325 | {file = "greenlet-1.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2bde6792f313f4e918caabc46532aa64aa27a0db05d75b20edfc5c6f46479de2"}, 326 | {file = "greenlet-1.1.2-cp38-cp38-win32.whl", hash = "sha256:288c6a76705dc54fba69fbcb59904ae4ad768b4c768839b8ca5fdadec6dd8cfd"}, 327 | {file = "greenlet-1.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:8d2f1fb53a421b410751887eb4ff21386d119ef9cde3797bf5e7ed49fb51a3b3"}, 328 | {file = "greenlet-1.1.2-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:166eac03e48784a6a6e0e5f041cfebb1ab400b394db188c48b3a84737f505b67"}, 329 | {file = "greenlet-1.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:572e1787d1460da79590bf44304abbc0a2da944ea64ec549188fa84d89bba7ab"}, 330 | {file = "greenlet-1.1.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:be5f425ff1f5f4b3c1e33ad64ab994eed12fc284a6ea71c5243fd564502ecbe5"}, 331 | {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1692f7d6bc45e3200844be0dba153612103db241691088626a33ff1f24a0d88"}, 332 | {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7227b47e73dedaa513cdebb98469705ef0d66eb5a1250144468e9c3097d6b59b"}, 333 | {file = "greenlet-1.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ff61ff178250f9bb3cd89752df0f1dd0e27316a8bd1465351652b1b4a4cdfd3"}, 334 | {file = "greenlet-1.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0051c6f1f27cb756ffc0ffbac7d2cd48cb0362ac1736871399a739b2885134d3"}, 335 | {file = "greenlet-1.1.2-cp39-cp39-win32.whl", hash = "sha256:f70a9e237bb792c7cc7e44c531fd48f5897961701cdaa06cf22fc14965c496cf"}, 336 | {file = "greenlet-1.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:013d61294b6cd8fe3242932c1c5e36e5d1db2c8afb58606c5a67efce62c1f5fd"}, 337 | {file = "greenlet-1.1.2.tar.gz", hash = "sha256:e30f5ea4ae2346e62cedde8794a56858a67b878dd79f7df76a0767e356b1744a"}, 338 | ] 339 | idna = [ 340 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, 341 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, 342 | ] 343 | iniconfig = [ 344 | {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"}, 345 | {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"}, 346 | ] 347 | packaging = [ 348 | {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, 349 | {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, 350 | ] 351 | playwright = [ 352 | {file = "playwright-1.22.0-py3-none-macosx_10_13_x86_64.whl", hash = "sha256:9506d582e1a36aa19b06f5b5f8ae154265ec2fc5039217cfe8dfd66edb1a7563"}, 353 | {file = "playwright-1.22.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cd2e87b4d2a3d736bbd4a85ed7638c577dfc8098bcabd92d248440d51beac50b"}, 354 | {file = "playwright-1.22.0-py3-none-macosx_11_0_universal2.whl", hash = "sha256:a36dfaf9be0228985b7b001887f66dc61e8300970c0602ae9d5b191da510a982"}, 355 | {file = "playwright-1.22.0-py3-none-manylinux1_x86_64.whl", hash = "sha256:78ddf51d0c08d94edb6d2ddf7b262f0d87a1c0ab5e121bfefa1945214c308f95"}, 356 | {file = "playwright-1.22.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:217b2f623527ded15d602ccca5138395a1677c67399c4494844d5341420d34e0"}, 357 | {file = "playwright-1.22.0-py3-none-win32.whl", hash = "sha256:a4977211414532a525a057f8d461a7277fdfefa20eb577b2e0247523815ba521"}, 358 | {file = "playwright-1.22.0-py3-none-win_amd64.whl", hash = "sha256:bc7827fcc037a9a6f571328c524d50bfd46db4ebdc415ab8b8efafae30c1d597"}, 359 | ] 360 | pluggy = [ 361 | {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, 362 | {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, 363 | ] 364 | py = [ 365 | {file = "py-1.11.0-py2.py3-none-any.whl", hash = "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378"}, 366 | {file = "py-1.11.0.tar.gz", hash = "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719"}, 367 | ] 368 | pyee = [ 369 | {file = "pyee-8.1.0-py2.py3-none-any.whl", hash = "sha256:383973b63ad7ed5e3c0311f8b179c52981f9e7b3eaea0e9a830d13ec34dde65f"}, 370 | {file = "pyee-8.1.0.tar.gz", hash = "sha256:92dacc5bd2bdb8f95aa8dd2585d47ca1c4840e2adb95ccf90034d64f725bfd31"}, 371 | ] 372 | pyparsing = [ 373 | {file = "pyparsing-3.0.9-py3-none-any.whl", hash = "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc"}, 374 | {file = "pyparsing-3.0.9.tar.gz", hash = "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb"}, 375 | ] 376 | pytest = [ 377 | {file = "pytest-7.1.2-py3-none-any.whl", hash = "sha256:13d0e3ccfc2b6e26be000cb6568c832ba67ba32e719443bfe725814d3c42433c"}, 378 | {file = "pytest-7.1.2.tar.gz", hash = "sha256:a06a0425453864a270bc45e71f783330a7428defb4230fb5e6a731fde06ecd45"}, 379 | ] 380 | pytest-base-url = [ 381 | {file = "pytest-base-url-2.0.0.tar.gz", hash = "sha256:e1e88a4fd221941572ccdcf3bf6c051392d2f8b6cef3e0bc7da95abec4b5346e"}, 382 | {file = "pytest_base_url-2.0.0-py3-none-any.whl", hash = "sha256:ed36fd632c32af9f1c08f2c2835dcf42ca8fcd097d6ed44a09f253d365ad8297"}, 383 | ] 384 | pytest-playwright = [ 385 | {file = "pytest-playwright-0.3.0.tar.gz", hash = "sha256:62b843b73d259431380393b335bee65cc7a420c9095c83f78274b6b508cb2f33"}, 386 | {file = "pytest_playwright-0.3.0-py3-none-any.whl", hash = "sha256:2f41058f4a769a2c9631baacec65e9ebb3255e34519f0aab7f46e4c7fe66d127"}, 387 | ] 388 | python-slugify = [ 389 | {file = "python-slugify-6.1.2.tar.gz", hash = "sha256:272d106cb31ab99b3496ba085e3fea0e9e76dcde967b5e9992500d1f785ce4e1"}, 390 | {file = "python_slugify-6.1.2-py2.py3-none-any.whl", hash = "sha256:7b2c274c308b62f4269a9ba701aa69a797e9bca41aeee5b3a9e79e36b6656927"}, 391 | ] 392 | requests = [ 393 | {file = "requests-2.27.1-py2.py3-none-any.whl", hash = "sha256:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d"}, 394 | {file = "requests-2.27.1.tar.gz", hash = "sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61"}, 395 | ] 396 | text-unidecode = [ 397 | {file = "text-unidecode-1.3.tar.gz", hash = "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93"}, 398 | {file = "text_unidecode-1.3-py2.py3-none-any.whl", hash = "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8"}, 399 | ] 400 | tomli = [ 401 | {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, 402 | {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, 403 | ] 404 | urllib3 = [ 405 | {file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"}, 406 | {file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"}, 407 | ] 408 | websockets = [ 409 | {file = "websockets-10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:38db6e2163b021642d0a43200ee2dec8f4980bdbda96db54fde72b283b54cbfc"}, 410 | {file = "websockets-10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e1b60fd297adb9fc78375778a5220da7f07bf54d2a33ac781319650413fc6a60"}, 411 | {file = "websockets-10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:3477146d1f87ead8df0f27e8960249f5248dceb7c2741e8bbec9aa5338d0c053"}, 412 | {file = "websockets-10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb01ea7b5f52e7125bdc3c5807aeaa2d08a0553979cf2d96a8b7803ea33e15e7"}, 413 | {file = "websockets-10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9fd62c6dc83d5d35fb6a84ff82ec69df8f4657fff05f9cd6c7d9bec0dd57f0f6"}, 414 | {file = "websockets-10.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3bbf080f3892ba1dc8838786ec02899516a9d227abe14a80ef6fd17d4fb57127"}, 415 | {file = "websockets-10.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5560558b0dace8312c46aa8915da977db02738ac8ecffbc61acfbfe103e10155"}, 416 | {file = "websockets-10.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:667c41351a6d8a34b53857ceb8343a45c85d438ee4fd835c279591db8aeb85be"}, 417 | {file = "websockets-10.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:468f0031fdbf4d643f89403a66383247eb82803430b14fa27ce2d44d2662ca37"}, 418 | {file = "websockets-10.1-cp310-cp310-win32.whl", hash = "sha256:d0d81b46a5c87d443e40ce2272436da8e6092aa91f5fbeb60d1be9f11eff5b4c"}, 419 | {file = "websockets-10.1-cp310-cp310-win_amd64.whl", hash = "sha256:b68b6caecb9a0c6db537aa79750d1b592a841e4f1a380c6196091e65b2ad35f9"}, 420 | {file = "websockets-10.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a249139abc62ef333e9e85064c27fefb113b16ffc5686cefc315bdaef3eefbc8"}, 421 | {file = "websockets-10.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8877861e3dee38c8d302eee0d5dbefa6663de3b46dc6a888f70cd7e82562d1f7"}, 422 | {file = "websockets-10.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e3872ae57acd4306ecf937d36177854e218e999af410a05c17168cd99676c512"}, 423 | {file = "websockets-10.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b66e6d514f12c28d7a2d80bb2a48ef223342e99c449782d9831b0d29a9e88a17"}, 424 | {file = "websockets-10.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9f304a22ece735a3da8a51309bc2c010e23961a8f675fae46fdf62541ed62123"}, 425 | {file = "websockets-10.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:189ed478395967d6a98bb293abf04e8815349e17456a0a15511f1088b6cb26e4"}, 426 | {file = "websockets-10.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:08a42856158307e231b199671c4fce52df5786dd3d703f36b5d8ac76b206c485"}, 427 | {file = "websockets-10.1-cp37-cp37m-win32.whl", hash = "sha256:3ef6f73854cded34e78390dbdf40dfdcf0b89b55c0e282468ef92646fce8d13a"}, 428 | {file = "websockets-10.1-cp37-cp37m-win_amd64.whl", hash = "sha256:89e985d40d407545d5f5e2e58e1fdf19a22bd2d8cd54d20a882e29f97e930a0a"}, 429 | {file = "websockets-10.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:002071169d2e44ce8eb9e5ebac9fbce142ba4b5146eef1cfb16b177a27662657"}, 430 | {file = "websockets-10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cfae282c2aa7f0c4be45df65c248481f3509f8c40ca8b15ed96c35668ae0ff69"}, 431 | {file = "websockets-10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:97b4b68a2ddaf5c4707ae79c110bfd874c5be3c6ac49261160fb243fa45d8bbb"}, 432 | {file = "websockets-10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c9407719f42cb77049975410490c58a705da6af541adb64716573e550e5c9db"}, 433 | {file = "websockets-10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1d858fb31e5ac992a2cdf17e874c95f8a5b1e917e1fb6b45ad85da30734b223f"}, 434 | {file = "websockets-10.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7bdd3d26315db0a9cf8a0af30ca95e0aa342eda9c1377b722e71ccd86bc5d1dd"}, 435 | {file = "websockets-10.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e259be0863770cb91b1a6ccf6907f1ac2f07eff0b7f01c249ed751865a70cb0d"}, 436 | {file = "websockets-10.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6b014875fae19577a392372075e937ebfebf53fd57f613df07b35ab210f31534"}, 437 | {file = "websockets-10.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:98de71f86bdb29430fd7ba9997f47a6b10866800e3ea577598a786a785701bb0"}, 438 | {file = "websockets-10.1-cp38-cp38-win32.whl", hash = "sha256:3a02ab91d84d9056a9ee833c254895421a6333d7ae7fff94b5c68e4fa8095519"}, 439 | {file = "websockets-10.1-cp38-cp38-win_amd64.whl", hash = "sha256:7d6673b2753f9c5377868a53445d0c321ef41ff3c8e3b6d57868e72054bfce5f"}, 440 | {file = "websockets-10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ddab2dc69ee5ae27c74dbfe9d7bb6fee260826c136dca257faa1a41d1db61a89"}, 441 | {file = "websockets-10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:14e9cf68a08d1a5d42109549201aefba473b1d925d233ae19035c876dd845da9"}, 442 | {file = "websockets-10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e4819c6fb4f336fd5388372cb556b1f3a165f3f68e66913d1a2fc1de55dc6f58"}, 443 | {file = "websockets-10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05e7f098c76b0a4743716590bb8f9706de19f1ef5148d61d0cf76495ec3edb9c"}, 444 | {file = "websockets-10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5bb6256de5a4fb1d42b3747b4e2268706c92965d75d0425be97186615bf2f24f"}, 445 | {file = "websockets-10.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:888a5fa2a677e0c2b944f9826c756475980f1b276b6302e606f5c4ff5635be9e"}, 446 | {file = "websockets-10.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6fdec1a0b3e5630c58e3d8704d2011c678929fce90b40908c97dfc47de8dca72"}, 447 | {file = "websockets-10.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:531d8eb013a9bc6b3ad101588182aa9b6dd994b190c56df07f0d84a02b85d530"}, 448 | {file = "websockets-10.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0d93b7cadc761347d98da12ec1930b5c71b2096f1ceed213973e3cda23fead9c"}, 449 | {file = "websockets-10.1-cp39-cp39-win32.whl", hash = "sha256:d9b245db5a7e64c95816e27d72830e51411c4609c05673d1ae81eb5d23b0be54"}, 450 | {file = "websockets-10.1-cp39-cp39-win_amd64.whl", hash = "sha256:882c0b8bdff3bf1bd7f024ce17c6b8006042ec4cceba95cf15df57e57efa471c"}, 451 | {file = "websockets-10.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:10edd9d7d3581cfb9ff544ac09fc98cab7ee8f26778a5a8b2d5fd4b0684c5ba5"}, 452 | {file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baa83174390c0ff4fc1304fbe24393843ac7a08fdd59295759c4b439e06b1536"}, 453 | {file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:483edee5abed738a0b6a908025be47f33634c2ad8e737edd03ffa895bd600909"}, 454 | {file = "websockets-10.1-pp37-pypy37_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:816ae7dac2c6522cfa620947ead0ca95ac654916eebf515c94d7c28de5601a6e"}, 455 | {file = "websockets-10.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1dafe98698ece09b8ccba81b910643ff37198e43521d977be76caf37709cf62b"}, 456 | {file = "websockets-10.1.tar.gz", hash = "sha256:181d2b25de5a437b36aefedaf006ecb6fa3aa1328ec0236cdde15f32f9d3ff6d"}, 457 | ] 458 | -------------------------------------------------------------------------------- /poetry.toml: -------------------------------------------------------------------------------- 1 | [virtualenvs] 2 | in-project = true 3 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "nix-playwright-poc" 3 | version = "0.1.0" 4 | description = "Proof of concept for nix-playwright-wiring" 5 | authors = ["Simon Kohlmeyer "] 6 | license = "IDGAF" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.9" 10 | 11 | [tool.poetry.dev-dependencies] 12 | playwright = "^1.22.0" 13 | pytest-playwright = "^0.3.0" 14 | pytest = "^7.1.2" 15 | 16 | [build-system] 17 | requires = ["poetry-core>=1.0.0"] 18 | build-backend = "poetry.core.masonry.api" 19 | -------------------------------------------------------------------------------- /run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | exec nix-shell --run "poetry install && poetry run pytest" 3 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | # confirmed working with rev 934e076a441e318897aa17540f6cf7caadc69028 2 | { pkgs ? import {} }: 3 | 4 | let 5 | # Make sure to adjust data_sha256 when updating this or it will use stale data! 6 | playwright_version = "1.22.0"; 7 | playwright-browsers = import ./nix/playwright-browsers.nix { 8 | inherit playwright_version; 9 | data_sha256 = "sha256:1jbq5xdklw2n8cqxjv912q124wmlqkwv6inlf6qw76x9ns16lv18"; 10 | }; 11 | in 12 | pkgs.mkShell { 13 | packages = [ 14 | pkgs.poetry 15 | pkgs.python 16 | ]; 17 | 18 | "POC_PLAYWRIGHT_VERSION" = playwright_version; 19 | "POC_PLAYWRIGHT_CHROMIUM" = playwright-browsers.chromium; 20 | "POC_PLAYWRIGHT_FIREFOX" = playwright-browsers.firefox; 21 | } 22 | -------------------------------------------------------------------------------- /src/conftest.py: -------------------------------------------------------------------------------- 1 | import os 2 | from typing import Dict, Optional 3 | 4 | import playwright 5 | import pytest 6 | 7 | 8 | @pytest.fixture(scope="session") 9 | def browser_type_launch_args( 10 | browser_name: Optional[str], browser_type_launch_args: Dict 11 | ) -> Dict: 12 | assert browser_name 13 | assert ( 14 | os.environ["POC_PLAYWRIGHT_VERSION"] == playwright._repo_version.version # type: ignore 15 | ), "Mismatched playwright browsers, did you update playwright without fixing the version in shell.nix?" 16 | 17 | env_var = f"POC_PLAYWRIGHT_{browser_name.upper()}" 18 | browser_type_launch_args["executable_path"] = os.environ[env_var] 19 | return browser_type_launch_args 20 | -------------------------------------------------------------------------------- /src/test_playwright_nix.py: -------------------------------------------------------------------------------- 1 | def test_playwright_is_working(page): 2 | page.goto("https://example.com") 3 | assert page.inner_text("h1") == "Example Domain" 4 | page.click("text=More information") 5 | --------------------------------------------------------------------------------