Compare commits
3 commits
main
...
fix/uninst
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9082357414 | ||
|
|
f6b341bc70 | ||
|
|
2e3d0c151c |
6 changed files with 211 additions and 20 deletions
|
|
@ -3,8 +3,10 @@
|
||||||
#
|
#
|
||||||
# Unsloth Studio uninstaller for Windows PowerShell.
|
# Unsloth Studio uninstaller for Windows PowerShell.
|
||||||
# Stops running servers and removes install dir, launcher data, CLI shim,
|
# Stops running servers and removes install dir, launcher data, CLI shim,
|
||||||
# desktop and Start Menu shortcuts, the user PATH entry, and the PathBackup
|
# desktop and Start Menu shortcuts, the user PATH entry, the PathBackup
|
||||||
# registry key. Honors custom roots set via UNSLOTH_STUDIO_HOME / STUDIO_HOME
|
# registry key, and WebView2 runtime data keyed by the app bundle id
|
||||||
|
# (EBWebView caches created at first desktop-app launch, not by install.ps1).
|
||||||
|
# Honors custom roots set via UNSLOTH_STUDIO_HOME / STUDIO_HOME
|
||||||
# at install time (read back from share\studio.conf).
|
# at install time (read back from share\studio.conf).
|
||||||
#
|
#
|
||||||
# Usage: irm https://raw.githubusercontent.com/unslothai/unsloth/main/scripts/uninstall.ps1 | iex
|
# Usage: irm https://raw.githubusercontent.com/unslothai/unsloth/main/scripts/uninstall.ps1 | iex
|
||||||
|
|
@ -398,6 +400,32 @@ function Uninstall-UnslothStudio {
|
||||||
_RemovePath $defaultUnslothHome
|
_RemovePath $defaultUnslothHome
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# WebView2/app runtime data keyed by the Tauri bundle id. Created at first
|
||||||
|
# desktop-app launch, not by install.ps1. LOCALAPPDATA holds the EBWebView
|
||||||
|
# profile (a leftover copy serves a stale frontend to the next install);
|
||||||
|
# APPDATA holds the app config dir.
|
||||||
|
_Step "Removing WebView caches and app data (ai.unsloth.studio)..."
|
||||||
|
$bundleId = "ai.unsloth.studio"
|
||||||
|
$webviewDataDirs = @()
|
||||||
|
if ($env:LOCALAPPDATA) { $webviewDataDirs += Join-Path $env:LOCALAPPDATA $bundleId }
|
||||||
|
if ($env:APPDATA) { $webviewDataDirs += Join-Path $env:APPDATA $bundleId }
|
||||||
|
# Stop the desktop app and any msedgewebview2.exe helper using these dirs
|
||||||
|
# first; WebView2 keeps open handles that make the delete fail.
|
||||||
|
try { Stop-Process -Name "unsloth-studio" -Force -ErrorAction SilentlyContinue } catch { }
|
||||||
|
try {
|
||||||
|
foreach ($proc in (Get-CimInstance Win32_Process -Filter "Name = 'msedgewebview2.exe'" -ErrorAction SilentlyContinue)) {
|
||||||
|
$cl = $proc.CommandLine
|
||||||
|
if (-not $cl) { continue }
|
||||||
|
foreach ($d in $webviewDataDirs) {
|
||||||
|
if ($cl -ilike "*$d*") {
|
||||||
|
try { Stop-Process -Id $proc.ProcessId -Force -ErrorAction SilentlyContinue } catch { }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch { }
|
||||||
|
foreach ($d in $webviewDataDirs) { _RemovePath $d }
|
||||||
|
|
||||||
# ── Remove desktop and Start Menu shortcuts ──
|
# ── Remove desktop and Start Menu shortcuts ──
|
||||||
_Step "Removing desktop and Start Menu shortcuts..."
|
_Step "Removing desktop and Start Menu shortcuts..."
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@
|
||||||
#
|
#
|
||||||
# Unsloth Studio uninstaller (macOS / Linux / WSL).
|
# Unsloth Studio uninstaller (macOS / Linux / WSL).
|
||||||
# Stops running servers and removes install dir, launcher data,
|
# Stops running servers and removes install dir, launcher data,
|
||||||
# CLI shim, desktop shortcut, .app bundle, and Launch Services entry.
|
# CLI shim, desktop shortcut, .app bundle, Launch Services entry, and
|
||||||
|
# WebView runtime data keyed by the app bundle id (WebKit/webkit2gtk
|
||||||
|
# caches created at first app launch, not by install.sh).
|
||||||
# Honors custom roots set via UNSLOTH_STUDIO_HOME / STUDIO_HOME at
|
# Honors custom roots set via UNSLOTH_STUDIO_HOME / STUDIO_HOME at
|
||||||
# install time (read back from studio.conf).
|
# install time (read back from studio.conf).
|
||||||
#
|
#
|
||||||
|
|
@ -80,6 +82,13 @@ $_roots_from_conf"
|
||||||
pkill -KILL -f "$_pat" 2>/dev/null || true
|
pkill -KILL -f "$_pat" 2>/dev/null || true
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Tauri desktop app (binary "unsloth-studio"): stop it so its WebView
|
||||||
|
# helpers can't hold or re-create the runtime caches removed below.
|
||||||
|
# -x is an exact name match, so the "unsloth" CLI shim is never touched.
|
||||||
|
pkill -TERM -x unsloth-studio 2>/dev/null || true
|
||||||
|
sleep 0.5
|
||||||
|
pkill -KILL -x unsloth-studio 2>/dev/null || true
|
||||||
}
|
}
|
||||||
|
|
||||||
_remove_path() {
|
_remove_path() {
|
||||||
|
|
@ -261,6 +270,25 @@ case "$_os" in
|
||||||
if [ -x "$_lsr" ]; then
|
if [ -x "$_lsr" ]; then
|
||||||
"$_lsr" -u "$HOME/Applications/Unsloth Studio.app" 2>/dev/null || true
|
"$_lsr" -u "$HOME/Applications/Unsloth Studio.app" 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
# WebView/app runtime data keyed by the bundle id. Created by macOS +
|
||||||
|
# WKWebView at first app launch, not by install.sh, so it survives an
|
||||||
|
# uninstall and a leftover WebKit cache then serves a stale frontend
|
||||||
|
# to the next install.
|
||||||
|
_bid="ai.unsloth.studio"
|
||||||
|
echo "Removing WebView caches and app data ($_bid)..."
|
||||||
|
_remove_path "$HOME/Library/Caches/$_bid"
|
||||||
|
_remove_path "$HOME/Library/WebKit/$_bid"
|
||||||
|
_remove_path "$HOME/Library/Application Support/$_bid"
|
||||||
|
_remove_path "$HOME/Library/HTTPStorages/$_bid"
|
||||||
|
_remove_path "$HOME/Library/HTTPStorages/$_bid.binarycookies"
|
||||||
|
_remove_path "$HOME/Library/Cookies/$_bid.binarycookies"
|
||||||
|
_remove_path "$HOME/Library/Saved Application State/$_bid.savedState"
|
||||||
|
# Delete via defaults first: cfprefsd caches plists in memory and can
|
||||||
|
# re-write the file after a bare rm.
|
||||||
|
if command -v defaults >/dev/null 2>&1; then
|
||||||
|
defaults delete "$_bid" >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
_remove_path "$HOME/Library/Preferences/$_bid.plist"
|
||||||
;;
|
;;
|
||||||
Linux)
|
Linux)
|
||||||
if [ "$_is_wsl" = "1" ]; then
|
if [ "$_is_wsl" = "1" ]; then
|
||||||
|
|
@ -403,6 +431,15 @@ case "$_os" in
|
||||||
echo " sudo rm -rf /opt/rocm /opt/rocm-* && sudo ldconfig"
|
echo " sudo rm -rf /opt/rocm /opt/rocm-* && sudo ldconfig"
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
# WebView/app runtime data keyed by the Tauri bundle id. Created by
|
||||||
|
# webkit2gtk at first desktop-app launch, not by install.sh; a
|
||||||
|
# leftover cache serves a stale frontend to the next install.
|
||||||
|
_bid="ai.unsloth.studio"
|
||||||
|
echo "Removing WebView caches and app data ($_bid)..."
|
||||||
|
_remove_path "${XDG_CACHE_HOME:-$HOME/.cache}/$_bid"
|
||||||
|
_remove_path "${XDG_DATA_HOME:-$HOME/.local/share}/$_bid"
|
||||||
|
_remove_path "${XDG_CONFIG_HOME:-$HOME/.config}/$_bid"
|
||||||
|
_remove_path "${XDG_STATE_HOME:-$HOME/.local/state}/$_bid"
|
||||||
echo "Removing Linux .desktop entry..."
|
echo "Removing Linux .desktop entry..."
|
||||||
_remove_path "$HOME/.local/share/applications/unsloth-studio.desktop"
|
_remove_path "$HOME/.local/share/applications/unsloth-studio.desktop"
|
||||||
if command -v update-desktop-database >/dev/null 2>&1; then
|
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ sh "$TESTS_DIR/sh/test_nvcc_meets_llama_minimum.sh"
|
||||||
sh "$TESTS_DIR/sh/test_resolve_cuda_archs.sh"
|
sh "$TESTS_DIR/sh/test_resolve_cuda_archs.sh"
|
||||||
sh "$TESTS_DIR/sh/test_strixhalo_wsl_reroute.sh"
|
sh "$TESTS_DIR/sh/test_strixhalo_wsl_reroute.sh"
|
||||||
sh "$TESTS_DIR/sh/test_uninstall_shared_icon.sh"
|
sh "$TESTS_DIR/sh/test_uninstall_shared_icon.sh"
|
||||||
|
sh "$TESTS_DIR/sh/test_uninstall_webview_data.sh"
|
||||||
sh "$TESTS_DIR/sh/test_torch_flavor.sh"
|
sh "$TESTS_DIR/sh/test_torch_flavor.sh"
|
||||||
sh "$TESTS_DIR/sh/test_redact_install_output.sh"
|
sh "$TESTS_DIR/sh/test_redact_install_output.sh"
|
||||||
sh "$TESTS_DIR/sh/test_install_uv_override_space.sh"
|
sh "$TESTS_DIR/sh/test_install_uv_override_space.sh"
|
||||||
|
|
|
||||||
129
tests/sh/test_uninstall_webview_data.sh
Executable file
129
tests/sh/test_uninstall_webview_data.sh
Executable file
|
|
@ -0,0 +1,129 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
# Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
||||||
|
# Regression tests for WebView runtime-data cleanup in scripts/uninstall.sh.
|
||||||
|
#
|
||||||
|
# WKWebView (macOS) and webkit2gtk (Linux) create data keyed by the bundle id
|
||||||
|
# at first app launch, not at install time, so the uninstaller used to miss it
|
||||||
|
# and a leftover cache served a stale frontend to the next install. Runs the
|
||||||
|
# full script against a fixture HOME (pkill/defaults stubbed via PATH, OS
|
||||||
|
# branch picked by a stubbed uname, /proc/version WSL probe force-failed) and
|
||||||
|
# asserts bundle-id paths are removed while unrelated app data survives.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
UNINSTALL_SH="$SCRIPT_DIR/../../scripts/uninstall.sh"
|
||||||
|
BID="ai.unsloth.studio"
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
_TMP_ROOT=$(mktemp -d)
|
||||||
|
trap 'rm -rf "$_TMP_ROOT"' EXIT
|
||||||
|
|
||||||
|
assert_gone() { _l="$1"; if [ -e "$2" ]; then echo " FAIL: $_l (still present: $2)"; FAIL=$((FAIL+1)); else echo " PASS: $_l"; PASS=$((PASS+1)); fi; }
|
||||||
|
assert_present() { _l="$1"; if [ -e "$2" ]; then echo " PASS: $_l"; PASS=$((PASS+1)); else echo " FAIL: $_l (missing: $2)"; FAIL=$((FAIL+1)); fi; }
|
||||||
|
|
||||||
|
# Stub out process kills and macOS pref/LaunchServices tools so the script can
|
||||||
|
# run against a fixture HOME without touching the real system.
|
||||||
|
STUB_BIN="$_TMP_ROOT/stubbin"
|
||||||
|
mkdir -p "$STUB_BIN"
|
||||||
|
for _tool in pkill defaults; do
|
||||||
|
printf '#!/bin/sh\nexit 0\n' > "$STUB_BIN/$_tool"
|
||||||
|
chmod +x "$STUB_BIN/$_tool"
|
||||||
|
done
|
||||||
|
# Force the non-WSL path: with uname stubbed to Linux on a WSL host, the
|
||||||
|
# script's `grep -qi microsoft /proc/version` probe would still fire and the
|
||||||
|
# real WSL cleanup would touch the host's /mnt/* shortcuts and /etc profile.
|
||||||
|
# Fail that one probe; delegate every other grep call to the real grep.
|
||||||
|
# REAL_GREP must be an absolute path: a bare "grep" (e.g. from an alias-shaped
|
||||||
|
# `command -v` result) would resolve back to this stub and self-exec forever.
|
||||||
|
REAL_GREP=$(command -v grep)
|
||||||
|
case "$REAL_GREP" in /*) ;; *) REAL_GREP=/usr/bin/grep ;; esac
|
||||||
|
cat > "$STUB_BIN/grep" <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
for _a in "\$@"; do
|
||||||
|
[ "\$_a" = "/proc/version" ] && exit 1
|
||||||
|
done
|
||||||
|
exec "$REAL_GREP" "\$@"
|
||||||
|
EOF
|
||||||
|
chmod +x "$STUB_BIN/grep"
|
||||||
|
# Belt and braces should the WSL branch ever be entered anyway: powershell.exe
|
||||||
|
# exiting 0 makes its no-op path taken (and skips the /mnt/* drvfs fallback);
|
||||||
|
# sudo exiting 0 without running its argv keeps /etc untouched.
|
||||||
|
for _tool in powershell.exe sudo; do
|
||||||
|
printf '#!/bin/sh\nexit 0\n' > "$STUB_BIN/$_tool"
|
||||||
|
chmod +x "$STUB_BIN/$_tool"
|
||||||
|
done
|
||||||
|
|
||||||
|
# run_uninstall <home> <uname_output> : run the full script with a stubbed OS.
|
||||||
|
run_uninstall() {
|
||||||
|
printf '#!/bin/sh\necho %s\n' "$2" > "$STUB_BIN/uname"
|
||||||
|
chmod +x "$STUB_BIN/uname"
|
||||||
|
env -u UNSLOTH_STUDIO_HOME -u STUDIO_HOME \
|
||||||
|
-u XDG_CACHE_HOME -u XDG_DATA_HOME -u XDG_CONFIG_HOME -u XDG_STATE_HOME \
|
||||||
|
HOME="$1" PATH="$STUB_BIN:$PATH" sh "$UNINSTALL_SH" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── 1. macOS: every bundle-id-keyed ~/Library path is removed ──
|
||||||
|
H=$(mktemp -d -p "$_TMP_ROOT")
|
||||||
|
mkdir -p "$H/Library/Caches/$BID/WebKit/NetworkCache" \
|
||||||
|
"$H/Library/WebKit/$BID/WebsiteData/CacheStorage" \
|
||||||
|
"$H/Library/Application Support/$BID" \
|
||||||
|
"$H/Library/HTTPStorages/$BID" \
|
||||||
|
"$H/Library/Saved Application State/$BID.savedState" \
|
||||||
|
"$H/Library/Preferences" \
|
||||||
|
"$H/Library/Cookies" \
|
||||||
|
"$H/Library/Caches/com.other.app"
|
||||||
|
: > "$H/Library/HTTPStorages/$BID.binarycookies"
|
||||||
|
: > "$H/Library/Cookies/$BID.binarycookies"
|
||||||
|
: > "$H/Library/Preferences/$BID.plist"
|
||||||
|
: > "$H/Library/Caches/$BID/stale-frontend.js"
|
||||||
|
: > "$H/Library/Caches/com.other.app/keepme"
|
||||||
|
run_uninstall "$H" Darwin
|
||||||
|
assert_gone "macOS: Caches/$BID removed" "$H/Library/Caches/$BID"
|
||||||
|
assert_gone "macOS: WebKit/$BID removed" "$H/Library/WebKit/$BID"
|
||||||
|
assert_gone "macOS: Application Support/$BID removed" "$H/Library/Application Support/$BID"
|
||||||
|
assert_gone "macOS: HTTPStorages/$BID removed" "$H/Library/HTTPStorages/$BID"
|
||||||
|
assert_gone "macOS: HTTPStorages/$BID.binarycookies removed" "$H/Library/HTTPStorages/$BID.binarycookies"
|
||||||
|
assert_gone "macOS: Cookies/$BID.binarycookies removed" "$H/Library/Cookies/$BID.binarycookies"
|
||||||
|
assert_gone "macOS: Saved Application State removed" "$H/Library/Saved Application State/$BID.savedState"
|
||||||
|
assert_gone "macOS: Preferences/$BID.plist removed" "$H/Library/Preferences/$BID.plist"
|
||||||
|
assert_present "macOS: unrelated app cache kept" "$H/Library/Caches/com.other.app/keepme"
|
||||||
|
|
||||||
|
# ── 2. Linux: bundle-id-keyed XDG default paths are removed ──
|
||||||
|
H=$(mktemp -d -p "$_TMP_ROOT")
|
||||||
|
mkdir -p "$H/.cache/$BID" "$H/.local/share/$BID" "$H/.config/$BID" \
|
||||||
|
"$H/.local/state/$BID" "$H/.cache/other.app"
|
||||||
|
run_uninstall "$H" Linux
|
||||||
|
assert_gone "linux: ~/.cache/$BID removed" "$H/.cache/$BID"
|
||||||
|
assert_gone "linux: ~/.local/share/$BID removed" "$H/.local/share/$BID"
|
||||||
|
assert_gone "linux: ~/.config/$BID removed" "$H/.config/$BID"
|
||||||
|
assert_gone "linux: ~/.local/state/$BID removed" "$H/.local/state/$BID"
|
||||||
|
assert_present "linux: unrelated app cache kept" "$H/.cache/other.app"
|
||||||
|
|
||||||
|
# ── 3. Linux: XDG_*_HOME overrides are honored ──
|
||||||
|
H=$(mktemp -d -p "$_TMP_ROOT")
|
||||||
|
XDG=$(mktemp -d -p "$_TMP_ROOT")
|
||||||
|
mkdir -p "$XDG/cache/$BID" "$XDG/data/$BID" "$XDG/config/$BID" "$XDG/state/$BID"
|
||||||
|
printf '#!/bin/sh\necho Linux\n' > "$STUB_BIN/uname"
|
||||||
|
chmod +x "$STUB_BIN/uname"
|
||||||
|
env -u UNSLOTH_STUDIO_HOME -u STUDIO_HOME \
|
||||||
|
XDG_CACHE_HOME="$XDG/cache" XDG_DATA_HOME="$XDG/data" \
|
||||||
|
XDG_CONFIG_HOME="$XDG/config" XDG_STATE_HOME="$XDG/state" \
|
||||||
|
HOME="$H" PATH="$STUB_BIN:$PATH" sh "$UNINSTALL_SH" >/dev/null 2>&1
|
||||||
|
assert_gone "linux: XDG_CACHE_HOME override honored" "$XDG/cache/$BID"
|
||||||
|
assert_gone "linux: XDG_DATA_HOME override honored" "$XDG/data/$BID"
|
||||||
|
assert_gone "linux: XDG_CONFIG_HOME override honored" "$XDG/config/$BID"
|
||||||
|
assert_gone "linux: XDG_STATE_HOME override honored" "$XDG/state/$BID"
|
||||||
|
|
||||||
|
# ── 4. Nothing to remove is a clean no-op (fresh HOME, exit 0) ──
|
||||||
|
H=$(mktemp -d -p "$_TMP_ROOT")
|
||||||
|
if run_uninstall "$H" Darwin; then
|
||||||
|
echo " PASS: empty HOME -> no-op exit 0"; PASS=$((PASS+1))
|
||||||
|
else
|
||||||
|
echo " FAIL: empty HOME -> nonzero exit"; FAIL=$((FAIL+1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Results: $PASS passed, $FAIL failed"
|
||||||
|
[ "$FAIL" = 0 ]
|
||||||
|
|
@ -38,7 +38,11 @@ def fail(m):
|
||||||
raise AssertionError(f"[font-scale] FAIL: {m}")
|
raise AssertionError(f"[font-scale] FAIL: {m}")
|
||||||
|
|
||||||
|
|
||||||
def near(a, b, tol = 0.35):
|
def near(
|
||||||
|
a,
|
||||||
|
b,
|
||||||
|
tol = 0.35,
|
||||||
|
):
|
||||||
return a is not None and b is not None and abs(a - b) <= tol
|
return a is not None and b is not None and abs(a - b) <= tol
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -86,9 +90,7 @@ def open_appearance(page):
|
||||||
page.wait_for_timeout(700)
|
page.wait_for_timeout(700)
|
||||||
if page.get_by_role("dialog").count() == 0:
|
if page.get_by_role("dialog").count() == 0:
|
||||||
fail("settings dialog did not open")
|
fail("settings dialog did not open")
|
||||||
page.get_by_role("dialog").get_by_role("button").filter(
|
page.get_by_role("dialog").get_by_role("button").filter(has_text = "Appearance").first.click()
|
||||||
has_text = "Appearance"
|
|
||||||
).first.click()
|
|
||||||
page.wait_for_timeout(600)
|
page.wait_for_timeout(600)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -153,9 +155,7 @@ def main():
|
||||||
page.wait_for_timeout(400)
|
page.wait_for_timeout(400)
|
||||||
|
|
||||||
step("overflowing select scrolls its Radix viewport")
|
step("overflowing select scrolls its Radix viewport")
|
||||||
page.get_by_role("dialog").get_by_role("button").filter(
|
page.get_by_role("dialog").get_by_role("button").filter(has_text = "Voice").first.click()
|
||||||
has_text = "Voice"
|
|
||||||
).first.click()
|
|
||||||
page.wait_for_timeout(600)
|
page.wait_for_timeout(600)
|
||||||
page.set_viewport_size({"width": 1440, "height": 480})
|
page.set_viewport_size({"width": 1440, "height": 480})
|
||||||
page.locator("[aria-label='Dictation language']").click()
|
page.locator("[aria-label='Dictation language']").click()
|
||||||
|
|
@ -194,9 +194,7 @@ def main():
|
||||||
page.wait_for_timeout(400)
|
page.wait_for_timeout(400)
|
||||||
|
|
||||||
step("default restores exactly")
|
step("default restores exactly")
|
||||||
page.get_by_role("dialog").get_by_role("button").filter(
|
page.get_by_role("dialog").get_by_role("button").filter(has_text = "Appearance").first.click()
|
||||||
has_text = "Appearance"
|
|
||||||
).first.click()
|
|
||||||
page.wait_for_timeout(500)
|
page.wait_for_timeout(500)
|
||||||
set_input(page, "UI font size", DEFAULT)
|
set_input(page, "UI font size", DEFAULT)
|
||||||
final = measure(page)
|
final = measure(page)
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,7 @@ from pathlib import Path
|
||||||
REPO = Path(__file__).resolve().parents[2]
|
REPO = Path(__file__).resolve().parents[2]
|
||||||
SRC = REPO / "studio/frontend/src"
|
SRC = REPO / "studio/frontend/src"
|
||||||
INDEX_CSS = (SRC / "index.css").read_text(encoding = "utf-8")
|
INDEX_CSS = (SRC / "index.css").read_text(encoding = "utf-8")
|
||||||
STORE = (SRC / "features/settings/stores/appearance-custom-store.ts").read_text(
|
STORE = (SRC / "features/settings/stores/appearance-custom-store.ts").read_text(encoding = "utf-8")
|
||||||
encoding = "utf-8"
|
|
||||||
)
|
|
||||||
SELECT = (SRC / "components/ui/select.tsx").read_text(encoding = "utf-8")
|
SELECT = (SRC / "components/ui/select.tsx").read_text(encoding = "utf-8")
|
||||||
|
|
||||||
# Raw numeric fontSize props are only allowed where a scaled stylesheet rule
|
# Raw numeric fontSize props are only allowed where a scaled stylesheet rule
|
||||||
|
|
@ -70,9 +68,7 @@ def test_ui_token_families_exist():
|
||||||
|
|
||||||
|
|
||||||
def test_explicit_code_font_size_is_never_multiplied():
|
def test_explicit_code_font_size_is_never_multiplied():
|
||||||
match = re.search(
|
match = re.search(r"html\[data-code-font-size\][^{]*\{([^}]*)\}", INDEX_CSS)
|
||||||
r"html\[data-code-font-size\][^{]*\{([^}]*)\}", INDEX_CSS
|
|
||||||
)
|
|
||||||
assert match is not None
|
assert match is not None
|
||||||
body = match.group(1)
|
body = match.group(1)
|
||||||
assert "var(--custom-code-font-size)" in body
|
assert "var(--custom-code-font-size)" in body
|
||||||
|
|
@ -83,7 +79,9 @@ def test_radix_select_viewport_owns_the_scroll_state():
|
||||||
viewport = SELECT[SELECT.index("SelectPrimitive.Viewport") :]
|
viewport = SELECT[SELECT.index("SelectPrimitive.Viewport") :]
|
||||||
assert "overflow-y-auto" in viewport.split("</SelectPrimitive.Viewport>")[0]
|
assert "overflow-y-auto" in viewport.split("</SelectPrimitive.Viewport>")[0]
|
||||||
# The rounded surface itself must not scroll (WebKit squares its corners).
|
# The rounded surface itself must not scroll (WebKit squares its corners).
|
||||||
content_cls = re.search(r"SelectPrimitive\.Content[\s\S]*?className=\{cn\(\s*\"([^\"]+)\"", SELECT)
|
content_cls = re.search(
|
||||||
|
r"SelectPrimitive\.Content[\s\S]*?className=\{cn\(\s*\"([^\"]+)\"", SELECT
|
||||||
|
)
|
||||||
assert content_cls is not None
|
assert content_cls is not None
|
||||||
assert "overflow-hidden" in content_cls.group(1)
|
assert "overflow-hidden" in content_cls.group(1)
|
||||||
assert "overflow-y-auto" not in content_cls.group(1)
|
assert "overflow-y-auto" not in content_cls.group(1)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue