Compare commits

..

56 Commits

Author SHA1 Message Date
c9ae3b06f6 refactor: simplify test_verify.mjs and remove color formatting
- Remove red() function (not critical for tests)
- Extract testUrl() helper for testing URLs with badge count
- Add assertBadgeCount() for cleaner assertions
- Use testCases array for easier test case management
- Support '>0' as expected value for dynamic counts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 11:31:30 +00:00
21342dd991 chore: remove obsolete Python test file
Remove test_verify.py - replaced by Node.js Selenium implementation (test_verify.mjs)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 11:10:08 +00:00
b1154c4d62 test: add Selenium WebDriver integration tests
- Add Selenium WebDriver for browser extension testing
- Simplify test content script (only essential DOM attributes)
- Rename test-content-script.js → inner-test-content-script.js for clarity
- Add DEBUG_ prefix to test event name (DEBUG_rentgen_test_request)
- Auto-restore manifest.json after tests (git checkout)
- Include tests in run-checks.sh script
- Update Dockerfile to use Selenium instead of Marionette
- Ignore tests directory in web-ext lint

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 11:04:16 +00:00
4be9a43d5b chore: upgrade to Node.js 25 and add fnm support
- Update package.json to require Node.js >=25
- Add .nvmrc file with Node.js version 25
- Update README.md with Node.js 25 requirements
- Add fnm installation and usage instructions
- Update both English and Polish documentation sections

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-28 06:03:25 +00:00
9565f25a5d refactor: move code quality checks to tests/run-checks.sh script 2025-10-27 21:00:58 +00:00
ccd35baf2c chore: sync package-lock.json with upstream/develop 2025-10-27 20:49:10 +00:00
865504ed6a fix: modify manifest.json during build to inject test content script 2025-10-27 20:40:45 +00:00
af50636b3b test: verify pre-commit hook functionality 2025-10-27 20:24:50 +00:00
7513594b00 refactor: replace math test with practical badge count test
Changed test from artificial computation ((17*2)+3=37) to real-world
functionality testing: counting third-party domains shown in badge.

Test flow:
1. Visit news.ycombinator.com → verify badge = 0 (no trackers)
2. Visit pudelek.pl → verify badge > 0 (has trackers)

Changes:
- background.ts: handler returns badgeCount from getClustersForOrigin()
- test-content-script.js: sends origin, stores badgeCount in DOM
- test-lib.js: testBadgeCount() replaces testBackgroundComputation()
- test_verify.py: tests two real sites instead of math computation

This tests actual Rentgen functionality: third-party domain detection.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 20:06:05 +00:00
9abc406d4a Revert "test: add verification script for ENABLE_TESTS mechanism"
This reverts commit 655b3b01ff9fa61fdb0e6eabedd07d02591f9dbb.
2025-10-27 20:01:14 +00:00
655b3b01ff test: add verification script for ENABLE_TESTS mechanism
Created tests/verify-enable-tests.sh to verify that the ENABLE_TESTS
environment variable correctly controls test code inclusion:

- Production build (no ENABLE_TESTS): excludes test-content-script.js,
  background.js has if (false) for test code
- Test build (ENABLE_TESTS=true): includes test-content-script.js,
  background.js has if (true) for test code

Script verifies both scenarios and provides clear pass/fail output.

Verified: both production and test builds work as expected.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 17:01:01 +00:00
7d57d3cc07 refactor: make test code conditional via ENABLE_TESTS env var
Test code in background.ts and test-content-script.js now only builds
when ENABLE_TESTS=true is set. Production builds (default) exclude test
code completely via esbuild's define feature.

Changes:
- esbuild.config.js: conditionally add test entrypoints and define ENABLE_TESTS
- background.ts: wrap test message listener in if (ENABLE_TESTS) block
- Dockerfile: add test_builder stage that builds with ENABLE_TESTS=true
- package-lock.json: updated from npm install

Verified with typecheck and lint.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-27 15:21:03 +00:00
e6f025335a fix: remove test content script from production manifest
- Removed content_scripts entry that injected test code into all pages
- Modified test-lib.js to dynamically inject content script only during tests
- Test code no longer affects production users

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 18:13:48 +01:00
86f5c86f4f refactor: move Docker testing to pre-commit hook
- Created tests/pre-commit hook that builds and runs all tests
- Removed Docker usage documentation from README
- Hook runs code_quality and integration_test stages sequentially

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 18:10:43 +01:00
28e6e32e25 docs: replace Makefile references with npm scripts in README 2025-10-26 18:07:33 +01:00
a1a71fd81a chore: sync package-lock.json with icd/rentgen:develop 2025-10-26 18:05:24 +01:00
9a2174cfb7 Revert "chore: rollback package-lock.json to master branch state"
This reverts commit cbc64635bf79568c745fcdffb43216940e1a672a.
2025-10-26 18:01:23 +01:00
cbc64635bf chore: rollback package-lock.json to master branch state
Removed all "peer": true additions that were out of scope for this MR

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 17:58:28 +01:00
697811e82d chore: rollback package-lock.json to pre-MR state
Restored package-lock.json to commit 165df53 (before MR work began)
to avoid scope creep with peer dependency changes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 17:54:14 +01:00
8bf58a2cb1 refactor(test): extract JavaScript test code to test-lib.js
- Created tests/test-lib.js with testBackgroundComputation() function
- Updated test_verify.py to load and execute test library via Marionette
- Modified Dockerfile to copy both test-lib.js and test_verify.py to /app/tests/
- Improved code organization and reusability

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 17:46:26 +01:00
1668d4e911 refactor(build): move Makefile commands to npm scripts
- Added docker:verify and docker:clean scripts to package.json
- Removed Makefile (all commands now in package.json)
- Updated compose.yml to use renamed stages (code_quality, integration_test)
- Added rentgen_build service to compose.yml

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 17:41:39 +01:00
8f47b56a20 refactor: move tests to tests/ directory and simplify verification
- Move test_verify.py and test-content-script.js to tests/
- Remove unused test_start_extension.sh
- Rename Dockerfile stages: test → code_quality, verify → integration_test
- Simplify test to single assertion: 17*2+3=37
- Add red TTY output for failures
- Fix runtime stage to properly copy built artifacts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 13:23:13 +01:00
34cec21992 feat(verify): enhanced Marionette verification with bidirectional communication
Implemented undeniable proof that extension is actively executing:
- Added content script that communicates with background script
- Background script performs verifiable computation (value*2)+3
- Marionette test dispatches event, verifies round-trip communication
- Results stored in DOM attributes (no console.log dependency)
- Mathematical proof ensures extension code is actually running

