訂判斷及所需輸入
假設輸入是「中文」。首次 Enter 的 isComposing=true,用來確認候選字;第二次是 false,才可送出。逢 Enter 都送會提交兩次;更正後只送一次,完整文字仍保留。
| 情況 | 輸入 JSON |
|---|---|
| 原例測試 | {"text":"中文","enterCompositionFlags":[true,false]} |
跑所附比較
把所附標記放進真正 DOM KeyboardEvent,實際試監聽器;未表示已試所有實體輸入法或作業系統。
| 路徑 | 輸出 JSON |
|---|---|
| 錯誤路徑 | {"submissions":2,"finalText":"中文"} |
| 所需路徑 | {"submissions":1,"finalText":"中文"} |
import assert from 'node:assert/strict';
import {createRequire} from 'node:module';
const htmlEscape=s=>String(s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
async function withPage(action,options={}){
const require=createRequire(import.meta.url);const {chromium}=require('playwright');
const browser=await chromium.launch({headless:true});
try{const context=await browser.newContext(options);const page=await context.newPage();
const requests=[];await page.route('**/*',route=>{requests.push(route.request().url());return route.abort();});
return await action(page,context,requests);
}finally{await browser.close();}
}
const run=async function run(i,corrected=true){
if(!i.enterCompositionFlags.every(v=>typeof v==='boolean'))return {held:true};
return withPage(async page=>{await page.setContent('<label>Memo<input id="memo"></label><output id="count">0</output>');
await page.evaluate(corrected=>{let count=0;document.querySelector('#memo').addEventListener('keydown',e=>{if(e.key==='Enter'&&(!corrected||!e.isComposing))document.querySelector('#count').textContent=String(++count);});},corrected);
await page.fill('#memo',i.text);for(const composing of i.enterCompositionFlags)await page.dispatchEvent('#memo','keydown',{key:'Enter',isComposing:composing});
return page.evaluate(()=>({submissions:Number(document.querySelector('#count').textContent),finalText:document.querySelector('#memo').value}));});
};
const cases=[{"input":{"text":"中文","enterCompositionFlags":[true,false]},"corrected":false,"expected":{"submissions":2,"finalText":"中文"}},{"input":{"text":"中文","enterCompositionFlags":[true,false]},"corrected":true,"expected":{"submissions":1,"finalText":"中文"}},{"input":{"text":"你","enterCompositionFlags":[true]},"corrected":true,"expected":{"submissions":0,"finalText":"你"}},{"input":{"text":"Ada","enterCompositionFlags":[false]},"corrected":true,"expected":{"submissions":1,"finalText":"Ada"}}];
for(const test of cases){const actual=await run(test.input,test.corrected);assert.deepEqual(actual,test.expected);console.log(actual);}
按邊界依據作判斷
只有候選確認是零次;普通非組字 Enter 一次,欄內文字不清。
- 核組字標記
- 只放行所需送出事件
- 核次數及原文
確認候選字不等於提交。
| 條件 | 輸入 JSON | 預期輸出 JSON |
|---|---|---|
| 只確認候選字 | {"text":"你","enterCompositionFlags":[true]} | {"submissions":0,"finalText":"你"} |
| 普通 Enter | {"text":"Ada","enterCompositionFlags":[false]} | {"submissions":1,"finalText":"Ada"} |
原例兩個事件,應送出幾次?
查看參考答案
一次,在非組字 Enter 後;確認候選字是另一個動作。