mirror of
https://github.com/nexu-io/open-design.git
synced 2026-09-20 06:15:06 +08:00
94d48f41f2
* feat(plugins-home): order the template gallery by usage (OPEND-449) Non-prototype facets (Slides, Image, Video, …) now lead by real usage — a trailing 28-day blend of distinct-users + runs from `run_finished`. The Prototype facet keeps its curated editorial order; the default mode-seeds (Web Prototype, Simple Deck) and no-preview / manually-sunk tiles drop to the bottom of their facet and the All view. Ordering lives in one shared comparator (`comparePluginGalleryOrder`) used by the grid and the Home rail; the All view keeps its visual-appeal order with sunk tiles pushed last. A weekly in-repo workflow rebuilds the popularity snapshot from PostHog and opens a self-PR; a committed snapshot ships the ordering immediately. * test(plugins-home): make Live Artifact grouping assertions order-insensitive The rail + Community-section tests hardcoded the curated Live Artifact preset order. Facet order is now usage/sink-driven (OPEND-449), so these grouping/ routing tests assert membership via a sorted comparison instead. --------- Co-authored-by: Scarlett Huang <scarletthuang@Scarletts-MacBook-Air.local>
67 lines
2.6 KiB
TypeScript
67 lines
2.6 KiB
TypeScript
import { describe, expect, it } from 'vitest';
|
|
import {
|
|
PLUGIN_POPULARITY,
|
|
PLUGIN_NO_PREVIEW,
|
|
} from '../../src/components/plugins-home/pluginPopularity.generated';
|
|
import {
|
|
ALWAYS_LAST,
|
|
POPULARITY_LEADS_CURATION,
|
|
comparePluginGalleryOrder,
|
|
isSunkToBottom,
|
|
pluginPopularityScore,
|
|
} from '../../src/components/plugins-home/pluginPopularity';
|
|
|
|
// Assertions are written against invariants, not specific mid-list ids, so the
|
|
// weekly data refresh does not turn this red.
|
|
const scoredIds = Object.keys(PLUGIN_POPULARITY);
|
|
const UNSCORED = '__retired_or_unknown_plugin__';
|
|
// Two scored ids that are NOT sunk, highest and lowest, to check the ordering.
|
|
const rankable = scoredIds.filter((id) => !isSunkToBottom(id)).sort((a, b) => PLUGIN_POPULARITY[b]! - PLUGIN_POPULARITY[a]!);
|
|
|
|
describe('pluginPopularity', () => {
|
|
it('scores every listed template in (0, 1] and returns null for unknown/retired ids', () => {
|
|
expect(pluginPopularityScore(UNSCORED)).toBeNull();
|
|
expect(scoredIds.length).toBeGreaterThan(0);
|
|
for (const id of scoredIds) {
|
|
const score = pluginPopularityScore(id);
|
|
expect(score).not.toBeNull();
|
|
expect(score!).toBeGreaterThan(0);
|
|
expect(score!).toBeLessThanOrEqual(1);
|
|
}
|
|
});
|
|
|
|
it('sinks the default mode-seeds and the no-preview tiles to the bottom', () => {
|
|
for (const id of [...ALWAYS_LAST, ...PLUGIN_NO_PREVIEW]) {
|
|
expect(isSunkToBottom(id)).toBe(true);
|
|
}
|
|
expect(isSunkToBottom('example-open-design-landing')).toBe(false);
|
|
});
|
|
|
|
it('orders a sunk tile after a normal one regardless of usage', () => {
|
|
const normal = rankable[0]!;
|
|
const sunk = ALWAYS_LAST[0]!; // example-web-prototype — the top-usage seed
|
|
expect(comparePluginGalleryOrder(normal, sunk, false, false)).toBeLessThan(0);
|
|
expect(comparePluginGalleryOrder(sunk, normal, false, false)).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('orders non-prototype tiles by usage (higher score first)', () => {
|
|
const hi = rankable[0]!;
|
|
const lo = rankable[rankable.length - 1]!;
|
|
if (PLUGIN_POPULARITY[hi]! !== PLUGIN_POPULARITY[lo]!) {
|
|
expect(comparePluginGalleryOrder(hi, lo, false, false)).toBeLessThan(0);
|
|
expect(comparePluginGalleryOrder(lo, hi, false, false)).toBeGreaterThan(0);
|
|
}
|
|
});
|
|
|
|
it('skips usage for curation-governed (prototype) tiles, deferring to curated order', () => {
|
|
const hi = rankable[0]!;
|
|
const lo = rankable[rankable.length - 1]!;
|
|
// both governed by curation -> popularity ignored -> defer (0)
|
|
expect(comparePluginGalleryOrder(hi, lo, true, true)).toBe(0);
|
|
});
|
|
|
|
it('keeps the usage-ordering knob on (OPEND-449)', () => {
|
|
expect(POPULARITY_LEADS_CURATION).toBe(true);
|
|
});
|
|
});
|