57 lines
3.9 KiB
JavaScript
57 lines
3.9 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import fs from 'node:fs';
|
|
import {createRequire} from 'node:module';
|
|
import React from 'react';
|
|
import ts from 'typescript';
|
|
|
|
// Exercise the real component's event/state contract; this is not browser visual QA.
|
|
const require=createRequire(import.meta.url);
|
|
const source=fs.readFileSync(new URL('../../resources/js/components/territory-search-select.tsx',import.meta.url),'utf8');
|
|
const compiled=ts.transpileModule(source,{compilerOptions:{module:ts.ModuleKind.CommonJS,target:ts.ScriptTarget.ES2022,jsx:ts.JsxEmit.ReactJSX}}).outputText;
|
|
function harness(overrides={}){
|
|
const memory=[];let cursor=0;const chosen=[];
|
|
const hooks={...React,useState(initial){const i=cursor++;if(!(i in memory))memory[i]=initial;return[memory[i],value=>{memory[i]=typeof value==='function'?value(memory[i]):value;}];},useRef:()=>({current:null}),useEffect:()=>{}};
|
|
const module={exports:{}};
|
|
new Function('require','module','exports',compiled)(name=>name==='react'?hooks:require(name),module,module.exports);
|
|
const options=[{id:'province-a',name:'Nampula'},{id:'province-b',name:'Maputo Província'}];
|
|
const props={id:'territory',label:'Província',search:'',options,onSearch(query){props.search=query;},onChange(option){chosen.push(option);props.selected=option;},...overrides};
|
|
let tree;
|
|
const flat=node=>!node||typeof node!=='object'?[]:[node,...React.Children.toArray(node.props?.children).flatMap(flat)];
|
|
const render=()=>{cursor=0;tree=module.exports.default(props);return tree;};
|
|
const find=predicate=>flat(tree).find(predicate);
|
|
const input=()=>find(node=>node.type==='input');
|
|
const key=value=>input().props.onKeyDown({key:value,preventDefault(){}});
|
|
render();return{props,chosen,render,find,input,key,options};
|
|
}
|
|
test('one search input owns the dropdown; the separate select is absent',()=>{
|
|
const h=harness();assert.equal(h.find(node=>node.type==='select'),undefined);
|
|
assert.equal(h.input().props.role,'combobox');assert.equal(h.input().props['aria-expanded'],false);
|
|
h.input().props.onFocus();h.render();
|
|
assert.equal(h.input().props['aria-expanded'],true);
|
|
assert.ok(h.find(node=>node.props?.role==='listbox'));
|
|
h.find(node=>node.props?.role==='option'&&node.props.id==='territory-options-1').props.onClick();h.render();
|
|
assert.deepEqual(h.chosen,[h.options[1]]);assert.equal(h.input().props.value,'Maputo Província');
|
|
assert.equal(h.input().props['aria-expanded'],false);
|
|
});
|
|
test('typing and Escape preserve the existing selection until an option is explicitly chosen',()=>{
|
|
const selected={id:'saved',name:'Selecção guardada fora desta página'};const h=harness({selected});
|
|
h.input().props.onFocus();h.render();h.input().props.onChange({target:{value:'Map'}});h.render();
|
|
assert.equal(h.props.search,'Map');assert.deepEqual(h.chosen,[]);
|
|
h.key('Escape');h.render();assert.equal(h.input().props.value,selected.name);assert.deepEqual(h.chosen,[]);
|
|
});
|
|
test('arrow keys and Enter choose from the list; loading cannot commit an old result',()=>{
|
|
const h=harness();h.input().props.onFocus();h.render();h.key('ArrowDown');h.render();h.key('ArrowDown');h.render();
|
|
assert.equal(h.input().props['aria-activedescendant'],'territory-options-1');
|
|
h.key('Enter');h.render();assert.deepEqual(h.chosen,[h.options[1]]);
|
|
const busy=harness({loading:true});busy.input().props.onFocus();busy.render();busy.key('Enter');
|
|
assert.deepEqual(busy.chosen,[]);
|
|
});
|
|
test('clear is explicit; disabled dependent levels cannot expose the dropdown',()=>{
|
|
const h=harness({selected:{id:'saved',name:'Nampula'}});
|
|
h.find(node=>node.props?.['aria-label']==='Limpar Província').props.onClick();h.render();
|
|
assert.deepEqual(h.chosen,[undefined]);assert.equal(h.input().props.value,'');
|
|
const disabled=harness({disabled:true});assert.equal(disabled.input().props.disabled,true);
|
|
assert.equal(disabled.find(node=>node.props?.role==='listbox'),undefined);
|
|
});
|