import assert from 'node:assert/strict'; import test from 'node:test'; import {readFileSync} from 'node:fs'; const listeners={}; const input={value:'',addEventListener:(name,handler)=>{listeners[`input:${name}`]=handler},focus:()=>{}}; const closeButton={addEventListener:(name,handler)=>{listeners[`close:${name}`]=handler}}; const hint={hidden:true,setAttribute:()=>{},querySelector:()=>closeButton}; const form={dataset:{chatCoachKey:'test-ticket'},action:'/tickets/example/reply/',querySelector:()=>input}; const renderedMessages=[]; let appendedHint=null; globalThis.document={ body:{append:node=>{appendedHint=node}}, querySelectorAll:selector=>selector==='form[data-chat-coach]'?[form]:renderedMessages, querySelector:selector=>selector==='[data-conversation]'?{}:null, createElement:()=>hint, }; globalThis.location={origin:'http://testserver',pathname:'/tickets/example/'}; globalThis.MutationObserver=class{constructor(handler){listeners.mutation=handler}observe(){}}; globalThis.setTimeout=handler=>{listeners.autoDismiss=handler;return 1}; globalThis.clearTimeout=()=>{delete listeners.autoDismiss}; let now=1_000_000; Date.now=()=>now; await import('../../static/js/chat-coach.js'); test('mounts guidance directly on body so collapsed forms cannot hide it',()=>{ assert.equal(appendedHint,hint); assert.match(hint.className,/chat-coach-floating/); }); test('shows guidance when the last two messages are at most two minutes apart',()=>{ renderedMessages.push( {dataset:{chatCoachAt:String((now-40_000)/1000)}}, {dataset:{chatCoachAt:String((now-10_000)/1000)}}, ); listeners.mutation(); assert.equal(hint.hidden,false); }); test('hides guidance when the last two messages are more than two minutes apart',()=>{ renderedMessages.splice(0,renderedMessages.length, {dataset:{chatCoachAt:String((now-200_000)/1000)}}, {dataset:{chatCoachAt:String((now-10_000)/1000)}}, ); listeners.mutation(); assert.equal(hint.hidden,true); }); test('guidance is configured to disappear automatically after three seconds',()=>{ const script=readFileSync('static/js/chat-coach.js','utf8'); assert.match(script,/autoDismissMs=3000/); assert.match(script,/setTimeout\(\(\)=>dismiss\(\),autoDismissMs\)/); assert.equal(typeof listeners.autoDismiss,'function'); }); test('large close button dismisses the guidance for the current page',()=>{ renderedMessages.splice(0,renderedMessages.length, {dataset:{chatCoachAt:String((now-30_000)/1000)}}, {dataset:{chatCoachAt:String((now-5_000)/1000)}}, ); listeners.mutation(); assert.equal(hint.hidden,false); listeners['close:click'](); assert.equal(hint.hidden,true); });