diff --git a/src/core/homework-page.ts b/src/core/homework-page.ts index 566169e..b3f7845 100644 --- a/src/core/homework-page.ts +++ b/src/core/homework-page.ts @@ -73,9 +73,17 @@ export function parseHomeworkPage(html: string): SubmissionDetail | undefined { // The student's own text: a textarea while the submission is still editable, // a plain div once it is not. + // + // The read-only div is a *sibling after* ``, not a + // child of it — that section then holds only the file list. Scoping this + // search to the section therefore found nothing for every submission past + // its due date, silently dropping the submitted text while still reporting + // the grade. `class="comment"` (with the quote right after the word) is + // specific enough to search the whole page: the teacher's feedback is + // `class="comment ckcontent"` and so cannot match. const typed = /data-testid="submission-text"[^>]*>([\s\S]*?)<\/textarea>/.exec(html)?.[1] ?? - (submission ? /
]*>([\s\S]*?)<\/div>/.exec(submission)?.[1] : undefined); + /
]*>([\s\S]*?)<\/div>/.exec(html)?.[1]; const typedText = clean(typed); if (typedText) detail.submittedText = typedText; diff --git a/test/homework-page.test.ts b/test/homework-page.test.ts index 0327499..e14570b 100644 --- a/test/homework-page.test.ts +++ b/test/homework-page.test.ts @@ -7,11 +7,12 @@ import { parseHomeworkPage } from '../src/core/homework-page.ts'; * as actually served — including the parts that are absent for a student, which * is what makes every field optional. */ -const page = (parts: { submission?: string; feedback?: string }) => ` +const page = (parts: { submission?: string; feedback?: string; afterSubmission?: string }) => `
keine Beschreibung vorhanden
${parts.submission ?? ''}
+${parts.afterSubmission ?? ''} ${parts.feedback === undefined ? '' : `
${parts.feedback}
`} `; @@ -39,10 +40,30 @@ describe('parseHomeworkPage', () => { }); it('reads the typed answer from the read-only form once submission closed', () => { - const html = page({ submission: `

Abgegebener Text

` }); + // The read-only `
` is a sibling *after* the closed + // submission section, not a child of it — verified against a real 33.40 + // page for a past-due submission. A fixture that nested it inside the + // section (as an earlier one did) hid a bug where every past-due + // submission lost its text. See src/core/homework-page.ts. + const html = page({ + submission: `
`, + afterSubmission: `

Abgegebener Text

`, + }); assert.equal(parseHomeworkPage(html)?.submittedText, 'Abgegebener Text'); }); + it('does not confuse the submitted text with the teacher feedback comment', () => { + // Both are comment divs, distinguished only by the exact class: the + // student's is `class="comment"`, the teacher's `class="comment ckcontent"`. + const html = page({ + afterSubmission: `

Meine Abgabe

`, + feedback: `

Gut gemacht

`, + }); + const detail = parseHomeworkPage(html); + assert.equal(detail?.submittedText, 'Meine Abgabe'); + assert.equal(detail?.gradeComment, 'Gut gemacht'); + }); + it('separates submitted files from files the teacher returned', () => { const html = page({ submission: fileCard('a'.repeat(24), 'meine-abgabe.pdf'),