first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -0,0 +1,328 @@
|
||||
use ai::LLMId;
|
||||
use galaxy_core::features::FeatureFlag;
|
||||
use galaxy_core::telemetry::testing::MockTelemetryContextProvider;
|
||||
use galaxyui_core::{App, ModelHandle};
|
||||
|
||||
use crate::model::{
|
||||
AiSetupChoice, NoAiConfirmationSource, OnboardingAuthState, OnboardingStateModel,
|
||||
OnboardingStep, SelectedSettings,
|
||||
};
|
||||
use crate::OnboardingIntention;
|
||||
|
||||
fn add_test_model(app: &mut App) -> ModelHandle<OnboardingStateModel> {
|
||||
app.update(MockTelemetryContextProvider::register);
|
||||
app.add_model(|_| {
|
||||
OnboardingStateModel::new(
|
||||
Vec::new(),
|
||||
LLMId::from("auto"),
|
||||
false,
|
||||
true,
|
||||
OnboardingAuthState::FreeUser,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn step(app: &App, model: &ModelHandle<OnboardingStateModel>) -> OnboardingStep {
|
||||
model.read(app, |model, _| model.step())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_path_routes_through_ai_setup() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
|
||||
// Default intention is agent-driven development.
|
||||
model.update(&mut app, |model, ctx| model.next(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Intention);
|
||||
model.update(&mut app, |model, ctx| model.next(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::AiSetup);
|
||||
|
||||
// The default AI setup choice is the Warp agent.
|
||||
model.update(&mut app, |model, ctx| model.next(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Agent);
|
||||
model.update(&mut app, |model, ctx| model.next(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::AiAccess);
|
||||
model.update(&mut app, |model, ctx| model.next(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Customize);
|
||||
model.update(&mut app, |model, ctx| model.next(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::ThemePicker);
|
||||
|
||||
// Back navigation mirrors the forward path.
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Customize);
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::AiAccess);
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Agent);
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::AiSetup);
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Intention);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn third_party_choice_routes_to_third_party_slide() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
|
||||
model.update(&mut app, |model, ctx| {
|
||||
model.next(ctx); // Intro → Intention
|
||||
model.next(ctx); // Intention → AiSetup
|
||||
model.set_ai_setup_choice(AiSetupChoice::ThirdParty, ctx);
|
||||
model.next(ctx); // AiSetup → ThirdParty
|
||||
});
|
||||
assert_eq!(step(&app, &model), OnboardingStep::ThirdParty);
|
||||
|
||||
model.update(&mut app, |model, ctx| model.next(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Customize);
|
||||
|
||||
// Back from Customize returns to the chosen AI-setup slide.
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::ThirdParty);
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::AiSetup);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirm_no_ai_switches_to_terminal_path() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
|
||||
model.update(&mut app, |model, ctx| {
|
||||
model.next(ctx); // Intro → Intention
|
||||
model.set_intention_terminal(ctx);
|
||||
model.request_no_ai_confirmation(NoAiConfirmationSource::Intention, ctx);
|
||||
});
|
||||
|
||||
// The confirmation modal is shown without leaving the intention slide yet.
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Intention);
|
||||
model.read(&app, |model, _| {
|
||||
assert_eq!(
|
||||
model.no_ai_confirmation(),
|
||||
Some(NoAiConfirmationSource::Intention)
|
||||
);
|
||||
});
|
||||
|
||||
// Confirming "I don't want AI" lands on the terminal path, never a dead end.
|
||||
model.update(&mut app, |model, ctx| model.confirm_no_ai(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Customize);
|
||||
model.read(&app, |model, _| {
|
||||
assert_eq!(model.no_ai_confirmation(), None);
|
||||
assert_eq!(*model.intention(), OnboardingIntention::Terminal);
|
||||
assert!(!model.settings().is_ai_enabled());
|
||||
});
|
||||
|
||||
// The terminal path continues to completion, skipping the third-party slide.
|
||||
model.update(&mut app, |model, ctx| model.next(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::ThemePicker);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirm_no_ai_from_intention_then_back_returns_to_intention() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
|
||||
model.update(&mut app, |model, ctx| {
|
||||
model.next(ctx); // Intro → Intention
|
||||
model.set_intention_terminal(ctx);
|
||||
model.request_no_ai_confirmation(NoAiConfirmationSource::Intention, ctx);
|
||||
});
|
||||
|
||||
// "Just use the terminal" + Next does not advance until the user confirms.
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Intention);
|
||||
|
||||
model.update(&mut app, |model, ctx| model.confirm_no_ai(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Customize);
|
||||
|
||||
// Back from Customize goes to the intention fork, not the AI-setup slide.
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Intention);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_no_ai_from_intention_routes_to_ai_setup() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
|
||||
model.update(&mut app, |model, ctx| {
|
||||
model.next(ctx); // Intro → Intention
|
||||
model.set_intention_terminal(ctx);
|
||||
model.request_no_ai_confirmation(NoAiConfirmationSource::Intention, ctx);
|
||||
});
|
||||
|
||||
// "Give me AI features" switches onto the AI path and opens the AI-setup slide.
|
||||
model.update(&mut app, |model, ctx| model.cancel_no_ai(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::AiSetup);
|
||||
model.read(&app, |model, _| {
|
||||
assert_eq!(model.no_ai_confirmation(), None);
|
||||
assert_eq!(
|
||||
*model.intention(),
|
||||
OnboardingIntention::AgentDrivenDevelopment
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dismiss_no_ai_closes_without_changing_path() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
|
||||
model.update(&mut app, |model, ctx| {
|
||||
model.next(ctx); // Intro → Intention
|
||||
model.set_intention_terminal(ctx);
|
||||
model.request_no_ai_confirmation(NoAiConfirmationSource::Intention, ctx);
|
||||
model.dismiss_no_ai(ctx);
|
||||
});
|
||||
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Intention);
|
||||
model.read(&app, |model, _| {
|
||||
assert_eq!(model.no_ai_confirmation(), None);
|
||||
assert_eq!(*model.intention(), OnboardingIntention::Terminal);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminal_settings_disable_ai() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
model.update(&mut app, |model, ctx| model.set_intention_terminal(ctx));
|
||||
model.read(&app, |model, _| {
|
||||
assert!(matches!(
|
||||
model.settings(),
|
||||
SelectedSettings::Terminal { .. }
|
||||
));
|
||||
assert!(!model.settings().is_ai_enabled());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn agent_intent_keeps_ai_enabled_for_any_setup_choice() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
|
||||
// Default agent intention + "Use Warp Agent" enables AI.
|
||||
model.read(&app, |model, _| assert!(model.settings().is_ai_enabled()));
|
||||
|
||||
// "Use third party agents" still keeps AI enabled: agent intent always
|
||||
// means the user wants AI, even when bringing their own agents.
|
||||
model.update(&mut app, |model, ctx| {
|
||||
model.set_ai_setup_choice(AiSetupChoice::ThirdParty, ctx)
|
||||
});
|
||||
model.read(&app, |model, _| assert!(model.settings().is_ai_enabled()));
|
||||
|
||||
// Switching back to Warp Agent also keeps AI enabled.
|
||||
model.update(&mut app, |model, ctx| {
|
||||
model.set_ai_setup_choice(AiSetupChoice::WarpAgent, ctx)
|
||||
});
|
||||
model.read(&app, |model, _| assert!(model.settings().is_ai_enabled()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn terminal_path_skips_third_party() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
model.update(&mut app, |model, ctx| model.set_intention_terminal(ctx));
|
||||
|
||||
// Terminal goes Intention → Customize → ThemePicker; the "Customize third
|
||||
// party agents" slide is only for the agent → third-party choice.
|
||||
for expected in [
|
||||
OnboardingStep::Intention,
|
||||
OnboardingStep::Customize,
|
||||
OnboardingStep::ThemePicker,
|
||||
] {
|
||||
model.update(&mut app, |model, ctx| model.next(ctx));
|
||||
assert_eq!(step(&app, &model), expected);
|
||||
}
|
||||
|
||||
// Back navigation mirrors the forward path, also skipping third-party.
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Customize);
|
||||
model.update(&mut app, |model, ctx| model.back(ctx));
|
||||
assert_eq!(step(&app, &model), OnboardingStep::Intention);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn progress_reports_v3_positions_for_agent_path() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
|
||||
// Warp Agent path: Intention → AiSetup → Agent → AiAccess → Customize → ThemePicker.
|
||||
let cases = [
|
||||
(OnboardingStep::Intention, (0, 6)),
|
||||
(OnboardingStep::AiSetup, (1, 6)),
|
||||
(OnboardingStep::Agent, (2, 6)),
|
||||
(OnboardingStep::AiAccess, (3, 6)),
|
||||
(OnboardingStep::Customize, (4, 6)),
|
||||
(OnboardingStep::ThemePicker, (5, 6)),
|
||||
];
|
||||
for (target, expected) in cases {
|
||||
model.update(&mut app, |model, ctx| model.set_step(target, ctx));
|
||||
let progress = model.read(&app, |model, _| model.progress());
|
||||
assert_eq!(progress, expected, "unexpected dots for {target:?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn progress_reports_v3_positions_for_third_party_path() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
model.update(&mut app, |model, ctx| {
|
||||
model.set_ai_setup_choice(AiSetupChoice::ThirdParty, ctx)
|
||||
});
|
||||
|
||||
// Third-party path has no "Choose how to access AI" step, so it is one
|
||||
// dot shorter than the Warp Agent path.
|
||||
let cases = [
|
||||
(OnboardingStep::Intention, (0, 5)),
|
||||
(OnboardingStep::AiSetup, (1, 5)),
|
||||
(OnboardingStep::ThirdParty, (2, 5)),
|
||||
(OnboardingStep::Customize, (3, 5)),
|
||||
(OnboardingStep::ThemePicker, (4, 5)),
|
||||
];
|
||||
for (target, expected) in cases {
|
||||
model.update(&mut app, |model, ctx| model.set_step(target, ctx));
|
||||
let progress = model.read(&app, |model, _| model.progress());
|
||||
assert_eq!(progress, expected, "unexpected dots for {target:?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn progress_reports_terminal_path_uses_three_dot_variant() {
|
||||
let _flag = FeatureFlag::OpenWarpNewSettingsModes.override_enabled(true);
|
||||
App::test((), |mut app| async move {
|
||||
let model = add_test_model(&mut app);
|
||||
model.update(&mut app, |model, ctx| model.set_intention_terminal(ctx));
|
||||
let cases = [
|
||||
(OnboardingStep::Intention, (0, 3)),
|
||||
(OnboardingStep::Customize, (1, 3)),
|
||||
(OnboardingStep::ThemePicker, (2, 3)),
|
||||
];
|
||||
for (target, expected) in cases {
|
||||
model.update(&mut app, |model, ctx| model.set_step(target, ctx));
|
||||
let progress = model.read(&app, |model, _| model.progress());
|
||||
assert_eq!(progress, expected, "unexpected dots for {target:?}");
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user