feat(M6): Weitere Hosts — MiniApps in Fenstern + AI-Agenten-Ausgabe-Bloecke (#364)
- Windows-Host: openMiniAppWindow-Helper + MiniAppWindowContent (windowStore); Oeffnen-Buttons im Chat-Block (MiniAppBlock) und Dashboard-Widget - AI-Agenten-Host: Core-Tool send_miniapp (app/ai/miniapp_tools.py) — miniapp-Block in Agent-Chat (approval_request-Praezedenz), Permission fail-closed gegen aufrufenden User pro App; Registrierung im lifespan - agent_loop: tool_context + agent_name (Raum-Aufloesung) - Fix: MiniAppBlock nutzt useMiniapps (component-Feld) statt Legacy /comm/miniapps - Tests: M6 7/7 (TDD rot->gruen), Backend-Regression 57/57, Vitest 26/26 (4 neue Window-Tests), tsc clean, build OK
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
import React from 'react';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { MiniAppWindowContent } from '@/components/dashboard/MiniAppWindowContent';
|
||||
import { openMiniAppWindow } from '@/components/dashboard/openMiniAppWindow';
|
||||
import { useWindowStore } from '@/store/windowStore';
|
||||
|
||||
/**
|
||||
* M6 — Windows host tests: MiniApps open in floating windows via the
|
||||
* existing window manager (openMiniAppWindow + MiniAppWindowContent).
|
||||
*/
|
||||
|
||||
vi.mock('react-i18next', () => ({
|
||||
useTranslation: () => ({ t: (key: string) => key }),
|
||||
}));
|
||||
|
||||
vi.mock('@/api/miniapps', () => ({
|
||||
useMiniapps: () => ({
|
||||
data: {
|
||||
items: [
|
||||
{
|
||||
app_id: 'recent_contacts', name: 'Recent Contacts', icon: 'Users', description: 'desc',
|
||||
plugin_name: 'contacts', render_schema: {}, permission: '', settings_schema: {},
|
||||
col_span: 2, row_span: 1, hosts: ['chat', 'dashboard', 'window'],
|
||||
component: '@/components/dashboard/RecentContactsWidget', order: 10, builtin: true,
|
||||
},
|
||||
],
|
||||
total: 1,
|
||||
},
|
||||
isLoading: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/dashboard/MiniAppHost', () => ({
|
||||
MiniAppHost: ({ appId }: { appId: string }) => (
|
||||
<div data-testid={`miniapp-${appId}`}>host:{appId}</div>
|
||||
),
|
||||
}));
|
||||
|
||||
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||||
|
||||
function renderContent() {
|
||||
return render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<MiniAppWindowContent appId="recent_contacts" settings={{ limit: 5 }} />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
|
||||
describe('MiniAppWindowContent', () => {
|
||||
it('renders the MiniApp host with the given app id', () => {
|
||||
renderContent();
|
||||
expect(screen.getByTestId('miniapp-window-recent_contacts')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('miniapp-recent_contacts')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the app description when present', () => {
|
||||
renderContent();
|
||||
expect(screen.getByText('desc')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('openMiniAppWindow', () => {
|
||||
it('opens a window with miniapp type and props', () => {
|
||||
const spy = vi.fn();
|
||||
// spy on the store's openWindow implementation
|
||||
const state = useWindowStore.getState();
|
||||
const origOpen = state.openWindow;
|
||||
useWindowStore.setState({ openWindow: spy as never });
|
||||
|
||||
openMiniAppWindow({
|
||||
appId: 'recent_contacts',
|
||||
settings: { limit: 3 },
|
||||
component: MiniAppWindowContent as never,
|
||||
});
|
||||
|
||||
expect(spy).toHaveBeenCalledTimes(1);
|
||||
const config = spy.mock.calls[0][0];
|
||||
expect(config.type).toBe('miniapp-recent_contacts');
|
||||
expect(config.title).toBe('recent_contacts');
|
||||
expect(config.componentProps).toEqual({ appId: 'recent_contacts', settings: { limit: 3 } });
|
||||
|
||||
// restore
|
||||
useWindowStore.setState({ openWindow: origOpen as never, windows: [] });
|
||||
});
|
||||
|
||||
it('uses the def name as window title when provided', () => {
|
||||
const spy = vi.fn();
|
||||
const state = useWindowStore.getState();
|
||||
const origOpen = state.openWindow;
|
||||
useWindowStore.setState({ openWindow: spy as never });
|
||||
|
||||
openMiniAppWindow({
|
||||
appId: 'x',
|
||||
def: { name: 'Mein Widget' } as never,
|
||||
component: MiniAppWindowContent as never,
|
||||
});
|
||||
|
||||
expect(spy.mock.calls[0][0].title).toBe('Mein Widget');
|
||||
useWindowStore.setState({ openWindow: origOpen as never, windows: [] });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user