pediatric-ai-scribe-v3/test/assistant-enter-to-send.test.js
Daniel 74aa0c1b89
Some checks failed
Forgejo Android APK / Root app tests (push) Successful in 54s
Forgejo Docker Build / Root app tests (push) Successful in 48s
Forgejo Android APK / Build signed APK (push) Successful in 2m23s
Forgejo Docker Build / Build Docker image (push) Successful in 9s
Forgejo Docker Build / Deploy to the host (push) Failing after 0s
feat: Enter sends in the assistant, and the person chooses
Enter made a newline and Ctrl+Enter sent, which is backwards from every chat
people use. Enter now sends by default, with a toggle in the composer's + menu
to put it back.

Two rules hold whatever is chosen, because they are the habits people arrive
with and a setting that broke either would be worse than no setting:
Shift+Enter is always a newline, Ctrl/Cmd+Enter always sends. Both are checked
before the preference, so neither can be switched off.

A keystroke during IME composition never sends. Enter accepts a candidate word
in Chinese, Japanese and Korean, and on predictive Android keyboards; sending
there would cut a sentence off mid-word.

Stored per device rather than per account, because a keyboard preference
belongs to the keyboard: Enter-to-send suits a desk and usually does not suit a
phone, where Enter is how you get a second line. Unset, it defaults by device
class — send where there is a real keyboard, newline on a touch screen — and a
blocked localStorage falls through to that default rather than throwing.

The composer's tooltip says which key sends, where someone already looks when
they wonder.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Dv6sqaY6Vq3ChZHMem3cnU
2026-09-12 18:05:36 +02:00

51 lines
2.7 KiB
JavaScript

// Enter sends, or makes a newline, and the person decides. Two rules hold
// whatever they choose: Shift+Enter is always a newline, Ctrl/Cmd+Enter always
// sends. Those are the habits people arrive with.
const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const src = fs.readFileSync(path.join(__dirname, '..', 'public/js/clinicalAssistant.js'), 'utf8');
const handler = src.slice(src.indexOf("input.addEventListener('keydown'"), src.indexOf('var enterToggle'));
test('Ctrl or Cmd + Enter always sends, whatever the preference', () => {
assert.match(handler, /if \(\(e\.ctrlKey \|\| e\.metaKey\)\) \{ e\.preventDefault\(\); onAsk\(e\); return; \}/);
// It is checked before the preference, so it cannot be switched off.
assert.ok(handler.indexOf('ctrlKey') < handler.indexOf('enterSends()'),
'the modifier must win over the preference');
});
test('Shift+Enter is always a newline, whatever the preference', () => {
assert.match(handler, /if \(e\.shiftKey \|\| e\.altKey\) return;/);
assert.ok(handler.indexOf('shiftKey') < handler.indexOf('enterSends()'));
});
test('an IME composition keystroke never sends', () => {
// Enter accepts a candidate word in Chinese, Japanese, Korean and on
// predictive Android keyboards. Sending there cuts a sentence mid-word.
assert.match(handler, /if \(e\.isComposing \|\| e\.keyCode === 229\) return;/);
assert.ok(handler.indexOf('isComposing') < handler.indexOf('ctrlKey'),
'composition is checked before anything can send');
});
test('the preference is per device, and survives storage being unavailable', () => {
const pref = src.slice(src.indexOf('function enterSends()'), src.indexOf('function applyEnterHint'));
assert.match(pref, /localStorage\.getItem\(ENTER_KEY\)/);
assert.match(pref, /catch \(e\)/, 'a private window must not throw here');
// Unset defaults by device class rather than guessing one answer for both.
assert.match(pref, /\(hover: none\) and \(pointer: coarse\)/);
});
test('the toggle reflects and writes the preference', () => {
const bind = src.slice(src.indexOf('var enterToggle'), src.indexOf('bindExampleButtons'));
assert.match(bind, /enterToggle\.checked = enterSends\(\)/, 'opens showing the current state');
assert.match(bind, /setEnterSends\(enterToggle\.checked\)/);
const html = fs.readFileSync(path.join(__dirname, '..', 'public/components/assistant.html'), 'utf8');
assert.match(html, /id="assistant-enter-sends"/);
});
test('the composer says which key sends', () => {
assert.match(src, /Enter sends · Shift\+Enter for a new line/);
assert.match(src, /Enter for a new line · Ctrl\+Enter sends/);
});