Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions src/components/PricingGrid.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/* PricingGrid.test.tsx — pricing copy regression guards.
*
* BIZ-3 (2026-05-29): the dashboard in-app pricing tile shipped with copy
* inherited from a retired "deployment_size" field on /deploy/new:
* - Hobby tile: "1 small deployment"
* - Pro tile: "50 GB object storage · 10 medium deployments"
*
* The backend handler (api/internal/handlers/deploy.go) has no
* deployment_size field — there are no small/medium/large pod sizes.
* Numbers come from plans.yaml deployments_apps. Marketing PricingPage
* (src/pages/PricingPage.tsx) dropped the size adjectives in the
* 2026-05-20 DOC-REALITY-DELTA sweep; the dashboard surface lagged.
*
* This test pins both strings out of the in-app pricing surface so any
* future copy edit that re-introduces them fails CI before it ships.
*/

import { describe, it, expect, afterEach } from 'vitest'
import { render, cleanup } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { PricingGrid } from './PricingGrid'

afterEach(() => cleanup())

function renderGrid() {
return render(
<MemoryRouter>
<PricingGrid
currentTier="free"
frequency="monthly"
onFrequencyChange={() => {}}
onSelectTier={() => {}}
/>
</MemoryRouter>,
)
}

describe('PricingGrid — BIZ-3 deployment copy regression', () => {
it('does not render "small deployment" copy anywhere', () => {
renderGrid()
expect(document.body.textContent ?? '').not.toMatch(/small deployment/i)
})

it('does not render "medium deployments" copy anywhere', () => {
renderGrid()
expect(document.body.textContent ?? '').not.toMatch(/medium deployments/i)
})

it('Hobby tile says "1 deployment" (matches plans.yaml deployments_apps=1)', () => {
renderGrid()
expect(document.body.textContent ?? '').toContain('1 deployment')
})

it('Pro tile says "10 deployments" (matches plans.yaml deployments_apps=10)', () => {
renderGrid()
expect(document.body.textContent ?? '').toContain('10 deployments')
})
})
4 changes: 2 additions & 2 deletions src/components/PricingGrid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export const PRICING_GRID_TIERS: TierDefinition[] = [
{ text: '1 GB Postgres · 8 conn' },
{ text: '50 MB Redis' },
{ text: '100 MB MongoDB · 5 conn' },
{ text: '1 small deployment' },
{ text: '1 deployment' },
{ text: '20 vault entries · production env' },
{ text: '1,000 stored webhooks' },
],
Expand Down Expand Up @@ -161,7 +161,7 @@ export const PRICING_GRID_TIERS: TierDefinition[] = [
{ text: '10 GB Postgres · 20 conn' },
{ text: '512 MB Redis' },
{ text: '5 GB MongoDB · 20 conn' },
{ text: '50 GB object storage · 10 medium deployments' },
{ text: '50 GB object storage · 10 deployments' },
{ text: '200 vault entries · multi-env' },
{ text: 'custom domain · 10k stored webhooks' },
],
Expand Down
17 changes: 17 additions & 0 deletions src/pages/MarketingPage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,21 @@ describe('MarketingPage — claim consistency (T18 P1-4 / P1-6)', () => {
expect(text).not.toMatch(/<10s/)
expect(text).toMatch(/~60s/)
})

// BIZ-3 (2026-05-29): the landing pricing tile shipped "1 small deployment"
// and "10 medium deployments" copy from the days when /deploy/new had a
// deployment_size field. The backend dropped that field; marketing
// /pricing dropped the size adjectives in the 2026-05-20 sweep; the
// landing tile lagged. Pin both strings out so a future copy edit can't
// silently re-introduce a contract claim the API doesn't honor.
it('landing pricing tile no longer claims "small / medium" deployment sizes', () => {
const { container } = render(
<MemoryRouter initialEntries={['/']}>
<MarketingPage />
</MemoryRouter>,
)
const text = container.textContent ?? ''
expect(text).not.toMatch(/small deployment/i)
expect(text).not.toMatch(/medium deployments/i)
})
})
4 changes: 2 additions & 2 deletions src/pages/MarketingPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ const PLANS: Plan[] = [
features: [
'1 GB Postgres · 8 conn',
'50 MB Redis · 100 MB Mongo · 5 conn',
'1 small deployment · *.deployment.instanode.dev',
'1 deployment · *.deployment.instanode.dev',
'20 vault entries · production env',
],
cta: { label: 'Start hobby →', href: ROUTES.signin, variant: 'secondary' },
Expand All @@ -186,7 +186,7 @@ const PLANS: Plan[] = [
features: [
'10 GB Postgres · 20 conn',
'512 MB Redis · 5 GB Mongo · 20 conn',
'50 GB object storage · 10 medium deployments · custom domain',
'50 GB object storage · 10 deployments · custom domain',
// BugBash P3-08: kept in sync with PricingPage's "Vault envs" row
// and the multi-env FAQ — Pro's multi-env is dev/staging/prod. The
// earlier "+ custom" claim implied arbitrary named environments the
Expand Down
Loading