11import type { ElectronApplication , Page } from '@playwright/test' ;
22
3+ import { launchInsomnia } from '../launch' ;
34import { ExportModal } from './components/export-modal' ;
45import { NavigationSidebar } from './components/navigation-sidebar' ;
56import { StatusbarComponent } from './components/statusbar' ;
67import { PreferencesPage } from './preferences' ;
78import { ProjectPage } from './project' ;
89import { WorkspacePage } from './workspace' ;
910
11+ /**
12+ * `ElectronApplication` with the launch-env metadata stashed by the `app`
13+ * fixture. Named here to avoid the duplicated intersection cast that used to
14+ * appear inside `relaunch()` and `launchClone()`.
15+ */
16+ type StashedApp = ElectronApplication & {
17+ __launchEnv ?: Record < string , string > ;
18+ __playwright ?: any ;
19+ } ;
20+
21+ /** Attach launch-env metadata to an `ElectronApplication` instance. */
22+ function stashLaunchEnv ( app : ElectronApplication , env : Record < string , string > , playwright : any ) {
23+ const s = app as StashedApp ;
24+ s . __launchEnv = env ;
25+ s . __playwright = playwright ;
26+ }
27+
1028/**
1129 * Root facade for the Insomnia E2E Page Object Model.
1230 *
@@ -42,40 +60,67 @@ export class InsomniaApp {
4260 // ===========================================================================
4361
4462 /** Statusbar (footer) — always visible. */
45- readonly statusbar : StatusbarComponent ;
63+ statusbar ! : StatusbarComponent ;
4664
4765 // global export modal
48- readonly exportModal : ExportModal ;
66+ exportModal ! : ExportModal ;
4967
5068 /** Project navigation sidebar — always visible (except login). */
51- readonly navigationSidebar : NavigationSidebar ;
69+ navigationSidebar ! : NavigationSidebar ;
5270
5371 // ===========================================================================
5472 // Page objects
5573 // ===========================================================================
5674
5775 /** Project page (project/file list). */
58- readonly projectPage : ProjectPage ;
76+ projectPage ! : ProjectPage ;
5977
6078 /** Workspace page (debug view). */
61- readonly workspacePage : WorkspacePage ;
79+ workspacePage ! : WorkspacePage ;
6280
6381 /** Preferences page (settings modal). */
64- readonly preferencesPage : PreferencesPage ;
65-
66- constructor (
67- readonly page : Page ,
68- readonly app : ElectronApplication ,
69- ) {
70- // Shared components
71- this . statusbar = new StatusbarComponent ( page ) ;
72- this . exportModal = new ExportModal ( page ) ;
73- this . navigationSidebar = new NavigationSidebar ( page ) ;
74-
75- // Pages
76- this . projectPage = new ProjectPage ( page , app ) ;
77- this . workspacePage = new WorkspacePage ( page , app ) ;
78- this . preferencesPage = new PreferencesPage ( page , app ) ;
82+ preferencesPage ! : PreferencesPage ;
83+
84+ // Private backing fields exposed as readonly getters so that external callers
85+ // cannot reassign them, while `relaunch()` can update them after a relaunch.
86+ private _page : Page ;
87+ private _app : ElectronApplication ;
88+
89+ get page ( ) : Page {
90+ return this . _page ;
91+ }
92+
93+ get app ( ) : ElectronApplication {
94+ return this . _app ;
95+ }
96+
97+ constructor ( page : Page , app : ElectronApplication ) {
98+ this . _page = page ;
99+ this . _app = app ;
100+ this . _initPageObjects ( ) ;
101+ }
102+
103+ private _initPageObjects ( ) {
104+ this . statusbar = new StatusbarComponent ( this . _page ) ;
105+ this . exportModal = new ExportModal ( this . _page ) ;
106+ this . navigationSidebar = new NavigationSidebar ( this . _page ) ;
107+ this . projectPage = new ProjectPage ( this . _page , this . _app ) ;
108+ this . workspacePage = new WorkspacePage ( this . _page , this . _app ) ;
109+ this . preferencesPage = new PreferencesPage ( this . _page , this . _app ) ;
110+ }
111+
112+ /** Read the stashed launch env/playwright from the underlying Electron app, or throw. */
113+ private _unstash ( ) : { env : Record < string , string > ; playwright : any } {
114+ const s = this . _app as StashedApp ;
115+ const env = s . __launchEnv ;
116+ const playwright = s . __playwright ;
117+ if ( ! env || ! playwright ) {
118+ throw new Error (
119+ 'Launch env was not stashed on the ElectronApplication. ' +
120+ 'Ensure the test was started via the `app` fixture in playwright/test.ts.' ,
121+ ) ;
122+ }
123+ return { env, playwright } ;
79124 }
80125
81126 // ===========================================================================
@@ -84,6 +129,66 @@ export class InsomniaApp {
84129
85130 /** Press Escape on the app container (closes modals, dropdowns, overlays). */
86131 async pressEscape ( ) : Promise < void > {
87- await this . page . locator ( '.app' ) . press ( 'Escape' ) ;
132+ await this . _page . locator ( '.app' ) . press ( 'Escape' ) ;
133+ }
134+
135+ /**
136+ * Queue a fake response for the next Electron `showOpenDialog` call.
137+ * Consumed by the main-process handler when `PLAYWRIGHT === 'true'`.
138+ */
139+ async queueOpenDialogResponse ( filePaths : string [ ] , canceled = false ) : Promise < void > {
140+ await this . _app . evaluate (
141+ ( _electron , payload ) => {
142+ const g = globalThis as any ;
143+ g . __PLAYWRIGHT_OPEN_DIALOG_QUEUE__ ||= [ ] ;
144+ g . __PLAYWRIGHT_OPEN_DIALOG_QUEUE__ . push ( payload ) ;
145+ } ,
146+ { filePaths, canceled } ,
147+ ) ;
148+ }
149+
150+ /**
151+ * Close the current Electron process and relaunch it reusing the same env
152+ * vars (including INSOMNIA_DATA_PATH) so on-disk state — NeDB, secret store —
153+ * is preserved across the cycle. After this returns, `this.app` and
154+ * `this.page` point at the fresh process; all page objects are rebuilt.
155+ *
156+ * The launch env is stashed on the app instance by the `app` fixture in
157+ * `playwright/test.ts`; callers don't need to pass anything.
158+ */
159+ async relaunch ( ) : Promise < void > {
160+ const { env, playwright } = this . _unstash ( ) ;
161+ await this . _app . close ( ) ;
162+
163+ const next = await launchInsomnia ( playwright , env as any ) ;
164+ stashLaunchEnv ( next , env , playwright ) ;
165+
166+ this . _app = next ;
167+ this . _page = await next . firstWindow ( { timeout : 60_000 } ) ;
168+ await this . _page . waitForLoadState ( ) ;
169+ // Re-seed the konnect PAT like the page fixture does.
170+ await this . _page . evaluate ( ( ) => ( window as any ) . main . secretStorage . setSecret ( 'konnectPat' , 'kpat_test' ) ) ;
171+
172+ this . _initPageObjects ( ) ;
173+ }
174+
175+ /**
176+ * Launch a second Electron instance with a fresh data path and optional env
177+ * overrides (e.g. a different INSOMNIA_SESSION for a different user). The
178+ * returned InsomniaApp is independent — it has its own page and app references
179+ * and will be cleaned up by the `app` fixture's liveApps teardown.
180+ */
181+ async launchClone ( newDataPath : string , envOverrides : Record < string , string > = { } ) : Promise < InsomniaApp > {
182+ const { env, playwright } = this . _unstash ( ) ;
183+ const cloneEnv = { ...env , INSOMNIA_DATA_PATH : newDataPath , ...envOverrides } ;
184+
185+ const next = await launchInsomnia ( playwright , cloneEnv as any ) ;
186+ stashLaunchEnv ( next , cloneEnv , playwright ) ;
187+
188+ const page = await next . firstWindow ( { timeout : 60_000 } ) ;
189+ await page . waitForLoadState ( ) ;
190+ await page . evaluate ( ( ) => ( window as any ) . main . secretStorage . setSecret ( 'konnectPat' , 'kpat_test' ) ) ;
191+
192+ return new InsomniaApp ( page , next ) ;
88193 }
89194}
0 commit comments