§
    ß°½jY(  ã                   ó  — d Z ddlZddlZddlZddlmZ  ee¦  «                             ¦   «         j        j        j        Z	e	dz  dz  dz  dz  dz  Z
d	ed
efd„Z G d„ dej        ¦  «        Zedk    r ej        ¦   «          dS dS )aÊ  
Adversarial Challenger Test Suite: PointerEvents & Ergonomics Verifier (Milestone 1)
Empirical verification of:
1. Multi-touch pointer isolation (left thumb joystick vs simultaneous 3-skill right thumb taps)
2. Dead-zone model: strict clamping (< 0.15) and smooth scaling (0.151 -> 1.0 -> over-drag)
3. Lost pointer capture resilience (pointercancel, off-canvas tracking, window blur, resize)
4. Mixed Pointer/Touch event defense & second-finger contention
é    N)ÚPathÚclientÚwebappÚjsÚenginezjoystick.jsÚscript_bodyÚreturnc                 ó`  — dt          j        t          t          ¦  «        ¦  «        › d| › d�}t	          j        dd|gddt          t          ¦  «        ¬¦  «        }|j        dk    rt          d	|j	        › d|j
        › �¦  «        ‚t          j        |j
                             ¦   «         ¦  «        S )
z9Executes an empirical JavaScript test harness in Node.js.a‚  
const fs = require('fs');
function setupEnvironment() {
  const wL = {}, el = {};
  function mkEl(id) {
    const l = {};
    return {
      id, style: {}, classList: { add() {}, remove() {}, contains() { return false; } },
      getBoundingClientRect: () => ({ left: 0, top: 0, width: 320, height: 480 }),
      addEventListener(e, h) { (l[e] = l[e] || []).push(h); },
      dispatchEvent(e) { (l[e.type] || []).forEach(h => h(e)); },
      setPointerCapture(id) { this.cap = id; },
      releasePointerCapture(id) { if (this.cap === id) this.cap = null; }
    };
  }
  const win = {
    addEventListener(e, h) { (wL[e] = wL[e] || []).push(h); },
    dispatchEvent(e) { (wL[e.type] || []).forEach(h => h(e)); },
    setTimeout: (fn) => fn(), clearTimeout: () => {}
  };
  const doc = { getElementById: (id) => (el[id] = el[id] || mkEl(id)), activeElement: null };
  const player = { dirX: 0, dirY: 0 };
  ['joystick-zone', 'joystick-base', 'joystick-stick', 'app-viewport', 'skill-fire', 'skill-thunder', 'skill-frost'].forEach(id => doc.getElementById(id));
  global.window = win; global.document = doc; global.player = player;
  eval(fs.readFileSync(z5, 'utf8'));
  return { win, doc, player, el, wL };
}
ú
Únodez-eT)Úcapture_outputÚtextÚcwdr   zHarness failed:
)ÚjsonÚdumpsÚstrÚJOYSTICK_JSÚ
subprocessÚrunÚPROJECT_ROOTÚ
returncodeÚRuntimeErrorÚstderrÚstdoutÚloadsÚstrip)r   ÚrunnerÚress      úGC:\Projects\FreeExile\tests\unit\test_challenger_m1_joystick_pointer.pyÚrun_node_harnessr       s·   € ðõ0 œ
¥3¥{Ñ#3Ô#3Ñ4Ô4ð1ð ð6 ð7ð ð €Fõ: Œ.˜& $¨Ð/ÀÈ4ÕUXÕYeÑUfÔUfÐ
gÑ
gÔ
g€CØ
„~˜ÒÐÝÐI¨s¬zÐIÐI¸S¼ZÐIÐIÑJÔJÐJÝŒ:�c”j×&Ò&Ñ(Ô(Ñ)Ô)Ð)ó    c                   ó0   — e Zd ZdZd„ Zd„ Zd„ Zd„ Zd„ ZdS )Ú$TestAdversarialJoystickPointerEventszBAdversarial stress-testing suite for PointerEvents and ergonomics.c                 óä  — d}t          |¦  «        }|                      |d         d         ¦  «         |                      |d         d         d¦  «         |                      |d         d         d¦  «         |                      |d	         d         ¦  «         |                      |d	         d         |d         d         d
¬¦  «         |                      |d         d         d¦  «         |                      |d         d         d¦  «         |                      |d         d         ¦  «         |                      |d         d         d¦  «         |                      |d         d         ¦  «         dS )z<Simultaneous left thumb movement + right thumb 3-skill spam.a  
        const env = setupEnvironment(), jz = env.el['joystick-zone'];
        const [sF, sT, sFr] = [env.el['skill-fire'], env.el['skill-thunder'], env.el['skill-frost']];
        const res = {};
        jz.dispatchEvent({ type: 'pointerdown', pointerId: 101, clientX: 100, clientY: 200, pointerType: 'touch' });
        res.down = { active: env.win.joystickActive, cap: jz.cap };
        jz.dispatchEvent({ type: 'pointermove', pointerId: 101, clientX: 130, clientY: 200 });
        res.move1 = { dirX: global.player.dirX, dirY: global.player.dirY };
        [sF, sT, sFr].forEach((s, idx) => {
          s.dispatchEvent({ type: 'pointerdown', pointerId: 102 + idx, clientX: 280, clientY: 400 });
          s.dispatchEvent({ type: 'pointerup', pointerId: 102 + idx, clientX: 280, clientY: 400 });
        });
        res.afterSkills = { active: env.win.joystickActive, dirX: global.player.dirX };
        jz.dispatchEvent({ type: 'pointermove', pointerId: 101, clientX: 130, clientY: 230 });
        res.move2 = { dirX: global.player.dirX, dirY: global.player.dirY };
        jz.dispatchEvent({ type: 'pointerup', pointerId: 101, clientX: 130, clientY: 230 });
        res.up = { active: env.win.joystickActive, dirX: global.player.dirX, cap: jz.cap };
        console.log(JSON.stringify(res));
        ÚdownÚactiveÚcapée   Úmove1ÚdirXç        ÚafterSkillsé   ©ÚplacesÚmove2ÚdirYÚupN)r    Ú
assertTrueÚassertEqualÚassertGreaterÚassertAlmostEqualÚassertFalseÚassertIsNone©Úselfr   Úrs      r   Ú3test_multitouch_pointer_isolation_during_skill_spamzXTestAdversarialJoystickPointerEvents.test_multitouch_pointer_isolation_during_skill_spam;   sW  € ðˆõ& ˜RÑ Ô ˆØ�Š˜˜&œ	 (Ô+Ñ,Ô,Ð,Ø×Ò˜˜6œ 5Ô)¨3Ñ/Ô/Ð/Ø×Ò˜1˜Wœ: fÔ-¨sÑ3Ô3Ð3Ø�Š˜˜-Ô(¨Ô2Ñ3Ô3Ð3Ø×Ò˜q Ô/°Ô7¸¸7¼ÀFÔ9KÐTUÐÑVÔVÐVØ×Ò˜1˜Wœ: fÔ-¨sÑ3Ô3Ð3Ø×Ò˜1˜Wœ: fÔ-¨sÑ3Ô3Ð3Ø×Ò˜˜4œ Ô*Ñ+Ô+Ð+Ø×Ò˜˜4œ œ¨#Ñ.Ô.Ð.Ø×Ò˜!˜Dœ' %œ.Ñ)Ô)Ð)Ð)Ð)r!   c                 óš  — d}t          |¦  «        }dD ]P}|                      ||         d         dd|› d�¦  «         |                      ||         d         dd|› d�¦  «         ŒQ|                      |d         d         d¦  «         |                      |d         d         d	¦  «         |                      |d         d         |d
         d         ¦  «         |                      |d
         d         |d         d         ¦  «         |                      |d         d         |d         d         ¦  «         |                      |d         d         |d         d         ¦  «         |                      |d         d         dd¬¦  «         |                      |d         d         dd¬¦  «         |                      d|d         d         ¦  «         dS )zEDead-zone clamp (< 0.15), boundary onset (0.151), and smooth scaling.ag  
        const env = setupEnvironment(), jz = env.el['joystick-zone'];
        jz.dispatchEvent({ type: 'pointerdown', pointerId: 1, clientX: 100, clientY: 200, pointerType: 'touch' });
        const testMags = [0.05, 0.10, 0.149, 0.151, 0.25, 0.50, 0.75, 1.00, 1.50];
        const res = {};
        testMags.forEach(m => {
          jz.dispatchEvent({ type: 'pointermove', pointerId: 1, clientX: 100 + m * 46, clientY: 200 });
          res[m] = { dirX: global.player.dirX, dirY: global.player.dirY, stick: env.el['joystick-stick'].style.transform };
        });
        console.log(JSON.stringify(res));
        )z0.05z0.1z0.149r*   r+   zMag z must strictly clamp to 0r1   z0.151g{®Gázt?z0.25z0.5z0.75Ú1g      ð?r-   r.   z1.5Ú46pxÚstickN)r    r4   r5   Ú
assertLessr6   ÚassertIn)r:   r   r;   Úms       r   Ú*test_dead_zone_clamping_and_smooth_scalingzOTestAdversarialJoystickPointerEvents.test_dead_zone_clamping_and_smooth_scaling\   sÁ  € ð
ˆõ ˜RÑ Ô ˆØ)ð 	Uð 	UˆAØ×Ò˜Q˜qœT &œ\¨3Ð0S°qÐ0SÐ0SÐ0SÑTÔTÐTØ×Ò˜Q˜qœT &œ\¨3Ð0S°qÐ0SÐ0SÐ0SÑTÔTÐTÐTØ×Ò˜1˜Wœ: fÔ-¨sÑ3Ô3Ð3Ø�Š˜˜'œ
 6Ô*¨EÑ2Ô2Ð2Ø�Š˜˜'œ
 6Ô*¨A¨f¬I°fÔ,=Ñ>Ô>Ð>Ø�Š˜˜&œ	 &Ô)¨1¨U¬8°FÔ+;Ñ<Ô<Ð<Ø�Š˜˜%œ Ô(¨!¨F¬)°FÔ*;Ñ<Ô<Ð<Ø�Š˜˜&œ	 &Ô)¨1¨S¬6°&¬>Ñ:Ô:Ð:Ø×Ò˜q œv fœ~¨s¸1ÐÑ=Ô=Ð=Ø×Ò˜q œx¨Ô/°¸QÐÑ?Ô?Ð?Ø�Š�f˜a œh wÔ/Ñ0Ô0Ð0Ð0Ð0r!   c                 ó(  — d}t          |¦  «        }|                      |d         ¦  «         |                      |d         d         ¦  «         |                      |d         d         d¦  «         |                      |d         d         ¦  «         |                      |d         d         d¦  «         |                      |d	         d         ¦  «         |                      |d	         d         d¦  «         |                      |d
         d¦  «         dS )zApointercancel on active vs foreign, off-canvas, blur, and resize.aÃ  
        const env = setupEnvironment(), jz = env.el['joystick-zone'];
        jz.dispatchEvent({ type: 'pointerdown', pointerId: 55, clientX: 100, clientY: 200, pointerType: 'touch' });
        jz.dispatchEvent({ type: 'pointermove', pointerId: 55, clientX: 130, clientY: 200 });
        jz.dispatchEvent({ type: 'pointercancel', pointerId: 999 });
        const foreignActive = env.win.joystickActive;
        jz.dispatchEvent({ type: 'pointercancel', pointerId: 55 });
        const activeCancel = { active: env.win.joystickActive, dirX: global.player.dirX, cap: jz.cap };
        jz.dispatchEvent({ type: 'pointerdown', pointerId: 77, clientX: 100, clientY: 200, pointerType: 'touch' });
        jz.dispatchEvent({ type: 'pointermove', pointerId: 77, clientX: -800, clientY: 3000 });
        const offCanvas = { dirX: global.player.dirX, dirY: global.player.dirY };
        env.win.dispatchEvent({ type: 'blur' });
        const blur = { active: env.win.joystickActive, dirX: global.player.dirX };
        env.win.dispatchEvent({ type: 'resize' });
        const restLeft = env.el['joystick-base'].style.left;
        console.log(JSON.stringify({ foreignActive, activeCancel, offCanvas, blur, restLeft }));
        ÚforeignActiveÚactiveCancelr&   r*   r+   r'   Ú	offCanvasÚblurÚrestLeftÚ90pxN)r    r3   r7   r4   r8   ÚassertNotEqualr9   s      r   Ú-test_lost_pointer_capture_and_edge_resiliencezRTestAdversarialJoystickPointerEvents.test_lost_pointer_capture_and_edge_resiliencew   s  € ðˆõ" ˜RÑ Ô ˆØ�Š˜˜/Ô*Ñ+Ô+Ð+Ø×Ò˜˜>Ô*¨8Ô4Ñ5Ô5Ð5Ø×Ò˜˜>Ô*¨6Ô2°CÑ8Ô8Ð8Ø×Ò˜!˜NÔ+¨EÔ2Ñ3Ô3Ð3Ø×Ò˜A˜kœN¨6Ô2°CÑ8Ô8Ð8Ø×Ò˜˜6œ 8Ô,Ñ-Ô-Ð-Ø×Ò˜˜6œ 6Ô*¨CÑ0Ô0Ð0Ø×Ò˜˜:œ¨Ñ/Ô/Ð/Ð/Ð/r!   c                 ó  — d}t          |¦  «        }|                      |d         d¦  «         |                      |d         |d         ¦  «         |                      |d         ¦  «         |                      |d         |d         ¦  «         dS )	zHSecond finger on joystickZone must not steal pointer or cancel movement.aÝ  
        const env = setupEnvironment(), jz = env.el['joystick-zone'];
        jz.dispatchEvent({ type: 'pointerdown', pointerId: 1, clientX: 100, clientY: 200, pointerType: 'touch' });
        jz.dispatchEvent({ type: 'pointermove', pointerId: 1, clientX: 130, clientY: 200 });
        const dir1 = global.player.dirX;
        jz.dispatchEvent({ type: 'pointerdown', pointerId: 2, clientX: 110, clientY: 210, pointerType: 'touch' });
        const capDuring = jz.cap;
        jz.dispatchEvent({ type: 'pointermove', pointerId: 2, clientX: 150, clientY: 250 });
        const dirDuring = global.player.dirX;
        jz.dispatchEvent({ type: 'pointerup', pointerId: 2, clientX: 150, clientY: 250 });
        const activeAfter = env.win.joystickActive;
        jz.dispatchEvent({ type: 'pointermove', pointerId: 1, clientX: 140, clientY: 200 });
        const dirFinal = global.player.dirX;
        console.log(JSON.stringify({ capDuring, dir1, dirDuring, activeAfter, dirFinal }));
        Ú	capDuringé   Ú	dirDuringÚdir1ÚactiveAfterÚdirFinalN)r    r4   r3   r5   r9   s      r   Ú;test_multitouch_second_finger_on_joystick_zone_pure_pointerz`TestAdversarialJoystickPointerEvents.test_multitouch_second_finger_on_joystick_zone_pure_pointer”   s‹   € ðˆõ ˜RÑ Ô ˆØ×Ò˜˜;œ¨Ñ+Ô+Ð+Ø×Ò˜˜;œ¨¨6¬Ñ3Ô3Ð3Ø�Š˜˜-Ô(Ñ)Ô)Ð)Ø×Ò˜1˜Zœ=¨!¨F¬)Ñ4Ô4Ð4Ð4Ð4r!   c                 ó–   — d}t          |¦  «        }|                      |d         ¦  «         |                      |d         d¦  «         dS )zPEmpirically confirms that unshielded touchend at line 145 calls resetJoystick().a  
        const env = setupEnvironment(), jz = env.el['joystick-zone'];
        jz.dispatchEvent({ type: 'pointerdown', pointerId: 1, clientX: 100, clientY: 200, pointerType: 'touch' });
        jz.dispatchEvent({ type: 'pointermove', pointerId: 1, clientX: 130, clientY: 200 });
        const activeBefore = env.win.joystickActive;
        jz.dispatchEvent({ type: 'touchend', preventDefault: () => {} });
        const activeAfter = env.win.joystickActive;
        console.log(JSON.stringify({ activeBefore, activeAfter }));
        ÚactiveBeforerS   z3Confirms unshielded touchend resets active joystickN)r    r3   r7   r9   s      r   Ú3test_multitouch_mixed_legacy_touchend_vulnerabilityzXTestAdversarialJoystickPointerEvents.test_multitouch_mixed_legacy_touchend_vulnerability«   sQ   € ðˆõ ˜RÑ Ô ˆØ�Š˜˜.Ô)Ñ*Ô*Ð*à×Ò˜˜=Ô)Ð+`ÑaÔaÐaÐaÐar!   N)	Ú__name__Ú
__module__Ú__qualname__Ú__doc__r<   rD   rM   rU   rX   © r!   r   r#   r#   8   sk   € € € € € ØLÐLð*ð *ð *ðB1ð 1ð 1ð60ð 0ð 0ð:5ð 5ð 5ð.bð bð bð bð br!   r#   Ú__main__)r\   r   r   ÚunittestÚpathlibr   Ú__file__ÚresolveÚparentr   r   r   Údictr    ÚTestCaser#   rY   Úmainr]   r!   r   ú<module>rg      s  ððð ð €€€Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ð Ð àˆt�H‰~Œ~×%Ò%Ñ'Ô'Ô.Ô5Ô<€Ø˜XÑ%¨Ñ0°4Ñ7¸(ÑBÀ]ÑR€ð"* #ð "*¨$ð "*ð "*ð "*ð "*ðJAbð Abð Abð Abð Ab¨8Ô+<ñ Abô Abð AbðH ˆzÒÐØ€H„M�O„O€O€O€Oð Ðr!   