§
    ôF¿jY  ã                  óÐ   — d Z ddlmZ ddlZddlZddlZddlmZ ddlZ ee	¦  «         
                    ¦   «         j        j        j        Zedz  dz  dz  dz  Z G d	„ d
¦  «        ZdS )u  
Unit Test Suite for FreeExile PC Desktop Input Controller (client/web_pc/js/pc_input_controller.js).
Verifies:
- Kinematics & 2:1 isometric coordinate transformation invariants
- Click-to-Move arrival threshold (<= 0.22) and continuous Hold-to-Move logic
- Decoupling of key 'W' from movement and mapping to Martial Skill 2
- 1-5 Survival Flask properties and cooldowns
- Huyá»…n áº¢nh Bá»™ (Spacebar dodge) 0.25s i-frame window
- Input field typing focus exclusion guard
- Code hygiene & line cap limits (<= 350 lines soft cap)
é    )ÚannotationsN)ÚPathÚclientÚweb_pcÚjszpc_input_controller.jsc                  ój   — e Zd ZdZdd„Zdd„Zdd„Zdd„Zdd„Zdd	„Z	dd
„Z
dd„Zdd„Zdd„Zdd„ZdS )ÚTestPcInputControllerUnitzFEmpirical unit tests for PC Input Controller kinematics and contracts.ÚreturnÚNonec                ó<  — t                                ¦   «         s
J d¦   «         ‚t                                d¬¦  «                             ¦   «         }t	          |¦  «        dk    sJ dt	          |¦  «        › d�¦   «         ‚t	          |¦  «        dk    s
J d¦   «         ‚d	S )
zCVerifies pc_input_controller.js exists and complies with line caps.z!pc_input_controller.js must existúutf-8©Úencodingi^  zFile length z exceeds 350 lines soft capéP   z"File should have substantive logicN)ÚPC_INPUT_JSÚexistsÚ	read_textÚ
splitlinesÚlen)ÚselfÚliness     ú<C:\Projects\FreeExile\tests\unit\test_pc_input_controller.pyÚtest_file_exists_and_hygienez6TestPcInputControllerUnit.test_file_exists_and_hygiene   sœ   € å×!Ò!Ñ#Ô#ÐHÐHÐ%HÑHÔHÐ#Ý×%Ò%¨wÐ%Ñ7Ô7×BÒBÑDÔDˆÝ�5‰zŒz˜SÒ Ð Ð Ð"Xµ°U±´Ð"XÐ"XÐ"XÑ Ô Ð Ý�5‰zŒz˜RÒÐÐÐ!EÑÔÐÐÐó    c                ól   — t                                d¬¦  «        }d|v sJ ‚d|v sJ ‚d|v sJ ‚d|v sJ ‚dS )zIVerifies window.enableWasdMovement is set false and W is mapped to skill.r   r   z!window.enableWasdMovement = falsezwindow.keys = {}zkey === 'W'Ú	doThunderN©r   r   ©r   Úcontents     r   Ú(test_wasd_decoupling_and_w_key_isolationzBTestPcInputControllerUnit.test_wasd_decoupling_and_w_key_isolation"   sb   € å×'Ò'°Ð'Ñ9Ô9ˆØ2°gÐ=Ð=Ð=Ð=Ø! WÐ,Ð,Ð,Ð,à Ð'Ð'Ð'Ð'Ø˜gÐ%Ð%Ð%Ð%Ð%Ð%r   c                ó¨   — t                                d¬¦  «        }dD ]}d|› d�|v sJ ‚ŒdD ]}d|› d�|v sJ ‚Œd|v sJ ‚d|v sJ ‚d|v sJ ‚d	|v sJ ‚d
S )z?Verifies all required PC ARPG hotkeys are handled in onKeyDown.r   r   )Ú1Ú2Ú3Ú4Ú5ú')ÚQÚWÚEÚRÚSpacez'I'z'C'z'ESCAPE'Nr   )r   r   ÚnumÚkeys       r   Útest_keybinding_registrationz6TestPcInputControllerUnit.test_keybinding_registration+   s¸   € å×'Ò'°Ð'Ñ9Ô9ˆà,ð 	)ð 	)ˆCØ�s�:�:�: Ð(Ð(Ð(Ð(Ð(à'ð 	)ð 	)ˆCØ�s�:�:�: Ð(Ð(Ð(Ð(Ð(à˜'Ð!Ð!Ð!Ð!à˜ÐÐÐÐØ˜ÐÐÐÐØ˜WÐ$Ð$Ð$Ð$Ð$Ð$r   c                óH  — d\  }}d\  }}t          j        ||z
  ||z
  ¦  «        }|dk    s
J d¦   «         ‚||z
  |z  }||z
  |z  }t          j        t          j        ||¦  «        dd¬¦  «        sJ ‚d\  }}	t          j        ||z
  |	|z
  ¦  «        }
