Files
baiyue-logistics-chat/tests/frontend/support.test.cjs
T

123 lines
5.2 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const source = fs.readFileSync(path.join(__dirname, '../../static/js/support.js'), 'utf8');
function createSupportUI(){
let document;
class Element {
constructor(tagName = 'div'){
this.tagName = tagName.toUpperCase();
this.children = [];
this.attributes = {};
this.listeners = {};
this.style = {};
this.value = '';
this.parent = null;
this.className = '';
this.text = '';
}
set textContent(value){ this.text = String(value); this.children = []; }
get textContent(){ return this.text + this.children.map(child => child.textContent).join(''); }
appendChild(child){ child.parent = this; this.children.push(child); return child; }
remove(){ if(this.parent) this.parent.children = this.parent.children.filter(child => child !== this); this.parent = null; }
setAttribute(name, value){ this.attributes[name] = String(value); }
getAttribute(name){ return this.attributes[name] ?? null; }
addEventListener(name, callback){ (this.listeners[name] ||= []).push(callback); }
contains(target){ return this === target || this.children.some(child => child.contains(target)); }
querySelectorAll(selector){
const tags = selector.split(',').map(value => value.trim().toUpperCase());
return this.children.flatMap(child => [
...(tags.includes(child.tagName) ? [child] : []),
...child.querySelectorAll(selector),
]);
}
focus(){ document.activeElement = this; }
dispatch(name, event = {}){
event.target ||= this;
event.preventDefault ||= function(){ this.defaultPrevented = true; };
event.stopPropagation ||= function(){};
if(this['on' + name]) this['on' + name](event);
(this.listeners[name] || []).forEach(callback => callback(event));
}
click(){ this.dispatch('click'); }
}
document = {
body: new Element('body'),
activeElement: null,
createElement: tagName => new Element(tagName),
};
const context = vm.createContext({document, window:{}});
vm.runInContext(source, context, {filename:'support.js'});
return {ui:context.SupportUI, document};
}
test('confirm resolves from the in-page dialog and restores focus', async () => {
const {ui, document} = createSupportUI();
const trigger = document.createElement('button');
document.body.appendChild(trigger);
trigger.focus();
const result = ui.confirm('确定清空记录?', {title:'请确认', confirmText:'清空', cancelText:'取消'});
await Promise.resolve();
const dialog = document.body.children[1].children[0];
assert.equal(dialog.getAttribute('role'), 'dialog');
assert.equal(dialog.getAttribute('aria-modal'), 'true');
assert.match(dialog.textContent, /确定清空记录?/);
const [cancel, confirm] = dialog.querySelectorAll('button');
assert.equal(document.activeElement, cancel);
confirm.click();
assert.equal(await result, true);
assert.equal(document.body.children.length, 1);
assert.equal(document.activeElement, trigger);
});
test('confirm returns false when cancelled with Escape', async () => {
const {ui, document} = createSupportUI();
const result = ui.confirm('是否继续?');
await Promise.resolve();
const dialog = document.body.children[0].children[0];
const event = {key:'Escape'};
dialog.dispatch('keydown', event);
assert.equal(event.defaultPrevented, true);
assert.equal(await result, false);
assert.equal(document.body.children.length, 0);
});
test('prompt returns entered text and focuses its labelled input', async () => {
const {ui, document} = createSupportUI();
const result = ui.prompt('请输入运单号', {title:'查询物流', inputLabel:'运单号'});
await Promise.resolve();
const dialog = document.body.children[0].children[0];
const input = dialog.querySelectorAll('input')[0];
assert.ok(input);
assert.equal(input.getAttribute('aria-label'), '运单号');
assert.equal(document.activeElement, input);
input.value = 'AB123';
dialog.querySelectorAll('button')[1].click();
assert.equal(await result, 'AB123');
});
test('prompt returns null when the backdrop is clicked', async () => {
const {ui, document} = createSupportUI();
const result = ui.prompt('请输入留言');
await Promise.resolve();
document.body.children[0].click();
assert.equal(await result, null);
});
test('dialog keeps keyboard focus inside while tabbing', async () => {
const {ui, document} = createSupportUI();
ui.confirm('确认操作?');
await Promise.resolve();
const dialog = document.body.children[0].children[0];
const [cancel, confirm] = dialog.querySelectorAll('button');
cancel.focus();
dialog.dispatch('keydown', {key:'Tab', shiftKey:true});
assert.equal(document.activeElement, confirm);
dialog.dispatch('keydown', {key:'Tab'});
assert.equal(document.activeElement, cancel);
});