Skip to content

Commit 1b087d7

Browse files
chore(billing): genericize demo meter-category vocabulary for the public devkit (#4472)
Rename downstream-specific meter-category names + demo copy in the billing meter-breakdown feature to neutral generic terms (the component/composable LOGIC was already generic; only example/comment/ test VALUES leaked downstream product vocabulary): - meter-category keys: scrap -> compute, autofix -> automation, wizard -> export (applied consistently everywhere they appear as breakdown keys, aligning with the already-merged pack copy). - demo/copy strings genericized: pack/pricing feature blurbs, the meterBreakdownChart JSDoc usage example, the useMeter in-flight-debt comment, the seo-inject fixture description, the app.router test description, and the pageHeader breadcrumb fixture text. Closes #4471 Claude-Session: https://claude.ai/code/session_01WfNC8bt1TgL4AsiYgCEGup
1 parent 08f6998 commit 1b087d7

12 files changed

Lines changed: 45 additions & 45 deletions

src/lib/plugins/tests/seo-inject.unit.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,7 @@ describe('seoInjectPlugin', () => {
418418
app: {
419419
title: 'Example',
420420
url: 'https://example.com',
421-
description: 'Self-driving scrapers',
421+
description: 'Automated workflows for busy teams',
422422
seo: {
423423
schemas: [
424424
{ type: 'Organization', name: 'Example' },

src/modules/app/tests/app.router.unit.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ describe('app.router', () => {
234234
expect(router.currentRoute.value.meta.marketing).toBe(true);
235235
});
236236

237-
it('app routes (/, /scraps-equivalent) do NOT have meta.marketing', async () => {
237+
it('app routes (/, /dashboard-equivalent) do NOT have meta.marketing', async () => {
238238
const router = getRouter();
239239
await router.push('/');
240240
await router.isReady();

src/modules/billing/components/billing.meterBreakdownChart.component.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
height/rounded/bg-color as the Weekly compute bar for visual consistency.
66
77
USAGE:
8-
<BillingMeterBreakdownChartComponent :breakdown="{ scrap: 60, autofix: 40 }" />
8+
<BillingMeterBreakdownChartComponent :breakdown="{ compute: 60, automation: 40 }" />
99
-->
1010
<template>
1111
<div class="billing-meter-breakdown-chart">

src/modules/billing/composables/billing.useMeter.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export function useMeter({ pollIntervalMs = 30000, refreshOnFocus = true } = {})
113113
/**
114114
* @type {import('vue').ComputedRef<number>}
115115
* Unclamped balance: quota - used + extras. Can be negative when in-flight jobs
116-
* push usage past quota (e.g. a long-running scrape finishing after quota is hit).
116+
* push usage past quota (e.g. a long-running job finishing after quota is hit).
117117
* Use this in detail views / ledger displays that must show the real debt.
118118
*/
119119
const netRemainingRaw = computed(() => quota.value - used.value + extras.value);

src/modules/billing/tests/billing.card.component.unit.tests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,8 @@ describe('BillingCardComponent', () => {
8585
});
8686

8787
it('renders info line when present', () => {
88-
const wrapper = mountComponent(makeItem({ info: 'Ops-eval: 200 scraps / month' }));
89-
expect(wrapper.text()).toContain('Ops-eval: 200 scraps / month');
88+
const wrapper = mountComponent(makeItem({ info: 'Ops-eval: 200 operations / month' }));
89+
expect(wrapper.text()).toContain('Ops-eval: 200 operations / month');
9090
});
9191

9292
it('does not render info paragraph when info is null', () => {

src/modules/billing/tests/billing.meterBreakdownChart.component.unit.tests.js

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@ describe('BillingMeterBreakdownChartComponent', () => {
2525
// ── Non-zero buckets only ─────────────────────────────────────────────────
2626

2727
it('renders only non-zero buckets', () => {
28-
const wrapper = mountComponent({ breakdown: { scrap: 100, autofix: 0, wizard: 50 } });
28+
const wrapper = mountComponent({ breakdown: { compute: 100, automation: 0, export: 50 } });
2929
const buckets = wrapper.vm.activeBuckets;
3030
expect(buckets).toHaveLength(2);
31-
expect(buckets.map((b) => b.key)).toEqual(['scrap', 'wizard']);
31+
expect(buckets.map((b) => b.key)).toEqual(['compute', 'export']);
3232
});
3333

3434
it('renders empty state when all buckets are zero', () => {
35-
const wrapper = mountComponent({ breakdown: { scrap: 0, autofix: 0 } });
35+
const wrapper = mountComponent({ breakdown: { compute: 0, automation: 0 } });
3636
expect(wrapper.text()).toContain('No usage data');
3737
expect(wrapper.vm.activeBuckets).toHaveLength(0);
3838
});
@@ -45,21 +45,21 @@ describe('BillingMeterBreakdownChartComponent', () => {
4545
// ── Total computation ─────────────────────────────────────────────────────
4646

4747
it('computes total as sum of breakdown values when total prop is omitted', () => {
48-
const wrapper = mountComponent({ breakdown: { scrap: 100, autofix: 50, wizard: 200 } });
48+
const wrapper = mountComponent({ breakdown: { compute: 100, automation: 50, export: 200 } });
4949
expect(wrapper.vm.effectiveTotal).toBe(350);
5050
});
5151

5252
it('uses the total prop when provided', () => {
5353
const wrapper = mountComponent({
54-
breakdown: { scrap: 100, autofix: 50 },
54+
breakdown: { compute: 100, automation: 50 },
5555
total: 500,
5656
});
5757
expect(wrapper.vm.effectiveTotal).toBe(500);
5858
});
5959

6060
it('falls back to sum when total prop is 0', () => {
6161
const wrapper = mountComponent({
62-
breakdown: { scrap: 100, autofix: 50 },
62+
breakdown: { compute: 100, automation: 50 },
6363
total: 0,
6464
});
6565
// total=0 is falsy, falls back to sum
@@ -69,27 +69,27 @@ describe('BillingMeterBreakdownChartComponent', () => {
6969
// ── Percentage computation ────────────────────────────────────────────────
7070

7171
it('computes percentages summing to ~100 for multiple buckets', () => {
72-
const wrapper = mountComponent({ breakdown: { scrap: 100, autofix: 100, wizard: 100 } });
72+
const wrapper = mountComponent({ breakdown: { compute: 100, automation: 100, export: 100 } });
7373
const total = wrapper.vm.activeBuckets.reduce((s, b) => s + b.pct, 0);
7474
// With 3 equal buckets each 33% → sum is 99 due to rounding
7575
expect(total).toBeGreaterThanOrEqual(99);
7676
expect(total).toBeLessThanOrEqual(101);
7777
});
7878

7979
it('computes correct percentage for a single bucket', () => {
80-
const wrapper = mountComponent({ breakdown: { scrap: 200 } });
80+
const wrapper = mountComponent({ breakdown: { compute: 200 } });
8181
expect(wrapper.vm.activeBuckets[0].pct).toBe(100);
8282
});
8383

8484
it('computes percentage relative to custom total', () => {
85-
const wrapper = mountComponent({ breakdown: { scrap: 100 }, total: 400 });
85+
const wrapper = mountComponent({ breakdown: { compute: 100 }, total: 400 });
8686
expect(wrapper.vm.activeBuckets[0].pct).toBe(25);
8787
});
8888

8989
// ── Color cycle determinism ────────────────────────────────────────────────
9090

9191
it('assigns colors deterministically for the same set of buckets', () => {
92-
const breakdown = { scrap: 100, autofix: 50, wizard: 200 };
92+
const breakdown = { compute: 100, automation: 50, export: 200 };
9393
const w1 = mountComponent({ breakdown });
9494
const w2 = mountComponent({ breakdown });
9595
const colors1 = w1.vm.activeBuckets.map((b) => b.color);
@@ -98,12 +98,12 @@ describe('BillingMeterBreakdownChartComponent', () => {
9898
});
9999

100100
it('first bucket gets primary color', () => {
101-
const wrapper = mountComponent({ breakdown: { scrap: 100, autofix: 50 } });
101+
const wrapper = mountComponent({ breakdown: { compute: 100, automation: 50 } });
102102
expect(wrapper.vm.activeBuckets[0].color).toBe('primary');
103103
});
104104

105105
it('second bucket gets secondary color', () => {
106-
const wrapper = mountComponent({ breakdown: { scrap: 100, autofix: 50 } });
106+
const wrapper = mountComponent({ breakdown: { compute: 100, automation: 50 } });
107107
expect(wrapper.vm.activeBuckets[1].color).toBe('secondary');
108108
});
109109

@@ -134,14 +134,14 @@ describe('BillingMeterBreakdownChartComponent', () => {
134134
// ── Linear bar rendering (new v-progress-linear design) ──────────────────
135135

136136
it('renders one v-progress-linear per non-zero bucket', () => {
137-
const wrapper = mountComponent({ breakdown: { scrap: 100, autofix: 50, wizard: 0 } });
137+
const wrapper = mountComponent({ breakdown: { compute: 100, automation: 50, export: 0 } });
138138
const bars = wrapper.findAllComponents({ name: 'v-progress-linear' });
139-
// Only scrap and autofix are non-zero → 2 bars
139+
// Only compute and automation are non-zero → 2 bars
140140
expect(bars).toHaveLength(2);
141141
});
142142

143143
it('v-progress-linear bars have height="10" and rounded props', () => {
144-
const wrapper = mountComponent({ breakdown: { scrap: 100, autofix: 50 } });
144+
const wrapper = mountComponent({ breakdown: { compute: 100, automation: 50 } });
145145
const bars = wrapper.findAllComponents({ name: 'v-progress-linear' });
146146
for (const bar of bars) {
147147
expect(bar.props('height')).toBe('10');
@@ -150,31 +150,31 @@ describe('BillingMeterBreakdownChartComponent', () => {
150150
});
151151

152152
it('v-progress-linear bars have bg-color="surface-variant"', () => {
153-
const wrapper = mountComponent({ breakdown: { scrap: 100 } });
153+
const wrapper = mountComponent({ breakdown: { compute: 100 } });
154154
const bar = wrapper.findComponent({ name: 'v-progress-linear' });
155155
expect(bar.props('bgColor')).toBe('surface-variant');
156156
});
157157

158158
it('renders bucket label and percentage above each bar', () => {
159-
const wrapper = mountComponent({ breakdown: { scrap: 100, autofix: 100 } });
159+
const wrapper = mountComponent({ breakdown: { compute: 100, automation: 100 } });
160160
const text = wrapper.text();
161161
// Both keys visible
162-
expect(text).toContain('scrap');
163-
expect(text).toContain('autofix');
162+
expect(text).toContain('compute');
163+
expect(text).toContain('automation');
164164
// Percentages (50% each for equal split)
165165
expect(text).toContain('50%');
166166
});
167167

168168
it('does not render zero-value bucket bar or label', () => {
169-
const wrapper = mountComponent({ breakdown: { scrap: 100, autofix: 0 } });
170-
// autofix is zero → not in active buckets → no label rendered
171-
expect(wrapper.text()).not.toContain('autofix');
172-
// Only 1 bar for scrap
169+
const wrapper = mountComponent({ breakdown: { compute: 100, automation: 0 } });
170+
// automation is zero → not in active buckets → no label rendered
171+
expect(wrapper.text()).not.toContain('automation');
172+
// Only 1 bar for compute
173173
expect(wrapper.findAllComponents({ name: 'v-progress-linear' })).toHaveLength(1);
174174
});
175175

176176
it('does not render any bar when all buckets are zero', () => {
177-
const wrapper = mountComponent({ breakdown: { scrap: 0 } });
177+
const wrapper = mountComponent({ breakdown: { compute: 0 } });
178178
expect(wrapper.findAllComponents({ name: 'v-progress-linear' })).toHaveLength(0);
179179
expect(wrapper.text()).toContain('No usage data');
180180
});

src/modules/billing/tests/billing.packs.component.unit.tests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ const mockPacksV4 = [
7878
cta: 'Buy Starter',
7979
info: '5,000 compute',
8080
features: [
81-
{ icon: 'fa-solid fa-check', color: 'primary', text: '~40 easy scraps' },
81+
{ icon: 'fa-solid fa-check', color: 'primary', text: '~40 easy jobs' },
8282
],
8383
meta: { packId: 'pack_starter', priceUsd: 9, meterUnits: 5000 },
8484
},
@@ -92,7 +92,7 @@ const mockPacksV4 = [
9292
cta: 'Buy Power',
9393
info: '20,000 compute',
9494
features: [
95-
{ icon: 'fa-solid fa-check', color: 'primary', text: '~160 easy scraps' },
95+
{ icon: 'fa-solid fa-check', color: 'primary', text: '~160 easy jobs' },
9696
],
9797
meta: { packId: 'pack_power', priceUsd: 25, meterUnits: 20000 },
9898
},

src/modules/billing/tests/billing.pricing.view.unit.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ describe('BillingPricingView — resolvedPlanItems (V4 unified schema)', () => {
443443
highlight: true,
444444
badge: 'MOST POPULAR',
445445
cta: 'Upgrade',
446-
features: [{ icon: 'fa-solid fa-check', color: 'primary', text: 'Unlimited scraps' }],
446+
features: [{ icon: 'fa-solid fa-check', color: 'primary', text: 'Unlimited operations' }],
447447
info: null,
448448
meta: { monthlyPrice: 39, annualPrice: 390 },
449449
},

src/modules/billing/tests/billing.store.unit.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ describe('Billing Store', () => {
292292
weekResetAt: '2026-05-04T00:00:00Z',
293293
meterUsed: 1234,
294294
meterQuota: 8000,
295-
meterBreakdown: { scrap: 800, autofix: 434 },
295+
meterBreakdown: { compute: 800, automation: 434 },
296296
extrasRemaining: 12500,
297297
packsAvailable: [],
298298
};

src/modules/billing/tests/billing.subscriptions.component.unit.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const mockUsageMeterNormal = {
6767
weekResetAt: new Date(Date.now() + 86400000).toISOString(),
6868
meterUsed: 120,
6969
meterQuota: 500,
70-
meterBreakdown: { scrap: 80, autofix: 40 },
70+
meterBreakdown: { compute: 80, automation: 40 },
7171
extrasRemaining: 50,
7272
packsAvailable: [
7373
{ packId: 'pack_500', label: '500 units', priceUsd: 9, meterUnits: 500 },

0 commit comments

Comments
 (0)