@@ -5,21 +5,35 @@ import { join } from "node:path";
55
66import type { Config , UnconfiguredConfig } from "../config/index.js" ;
77import type { ProviderSetupConfig } from "./provider-setup.js" ;
8+ import type { WelcomeConfig } from "./welcome.js" ;
89import { withMockedModule } from "../../tests/helpers/mock-module.js" ;
910
1011let testHome = "" ;
1112let setup : ( config : ProviderSetupConfig ) => Promise < void > = async ( ) => { } ;
13+ let welcome : ( config : WelcomeConfig ) => Promise < boolean > = async ( ) => true ;
1214let tuiConfig : Config | undefined ;
15+ const callOrder : string [ ] = [ ] ;
1316
1417await withMockedModule ( import . meta. resolve ( "node:os" ) , ( real : typeof import ( "node:os" ) ) => ( {
1518 ...real ,
1619 homedir : ( ) => testHome ,
1720} ) ) ;
21+ await withMockedModule (
22+ import . meta. resolve ( "./welcome.js" ) ,
23+ ( real : typeof import ( "./welcome.js" ) ) => ( {
24+ ...real ,
25+ runWelcome : async ( config : WelcomeConfig = { } ) => {
26+ callOrder . push ( "welcome" ) ;
27+ return welcome ( config ) ;
28+ } ,
29+ } ) ,
30+ ) ;
1831await withMockedModule (
1932 import . meta. resolve ( "./provider-setup.js" ) ,
2033 ( real : typeof import ( "./provider-setup.js" ) ) => ( {
2134 ...real ,
2235 runProviderSetup : async ( config : ProviderSetupConfig ) => {
36+ callOrder . push ( "setup" ) ;
2337 await setup ( config ) ;
2438 return true ;
2539 } ,
@@ -78,7 +92,102 @@ async function writeXAIAuthProfile(home: string, profile: string): Promise<void>
7892
7993afterEach ( ( ) => {
8094 setup = async ( ) => { } ;
95+ welcome = async ( ) => true ;
8196 tuiConfig = undefined ;
97+ callOrder . length = 0 ;
98+ } ) ;
99+
100+ describe ( "runOnboarding welcome gate" , ( ) => {
101+ test ( "fresh user sees welcome before provider setup and marks onboarded" , async ( ) => {
102+ testHome = await mkdtemp ( join ( tmpdir ( ) , "corbits-onboarding-welcome-home-" ) ) ;
103+ const cwd = await mkdtemp ( join ( tmpdir ( ) , "corbits-onboarding-welcome-cwd-" ) ) ;
104+ const configPath = join ( testHome , ".corbits" , "settings.json" ) ;
105+ try {
106+ await mkdir ( join ( testHome , ".corbits" ) , { recursive : true } ) ;
107+ await writeFile ( configPath , JSON . stringify ( { providers : { } } ) ) ;
108+ const config = await unconfiguredConfig ( cwd , { programmaticConfigPath : configPath } ) ;
109+
110+ setup = async ( { onSubmit } ) => {
111+ await onSubmit (
112+ {
113+ name : "custom" ,
114+ baseURL : "https://provider.example.com/v1" ,
115+ apiKey : "test-key" ,
116+ model : "test-model" ,
117+ oauthProfile : "" ,
118+ } ,
119+ ( ) => { } ,
120+ { skipValidation : true } ,
121+ ) ;
122+ } ;
123+
124+ expect ( await runOnboarding ( config ) ) . toBe ( 0 ) ;
125+ expect ( callOrder ) . toEqual ( [ "welcome" , "setup" ] ) ;
126+
127+ const persisted = JSON . parse ( await readFile ( configPath , "utf8" ) ) as {
128+ onboarded ?: boolean ;
129+ } ;
130+ expect ( persisted . onboarded ) . toBe ( true ) ;
131+ } finally {
132+ await rm ( testHome , { recursive : true , force : true } ) ;
133+ await rm ( cwd , { recursive : true , force : true } ) ;
134+ }
135+ } ) ;
136+
137+ test ( "already-onboarded skips welcome and opens setup directly" , async ( ) => {
138+ testHome = await mkdtemp ( join ( tmpdir ( ) , "corbits-onboarding-skip-home-" ) ) ;
139+ const cwd = await mkdtemp ( join ( tmpdir ( ) , "corbits-onboarding-skip-cwd-" ) ) ;
140+ const configPath = join ( testHome , ".corbits" , "settings.json" ) ;
141+ try {
142+ await mkdir ( join ( testHome , ".corbits" ) , { recursive : true } ) ;
143+ await writeFile ( configPath , JSON . stringify ( { providers : { } , onboarded : true } ) ) ;
144+ const config = await unconfiguredConfig ( cwd , { programmaticConfigPath : configPath } ) ;
145+
146+ setup = async ( { onSubmit } ) => {
147+ await onSubmit (
148+ {
149+ name : "custom" ,
150+ baseURL : "https://provider.example.com/v1" ,
151+ apiKey : "test-key" ,
152+ model : "test-model" ,
153+ oauthProfile : "" ,
154+ } ,
155+ ( ) => { } ,
156+ { skipValidation : true } ,
157+ ) ;
158+ } ;
159+
160+ expect ( await runOnboarding ( config ) ) . toBe ( 0 ) ;
161+ expect ( callOrder ) . toEqual ( [ "setup" ] ) ;
162+ } finally {
163+ await rm ( testHome , { recursive : true , force : true } ) ;
164+ await rm ( cwd , { recursive : true , force : true } ) ;
165+ }
166+ } ) ;
167+
168+ test ( "cancelled welcome does not mark onboarded or open setup" , async ( ) => {
169+ testHome = await mkdtemp ( join ( tmpdir ( ) , "corbits-onboarding-cancel-home-" ) ) ;
170+ const cwd = await mkdtemp ( join ( tmpdir ( ) , "corbits-onboarding-cancel-cwd-" ) ) ;
171+ const configPath = join ( testHome , ".corbits" , "settings.json" ) ;
172+ try {
173+ await mkdir ( join ( testHome , ".corbits" ) , { recursive : true } ) ;
174+ await writeFile ( configPath , JSON . stringify ( { providers : { } } ) ) ;
175+ const config = await unconfiguredConfig ( cwd , { programmaticConfigPath : configPath } ) ;
176+
177+ welcome = async ( ) => false ;
178+
179+ expect ( await runOnboarding ( config ) ) . toBe ( 1 ) ;
180+ expect ( callOrder ) . toEqual ( [ "welcome" ] ) ;
181+
182+ const persisted = JSON . parse ( await readFile ( configPath , "utf8" ) ) as {
183+ onboarded ?: boolean ;
184+ } ;
185+ expect ( persisted . onboarded ) . toBeUndefined ( ) ;
186+ } finally {
187+ await rm ( testHome , { recursive : true , force : true } ) ;
188+ await rm ( cwd , { recursive : true , force : true } ) ;
189+ }
190+ } ) ;
82191} ) ;
83192
84193describe ( "runOnboarding settings source" , ( ) => {
0 commit comments