Files
awesome-copilot/eng/board-preview.test.mjs
T
Aaron PowellandCopilot App a10206b7f6 fix(canvases): dismiss previews and enable PR card dragging
Close review board previews after successful item actions and outside clicks without dismissing a different item during in-flight requests. Explicitly enable native dragging on editable PR cards and add regression coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: cd29e26b-0e56-48bf-a046-ac749aef86ce
2026-10-14 11:25:57 +11:00

186 lines
8.1 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import vm from "node:vm";
import { test } from "node:test";
async function createUI(boardName, request = async () => ({
status: "submitted", body: "/approve", pending: true, message: "Queued",
})) {
const nodes = new Map();
function node() {
const classes = new Set();
return {
content: "", value: "", dataset: {}, listeners: {}, attributes: {},
classList: {
add: (name) => classes.add(name),
remove: (name) => classes.delete(name),
contains: (name) => classes.has(name),
},
addEventListener(name, handler) { this.listeners[name] = handler; },
setAttribute(name, value) { this.attributes[name] = value; },
querySelectorAll: () => [],
querySelector: () => null,
contains(target) { return target === this || target.insideDrawer === true; },
focus() { this.focused = true; },
};
}
const listeners = new Map();
const document = {
querySelector: node,
querySelectorAll: () => [],
getElementById(id) {
if (!nodes.has(id)) nodes.set(id, node());
return nodes.get(id);
},
addEventListener(name, handler, capture) { listeners.set(name, { handler, capture }); },
};
const messages = [];
const context = vm.createContext({
document, setTimeout: () => 1, clearTimeout() {}, confirm: () => true,
});
const isPR = boardName === "open-pr-board";
const source = await readFile(new URL(`../.github/extensions/${boardName}/public/app.js`, import.meta.url), "utf8");
const end = isPR ? "const events = new EventSource" : "function connect()";
vm.runInContext(source.slice(0, source.indexOf(end)), context);
context.request = request;
context.notify = (message) => messages.push(message);
vm.runInContext(`
board = { items: [1, 2].map(number => ({
number, column: "approve", headRefOid: "first", reviewEligible: true,
review: { suggestedComment: "Feedback" },
})), columns: [] };
renderBoard = renderDrawer = renderDrawerHeader = renderDrawerBody = () => {};
api = request;
toast = notify;
`, context);
const drawer = nodes.get("drawer");
function select(number) {
vm.runInContext(`${isPR ? "selectedNumber" : "selected"} = ${number}`, context);
drawer.classList.add("open");
drawer.setAttribute("aria-hidden", "false");
drawer.inert = false;
nodes.get("board").inert = isPR;
if (isPR) {
vm.runInContext(`drawerOriginNumber = ${number}`, context);
vm.runInContext("el.toolbar.inert = true", context);
}
nodes.get("decision-comment").value = "Feedback";
}
select(1);
async function decide(kind = "approve") {
if (isPR) {
const button = nodes.get("decision-approve") ?? node();
await context.submitDecision(button, kind);
return context.submitDecision(button, kind);
}
await context.decide(kind);
return context.decide(kind);
}
return {
context, nodes, drawer, listeners, messages, select, decide,
selected: () => vm.runInContext(isPR ? "selectedNumber" : "selected", context),
};
}
for (const boardName of ["open-pr-board", "external-plugin-review-board"]) {
test(`${boardName}: clicking inside retains the preview; clicking outside closes it`, async () => {
const ui = await createUI(boardName);
const { handler, capture } = ui.listeners.get("pointerdown");
assert.equal(capture, true);
handler({ target: { insideDrawer: true } });
assert.equal(ui.selected(), 1);
handler({ target: {} });
assert.equal(ui.selected(), null);
assert.equal(ui.drawer.classList.contains("open"), false);
assert.equal(ui.drawer.attributes["aria-hidden"], "true");
assert.equal(ui.nodes.get("board").inert, false);
if (boardName === "open-pr-board") {
assert.equal(ui.drawer.inert, true);
assert.equal(ui.nodes.get("filter").focused, true);
assert.equal(vm.runInContext("el.toolbar.inert", ui.context), false);
}
handler({ target: {} });
assert.equal(ui.selected(), null);
});
const kinds = boardName === "open-pr-board"
? ["approve", "close", "request-changes", "comment"] : ["approve", "reject"];
for (const kind of kinds) {
test(`${boardName}: successful ${kind} closes the preview`, async () => {
const ui = await createUI(boardName);
await ui.decide(kind);
assert.equal(ui.selected(), null);
assert.equal(ui.drawer.classList.contains("open"), false);
assert.equal(ui.messages.length, 1);
});
}
test(`${boardName}: a failed action leaves the preview open`, async () => {
const ui = await createUI(boardName, async () => { throw new Error("Offline"); });
await ui.decide();
assert.equal(ui.selected(), 1);
assert.equal(ui.drawer.classList.contains("open"), true);
assert.equal(ui.messages[0], "Offline");
});
test(`${boardName}: completion does not close a different preview opened in flight`, async () => {
let release;
const ui = await createUI(boardName, () => new Promise(resolve => { release = resolve; }));
const pending = ui.decide();
await new Promise(resolve => setImmediate(resolve));
ui.listeners.get("pointerdown").handler({ target: {} });
ui.select(2);
release({ status: "submitted", body: "/approve" });
await pending;
assert.equal(ui.selected(), 2);
assert.equal(ui.drawer.classList.contains("open"), true);
});
test(`${boardName}: successful bucket moves close the preview`, async () => {
const ui = await createUI(boardName);
if (boardName === "open-pr-board") {
await ui.nodes.get("drawer-column").listeners.change({ target: { value: "close" } });
} else {
await ui.context.moveItem(1, "straight-reject");
}
assert.equal(ui.selected(), null);
});
test(`${boardName}: successful re-review closes the preview`, async () => {
const ui = await createUI(boardName);
if (boardName === "open-pr-board") {
await ui.nodes.get("rereview-start").listeners.click({ currentTarget: ui.nodes.get("rereview-start") });
} else {
await ui.nodes.get("rereview-panel").listeners.submit({ preventDefault() {} });
}
assert.equal(ui.selected(), null);
});
test(`${boardName}: failed bucket moves and re-reviews leave the preview open`, async () => {
const ui = await createUI(boardName, async () => { throw new Error("Offline"); });
if (boardName === "open-pr-board") {
await ui.nodes.get("drawer-column").listeners.change({ target: { value: "close" } });
await ui.nodes.get("rereview-start").listeners.click({ currentTarget: ui.nodes.get("rereview-start") });
} else {
await ui.context.moveItem(1, "straight-reject");
await ui.nodes.get("rereview-panel").listeners.submit({ preventDefault() {} });
}
assert.equal(ui.selected(), 1);
assert.equal(ui.drawer.classList.contains("open"), true);
assert.deepEqual(ui.messages, ["Offline", "Offline"]);
});
test(`${boardName}: bulk decisions close a preview of a successfully actioned item`, async () => {
const ui = await createUI(boardName);
await ui.context.bulkAction("approve", "approve");
assert.equal(ui.selected(), null);
});
}
test("open-pr-board: partial approval success closes the preview and keeps the warning visible", async () => {
const ui = await createUI("open-pr-board", async () => ({ warning: "Auto-merge could not be enabled" }));
await ui.decide();
assert.equal(ui.selected(), null);
assert.equal(ui.messages[0], "Auto-merge could not be enabled");
});