Files
Pamela Chia 38b74af3f1 fix(studio): fall back for framework icons without an asset (#51065)
I made the connected-project framework icons fall back when no shipped
SVG exists for a framework. The three icon sites built
`/img/icons/frameworks/<framework>.svg` straight from the integration's
framework preset, which is an open-ended string. They only fell back
when the value was empty, so any preset without an asset (`express`,
`hono`, `fastapi`, `tanstack-start` and others) showed a broken image
and logged a 404.

**Changed:**
- **Broken framework icons**: `getFrameworkIconUrl` returns the asset
URL only for slugs in a set that mirrors `public/img/icons/frameworks/`.
The integration connection row, the org project linker and the
marketplace project picker now show their existing fallback icon for any
other slug. A test keeps the set equal to the directory listing.
- **Framework type**: I deleted the hand-kept `VercelFramework` union.
It listed exactly the shipped icon slugs, while the API types the field
as `string | null`, and that mismatch is what made the old empty-only
check look safe.

**Note:** I rejected an `onError` fallback because the browser still
sends the 404 request. Adding logos for common presets is left for
design.

## To test

Tested on Vercel preview (staging): no real connection there uses these
presets, so I rewrote the org integrations response in the browser to
give one integration four connections.
- [x] Open an org's Integrations page with connections whose framework
has no shipped icon (`express`, `eve`, `tanstack-start-lovable`). Expect
the fallback badge and no request under
`/dashboard/img/icons/frameworks/` for those slugs. Observed: all three
rows showed the badge and the network log had no request for their SVGs.
- [x] Same page with a `nextjs` connection. Expect its framework logo.
Observed: `nextjs.svg` loaded with a 200.
- [x] Same page with the real, unmodified response (one connection with
`framework: null`). Expect the badge, no frameworks requests, and no new
console errors. Observed: as expected.

## Linear
- fixes GROWTH-1309


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Bug Fixes**
* Vercel integration and project views now display framework icons when
available and fall back to the Vercel icon when no matching icon exists.
* Framework metadata now supports values beyond a fixed list, while
unsupported frameworks continue to use the fallback icon.

* **Tests**
* Added coverage for supported and unsupported framework icons,
including base-path handling.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-09-30 12:59:36 +08:00

40 lines
1.4 KiB
TypeScript

import { describe, expect, it, vi } from 'vitest'
import { IntegrationConnection } from './IntegrationPanels'
import type { IntegrationProjectConnection } from '@/data/integrations/integrations.types'
import { render } from '@/tests/helpers'
vi.mock('@/data/projects/project-detail-query', () => ({
useProjectDetailQuery: () => ({ data: undefined, isPending: false }),
}))
const connectionWithFramework = (framework: string) =>
({
id: 'connection-1',
supabase_project_ref: 'project-ref',
foreign_project_id: 'vercel-project-1',
metadata: { id: 'vercel-project-1', name: 'my-app', framework },
}) as IntegrationProjectConnection
const frameworkIconSelector = 'img[src*="/img/icons/frameworks/"]'
describe('IntegrationConnection', () => {
it('renders the fallback badge for a framework without a shipped icon', () => {
const { container } = render(
<IntegrationConnection type="Vercel" connection={connectionWithFramework('express')} />
)
expect(container.querySelector(frameworkIconSelector)).toBeNull()
})
it('renders the framework icon for a framework with a shipped icon', () => {
const { container } = render(
<IntegrationConnection type="Vercel" connection={connectionWithFramework('nextjs')} />
)
expect(container.querySelector(frameworkIconSelector)?.getAttribute('src')).toMatch(
/\/img\/icons\/frameworks\/nextjs\.svg$/
)
})
})