|
dk    s
J d	¦   «         ‚d
S )zCVerifies arrival threshold mathematical formula (0.22 world units).)ç      $@r1   )ç      %@r2   g)\�Âõ(Ì?z/Destination must be active when distance > 0.22ç      ð?gñhãˆµøä>)Úrel_tol)ç333333$@r5   z-Destination should snap inactive when <= 0.22N)ÚmathÚhypotÚisclose)r   Ú	player_wxÚ	player_wyÚdest_wxÚdest_wyÚdistÚmxÚmyÚclose_wxÚclose_wyÚ
close_dists              r   Ú%test_click_to_move_arrival_kinematicsz?TestPcInputControllerUnit.test_click_to_move_arrival_kinematics;   sÕ   € ð  *Ñˆ	�9à%Ñˆ�ÝŒz˜' IÑ-¨w¸Ñ/BÑCÔCˆØ�dŠ{ˆ{ˆ{ÐM‰{Œ{ˆ{Ø˜	Ñ! TÑ)ˆØ˜	Ñ! TÑ)ˆÝŒ|�DœJ r¨2Ñ.Ô.°¸TÐBÑBÔBÐBÐBÐBð (Ñˆ�(Ý”Z ¨9Ñ 4°hÀÑ6JÑKÔKˆ
Ø˜TÒ!Ð!Ð!Ð#RÑ!Ô!Ð!Ð!Ð!r   c                ól   — t                                d¬¦  «        }d|v sJ ‚d|v sJ ‚d|v sJ ‚d|v sJ ‚dS )z;Verifies isLmbHeld state and pointermove continuous update.r   r   zisLmbHeld: falsezthis.isLmbHeld = truezthis.isLmbHeld = falsez;this.executeMoveTo(this.cursorWorld.wx, this.cursorWorld.wyNr   r   s     r   Ú"test_continuous_hold_to_move_logicz<TestPcInputControllerUnit.test_continuous_hold_to_move_logicL   sc   € å×'Ò'°Ð'Ñ9Ô9ˆØ! WÐ,Ð,Ð,Ð,Ø&¨'Ð1Ð1Ð1Ð1Ø'¨7Ð2Ð2Ð2Ð2ØLÐPWÐWÐWÐWÐWÐWÐWr   c                ól   — t                                d¬¦  «        }d|v sJ ‚d|v sJ ‚d|v sJ ‚d|v sJ ‚dS )zGVerifies right-click suppresses contextmenu and triggers primary skill.r   r   ze.preventDefault()Úcontextmenuze.button === 2ÚexecutePrimarySkillNr   r   s     r   Ú"test_contextmenu_prevention_on_rmbz<TestPcInputControllerUnit.test_contextmenu_prevention_on_rmbT   sb   € å×'Ò'°Ð'Ñ9Ô9ˆØ# wÐ.Ð.Ð.Ð.Ø Ð'Ð'Ð'Ð'Ø 7Ð*Ð*Ð*Ð*Ø$¨Ð/Ð/Ð/Ð/Ð/Ð/r   c                óª  — d\  }}t          j        t          j        dd¦  «        d¦  «        sJ ‚t          j        t          j        dd¦  «        t           j        dz  ¦  «        sJ ‚t          j        t          j        dd¦  «        t           j        ¦  «        sJ ‚t          j        t          j        dd¦  «        t           j         dz  ¦  «        sJ ‚dS )zDVerifies Math.atan2 correctly calculates 8-directional facing angle.©ç        rL   rL   r3   é   g      ð¿N)r6   r8   Úatan2Úpi)r   Úplayer_xÚplayer_ys      r   Útest_facing_angle_trigonometryz8TestPcInputControllerUnit.test_facing_angle_trigonometry\   s²   € à%Ñˆ�(åŒ|�DœJ s¨CÑ0Ô0°#Ñ6Ô6Ð6Ð6Ð6åŒ|�DœJ s¨CÑ0Ô0µ$´'¸A±+Ñ>Ô>Ð>Ð>Ð>åŒ|�DœJ s¨DÑ1Ô1µ4´7Ñ;Ô;Ð;Ð;Ð;åŒ|�DœJ t¨SÑ1Ô1µD´G°8¸a±<Ñ@Ô@Ð@Ð@Ð@Ð@Ð@r   c                óx   — t                                d¬¦  «        }d|v sJ ‚d|v sJ ‚d|v sJ ‚d|v sJ ‚d|v sJ ‚dS )	zJVerifies Space triggers 0.25s i-frame, dodge animState, and 3.0s cooldown.r   r   ÚdoDodgezplayer.isIFrame = truezplayer.iFrameTimer = 0.25zplayer.animState = 'dodge'ztriggerUiCooldown('dodge', 3.0)Nr   r   s     r   Ú!test_spacebar_dodge_iframe_windowz;TestPcInputControllerUnit.test_spacebar_dodge_iframe_windowh   ss   € å×'Ò'°Ð'Ñ9Ô9ˆØ˜GÐ#Ð#Ð#Ð#Ø'¨7Ð2Ð2Ð2Ð2Ø*¨gÐ5Ð5Ð5Ð5Ø+¨wÐ6Ð6Ð6Ð6Ø0°GÐ;Ð;Ð;Ð;Ð;Ð;r   c                óx   — t                                d¬¦  «        }d|v sJ ‚d|v sJ ‚d|v sJ ‚d|v sJ ‚d|v sJ ‚dS )	zHVerifies all 5 flasks are specified with accurate durations and effects.r   r   Ú450Ú50z1.35zdefenseBuff = 0.5zdamageMultiplier = 1.5Nr   r   s     r   Ú"test_survival_flask_specificationsz<TestPcInputControllerUnit.test_survival_flask_specificationsq   so   € å×'Ò'°Ð'Ñ9Ô9ˆà˜ÐÐÐÐà�wˆˆˆˆà˜Ð Ð Ð Ð à" gÐ-Ð-Ð-Ð-à'¨7Ð2Ð2Ð2Ð2Ð2Ð2r   c                ól   — t                                d¬¦  «        }d|v sJ ‚d|v sJ ‚d|v sJ ‚d|v sJ ‚dS )zKVerifies isTypingFocus inspects activeElement tag to avoid hotkey misfires.r   r   ÚisTypingFocusz'INPUT'z
'TEXTAREA'ÚisContentEditableNr   r   s     r   Útest_input_typing_focus_guardz7TestPcInputControllerUnit.test_input_typing_focus_guard   sb   € å×'Ò'°Ð'Ñ9Ô9ˆØ 'Ð)Ð)Ð)Ð)Ø˜GÐ#Ð#Ð#Ð#Ø˜wÐ&Ð&Ð&Ð&Ø" gÐ-Ð-Ð-Ð-Ð-Ð-r   c                ó\  — d\  }}d\  }}d\  }}dD ]™\  }}||z
  }	||z
  }
