# Testing Guidelines and Platform Status

courieR is transparent about how it is tested. This document describes the test suite, how to run it on your machine (or as an automated agent), and the current verification status across platforms.

The package is fully AI-implemented and human-directed. Both human and agent testers are listed in the status table below.

## Test suite overview

Tests live in `tests/testthat/` and are organised into three layers:

Layer | Files | What it covers
--- | --- | ---
Unit | `test-inventory.R`, `test-manifest.R`, `test-ship.R`, `test-copy-packages.R`, `test-estimate.R`, and ~12 more | Pure R logic — comparison, plan building, log parsing, route filtering, rate calibration
Integration | `test-find_routes.R`, `test-migrate.R`, `test-wrap.R` | Spawns real R subprocesses; needs at least one R installation; guarded with `skip_on_cran()`
End-to-end (E2E) | `test-e2e-ship.R`, `test-app-refresh.R` | Drives the full Shiny dashboard in a headless Chrome browser; requires two R installations, a Chrome binary, and `COURIER_E2E=true`

## Running tests

### 1. Unit + integration tests (standard)

    # From an R session in the package root
    devtools::test()

    # Or from the shell
    R CMD check --no-manual .

All unit tests and CRAN-safe integration tests run without any environment variables. Tests that spawn subprocesses or modify libraries are guarded with `skip_on_cran()` and will run locally but not on CRAN.

### 2. E2E browser tests

The E2E tests drive the full dashboard UI with a real Chrome browser through `shinytest2` and `chromote`. They require:

- Two real R installations (source and target must differ in version or path)
- Chrome or Chromium installed
- `shinytest2` and `chromote` R packages
- The working-tree courieR installed first (`R CMD INSTALL .`)

    # 1. Install working-tree version (required — app subprocess does library(courieR))
    R CMD INSTALL .

    # 2. Set environment variables
    export COURIER_E2E=true
    export COURIER_E2E_SRC=/path/to/source/Rscript   # e.g. /opt/R/4.4.3/bin/Rscript
    export COURIER_E2E_TGT=/path/to/target/Rscript   # e.g. /opt/R/4.5.2/bin/Rscript
    export COURIER_E2E_TGT_LIB=/path/to/target/library  # writable target library dir

    # 3. Run
    Rscript -e "testthat::test_file('tests/testthat/test-e2e-ship.R')"

The E2E test:

1. Opens the dashboard in a 1500×950 headless browser
2. Picks source and target in Bulk Dispatch, clicks Compare, previews the plan
3. Switches to Custom Dispatch, searches for a test package (`broman`), selects offline mode, ships it
4. Verifies independently (via `manifest()` in a clean subprocess) that the package was installed into the target library with a valid layout and is loadable from target R

### 3. Manual Shiny UI checklist

Run `hub()` and work through the checklist below. The E2E test covers the same paths automatically, but manual testing catches visual regressions and UX issues that browser automation misses.

#### Bulk Dispatch

- All R installations appear in the source/target dropdowns
- Selecting source constrains target to same-or-newer R versions only
- Compare populates the summary chips (identical / newer in source / not in target / etc.)
- The comparison table defaults to showing only not in target packages
- Clicking a chip toggles that status on/off in the table
- Package with an unknown source shows the Source cell in red
- Ship opens a confirmation dialog with a time estimate
- During ship: the hero panel counts delivered packages live and shows elapsed time
- During ship: the log pane updates with each package installed
- After ship: the table refreshes automatically and the shipped packages disappear from the “not in target” view

#### Custom Dispatch

- Source/target header shows R version, install path, and library path
- Filter chips are colour-coded: orange = source-side, teal = target-side
- Table defaults to not in target after Compare
- Repo column shows the package source (CRAN, Bioconductor, GitHub, unknown)
- Packages with unknown Repo show in red
- Selecting individual rows and clicking Ship installs only those packages
- During ship: hero panel and log update live
- After ship: table refreshes, shipped rows disappear or change status

#### Error reporter

- Trigger an error (e.g. point source and target at the same installation)
- The error modal appears with the error message
- Clicking Send Report opens a pre-filled GitHub issue in the browser
- Nothing is submitted automatically — you control the final click

#### CLI

    library(courieR)
    # Startup message shows version, hub(), ?ship, and the vignette link

    report_issue("test error message")
    # Should open a pre-filled GitHub issue in the browser

    find_routes()
    # Should find all R installations on the machine

    vignette("get-started", package = "courieR")
    # Should open the HTML user guide

## Platform test status

Last updated: 2026-06-13.

Platform | Tester | Unit tests | Integration | E2E browser | Manual UI
--- | --- | --- | --- | --- | ---
Windows 11 (OneDrive + Defender) | Human — Lennon Li | ✅ | ✅ | ⬜ | ✅
Linux — WSL2 Ubuntu 24.04, R 4.6.0 | Agent — Claude Code (Ming) | ✅ | ✅ | ✅ | ✅ partial
macOS — Mac mini, Apple Silicon, R 4.6.0 | Agent — Claude Code | ✅ | ✅ | ⬜ | ⬜

