Refactor VPN proxy components and update related behavior
This commit is contained in:
@@ -22,7 +22,7 @@ function rule(selector, source = styles) {
|
||||
return new RegExp(`^${escaped}\\s*\\{([\\s\\S]*?)\\n\\}`, 'm').exec(source)?.[1] || '';
|
||||
}
|
||||
|
||||
test('desktop layout keeps the power control on a symmetric center axis', () => {
|
||||
test('desktop layout keeps the power control on a symmetric center axis beside the subscription drawer', () => {
|
||||
const panel = rule('.client-panel');
|
||||
const power = rule('.client-power-section');
|
||||
const form = rule('.client-form');
|
||||
@@ -33,12 +33,11 @@ test('desktop layout keeps the power control on a symmetric center axis', () =>
|
||||
assert.match(form, /position:\s*static/);
|
||||
assert.match(form, /overflow:\s*visible/);
|
||||
assert.doesNotMatch(form, /\bleft\s*:|translateX|transition:[^;]*(?:left|width|transform)/);
|
||||
assert.match(styles, /\.client-panel\.has-subscription \.client-form \{[\s\S]*height:\s*var\(--client-work-height\)/);
|
||||
assert.match(rule('.client-form-content'), /align-content:\s*start/);
|
||||
assert.doesNotMatch(styles, /\.client-panel\.has-subscription \.client-form\b/);
|
||||
assert.doesNotMatch(styles, /\.client-form-content\b/);
|
||||
assert.match(styles, /--client-power-top:\s*calc\(\(var\(--client-work-height\) - 96px\) \/ 2\)/);
|
||||
assert.match(styles, /\.client-panel\.has-subscription \{[\s\S]*transform:\s*translateY\(-9vh\)/);
|
||||
assert.match(styles, /\.client-panel\.has-subscription \.client-power-section \{[\s\S]*height:\s*var\(--client-work-height\);[\s\S]*padding-top:\s*var\(--client-power-top\)/);
|
||||
assert.match(styles, /\.client-panel\.has-subscription \.client-form-content \{[\s\S]*padding-top:\s*var\(--client-power-top\)/);
|
||||
assert.match(rule('.client-panel.is-gateway-home'), /min-height:\s*max\(620px, calc\(100dvh - 80px\)\)[\s\S]*grid-template-rows:\s*minmax\(260px, 1fr\) auto[\s\S]*row-gap:\s*36px/);
|
||||
assert.match(rule('.client-gateway-summary'), /grid-column:\s*1 \/ -1[\s\S]*grid-row:\s*2[\s\S]*justify-self:\s*center[\s\S]*width:\s*min\(860px, calc\(100% - 48px\)\)/);
|
||||
assert.match(styles, /\.client-power-section\.is-gateway \.client-power-control,[\s\S]*width:\s*84px;[\s\S]*height:\s*84px/);
|
||||
@@ -49,9 +48,11 @@ test('desktop layout keeps the power control on a symmetric center axis', () =>
|
||||
assert.match(rule('.client-gateway-traffic-chart'), /min-height:\s*180px/);
|
||||
assert.match(styles, /@media \(max-width: 560px\)[\s\S]*\.client-gateway-traffic-chart,[\s\S]*min-height:\s*130px;[\s\S]*height:\s*130px/);
|
||||
assert.doesNotMatch(rule('.client-gateway-summary'), /background|border|box-shadow/);
|
||||
assert.match(rule('.client-gateway-route-slot'), /min-height:\s*16px[\s\S]*overflow:\s*hidden[\s\S]*text-overflow:\s*ellipsis[\s\S]*white-space:\s*nowrap/);
|
||||
assert.match(rule('.client-applied-operation'), /min-height:\s*15px[\s\S]*overflow:\s*hidden[\s\S]*text-overflow:\s*ellipsis[\s\S]*white-space:\s*nowrap/);
|
||||
assert.match(component, /client-panel\$\{showPower \? '' : ' is-setup'\}[\s\S]*is-gateway-home/);
|
||||
assert.match(component, /const showPower = isGateway \|\| \(hasSubscription && Boolean\(selectedServerId\)\)/);
|
||||
assert.match(component, /const showPower = hasSubscription/);
|
||||
assert.match(component, /\$\{!hasSubscription \? ' is-first-run' : ''\}/);
|
||||
assert.match(component, /\$\{isGateway && hasSubscription \? ' is-gateway-home' : ''\}/);
|
||||
});
|
||||
|
||||
test('page reload plays one stable startup sequence', () => {
|
||||
@@ -66,7 +67,7 @@ test('page reload plays one stable startup sequence', () => {
|
||||
assert.match(styles, /@media \(prefers-reduced-motion: reduce\)[\s\S]*\.client-shell\.is-intro \.client-panel,[\s\S]*animation: none/);
|
||||
});
|
||||
|
||||
test('server rows scroll without moving the subscription column or showing a scrollbar', () => {
|
||||
test('server rows scroll inside the subscription drawer without moving the main layout', () => {
|
||||
const scroll = rule('.client-server-scroll');
|
||||
const simpleScroll = rule('.client-server-mode-panel.is-simple .client-server-scroll');
|
||||
const grid = rule('.client-server-grid');
|
||||
@@ -77,7 +78,8 @@ test('server rows scroll without moving the subscription column or showing a scr
|
||||
assert.match(simpleScroll, /max-height:\s*none/);
|
||||
assert.match(simpleScroll, /overflow:\s*visible/);
|
||||
assert.match(grid, /width:\s*min\(100%, 220px\)/);
|
||||
assert.match(rule('.client-form-content'), /gap:\s*24px/);
|
||||
assert.match(rule('.client-drawer'), /overflow-y:\s*auto/);
|
||||
assert.match(subscription, /client-profile-list[\s\S]*feature\.profiles\.map\(\(profile\) => <ProfileGroup/);
|
||||
assert.match(rule('.client-server-toolbar-title'), /grid-column:\s*2/);
|
||||
assert.match(rule('.client-server-mode-toggle'), /grid-row:\s*2/);
|
||||
assert.match(rule('.client-server-check'), /grid-column:\s*3/);
|
||||
@@ -101,7 +103,7 @@ test('tablet and mobile regions use normal flow with viewport-safe widths', () =
|
||||
|
||||
assert.match(rule('.client-duration-toggle'), /width:\s*min\(290px, 100%\)/);
|
||||
assert.match(mobile, /\.client-server-toolbar \{[\s\S]*grid-template-rows:\s*44px 44px/);
|
||||
assert.match(mobile, /\.client-form-content \{[\s\S]*gap:\s*24px/);
|
||||
assert.match(mobile, /\.client-secondary-menu \{[\s\S]*right:\s*8px/);
|
||||
assert.match(mobile, /\.client-power-control > \.harbor-brand \{[\s\S]*scale\(1\.35\)/);
|
||||
assert.match(mobile, /\.client-server-check \{[\s\S]*width:\s*44px/);
|
||||
assert.match(styles, /\.client-instructions\s*\{[\s\S]*width:\s*min\(470px, 100vw\)/);
|
||||
@@ -239,13 +241,14 @@ test('tooltips stay opaque, above adjacent content, and do not stick after point
|
||||
assert.match(rule('.harbor-mode-tooltip'), /background:\s*oklch\(0\.14 0\.012 145\)/);
|
||||
});
|
||||
|
||||
test('subscription validation waits for the provider and keeps diagnostics below errors', () => {
|
||||
assert.match(subscription, /validateSubscription\(normalizedUrl/);
|
||||
assert.match(subscription, /status: 'checking'/);
|
||||
assert.match(subscription, /status: 'valid'/);
|
||||
assert.match(subscription, /message: ERROR_DEFINITIONS\.SUBSCRIPTION_INVALID\.message/);
|
||||
assert.match(subscription, /validationStatus === 'checking' \? '…' : '×'/);
|
||||
assert.match(subscription, /if \(error\?\.context === 'subscription'\) onDismissError\(\)/);
|
||||
test('subscription validation is local and provider errors keep stable detail slots', () => {
|
||||
assert.match(subscription, /const validationStatus = !normalizedUrl[\s\S]*isSubscriptionUrlValid\(normalizedUrl\) \? 'valid' : 'invalid'/);
|
||||
assert.doesNotMatch(subscription, /validateSubscription\(|AbortController|status: 'checking'/);
|
||||
assert.match(subscription, /const message = feature\.duplicateLabel[\s\S]*feature\.validationStatus === 'invalid'[\s\S]*ERROR_DEFINITIONS\.SUBSCRIPTION_INVALID\.message[\s\S]*feature\.addError\?\.message/);
|
||||
assert.match(subscription, /aria-invalid=\{feature\.validationStatus === 'invalid'\}/);
|
||||
assert.match(subscription, /disabled=\{feature\.addBlocked \|\| !feature\.label\.trim\(\) \|\| feature\.duplicateLabel \|\| feature\.validationStatus !== 'valid'\}/);
|
||||
assert.match(subscription, /if \(!await onAdd\(normalizedLabel, normalizedUrl\)\) return;[\s\S]*resetAdd\(\)/);
|
||||
assert.match(subscription, /setUrl: \(value: string\) => \{[\s\S]*onDismissError\(\)/);
|
||||
assert.match(component, /error\.retry[\s\S]*error\.correlationId/);
|
||||
assert.match(rule('.client-inline-error.is-subscription small'), /flex-basis:\s*100%/);
|
||||
assert.match(rule('.client-inline-error.is-subscription small'), /opacity:\s*0\.45/);
|
||||
|
||||
Reference in New Issue
Block a user