Skip to content

Commit 42c9402

Browse files
cursoragentjon-bell
andcommitted
Address PR feedback on CSV escaping and E2E parsing
Co-authored-by: Jonathan Bell <jon@jonbell.net>
1 parent bd6167e commit 42c9402

File tree

3 files changed

+22
-16
lines changed

3 files changed

+22
-16
lines changed

app/course/[course_id]/manage/gradebook/gradebookTable.tsx

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1842,14 +1842,17 @@ export default function GradebookTable() {
18421842
const csv = gradebookController.exportGradebook(courseController, {
18431843
useRenderExpressions: exportWithRenderExpressions
18441844
});
1845-
const blob = new Blob(
1846-
[
1847-
csv
1848-
.map((row) => row.map((cell) => (typeof cell === "string" ? `"${cell.replace(/"/g, "")}"` : cell)).join(","))
1849-
.join("\n")
1850-
],
1851-
{ type: "text/csv" }
1852-
);
1845+
const csvText = csv
1846+
.map((row) =>
1847+
row
1848+
.map((cell) => {
1849+
const stringCell = cell === null || cell === undefined ? "" : String(cell);
1850+
return `"${stringCell.replace(/"/g, '""')}"`;
1851+
})
1852+
.join(",")
1853+
)
1854+
.join("\n");
1855+
const blob = new Blob([csvText], { type: "text/csv" });
18531856
const url = URL.createObjectURL(blob);
18541857
const a = document.createElement("a");
18551858
a.href = url;

tests/e2e/TestingUtils.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -387,10 +387,8 @@ async function signInWithMagicLinkAndRetry(page: Page, testingUser: TestingUser,
387387
}
388388
throw new Error(`Magic link sign-in did not complete (final URL: ${page.url()})`);
389389
} catch (error) {
390-
if (retriesRemaining > 0 && (error as Error).message.includes("did not complete")) {
391-
return await signInWithMagicLinkAndRetry(page, testingUser, retriesRemaining - 1);
392-
}
393-
throw new Error(`Failed to sign in with magic link: ${(error as Error).message}`);
390+
const message = error instanceof Error ? error.message : String(error);
391+
throw new Error(`Failed to sign in with magic link: ${message}`);
394392
}
395393
}
396394
export async function loginAsUser(page: Page, testingUser: TestingUser, course?: Course) {

tests/e2e/gradebook.test.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { Page, Locator } from "@playwright/test";
44
import { argosScreenshot } from "@argos-ci/playwright";
55
import dotenv from "dotenv";
66
import { promises as fs } from "node:fs";
7+
import Papa from "papaparse";
78
import {
89
createClass,
910
createUsersInClass,
@@ -51,10 +52,14 @@ async function readCellText(page: Page, rowName: string, columnName: string) {
5152
}
5253

5354
function parseCsv(csvText: string) {
54-
return csvText
55-
.trim()
56-
.split(/\r?\n/)
57-
.map((line) => line.split(",").map((cell) => cell.trim().replace(/^"(.*)"$/, "$1")));
55+
const parsed = Papa.parse<string[]>(csvText, {
56+
skipEmptyLines: true
57+
});
58+
if (parsed.errors.length > 0) {
59+
const firstError = parsed.errors[0];
60+
throw new Error(`Failed to parse CSV: ${firstError.message}`);
61+
}
62+
return parsed.data.map((row) => row.map((cell) => cell ?? ""));
5863
}
5964

6065
function getCsvCellValue(csvRows: string[][], email: string, columnName: string) {

0 commit comments

Comments
 (0)