Legend: ✅ pass · ❌ fail · ⚠️ pass with caveats · ⬜ not yet run

### Windows notes

- Probe timeouts were the trigger for raising `find_routes()` timeout from 3 s to 30 s. Cold-started R on OneDrive-synced drives regularly exceeded 3 s, causing installations to flicker in and out between scans.
- E2E browser tests require Chrome and `chromote`. These have not been run on Windows; `skip_if_not_installed("chromote")` protects the test suite.
- `R CMD check --no-manual` passes (inconsolata.sty is absent; `--no-manual` skips the PDF reference manual).

### Linux notes

- Tested on WSL2 Ubuntu 24.04 with R 4.6.0 (`/usr/bin/R`). Per-version libraries live under `~/R/x86_64-pc-linux-gnu-library/<version>/`.
- E2E test verified the `.copy_plan()` nesting bug fix end-to-end: package installed with a valid layout and loadable from target R.
- `R CMD check --as-cran --no-manual` produces 0 errors, 0 warnings, 0 notes.

### macOS notes

Results — 2026-06-13, Mac mini (Apple Silicon), R 4.6.0

`devtools::test()`: FAIL 0 | WARN 0 | SKIP 16 | PASS 180

`devtools::check(manual = FALSE, vignettes = FALSE)`: 0 errors | 0 warnings | 1 note

The 1 note is the `:::` call to `.build_issue_url()` inside `mod_error_reporter.R`. This is intentional — the function is package-internal and called only from the embedded Shiny app, not from user code.

macOS-specific quirks discovered

1. `/var` → `/private/var` symlink. `withr::local_tempdir()` returns paths under `/var/folders/...`, but `find_routes()` normalises paths via `fs::path_real()`, resolving them to `/private/var/folders/...`. The test `"find_routes detects installs whose probe takes longer than 3s"` was failing because `fake %in% res$rscript_path` compared the unresolved path against the resolved one. Fixed by wrapping `fake` in `fs::path_real()` in the test assertion.

2. CRAN framework `R_HOME` overwrite. The official CRAN macOS framework wrapper scripts (`Resources/bin/R`) hardcode `R_HOME_DIR` to the symlink `/Library/Frameworks/R.framework/Resources`, which always points to the current active version. Running any version-specific wrapper therefore redirects `R_HOME` to whichever version is set as `Current`. Multi-version integration tests must use `rig`-managed binaries or the `exec/R` binary directly; the standard framework wrappers are unreliable for version isolation. The test suite skips multi-install integration tests automatically when only one distinct library is detected.

For future macOS agent runs

Follow the steps below in order.

Environment setup

    # Check what R installations are present
    ls /Library/Frameworks/R.framework/Versions/
    ls ~/Library/Frameworks/R.framework/Versions/   # user-local installs
    ls /opt/homebrew/bin/R* 2>/dev/null             # Homebrew
    ~/.local/share/rig/bin/rig list 2>/dev/null     # rig-managed

    # Verify at least two R installations exist. If only one is present,
    # install a second via rig:
    curl -Ls https://rig.r-lib.org/macos.sh | bash
    rig install 4.4                                  # example second version

Install Chrome if not present (needed for E2E)

    # Check
    which google-chrome || which chromium || ls /Applications/Google\ Chrome.app

Run unit + integration tests

    # In the package root
    devtools::test()

Expected: 0 failures. Tests that require two R installations skip automatically if only one is found.

Run E2E tests

    R CMD INSTALL .

    export COURIER_E2E=true
    # Set to actual paths from `rig list` output
    export COURIER_E2E_SRC=/Library/Frameworks/R.framework/Versions/4.4/Resources/bin/Rscript
    export COURIER_E2E_TGT=/Library/Frameworks/R.framework/Versions/4.5/Resources/bin/Rscript
    export COURIER_E2E_TGT_LIB=~/Library/R/x86_64/4.5/library   # adjust arch/version

    Rscript -e "testthat::test_file('tests/testthat/test-e2e-ship.R')"

Run manual checklist

    library(courieR)
    hub()

Work through the manual checklist above. Pay particular attention to `find_routes()` detecting Homebrew and framework R separately — macOS has the most detection sources of any platform.

Report results

Update this table in a PR or open an issue with the `testing` label. Include:

- macOS version and chip (Intel / Apple Silicon)
- R version(s) tested
- Which tests passed, skipped, or failed
- Any macOS-specific behaviour differences

## Adding tests

- Unit tests go in `tests/testthat/test-<topic>.R`
- Use `skip_on_cran()` for anything that spawns a subprocess or modifies a library
- E2E tests go in `test-e2e-ship.R` (or a new `test-e2e-<feature>.R`) and must check `Sys.getenv("COURIER_E2E") == "true"` before running
- `R CMD check --no-manual` must stay at 0 errors / 0 warnings before merging