Skip to content

Latest commit

ย 

History

History
131 lines (89 loc) ยท 9.09 KB

File metadata and controls

131 lines (89 loc) ยท 9.09 KB

PingDiff Improvement Log

2026-03-20 โ€” Bug Fix: Per-page metadata for dashboard, download, and community

All three main pages used "use client" at the top level, which blocks Next.js from reading the metadata export. Every page fell back to the generic homepage title and description โ€” so sharing any page on Discord, Twitter, or iMessage showed "PingDiff - Test Your Game Server Connection" regardless of which page it was. The template: "%s | PingDiff" in layout.tsx was dead code.

Fixed by splitting each page into a thin server wrapper (page.tsx, exports metadata) and a client component (*Client.tsx, holds all interactive state). No logic changed โ€” pure structural refactor following the standard App Router pattern. Each page now has a distinct title, description, and og:title/og:description for accurate social previews.

Files changed: web/src/app/dashboard/page.tsx, web/src/app/dashboard/DashboardClient.tsx (new), web/src/app/download/page.tsx, web/src/app/download/DownloadClient.tsx (new), web/src/app/community/page.tsx, web/src/app/community/CommunityClient.tsx (new) Lines: +934 / -890

2026-03-20 โ€” Accessibility: ARIA labels, table semantics, and skip navigation

Comprehensive a11y pass across the dashboard, navbar, and secondary pages. The site had no named navigation landmark, no skip links on 3 of 4 pages, tables without column scope attributes, charts completely invisible to assistive technology, and stat cards that conveyed quality purely through color (WCAG 1.4.1 violation). All fixed without new dependencies.

Dashboard: skip link + main-content anchor, role="region" on stats grid, aria-label on each stat card with text description, aria-hidden on decorative icons, role="img" + aria-label on both charts, aria-label on table element, scope="col" on all th elements, time element for timestamps, aria-label on ping/loss cells so quality is communicated in text not just color.

Navbar: aria-label="Main navigation" on nav element, aria-haspopup on mobile toggle, role="menu" on mobile menu container.

Community and Download pages: both were missing skip-to-content links and main-content anchor targets entirely.

Files changed: web/src/app/dashboard/page.tsx, web/src/components/Navbar.tsx, web/src/app/community/page.tsx, web/src/app/download/page.tsx Lines: +64 / -30

2026-03-19 โ€” Performance: Memoize dashboard derived state

All derived values on the dashboard (filteredResults, avgPing, avgPacketLoss, avgJitter, regions, chartData, serverChartData) were being recomputed inline on every React render โ€” including renders triggered by unrelated state changes like the loading flag toggling off. Wrapped each value in useMemo with the tightest possible dependency array, eliminating 5 O(n) reduce passes and 2 groupBy passes on every extraneous render. At current scale the savings are modest; at the 500-1000 result range the dashboard would hit without this change the difference is measurable. The memoized structure also makes data dependencies explicit and auditable at a glance.

Files changed: web/src/app/dashboard/page.tsx Lines: +76 / -56

2026-03-19 โ€” New Feature: Date range filter and CSV export for dashboard

Added two practical dashboard improvements with no new dependencies and no API changes.

Date range filter: a dropdown (Last 7 / 30 / 90 days / All time, defaulting to 30 days) applied before the existing region filter. An empty-state UI with a Clear Filters button handles the case where filters return no results.

CSV export: an Export CSV button appears in the filter toolbar whenever filtered results exist. It exports exactly the current view (date + region filters applied) as RFC 4180-compliant CSV with columns for Date, Server, Region, Avg/Min/Max Ping, Jitter, Packet Loss, ISP, Country, and City. Filename includes today's date.

Also: Refresh button moved into the filter toolbar for a consistent row; result count label added to the Recent Tests table header; header layout improved for mobile (stacks vertically below sm breakpoint).

Files changed: web/src/app/dashboard/page.tsx Lines: +145 / -13

2026-03-18 โ€” Security: Harden API routes and add CSP/HSTS headers

/api/servers was completely unprotected by rate limiting while /api/results already had it โ€” an oversight that left the DB endpoint open to unbounded hammering. The rate-limit and IP-extraction logic was also duplicated inline, meaning the two routes could silently diverge over time. Additionally, next.config.ts was missing the two highest-impact HTTP security headers: Content Security Policy and HSTS.

