Skip to content

Commit 2a8f11f

Browse files
committed
Capture and annotate post-submit review state (Section 8)
After the user authorized and ran scripts/capture/submit-and-capture- review.mjs, a Comment-style review was submitted on PR #121 (no Approve, no stage advancement). The review body is marked '[Documentation training sandbox]' so future viewers of the PR know it was synthetic. fig-09-review-submitted: three callouts on the timeline entry — 1. The 'rmctestreviewer reviewed now' event header 2. The 'View reviewed changes' link that jumps to a Files-changed view of what was reviewed 3. The review comment card showing the body the reviewer typed The submit-and-capture-review.mjs selector for the textarea was tightened to scope inside the dialog (originally picked up GitHub's hidden feedback form). Added probe-review-submitted.mjs to pin sub-element coords inside the post-submit timeline block.
1 parent f7fa3ed commit 2a8f11f

8 files changed

Lines changed: 184 additions & 9 deletions

File tree

418 KB
Loading
87.4 KB
Loading
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"url": "https://github.com/USACE-RMC/RMC-Software-Documentation/pull/121",
3+
"capturedAt": "2026-05-20T20:37:36.917Z",
4+
"elements": {
5+
"reviewEvent": {
6+
"x": 168,
7+
"y": 2532,
8+
"w": 808,
9+
"h": 242
10+
},
11+
"reviewCard": {
12+
"x": 168,
13+
"y": 2532,
14+
"w": 808,
15+
"h": 242
16+
},
17+
"reviewedHeader": {
18+
"x": 184,
19+
"y": 2532,
20+
"w": 792,
21+
"h": 65
22+
},
23+
"reviewedHeaderRow": {
24+
"x": 184,
25+
"y": 2532,
26+
"w": 792,
27+
"h": 65
28+
},
29+
"viewLink": {
30+
"x": 832,
31+
"y": 2553,
32+
"w": 145,
33+
"h": 28
34+
},
35+
"commentHeader": null,
36+
"commentCard": null
37+
}
38+
}
59.1 KB
Binary file not shown.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""
2+
Annotated "review submitted" figure for Section 8 of the reviewer-workflow
3+
chapter — what the reviewer sees on the Conversation tab right after
4+
clicking Submit review.
5+
6+
Source:
7+
planning/assets/captures/conversation-post-submit.png
8+
planning/assets/captures/post-submit.coords.json
9+
10+
Highlights:
11+
1. The "rmctestreviewer reviewed now" timeline event header
12+
2. "View reviewed changes" link — jumps to a Files-changed view that
13+
shows only what was reviewed
14+
3. The review comment card showing the overall body the reviewer typed
15+
"""
16+
17+
import json
18+
import sys
19+
from pathlib import Path
20+
21+
sys.path.insert(0, str(Path(__file__).resolve().parent))
22+
from annotate_lib import annotate_and_crop # noqa: E402
23+
24+
REPO_ROOT = Path(__file__).resolve().parents[2]
25+
SRC = REPO_ROOT / "planning" / "assets" / "captures" / "conversation-post-submit.png"
26+
COORDS = REPO_ROOT / "planning" / "assets" / "captures" / "post-submit.coords.json"
27+
OUT = REPO_ROOT / "static" / "figures" / "dev" / "reviewer-workflow" / "fig-09-review-submitted.png"
28+
PPTX = REPO_ROOT / "planning" / "assets" / "figure-pptx" / "fig-09-review-submitted.pptx"
29+
30+
elements = json.loads(COORDS.read_text())["elements"]
31+
view_link = elements["viewLink"] # (832, 2553, 145, 28)
32+
reviewed_row = elements["reviewedHeader"] # (184, 2532, 792, 65) — full row
33+
34+
# Tighten box 1 to just the "rmctestreviewer reviewed now" text on the left.
35+
reviewed_text = (reviewed_row["x"], reviewed_row["y"] + 18, 340, 30)
36+
# Comment card sits just below the event row (probe didn't find it cleanly).
37+
comment_card = (168, reviewed_row["y"] + 75, 808, 170)
38+
39+
# Crop with a little context above and below.
40+
top = reviewed_row["y"] - 50
41+
bottom = comment_card[1] + comment_card[3] + 50
42+
CROP = (60, top, 1340, bottom)
43+
44+
callouts = [
45+
(reviewed_text[0], reviewed_text[1], reviewed_text[2], reviewed_text[3], "tl", 1),
46+
(view_link["x"], view_link["y"], view_link["w"], view_link["h"], "tr", 2),
47+
(comment_card[0], comment_card[1], comment_card[2], comment_card[3], "tl", 3),
48+
]
49+
50+
annotate_and_crop(SRC, callouts, CROP, OUT, pptx_out=PPTX)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Find specific sub-element coords inside the "review submitted" block
2+
// on the PR #121 Conversation tab.
3+
4+
import { chromium } from 'playwright';
5+
import { readFileSync, writeFileSync } from 'node:fs';
6+
import { resolve } from 'node:path';
7+
8+
const AUTH_FILE = resolve(process.cwd(), '.playwright-auth', 'github.json');
9+
const COORDS_FILE = resolve(process.cwd(), 'planning', 'assets', 'captures', 'post-submit.coords.json');
10+
11+
const browser = await chromium.launch({ headless: true });
12+
const context = await browser.newContext({ storageState: AUTH_FILE, viewport: { width: 1440, height: 900 } });
13+
const page = await context.newPage();
14+
await page.goto('https://github.com/USACE-RMC/RMC-Software-Documentation/pull/121', { waitUntil: 'domcontentloaded' });
15+
await page.waitForTimeout(3000);
16+
// Scroll to bottom to load the recent review event
17+
await page.evaluate(() => window.scrollTo(0, document.documentElement.scrollHeight));
18+
await page.waitForTimeout(800);
19+
20+
const info = await page.evaluate(() => {
21+
const r = (el) => { if (!el) return null; const b = el.getBoundingClientRect(); return { x: Math.round(b.x + window.scrollX), y: Math.round(b.y + window.scrollY), w: Math.round(b.width), h: Math.round(b.height) }; };
22+
23+
// The exact "rmctestreviewer reviewed now" text
24+
let reviewedHeader = null;
25+
for (const el of document.querySelectorAll('a, span, h3, h4, div')) {
26+
const t = (el.innerText || '').trim();
27+
if (/^rmctestreviewer reviewed/.test(t) && t.length < 80) {
28+
reviewedHeader = el;
29+
break;
30+
}
31+
}
32+
// Climb to the containing row
33+
let reviewedHeaderRow = reviewedHeader;
34+
if (reviewedHeader) {
35+
let p = reviewedHeader;
36+
for (let i = 0; i < 6 && p.parentElement; i++) {
37+
const pr = p.getBoundingClientRect();
38+
if (pr.width > 600 && pr.height < 60) { reviewedHeaderRow = p; break; }
39+
p = p.parentElement;
40+
}
41+
}
42+
43+
// "View reviewed changes" link
44+
const viewLink = Array.from(document.querySelectorAll('a, button')).find((e) => /^View reviewed changes$/.test((e.innerText || '').trim()));
45+
46+
// The blue "left a comment" header strip
47+
let commentHeader = null;
48+
for (const el of document.querySelectorAll('div, header, span')) {
49+
const t = (el.innerText || '').trim();
50+
if (/^rmctestreviewer left a comment$/.test(t) && t.length < 80) {
51+
commentHeader = el;
52+
break;
53+
}
54+
}
55+
// The full review comment card (the bordered box)
56+
let commentCard = null;
57+
if (commentHeader) {
58+
let p = commentHeader;
59+
for (let i = 0; i < 8 && p.parentElement; i++) {
60+
const pr = p.getBoundingClientRect();
61+
if (pr.height > 130 && pr.width > 700 && pr.width < 900) { commentCard = p; break; }
62+
p = p.parentElement;
63+
}
64+
}
65+
66+
return {
67+
reviewedHeader: r(reviewedHeader),
68+
reviewedHeaderRow: r(reviewedHeaderRow),
69+
viewLink: r(viewLink),
70+
commentHeader: r(commentHeader),
71+
commentCard: r(commentCard),
72+
pageHeight: document.documentElement.scrollHeight,
73+
};
74+
});
75+
76+
console.log(JSON.stringify(info, null, 2));
77+
78+
const existing = JSON.parse(readFileSync(COORDS_FILE, 'utf8'));
79+
Object.assign(existing.elements, info);
80+
delete existing.elements.pageHeight;
81+
writeFileSync(COORDS_FILE, JSON.stringify(existing, null, 2));
82+
83+
await browser.close();

scripts/capture/submit-and-capture-review.mjs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,25 +40,29 @@ console.log(' opening submit-review dialog...');
4040
await page.locator('button:has-text("Submit review"), button:has-text("Add your review"), button:has-text("Finish your review")').first().click({ timeout: 4000 });
4141
await page.waitForTimeout(1200);
4242

43-
// 2. Fill the comment textarea
43+
// Locate the open dialog — GitHub's "Finish your review" overlay. Every
44+
// click after this is scoped inside the dialog so we don't accidentally
45+
// hit the page's hidden feedback form (id=feedback) or the dialog-open
46+
// button at the top right.
47+
const dialog = page.locator('div[role="dialog"], [aria-modal="true"], dialog')
48+
.filter({ hasText: /Finish your review|Submit your review/i })
49+
.first();
50+
await dialog.waitFor({ state: 'visible', timeout: 5000 });
51+
52+
// 2. Fill the comment textarea (only the one inside the dialog)
4453
console.log(' typing review body...');
45-
const textarea = page.locator('textarea[placeholder*="comment" i], textarea[name*="body" i], textarea').first();
54+
const textarea = dialog.locator('textarea').first();
4655
await textarea.fill(REVIEW_BODY, { timeout: 4000 });
4756
await page.waitForTimeout(400);
4857

4958
// 3. Confirm "Comment" radio is selected (it's the default, but be explicit)
50-
const commentRadio = page.locator('input[type="radio"][value="comment" i], input[type="radio"]').first();
59+
const commentRadio = dialog.locator('input[type="radio"]').first();
5160
const checked = await commentRadio.isChecked().catch(() => true);
5261
if (!checked) await commentRadio.click({ timeout: 2000 }).catch(() => {});
5362

5463
// 4. Click the dialog's Submit button
5564
console.log(' submitting review...');
56-
// The dialog has its own Submit button — find by its position inside the
57-
// overlay rather than the top-right open-dialog button.
58-
await page.locator('div[role="dialog"] button:has-text("Submit review"), .Overlay button:has-text("Submit review")').first().click({ timeout: 5000 }).catch(async () => {
59-
// Fallback — click any Submit review button visible after dialog open
60-
await page.locator('button:has-text("Submit review")').last().click({ timeout: 4000 });
61-
});
65+
await dialog.locator('button:has-text("Submit review")').first().click({ timeout: 5000 });
6266

6367
// Wait for the dialog to close and the page to refresh state
6468
await page.waitForTimeout(4000);
33.1 KB
Loading

0 commit comments

Comments
 (0)