Set the decision and its input
The synthetic input contains 中文. The first Enter has isComposing=true and confirms the candidate; the next is false and may submit. A listener reacting to every Enter submits twice. The corrected listener submits once while preserving the complete text.
| Case | Input JSON |
|---|---|
| Base test | {"text":"中文","enterCompositionFlags":[true,false]} |
Run the supplied comparison
Actual DOM KeyboardEvents with the supplied flags exercise the handler. This does not claim that every physical input method or operating system has been tested.
| Path | Output JSON |
|---|---|
| Faulty path | {"submissions":2,"finalText":"中文"} |
| Required path | {"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);}
Choose from the boundary evidence
Candidate confirmation alone gives zero submissions. Ordinary non-composing Enter gives one without clearing the field.
- Read the composition flag
- Permit the intended submit event
- Check count and retained text
Do not infer submission from candidate confirmation.
| Condition | Input JSON | Expected output JSON |
|---|---|---|
| candidate confirmation only | {"text":"你","enterCompositionFlags":[true]} | {"submissions":0,"finalText":"你"} |
| ordinary Enter | {"text":"Ada","enterCompositionFlags":[false]} | {"submissions":1,"finalText":"Ada"} |
How many submissions belong to the base sequence?
Show the worked answer
One, after the non-composing Enter. Confirming a candidate is a different action.