mirror of
https://github.com/PeWu/topola-viewer.git
synced 2026-07-17 17:21:48 +00:00
Format markdown files
This commit is contained in:
@@ -3,34 +3,33 @@
|
||||
## 1. Problem Statement
|
||||
|
||||
Topola Viewer is deployed to two environments — GitHub Pages and
|
||||
apps.wikitree.com — and depends on external services outside of our control:
|
||||
the WikiTree API and a third-party CORS proxy (`topolaproxy.bieda.it`). Any of
|
||||
these moving parts can break independently of our code: the WikiTree API can
|
||||
change its response schema or rate-limit requests, the CORS proxy can go down
|
||||
or change its URL scheme, and a deployment can silently introduce a routing or
|
||||
build issue that only manifests in production. Our existing test suite is
|
||||
hermetic — it mocks all network calls — so it verifies code correctness but
|
||||
cannot detect when the live, deployed system stops working end-to-end. We need
|
||||
lightweight smoke tests ("probers") that run against the live deployed URLs to
|
||||
catch real-world breakage, both immediately after each deployment and on a
|
||||
daily schedule.
|
||||
apps.wikitree.com — and depends on external services outside of our control: the
|
||||
WikiTree API and a third-party CORS proxy (`topolaproxy.bieda.it`). Any of these
|
||||
moving parts can break independently of our code: the WikiTree API can change
|
||||
its response schema or rate-limit requests, the CORS proxy can go down or change
|
||||
its URL scheme, and a deployment can silently introduce a routing or build issue
|
||||
that only manifests in production. Our existing test suite is hermetic — it
|
||||
mocks all network calls — so it verifies code correctness but cannot detect when
|
||||
the live, deployed system stops working end-to-end. We need lightweight smoke
|
||||
tests ("probers") that run against the live deployed URLs to catch real-world
|
||||
breakage, both immediately after each deployment and on a daily schedule.
|
||||
|
||||
## 2. The Technical Plan
|
||||
|
||||
The prober system consists of four independent smoke tests, each targeting a
|
||||
specific combination of deployment target and data path. Three of the four
|
||||
tests launch a real browser and navigate to a live deployed URL. The fourth
|
||||
pulls the Docker image published to GHCR, runs it locally, and
|
||||
verifies that the containerized application starts and renders data. All tests
|
||||
verify that the chart renders, the side panel shows the expected person's name,
|
||||
and no error message is displayed.
|
||||
specific combination of deployment target and data path. Three of the four tests
|
||||
launch a real browser and navigate to a live deployed URL. The fourth pulls the
|
||||
Docker image published to GHCR, runs it locally, and verifies that the
|
||||
containerized application starts and renders data. All tests verify that the
|
||||
chart renders, the side panel shows the expected person's name, and no error
|
||||
message is displayed.
|
||||
|
||||
The four probers are:
|
||||
|
||||
1. **WikiTree direct API prober** — Loads a known WikiTree profile
|
||||
(`Skłodowska-2`) from the app deployed on `apps.wikitree.com`. This
|
||||
exercises the direct WikiTree API path (no CORS proxy) and confirms the
|
||||
WikiTree deployment is healthy.
|
||||
(`Skłodowska-2`) from the app deployed on `apps.wikitree.com`. This exercises
|
||||
the direct WikiTree API path (no CORS proxy) and confirms the WikiTree
|
||||
deployment is healthy.
|
||||
|
||||
2. **GitHub Pages GEDCOM prober** — Loads a GEDCOM file from a raw GitHub URL
|
||||
through the app on `pewu.github.io`. Because the app is not on the
|
||||
@@ -44,12 +43,12 @@ The four probers are:
|
||||
default. This confirms the CORS proxy is reachable from the WikiTree
|
||||
deployment.
|
||||
|
||||
4. **Docker container prober** — Pulls the Docker image published to GHCR
|
||||
by `deploy-docker.yml`, runs it locally with the test GEDCOM file mounted
|
||||
via `STATIC_URL`, and verifies that the application renders the chart.
|
||||
This exercises the Docker build path (multi-stage `Dockerfile`, Caddy
|
||||
server configuration, static URL template injection) and confirms the
|
||||
published container image starts and serves data correctly.
|
||||
4. **Docker container prober** — Pulls the Docker image published to GHCR by
|
||||
`deploy-docker.yml`, runs it locally with the test GEDCOM file mounted via
|
||||
`STATIC_URL`, and verifies that the application renders the chart. This
|
||||
exercises the Docker build path (multi-stage `Dockerfile`, Caddy server
|
||||
configuration, static URL template injection) and confirms the published
|
||||
container image starts and serves data correctly.
|
||||
|
||||
Each prober is a standalone GitHub Actions workflow that can be triggered in
|
||||
three ways: automatically after a deploy finishes, on a daily schedule, or
|
||||
@@ -110,21 +109,21 @@ flowchart TD
|
||||
|
||||
Each prober is a small Playwright test spec. Three specs run against live
|
||||
deployed URLs; the Docker prober spec runs against a local Docker container
|
||||
started by the workflow. The specs live in a separate `tests/probers/`
|
||||
directory with their own Playwright configuration
|
||||
(`playwright.prober.config.ts`) so they are completely isolated from the
|
||||
existing hermetic test suite. Note: because the existing `playwright.config.ts`
|
||||
uses `testDir: './tests'` and Playwright searches recursively, the e2e project
|
||||
in the existing config must add `testIgnore: ['*_visual.spec.ts', 'probers/**']`
|
||||
to prevent prober specs from being picked up by the regular CI test run
|
||||
(`npm run test:e2e` or `npm run test:visual`). The prober config does not start
|
||||
a local dev server — each spec navigates to a full absolute URL (or
|
||||
`localhost:8080` for the Docker prober). A successful prober means a user can
|
||||
load the app and see data; a failure means something in the chain is broken
|
||||
and triggers an email notification. Note: GitHub Actions only sends email
|
||||
notifications if the user has explicitly enabled email notifications in their
|
||||
GitHub notification settings (Settings → Notifications → Email). If email
|
||||
notifications are disabled, failures are only visible in the Actions UI.
|
||||
started by the workflow. The specs live in a separate `tests/probers/` directory
|
||||
with their own Playwright configuration (`playwright.prober.config.ts`) so they
|
||||
are completely isolated from the existing hermetic test suite. Note: because the
|
||||
existing `playwright.config.ts` uses `testDir: './tests'` and Playwright
|
||||
searches recursively, the e2e project in the existing config must add
|
||||
`testIgnore: ['*_visual.spec.ts', 'probers/**']` to prevent prober specs from
|
||||
being picked up by the regular CI test run (`npm run test:e2e` or
|
||||
`npm run test:visual`). The prober config does not start a local dev server —
|
||||
each spec navigates to a full absolute URL (or `localhost:8080` for the Docker
|
||||
prober). A successful prober means a user can load the app and see data; a
|
||||
failure means something in the chain is broken and triggers an email
|
||||
notification. Note: GitHub Actions only sends email notifications if the user
|
||||
has explicitly enabled email notifications in their GitHub notification settings
|
||||
(Settings → Notifications → Email). If email notifications are disabled,
|
||||
failures are only visible in the Actions UI.
|
||||
|
||||
## 3. Alternatives Considered & Rejected
|
||||
|
||||
@@ -133,143 +132,142 @@ explicitly rejected. They are documented here to prevent future re-litigation
|
||||
and to serve as guardrails against scope creep.
|
||||
|
||||
### Alternative A: Unit-level API integration tests against the live
|
||||
|
||||
WikiTree API
|
||||
|
||||
* **Considered:** Writing tests that call the raw `wikitree-js` library
|
||||
- **Considered:** Writing tests that call the raw `wikitree-js` library
|
||||
functions directly against the live WikiTree API, verifying response schemas
|
||||
and field presence.
|
||||
* **Why Rejected:** This tests the `wikitree-js` dependency, not our code. Our
|
||||
existing Jest unit tests already cover our transformation logic using
|
||||
mocked API responses. The goal of probers is to verify the full
|
||||
end-to-end chain — browser, deployed app, network, API, proxy — not to
|
||||
re-verify API response shapes. Adding a separate layer of API-level
|
||||
integration tests would duplicate coverage without catching deployment or
|
||||
proxy issues.
|
||||
- **Why Rejected:** This tests the `wikitree-js` dependency, not our code. Our
|
||||
existing Jest unit tests already cover our transformation logic using mocked
|
||||
API responses. The goal of probers is to verify the full end-to-end chain —
|
||||
browser, deployed app, network, API, proxy — not to re-verify API response
|
||||
shapes. Adding a separate layer of API-level integration tests would duplicate
|
||||
coverage without catching deployment or proxy issues.
|
||||
|
||||
### Alternative B: Testing the CORS proxy on apps.wikitree.com via the
|
||||
|
||||
WikiTree data path
|
||||
|
||||
* **Considered:** Forcing the WikiTree API calls through the CORS proxy when
|
||||
the app is deployed on `apps.wikitree.com`, to test the proxy from that
|
||||
- **Considered:** Forcing the WikiTree API calls through the CORS proxy when the
|
||||
app is deployed on `apps.wikitree.com`, to test the proxy from that domain.
|
||||
- **Why Rejected:** The app hardcodes `handleCors` based on hostname — on
|
||||
`apps.wikitree.com`, WikiTree API calls always go direct (no proxy). There is
|
||||
no URL parameter to override this for the WikiTree data source. Forcing the
|
||||
proxy path would require a code change for test-only purposes, which is not
|
||||
justified. Instead, the CORS proxy is tested on `apps.wikitree.com` through
|
||||
the GEDCOM-from-URL path, which uses the proxy by default regardless of
|
||||
domain.
|
||||
* **Why Rejected:** The app hardcodes `handleCors` based on hostname — on
|
||||
`apps.wikitree.com`, WikiTree API calls always go direct (no proxy). There
|
||||
is no URL parameter to override this for the WikiTree data source. Forcing
|
||||
the proxy path would require a code change for test-only purposes, which is
|
||||
not justified. Instead, the CORS proxy is tested on `apps.wikitree.com`
|
||||
through the GEDCOM-from-URL path, which uses the proxy by default
|
||||
regardless of domain.
|
||||
|
||||
### Alternative C: Monolithic prober workflow with multiple jobs
|
||||
|
||||
* **Considered:** A single `prober.yml` workflow containing all four prober
|
||||
- **Considered:** A single `prober.yml` workflow containing all four prober
|
||||
tests as separate jobs within it.
|
||||
* **Why Rejected:** Separate workflow files give finer-grained control in the
|
||||
- **Why Rejected:** Separate workflow files give finer-grained control in the
|
||||
GitHub Actions UI — each prober can be triggered, re-run, or inspected
|
||||
independently. They also allow each prober to declare a targeted `needs`
|
||||
dependency on only the relevant deploy job (e.g., the WikiTree prober
|
||||
depends on `deploy-wikitree-apps`, not `deploy-gh-pages`). A monolithic
|
||||
workflow would couple all probers to the same trigger and make partial
|
||||
failures harder to manage.
|
||||
dependency on only the relevant deploy job (e.g., the WikiTree prober depends
|
||||
on `deploy-wikitree-apps`, not `deploy-gh-pages`). A monolithic workflow would
|
||||
couple all probers to the same trigger and make partial failures harder to
|
||||
manage.
|
||||
|
||||
### Alternative D: Probers that depend on all deploys finishing
|
||||
|
||||
* **Considered:** Making all four probers wait for all of `deploy-gh-pages`,
|
||||
`deploy-wikitree-apps`, and `deploy-docker` to complete before running
|
||||
any of them.
|
||||
* **Why Rejected:** This unnecessarily delays probers whose target has
|
||||
already been deployed. The WikiTree probers only need the WikiTree deploy
|
||||
to finish; the GitHub Pages prober only needs the GitHub Pages deploy.
|
||||
Coupling them to all deploys adds latency without benefit, and means a
|
||||
failure in one deploy would block probers for the other.
|
||||
- **Considered:** Making all four probers wait for all of `deploy-gh-pages`,
|
||||
`deploy-wikitree-apps`, and `deploy-docker` to complete before running any of
|
||||
them.
|
||||
- **Why Rejected:** This unnecessarily delays probers whose target has already
|
||||
been deployed. The WikiTree probers only need the WikiTree deploy to finish;
|
||||
the GitHub Pages prober only needs the GitHub Pages deploy. Coupling them to
|
||||
all deploys adds latency without benefit, and means a failure in one deploy
|
||||
would block probers for the other.
|
||||
|
||||
### Alternative E: Unconditional sleep before every prober run
|
||||
|
||||
* **Considered:** Always waiting 3 minutes at the start of every prober run,
|
||||
- **Considered:** Always waiting 3 minutes at the start of every prober run,
|
||||
regardless of trigger source.
|
||||
* **Why Rejected:** The sleep is only necessary after a deploy, to allow
|
||||
GitHub Pages or WikiTree to propagate the new version. For daily scheduled
|
||||
runs and manual triggers, there is no recent deploy to wait for, so the
|
||||
sleep wastes 3 minutes. Instead, a `wait_for_propagation` input flag is
|
||||
passed as `true` only when the prober is invoked from the deploy workflow.
|
||||
- **Why Rejected:** The sleep is only necessary after a deploy, to allow GitHub
|
||||
Pages or WikiTree to propagate the new version. For daily scheduled runs and
|
||||
manual triggers, there is no recent deploy to wait for, so the sleep wastes 3
|
||||
minutes. Instead, a `wait_for_propagation` input flag is passed as `true` only
|
||||
when the prober is invoked from the deploy workflow.
|
||||
|
||||
### Alternative F: Correctness assertions against specific WikiTree profile
|
||||
|
||||
data
|
||||
|
||||
* **Considered:** Asserting detailed data fields (e.g., specific birth dates,
|
||||
parent IDs, spouse counts) from the `Skłodowska-2` WikiTree profile to
|
||||
verify data correctness.
|
||||
* **Why Rejected:** Probers are smoke tests — their job is to verify "does
|
||||
the pipe work?", not "is the data correct?". Data correctness is already
|
||||
verified by the hermetic test suite with controlled fixtures. Coupling
|
||||
probers to specific WikiTree profile data creates fragility: if anyone
|
||||
edits the WikiTree profile, the prober would break even though the system
|
||||
is healthy. Probers assert only that the expected person's name appears in
|
||||
the chart and side panel, and that no error is displayed.
|
||||
- **Considered:** Asserting detailed data fields (e.g., specific birth dates,
|
||||
parent IDs, spouse counts) from the `Skłodowska-2` WikiTree profile to verify
|
||||
data correctness.
|
||||
- **Why Rejected:** Probers are smoke tests — their job is to verify "does the
|
||||
pipe work?", not "is the data correct?". Data correctness is already verified
|
||||
by the hermetic test suite with controlled fixtures. Coupling probers to
|
||||
specific WikiTree profile data creates fragility: if anyone edits the WikiTree
|
||||
profile, the prober would break even though the system is healthy. Probers
|
||||
assert only that the expected person's name appears in the chart and side
|
||||
panel, and that no error is displayed.
|
||||
|
||||
## 4. Detailed Implementation Plan
|
||||
|
||||
This section enumerates every file that will be created or modified, in
|
||||
the order they should be implemented, along with the rationale for each
|
||||
change. The implementation is divided into five steps.
|
||||
This section enumerates every file that will be created or modified, in the
|
||||
order they should be implemented, along with the rationale for each change. The
|
||||
implementation is divided into five steps.
|
||||
|
||||
### Step 1: Prober Playwright configuration
|
||||
|
||||
**Create:** `playwright.prober.config.ts`
|
||||
|
||||
A separate Playwright configuration file dedicated to prober tests. This
|
||||
file is distinct from the existing `playwright.config.ts` and serves a
|
||||
different purpose: it does not start a local dev server, does not define
|
||||
visual regression projects, and runs only against live deployed URLs.
|
||||
A separate Playwright configuration file dedicated to prober tests. This file is
|
||||
distinct from the existing `playwright.config.ts` and serves a different
|
||||
purpose: it does not start a local dev server, does not define visual regression
|
||||
projects, and runs only against live deployed URLs.
|
||||
|
||||
Rationale for key configuration decisions:
|
||||
|
||||
* **No `webServer`** — The existing config starts a Vite dev/preview server
|
||||
on `localhost:3000`. The prober config does not use Playwright's
|
||||
`webServer` feature. Live-URL probers navigate to full absolute URLs;
|
||||
the Docker prober's workflow starts the container externally (via
|
||||
`docker run`) before the test runs, so Playwright connects to
|
||||
`localhost:8080` without a `webServer` definition.
|
||||
* **`testDir: './tests/probers'`** — Prober specs are isolated in their own
|
||||
directory. Additionally, the existing `playwright.config.ts` e2e project
|
||||
must add `testIgnore: ['*_visual.spec.ts', 'probers/**']` to prevent
|
||||
prober specs from being discovered by the regular CI test run, since
|
||||
Playwright searches `testDir` recursively.
|
||||
* **`fullyParallel: false`** — Tests run sequentially to avoid hammering the
|
||||
live WikiTree API and CORS proxy with concurrent requests, which could
|
||||
trigger rate-limiting.
|
||||
* **`retries: 2`** — The WikiTree API and CORS proxy can have transient
|
||||
- **No `webServer`** — The existing config starts a Vite dev/preview server on
|
||||
`localhost:3000`. The prober config does not use Playwright's `webServer`
|
||||
feature. Live-URL probers navigate to full absolute URLs; the Docker prober's
|
||||
workflow starts the container externally (via `docker run`) before the test
|
||||
runs, so Playwright connects to `localhost:8080` without a `webServer`
|
||||
definition.
|
||||
- **`testDir: './tests/probers'`** — Prober specs are isolated in their own
|
||||
directory. Additionally, the existing `playwright.config.ts` e2e project must
|
||||
add `testIgnore: ['*_visual.spec.ts', 'probers/**']` to prevent prober specs
|
||||
from being discovered by the regular CI test run, since Playwright searches
|
||||
`testDir` recursively.
|
||||
- **`fullyParallel: false`** — Tests run sequentially to avoid hammering the
|
||||
live WikiTree API and CORS proxy with concurrent requests, which could trigger
|
||||
rate-limiting.
|
||||
- **`retries: 2`** — The WikiTree API and CORS proxy can have transient
|
||||
failures. Two retries (same as the existing CI config) provides a buffer
|
||||
against flakiness without masking persistent failures.
|
||||
* **`timeout: 120000`** — The WikiTree API prober makes multiple sequential
|
||||
API calls (ancestors, descendants, relatives) that can take over 30
|
||||
seconds under load. The default 30s timeout is too short for live API
|
||||
probers; 120 seconds provides adequate headroom.
|
||||
* **`reporter: [['html', {open: 'never'}], ['list']]`** — Generates an HTML
|
||||
report for upload as a workflow artifact, plus list output for console
|
||||
logs. Without this, no HTML report is produced and there is nothing to
|
||||
upload.
|
||||
* **`forbidOnly: true`** — Since probers always run in CI, `forbidOnly`
|
||||
should be set to `true` to prevent `test.only` from accidentally blocking
|
||||
all other prober specs. (The existing config uses `forbidOnly:
|
||||
!!process.env.CI`, which achieves the same effect when `CI` is set, but
|
||||
probers should enforce this unconditionally.)
|
||||
* **Single project named `prober` using `devices['Desktop Chrome']`** — No
|
||||
need for separate e2e/visual projects. All prober specs are smoke tests.
|
||||
The project must explicitly use `devices['Desktop Chrome']` to ensure a
|
||||
desktop viewport, because the side panel visibility depends on
|
||||
`window.matchMedia('(max-width: 767px)')` (see the `getShowSidePanel` function in
|
||||
`src/util/url_args.ts`).
|
||||
Without an explicit device, Playwright's default viewport may be too
|
||||
narrow, causing the side panel to be hidden and the `.details` assertion
|
||||
to fail.
|
||||
* **No `expect.toHaveScreenshot`** — Probers do not do visual regression
|
||||
- **`timeout: 120000`** — The WikiTree API prober makes multiple sequential API
|
||||
calls (ancestors, descendants, relatives) that can take over 30 seconds under
|
||||
load. The default 30s timeout is too short for live API probers; 120 seconds
|
||||
provides adequate headroom.
|
||||
- **`reporter: [['html', {open: 'never'}], ['list']]`** — Generates an HTML
|
||||
report for upload as a workflow artifact, plus list output for console logs.
|
||||
Without this, no HTML report is produced and there is nothing to upload.
|
||||
- **`forbidOnly: true`** — Since probers always run in CI, `forbidOnly` should
|
||||
be set to `true` to prevent `test.only` from accidentally blocking all other
|
||||
prober specs. (The existing config uses `forbidOnly: !!process.env.CI`, which
|
||||
achieves the same effect when `CI` is set, but probers should enforce this
|
||||
unconditionally.)
|
||||
- **Single project named `prober` using `devices['Desktop Chrome']`** — No need
|
||||
for separate e2e/visual projects. All prober specs are smoke tests. The
|
||||
project must explicitly use `devices['Desktop Chrome']` to ensure a desktop
|
||||
viewport, because the side panel visibility depends on
|
||||
`window.matchMedia('(max-width: 767px)')` (see the `getShowSidePanel` function
|
||||
in `src/util/url_args.ts`). Without an explicit device, Playwright's default
|
||||
viewport may be too narrow, causing the side panel to be hidden and the
|
||||
`.details` assertion to fail.
|
||||
- **No `expect.toHaveScreenshot`** — Probers do not do visual regression
|
||||
testing; that is handled by the existing visual test project.
|
||||
* **`trace: 'on-first-retry'`, `screenshot: 'only-on-failure'`,
|
||||
`video: 'on-first-retry'`** — For live-URL probers where failures are hard
|
||||
to reproduce, trace files, failure screenshots, and retry videos are
|
||||
essential for debugging.
|
||||
* **`locale: 'en-US'`** — Forces consistent rendering and translation keys,
|
||||
- **`trace: 'on-first-retry'`, `screenshot: 'only-on-failure'`,
|
||||
`video: 'on-first-retry'`** — For live-URL probers where failures are hard to
|
||||
reproduce, trace files, failure screenshots, and retry videos are essential
|
||||
for debugging.
|
||||
- **`locale: 'en-US'`** — Forces consistent rendering and translation keys,
|
||||
matching the existing CI config. Without this, the app renders in the CI
|
||||
runner's default locale, which is non-deterministic.
|
||||
|
||||
@@ -280,65 +278,65 @@ targets a different URL and asserts a different expected name.
|
||||
|
||||
**Create:** `tests/probers/wikitree.spec.ts`
|
||||
|
||||
* **Target URL:**
|
||||
- **Target URL:**
|
||||
`https://apps.wikitree.com/apps/wiech13/topola-viewer/#/view?source=wikitree&indi=Sk%C5%82odowska-2`
|
||||
(URL-encoded `Skłodowska-2` to avoid encoding ambiguity with the non-ASCII
|
||||
character `ł` in source code).
|
||||
* **Expected name:** `Skłodowska` (from the WikiTree profile
|
||||
`Skłodowska-2` — Marie Skłodowska-Curie). The chart displays
|
||||
`LastNameAtBirth`, which is `Skłodowska` for this profile.
|
||||
* **What it exercises:** WikiTree direct API (no CORS proxy), WikiTree
|
||||
- **Expected name:** `Skłodowska` (from the WikiTree profile `Skłodowska-2` —
|
||||
Marie Skłodowska-Curie). The chart displays `LastNameAtBirth`, which is
|
||||
`Skłodowska` for this profile.
|
||||
- **What it exercises:** WikiTree direct API (no CORS proxy), WikiTree
|
||||
deployment.
|
||||
* **Note:** Does not use `standalone=true` in the URL. The app defaults to
|
||||
- **Note:** Does not use `standalone=true` in the URL. The app defaults to
|
||||
standalone mode when not embedded and no static URL is set (see
|
||||
`src/util/url_args.ts`).
|
||||
|
||||
**Create:** `tests/probers/gh-pages-gedcom.spec.ts`
|
||||
|
||||
* **Target URL:**
|
||||
- **Target URL:**
|
||||
`https://pewu.github.io/topola-viewer/#/view?url=https://raw.githubusercontent.com/PeWu/topola-viewer/master/src/datasource/testdata/test.ged&indi=I1`
|
||||
* **Expected name:** `Bonifacy` (individual `@I1@` in `test.ged`, line 16:
|
||||
- **Expected name:** `Bonifacy` (individual `@I1@` in `test.ged`, line 16:
|
||||
`1 NAME Bonifacy /Gibbs/`).
|
||||
* **What it exercises:** GitHub Pages deployment, CORS proxy
|
||||
(`topolaproxy.bieda.it`), GEDCOM-from-URL loading. The app uses the CORS
|
||||
proxy by default for GEDCOM URLs (`handleCors` defaults to `true` — see
|
||||
- **What it exercises:** GitHub Pages deployment, CORS proxy
|
||||
(`topolaproxy.bieda.it`), GEDCOM-from-URL loading. The app uses the CORS proxy
|
||||
by default for GEDCOM URLs (`handleCors` defaults to `true` — see
|
||||
`src/util/url_args.ts:156`).
|
||||
|
||||
**Create:** `tests/probers/wikitree-cors-gedcom.spec.ts`
|
||||
|
||||
* **Target URL:**
|
||||
- **Target URL:**
|
||||
`https://apps.wikitree.com/apps/wiech13/topola-viewer/#/view?url=https://raw.githubusercontent.com/PeWu/topola-viewer/master/src/datasource/testdata/test.ged&indi=I1`
|
||||
* **Expected name:** `Bonifacy` (same as above).
|
||||
* **What it exercises:** WikiTree deployment, CORS proxy from the WikiTree
|
||||
domain. Even on `apps.wikitree.com`, GEDCOM-from-URL uses the CORS proxy
|
||||
by default (the `handleCors` hostname check in `src/datasource/wikitree_api.ts`
|
||||
- **Expected name:** `Bonifacy` (same as above).
|
||||
- **What it exercises:** WikiTree deployment, CORS proxy from the WikiTree
|
||||
domain. Even on `apps.wikitree.com`, GEDCOM-from-URL uses the CORS proxy by
|
||||
default (the `handleCors` hostname check in `src/datasource/wikitree_api.ts`
|
||||
only affects WikiTree API calls, not GEDCOM URL fetches in
|
||||
`src/datasource/load_data.ts`). Note: probers do not block Google Analytics
|
||||
scripts, so live-URL prober runs generate real analytics events on each
|
||||
run. This is intentional — the prober tests the unmodified deployed app,
|
||||
and blocking analytics would not reflect the real user experience.
|
||||
scripts, so live-URL prober runs generate real analytics events on each run.
|
||||
This is intentional — the prober tests the unmodified deployed app, and
|
||||
blocking analytics would not reflect the real user experience.
|
||||
|
||||
**Create:** `tests/probers/docker.spec.ts`
|
||||
|
||||
* **Target URL:** `http://localhost:8080/` (local Docker container).
|
||||
* **Expected name:** `Bonifacy` (same GEDCOM test file, mounted into the
|
||||
- **Target URL:** `http://localhost:8080/` (local Docker container).
|
||||
- **Expected name:** `Bonifacy` (same GEDCOM test file, mounted into the
|
||||
container via `STATIC_URL=test.ged`).
|
||||
* **What it exercises:** Published Docker image from GHCR (multi-stage
|
||||
`Dockerfile` build output, Caddy server configuration, static URL
|
||||
template injection (`{{ env "STATIC_URL" }}` in `index.html`)), and app
|
||||
rendering with a pre-loaded GEDCOM.
|
||||
* **Note:** The workflow pulls the Docker image published to GHCR
|
||||
- **What it exercises:** Published Docker image from GHCR (multi-stage
|
||||
`Dockerfile` build output, Caddy server configuration, static URL template
|
||||
injection (`{{ env "STATIC_URL" }}` in `index.html`)), and app rendering with
|
||||
a pre-loaded GEDCOM.
|
||||
- **Note:** The workflow pulls the Docker image published to GHCR
|
||||
(`ghcr.io/pewu/topola-viewer:latest`), runs it with
|
||||
`docker run -p 8080:8080 -e STATIC_URL=test.ged`, mounts
|
||||
`src/datasource/testdata/test.ged` into the container, and points
|
||||
Playwright at `localhost:8080`. The app loads in non-standalone mode
|
||||
(because `staticUrl` is set) and navigates directly to the chart view
|
||||
(see `app.tsx` routing logic). The Docker image does not include Google
|
||||
credentials (`VITE_GOOGLE_CLIENT_ID`, `VITE_GOOGLE_API_KEY`), so the
|
||||
Google Drive integration is non-functional in the containerized app.
|
||||
This is acceptable for the prober, which only tests chart rendering.
|
||||
Note: the Docker prober tests the image published to GHCR by
|
||||
`deploy-docker.yml`, ensuring the published artifact is functional.
|
||||
`src/datasource/testdata/test.ged` into the container, and points Playwright
|
||||
at `localhost:8080`. The app loads in non-standalone mode (because `staticUrl`
|
||||
is set) and navigates directly to the chart view (see `app.tsx` routing
|
||||
logic). The Docker image does not include Google credentials
|
||||
(`VITE_GOOGLE_CLIENT_ID`, `VITE_GOOGLE_API_KEY`), so the Google Drive
|
||||
integration is non-functional in the containerized app. This is acceptable for
|
||||
the prober, which only tests chart rendering. Note: the Docker prober tests
|
||||
the image published to GHCR by `deploy-docker.yml`, ensuring the published
|
||||
artifact is functional.
|
||||
|
||||
**Shared test structure** (in `tests/probers/helpers.ts`, called by each spec):
|
||||
|
||||
@@ -368,40 +366,39 @@ Playwright's `getByTestId` searches the entire document, so it matches the
|
||||
|
||||
Selectors are derived from the source code:
|
||||
|
||||
* `#content` — main container, visible when chart state is `SHOWING_CHART`
|
||||
(see the `renderMainArea` function in `src/pages/view_page.tsx`).
|
||||
* `#chart` — SVG group inside the chart (see `src/chart.tsx`).
|
||||
* `div.details` — side panel Details tab content (see the `Details` component in
|
||||
- `#content` — main container, visible when chart state is `SHOWING_CHART` (see
|
||||
the `renderMainArea` function in `src/pages/view_page.tsx`).
|
||||
- `#chart` — SVG group inside the chart (see `src/chart.tsx`).
|
||||
- `div.details` — side panel Details tab content (see the `Details` component in
|
||||
`src/sidepanel/details/details.tsx`).
|
||||
* `.ui.error.message` — fatal error replacing the chart (see
|
||||
`src/components/error_display.tsx`, rendered when state is `ERROR`). The `ui` and
|
||||
`message` classes are added by Semantic UI React's `<Message>`
|
||||
component; the `error` class comes from the custom `className="error"`
|
||||
prop in `ErrorMessage`. The resulting DOM element is
|
||||
`<div class="ui negative message error">`, so the selector
|
||||
`.ui.error.message` matches it.
|
||||
* `.ui.errorPopup.message` — dismissable popup error (see
|
||||
- `.ui.error.message` — fatal error replacing the chart (see
|
||||
`src/components/error_display.tsx`, rendered when state is `ERROR`). The `ui`
|
||||
and `message` classes are added by Semantic UI React's `<Message>` component;
|
||||
the `error` class comes from the custom `className="error"` prop in
|
||||
`ErrorMessage`. The resulting DOM element is
|
||||
`<div class="ui negative message error">`, so the selector `.ui.error.message`
|
||||
matches it.
|
||||
- `.ui.errorPopup.message` — dismissable popup error (see
|
||||
`src/components/error_display.tsx`). As above, `ui` and `message` come from
|
||||
Semantic UI React's `<Message>`, and `errorPopup` comes from the
|
||||
custom `className="errorPopup"` prop. Note: `ErrorPopup` uses Semantic
|
||||
UI React's `<Portal>`, which renders its content at `document.body`
|
||||
level, not inside `#content` in the DOM. When the popup is closed
|
||||
(`open={false}`), the Portal renders nothing, so this assertion
|
||||
verifies absence rather than visibility.
|
||||
Semantic UI React's `<Message>`, and `errorPopup` comes from the custom
|
||||
`className="errorPopup"` prop. Note: `ErrorPopup` uses Semantic UI React's
|
||||
`<Portal>`, which renders its content at `document.body` level, not inside
|
||||
`#content` in the DOM. When the popup is closed (`open={false}`), the Portal
|
||||
renders nothing, so this assertion verifies absence rather than visibility.
|
||||
|
||||
The side panel is expanded by default on desktop viewports (the prober project
|
||||
uses `devices['Desktop Chrome']`). The `getShowSidePanel` function in
|
||||
`src/util/url_args.ts` returns `true` on non-mobile screens, so the `div.details`
|
||||
container is visible without any URL parameters.
|
||||
`src/util/url_args.ts` returns `true` on non-mobile screens, so the
|
||||
`div.details` container is visible without any URL parameters.
|
||||
|
||||
All prober selectors use `data-testid` attributes (e.g., `data-testid="content"`,
|
||||
`data-testid="chart"`, `data-testid="details"`, `data-testid="error-message"`,
|
||||
`data-testid="error-popup"`) rather than CSS classes or element IDs. This makes
|
||||
selectors resilient to CSS class refactors and Semantic UI React internal
|
||||
changes. The `data-testid` attributes are added to the source components
|
||||
alongside existing IDs and classes. A shared helper (`tests/probers/helpers.ts`)
|
||||
encapsulates the prober flow and selector logic, eliminating duplication across
|
||||
spec files.
|
||||
All prober selectors use `data-testid` attributes (e.g.,
|
||||
`data-testid="content"`, `data-testid="chart"`, `data-testid="details"`,
|
||||
`data-testid="error-message"`, `data-testid="error-popup"`) rather than CSS
|
||||
classes or element IDs. This makes selectors resilient to CSS class refactors
|
||||
and Semantic UI React internal changes. The `data-testid` attributes are added
|
||||
to the source components alongside existing IDs and classes. A shared helper
|
||||
(`tests/probers/helpers.ts`) encapsulates the prober flow and selector logic,
|
||||
eliminating duplication across spec files.
|
||||
|
||||
### Step 3: Prober GitHub Actions workflows
|
||||
|
||||
@@ -413,16 +410,15 @@ permissions:
|
||||
actions: write
|
||||
```
|
||||
|
||||
All prober workflows should use `actions/checkout@v4` (not v2, which is
|
||||
used by some older deploy workflows).
|
||||
All prober workflows should use `actions/checkout@v4` (not v2, which is used by
|
||||
some older deploy workflows).
|
||||
|
||||
All prober workflows should set `timeout-minutes: 15` on each job to prevent
|
||||
hanging runs from consuming runner minutes (default GitHub Actions timeout is
|
||||
6 hours).
|
||||
hanging runs from consuming runner minutes (default GitHub Actions timeout is 6
|
||||
hours).
|
||||
|
||||
All prober workflows should define a `concurrency` group to prevent
|
||||
overlapping runs (e.g., a deploy-triggered run overlapping with a
|
||||
schedule-triggered run):
|
||||
All prober workflows should define a `concurrency` group to prevent overlapping
|
||||
runs (e.g., a deploy-triggered run overlapping with a schedule-triggered run):
|
||||
|
||||
```yaml
|
||||
concurrency:
|
||||
@@ -430,43 +426,41 @@ concurrency:
|
||||
cancel-in-progress: false
|
||||
```
|
||||
|
||||
`cancel-in-progress: false` ensures a deploy-triggered run is not cancelled
|
||||
by a scheduled run — both complete independently.
|
||||
`cancel-in-progress: false` ensures a deploy-triggered run is not cancelled by a
|
||||
scheduled run — both complete independently.
|
||||
|
||||
Four reusable workflow files, one per prober. The three live-URL probers
|
||||
are identical in structure — only the name and artifact name differ. The
|
||||
Docker prober has a different structure (it builds and runs the container
|
||||
before testing).
|
||||
Four reusable workflow files, one per prober. The three live-URL probers are
|
||||
identical in structure — only the name and artifact name differ. The Docker
|
||||
prober has a different structure (it builds and runs the container before
|
||||
testing).
|
||||
|
||||
**Create:** `.github/workflows/prober-wikitree.yml`
|
||||
|
||||
* **Triggers:** `workflow_call` (with `wait_for_propagation` input),
|
||||
`workflow_dispatch` (with `wait_for_propagation` input), `schedule`
|
||||
(daily at `0 5 * * *` UTC = ~6:00/7:00 CET).
|
||||
* **Depends on (when called from deploy):** `deploy-wikitree-apps.yml`
|
||||
only.
|
||||
* **Artifact name:** `prober-report-wikitree`.
|
||||
- **Triggers:** `workflow_call` (with `wait_for_propagation` input),
|
||||
`workflow_dispatch` (with `wait_for_propagation` input), `schedule` (daily at
|
||||
`0 5 * * *` UTC = ~6:00/7:00 CET).
|
||||
- **Depends on (when called from deploy):** `deploy-wikitree-apps.yml` only.
|
||||
- **Artifact name:** `prober-report-wikitree`.
|
||||
|
||||
**Create:** `.github/workflows/prober-gh-pages.yml`
|
||||
|
||||
* Same structure.
|
||||
* **Depends on (when called from deploy):** `deploy-gh-pages.yml` only.
|
||||
* **Artifact name:** `prober-report-gh-pages`.
|
||||
- Same structure.
|
||||
- **Depends on (when called from deploy):** `deploy-gh-pages.yml` only.
|
||||
- **Artifact name:** `prober-report-gh-pages`.
|
||||
|
||||
**Create:** `.github/workflows/prober-wikitree-cors.yml`
|
||||
|
||||
* Same structure.
|
||||
* **Depends on (when called from deploy):** `deploy-wikitree-apps.yml`
|
||||
only.
|
||||
* **Artifact name:** `prober-report-wikitree-cors`.
|
||||
- Same structure.
|
||||
- **Depends on (when called from deploy):** `deploy-wikitree-apps.yml` only.
|
||||
- **Artifact name:** `prober-report-wikitree-cors`.
|
||||
|
||||
**Create:** `.github/workflows/prober-docker.yml`
|
||||
|
||||
* **Triggers:** `workflow_call`, `workflow_dispatch`, `schedule`
|
||||
(daily at `0 5 * * *` UTC).
|
||||
* **Depends on (when called from deploy):** `deploy-docker.yml` only.
|
||||
* **Artifact name:** `prober-report-docker`.
|
||||
* **No `wait_for_propagation` input** — The Docker container is available
|
||||
- **Triggers:** `workflow_call`, `workflow_dispatch`, `schedule` (daily at
|
||||
`0 5 * * *` UTC).
|
||||
- **Depends on (when called from deploy):** `deploy-docker.yml` only.
|
||||
- **Artifact name:** `prober-report-docker`.
|
||||
- **No `wait_for_propagation` input** — The Docker container is available
|
||||
immediately after startup; no propagation delay is needed.
|
||||
|
||||
**Shared workflow structure** (live-URL probers):
|
||||
@@ -497,7 +491,7 @@ before testing).
|
||||
|
||||
**Docker prober workflow structure** (different from live-URL probers):
|
||||
|
||||
```
|
||||
````
|
||||
1. Checkout repository (actions/checkout@v4).
|
||||
2. Pull Docker image: docker pull ghcr.io/pewu/topola-viewer:latest
|
||||
(Pull the image published by deploy-docker.yml. This tests the actual
|
||||
@@ -527,27 +521,26 @@ before testing).
|
||||
sleep 1
|
||||
done
|
||||
curl -sf -o /dev/null http://localhost:8080/
|
||||
```
|
||||
The final `curl` ensures the workflow fails with a clear error if
|
||||
the container never became ready. This prevents a race condition
|
||||
where the test runs before Caddy is ready to serve requests. This
|
||||
step runs after Node/Playwright setup so the container doesn't sit
|
||||
idle during dependency installation.
|
||||
The Docker spec also includes a guard that checks if localhost:8080
|
||||
is reachable before running the prober. If the container is not
|
||||
running (e.g., when running probers locally without Docker), the
|
||||
test is skipped with a helpful message instead of failing with a
|
||||
confusing ECONNREFUSED error.
|
||||
10. Run: npx playwright test --config=playwright.prober.config.ts docker.spec.ts
|
||||
11. Upload Playwright HTML report as artifact (if: always()). Set
|
||||
`retention-days: 30` (same as live-URL probers).
|
||||
12. Stop and remove container (if: always()): docker stop
|
||||
topola-prober-${{ github.run_id }} 2>/dev/null || true; docker rm
|
||||
topola-prober-${{ github.run_id }} 2>/dev/null || true
|
||||
(The if: always() ensures cleanup runs even on failure. The
|
||||
2>/dev/null and trailing true prevent errors if the container was
|
||||
never started, e.g., pull failed at step 2.)
|
||||
```
|
||||
````
|
||||
|
||||
The final `curl` ensures the workflow fails with a clear error if the container
|
||||
never became ready. This prevents a race condition where the test runs before
|
||||
Caddy is ready to serve requests. This step runs after Node/Playwright setup so
|
||||
the container doesn't sit idle during dependency installation. The Docker spec
|
||||
also includes a guard that checks if localhost:8080 is reachable before running
|
||||
the prober. If the container is not running (e.g., when running probers locally
|
||||
without Docker), the test is skipped with a helpful message instead of failing
|
||||
with a confusing ECONNREFUSED error. 10. Run: npx playwright test
|
||||
--config=playwright.prober.config.ts docker.spec.ts 11. Upload Playwright HTML
|
||||
report as artifact (if: always()). Set `retention-days: 30` (same as live-URL
|
||||
probers). 12. Stop and remove container (if: always()): docker stop
|
||||
topola-prober-${{ github.run_id }} 2>/dev/null || true; docker rm
|
||||
topola-prober-${{ github.run_id }}
|
||||
2>/dev/null || true (The if: always() ensures cleanup runs even on failure. The
|
||||
2>/dev/null and trailing true prevent errors if the container was never started,
|
||||
e.g., pull failed at step 2.)
|
||||
|
||||
````
|
||||
|
||||
**`wait_for_propagation` input flag** (live-URL probers only):
|
||||
|
||||
@@ -580,7 +573,7 @@ jobs:
|
||||
deploy-docker:
|
||||
uses: ./.github/workflows/deploy-docker.yml
|
||||
secrets: inherit
|
||||
```
|
||||
````
|
||||
|
||||
After changes:
|
||||
|
||||
@@ -622,34 +615,34 @@ jobs:
|
||||
|
||||
Rationale for dependency mapping:
|
||||
|
||||
* `prober-wikitree` needs `deploy-wikitree-apps` — it tests the WikiTree
|
||||
- `prober-wikitree` needs `deploy-wikitree-apps` — it tests the WikiTree
|
||||
deployment.
|
||||
* `prober-gh-pages` needs `deploy-gh-pages` — it tests the GitHub Pages
|
||||
- `prober-gh-pages` needs `deploy-gh-pages` — it tests the GitHub Pages
|
||||
deployment.
|
||||
* `prober-wikitree-cors` needs `deploy-wikitree-apps` — it tests the
|
||||
WikiTree deployment (with CORS proxy).
|
||||
* `prober-docker` needs `deploy-docker` — it tests the Docker image
|
||||
published to GHCR by `deploy-docker.yml` (Dockerfile, Caddy config, app
|
||||
startup). It does not pass `wait_for_propagation` because the container is
|
||||
available immediately after `docker run`.
|
||||
* If a prober fails, the `deploy-everywhere` workflow is marked as failed
|
||||
(red X), triggering an email notification (if GitHub email notifications
|
||||
are enabled — see note in Section 2).
|
||||
* Note: Individual deploy workflows (`deploy-gh-pages.yml`,
|
||||
- `prober-wikitree-cors` needs `deploy-wikitree-apps` — it tests the WikiTree
|
||||
deployment (with CORS proxy).
|
||||
- `prober-docker` needs `deploy-docker` — it tests the Docker image published to
|
||||
GHCR by `deploy-docker.yml` (Dockerfile, Caddy config, app startup). It does
|
||||
not pass `wait_for_propagation` because the container is available immediately
|
||||
after `docker run`.
|
||||
- If a prober fails, the `deploy-everywhere` workflow is marked as failed (red
|
||||
X), triggering an email notification (if GitHub email notifications are
|
||||
enabled — see note in Section 2).
|
||||
- Note: Individual deploy workflows (`deploy-gh-pages.yml`,
|
||||
`deploy-wikitree-apps.yml`, `deploy-docker.yml`) also support
|
||||
`workflow_dispatch`. If a deploy is triggered directly (instead of
|
||||
through `deploy-everywhere.yml`), no probers run because probers are only
|
||||
called from `deploy-everywhere.yml`. To ensure probers always run after a
|
||||
deploy, always trigger deploys through `deploy-everywhere.yml`.
|
||||
`workflow_dispatch`. If a deploy is triggered directly (instead of through
|
||||
`deploy-everywhere.yml`), no probers run because probers are only called from
|
||||
`deploy-everywhere.yml`. To ensure probers always run after a deploy, always
|
||||
trigger deploys through `deploy-everywhere.yml`.
|
||||
|
||||
### Step 5: Update supporting files
|
||||
|
||||
**Modify:** `playwright.config.ts`
|
||||
|
||||
Add `testIgnore: ['*_visual.spec.ts', 'probers/**']` to the e2e project to
|
||||
prevent prober specs in `tests/probers/` from being discovered by the regular
|
||||
CI e2e test run. Without this, `npm run test:e2e` would try to execute
|
||||
prober specs against the local dev server, causing failures.
|
||||
prevent prober specs in `tests/probers/` from being discovered by the regular CI
|
||||
e2e test run. Without this, `npm run test:e2e` would try to execute prober specs
|
||||
against the local dev server, causing failures.
|
||||
|
||||
**Modify:** `package.json`
|
||||
|
||||
@@ -662,8 +655,7 @@ Add a `test:probers` script for running probers locally during development:
|
||||
**Modify:** `tests/tsconfig.json`
|
||||
|
||||
Add `probers/` to the `include` array so prober specs are type-checked by
|
||||
`tsc -p tests/tsconfig.json --noEmit` (which runs in CI via
|
||||
`node.js.yml`).
|
||||
`tsc -p tests/tsconfig.json --noEmit` (which runs in CI via `node.js.yml`).
|
||||
|
||||
Current state:
|
||||
|
||||
@@ -675,35 +667,34 @@ Current state:
|
||||
```
|
||||
|
||||
The existing `./**/*.ts` glob already includes `tests/probers/` — no
|
||||
modification needed. The `./**/*.d.ts` glob covers type declaration files
|
||||
and does not affect prober spec discovery.
|
||||
modification needed. The `./**/*.d.ts` glob covers type declaration files and
|
||||
does not affect prober spec discovery.
|
||||
|
||||
**Modify:** `.github/workflows/README.md`
|
||||
|
||||
Add entries for the four new prober workflows to the file registry, e.g.:
|
||||
|
||||
```markdown
|
||||
- [prober-wikitree.yml](prober-wikitree.yml): Reusable prober that
|
||||
smoke-tests the WikiTree direct API path on the live WikiTree deployment.
|
||||
Runs daily and after deploy.
|
||||
- [prober-gh-pages.yml](prober-gh-pages.yml): Reusable prober that
|
||||
smoke-tests the GitHub Pages deployment with GEDCOM-from-URL through the
|
||||
CORS proxy. Runs daily and after deploy.
|
||||
- [prober-wikitree-cors.yml](prober-wikitree-cors.yml): Reusable prober
|
||||
that smoke-tests the CORS proxy from the WikiTree deployment with
|
||||
GEDCOM-from-URL. Runs daily and after deploy.
|
||||
- [prober-docker.yml](prober-docker.yml): Reusable prober that
|
||||
smoke-tests the published Docker image from GHCR (Dockerfile, Caddy
|
||||
config, app startup) by pulling and running it locally. Runs daily and
|
||||
- [prober-wikitree.yml](prober-wikitree.yml): Reusable prober that smoke-tests
|
||||
the WikiTree direct API path on the live WikiTree deployment. Runs daily and
|
||||
after deploy.
|
||||
- [prober-gh-pages.yml](prober-gh-pages.yml): Reusable prober that smoke-tests
|
||||
the GitHub Pages deployment with GEDCOM-from-URL through the CORS proxy. Runs
|
||||
daily and after deploy.
|
||||
- [prober-wikitree-cors.yml](prober-wikitree-cors.yml): Reusable prober that
|
||||
smoke-tests the CORS proxy from the WikiTree deployment with GEDCOM-from-URL.
|
||||
Runs daily and after deploy.
|
||||
- [prober-docker.yml](prober-docker.yml): Reusable prober that smoke-tests the
|
||||
published Docker image from GHCR (Dockerfile, Caddy config, app startup) by
|
||||
pulling and running it locally. Runs daily and after deploy.
|
||||
```
|
||||
|
||||
**Create:** `tests/probers/README.md`
|
||||
|
||||
Document the prober test directory, explaining that these are live smoke
|
||||
tests (not hermetic), how to run them locally (`npm run test:probers`), and
|
||||
that they require network access to external services (WikiTree API, CORS
|
||||
proxy, GitHub raw URLs).
|
||||
Document the prober test directory, explaining that these are live smoke tests
|
||||
(not hermetic), how to run them locally (`npm run test:probers`), and that they
|
||||
require network access to external services (WikiTree API, CORS proxy, GitHub
|
||||
raw URLs).
|
||||
|
||||
**Modify:** `PROJECT_STRUCTURE.md`
|
||||
|
||||
@@ -715,60 +706,59 @@ Add entries for the new `tests/probers/` directory and
|
||||
Add an entry for this design document to the registry:
|
||||
|
||||
```markdown
|
||||
* **[PROBERS_DESIGN.md](PROBERS_DESIGN.md)**: Live prober smoke tests
|
||||
against deployed GitHub Pages, WikiTree URLs, and local Docker container,
|
||||
covering WikiTree API, CORS proxy, GEDCOM-from-URL, and Docker build paths.
|
||||
- **[PROBERS_DESIGN.md](PROBERS_DESIGN.md)**: Live prober smoke tests against
|
||||
deployed GitHub Pages, WikiTree URLs, and local Docker container, covering
|
||||
WikiTree API, CORS proxy, GEDCOM-from-URL, and Docker build paths.
|
||||
```
|
||||
|
||||
### Summary of all files
|
||||
|
||||
| File | Action | Purpose |
|
||||
|---|---|---|
|
||||
| `playwright.prober.config.ts` | Create | Separate Playwright config for probers (no local server, live URLs) |
|
||||
| `playwright.config.ts` | Modify | Add `testIgnore` for `probers/**` to e2e project |
|
||||
| `package.json` | Modify | Add `test:probers` script |
|
||||
| `tests/probers/helpers.ts` | Create | Shared prober flow, diagnostics capture, and selector logic |
|
||||
| `tests/probers/wikitree.spec.ts` | Create | WikiTree direct API smoke test |
|
||||
| `tests/probers/gh-pages-gedcom.spec.ts` | Create | GitHub Pages + CORS proxy smoke test |
|
||||
| `tests/probers/wikitree-cors-gedcom.spec.ts` | Create | WikiTree + CORS proxy smoke test |
|
||||
| `tests/probers/docker.spec.ts` | Create | Docker container smoke test (with reachability guard) |
|
||||
| `src/pages/view_page.tsx` | Modify | Add `data-testid="content"` |
|
||||
| `src/chart.tsx` | Modify | Add `data-testid="chart"` |
|
||||
| `src/sidepanel/details/details.tsx` | Modify | Add `data-testid="details"` |
|
||||
| `src/components/error_display.tsx` | Modify | Add `data-testid` for error message and popup |
|
||||
| `.github/workflows/prober-wikitree.yml` | Create | Reusable workflow: WikiTree prober |
|
||||
| `.github/workflows/prober-gh-pages.yml` | Create | Reusable workflow: GH Pages prober |
|
||||
| `.github/workflows/prober-wikitree-cors.yml` | Create | Reusable workflow: WikiTree CORS prober |
|
||||
| `.github/workflows/prober-docker.yml` | Create | Reusable workflow: Docker prober (pulls GHCR image) |
|
||||
| `.github/workflows/deploy-everywhere.yml` | Modify | Add prober jobs with targeted deploy dependencies |
|
||||
| `tests/tsconfig.json` | Modify | Ensure prober specs are type-checked |
|
||||
| `tests/probers/README.md` | Create | Document prober directory and usage |
|
||||
| `.github/workflows/README.md` | Modify | Document new prober workflows |
|
||||
| `docs/README.md` | Modify | Add prober design doc to registry |
|
||||
| `PROJECT_STRUCTURE.md` | Modify | Add prober directory and config file |
|
||||
|
||||
| File | Action | Purpose |
|
||||
| -------------------------------------------- | ------ | ------------------------------------------------------------------- |
|
||||
| `playwright.prober.config.ts` | Create | Separate Playwright config for probers (no local server, live URLs) |
|
||||
| `playwright.config.ts` | Modify | Add `testIgnore` for `probers/**` to e2e project |
|
||||
| `package.json` | Modify | Add `test:probers` script |
|
||||
| `tests/probers/helpers.ts` | Create | Shared prober flow, diagnostics capture, and selector logic |
|
||||
| `tests/probers/wikitree.spec.ts` | Create | WikiTree direct API smoke test |
|
||||
| `tests/probers/gh-pages-gedcom.spec.ts` | Create | GitHub Pages + CORS proxy smoke test |
|
||||
| `tests/probers/wikitree-cors-gedcom.spec.ts` | Create | WikiTree + CORS proxy smoke test |
|
||||
| `tests/probers/docker.spec.ts` | Create | Docker container smoke test (with reachability guard) |
|
||||
| `src/pages/view_page.tsx` | Modify | Add `data-testid="content"` |
|
||||
| `src/chart.tsx` | Modify | Add `data-testid="chart"` |
|
||||
| `src/sidepanel/details/details.tsx` | Modify | Add `data-testid="details"` |
|
||||
| `src/components/error_display.tsx` | Modify | Add `data-testid` for error message and popup |
|
||||
| `.github/workflows/prober-wikitree.yml` | Create | Reusable workflow: WikiTree prober |
|
||||
| `.github/workflows/prober-gh-pages.yml` | Create | Reusable workflow: GH Pages prober |
|
||||
| `.github/workflows/prober-wikitree-cors.yml` | Create | Reusable workflow: WikiTree CORS prober |
|
||||
| `.github/workflows/prober-docker.yml` | Create | Reusable workflow: Docker prober (pulls GHCR image) |
|
||||
| `.github/workflows/deploy-everywhere.yml` | Modify | Add prober jobs with targeted deploy dependencies |
|
||||
| `tests/tsconfig.json` | Modify | Ensure prober specs are type-checked |
|
||||
| `tests/probers/README.md` | Create | Document prober directory and usage |
|
||||
| `.github/workflows/README.md` | Modify | Document new prober workflows |
|
||||
| `docs/README.md` | Modify | Add prober design doc to registry |
|
||||
| `PROJECT_STRUCTURE.md` | Modify | Add prober directory and config file |
|
||||
|
||||
## 5. Future Considerations
|
||||
|
||||
### WikiTree Login Flow Prober
|
||||
|
||||
The current WikiTree prober tests the unauthenticated API path (loading a
|
||||
public profile without an authcode). A future prober could test the
|
||||
authenticated login flow — logging in with an authcode and verifying that
|
||||
private profiles are accessible. This would require obtaining a dedicated
|
||||
test account on wikitree.com and storing the authcode as a GitHub Actions
|
||||
secret. This is deferred because it adds complexity (secret management,
|
||||
authcode expiry, test account maintenance) and the unauthenticated path
|
||||
already covers the most common deployment scenario.
|
||||
The current WikiTree prober tests the unauthenticated API path (loading a public
|
||||
profile without an authcode). A future prober could test the authenticated login
|
||||
flow — logging in with an authcode and verifying that private profiles are
|
||||
accessible. This would require obtaining a dedicated test account on
|
||||
wikitree.com and storing the authcode as a GitHub Actions secret. This is
|
||||
deferred because it adds complexity (secret management, authcode expiry, test
|
||||
account maintenance) and the unauthenticated path already covers the most common
|
||||
deployment scenario.
|
||||
|
||||
### Google Drive Integration Prober
|
||||
|
||||
A prober for the Google Drive integration (loading a GEDCOM file from
|
||||
Google Drive) is not included. Google's OAuth flow is designed for human
|
||||
interaction and includes bot detection (CAPTCHA, device verification) that
|
||||
would likely prevent automated login. Additionally, the Google Drive
|
||||
integration requires `VITE_GOOGLE_CLIENT_ID` and `VITE_GOOGLE_API_KEY`
|
||||
secrets, which are not available in the prober environment. A possible
|
||||
workaround would be to use a pre-authorized service account or a long-lived
|
||||
refresh token stored as a secret, but this is complex and fragile. This is
|
||||
deferred until a reliable automation approach is identified.
|
||||
A prober for the Google Drive integration (loading a GEDCOM file from Google
|
||||
Drive) is not included. Google's OAuth flow is designed for human interaction
|
||||
and includes bot detection (CAPTCHA, device verification) that would likely
|
||||
prevent automated login. Additionally, the Google Drive integration requires
|
||||
`VITE_GOOGLE_CLIENT_ID` and `VITE_GOOGLE_API_KEY` secrets, which are not
|
||||
available in the prober environment. A possible workaround would be to use a
|
||||
pre-authorized service account or a long-lived refresh token stored as a secret,
|
||||
but this is complex and fragile. This is deferred until a reliable automation
|
||||
approach is identified.
|
||||
|
||||
Reference in New Issue
Block a user