"""The password meter and "Copy all", in a real browser. The meter is a word and a bar from zxcvbn, guidance only: a weak password can still be saved. "Copy all" has to work on plain HTTP, where the clipboard API does not exist, which is how test users ran it. """ from __future__ import annotations import json import re import subprocess import sys from pathlib import Path import pytest from test_routes import make_login, session_csrf ROOT = Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT)) from tools.browser import find_chrome # noqa: E402 pytestmark = pytest.mark.visual CHROME = find_chrome() if CHROME is None: pytest.skip("no headless Chrome on this machine", allow_module_level=True) HTML = {"accept": "text/html"} STATIC = ROOT / "app" / "static" ZXCVBN = (STATIC / "vendor" / "zxcvbn.js").read_text(encoding="utf-8") def _standalone(page: str, driver: str) -> str: """The page with its stylesheet and scripts inlined, for `file://`.""" def script(match: re.Match) -> str: path = STATIC / match.group(1) if match.group(1) == "idle.js" or not path.is_file(): return "" return "" page = re.sub(r'', script, page) page = re.sub(r']*>', lambda _m: "", page) return page.replace("", driver + "") RESULT = """ var pre = document.createElement("pre"); pre.id = "result"; pre.textContent = JSON.stringify(out); document.body.appendChild(pre); """ METER = """ """ COPY = """ """ def _run(page: str, driver: str, tmp_path: Path, head: str = "") -> dict: target = tmp_path / "page.html" target.write_text(_standalone(page, driver).replace("", "" + head, 1), encoding="utf-8") dom = subprocess.run( [CHROME, "--headless", "--disable-gpu", "--dump-dom", "--no-sandbox", "--allow-file-access-from-files", "--window-size=1200,1000", "--virtual-time-budget=8000", target.as_uri()], capture_output=True, text=True, timeout=90).stdout found = re.search(r'
(.*?)
', dom, re.S) assert found, "the driver did not finish" return json.loads(found.group(1).replace(""", '"')) def test_the_meter_says_how_strong_and_never_stops_a_save(client, session_factory, tmp_path): make_login(client, session_factory) resp = client.post("/members/invite", follow_redirects=False, data={"_csrf": session_csrf(session_factory), "role": "member"}) token = re.search(r"/invite/([A-Za-z0-9_-]+)", resp.text).group(1) client.post("/logout", data={"_csrf": session_csrf(session_factory)}, follow_redirects=False) page = client.get(f"/invite/{token}", headers=HTML).text # zxcvbn is inlined: the page would load it from /static on first input. out = _run(page, METER, tmp_path, head="") assert out["before"] == "", "nothing to say before anything is typed" assert out["password"] == "Very weak" # The app's name and the person's own email count against it. assert out["ownWords"] == "Weak" assert out["passphrase"] == "Very strong" assert out["stopped"] == [False] def test_copy_all_works_without_the_clipboard_api(client, session_factory, tmp_path): make_login(client, session_factory) page = client.post("/profile/recovery", data={"_csrf": session_csrf(session_factory)}, headers=HTML).text out = _run(page, COPY, tmp_path) assert out["label"] == "Copy all" assert out["copied"] == "\n".join(out["codes"]) and len(out["codes"]) >= 8 assert out["after"] == "Copied"