@artymclabin/qa-review 0.3.10 → 0.3.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -882,8 +882,8 @@ export function QAReviewOverlay({ items, target, gateParam, stateUrl, submitUrl,
882
882
  }) })), _jsxs("div", { className: "qar-finish-actions", children: [submitUrl && (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", onClick: saveToDb, disabled: save.status === "saving" || save.status === "ok", className: "qar-btn-primary", "data-qatip": "Store a snapshot of this whole run (verdicts are already saved individually)", children: [_jsx(Database, { size: 16, "aria-hidden": true }), save.status === "saving"
883
883
  ? "Saving…"
884
884
  : save.status === "ok"
885
- ? "Saved to database ✓"
886
- : "Save review to database"] }), save.status === "error" && _jsx("p", { className: "qar-warn", children: save.message })] })), _jsxs("button", { type: "button", onClick: copyResults, className: "qar-btn-outline-accent", "data-qatip": "Copy all verdicts of this page as JSON", children: [_jsx(ClipboardCopy, { size: 16, "aria-hidden": true }), copied ? "Copied!" : "Copy JSON"] }), roundTotal > 0 && (_jsx("button", { type: "button", onClick: () => {
885
+ ? "Snapshot saved ✓"
886
+ : "Save session snapshot (optional)"] }), save.status !== "ok" && (_jsx("p", { className: "qar-finish-autosaved", style: { marginTop: 4 }, children: "Your verdicts above are already saved - this only logs a snapshot of the whole run." })), save.status === "error" && _jsx("p", { className: "qar-warn", children: save.message })] })), _jsxs("button", { type: "button", onClick: copyResults, className: "qar-btn-outline-accent", "data-qatip": "Copy all verdicts of this page as JSON", children: [_jsx(ClipboardCopy, { size: 16, "aria-hidden": true }), copied ? "Copied!" : "Copy JSON"] }), roundTotal > 0 && (_jsx("button", { type: "button", onClick: () => {
887
887
  setFinished(false);
888
888
  // 🚨 THE LAST ITEM, NOT THE FIRST (Arty 2026-08-09: "Clicking
889
889
  // on 'Back to review' in the QA interactive panel should go to
@@ -5,9 +5,24 @@ export declare function normalizeText(text: string): string;
5
5
  export declare function fingerprintText(text: string): string;
6
6
  /**
7
7
  * Fingerprint the CURRENT content of an item: anchored items hash the
8
- * element's rendered innerText; off-DOM task items hash their question text
9
- * (sub, falling back to title). Returns null when the anchor is not present
10
- * (e.g. other-viewport element) - no judgement is made then.
8
+ * element's textContent; off-DOM task items hash their question text (sub,
9
+ * falling back to title). Returns null when the anchor is not present (e.g.
10
+ * other-viewport element) - no judgement is made then.
11
+ *
12
+ * 🚨 textContent, NEVER innerText (Arty 2026-09-24, GameReady fundraise FAQ).
13
+ * innerText is spec'd to approximate what a user currently SEES, so it
14
+ * excludes anything not laid out - which includes the body of a closed
15
+ * native `<details>`. Every accordion-style FAQ defaults to collapsed, so an
16
+ * anchor on the answer paragraph inside it fingerprinted ONLY the always-
17
+ * visible summary/question text forever, regardless of how the answer was
18
+ * edited. "NOT ALTERED since your rejection" then showed on an item that
19
+ * had, in fact, been fixed the same day it was rejected - the exact
20
+ * regression this poka-yoke exists to prevent, silently defeated by the one
21
+ * DOM shape (collapsed accordion) FAQ content overwhelmingly uses. textContent
22
+ * does not care about layout/visibility, which is what "did the words
23
+ * change" actually needs - this was invisible in tests because jsdom does
24
+ * not implement `<details>` collapse or innerText's visibility semantics at
25
+ * all, so the bug could only ever surface against a real rendered page.
11
26
  */
12
27
  export declare function itemFingerprint(item: QAReviewItem, doc: Document): string | null;
13
28
  export type FingerprintStatus = "unchanged" | "changed";
@@ -16,9 +16,24 @@ export function fingerprintText(text) {
16
16
  }
17
17
  /**
18
18
  * Fingerprint the CURRENT content of an item: anchored items hash the
19
- * element's rendered innerText; off-DOM task items hash their question text
20
- * (sub, falling back to title). Returns null when the anchor is not present
21
- * (e.g. other-viewport element) - no judgement is made then.
19
+ * element's textContent; off-DOM task items hash their question text (sub,
20
+ * falling back to title). Returns null when the anchor is not present (e.g.
21
+ * other-viewport element) - no judgement is made then.
22
+ *
23
+ * 🚨 textContent, NEVER innerText (Arty 2026-09-24, GameReady fundraise FAQ).
24
+ * innerText is spec'd to approximate what a user currently SEES, so it
25
+ * excludes anything not laid out - which includes the body of a closed
26
+ * native `<details>`. Every accordion-style FAQ defaults to collapsed, so an
27
+ * anchor on the answer paragraph inside it fingerprinted ONLY the always-
28
+ * visible summary/question text forever, regardless of how the answer was
29
+ * edited. "NOT ALTERED since your rejection" then showed on an item that
30
+ * had, in fact, been fixed the same day it was rejected - the exact
31
+ * regression this poka-yoke exists to prevent, silently defeated by the one
32
+ * DOM shape (collapsed accordion) FAQ content overwhelmingly uses. textContent
33
+ * does not care about layout/visibility, which is what "did the words
34
+ * change" actually needs - this was invisible in tests because jsdom does
35
+ * not implement `<details>` collapse or innerText's visibility semantics at
36
+ * all, so the bug could only ever surface against a real rendered page.
22
37
  */
23
38
  export function itemFingerprint(item, doc) {
24
39
  if (!item.selector)
@@ -26,7 +41,7 @@ export function itemFingerprint(item, doc) {
26
41
  const el = doc.querySelector(item.selector);
27
42
  if (!el)
28
43
  return null;
29
- return fingerprintText(el.innerText ?? el.textContent ?? "");
44
+ return fingerprintText(el.textContent ?? "");
30
45
  }
31
46
  /**
32
47
  * Compare the fingerprint STORED with the last verdict against the current
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@artymclabin/qa-review",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "description": "Interactive on-page QA review overlay (spotlight walkthrough, approve/reject verdict ledger) with a framework-agnostic server handler factory and a self-provisioning Postgres store.",
5
5
  "license": "MIT",
6
6
  "repository": {