fix: stop a large diff and a second SSH error from taking the app down
Two failure modes that only show up under conditions the tests never reached. renderDiff built one span per diff line with no bound. A regenerated lock file is an ordinary change: 50,000 lines produce 4 MB of markup and 50,000 elements that then have to be parsed and laid out inside the full shell replacement, and 200,000 lines produce 16 MB. The rendered view now stops at 2,000 lines and says how many were left out; ui.diff keeps the whole change, so Copy diff, the editor and hunk staging are unaffected. The line scan also runs once now instead of three times. withClient registered the connection error handler with once(). A connection that fails and then emits a second error while it is being torn down - a reset during client.end() is the ordinary case - leaves that event unhandled, and an unhandled 'error' on an EventEmitter reaches the uncaughtException handler, which calls app.exit(1). The handler stays attached and ignores anything after the first failure. Both are covered by tests that were confirmed to fail without the fix, together with the SSH paths that had none: host key mismatch reporting, the trusted fingerprint requirement for exec and upload, and remote upload path validation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
a5666e95f2
commit
5cecaa080d
@@ -272,6 +272,37 @@ test("sections that used to be injected after render are part of the rendered ma
|
||||
await expect(auditPanel.locator("table.data-table")).toContainText("deployment.requested");
|
||||
});
|
||||
|
||||
test("a very large diff is capped instead of freezing the window", async ({ page }) => {
|
||||
const selected = await page.evaluate(() => {
|
||||
const withChanges = ui.repositories.find((repository) => repository.localStatus?.counts?.changed);
|
||||
if (!withChanges) return null;
|
||||
selectRepository(withChanges.id);
|
||||
return withChanges.fullName;
|
||||
});
|
||||
expect(selected, "the demo needs a repository with local changes").not.toBeNull();
|
||||
await expect(page.locator(".diff-view")).toBeVisible();
|
||||
|
||||
const measured = await page.evaluate(() => {
|
||||
const newline = String.fromCharCode(10);
|
||||
const lines = ["diff --git a/package-lock.json b/package-lock.json"];
|
||||
for (let index = 0; index < 40_000; index += 1) lines.push(`+ "package-${index}": "^1.2.3",`);
|
||||
ui.diff = lines.join(newline);
|
||||
ui.repositoryTab = "changes";
|
||||
const started = performance.now();
|
||||
render();
|
||||
return {
|
||||
renderMs: performance.now() - started,
|
||||
rendered: document.querySelectorAll(".diff-line").length,
|
||||
storedLines: ui.diff.split(newline).length,
|
||||
};
|
||||
});
|
||||
|
||||
expect(measured.storedLines).toBe(40_001);
|
||||
expect(measured.rendered).toBeLessThan(2100);
|
||||
expect(measured.renderMs).toBeLessThan(3000);
|
||||
await expect(page.locator(".diff-view")).toContainText("more lines are not shown");
|
||||
});
|
||||
|
||||
test("an unchanged render leaves the existing DOM in place", async ({ page }) => {
|
||||
await page.locator('[data-action="select-repo"]').first().click();
|
||||
const marked = await page.evaluate(() => {
|
||||
|
||||
Reference in New Issue
Block a user