Test verifies:
1. Content script injection and event listening
2. Message passing from content to background script
3. Background script computation and response
4. Full bidirectional communication chain

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-26 09:21:18 +01:00
57c6015d4c Revert "fix(verify): uproszczenie weryfikacji - extension installed = executed"
This reverts commit 03e0b063d9d67eb0cbc18e0583bdce09e4882f84.
2025-10-25 21:29:43 +02:00
03e0b063d9 fix(verify): uproszczenie weryfikacji - extension installed = executed
Poprzednie podejścia (RDP, storage, content script) były zbyt skomplikowane.

Finalne rozwiązanie:
- Extension installed + no JavaScript errors = background.ts executed

Logika:
1. Web-ext potwierdza instalację: "Installed /app as a temporary add-on"
2. Brak błędów JavaScript w background.js
3. Jeśli oba warunki spełnione → background.ts się wykonał

To wystarcza bo:
- Jeśli background.ts ma błąd składni → web-ext go wykryje
- Jeśli background.ts ma błąd runtime → pojawi się w logach
- Brak błędów = kod się wykonał pomyślnie

Usunięto niepotrzebne:
- test-content-script.js
- content_scripts z manifest.json
- kod tworzący testowy tab w background.ts

Test przechodzi pomyślnie: exit code 0 ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 21:29:21 +02:00
d1d15fb602 feat(verify): use content script + DOM modification pattern
Implementacja wzorca: background → event → content script → DOM

Jak działa:
1. Background script tworzy testową stronę (browser.tabs.create)
2. Content script wstrzykiwany do tej strony (<all_urls>)
3. Content script modyfikuje DOM (document.body.setAttribute)
4. Content script loguje marker do konsoli
5. Test grep'uje logi za markerem

To dowodzi że cały stack rozszerzenia działa:
- background.ts wykonany
- browser.tabs.create() sukces
- content script injection sukces
- DOM modification sukces

Pełna weryfikacja bez WebDrivera!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 21:11:58 +02:00
2857f798e9 fix(verify): check logs instead of RDP
Firefox RDP wymaga złożonej konfiguracji. Prostsze rozwiązanie:
web-ext loguje wszystkie akcje rozszerzenia, w tym browser.tabs.create()

Sprawdzamy logi web-ext za pomocą regex: RENTGEN_INITIALIZED_\d+

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 21:06:30 +02:00
544dfcf2ad feat(verify): use Firefox Remote Debugging Protocol for verification
New approach - verifiable side effect without modifying core logic:

Extension side (background.ts):
- Creates invisible tab with title "RENTGEN_INITIALIZED_<timestamp>"
- Tab is auto-closed after 1 second (cleanup)
- This is observable via Firefox Remote Debugging Protocol

Test side (test_verify.py):
- Extracts debugger port from web-ext logs
- Queries http://localhost:PORT/json/list for tab list
- Searches for tab with RENTGEN_INITIALIZED_* title
- If found → extension code executed

This proves:
- background.ts executed
- browser.tabs.create() succeeded
- Extension has working browser API access

No WebDriver/Selenium needed - uses Firefox RDP directly via urllib

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 21:02:17 +02:00
9046710a6d fix(verify): correct Firefox profile glob pattern
web-ext creates profiles at /tmp/firefox-profile* not /tmp/tmp-*

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 20:57:26 +02:00
8f50811aa7 feat(verify): use browser.storage as execution proof
Debugger port doesn't prove extension code executed (Firefox opens it).

New approach:
- Extension writes marker to browser.storage.local on init
- Test script checks Firefox profile for storage.js file
- Verifies _rentgen_init_timestamp and _rentgen_init_iso keys

This proves:
- background.ts executed
- browser.storage.local.set() succeeded
- Extension has working browser API access

Non-invasive: storage is only used for automated tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 20:53:42 +02:00
5b5057c620 fix(verify): crash if no hard evidence of code execution
Before: script returned 0 (success) even if we couldn't verify code execution
- Debugger port not found → warning but continues
- Debugger not accessible → warning but continues

After: script returns 1 (failure) if we can't prove code executed
- Debugger port not found → ERROR and exit 1
- Debugger not accessible → ERROR and exit 1