|	|
z
  |dz  z  |z   }|	|
z   |dz  z  |z   }||z
  }||z
  }||dz  z  ||dz  z  z   dz  }||dz  z  ||dz  z  z
  dz  }||z   }||z   }t          j        ||d¬¦  «        sJ ‚t          j        ||d¬¦  «        sJ ‚ŒšdS )	z>Verifies 2:1 isometric coordinate math round-trip consistency.)g      P@g      @@)g      Ž@g     à€@)g      @r1   )rK   )g      )@gš™™™™™ À)g      Y@g     @o@g       @g�íµ ÷Æ°>)Úabs_tolN)r6   r8   )r   Útile_wÚtile_hÚcxÚcyÚcam_xÚcam_yÚtest_wxÚtest_wyÚrel_wxÚrel_wyÚsxÚsyÚdiff_sxÚdiff_syÚcalc_rel_wxÚcalc_rel_wyÚcalc_wxÚcalc_wys                      r   Ú(test_isometric_math_roundtrip_invariancezBTestPcInputControllerUnit.test_isometric_math_roundtrip_invariance‡   s(  € à#‰ˆ�Ø‰ˆˆBØ ‰ˆˆuà Jð 	@ð 	@ÑˆG�Wà˜u‘_ˆFØ˜u‘_ˆFØ˜6‘/ f¨s¡lÑ3°bÑ8ˆBØ˜6‘/ f¨s¡lÑ3°bÑ8ˆBð ˜2‘gˆGØ˜2‘gˆGØ" f¨s¡lÑ3°gÀÈ#ÁÑ6NÑNÐRUÑUˆKØ" f¨s¡lÑ3°gÀÈ#ÁÑ6NÑNÐRUÑUˆKØ! EÑ)ˆGØ! EÑ)ˆGå”< ¨¸$Ð?Ñ?Ô?Ð?Ð?Ð?Ý”< ¨¸$Ð?Ñ?Ô?Ð?Ð?Ð?Ð?ð!	@ð 	@r   N)r
   r   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r    r/   rC   rE   rI   rR   rU   rY   r]   rr   © r   r   r	   r	      s  € € € € € ØPÐPðFð Fð Fð Fð&ð &ð &ð &ð%ð %ð %ð %ð Sð Sð Sð Sð"Xð Xð Xð Xð0ð 0ð 0ð 0ð
Að 
Að 
Að 
Að<ð <ð <ð <ð3ð 3ð 3ð 3ð.ð .ð .ð .ð@ð @ð @ð @ð @ð @r   r	   )rv   Ú
__future__r   r6   ÚreÚsysÚpathlibr   ÚpytestÚ__file__ÚresolveÚparentÚPROJECT_ROOTr   r	   rw   r   r   ú<module>r�      sÛ   ðð
ð 
ð #Ð "Ð "Ð "Ð "Ð "Ø €€€Ø 	€	€	€	Ø 
€
€
€
Ø Ð Ð Ð Ð Ð Ø €€€àˆt�H‰~Œ~×%Ò%Ñ'Ô'Ô.Ô5Ô<€Ø˜XÑ%¨Ñ0°4Ñ7Ð:RÑR€ðE@ð E@ð E@ð E@ð E@ñ E@ô E@ð E@ð E@ð E@r   