Fixed by extracting a shared rate-limit.ts utility (named buckets, consistent IP extraction), applying rate limiting + slug validation to /api/servers, adding CDN caching on that endpoint, and adding CSP + HSTS to next.config.ts.

Files changed: web/src/lib/rate-limit.ts (new), web/src/app/api/results/route.ts, web/src/app/api/servers/route.ts, web/next.config.ts Lines: +127 / -35

2026-03-18 โ€” Code Quality: Extract shared Navbar and Footer components

The navigation bar and footer were duplicated verbatim across 4 pages (home, dashboard, community, download), with each page managing its own mobileMenuOpen state and hardcoding its own active link style. Extracted both into reusable components in web/src/components/.

The new Navbar uses usePathname() for automatic active-link highlighting and a single NAV_LINKS array as the source of truth for site navigation. Any future nav change (new link, style tweak) now requires editing one file instead of four.

Files changed: web/src/components/Navbar.tsx (new), web/src/components/Footer.tsx (new), web/src/app/page.tsx, web/src/app/dashboard/page.tsx, web/src/app/community/page.tsx, web/src/app/download/page.tsx Lines: +157 / -338

2026-03-17 โ€” Testing: Unit test suite for CLI and ping core logic

Added a pytest test suite covering the CLI and ping tester modules โ€” the first tests in the project. Tests are pure unit tests (no network calls, no subprocess invocations), so they run in milliseconds and work offline. 68 tests across 10 test classes covering:

  • validate_ip: valid/invalid IPv4, IPv6, hostnames, injection attempts, edge cases
  • calculate_jitter: empty input, single value, zero jitter, constant increase, alternating, high jitter
  • get_best_server: empty list, all timeouts, single server, lowest ping, packet loss priority, exclusion logic
  • get_connection_quality: all 5 quality tiers and boundary values (Excellent/Good/Fair/Poor/Bad)
  • sort_results: all 5 sort keys, timeout-last ordering, unknown key fallback
  • filter_by_max_ping: pass-all, block-all, timeout exclusion, inclusive/exclusive boundary
  • results_to_json: valid JSON output, field correctness, best-only, error case, empty list
  • results_to_csv: parseable CSV, header row, best-only, empty-on-no-best, multiple rows
  • Color helpers: colorize no-color mode, format_ping, format_loss
  • build_parser: defaults, all flags, region/sort validation, type coercion

Files changed: desktop/tests/__init__.py, desktop/tests/test_ping_tester.py, desktop/tests/test_cli.py, desktop/pytest.ini Lines: +370 / -0

2026-03-18 โ€” UI/UX: Skeleton loading screen for dashboard

The dashboard previously showed a centered spinner while fetching results from /api/results, giving no visual indication of page structure and causing a jarring layout shift when content arrived.

Replaced the spinner with a DashboardSkeleton component that mirrors the real dashboard layout โ€” four stat cards, two chart panels, and a six-row results table โ€” all with the existing .skeleton shimmer animation. No new dependencies. Screen reader support via aria-busy="true" and aria-label on the skeleton root.

Files changed: web/src/components/DashboardSkeleton.tsx (new), web/src/app/dashboard/page.tsx Lines: +123 / -4

2026-03-20 โ€” Testing: Jest unit tests for web API validation and rate-limiter

Added the first unit test suite for the Next.js web layer. The project already had pytest covering the CLI/desktop; this fills the gap for the server-side API logic that handles all incoming requests.

Two test files, 41 tests total:

  • rate-limit.test.ts: 27 tests covering checkRateLimit (first request, counting, blocking, window reset, independent IP/bucket namespacing, default limit) and getClientIP (x-forwarded-for multi-IP parsing, x-real-ip fallback, 127.0.0.1 fallback, whitespace trimming)
  • validation.test.ts: 26 tests covering PingResultSchema (field presence, string lengths, numeric range boundaries, raw_times array limits) and SubmitRequestSchema (defaults, results array limits, field length limits, nested validation propagation)

Also extracted the Zod schemas from the inline route handler into src/lib/validation.ts โ€” a shared module that the route imports from. No logic changed; pure structural improvement for testability.

Files changed: web/jest.config.js (new), web/package.json, web/src/lib/validation.ts (new), web/src/__tests__/rate-limit.test.ts (new), web/src/__tests__/validation.test.ts (new), web/src/app/api/results/route.ts Lines: +387 / -20