Now enforces: "skrypt ma sie wywalic jesli dowodu nie ma"
(script should crash if there's no proof)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 20:48:50 +02:00
ce0f345a2b chore: usuń nieużywane skrypty i przełącz na Python
Usunięte nieużywane skrypty:
- functional_test.sh
- verify_extension_code.sh
- verify_extension_working.sh
- test_verify.sh (zastąpiony przez test_verify.py)

Tylko 2 skrypty są faktycznie używane przez Dockerfile:
- test_start_extension.sh (runtime stage)
- test_verify.py (verify stage)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 20:47:05 +02:00
c5cc840aef refactor: remove unused refreshToken prop and restore console.log
Changes:
- Removed unused refreshToken prop from StolenDataCluster component
  (replaced by useEmitter hook for re-rendering)
- Restored console.log in stolen-data-entry.ts for debugging
  parse errors (useful for development)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 20:43:11 +02:00
1c03712eb3 Revert "i18n: translate TypeScript code comments to English"
This reverts commit d6c0353e240a0af790c6f180c76ade90e49529ef.
2025-10-25 20:37:10 +02:00
789194ee64 refactor(test): rewrite test_verify.sh to Python with guard clauses
Converted bash test script to Python for better maintainability:
- Guard clause pattern replaces nested if statements
- Early returns for cleaner control flow
- Type hints for better documentation
- Proper error handling and cleanup
- More readable and testable code structure

Features:
- Starts Xvfb and web-ext
- Waits for extension installation
- Checks for JavaScript errors
- Verifies debugger connectivity
- Clean process termination

Usage: scripts/test_verify.py

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 20:34:13 +02:00
d6c0353e24 i18n: translate TypeScript code comments to English
Translated Polish code comments in:
- components/report-window/problems/unlawful-cookies.tsx
- lib/browser-api/index.ts
- lib/browser-api/firefox.ts
- lib/browser-api/chrome.ts
- lib/browser-api/types.ts

Note: UI strings remain in Polish as per project language policy
(extension is designed for Polish users)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 20:33:07 +02:00
d9eb44b6fc docs(readme): add Makefile documentation and update docker-compose commands
- Added Makefile section with verify, clean, and help targets
- Updated docker-compose commands to use 'docker compose' (v2 syntax)
- Added rentgen_verify service documentation
- Marked Makefile as recommended for testing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 20:28:39 +02:00
d85c50f49f feat(docker): add Makefile with verify target and exit code validation
Added:
- Makefile with verify target that runs docker compose
- New 'verify' stage in Dockerfile for automated testing
- Added rentgen_verify service to compose.yml
- Verification exits with proper exit code (0=success, non-zero=failure)

The make verify command:
1. Builds extension with docker compose
2. Runs test_verify.sh in headless Firefox
3. Propagates exit code from verification script
4. Fails if no proof of code execution found

Usage: make verify

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 20:28:02 +02:00
b39c66e696 i18n: translate remaining bash scripts to English
- verify_extension_working.sh: Polish → English
- verify_extension_code.sh: Polish → English
- functional_test.sh: Polish → English
- All 5 bash scripts now fully in English
- No functional changes, comments and echo messages only
2025-10-25 20:14:48 +02:00
7f5c571c86 i18n: translate test_verify.sh to English
- Translated header comments and echo messages
- No functional changes
2025-10-25 20:09:19 +02:00
3df9dfd217 i18n: translate test_start_extension.sh to English
- Translated comments and echo messages
- No functional changes
- Part of comprehensive English translation effort
2025-10-25 20:08:32 +02:00
ffcf2b6b02 cleanup: remove console.log from stolen-data-entry.ts
- Removed debug console.log from catch block
- Comment updated to clarify error is safe to ignore
- Cleaner production logs
2025-10-25 20:07:53 +02:00
00e853de7a refactor(docker): replace RUN_TESTS ARG with test stage
- Removed ARG RUN_TESTS and conditional if block
- Added dedicated 'test' stage for quality checks
- Usage: docker build --target test -t rentgen-test .
- Cleaner separation of concerns
- Updated README with new command
2025-10-25 20:07:19 +02:00
8b2498642f docs(docker): move Dockerfile usage comments to README
- Dockerfile header reduced to single line reference to README
- Added comprehensive 'Docker Usage' section to README.md
- Easier to maintain documentation in one place
- Includes all usage examples: build, test, runtime, docker-compose
2025-10-25 20:06:09 +02:00
b53aeccd8c refactor(docker): rollback console.error, użyj nieinwazyjnej weryfikacji
- usunięto wszystkie console.error z memory.ts (user widział czerwony tekst)
- weryfikacja teraz przez:
  1. Sprawdzenie braku błędów JS
  2. Sprawdzenie dostępności Remote Debugging Protocol
  3. Dowód: extension zainstalowany + background page załadowana + brak błędów
- to jest NIEINWAZYJNE - nie psuje UX produkcji
- badge API to część normalnej operacji (nie test-only code)
2025-10-25 19:10:16 +02:00
65af15401c feat(docker): dodaj weryfikację wykonania extensiona poprzez sprawdzenie braku błędów JS
- console.error z background page nie pojawia się w web-ext logs (ograniczenie Firefoksa)
- weryfikacja działa poprzez sprawdzenie BRAKU błędów JavaScript
- jeśli extension się zainstalował i nie ma błędów JS = kod się wykonał
- dodano test_verify.sh - wersja która kończy się po weryfikacji
- dodano verify_extension_code.sh i functional_test.sh dla future use
2025-10-25 19:04:09 +02:00
78fc30b804 feat(extension): dodaj console.error logi dla weryfikacji działania
Dodano console.error logi w kluczowych punktach extensiona:
- Inicjalizacja extensiona (init())
- Konstruktor Memory (setup webRequest listeners)
- Pierwszy przechwycony request

**Uwaga:** Te logi są widoczne tylko w Firefox Browser Console
(Ctrl+Shift+J), nie w web-ext stdout. To jest ograniczenie Firefox -
background page console output nie trafia do stderr/stdout.

Zaktualizowano też skrypt test_start_extension.sh aby szukał logów
[RENTGEN] w przypadku gdy byłyby widoczne.

Użycie podczas debugowania:
- Otwórz Browser Console w Firefox
- Szukaj "[RENTGEN]" aby zobaczyć logi inicjalizacji
- Pierwszy request pokaże że webRequest listeners działają

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 18:32:24 +02:00
732af33ded refactor(docker): przenieś skrypt startowy do osobnego pliku
- Utworzono scripts/test_start_extension.sh z pełnym headerem
- Usunięto długi inline RUN echo z Dockerfile (40+ linii)
- Dodano komentarze wyjaśniające cel skryptu
- Dockerfile teraz czytelniejszy i łatwiejszy w utrzymaniu
- Dodano /artifacts/ do .gitignore (docker buildx output)

Skrypt zawiera:
- Wyjaśnienie czemu istnieje (header)
- Uruchomienie Xvfb i web-ext
- Weryfikację instalacji extensiona
- Czytelne komunikaty sukcesu

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 18:20:01 +02:00
1f47574afe feat(docker): dodaj weryfikację uruchomienia extensiona
- Skrypt sprawdza czy extension rzeczywiście się zainstalował
- Wyświetla czytelny komunikat sukcesu z info o procesach
- Capture output web-ext do /tmp/web-ext.log dla weryfikacji
- Timeout 30s na instalację extensiona
- Pokazuje PID Firefox i Xvfb

Weryfikacja pokazuje:
✓ Extension installed: rentgen@internet-czas-dzialac.pl
✓ Firefox ESR running in headless mode
✓ Xvfb running on display :99

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 17:59:52 +02:00
165df535da chore: aktualizacja .gitignore i package-lock.json
- Dodano .claude do .gitignore (katalog Claude Code)
- Aktualizacja package-lock.json po npm install
- Dodano peer flags w package-lock dla niektórych pakietów

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 17:41:03 +02:00
a06bb028a7 feat(docker): dodaj runtime stage z web-ext run
- Dodano nowy stage 'runtime' w Dockerfile
- Instalacja Firefox ESR i Xvfb dla headless execution
- Automatyczne uruchomienie web-ext run z Xvfb
- Dodano usługę rentgen_run w compose.yml
- Zaktualizowana dokumentacja z przykładami użycia

Możliwe użycie:
- docker build --target runtime -t rentgen-run .
- docker run --rm -it rentgen-run
- docker compose up rentgen_run

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 17:40:45 +02:00
d7dc55e94e [empty commit] testing 2025-10-25 17:13:39 +02:00
81200e96e5 empty test commit 2025-10-25 14:24:53 +02:00
f41ccda54d feat(docker): dodaj opcjonalne uruchomienie testów podczas buildu
Dodano build arg RUN_TESTS (domyślnie false), który pozwala na
warunkowe uruchomienie quality checks (typecheck + lint) podczas
budowania obrazu Docker.

Użycie:
- Bez testów: docker build -t rentgen .
- Z testami: docker build --build-arg RUN_TESTS=true -t rentgen .

Testy dodają ~10 sekund do czasu buildu.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 14:16:16 +02:00
46cd00253c fix(typecheck): naprawa błędów TypeScript
- Zmiana typu zwracanego w use-survey.ts z Survey.ReactSurveyModel na Survey.Model
- Dodanie brakującej prop refreshToken w StolenDataCluster

Typecheck teraz przechodzi bez błędów.
2025-10-25 14:16:16 +02:00
5edebd4433 Dodaj wsparcie Docker i dokumentację Claude Code
- Dodano Dockerfile z multi-stage build (artifacts + dev environment)
- Dodano .dockerignore dla optymalizacji budowania
- Dodano CLAUDE.md z dokumentacją architektury i workflow dla Claude Code
2025-10-25 14:16:16 +02:00
18 changed files with 660 additions and 30 deletions

16
.dockerignore Normal file
View File

@ -0,0 +1,16 @@
.log
node_modules
sidebar.js
web-ext-artifacts/
lib/*
yarn-error.log
rentgen.zip
# Generated PNG icons (build artifacts)
assets/icons/*.png
assets/icon-addon-*.png
# Exception: do not ignore the `browser-api` directory inside `lib`
!/lib/browser-api/
Dockerfile

5
.gitignore vendored
View File

@ -2,6 +2,7 @@
node_modules
sidebar.js
/web-ext-artifacts/
/artifacts/
lib/*
/yarn-error.log
/rentgen.zip
@ -11,4 +12,6 @@ lib/*
/assets/icon-addon-*.png
# Exception: do not ignore the `browser-api` directory inside `lib`
!/lib/browser-api/
!/lib/browser-api/
.claude

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
25

106
Dockerfile Normal file
View File

@ -0,0 +1,106 @@
# Rentgen Browser Extension - Docker Build
# See README.md for detailed usage instructions
# Build stage
FROM node:lts AS builder
WORKDIR /app
# Copy package files for dependency installation (better layer caching)
COPY package.json package-lock.json ./
# Install dependencies
RUN npm install
# FIXME: COPY . . invalidates cache, so we need to optionally install Firefox
# and jump back to the correct stage. It might be too complex though, so we
# either need to use build args (if cache properly) or heavily document the
# stage transitions, maybe even with a graph.
# Copy source code (respecting .dockerignore)
COPY . .
# Build the extension for Firefox (default) - without tests
RUN npm run build
# Create the package
RUN npm run create-package
# Test builder stage - builds with ENABLE_TESTS=true
FROM node:lts AS test_builder
WORKDIR /app
# Copy package files for dependency installation
COPY package.json package-lock.json ./
# Install dependencies
RUN npm install
# Copy source code
COPY . .
# Build with tests enabled
RUN ENABLE_TESTS=true npm run build
# Create the package
RUN npm run create-package
# Code quality stage - for running quality checks
FROM builder AS code_quality
COPY tests/run-checks.sh /app/tests/run-checks.sh
RUN chmod +x /app/tests/run-checks.sh && /app/tests/run-checks.sh
# Artifacts stage - only contains the built artifacts (for --output)
FROM scratch AS artifacts
# Copy only the built extension zip file to root
COPY --from=builder /app/web-ext-artifacts/*.zip /
# Default stage - full development environment
FROM builder
# Default command shows the built artifact
CMD ["ls", "-lh", "/app/web-ext-artifacts/"]
# Runtime stage - for running extension with Selenium WebDriver
FROM node:lts AS runtime
WORKDIR /app
# Copy built extension from test_builder (includes test code)
COPY --from=test_builder /app /app
# Install Firefox and Xvfb for headless execution
RUN apt-get update && apt-get install -y \
firefox-esr \
xvfb \
wget \
&& rm -rf /var/lib/apt/lists/*
# Install geckodriver for WebDriver protocol
RUN wget -q https://github.com/mozilla/geckodriver/releases/download/v0.34.0/geckodriver-v0.34.0-linux64.tar.gz \
&& tar -xzf geckodriver-v0.34.0-linux64.tar.gz \
&& mv geckodriver /usr/local/bin/ \
&& rm geckodriver-v0.34.0-linux64.tar.gz \
&& chmod +x /usr/local/bin/geckodriver
# Install Node.js test dependencies
RUN npm install
# Set display for Xvfb
ENV DISPLAY=:99
# Start script (not used in verify stage)
CMD ["echo", "Use verify stage for testing"]
# Integration test stage - automated testing with exit code
FROM runtime AS integration_test
# Copy verification scripts
COPY tests/test_verify.mjs /app/tests/test_verify.mjs
COPY tests/test-lib.js /app/tests/test-lib.js
RUN chmod +x /app/tests/test_verify.mjs
# Run verification and exit with proper exit code
CMD ["node", "/app/tests/test_verify.mjs"]

View File

@ -21,9 +21,21 @@ Firefox: https://addons.mozilla.org/en-US/firefox/addon/rentgen/
### Pre-requirements
- OS: Linux x86_64
- Node.js: 16.x version
- Node.js: 25.x version (recommended: use [fnm](https://github.com/Schniz/fnm) for automatic version management)
- npm: 7.x version or higher
**Using fnm (recommended):**
If you're using fnm, it will automatically use the correct Node.js version specified in `.nvmrc`:
```bash
# Install fnm (if not already installed)
curl -fsSL https://fnm.vercel.app/install | bash
# In the project directory, fnm will automatically use Node.js 25
fnm use
```
### Build steps
1. Pull repository or download a zip package
@ -82,9 +94,21 @@ Firefox: https://addons.mozilla.org/pl/firefox/addon/rentgen/
### Wymagania wstępne
- System operacyjny: Linux x86_64
- Node.js: 16.x
- Node.js: 25.x (zalecane: użyj [fnm](https://github.com/Schniz/fnm) do automatycznego zarządzania wersją)
- npm: 7.x lub wyższy
**Używanie fnm (zalecane):**
Jeśli używasz fnm, automatycznie użyje prawidłowej wersji Node.js określonej w `.nvmrc`:
```bash
# Zainstaluj fnm (jeśli nie jest zainstalowane)
curl -fsSL https://fnm.vercel.app/install | bash
# W katalogu projektu fnm automatycznie użyje Node.js 25
fnm use
```
### Proces budowy
1. Pobierz repozytorium przez `git pull https://git.internet-czas-dzialac.pl/icd/rentgen.git` lub pobierz archwium zip
@ -112,3 +136,4 @@ Każdy problem zostanie sprawdzony i przeniesiony na wewnętrzną listę problem
---
# Test pre-commit hook - without Docker check

View File

@ -1,3 +1,35 @@
import { init } from "./memory";
import { init, getMemory } from "./memory";
// Use global browser object directly (available in extension context)
declare const browser: any;
declare const ENABLE_TESTS: boolean;
init();
// Test verification handler for Marionette tests
// Tests real Rentgen functionality: counting third-party domains
if (ENABLE_TESTS) {
browser.runtime.onMessage.addListener((message: any, sender: any, sendResponse: any) => {
if (message.type === 'RENTGEN_TEST_VERIFICATION') {
// Get the origin from message (sent by content script)
const origin = message.origin;
// Access the memory to get clusters for this origin
const memory = getMemory();
const clusters = memory.getClustersForOrigin(origin);
const badgeCount = Object.keys(clusters).length;
// Send back the badge count (number of third-party domains)
const response = {
success: true,
badgeCount: badgeCount,
origin: origin,
clusterIds: Object.keys(clusters),
backgroundTimestamp: Date.now()
};
sendResponse(response);
return true; // Keep channel open for async response
}
});
}

View File

@ -8,7 +8,7 @@ import verbs, { v } from './verbs';
export default function useSurvey(
clusters: RequestCluster[],
{ onComplete }: { onComplete: (sender: { data: RawAnswers }) => void }
): Survey.ReactSurveyModel | null {
): Survey.Model | null {
const [survey, setSurvey] = React.useState<Survey.Model | null>(null);
React.useEffect(() => {
const model = generateSurveyQuestions(clusters);

View File

@ -43,7 +43,6 @@ export function StolenData({
origin={origin}
shorthost={cluster.id}
key={cluster.id + origin}
refreshToken={eventCounts[cluster.id] || 0}
minValueLength={minValueLength}
cookiesOnly={cookiesOnly}
cookiesOrOriginOnly={cookiesOrOriginOnly}

21
compose.yml Normal file
View File

@ -0,0 +1,21 @@
services:
rentgen_build:
build: .
rentgen_check:
build:
context: .
target: code_quality
rentgen_run:
build:
context: .
target: runtime
stdin_open: true
tty: true
rentgen_verify:
build:
context: .
target: integration_test
restart: "no"

View File

@ -1,5 +1,7 @@
import esbuild from 'esbuild';
import scss from 'esbuild-plugin-sass';
import fs from 'fs';
import path from 'path';
const watch = process.argv.includes('--watch') && {
onRebuild(error) {
@ -8,6 +10,8 @@ const watch = process.argv.includes('--watch') && {
},
};
const ENABLE_TESTS = process.env.ENABLE_TESTS === 'true';
// see https://github.com/evanw/esbuild/issues/806#issuecomment-779138268
let skipReactImports = {
name: 'skipReactImports',
@ -41,17 +45,23 @@ let skipReactImports = {
},
};
const entryPoints = [
'components/toolbar/toolbar.tsx',
'components/sidebar/sidebar.tsx',
'components/report-window/report-window.tsx',
'background.ts',
'diag.tsx',
'styles/global.scss',
'styles/fonts.scss',
];
if (ENABLE_TESTS) {
entryPoints.push('tests/inner-test-content-script.js');
}
esbuild
.build({
entryPoints: [
'components/toolbar/toolbar.tsx',
'components/sidebar/sidebar.tsx',
'components/report-window/report-window.tsx',
'background.ts',
'diag.tsx',
'styles/global.scss',
'styles/fonts.scss',
],
entryPoints,
bundle: true,
// minify: true,
outdir: './lib',
@ -60,9 +70,39 @@ esbuild
define: {
PLUGIN_NAME: '"Rentgen"',
PLUGIN_URL: '"https://addons.mozilla.org/pl/firefox/addon/rentgen/"',
ENABLE_TESTS: String(ENABLE_TESTS),
},
external: ['react', 'react-dom', 'survey-react'],
watch,
})
.then(() => console.log('Add-on was built'))
.then(() => {
console.log('Add-on was built');
// Modify manifest.json to include test content script when ENABLE_TESTS=true
if (ENABLE_TESTS) {
const manifestPath = path.join(process.cwd(), 'manifest.json');
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
// Add content_scripts for testing
if (!manifest.content_scripts) {
manifest.content_scripts = [];
}
// Check if test script is already added
const hasTestScript = manifest.content_scripts.some(
cs => cs.js && cs.js.includes('lib/tests/inner-test-content-script.js')
);
if (!hasTestScript) {
manifest.content_scripts.push({
matches: ['<all_urls>'],
js: ['lib/tests/inner-test-content-script.js'],
run_at: 'document_start'
});
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 4));
console.log('Added test content script to manifest.json');
}
}
})
.catch(() => process.exit(1));

View File

@ -13,6 +13,7 @@ function setDomainsCount(counter: number, tabId: number) {
export default class Memory extends SaferEmitter {
origin_to_history = {} as Record<string, Record<string, RequestCluster>>;
async register(request: ExtendedRequest) {
await request.init();
if (!request.isThirdParty()) {
@ -45,7 +46,6 @@ export default class Memory extends SaferEmitter {
constructor() {
super();
browser.webRequest.onBeforeRequest.addListener(
async (request) => {
new ExtendedRequest(request);

126
package-lock.json generated
View File

@ -25,9 +25,13 @@
"addons-linter": "^4.7.0",
"esbuild": "^0.14.14",
"esbuild-plugin-sass": "^1.0.1",
"selenium-webdriver": "^4.38.0",
"typescript": "^4.6.4",
"web-ext": "^6.7.0",
"web-ext-types": "^3.2.1"
},
"engines": {
"node": ">=25"
}
},
"node_modules/@babel/code-frame": {
@ -145,6 +149,13 @@
"regenerator-runtime": "^0.13.4"
}
},
"node_modules/@bazel/runfiles": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-6.5.0.tgz",
"integrity": "sha512-RzahvqTkfpY2jsDxo8YItPX+/iZ6hbiikw1YhE0bA9EKBR5Og8Pa6FHn9PO9M0zaXRVsr0GFQLKbB/0rzy9SzA==",
"dev": true,
"license": "Apache-2.0"
},
"node_modules/@devicefarmer/adbkit": {
"version": "2.11.3",
"resolved": "https://registry.npmjs.org/@devicefarmer/adbkit/-/adbkit-2.11.3.tgz",
@ -4130,9 +4141,9 @@
}
},
"node_modules/jszip": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz",
"integrity": "sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"dev": true,
"dependencies": {
"lie": "~3.3.0",
@ -5838,6 +5849,64 @@
"seek-table": "bin/seek-bzip-table"
}
},
"node_modules/selenium-webdriver": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.38.0.tgz",
"integrity": "sha512-5/UXXFSQmn7FGQkbcpAqvfhzflUdMWtT7QqpEgkFD6Q6rDucxB5EUfzgjmr6JbUj30QodcW3mDXehzoeS/Vy5w==",
"dev": true,
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/SeleniumHQ"
},
{
"type": "opencollective",
"url": "https://opencollective.com/selenium"
}
],
"license": "Apache-2.0",
"dependencies": {
"@bazel/runfiles": "^6.3.1",
"jszip": "^3.10.1",
"tmp": "^0.2.5",
"ws": "^8.18.3"
},
"engines": {
"node": ">= 20.0.0"
}
},
"node_modules/selenium-webdriver/node_modules/tmp": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=14.14"
}
},
"node_modules/selenium-webdriver/node_modules/ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=10.0.0"
},
"peerDependencies": {
"bufferutil": "^4.0.1",
"utf-8-validate": ">=5.0.2"
},
"peerDependenciesMeta": {
"bufferutil": {
"optional": true
},
"utf-8-validate": {
"optional": true
}
}
},
"node_modules/semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@ -7377,9 +7446,9 @@
}
},
"node_modules/yargs-parser": {
"version": "21.0.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
"integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==",
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true,
"engines": {
"node": ">=12"
@ -7506,6 +7575,12 @@
"regenerator-runtime": "^0.13.4"
}
},
"@bazel/runfiles": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@bazel/runfiles/-/runfiles-6.5.0.tgz",
"integrity": "sha512-RzahvqTkfpY2jsDxo8YItPX+/iZ6hbiikw1YhE0bA9EKBR5Og8Pa6FHn9PO9M0zaXRVsr0GFQLKbB/0rzy9SzA==",
"dev": true
},
"@devicefarmer/adbkit": {
"version": "2.11.3",
"resolved": "https://registry.npmjs.org/@devicefarmer/adbkit/-/adbkit-2.11.3.tgz",
@ -10478,9 +10553,9 @@
}
},
"jszip": {
"version": "3.10.0",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.0.tgz",
"integrity": "sha512-LDfVtOLtOxb9RXkYOwPyNBTQDL4eUbqahtoY6x07GiDJHwSYvn8sHHIw8wINImV3MqbMNve2gSuM1DDqEKk09Q==",
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
"integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
"dev": true,
"requires": {
"lie": "~3.3.0",
@ -11811,6 +11886,33 @@
"commander": "^2.8.1"
}
},
"selenium-webdriver": {
"version": "4.38.0",
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.38.0.tgz",
"integrity": "sha512-5/UXXFSQmn7FGQkbcpAqvfhzflUdMWtT7QqpEgkFD6Q6rDucxB5EUfzgjmr6JbUj30QodcW3mDXehzoeS/Vy5w==",
"dev": true,
"requires": {
"@bazel/runfiles": "^6.3.1",
"jszip": "^3.10.1",
"tmp": "^0.2.5",
"ws": "^8.18.3"
},
"dependencies": {
"tmp": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
"dev": true
},
"ws": {
"version": "8.18.3",
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
"integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
"dev": true,
"requires": {}
}
}
},
"semver": {
"version": "7.3.5",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
@ -13049,9 +13151,9 @@
}
},
"yargs-parser": {
"version": "21.0.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz",
"integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==",
"version": "21.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
"dev": true
},
"yauzl": {

View File

@ -19,7 +19,8 @@
"create-package:firefox": "web-ext build --overwrite-dest --artifacts-dir ../web-ext-artifacts",
"create-package:chrome": "cd dist-chrome && 7z a -tzip ../web-ext-artifacts/rentgen-chrome-0.1.10.zip * && cd ..",
"typecheck": "tsc --noEmit",
"lint": "web-ext lint"
"lint": "web-ext lint --ignore-files 'tests/**'",
"test": "ENABLE_TESTS=true npm run build && node tests/test_verify.mjs; git checkout manifest.json"
},
"repository": {
"type": "git",
@ -28,6 +29,9 @@
"homepage": "https://git.internet-czas-dzialac.pl/icd/rentgen",
"author": "Kuba Orlik, Arkadiusz Wieczorek",
"license": "GPL-3.0-or-later",
"engines": {
"node": ">=25"
},
"dependencies": {
"@iabtcf/core": "^1.3.1",
"@types/proposal-relative-indexing-method": "^0.1.0",
@ -57,6 +61,7 @@
"addons-linter": "^4.7.0",
"esbuild": "^0.14.14",
"esbuild-plugin-sass": "^1.0.1",
"selenium-webdriver": "^4.38.0",
"typescript": "^4.6.4",
"web-ext": "^6.7.0",
"web-ext-types": "^3.2.1"

View File

@ -0,0 +1,48 @@
// Test content script - RUNS INSIDE THE BROWSER EXTENSION
// Only loaded during automated testing (ENABLE_TESTS=true)
// This script proves bidirectional communication between content script and background
// Browser API polyfill for Chrome/Chromium compatibility
if (typeof browser === 'undefined') {
var browser = chrome;
}
// Set initial DOM marker to prove content script is injected
(function() {
function setMarker() {
if (document.body) {
document.body.setAttribute('data-rentgen-injected', 'true');
} else {
// Wait for DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
document.body.setAttribute('data-rentgen-injected', 'true');
});
}
}
}
setMarker();
})();
// Listen for test request from test script (test-lib.js)
document.addEventListener('DEBUG_rentgen_test_request', async (event) => {
try {
// Send message to background script to get badge count for this origin
// This tests real Rentgen functionality: counting third-party domains
const response = await browser.runtime.sendMessage({
type: 'RENTGEN_TEST_VERIFICATION',
origin: window.location.origin,
url: window.location.href,
title: document.title,
timestamp: event.detail?.timestamp || Date.now()
});
// Store the badge count in DOM for the test script to read
if (response && response.success) {
document.body.setAttribute('data-rentgen-badge-count', String(response.badgeCount));
}
} catch (error) {
// If there's an error, the test will timeout waiting for badge count
console.error('Test verification error:', error);
}
});

21
tests/pre-commit Executable file
View File

@ -0,0 +1,21 @@
#!/bin/bash
# Pre-commit hook for Rentgen extension
# Builds and runs verification tests before allowing commit
set -e
echo "Running pre-commit checks..."
# Build all stages
echo "Building Docker images..."
docker compose build
# Run code quality checks (typecheck + lint)
echo "Running code quality checks..."
docker compose up --abort-on-container-exit --exit-code-from rentgen_check rentgen_check
# Run integration tests
echo "Running integration tests..."
docker compose up --abort-on-container-exit --exit-code-from rentgen_verify rentgen_verify
echo "✓ All pre-commit checks passed!"

16
tests/run-checks.sh Executable file
View File

@ -0,0 +1,16 @@
#!/bin/bash
# Code quality checks for Rentgen extension
# Runs typecheck and lint
set -e
echo "Running type checking..."
npm run typecheck
echo "Running linter..."
npm run lint
echo "Running tests..."
npm run test
echo "✓ All code quality checks passed!"

55
tests/test-lib.js Normal file
View File

@ -0,0 +1,55 @@
// Test library for Marionette-based extension verification
// This JavaScript code runs in the browser context via Marionette
/**
* Check if test content script is loaded
* The content script is automatically injected by manifest.json when ENABLE_TESTS=true
* @returns {Promise<boolean>} - True if content script is loaded
*/
async function waitForTestContentScript() {
// Wait for content script to set the marker
let attempts = 0;
while (attempts < 50) {
if (document.body && document.body.getAttribute('data-rentgen-injected') === 'true') {
return true;
}
await new Promise(resolve => setTimeout(resolve, 100));
attempts++;
}
return false;
}
/**
* Test that background script returns badge count correctly
* Tests real Rentgen functionality: counting third-party domains
* @returns {Promise<number|null>} - Badge count (number of third-party domains) or null on failure
*/
async function testBadgeCount() {
// Wait for content script to be loaded
const loaded = await waitForTestContentScript();
if (!loaded) {
return -1; // Content script not loaded
}
// Dispatch test request to content script
document.dispatchEvent(new CustomEvent('DEBUG_rentgen_test_request', {
detail: { timestamp: Date.now() }
}));
// Wait for background response with badge count
return new Promise((resolve) => {
let attempts = 0;
const checkInterval = setInterval(() => {
attempts++;
const badgeCount = document.body.getAttribute('data-rentgen-badge-count');
if (badgeCount !== null) {
clearInterval(checkInterval);
resolve(parseInt(badgeCount));
} else if (attempts > 50) {
clearInterval(checkInterval);
resolve(null);
}
}, 100);
});
}

140
tests/test_verify.mjs Executable file
View File

@ -0,0 +1,140 @@
#!/usr/bin/env node
/**
* test_verify.mjs - Extension badge count verification test using Selenium WebDriver
*
* Verifies Rentgen's core functionality: counting third-party domains.
* Tests two scenarios:
* 1. Site without third-party domains (news.ycombinator.com) badge = 0
* 2. Site with third-party domains (pudelek.pl) badge > 0
*/
import { Builder } from 'selenium-webdriver';
import firefox from 'selenium-webdriver/firefox.js';
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Load test library once
const testLibPath = join(__dirname, 'test-lib.js');
const testLib = readFileSync(testLibPath, 'utf-8');
/**
* Test badge count for a specific URL
* @param {WebDriver} driver - Selenium WebDriver instance
* @param {string} url - URL to test
* @param {number} waitMs - How long to wait after page load
* @returns {Promise<number|null>} Badge count or null on failure
*/
async function testUrl(driver, url, waitMs = 5000) {
await driver.get(url);
await driver.sleep(waitMs);
const badgeCount = await driver.executeAsyncScript(`
const testLibCode = arguments[0];
const callback = arguments[1];
eval(testLibCode);
testBadgeCount().then(callback).catch(err => callback(null));
`, testLib);
return badgeCount;
}
/**
* Verify badge count matches expected value
* @param {number|null} actual - Actual badge count
* @param {number|string} expected - Expected value (number or '>0')
* @returns {boolean} Whether assertion passed
*/
function assertBadgeCount(actual, expected) {
if (actual === null) return false;
if (expected === '>0') return actual > 0;
return actual === expected;
}
async function testBadgeCount() {
let driver = null;
try {
const extensionPath = join(__dirname, '..');
console.log(' Launching Firefox with extension...');
const options = new firefox.Options();
if (process.env.HEADLESS !== 'false') {
options.addArguments('-headless');
}
driver = await new Builder()
.forBrowser('firefox')
.setFirefoxOptions(options)
.build();
console.log(' Installing extension...');
await driver.installAddon(extensionPath, true);
// Test cases: [url, expectedBadgeCount, waitMs, name]
const testCases = [
['https://news.ycombinator.com', 0, 5000, 'news.ycombinator.com'],
['https://pudelek.pl', '>0', 10000, 'pudelek.pl']
];
const results = {};
for (const [url, expected, waitMs, name] of testCases) {
console.log(` Testing ${name} (expected: ${expected === '>0' ? '>0' : expected} third-party domains)...`);
const badgeCount = await testUrl(driver, url, waitMs);
results[name] = badgeCount;
console.log(` → Badge count: ${badgeCount}`);
if (!assertBadgeCount(badgeCount, expected)) {
await driver.quit();
return {
success: false,
error: `${name}: expected ${expected}, got ${badgeCount}`
};
}
}
await driver.quit();
return { success: true, results };
} catch (error) {
if (driver) {
await driver.quit();
}
return { success: false, error: error.message };
}
}
async function main() {
console.log('Starting Rentgen badge count verification test...');
console.log('');
const result = await testBadgeCount();
console.log('');
if (!result.success) {
console.log(`FAIL: ${result.error}`);
process.exit(1);
}
console.log('PASS: Badge count test succeeded!');
const resultNames = Object.keys(result.results);
for (const name of resultNames) {
console.log(` - ${name}: ${result.results[name]} third-party domains`);
}
process.exit(0);
}
// Handle signals
process.on('SIGINT', () => process.exit(130));
process.on('SIGTERM', () => process.exit(143));
main().catch(error => {
console.error(`ERROR: ${error.message}`);
process.exit(1);
});