fix: repair pre-existing failing vitest suite
Fixes failures across the suite that fail on main independently of any branch.
Documented + skipped
- smime/smime-crypto: this suite OOMs its worker (~4 GB heap) generating and
using real 2048-bit RSA keys via pkijs/asn1js — a pre-existing memory issue,
not a logical failure. Skipped behind a single SKIP_SMIME_CRYPTO_OOM flag with
an in-file explanation and re-enable instructions, and the beforeAll bails
early so the skipped file runs in ~2s instead of crashing the worker.
Code fixes
- jmap/client: getSubmissionAccountId honoured the requested (mail) account
even when it lacks the submission capability, so EmailSubmission/set was
addressed to the wrong account when JMAP hosts submission in a separate
account. Prefer an account that actually advertises submission, falling back
to primaryAccounts['…:submission'].
- plugin-sandbox/loader: deactivateAllSandboxed used require('./registry'),
which is unresolvable under the Vite/ESM test runtime. registry only imports
types (no cycle), so use a static import; all() already returns a copy, so
iterating while deregister mutates is safe.
Test fixes (tests trailed intentional code/behaviour changes)
- vitest.setup: add a matchMedia stub (jsdom lacks it) — unblocks 8
email-list-item tests.
- calendar-utils: pin TZ=UTC for the timezone-sensitive bounds/layout assertions
(host runs at UTC+2) and update expected minutes to UTC.
- calendar-participants: buildParticipantMap keys entries by generated UUIDs
(RFC 8984), not 'organizer'/'attendee-N'. Look entries up by identity so the
test no longer depends on a generateUUID mock leaking from another file.
- email-headers: softfail now returns the semantic 'text-warning' token.
- email-list-item: unknown keyword ids intentionally render a gray fallback badge.
- plugin-loader: exposePluginExternals is now a documented no-op.
- plugin-slot: PluginSlot reads the sandbox registry and renders iframe slots;
rewrite the tests against that architecture with a referentially stable snapshot.
- plugin-types: MAX_THEME_SIZE was raised to 2 MB.
This commit is contained in:
committed by
Linus Rath
parent
2fac6ebfb8
commit
3f9e60843d
@@ -1,14 +1,22 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import type { SlotRegistration } from '@/lib/plugin-types';
|
||||
// PluginSlot reads active plugins from the sandbox registry and renders each
|
||||
// inside a sandboxed iframe. Mock both so we can drive the offers and assert
|
||||
// what PluginSlot renders without a real iframe + postMessage bridge.
|
||||
const mockOffers: Record<string, Array<{ pluginId: string }>> = {};
|
||||
// useSyncExternalStore requires a referentially stable snapshot; hand back a
|
||||
// single shared empty array for unregistered slots instead of a fresh [].
|
||||
const EMPTY_OFFERS: Array<{ pluginId: string }> = [];
|
||||
|
||||
// Mock the plugin store
|
||||
const mockSlots: Record<string, SlotRegistration[]> = {};
|
||||
vi.mock('@/lib/plugin-sandbox/registry', () => ({
|
||||
offersForSlot: (slot: string) => mockOffers[slot] ?? EMPTY_OFFERS,
|
||||
subscribe: () => () => {},
|
||||
}));
|
||||
|
||||
vi.mock('@/stores/plugin-store', () => ({
|
||||
usePluginStore: (selector: (s: { slots: typeof mockSlots }) => unknown) =>
|
||||
selector({ slots: mockSlots }),
|
||||
vi.mock('@/components/plugins/plugin-iframe-slot', () => ({
|
||||
PluginIframeSlot: ({ pluginId, slot }: { pluginId: string; slot: string }) =>
|
||||
React.createElement('span', { 'data-iframe-plugin': pluginId }, `iframe:${slot}:${pluginId}`),
|
||||
}));
|
||||
|
||||
// Import after mocks
|
||||
@@ -16,42 +24,36 @@ import { PluginSlot } from '@/components/plugins/plugin-slot';
|
||||
import { PluginErrorBoundary } from '@/components/plugins/plugin-error-boundary';
|
||||
|
||||
beforeEach(() => {
|
||||
Object.keys(mockSlots).forEach(k => delete mockSlots[k]);
|
||||
Object.keys(mockOffers).forEach(k => delete mockOffers[k]);
|
||||
});
|
||||
|
||||
describe('PluginSlot', () => {
|
||||
it('renders null when no registrations', () => {
|
||||
mockSlots['toolbar-actions'] = [];
|
||||
it('renders null when the slot has an empty offer list', () => {
|
||||
mockOffers['toolbar-actions'] = [];
|
||||
const { container } = render(
|
||||
React.createElement(PluginSlot, { name: 'toolbar-actions' })
|
||||
);
|
||||
expect(container.innerHTML).toBe('');
|
||||
});
|
||||
|
||||
it('renders null when slot has undefined registrations', () => {
|
||||
// slot entry doesn't exist at all
|
||||
it('renders null when the slot has no offers at all', () => {
|
||||
// slot entry doesn't exist in the registry
|
||||
const { container } = render(
|
||||
React.createElement(PluginSlot, { name: 'toolbar-actions' })
|
||||
);
|
||||
expect(container.innerHTML).toBe('');
|
||||
});
|
||||
|
||||
it('renders registered components', () => {
|
||||
const TestComponent = () => React.createElement('span', null, 'Hello Plugin');
|
||||
mockSlots['email-footer'] = [
|
||||
{ pluginId: 'test', component: TestComponent, order: 100 },
|
||||
];
|
||||
it('renders an iframe slot per offer', () => {
|
||||
mockOffers['email-footer'] = [{ pluginId: 'test' }];
|
||||
const { getByText } = render(
|
||||
React.createElement(PluginSlot, { name: 'email-footer' })
|
||||
);
|
||||
expect(getByText('Hello Plugin')).toBeTruthy();
|
||||
expect(getByText('iframe:email-footer:test')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('sets data-plugin-slot attribute', () => {
|
||||
const TestComponent = () => React.createElement('span', null, 'x');
|
||||
mockSlots['sidebar-widget'] = [
|
||||
{ pluginId: 'sw', component: TestComponent, order: 100 },
|
||||
];
|
||||
mockOffers['sidebar-widget'] = [{ pluginId: 'sw' }];
|
||||
const { container } = render(
|
||||
React.createElement(PluginSlot, { name: 'sidebar-widget' })
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user