app
SOLIDWORKS Bridge
Public Made by Adomby adom
Launch, model and quit SOLIDWORKS from any Adom container. Nothing is ever lost.
main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
//! SOLIDWORKS VISUALIZE (0.9.19): started through its platform shortcut, its
//! three measured dialogs told apart by their WORDS, and none of them answered.
mod common;
use common::{Bridge, TempDir};
use serde_json::{json, Value};
fn s(v: &Value) -> String {
serde_json::to_string_pretty(v).unwrap_or_default()
}
const MAIN_CLASS: &str = "HwndWrapper[SLDWORKSVisualize.exe;Main Application;6f1c]";
struct Machine {
dir: TempDir,
lnk: String,
}
impl Machine {
fn new(tag: &str, scenario: Value) -> Machine {
let dir = TempDir::new(tag);
std::fs::write(dir.join("visualize.json"), scenario.to_string()).unwrap();
std::fs::write(dir.join("windows.json"), "[]").unwrap();
let lnk = format!("{}/SOLIDWORKS Visualize.lnk", dir.path());
std::fs::write(&lnk, "a platform shortcut").unwrap();
Machine { dir, lnk }
}
fn boot(&self, ab: Option<&common::AbStub>) -> Bridge {
let mut env: Vec<(&str, &str)> = vec![
("SWX_LAUNCH_FAKE_DIR", Box::leak(self.dir.path().into_boxed_str())),
("SWX_VISUALIZE_SHORTCUT", Box::leak(self.lnk.clone().into_boxed_str())),
];
if let Some(a) = ab {
env.push(("ADOM_DIRECT_API_URL", Box::leak(a.url.clone().into_boxed_str())));
}
Bridge::start_in(TempDir::new("visualize-scratch"), &env)
}
fn invoked_with(&self) -> Option<String> {
std::fs::read_to_string(self.dir.join("visualize-invoked.json"))
.ok()
.and_then(|t| serde_json::from_str::<Value>(&t).ok())
.and_then(|v| v["shortcut"].as_str().map(String::from))
}
}
fn start(b: &Bridge) -> Value {
b.call("swx_start", json!({"app": "visualize", "reason": "Drew wants a Visualize render", "waitSeconds": 3}))
}
#[test]
fn visualize_is_started_through_its_platform_shortcut_and_comes_up_ready() {
let m = Machine::new("viz-ready", json!({"pid": 56488, "startsOnInvoke": true,
"windows": [{"hwnd": 9100, "className": MAIN_CLASS, "title": "SOLIDWORKS Visualize"}]}));
let b = m.boot(None);
let st0 = b.call("swx_status", json!({}));
check!(b, "before: Visualize is not running", st0["visualize"]["running"] == false, s(&st0["visualize"]));
let r = start(&b);
check!(
b,
"launched through the PLATFORM SHORTCUT (never the executable), and ready",
r["success"] == true && r["launchedNow"] == true && r["stage"] == "ready"
&& m.invoked_with().map(|p| p.ends_with("SOLIDWORKS Visualize.lnk")).unwrap_or(false),
format!("{} / invoked with {:?}", s(&r), m.invoked_with())
);
let st = b.call("swx_status", json!({}));
check!(
b,
"swx_status.visualize: running, pid, the HwndWrapper main window, nothing waiting",
st["visualize"]["running"] == true && st["visualize"]["pid"] == 56488 && st["visualize"]["hwnd"] == 9100
&& st["visualize"]["waitingOn"].is_null(),
s(&st["visualize"])
);
let again = start(&b);
check!(b, "a second start launches nothing", again["alreadyRunning"] == true && again["launchedNow"] == false, s(&again));
}
#[test]
fn the_first_run_content_folder_dialog_is_reported_and_never_answered() {
let ab = common::AbStub::start(json!([]));
let m = Machine::new("viz-content", json!({"pid": 56488, "startsOnInvoke": true,
"windows": [{"hwnd": 9101, "className": MAIN_CLASS, "title": "SOLIDWORKS Visualize",
"text": ["Please choose a location for your 'SOLIDWORKS Visualize Content' folder. This folder will be created to provide a place for this software to store data.",
"C:\\Users\\drew\\Documents"],
"buttons": ["...", "Continue"]}]}));
let b = m.boot(Some(&ab));
let r = start(&b);
check!(
b,
"waitingOn userVisualizeContentFolder, with the dialog's text and the prefilled path",
r["stage"] == "waitingOn" && r["waitingOn"] == "userVisualizeContentFolder"
&& r["visualize"]["dialog"]["prefilledPath"] == "C:\\Users\\drew\\Documents"
&& r["visualize"]["dialog"]["text"].as_str().unwrap_or("").contains("Visualize Content"),
s(&r)
);
let st = b.call("swx_status", json!({}));
check!(b, "swx_status says the same", st["visualize"]["waitingOn"] == "userVisualizeContentFolder", s(&st["visualize"]));
// MEASURED 2026-10-05: on first run the dialog IS the main window — the
// block said hwnd:null beside dialog.hwnd. The top-level hwnd is reported
// whenever the process has its HwndWrapper window, dialog or not.
check!(
b,
"the top-level hwnd is the main window even while it shows the dialog (same hwnd)",
st["visualize"]["hwnd"] == 9101 && st["visualize"]["dialog"]["hwnd"] == 9101 && st["visualize"]["ready"] == false,
s(&st["visualize"])
);
check!(
b,
"NOTHING was answered: no click, no text set, no keystroke",
ab.calls_to("desktop_ui_click").is_empty() && ab.calls_to("desktop_ui_set").is_empty()
&& ab.calls_to("desktop_click").is_empty() && ab.calls_to("desktop_type").is_empty()
&& ab.calls_to("desktop_press_key").is_empty(),
format!("{:?}", ab.calls())
);
}
#[test]
fn a_direct_start_notice_is_refused_with_the_fix_and_never_answered() {
let m = Machine::new("viz-direct", json!({"pid": 56488, "runningBefore": true,
"windows": [{"hwnd": 9102, "className": "#32770", "title": "SOLIDWORKS Visualize",
"text": ["SOLIDWORKS Visualize must be opened from the 3DEXPERIENCE Platform or from the desktop shortcut created from the Platform. Do you want to go to the 3DEXPERIENCE Platform?"],
"buttons": ["Yes", "No"]}]}));
let b = m.boot(None);
let r = start(&b);
check!(
b,
"visualize_direct_start_refused, naming the fix (the platform shortcut); nothing invoked",
r["errorCode"] == "visualize_direct_start_refused"
&& r["_hint"].as_str().unwrap_or("").contains("platform shortcut")
&& m.invoked_with().is_none(),
s(&r)
);
}
#[test]
fn a_visualize_login_points_at_the_sign_in_path() {
let m = Machine::new("viz-login", json!({"pid": 56488, "startsOnInvoke": true,
"windows": [{"hwnd": 4262148, "className": "WindowsForms10.Window.8.app.0.141b42a_r6_ad1",
"title": "Login - 3DEXPERIENCE ID - Dassault Systemes"}]}));
let b = m.boot(None);
let r = start(&b);
check!(
b,
"waitingOn userSignIn, pointing at swx_sign_in with window:\"visualize\"",
r["waitingOn"] == "userSignIn" && r["visualize"]["dialog"]["how"].as_str().unwrap_or("").contains("window:\"visualize\""),
s(&r)
);
}
#[test]
fn no_shortcut_means_no_start_and_a_reason_is_required() {
let dir = TempDir::new("viz-none");
std::fs::write(dir.join("visualize.json"), json!({"pid": 56488, "startsOnInvoke": true}).to_string()).unwrap();
let b = Bridge::start_in(TempDir::new("viz-none-scratch"), &[("SWX_LAUNCH_FAKE_DIR", &dir.path())]);
let r = b.call("swx_start", json!({"app": "visualize", "reason": "x"}));
check!(
b,
"no platform shortcut: visualize_shortcut_missing — the executable is never started instead",
r["errorCode"] == "visualize_shortcut_missing" && !dir.join("visualize-invoked.json").exists(),
s(&r)
);
let r = b.call("swx_start", json!({"app": "visualize"}));
check!(b, "a reason is required", r["errorCode"] == "reason_required", s(&r));
let r = b.call("swx_start", json!({"app": "photoshop", "reason": "x"}));
check!(b, "an unknown app is refused", r["errorCode"] == "bad_app", s(&r));
}
/// MEASURED 2026-10-12: after a lapsed platform session, invoking the
/// Visualize shortcut started SWXDesktopUpdate.exe and its 3DEXPERIENCE login
/// (title "3DEXPERIENCE"); Visualize never started; swx_start said
/// stage:"launching" and swx_status said waitingOn:null.
#[test]
fn a_start_parked_behind_the_platform_sign_in_says_so_instead_of_launching() {
let ab = common::AbStub::start(json!([]));
ab.set_response("desktop_ui_tree", json!({"success": true, "nodeCount": 14, "root": {"role": "Window", "name": "3DEXPERIENCE", "automationId": "SWXDesktopUpdateForm"}}));
ab.when("desktop_find_control", json!({"automationId": "username"}),
json!({"success": true, "data": {"found": true, "control": {"role": "Edit", "name": "Email or username", "automationId": "username", "settable": true,
"rect": {"x": 1286, "y": 824, "w": 400, "h": 40, "centerX": 1486, "centerY": 844}}}}));
ab.set_response("desktop_find_control", json!({"success": false, "error": "no control matched"}));
let m = Machine::new("viz-parked", json!({"pid": 56488, "startsOnInvoke": false, "windows": []}));
std::fs::write(m.dir.join("preexisting.json"), json!([{"pid": 61000, "image": "SWXDesktopUpdate.exe", "ageSeconds": 1}]).to_string()).unwrap();
std::fs::write(m.dir.join("sign-in.json"), json!({"hwnd": 857684, "ownerPid": 61000,
"className": "WindowsForms10.Window.8.app.0.30495d1_r6_ad1", "title": "3DEXPERIENCE"}).to_string()).unwrap();
let b = m.boot(Some(&ab));
let started = std::time::Instant::now();
let r = b.call("swx_start", json!({"app": "visualize", "reason": "Drew asked for Visualize", "waitSeconds": 20}));
check!(
b,
"swx_start: stage waitingOn, waitingOn userSignIn, the updater's window named — not \"launching\", and no 20 s wait",
r["stage"] == "waitingOn" && r["waitingOn"] == "userSignIn" && r["signInWindow"]["hwnd"] == 857684
&& r["visualize"]["parkedBehind"].is_string() && started.elapsed() < std::time::Duration::from_secs(15),
s(&r)
);
let st = b.call("swx_status", json!({}));
check!(
b,
"swx_status: visualize.waitingOn userSignIn and the top-level waitingOn too",
st["visualize"]["waitingOn"] == "userSignIn" && st["visualize"]["signInWindow"]["host"] == "desktopUpdate"
&& st["waitingOn"] == "userSignIn" && st["signInWindow"]["host"] == "desktopUpdate",
s(&st)
);
}
/// Any 3DEXPERIENCE login on the machine (here the launcher's, found by its
/// class) is surfaced by swx_start {app:"visualize"} instead of "launching".
#[test]
fn a_start_with_any_login_up_is_not_reported_as_launching() {
let m = Machine::new("viz-parked-launcher", json!({"pid": 56488, "startsOnInvoke": false, "windows": []}));
std::fs::write(m.dir.join("preexisting.json"), json!([{"pid": 70002, "image": "SWXDesktopLauncher.exe", "ageSeconds": 5}]).to_string()).unwrap();
std::fs::write(m.dir.join("sign-in.json"), json!({"hwnd": 1002, "ownerPid": 70002,
"className": "DSYSysLoginPanelWebView2Class", "title": "Login | 3DEXPERIENCE ID | Dassault Systèmes"}).to_string()).unwrap();
let b = m.boot(None);
let r = b.call("swx_start", json!({"app": "visualize", "reason": "Drew asked for Visualize", "waitSeconds": 1}));
check!(
b,
"stage waitingOn, waitingOn userSignIn, the login named",
r["stage"] == "waitingOn" && r["waitingOn"] == "userSignIn" && r["signInWindow"]["hwnd"] == 1002,
s(&r)
);
}