E2E #11: the account picker's fiscal-year default sent a 365-day request for a 405-day-old fiscal year; Swedbank answered by TERMINATING the session: zero transactions, connection expired, no error surfaced (initial_sync_requested_from 2025-08-26, returned min/max null). 90 days worked. - Default lookback mode is now 'fast' (90 days), labeled rekommenderas on a first connect; the fiscal-year option stays but carries an inline 'vissa banker avbryter kopplingen' note when its span exceeds 90 days, and the long-range helper names the real failure mode + the SIE/CSV path for older history. - SIE drop card: stage + arm in ONE click (three clicks was one too many): after the verdict, the single button reads 'Bokför: oåterkalleligt (BFL 5 kap 5 §)' and the deliberate click commits with confirmed=true. - Skill: never re-ask an answered question; when a connect card rendered, do not paste the URL as text too. Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
56 lines
2.4 KiB
TypeScript
56 lines
2.4 KiB
TypeScript
/**
|
|
* SIE drop widget: registration and wiring. The byte path itself (drop →
|
|
* preflight → import via tools/call with file_content_base64 + sha256) is
|
|
* asserted structurally on the HTML; the sha256/cap semantics live in
|
|
* sie-preflight.test.ts.
|
|
*/
|
|
import { describe, expect, it } from 'vitest'
|
|
import { tools } from '../server'
|
|
import { findUiWidget } from '../widgets'
|
|
|
|
describe('SIE drop widget', () => {
|
|
const widget = findUiWidget('ui://sie-drop/app.html')
|
|
|
|
it('is registered and renders the drop zone', () => {
|
|
expect(widget).toBeDefined()
|
|
expect(widget?.html).toContain('<!DOCTYPE html>')
|
|
expect(widget?.html).toContain('Importera bokföring')
|
|
expect(widget?.html).toContain("addEventListener('drop'")
|
|
})
|
|
|
|
it('carries the approval in-card: stage+arm in one click, BFL text on the commit button', () => {
|
|
const html = widget!.html
|
|
expect(html).toContain("callTool('gnubok_approve_pending_operation'")
|
|
// confirmed=true rides the deliberate click on a button whose label IS
|
|
// the irreversibility statement: the human acknowledgment, no default.
|
|
expect(html).toContain('args.confirmed = true')
|
|
expect(html).toContain('Bokför: oåterkalleligt (BFL 5 kap 5 §)')
|
|
})
|
|
|
|
it('passes exact bytes through tools/call with a sha256, never retyped or fetched', () => {
|
|
const html = widget!.html
|
|
expect(html).toContain("callTool('gnubok_sie_preflight'")
|
|
expect(html).toContain("callTool('gnubok_import_sie'")
|
|
expect(html).toContain('file_content_base64')
|
|
expect(html).toContain('sha256')
|
|
expect(html).toContain("crypto.subtle.digest('SHA-256'")
|
|
// No network from the iframe: bytes travel via the host bridge only.
|
|
expect(html).not.toContain('fetch(')
|
|
expect(html).not.toContain('XMLHttpRequest')
|
|
})
|
|
|
|
it('performs the ui/initialize handshake and narrates via ui/updateContext', () => {
|
|
const html = widget!.html
|
|
expect(html).toContain("sendRequest('ui/initialize'")
|
|
expect(html).toContain("sendNotification('ui/notifications/initialized')")
|
|
expect(html).toContain("sendNotification('ui/updateContext'")
|
|
})
|
|
|
|
it('is attached definition-level to gnubok_create_sie_upload', () => {
|
|
const tool = tools.find((t) => t.name === 'gnubok_create_sie_upload')!
|
|
expect((tool as { _meta?: { ui: { resourceUri: string } } })._meta).toEqual({
|
|
ui: { resourceUri: 'ui://sie-drop/app.html' },
|
|
})
|
|
})
|
|
})
|