Files
odysseus/tests/documentSelectionCaret.mjs
T
Alexandre Teixeira f74a262f73 merge: reconcile PR 40 with current lab
Integrate lab fff55a78 into PR #40 (cc25d5ba). Lab's modular email
backend/frontend, modular settings, split stylesheets (static/style.css
stays deleted), procfs compatibility, and request-scoped TurnContract
authority win; PR #40's routing classifiers, editor/email/task features,
and style.css changes are ported into lab's module and stylesheet homes.

Integration fixes:
- settings/api.js imports ui.js under its canonical versioned URL
- browser observations keep legacy CAPTCHA/access-block evidence
- artifact turns do not re-trigger broad-web research recovery
- env reference documents PR test-tool variables; page regenerated

PR #40 defects surfaced by lab gates and fixed here:
- web_fetch generic schema drops top-level anyOf (OpenAI contract);
  the compact preview contract still requires url or urls
- get_weather registered as a brokered network read
- new lazy editor modules precached for offline use
- SearXNG pin mirrored into GPU standalone compose files
- image model picker again skips offline endpoints

Tests updated where PR #40 changed behaviour on purpose, and PR tests
moved onto lab's document_source helpers.
2026-10-01 05:03:58 +01:00

49 lines
2.2 KiB
JavaScript

import assert from 'node:assert/strict';
import { documentSource } from './helpers/document_source.mjs';
import { chromium } from 'playwright';
const source = documentSource();
const start = source.indexOf(' function clearSelection(');
const cleanup = source.slice(start, source.indexOf(' function clearSelectionAt(', start));
assert.equal((source.match(/if \(_selections.length\) clearSelection\(\{ preserveCaret: true \}\);/g) || []).length, 2);
const browser = await chromium.launch({ headless: true });
try {
const page = await browser.newPage();
await page.setContent('<div id="rich" contenteditable="true">before SELECT after</div><span id="doc-selection-badge"></span>');
await page.evaluate(cleanup => {
let _selections = [{ kind: 'rich' }];
const _richSelectionHighlightName = 'test-selection';
const rich = document.getElementById('rich');
const _emailRichbodyActive = () => rich;
const _scheduleDocumentStats = () => {};
eval(cleanup + '\nwindow.clearPinned = clearSelection;');
rich.addEventListener('input', () => {
if (_selections.length) window.clearPinned({ preserveCaret: true });
});
rich.focus();
const range = document.createRange();
range.setStart(rich.firstChild, 7);
range.setEnd(rich.firstChild, 13);
window.getSelection().removeAllRanges();
window.getSelection().addRange(range);
}, cleanup);
await page.keyboard.type('new');
const result = await page.evaluate(() => ({
text: document.getElementById('rich').textContent,
offset: window.getSelection().anchorOffset,
collapsed: window.getSelection().isCollapsed,
ranges: window.getSelection().rangeCount,
badge: document.getElementById('doc-selection-badge').style.display,
}));
assert.equal(result.text, 'before new after');
assert.equal(result.offset, 10);
assert.equal(result.collapsed, true);
assert.equal(result.ranges, 1);
assert.equal(result.badge, 'none');
await page.evaluate(() => window.clearPinned());
assert.equal(await page.evaluate(() => window.getSelection().rangeCount), 0);
console.log('PASS: replacement typing preserves caret; explicit clear still removes selection.');
} finally {
await browser.close();
}