fix: stop readReports rewriting the whole report list on every read (#994) - #998
fix: stop readReports rewriting the whole report list on every read (#994)#998MOHITKOURAV01 wants to merge 4 commits into
Conversation
…ditya8369#994) The migration in readReports wrote its result back to localStorage unconditionally, whether or not anything had actually changed. Its own docstring says the migration "runs once ... and the result is written back"; it ran on every call and wrote on every one. readReports is called from the useState initialiser as well as on mount, so a plain page load serialised the entire report list — base64 image data URIs included, capped at 500 KB each — and wrote it straight back over the top of itself, synchronously, on the path to first paint. This is a store the component already treats as scarce: there is a 5 MB warning and a pruning path fifty lines further down. It was a correctness problem too. Two tabs open on the hub meant each one rewriting storage from whatever snapshot it read, so a report submitted in one tab could be erased by the other merely rendering. The write now happens only when the entity decode or a status rename actually changed something. The regression test for this was already in the tree and failing, but for a different reason than it looked: makeReport() defaults to status 'Pending', which is itself a legacy value the migration renames, so the "nothing needs migrating" fixture did need migrating. Gave it a current status and added a case that reads three times over and asserts silence. Second half: every control in the report form now has an accessible name. Three of the four selects had no label, no aria-label and no aria-labelledby, so a screen reader announced three unnamed combo boxes in a row and the tests had to reach past Testing Library to document.querySelectorAll('select')[0] and [1] to tell them apart. The form severity control is named "Incident severity" rather than "Severity", because the filters block already has a select named "Severity" and two identical names is barely better than none. Three test-side repairs fell out of this, all of them cases that were asserting against markup or copy that had moved on: - the report form became collapsible behind a "Report Pollution" toggle and the round-trip tests were never updated, so they queried a form that had not rendered - category is a required field now, so a submit without one never reaches onSubmit - the GPS tests looked for "Use GPS for Location" and "GPS Location attached", the inline JSX defaults; translation.json overrides both with "Use Current Location" and "Location attached", which is what a reader actually sees - vi.stubGlobal('navigator', { ...globalThis.navigator, geolocation }) copies nothing, because navigator's properties live on its prototype. It handed the component an empty navigator. Replaced with a defineProperty stub that restores the original afterwards.
|
@MOHITKOURAV01 is attempting to deploy a commit to the Aditya Mahajan's projects Team on Vercel. A member of the Team first needs to authorize it. |
Thank You for Your Contribution! 🎉Hi @MOHITKOURAV01, Thank you for opening this Pull Request and contributing to our project. We truly appreciate your efforts.
The maintainer @Aditya8369 will review your PR shortly! Happy Contributing! 🚀 |
|
Note on the red checks. CI is red on
|
|
@MOHITKOURAV01 conflicts |
1 similar comment
|
@MOHITKOURAV01 conflicts |
main restructured CommunityHub.jsx in 5dbb1a0 (community report moderation tools) — 236 insertions, including a re-indent of the report form — so both halves of this branch had to be re-applied onto the new file rather than merged line by line. readReports: the conditional write-back is back, unchanged. main's copy still serialises the whole report list and writes it over itself on every call, including the one in the useState initialiser. The form: all seven controls carry their id and aria-label again, at main's indentation. The moderation controls main added are untouched. The three translation files merged cleanly; the seven communityHub.label* keys are present in en, hi and bn.
|
Merged
localStorage.setItem(STORAGE_KEY, JSON.stringify(migrated));unconditionally, on every call, including the one in the The form — all seven controls carry their The three translation files merged cleanly on their own; I checked the seven versus 17 failed / 1584 passed on |
main restructured CommunityHub.jsx in 5dbb1a0 (community report moderation tools) — 236 insertions, including a re-indent of the report form — so neither half of this branch lined up against it. Both were re-applied onto main's file rather than merged line by line. readReports: the conditional write-back is back, unchanged. The form: all seven controls carry their id and aria-label again, at main's indentation. The moderation controls main added are untouched. The diff against main is now exactly the 60 lines this branch is for.
…rite-amplification # Conflicts: # src/components/CommunityHub.escaping.test.jsx # src/components/CommunityHub.jsx # src/components/CommunityHub.test.jsx
Fixes #994.
The write that happened whether or not anything changed
The docstring above
decodeStoredEntitiessays the migration "runs once, at the migration below, and the result is written back". It ran on every call and wrote on every one.readReports()is called from theuseStateinitialiser (line 143) and on mount, so a plain page load serialised the entire report list — base64 image data URIs, capped at 500 KB each — and wrote it straight back over the top of itself. Synchronously, on the path to first paint, into a store this component already treats as scarce: there is a 5 MB warning threshold and a pruning path fifty lines further down.It was also a correctness problem across tabs. Two tabs open on the hub means each one rewriting storage from whatever snapshot it read, so a report submitted in tab A can be erased by tab B merely rendering.
The write now happens only when the entity decode or a status rename actually changed something.
The regression test was failing for a different reason than it looked
does not rewrite storage when nothing needs migratingwas red on main, and I assumed it was catching the bug above. It was, but it would have stayed red after the fix:makeReport()defaults tostatus: 'Pending', which is itself a legacy value the migration renames to'New'. The "nothing needs migrating" fixture needed migrating.Gave that case a current status, and added one that reads three times over an already-migrated list and asserts total silence — which is the property that actually matters.
Second half: the form's controls had no names
A screen reader announced three unnamed combo boxes in a row.
CommunityHub.test.jsxfailed on it —— and had worked around it by reaching past Testing Library to
document.querySelectorAll('select')[0]and[1]. Index-based DOM access in a test is usually the markup telling you something.Every control in the form now carries an
aria-label, in all three locales. The form's severity control is named "Incident severity" rather than "Severity", because the filters block already has a properly-labelled select named "Severity" and two identical names is barely better than none — there is a test asserting the names within the form are distinct.One thing I backed out while doing this: I initially added an
aria-labelto the location filter input too, then removed it. That input is already inside a wrapping<label>reading "Location (Search)", and anaria-labelwould have overridden the visible text — a worse outcome than leaving it alone.Test repairs that fell out of this
Four separate cases of tests asserting against markup or copy that had moved on:
onSubmitnever runs./Use GPS for Location/iand/GPS Location attached/i, which are the inline defaults in the JSX.translation.jsonoverrides both with "Use Current Location" and "Location attached" — what a reader actually sees. The tests were looking for strings that never reach the screen.vi.stubGlobal('navigator', { ...globalThis.navigator, geolocation })copies nothing.navigator's properties live on its prototype, so the spread produces{}and the component was handed an empty navigator. Replaced with adefinePropertystub that restores the original descriptor in afinally.Checks
npx vitest run src/components/CommunityHub.test.jsx src/components/CommunityHub.escaping.test.jsx— 22 passed (was 16, with 6 failing)npx eslinton all three files — no new problems (one pre-existingexhaustive-depswarning at line 306, deliberately annotated in the source)npm run build— passesThree component suites remain red on main and are untouched by this PR:
CityPollutionLeaderboard(usesjest.mockin a Vitest project),RouteForm, andSymptomReportButton. Each has its own cause and its own issue.