11import { assert , describe , it } from "@effect/vitest" ;
2+ import { HostProcessPlatform } from "@t3tools/shared/hostProcess" ;
3+ import * as Cause from "effect/Cause" ;
24import * as Effect from "effect/Effect" ;
5+ import * as Layer from "effect/Layer" ;
36import * as Option from "effect/Option" ;
47import type * as Electron from "electron" ;
58import { beforeEach , vi } from "vite-plus/test" ;
@@ -24,6 +27,10 @@ vi.mock("electron", () => ({
2427
2528import * as ElectronMenu from "./ElectronMenu.ts" ;
2629
30+ const TestLayer = ElectronMenu . layer . pipe (
31+ Layer . provide ( Layer . succeed ( HostProcessPlatform , "linux" ) ) ,
32+ ) ;
33+
2734describe ( "ElectronMenu" , ( ) => {
2835 beforeEach ( ( ) => {
2936 buildFromTemplateMock . mockReset ( ) ;
@@ -42,7 +49,7 @@ describe("ElectronMenu", () => {
4249
4350 assert . isTrue ( Option . isNone ( selectedItemId ) ) ;
4451 assert . equal ( buildFromTemplateMock . mock . calls . length , 0 ) ;
45- } ) . pipe ( Effect . provide ( ElectronMenu . layer ) ) ,
52+ } ) . pipe ( Effect . provide ( TestLayer ) ) ,
4653 ) ;
4754
4855 it . effect ( "resolves with the clicked leaf item id" , ( ) =>
@@ -69,7 +76,7 @@ describe("ElectronMenu", () => {
6976 } ) ;
7077
7178 assert . equal ( Option . getOrNull ( selectedItemId ) , "copy" ) ;
72- } ) . pipe ( Effect . provide ( ElectronMenu . layer ) ) ,
79+ } ) . pipe ( Effect . provide ( TestLayer ) ) ,
7380 ) ;
7481
7582 it . effect ( "resolves with none when the menu closes without a click" , ( ) =>
@@ -93,7 +100,7 @@ describe("ElectronMenu", () => {
93100 enabled : true ,
94101 click : buildFromTemplateMock . mock . calls [ 0 ] ?. [ 0 ] [ 0 ] . click ,
95102 } ) ;
96- } ) . pipe ( Effect . provide ( ElectronMenu . layer ) ) ,
103+ } ) . pipe ( Effect . provide ( TestLayer ) ) ,
97104 ) ;
98105
99106 it . effect ( "defers popupTemplate side effects until the returned Effect runs" , ( ) =>
@@ -114,6 +121,89 @@ describe("ElectronMenu", () => {
114121
115122 assert . equal ( buildFromTemplateMock . mock . calls . length , 1 ) ;
116123 assert . equal ( popupMock . mock . calls . length , 1 ) ;
117- } ) . pipe ( Effect . provide ( ElectronMenu . layer ) ) ,
124+ } ) . pipe ( Effect . provide ( TestLayer ) ) ,
125+ ) ;
126+
127+ it . effect ( "preserves application-menu failures as structured defects" , ( ) =>
128+ Effect . gen ( function * ( ) {
129+ const cause = new Error ( "application menu build failed" ) ;
130+ buildFromTemplateMock . mockImplementationOnce ( ( ) => {
131+ throw cause ;
132+ } ) ;
133+
134+ const electronMenu = yield * ElectronMenu . ElectronMenu ;
135+ const exit = yield * Effect . exit (
136+ electronMenu . setApplicationMenu ( [ { label : "File" } , { label : "Edit" } ] ) ,
137+ ) ;
138+
139+ assert . equal ( exit . _tag , "Failure" ) ;
140+ if ( exit . _tag === "Failure" ) {
141+ const error = Cause . squash ( exit . cause ) ;
142+ assert . instanceOf ( error , ElectronMenu . ElectronMenuOperationError ) ;
143+ assert . equal ( error . operation , "set-application-menu" ) ;
144+ assert . equal ( error . platform , "linux" ) ;
145+ assert . isNull ( error . windowId ) ;
146+ assert . equal ( error . itemCount , 2 ) ;
147+ assert . strictEqual ( error . cause , cause ) ;
148+ assert . notInclude ( error . message , cause . message ) ;
149+ }
150+ } ) . pipe ( Effect . provide ( TestLayer ) ) ,
151+ ) ;
152+
153+ it . effect ( "preserves popup-template failures with window context" , ( ) =>
154+ Effect . gen ( function * ( ) {
155+ const cause = new Error ( "popup failed" ) ;
156+ buildFromTemplateMock . mockReturnValueOnce ( {
157+ popup : ( ) => {
158+ throw cause ;
159+ } ,
160+ } ) ;
161+
162+ const electronMenu = yield * ElectronMenu . ElectronMenu ;
163+ const exit = yield * Effect . exit (
164+ electronMenu . popupTemplate ( {
165+ window : { id : 41 } as Electron . BrowserWindow ,
166+ template : [ { label : "Copy" } ] ,
167+ } ) ,
168+ ) ;
169+
170+ assert . equal ( exit . _tag , "Failure" ) ;
171+ if ( exit . _tag === "Failure" ) {
172+ const error = Cause . squash ( exit . cause ) ;
173+ assert . instanceOf ( error , ElectronMenu . ElectronMenuOperationError ) ;
174+ assert . equal ( error . operation , "popup-template" ) ;
175+ assert . equal ( error . windowId , 41 ) ;
176+ assert . equal ( error . itemCount , 1 ) ;
177+ assert . strictEqual ( error . cause , cause ) ;
178+ }
179+ } ) . pipe ( Effect . provide ( TestLayer ) ) ,
180+ ) ;
181+
182+ it . effect ( "preserves context-menu failures with normalized item context" , ( ) =>
183+ Effect . gen ( function * ( ) {
184+ const cause = new Error ( "context menu build failed" ) ;
185+ buildFromTemplateMock . mockImplementationOnce ( ( ) => {
186+ throw cause ;
187+ } ) ;
188+
189+ const electronMenu = yield * ElectronMenu . ElectronMenu ;
190+ const exit = yield * Effect . exit (
191+ electronMenu . showContextMenu ( {
192+ window : { id : 42 } as Electron . BrowserWindow ,
193+ items : [ { id : "copy" , label : "Copy" } ] ,
194+ position : Option . none ( ) ,
195+ } ) ,
196+ ) ;
197+
198+ assert . equal ( exit . _tag , "Failure" ) ;
199+ if ( exit . _tag === "Failure" ) {
200+ const error = Cause . squash ( exit . cause ) ;
201+ assert . instanceOf ( error , ElectronMenu . ElectronMenuOperationError ) ;
202+ assert . equal ( error . operation , "show-context-menu" ) ;
203+ assert . equal ( error . windowId , 42 ) ;
204+ assert . equal ( error . itemCount , 1 ) ;
205+ assert . strictEqual ( error . cause , cause ) ;
206+ }
207+ } ) . pipe ( Effect . provide ( TestLayer ) ) ,
118208 ) ;
119209} ) ;
0 commit comments