import {describe,it,expect} from "vitest"; import {calculate,settingsFrom,validShortcut,relayTarget,htmlGame,accentInk,viewItems} from "../public/hub-model.js"; describe("Launcher preferences",()=>{ it("preserves explicitly disabled intro and motion settings",()=>{expect(settingsFrom({intro:false,motion:false}).intro).toBe(false);expect(settingsFrom({intro:false,motion:false}).motion).toBe(false);}); it("rejects malformed settings without breaking the page",()=>{expect(settingsFrom(null).theme).toBe("dark");expect(settingsFrom({theme:"bad",focusMinutes:-1}).focusMinutes).toBe(25);}); it("ignores unknown setting properties",()=>{expect(settingsFrom({theme:"light",unknown:"value"})).not.toHaveProperty("unknown");}); }); describe("Custom shortcut validation",()=>{ it("accepts HTTPS shortcuts and normalizes their URL",()=>{expect(validShortcut({name:"Example",url:"https://example.com",category:"apps"}).url).toBe("https://example.com/");}); it.each(["javascript:alert(1)","data:text/html,test","http://example.com","https://user:password@example.com"])("rejects unsafe address %s",url=>{expect(()=>validShortcut({name:"Test",url,category:"apps"})).toThrow();}); it("refuses unsupported categories and oversized titles",()=>{expect(()=>validShortcut({name:"Test",url:"https://example.com",category:"other"})).toThrow();expect(()=>validShortcut({name:"x".repeat(49),url:"https://example.com",category:"apps"})).toThrow();}); }); describe("Calculator",()=>{ it.each([["2*(3+4)","14"],["-3*2.5","-7.5"],["0.1+0.2","0.3"],["12÷3+2×4","12"],["10%3","1"],["-(2+3)*4","-20"]])("evaluates %s",(expression,result)=>{expect(calculate(expression)).toBe(result);}); it.each(["1/0","2**4","alert(1)","(1+2","1+","",".constructor"])("rejects %s",expression=>{expect(()=>calculate(expression)).toThrow();}); }); describe("New hub data",()=>{ it("accepts arbitrary hex accents and both icon sheets",()=>{expect(settingsFrom({accentHex:"#12aB9f",iconSet:"2"})).toMatchObject({accentHex:"#12aB9f",iconSet:"2"});}); it("rejects CSS payloads and unknown icon sets",()=>{expect(settingsFrom({accentHex:"red;display:none",iconSet:"remote"})).toMatchObject({accentHex:"",iconSet:"1"});}); }); describe("HTML imports and relay links",()=>{ it("normalizes an HTTPS relay target",()=>expect(relayTarget("https://example.com")).toBe("https://example.com/")); it.each(["javascript:alert(1)","https://user:password@example.com/","https://example.com:8443/","file:///test"])("rejects unsafe relay targets %s",url=>expect(()=>relayTarget(url)).toThrow()); it("preserves HTML as data and generates its own game identifier",()=>{const game=htmlGame({name:"Demo",html:""});expect(game.id).toMatch(/^html-/);expect(game.kind).toBe("html");expect(game.html).toContain("