home-widget-registry.test.ts3.7 KBView on GitHub /**
* The widget registry's one job that is not a lookup: surviving ids it does not know.
*
* Settings store IDS, and the only UI for removing an id is the rail itself — so a rail that
* threw (or blanked) on a retired id would be unrecoverable from the product. That makes
* `resolveHomeWidgets` skipping unknowns a correctness property, not a nicety.
*/
import {
DEFAULT_HOME_WIDGETS,
HOME_WIDGETS,
LISTED_HOME_WIDGETS,
resolveHomeWidgets,
} from '@/modules/home/widgets/registry';
describe('resolveHomeWidgets', () => {
it('resolves ids in the STORED order, not the registry order', () => {
// The rail is a list the user arranged; registry order is an implementation detail.
const ids = ['statistics', 'meetings'];
expect(resolveHomeWidgets(ids).map((w) => w.id)).toEqual(ids);
});
it('skips an id this build no longer knows rather than throwing', () => {
expect(resolveHomeWidgets(['meetings', 'retired-widget', 'pipeline']).map((w) => w.id)).toEqual([
'meetings',
'pipeline',
]);
});
it('still resolves an UNLISTED widget for a rail that already stores its id', () => {
// `unlisted` withholds a widget from the picker, not from the rails already naming it —
// resolving it away would silently take the tile off the screen of everyone who has it.
expect(resolveHomeWidgets(['meetings', 'agents', 'pipeline']).map((w) => w.id)).toEqual([
'meetings',
'agents',
'pipeline',
]);
});
it('answers empty for an empty rail, which is a real choice', () => {
expect(resolveHomeWidgets([])).toEqual([]);
});
});
describe('HOME_WIDGETS', () => {
it('has unique ids — they are the settings key', () => {
const ids = HOME_WIDGETS.map((w) => w.id);
expect(new Set(ids).size).toBe(ids.length);
});
it('gives every widget a title, icon and description for the picker', () => {
for (const widget of LISTED_HOME_WIDGETS) {
expect({ id: widget.id, ok: !!widget.title && !!widget.icon && !!widget.description }).toEqual(
{ id: widget.id, ok: true },
);
}
});
it('defaults to the three widgets everyone gets, all of them resolvable', () => {
expect(DEFAULT_HOME_WIDGETS).toEqual(['meetings', 'pipeline', 'statistics']);
// A default that names an id the registry has retired would silently give a brand-new
// user a shorter rail than intended, with nothing in the UI to explain it.
expect(resolveHomeWidgets(DEFAULT_HOME_WIDGETS).map((w) => w.id)).toEqual([
...DEFAULT_HOME_WIDGETS,
]);
});
it('keeps the unfinished agents tile out of the picker and out of the defaults', () => {
// TEMPORARY, and this test is the reminder: the tile is the agents surface's only entry
// point, so not offering it is what keeps the surface out of everyone else's way. It
// stays in the registry so the rails that already name it go on rendering it.
expect(HOME_WIDGETS.some((w) => w.id === 'agents')).toBe(true);
expect(LISTED_HOME_WIDGETS.some((w) => w.id === 'agents')).toBe(false);
expect(DEFAULT_HOME_WIDGETS).not.toContain('agents');
});
it('marks every metric widget as needing the shared overview', () => {
// Four separate stat tiles were merged into ONE "Statistics" widget: four bordered cards
// to say four numbers cost four cards' worth of a 21rem rail, and were not comparable.
const metrics = ['pipeline', 'statistics'];
for (const id of metrics) {
expect({ id, needsOverview: resolveHomeWidgets([id])[0]?.needsOverview }).toEqual({
id,
needsOverview: true,
});
}
expect(resolveHomeWidgets(['responseTime', 'followUpTime', 'emailsSent', 'actionStats'])).toEqual(
[],
);
});
});