Replace mock HTTP server + vi.mock approach with vi.stubGlobal('fetch')
using a public IP (1.2.3.4) that passes real SSRF validation. This
eliminates both the fragile vi.mock (broken under V8 coverage) and the
localhost network dependency (unreliable in CI).
Revert the SSRF_ALLOW_PRIVATE env var that broke ssrf unit tests.
Extend timeout for exotic format error resilience tests to 120s to
accommodate slow JXL + Image enhancement combination in CI.
Install ImageMagick, Ghostscript, libjxl-tools, and libopenjp2-tools in
CI so exotic format decoder tests (PSD, EPS, HDR, ICO, JP2, etc.) can
run. Relax ImageMagick EPS/PS security policy to match the Dockerfile.
Replace fragile vi.mock() of the SSRF module in fetch-urls tests with an
env-var guard (SSRF_ALLOW_PRIVATE) that bypasses private-IP checks in
the test environment. The vi.mock approach broke under V8 coverage
instrumentation in CI.
The default-view redirect in HomePage fired on every mount, not just the
initial page load. A module-level flag now gates the redirect so it only
applies once per session, allowing users to switch to sidebar view when
grid is the default.
Closes#128
captureException now checks isRequestOptedIn before forwarding errors
to Sentry, closing a gap where server errors leaked to an external
service even when no user had consented. The PII scrubbing regex is
also fixed: he[ic]f? failed to match .heic due to word-boundary
behavior and is replaced with hei[cf]? which correctly covers .heic,
.heif, and .hei.
Adds 88 new analytics tests across unit, integration, and e2e layers
proving PostHog/Sentry are never invoked when analytics is disabled or
users have not consented, plus full 7-day reminder lifecycle coverage.
PostHog SDK was initialized on app mount based only on the server-level
config flag, ignoring user consent. This caused network requests to
us-assets.i.posthog.com (config.js, web-vitals.js, dead-clicks-autocapture.js)
even when the user had not opted in or had explicitly declined telemetry.
- Replace static imports of posthog-js and @sentry/react with dynamic
import() so the SDK bundles are not downloaded until consent is granted
- Gate initAnalytics on analyticsConsent.analyticsEnabled === true,
not just server config.enabled
- Add consent re-check after each await import() to handle revocation
during the async load
- Add shutdownAnalytics() that calls opt_out_capturing() + reset()
for mid-session consent revocation
- setAnalyticsConsent(false) now triggers full SDK shutdown automatically
- Rewrite analytics test suite with 44 tests covering init gating,
shutdown lifecycle, consent toggle, race conditions, and Sentry callbacks
Closes#98
Add ~500 new E2E tests and ~300 new integration tests covering:
- 24 new GUI E2E specs: navigation, responsive layout, keyboard shortcuts,
tool UI for all 35 non-AI tools, batch/pipeline workflows, settings/RBAC,
visual regression, accessibility, and performance budgets
- 3 new E2E-Docker specs: batch workflows, advanced pipelines, cross-format
- 1 new adversarial integration test: memory pressure, corrupted files,
unicode filenames, extreme dimensions, pipeline/batch edge cases
- 29 expanded integration test files: HEIC/HEIF input, large files, parameter
boundaries, batch processing, format edge cases across all tools
- Cross-format matrix expanded: 641 tests covering every tool x 18 formats
- AI bridge unit tests expanded: lifecycle, tool modules, error propagation
- Unit test gaps filled: analytics, tool-registry, web stores
Also fixes:
- vitest.config.ts: exclude e2e-docs and e2e-landing from Vitest runner
- AI E2E specs: add sidecar health check to skip gracefully when Python
AI backend is not running instead of timing out
- Add Cloudflare Pages deployment for landing page (snapotter.com) and
docs (docs.snapotter.com)
- Create deploy-landing.yml and update deploy-docs.yml workflows
- Update CI to ignore apps/landing/** paths
- Fix logo transparency (remove white background) across all apps
- Recreate social-preview.png with SnapOtter branding
- Update all docs URLs from GitHub Pages to docs.snapotter.com
- Update VitePress config: light theme default, fix llms.txt paths
- Add .vitepress/cache/ and .env.* to gitignore
.worktrees/ and .claude/ contain git worktrees from other developers
which have their own test files. Without exclusion, vitest picks them
up and fails due to mismatched React/module resolution.
The Playwright-based Docker e2e tests use test.describe() which is
incompatible with Vitest. Exclude tests/e2e-docker/ from Vitest's
test discovery, matching the existing tests/e2e/ exclusion.
vitest was spawning multiple worker processes (one per test file) that
each independently opened the same SQLite database and raced to set
journal_mode = WAL. Adding pool: "forks" with singleFork: true runs all
test files in the same child process, eliminating the concurrent-open
race. The test suite (~830 tests) remains fast enough for CI.
- Health endpoint returns "healthy" instead of "ok" for consistency
- MAX_USERS now configurable via env var (default 5)
- People API returns team names instead of UUIDs in register/list
- PUT user update accepts team names (name-first lookup, fallback to ID)
- Login rate limit follows global rate limit when RATE_LIMIT_PER_MIN > 1000
- Strip-metadata preserves original format encoding instead of always PNG
- Fix e2e tests: rotate/crop/border button selectors match actual UI
- Fix e2e tests: create Engineering/Design teams in people test setup
- Fix e2e tests: people UI uses select for team field, not text input
- Update visual regression baseline for tablet home page
Playwright e2e tests were being collected by vitest in CI, causing
failures since Playwright isn't available in that context. Exclude
tests/e2e/** from vitest runs (e2e tests should run via pnpm test:e2e).