Address the review follow-ups from #2188, #2189 and #2190 (#2193)

* Read the collapsed-threads key through getJsonItem

`getFromLocalStorage` parsed the stored string directly, so anything
malformed under `__remarkCollapsed` threw out of `restoreCollapsedThreads`.
That call sits in `remark.tsx` ahead of the `render`, so the throw took the
whole widget with it: the reader was left on the preloader, over a view
preference.

`getJsonItem` in `common/local-storage.ts` already wraps a parse of a
localStorage key and returns null on failure, and null is a shape the check
below already reads as empty. The rest of that function is total against
whatever the browser holds, and the bare parse was the one way in.

* Stop retrying a failed e2e test in CI

The suite went in with one gotestsum rerun. It has no failures on record to
justify that: 31 CI runs since it landed, all green, and no rerun report has
ever been produced. A retry is what turns an intermittent regression into a
green build, and while the suite is this young its own failures are the
evidence worth keeping.

`E2E_RUN_ID` stays. It stamps the threads a run works on with the CI run id, so
a thread url in a trace or a log names the run it came from. It carries no data
across: the stack is disposable, and a local run under the same id gets those
urls on an empty database.

* Stop two chooseUnusedPort comments claiming collisions cannot happen

All four copies listen on :0, read the assigned port, close the listener
and bind later, so nothing holds the number across that gap and another
binary can take it. The copies in app/cmd and app/rest/api call a collision
very unlikely, which is accurate; the ones in app and the example module
said binaries never land on the same number, which is not, and a comment
ruling out a port collision is what would send the next person chasing one
somewhere else. All four now read the same.

Closing the window rather than describing it means the server binding :0
itself and reporting the address it got, which is a larger change.
This commit is contained in:
Dmitry Verkhoturov
2026-08-22 02:23:56 -05:00
committed by GitHub
parent 0b651dddd4
commit 49bf83b09c
7 changed files with 39 additions and 42 deletions
+2 -2
View File
@@ -43,9 +43,9 @@ The build tag keeps these out of `go test ./...`; nothing runs without `-tags=e2
## When something fails
A failed test writes a Playwright trace to `e2e/traces/`, which CI uploads as an artifact whether or not the job went green. Open one with `npx playwright show-trace e2e/traces/<name>.zip`.
A failed test writes a Playwright trace to `e2e/traces/`, which CI uploads as an artifact. Nothing else writes one, so a run carrying the artifact is a run with a failure to look at. Open one with `npx playwright show-trace e2e/traces/<name>.zip`.
CI runs the suite through `gotestsum` and gives a failing test one rerun, so a test that fails and then passes leaves the job green. That is the case worth looking at: it is named in `rerun-report.txt`, uploaded beside the traces. Only attempts that failed leave a trace, and they do not overwrite each other, so a flake leaves exactly one to open. `make e2e` locally does not rerun anything, so a test red on a laptop and green in CI is a flake with a report to read rather than a disagreement.
CI does not retry a failing test. The suite is young enough that a failure is evidence about the suite itself, and a retry is what would hide an intermittent regression. `E2E_RUN_ID` stamps the threads a run uses with the CI run id, so a thread url in a trace names the run it came from. It does not carry the data across: the stack is disposable, and a local run against the same id gets those urls on an empty database.
Beyond that: `docker compose -f compose-e2e-test.yml logs` for the server side, and mailpit's web UI on <http://127.0.0.1:8025> for anything email.
+4 -6
View File
@@ -73,9 +73,8 @@ var (
extraBrowsersMu sync.Mutex
// distinguishes this run's threads from those a previous run left in the database.
// a rerun is a fresh process, so without E2E_RUN_ID it would get its own threads and a
// failure caused by ordering or by state an earlier test left behind would pass on the
// second attempt whatever the code did
// E2E_RUN_ID pins it, so the urls a CI run works on name that run rather than the moment
// the process started
runID = firstNonEmpty(os.Getenv("E2E_RUN_ID"), fmt.Sprintf("%d", time.Now().UnixNano()))
authGate sync.Mutex
@@ -271,9 +270,8 @@ func newPageOn(t *testing.T, b playwright.Browser) playwright.Page {
}
// say so rather than swallowing it: this runs only on a test that already failed,
// and a silently missing trace is what the reader goes looking for
// the pid distinguishes attempts: a rerun is a fresh process that shares runID
// with the attempt it is retrying and restarts its own counter, so without it the
// rerun would overwrite the trace of the attempt that actually failed
// the pid keeps two processes sharing a run id, and so a trace directory, from
// overwriting each other: the counter restarts with every process
name := strings.ReplaceAll(t.Name(), "/", "-")
path := filepath.Join(traceDir, fmt.Sprintf("%s-%d-%d.zip", name, os.Getpid(), seq))
if serr := ctx.Tracing().Stop(path); serr != nil {