{
useSkillFileMock.mockReturnValue({ data: null, isLoading: false, error: null })
})
+ it('marks rendered content only after loading/error/missing guards, without treating rerenders as visits', () => {
+ const view = render(
)
+ expect(view.container.querySelectorAll('[data-analytics-id="skill.detail.render"]')).toHaveLength(1)
+ expect(screen.getByRole('button', { name: 'skillDetail.download' }).getAttribute('data-analytics-id')).toBe('skill.download.request')
+ view.rerender(
)
+ expect(view.container.querySelectorAll('[data-analytics-id="skill.detail.render"]')).toHaveLength(1)
+ for (const result of [
+ { data: undefined, isLoading: true, error: null },
+ { data: undefined, isLoading: false, error: new Error('synthetic error') },
+ { data: undefined, isLoading: false, error: null },
+ ]) {
+ useSkillDetailMock.mockReturnValue(result)
+ view.rerender(
)
+ expect(view.container.querySelector('[data-analytics-id="skill.detail.render"]')).toBeNull()
+ }
+ })
+
+ it('retains archive download guards on the marked intent button', () => {
+ useSkillDetailMock.mockReturnValue({ data: createSkill({ status: 'ARCHIVED' }), isLoading: false, isFetching: false, error: null })
+ render(
)
+ const download = screen.getByRole('button', { name: 'skillDetail.download' })
+ expect(download.getAttribute('data-analytics-id')).toBe('skill.download.request')
+ expect(download.hasAttribute('disabled')).toBe(true)
+ })
+
it('shows the removed owner read-only reason and keeps controlled draft download without write controls', () => {
useSkillDetailMock.mockReturnValue({ data: createSkill({
ownerReadOnly: true, canManageLifecycle: false, canSubmitPromotion: false,
diff --git a/web/src/pages/skill-detail.tsx b/web/src/pages/skill-detail.tsx
index ff400754b..58c4f2dac 100644
--- a/web/src/pages/skill-detail.tsx
+++ b/web/src/pages/skill-detail.tsx
@@ -828,7 +828,7 @@ export function SkillDetailPage() {
{t('skillDetail.loginRequired')}
{t('skillDetail.loginRequiredDesc')}
-
+
)
}
@@ -864,12 +864,12 @@ export function SkillDetailPage() {
}
return (
-
+
{/* Main Content */}
{skill.ownerReadOnly &&
{t('mySkills.ownerReadOnly')}
}
-
@{skill.namespace}/{skill.slug}
-
+
{skill.avatarUrl && user?.userId !== skill.ownerId && (namespace === 'global'
? isPromotionAdmin
@@ -942,7 +942,7 @@ export function SkillDetailPage() {
{(skill.labels?.length ?? 0) > 0 && (
{skill.labels!.map((label) => (
-
- {t('skillDetail.tabOverview')}
- {t('skillDetail.tabFiles')}
- {t('skillDetail.tabVersions')}
+ {t('skillDetail.tabOverview')}
+ {t('skillDetail.tabFiles')}
+ {t('skillDetail.tabVersions')}
@@ -1012,7 +1012,7 @@ export function SkillDetailPage() {
{isOverviewCollapsible ? (
-
- setRereleaseTarget(null)}>
+ setRereleaseTarget(null)}>
{t('dialog.cancel')}
- handleRereleaseVersion()} disabled={rereleaseVersionMutation.isPending || !targetVersionInput.trim()}>
+ handleRereleaseVersion()} disabled={rereleaseVersionMutation.isPending || !targetVersionInput.trim()}>
{rereleaseVersionMutation.isPending ? t('skillDetail.processing') : t('skillDetail.rereleaseVersion')}
-
-
{
if (!open) {
@@ -1877,7 +1877,7 @@ export function SkillDetailPage() {
onConfirm={handleConfirmPublish}
/>
- {
if (!open) {
@@ -1899,7 +1899,7 @@ export function SkillDetailPage() {
}
}}
>
-
+
{t('skillDetail.compareDialogTitle')}
@@ -1964,7 +1964,7 @@ export function SkillDetailPage() {
- {
setDiffSourceVersion(null)
@@ -1978,7 +1978,7 @@ export function SkillDetailPage() {
{/* File preview dialog */}
-
)
diff --git a/web/src/pages/skill-version-compare.tsx b/web/src/pages/skill-version-compare.tsx
index 483b06400..ef721dd57 100644
--- a/web/src/pages/skill-version-compare.tsx
+++ b/web/src/pages/skill-version-compare.tsx
@@ -157,12 +157,12 @@ export function SkillVersionComparePage() {
})
}}
>
-