test: coverage for learning-hub page mode, chat pin/rename menu, example auto-ask
This commit is contained in:
parent
050ec635d2
commit
b9ac93ddc6
3 changed files with 55 additions and 0 deletions
|
|
@ -23,6 +23,7 @@ function ui(t, options = {}) {
|
|||
var nextSaveId = 1;
|
||||
const context = { window, document: window.document, console, URL, Blob, TextDecoder, AbortController, crypto: webcrypto,
|
||||
navigator: { clipboard: { writeText: async text => { copies.push(text); } } },
|
||||
localStorage: { getItem: k => window.localStorage.getItem(k), setItem: (k,v) => window.localStorage.setItem(k,v) },
|
||||
setTimeout() {}, clearTimeout() {}, showToast: (...args) => toasts.push(args), EMPTY_PROMPT_SETS: [[]],
|
||||
createAssistantImageStore: () => ({ clear() {}, renderGeneratedImage: () => '' }),
|
||||
fetchAssistantStatus: async () => ({ success: true }),
|
||||
|
|
@ -111,6 +112,37 @@ test('saved-chat rows open on click; the hover trash deletes without opening', a
|
|||
assert.deepEqual(deleted, ['7'], 'menu delete removes the chat without opening it');
|
||||
});
|
||||
|
||||
test('chat menu pins a chat to the top and renames inline via the server', async t => {
|
||||
const app = ui(t);
|
||||
const c = app.context;
|
||||
const older = { id: 2, title: 'Older chat', updated_at: '2026-09-01T00:00:00Z' };
|
||||
const newer = { id: 9, title: 'Newer chat', updated_at: '2026-09-08T00:00:00Z' };
|
||||
c.fetchSavedAssistantChats = async () => ({ success: true, chats: [older, newer] });
|
||||
const renames = [];
|
||||
c.renameSavedAssistantChat = async (id, title) => { renames.push([String(id), title]); return { success: true }; };
|
||||
await c.loadSavedChats();
|
||||
const rows = () => [...app.document.querySelectorAll('[data-assistant-load-chat]')];
|
||||
assert.equal(rows()[0].getAttribute('data-assistant-load-chat'), '9', 'newest first before pinning');
|
||||
rows()[1].querySelector('[data-assistant-chat-menu]').click();
|
||||
await new Promise(r => setImmediate(r));
|
||||
const pop = app.document.querySelector('[data-assistant-chat-menu-pop]');
|
||||
pop.querySelector('[data-chat-action="pin"]').click();
|
||||
await new Promise(r => setImmediate(r)); await new Promise(r => setImmediate(r));
|
||||
assert.equal(rows()[0].getAttribute('data-assistant-load-chat'), '2', 'pinned chat jumps to the top');
|
||||
assert.match(rows()[0].querySelector('.assistant-saved-chat-title').innerHTML, /fa-thumbtack/, 'pin icon shown');
|
||||
rows()[0].querySelector('[data-assistant-chat-menu]').click();
|
||||
await new Promise(r => setImmediate(r));
|
||||
const pop2 = app.document.querySelector('[data-assistant-chat-menu-pop]');
|
||||
pop2.querySelector('[data-chat-action="rename"]').click();
|
||||
await new Promise(r => setImmediate(r));
|
||||
const input = app.document.querySelector('.assistant-saved-chat-rename');
|
||||
assert.ok(input, 'inline rename input appears');
|
||||
input.value = 'Renamed chat';
|
||||
input.dispatchEvent(new app.window.Event('blur'));
|
||||
await new Promise(r => setImmediate(r)); await new Promise(r => setImmediate(r));
|
||||
assert.deepEqual(renames, [['2', 'Renamed chat']], 'rename PATCH sent');
|
||||
});
|
||||
|
||||
test('regenerate on a non-latest answer refuses honestly without touching the chat', t => {
|
||||
const app = ui(t);
|
||||
const c = app.context;
|
||||
|
|
|
|||
|
|
@ -100,6 +100,15 @@ test('learning hub opens inside the assistant center column and keeps the right
|
|||
assert.equal(app.events.filter(e => e === 'learning').length, 2);
|
||||
});
|
||||
|
||||
test('Learning Hub opens as a full page: saved chats, image column and sources are hidden', async t => {
|
||||
const app = workspace(t);
|
||||
app.document.getElementById('btn-assistant-learning-view').click();
|
||||
const layout = app.document.getElementById('assistant-layout');
|
||||
assert.ok(layout.classList.contains('learning-mode'), 'learning-mode class applied (CSS hides the rail, image column and sources — browser-verified)');
|
||||
app.document.getElementById('btn-assistant-chat-view').click();
|
||||
assert.ok(!layout.classList.contains('learning-mode'), 'back to chat restores the rail and sources');
|
||||
});
|
||||
|
||||
test('go back leaves the assistant workspace for the last non-assistant tab', t => {
|
||||
const app = workspace(t);
|
||||
app.document.dispatchEvent(new app.window.CustomEvent('tabChanged', { detail: { tab: 'notes' } }));
|
||||
|
|
|
|||
|
|
@ -159,6 +159,10 @@ function client(t, options = {}) {
|
|||
translateAssistantMessage: (message, target, provider) => apiFetch('/api/clinical-assistant/translate', { method: 'POST', headers: {}, body: JSON.stringify({ message, target, provider }) }).then(r => r.json()),
|
||||
getAuthHeaders: () => ({ 'X-Synthetic': '1' }),
|
||||
fetchSavedAssistantChat: async id => ({ success: true, chat: { id, payload: { version: 2, messages: [{ role: 'user', content: 'Old question' }] } } }),
|
||||
openAssistantStream: async (payload) => {
|
||||
calls.push({ url: 'openAssistantStream', payload });
|
||||
return new Response('event: done\ndata: ' + JSON.stringify({ success: true, answer: 'Answer.', sources: [] }) + '\n\n');
|
||||
},
|
||||
startAssistantImageJob: async (prompt, history) => { calls.push({ url: 'image-job', prompt, history }); return { success: true, jobId: 'job-1' }; },
|
||||
fetch: apiFetch };
|
||||
vm.createContext(context);
|
||||
|
|
@ -241,6 +245,16 @@ test('Create image dialog: describe it or pick a chat; the latest chat is one ta
|
|||
assert.equal(jobs[1].history[0].content, 'Old question', 'generates from the selected chat');
|
||||
});
|
||||
|
||||
test('tapping an example question asks it immediately (no empty sends)', async t => {
|
||||
const app = client(t);
|
||||
const btn = app.document.querySelector('[data-assistant-example]');
|
||||
btn.click();
|
||||
await new Promise(r => setImmediate(r)); await new Promise(r => setImmediate(r));
|
||||
const asks = app.calls.filter(c => c.url === 'openAssistantStream');
|
||||
assert.equal(asks.length, 1, 'one request fired from the tap');
|
||||
assert.equal(asks[0].payload.message, btn.getAttribute('data-assistant-example'), 'the example text is the message');
|
||||
});
|
||||
|
||||
test('translate picker filters languages by the local provider availability', async t => {
|
||||
const app = client(t);
|
||||
const c = app.context;
|
||||
|
|
|
|||
Loading…
Reference in a new issue