Skip to content

Commit 96fdccf

Browse files
authored
Merge branch 'main' into feat/compare
2 parents 2adcf4a + f70a11b commit 96fdccf

File tree

6 files changed

+109
-22
lines changed

6 files changed

+109
-22
lines changed

app/components/User/Avatar.vue

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,15 @@ const textClass = computed(() => {
3131
}
3232
})
3333
34-
const { data: gravatarUrl } = useLazyFetch(() => `/api/gravatar/${props.username}`, {
35-
transform: res => (res.hash ? `/_avatar/${res.hash}?s=128&d=404` : null),
36-
getCachedData(key, nuxtApp) {
37-
return nuxtApp.static.data[key] ?? nuxtApp.payload.data[key]
34+
const { data: gravatarUrl } = useLazyFetch(
35+
() => `/api/gravatar/${encodeURIComponent(props.username)}`,
36+
{
37+
transform: res => (res.hash ? `/_avatar/${res.hash}?s=128&d=404` : null),
38+
getCachedData(key, nuxtApp) {
39+
return nuxtApp.static.data[key] ?? nuxtApp.payload.data[key]
40+
},
3841
},
39-
})
42+
)
4043
</script>
4144

4245
<template>

app/composables/useCompareReplacements.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,17 @@ export function useCompareReplacements(packageNames: MaybeRefOrGetter<string[]>)
4242
namesToCheck.map(async name => {
4343
try {
4444
const replacement = await $fetch<ModuleReplacement | null>(`/api/replacements/${name}`)
45-
return { name, replacement }
45+
return { name, replacement, failed: false as const }
4646
} catch {
47-
return { name, replacement: null }
47+
return { name, failed: true as const }
4848
}
4949
}),
5050
)
5151

