@@ -160,3 +160,245 @@ describe("Trashcan Class", () => {
160160 expect ( trashcan . overTrashcan ( 300 , 300 ) ) . toBe ( false ) ;
161161 } ) ;
162162} ) ;
163+
164+ describe ( "overTrashcan edge cases" , ( ) => {
165+ let trashcan ;
166+
167+ beforeEach ( ( ) => {
168+ jest . clearAllMocks ( ) ;
169+ trashcan = new Trashcan ( mockActivity ) ;
170+ trashcan . _container . x = 100 ;
171+ trashcan . _container . y = 200 ;
172+ } ) ;
173+
174+ it ( "should return true for a point exactly at the top-left corner" , ( ) => {
175+ expect ( trashcan . overTrashcan ( 100 , 200 ) ) . toBe ( true ) ;
176+ } ) ;
177+
178+ it ( "should return true for a point exactly at the top-right corner" , ( ) => {
179+ // TRASHWIDTH is 120, so top-right is (100 + 120, 200)
180+ expect ( trashcan . overTrashcan ( 220 , 200 ) ) . toBe ( true ) ;
181+ } ) ;
182+
183+ it ( "should return false for a point just left of the left edge" , ( ) => {
184+ expect ( trashcan . overTrashcan ( 99 , 200 ) ) . toBe ( false ) ;
185+ } ) ;
186+
187+ it ( "should return false for a point just above the top edge" , ( ) => {
188+ expect ( trashcan . overTrashcan ( 150 , 199 ) ) . toBe ( false ) ;
189+ } ) ;
190+
191+ it ( "should return false for a point just right of the right edge" , ( ) => {
192+ // x > tx + TRASHWIDTH (100 + 120 = 220), so 221 is out
193+ expect ( trashcan . overTrashcan ( 221 , 200 ) ) . toBe ( false ) ;
194+ } ) ;
195+
196+ it ( "should return true for a point far below the trashcan (no lower y bound)" , ( ) => {
197+ // overTrashcan has no lower y bound check
198+ expect ( trashcan . overTrashcan ( 150 , 10000 ) ) . toBe ( true ) ;
199+ } ) ;
200+
201+ it ( "should return true for a point exactly on the left edge" , ( ) => {
202+ expect ( trashcan . overTrashcan ( 100 , 250 ) ) . toBe ( true ) ;
203+ } ) ;
204+
205+ it ( "should return true for a point exactly on the right edge" , ( ) => {
206+ expect ( trashcan . overTrashcan ( 220 , 250 ) ) . toBe ( true ) ;
207+ } ) ;
208+
209+ it ( "should return false for negative x coordinates" , ( ) => {
210+ expect ( trashcan . overTrashcan ( - 50 , 250 ) ) . toBe ( false ) ;
211+ } ) ;
212+
213+ it ( "should return false for negative y coordinates" , ( ) => {
214+ expect ( trashcan . overTrashcan ( 150 , - 50 ) ) . toBe ( false ) ;
215+ } ) ;
216+
217+ it ( "should return true for the center of the trashcan area" , ( ) => {
218+ // center x = 100 + 60 = 160, y = 200 + 60 = 260
219+ expect ( trashcan . overTrashcan ( 160 , 260 ) ) . toBe ( true ) ;
220+ } ) ;
221+
222+ it ( "should return false when x is at left boundary but y is above" , ( ) => {
223+ expect ( trashcan . overTrashcan ( 100 , 199 ) ) . toBe ( false ) ;
224+ } ) ;
225+ } ) ;
226+
227+ describe ( "shouldResize edge cases" , ( ) => {
228+ let trashcan ;
229+
230+ beforeEach ( ( ) => {
231+ jest . clearAllMocks ( ) ;
232+ trashcan = new Trashcan ( mockActivity ) ;
233+ } ) ;
234+
235+ it ( "should return false when both dimensions match container position" , ( ) => {
236+ trashcan . _container . x = 500 ;
237+ trashcan . _container . y = 400 ;
238+ expect ( trashcan . shouldResize ( 500 , 400 ) ) . toBe ( false ) ;
239+ } ) ;
240+
241+ it ( "should return true when only x differs" , ( ) => {
242+ trashcan . _container . x = 500 ;
243+ trashcan . _container . y = 400 ;
244+ expect ( trashcan . shouldResize ( 600 , 400 ) ) . toBe ( true ) ;
245+ } ) ;
246+
247+ it ( "should return true when only y differs" , ( ) => {
248+ trashcan . _container . x = 500 ;
249+ trashcan . _container . y = 400 ;
250+ expect ( trashcan . shouldResize ( 500 , 300 ) ) . toBe ( true ) ;
251+ } ) ;
252+
253+ it ( "should return true when both dimensions differ" , ( ) => {
254+ trashcan . _container . x = 500 ;
255+ trashcan . _container . y = 400 ;
256+ expect ( trashcan . shouldResize ( 600 , 300 ) ) . toBe ( true ) ;
257+ } ) ;
258+
259+ it ( "should return false with zero positions matching" , ( ) => {
260+ trashcan . _container . x = 0 ;
261+ trashcan . _container . y = 0 ;
262+ expect ( trashcan . shouldResize ( 0 , 0 ) ) . toBe ( false ) ;
263+ } ) ;
264+
265+ it ( "should return true with zero vs non-zero" , ( ) => {
266+ trashcan . _container . x = 0 ;
267+ trashcan . _container . y = 0 ;
268+ expect ( trashcan . shouldResize ( 100 , 0 ) ) . toBe ( true ) ;
269+ } ) ;
270+ } ) ;
271+
272+ describe ( "stopHighlightAnimation" , ( ) => {
273+ let trashcan ;
274+
275+ beforeEach ( ( ) => {
276+ jest . clearAllMocks ( ) ;
277+ trashcan = new Trashcan ( mockActivity ) ;
278+ } ) ;
279+
280+ it ( "should do nothing if not in animation" , ( ) => {
281+ trashcan . _inAnimation = false ;
282+ const clearSpy = jest . spyOn ( global , "clearInterval" ) ;
283+
284+ trashcan . stopHighlightAnimation ( ) ;
285+
286+ expect ( clearSpy ) . not . toHaveBeenCalled ( ) ;
287+ clearSpy . mockRestore ( ) ;
288+ } ) ;
289+
290+ it ( "should clear interval and reset state when in animation" , ( ) => {
291+ trashcan . _inAnimation = true ;
292+ trashcan . _animationInterval = 42 ;
293+ trashcan . _animationLevel = 100 ;
294+ trashcan . _highlightPower = 128 ;
295+ trashcan . isVisible = true ;
296+ const clearSpy = jest . spyOn ( global , "clearInterval" ) ;
297+
298+ trashcan . stopHighlightAnimation ( ) ;
299+
300+ expect ( clearSpy ) . toHaveBeenCalledWith ( 42 ) ;
301+ expect ( trashcan . _inAnimation ) . toBe ( false ) ;
302+ expect ( trashcan . isVisible ) . toBe ( false ) ;
303+ expect ( trashcan . _animationLevel ) . toBe ( 0 ) ;
304+ expect ( trashcan . _highlightPower ) . toBe ( 255 ) ;
305+ clearSpy . mockRestore ( ) ;
306+ } ) ;
307+
308+ it ( "should be safe to call multiple times" , ( ) => {
309+ trashcan . _inAnimation = true ;
310+ trashcan . _animationInterval = 42 ;
311+
312+ trashcan . stopHighlightAnimation ( ) ;
313+ expect ( trashcan . _inAnimation ) . toBe ( false ) ;
314+
315+ // Second call should be a no-op since _inAnimation is now false
316+ trashcan . stopHighlightAnimation ( ) ;
317+ expect ( trashcan . _inAnimation ) . toBe ( false ) ;
318+ } ) ;
319+
320+ it ( "should reset animation level and highlight power to defaults" , ( ) => {
321+ trashcan . _inAnimation = true ;
322+ trashcan . _animationLevel = 500 ;
323+ trashcan . _highlightPower = 0 ;
324+
325+ trashcan . stopHighlightAnimation ( ) ;
326+
327+ expect ( trashcan . _animationLevel ) . toBe ( 0 ) ;
328+ expect ( trashcan . _highlightPower ) . toBe ( 255 ) ;
329+ } ) ;
330+ } ) ;
331+
332+ describe ( "scale and container positioning" , ( ) => {
333+ let trashcan ;
334+
335+ beforeEach ( ( ) => {
336+ jest . clearAllMocks ( ) ;
337+ trashcan = new Trashcan ( mockActivity ) ;
338+ } ) ;
339+
340+ it ( "should have default scale of 1" , ( ) => {
341+ expect ( trashcan . _scale ) . toBe ( 1 ) ;
342+ } ) ;
343+
344+ it ( "should update scale via resizeEvent" , ( ) => {
345+ trashcan . resizeEvent ( 2 ) ;
346+ expect ( trashcan . _scale ) . toBe ( 2 ) ;
347+ } ) ;
348+
349+ it ( "should update container position based on scale" , ( ) => {
350+ // window.innerWidth = 1024, window.innerHeight = 768 (jsdom defaults)
351+ trashcan . _scale = 1 ;
352+ trashcan . updateContainerPosition ( ) ;
353+
354+ const expectedX =
355+ window . innerWidth / trashcan . _scale - Trashcan . TRASHWIDTH - 2 * trashcan . _iconsize ;
356+ const expectedY =
357+ window . innerHeight / trashcan . _scale -
358+ Trashcan . TRASHHEIGHT -
359+ ( 5 / 4 ) * trashcan . _iconsize ;
360+
361+ expect ( trashcan . _container . x ) . toBe ( expectedX ) ;
362+ expect ( trashcan . _container . y ) . toBe ( expectedY ) ;
363+ } ) ;
364+
365+ it ( "should compute different positions at different scales" , ( ) => {
366+ trashcan . _scale = 1 ;
367+ trashcan . updateContainerPosition ( ) ;
368+ const x1 = trashcan . _container . x ;
369+ const y1 = trashcan . _container . y ;
370+
371+ trashcan . _scale = 2 ;
372+ trashcan . updateContainerPosition ( ) ;
373+ const x2 = trashcan . _container . x ;
374+ const y2 = trashcan . _container . y ;
375+
376+ // At scale 2, window dimensions are halved, so positions should be different
377+ expect ( x2 ) . not . toBe ( x1 ) ;
378+ expect ( y2 ) . not . toBe ( y1 ) ;
379+ } ) ;
380+
381+ it ( "should have static TRASHWIDTH and TRASHHEIGHT constants" , ( ) => {
382+ expect ( Trashcan . TRASHWIDTH ) . toBe ( 120 ) ;
383+ expect ( Trashcan . TRASHHEIGHT ) . toBe ( 120 ) ;
384+ } ) ;
385+
386+ it ( "should set iconsize based on trash bitmap bounds" , ( ) => {
387+ // _makeTrash sets _iconsize from bitmap getBounds().width (mocked as 100)
388+ expect ( trashcan . _iconsize ) . toBe ( 100 ) ;
389+ } ) ;
390+
391+ it ( "should initialize _borderHighlightBitmap during construction" , ( ) => {
392+ // resizeEvent(1) in constructor triggers _makeBorderHighlight
393+ expect ( trashcan . _borderHighlightBitmap ) . not . toBeNull ( ) ;
394+ } ) ;
395+
396+ it ( "should have _isHighlightInitialized set after construction" , ( ) => {
397+ // resizeEvent(1) in constructor triggers _makeBorderHighlight which sets this
398+ expect ( trashcan . _isHighlightInitialized ) . toBe ( true ) ;
399+ } ) ;
400+
401+ it ( "should initialize animationTime as 500" , ( ) => {
402+ expect ( trashcan . animationTime ) . toBe ( 500 ) ;
403+ } ) ;
404+ } ) ;
0 commit comments