Handle expired subscriptions in client profile UI
Build and Deploy Gateway / build-and-push (push) Successful in 21s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-13 15:06:13 +03:00
parent 0ea2f9d548
commit 019930924d
8 changed files with 68 additions and 39 deletions
@@ -107,11 +107,16 @@ test('feature keeps exact slots, truthy closes and subscription DOM order', () =
test('profiles render as flat accordion groups with scoped controls', () => {
assert.match(feature, /feature\.profiles\.map\(\(profile\) => <ProfileGroup/);
assert.match(feature, /aria-expanded=\{expanded\}/);
assert.match(feature, /const expired = profile\.subscription\.errorCode === 'SUBSCRIPTION_EXPIRED'/);
assert.match(feature, /profileDetails = expired[\s\S]*'Срок действия подписки истёк'[\s\S]*`Серверов:/);
assert.match(feature, /expired \? <div className="client-profile-disclosure is-static">[\s\S]*: <button[\s\S]*client-profile-chevron/);
assert.match(feature, /\{!expired && visited && <div/);
assert.match(page, /const subscriptionError = error\?\.context === 'subscription'[\s\S]*profile\.id === error\.profileId[\s\S]*profile\.subscription\.errorCode === 'SUBSCRIPTION_EXPIRED'[\s\S]*error: subscriptionError/);
assert.match(feature, /profile\.subscription\.status === 'stale'[\s\S]*profile\.subscription\.fetchedAt/);
assert.match(feature, /\{!localStatus && renderServerPicker\(profile/);
assert.doesNotMatch(feature, /Выберите сервер этой подписки|client-profile-server-hint/);
assert.match(feature, /feature\.operations\.profileRefresh\?\.target === profile\.id/);
assert.match(feature, /\{visibleServer && <button[\s\S]*client-profile-selected-server[\s\S]*aria-expanded=\{expanded\}/);
assert.match(feature, /\{!expired && visibleServer && <button[\s\S]*client-profile-selected-server[\s\S]*aria-expanded=\{expanded\}/);
assert.match(feature, /anchorServerId: visibleServer\?\.id \|\| ''/);
assert.match(feature, /const visibleServerId = applied[\s\S]*feature\.selection\.appliedServerId[\s\S]*desired \? profile\.desiredServerId : ''/);
assert.match(feature, /selectedServerId: visibleServerId/);
@@ -119,11 +124,12 @@ test('profiles render as flat accordion groups with scoped controls', () => {
assert.doesNotMatch(page, /onActivateProfile/);
assert.match(feature, /const desired = !feature\.connected[\s\S]*&& !feature\.gatewayDirect/);
assert.match(feature, /const profileDomain = subscriptionDomain\(profile\.subscription\.host\)/);
assert.match(feature, /const profileDetails = `Серверов: \$\{profile\.servers\.length\}/);
assert.doesNotMatch(feature, /<strong>\{profile\.label\}<\/strong>|Переименовать|client-profile-menu/);
assert.match(styles, /\.client-profile-group/);
assert.match(styles, /\.client-profile-body/);
assert.match(styles, /\.client-profile-body \{[\s\S]*grid-template-rows: 0fr[\s\S]*\.client-profile-body\.is-open \{[\s\S]*grid-template-rows: 1fr/);
assert.match(styles, /\.client-profile-body-inner \{[\s\S]*padding: 0 0 4px 24px/);
assert.match(styles, /\.client-profile-group\.is-expired \.client-profile-title small \{[\s\S]*color: oklch\(0\.68 0\.14 72\)/);
assert.match(styles, /@media \(prefers-reduced-motion: reduce\)[\s\S]*\.client-profile-body \*/);
assert.match(styles, /\.client-profile-refresh\.is-refreshing/);
assert.match(styles, /\.client-profile-refresh,[\s\S]*\.client-profile-delete\s*\{[\s\S]*width:\s*36px;[\s\S]*height:\s*36px;[\s\S]*display:\s*grid;[\s\S]*place-items:\s*center/);