5252
const newReplacements = new Map(replacements.value)
53-
for (const { name, replacement } of results) {
54-
newReplacements.set(name, replacement)
53+
for (const result of results) {
54+
if (result.failed) continue
55+
newReplacements.set(result.name, result.replacement)
5556
}
5657
replacements.value = newReplacements
5758
} finally {

i18n/locales/en.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@
324324
"edit": "Edit",
325325
"error": "Error",
326326
"view_on": {
327-
"npm": "view on npm",
327+
"npm": "View on npm",
328328
"github": "View on GitHub",
329329
"gitlab": "View on GitLab",
330330
"bitbucket": "View on Bitbucket",
@@ -334,7 +334,7 @@
334334
"gitea": "View on Gitea",
335335
"gitee": "View on Gitee",
336336
"radicle": "View on Radicle",
337-
"socket_dev": "view on socket.dev",
337+
"socket_dev": "View on socket.dev",
338338
"sourcehut": "View on SourceHut",
339339
"tangled": "View on Tangled"
340340
},

i18n/locales/zh-CN.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,9 @@
339339
"tangled": "在 Tangled 上查看"
340340
},
341341
"collapse": "收起",
342-
"expand": "展开"
342+
"collapse_with_name": "收起 {name}",
343+
"expand": "展开",
344+
"expand_with_name": "展开 {name}"
343345
},
344346
"profile": {
345347
"display_name": "显示名称",
@@ -1234,6 +1236,17 @@
12341236
"packages_selected": "已选择 {count}/{max} 个包。",
12351237
"add_hint": "至少添加 2 个包以进行比较。"
12361238
},
1239+
"scatter_chart": {
1240+
"title": "对比 {x} 与 {y}",
1241+
"freshness_score": "新鲜度评分",
1242+
"copy_alt": {
1243+
"analysis": "{package} : {x_name} ({x_value}) 和 {y_name} ({y_value})",
1244+
"description": "散点图:展示 {x_name} 和 {y_name} 对比结果。 {analysis}。 {watermark}"
1245+
},
1246+
"filename": "{x}-vs-{y}-散点图",
1247+
"x_axis": "X 轴 ↦",
1248+
"y_axis": "Y 轴 ↥"
1249+
},
12371250
"no_dependency": {
12381251
"label": "(不使用依赖)",
12391252
"typeahead_title": "James 会怎么做?",

i18n/locales/zh-TW.json

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,9 @@
338338
"tangled": "在 Tangled 上檢視"
339339
},
340340
"collapse": "收合",
341-
"expand": "展開"
341+
"collapse_with_name": "收合 {name}",
342+
"expand": "展開",
343+
"expand_with_name": "展開 {name}"
342344
},
343345
"profile": {
344346
"display_name": "顯示名稱",
@@ -1233,6 +1235,17 @@
12331235
"packages_selected": "已選擇 {count}/{max} 個套件。",
12341236
"add_hint": "至少新增 2 個套件以進行比較。"
12351237
},
1238+
"scatter_chart": {
1239+
"title": "比較 {x} 與 {y}",
1240+
"freshness_score": "新鮮度分數",
1241+
"copy_alt": {
1242+
"analysis": "{package} : {x_name} ({x_value}) 與 {y_name} ({y_value})",
1243+
"description": "散佈圖:顯示 {x_name} 與 {y_name} 的比較結果。 {analysis}。 {watermark}"
1244+
},
1245+
"filename": "{x}-vs-{y}-散佈圖",
1246+
"x_axis": "X 軸 ↦",
1247+
"y_axis": "Y 軸 ↥"
1248+
},
12361249
"no_dependency": {
12371250
"label": "(無相依套件)",
12381251
"typeahead_title": "如果是 James Garbutt 會怎麼做?",

test/nuxt/composables/use-compare-replacements.spec.ts

Lines changed: 66 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import type { ReplacementSuggestion } from '~/composables/useCompareReplacements
66
/**
77
* Helper to test useCompareReplacements by wrapping it in a component.
88
*/
9-
async function useCompareReplacementsInComponent(packageNames: string[]) {
9+
async function useCompareReplacementsInComponent(
10+
packageNames: Parameters<typeof useCompareReplacements>[0],
11+
) {
1012
const capturedNoDepSuggestions = ref<ReplacementSuggestion[]>([])
1113
const capturedInfoSuggestions = ref<ReplacementSuggestion[]>([])
1214
const capturedLoading = ref(false)
@@ -212,27 +214,82 @@ describe('useCompareReplacements', () => {
212214
})
213215

214216
it('handles fetch errors gracefully', async () => {
215-
vi.stubGlobal(
216-
'$fetch',
217-
vi.fn().mockImplementation(() => {
218-
return Promise.reject(new Error('Network error'))
219-
}),
220-
)
217+
const fetchMock = vi.fn().mockImplementation(() => {
218+
return Promise.reject(new Error('Network error'))
219+
})
220+
221+
vi.stubGlobal('$fetch', fetchMock)
221222

222223
const { noDepSuggestions, infoSuggestions, replacements } =
223224
await useCompareReplacementsInComponent(['some-package'])
224225

225226
await vi.waitFor(() => {
226-
expect(replacements.value.has('some-package')).toBe(true)
227+
expect(fetchMock).toHaveBeenCalledTimes(1)
227228
})
228229

229-
expect(replacements.value.get('some-package')).toBeNull()
230+
expect(replacements.value.has('some-package')).toBe(false)
230231
expect(noDepSuggestions.value).toHaveLength(0)
231232
expect(infoSuggestions.value).toHaveLength(0)
232233
})
233234
})
234235

235236
describe('caching', () => {
237+
it('retries a package after a transient fetch failure', async () => {
238+
const packageNames = ref(['is-even'])
239+
const fetchMock = vi
240+
.fn()
241+
.mockRejectedValueOnce(new Error('Temporary network error'))
242+
.mockResolvedValueOnce({
243+
type: 'simple',
244+
moduleName: 'is-even',
245+
replacement: 'Use (n % 2) === 0',
246+
category: 'micro-utilities',
247+
} satisfies ModuleReplacement)
248+
249+
vi.stubGlobal('$fetch', fetchMock)
250+
251+
const { noDepSuggestions, replacements } =
252+
await useCompareReplacementsInComponent(packageNames)
253+
254+
await vi.waitFor(() => {
255+
expect(fetchMock).toHaveBeenCalledTimes(1)
256+
})
257+
258+
expect(replacements.value.has('is-even')).toBe(false)
259+
260+
packageNames.value = []
261+
await nextTick()
262+
packageNames.value = ['is-even']
263+
264+
await vi.waitFor(() => {
265+
expect(fetchMock).toHaveBeenCalledTimes(2)
266+
expect(noDepSuggestions.value).toHaveLength(1)
267+
})
268+
269+
expect(replacements.value.get('is-even')?.type).toBe('simple')
270+
})
271+
272+
it('caches successful null replacement data and does not refetch', async () => {
273+
const fetchMock = vi.fn().mockResolvedValue(null)
274+
275+
vi.stubGlobal('$fetch', fetchMock)
276+
277+
const packageNames = ref(['react'])
278+
const { replacements } = await useCompareReplacementsInComponent(packageNames)
279+
280+
await vi.waitFor(() => {
281+
expect(replacements.value.has('react')).toBe(true)
282+
})
283+
284+
packageNames.value = []
285+
await nextTick()
286+
packageNames.value = ['react']
287+
288+
await vi.waitFor(() => {
289+
expect(fetchMock).toHaveBeenCalledTimes(1)
290+
})
291+
})
292+
236293
it('caches replacement data and does not refetch', async () => {
237294
const fetchMock = vi.fn().mockImplementation((url: string) => {
238295
if (url.includes('/api/replacements/is-even')) {

0 commit comments

Comments
 (0)