# deck-it - tests for frontend # Copyright (C) 2026 Danilo M. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) import http.client import json import os import socket import tempfile import threading import unittest import deckit.module as mod import deckit.server as srv class UnixConnection(http.client.HTTPConnection): def __init__(self, path): super().__init__("localhost") self.path = path def connect(self): self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.sock.connect(self.path) class Frontend(unittest.TestCase): @classmethod def setUpClass(cls): cls.dir = tempfile.TemporaryDirectory() cls.sock = os.path.join(cls.dir.name, "http.sock") srv.TOKEN = "t" srv.REGISTRY = mod.Registry([]) cls.server = srv.UnixHTTPServer(cls.sock, srv.Handler) threading.Thread(target=cls.server.serve_forever, daemon=True).start() @classmethod def tearDownClass(cls): cls.server.shutdown() cls.server.server_close() cls.dir.cleanup() def get(self, path): conn = UnixConnection(self.sock) conn.request("GET", path, headers={"Host": "127.0.0.1:8765"}) r = conn.getresponse() data = r.read() conn.close() return r.status, data def test_page_references_assets(self): status, body = self.get("/") self.assertEqual(status, 200) text = body.decode() for asset in ("app.js", "deck.css", "vendor/gridstack/gridstack-all.js"): self.assertIn(asset, text) def test_pwa_manifest(self): self.assertIn(b'rel="manifest"', self.get("/")[1]) manifest = json.loads(self.get("/manifest.json")[1]) self.assertEqual(manifest["display"], "fullscreen") self.assertEqual(manifest["start_url"], "./") for size in (192, 512): self.assertEqual(self.get(f"/icon-{size}.png")[0], 200) def test_vendor_assets_served(self): for path in ("/vendor/gridstack/gridstack-all.js", "/vendor/gridstack/gridstack.min.css"): self.assertEqual(self.get(path)[0], 200, path) def test_css_uses_udt_vars(self): status, body = self.get("/deck.css") self.assertEqual(status, 200) self.assertIn(b"--udt-", body) def assert_panel(self, name): status, body = self.get(f"/modules/{name}.js") self.assertEqual(status, 200, name) text = body.decode() for part in ("export default", "mount", "update"): self.assertIn(part, text, name) def test_panel_for_every_module(self): from deckit.modules import build names = [m["name"] for m in build({}).names()] self.assertIn("stats", names) for name in names: self.assert_panel(name) def test_panels_have_their_scoped_wrapper(self): self.assertIn(b'class="music"', self.get("/modules/music.js")[1]) self.assertIn(b'class="sbb"', self.get("/modules/sbbuilder.js")[1]) def test_tabbed_shell(self): self.assertIn(b'id="tabs"', self.get("/")[1]) self.assertIn(b'id="tab-body"', self.get("/")[1]) self.assertIn(b"stepTab", self.get("/app.js")[1]) app = self.get("/app.js")[1] for part in (b"deckit.tab", b"ensureTab", b"showTab"): self.assertIn(part, app) self.assertIn(b'tab: "OBS"', self.get("/modules/obs.js")[1]) self.assertIn(b'tab: "Windows"', self.get("/modules/windows.js")[1]) def test_layout_is_browser_local(self): status, body = self.get("/app.js") self.assertEqual(status, 200) text = body.decode() for part in ("GridStack", "localStorage", "deckit.layout", "gs-id"): self.assertIn(part, text) if __name__ == "__main__": unittest.main()