Desktop: tray mini card on left click (store status at a glance next to the tray icon; Refresh runs the main window's Refresh all)

This commit is contained in:
CRTkafa 2026-10-23 11:15:35 +08:00
parent c03938f9dc
commit 60a72e6947
23 changed files with 1052 additions and 11 deletions

View file

@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
use serde_json::{json, Value};
use storebound_desktop::policy::*;
use storebound_desktop::COMMANDS;
use storebound_desktop::{CARD_COMMANDS, COMMANDS};
use url::Url;
fn root() -> PathBuf {
@ -24,9 +24,10 @@ fn u(s: &str) -> Url {
#[test]
fn one_capability_for_the_main_window_with_exactly_the_app_commands() {
let dir = root().join("capabilities");
let files: Vec<PathBuf> = fs::read_dir(&dir).unwrap().map(|e| e.unwrap().path()).collect();
assert_eq!(files.len(), 1, "one capability file expected, found {files:?}");
let cap = read_json(&files[0]);
let mut files: Vec<PathBuf> = fs::read_dir(&dir).unwrap().map(|e| e.unwrap().path()).collect();
files.sort();
assert_eq!(files.len(), 2, "main and card capability files expected, found {files:?}");
let cap = read_json(&dir.join("main.json"));
assert_eq!(cap["identifier"], "main-window");
assert_eq!(cap["windows"], json!(["main"]));
assert!(cap.get("webviews").is_none(), "no webview-scoped grants");
@ -46,6 +47,30 @@ fn one_capability_for_the_main_window_with_exactly_the_app_commands() {
}
}
#[test]
fn the_card_window_gets_only_the_card_commands() {
let cap = read_json(&root().join("capabilities/card.json"));
assert_eq!(cap["identifier"], "card-window");
assert_eq!(cap["windows"], json!(["card"]));
assert!(cap.get("webviews").is_none() && cap.get("remote").is_none());
assert_ne!(cap["local"], json!(false));
let got: BTreeSet<String> = cap["permissions"].as_array().unwrap().iter().map(|p| p.as_str().unwrap().to_string()).collect();
let mut want: BTreeSet<String> = CARD_COMMANDS.iter().map(|c| format!("allow-{}", c.replace('_', "-"))).collect();
want.insert("core:event:allow-listen".into());
want.insert("core:event:allow-unlisten".into());
assert_eq!(got, want);
let cmds = fs::read_to_string(root().join("src/commands.rs")).unwrap();
for c in CARD_COMMANDS {
let at = cmds.find(&format!("pub fn {c}(")).unwrap_or_else(|| panic!("{c} not in commands.rs"));
let body = &cmds[at..];
let open = body.find('{').unwrap();
assert!(body[open + 1..].trim_start().starts_with("guard_card(&window)?;"), "{c} must start with guard_card(&window)?");
}
assert!(require_card("card", &u("tauri://localhost/card.html"), false).is_ok());
assert!(require_card("main", &u("tauri://localhost/"), false).is_err());
assert!(require_main("card", &u("tauri://localhost/card.html"), false).is_err());
}
#[test]
fn every_registered_command_is_in_the_list_and_nothing_else() {
let lib = fs::read_to_string(root().join("src/lib.rs")).unwrap();
@ -56,7 +81,7 @@ fn every_registered_command_is_in_the_list_and_nothing_else() {
.map(|s| s.trim().trim_start_matches("commands::").to_string())
.filter(|s| !s.is_empty())
.collect();
assert_eq!(registered, COMMANDS.iter().map(|s| s.to_string()).collect::<Vec<_>>());
assert_eq!(registered, COMMANDS.iter().chain(CARD_COMMANDS).map(|s| s.to_string()).collect::<Vec<_>>());
// every command checks the caller first
let cmds = fs::read_to_string(root().join("src/commands.rs")).unwrap();
for c in COMMANDS {
@ -84,7 +109,7 @@ fn csp_is_strict_and_the_config_has_no_backdoors() {
assert!(!v.contains("unsafe-eval") && !v.contains("unsafe-inline") && !v.contains('*'), "{k}: {v}");
assert!(!v.contains("https:") && !v.contains("http://") || v == "ipc: http://ipc.localhost", "{k}: no remote sources: {v}");
}
assert_eq!(conf["app"]["security"]["capabilities"], json!(["main-window"]));
assert_eq!(conf["app"]["security"]["capabilities"], json!(["main-window", "card-window"]));
assert_eq!(conf["app"]["security"]["freezePrototype"], json!(true));
assert!(conf["app"].get("withGlobalTauri").is_none() || conf["app"]["withGlobalTauri"] == json!(false));
let windows = conf["app"]["windows"].as_array().unwrap();