ui/phase-20: pick-first Send + lock after Modernize/Dismantle/Transfer
Send no longer carries a destination control inside the form: a click on the action drops the inspector straight into map-pick mode, and the form (ship count + confirm) only mounts after the player chooses a destination. Cancelling the picker leaves no form behind. A queued Modernize / Dismantle / Transfer for a given group locks every action button on its inspector and surfaces a banner that points the player at the order list. Cancelling the queued entry from the order tab releases the lock on the next render — the derivation watches draft.commands directly. Send / Load / Unload / Split / Join Fleet do not lock; Send is naturally followed by an out-of-orbit state at turn cutoff, the rest can stack legitimately. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -262,3 +262,108 @@ describe("ship-group inspector — implicit split + action", () => {
|
||||
expect(cmd.groupId).toBe("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa");
|
||||
});
|
||||
});
|
||||
|
||||
describe("ship-group inspector — destructive command lock", () => {
|
||||
const ALL_ACTION_TESTIDS = [
|
||||
"inspector-ship-group-action-split",
|
||||
"inspector-ship-group-action-send",
|
||||
"inspector-ship-group-action-load",
|
||||
"inspector-ship-group-action-unload",
|
||||
"inspector-ship-group-action-modernize",
|
||||
"inspector-ship-group-action-dismantle",
|
||||
"inspector-ship-group-action-transfer",
|
||||
"inspector-ship-group-action-join-fleet",
|
||||
];
|
||||
|
||||
test("a queued dismantleShipGroup disables every action with the lock tooltip", async () => {
|
||||
const groupId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
|
||||
await draft.add({
|
||||
kind: "dismantleShipGroup",
|
||||
id: crypto.randomUUID(),
|
||||
groupId,
|
||||
});
|
||||
const ui = mount(localGroup({ id: groupId, count: 3, cargo: "MAT", load: 0.5 }));
|
||||
const banner = ui.getByTestId("inspector-ship-group-actions-locked");
|
||||
expect(banner).toHaveTextContent(/dismantle/i);
|
||||
for (const id of ALL_ACTION_TESTIDS) {
|
||||
const button = ui.getByTestId(id);
|
||||
expect(button).toBeDisabled();
|
||||
expect(button.getAttribute("title")).toMatch(/order is already queued/i);
|
||||
}
|
||||
});
|
||||
|
||||
test("a queued upgradeShipGroup locks the inspector and reports modernize as the kind", async () => {
|
||||
const groupId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
|
||||
await draft.add({
|
||||
kind: "upgradeShipGroup",
|
||||
id: crypto.randomUUID(),
|
||||
groupId,
|
||||
tech: "ALL",
|
||||
level: 0,
|
||||
});
|
||||
const ui = mount(localGroup({ id: groupId, count: 2 }));
|
||||
expect(ui.getByTestId("inspector-ship-group-actions-locked")).toHaveTextContent(
|
||||
/modernize/i,
|
||||
);
|
||||
});
|
||||
|
||||
test("a queued transferShipGroup locks the inspector and reports transfer as the kind", async () => {
|
||||
const groupId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
|
||||
await draft.add({
|
||||
kind: "transferShipGroup",
|
||||
id: crypto.randomUUID(),
|
||||
groupId,
|
||||
acceptor: "Aliens",
|
||||
});
|
||||
const ui = mount(localGroup({ id: groupId }));
|
||||
expect(ui.getByTestId("inspector-ship-group-actions-locked")).toHaveTextContent(
|
||||
/transfer/i,
|
||||
);
|
||||
});
|
||||
|
||||
test("a queued sendShipGroup does NOT lock the group", async () => {
|
||||
const groupId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
|
||||
await draft.add({
|
||||
kind: "sendShipGroup",
|
||||
id: crypto.randomUUID(),
|
||||
groupId,
|
||||
destinationPlanetNumber: 99,
|
||||
});
|
||||
const ui = mount(localGroup({ id: groupId, count: 3 }));
|
||||
expect(
|
||||
ui.queryByTestId("inspector-ship-group-actions-locked"),
|
||||
).toBeNull();
|
||||
expect(ui.getByTestId("inspector-ship-group-action-split")).not.toBeDisabled();
|
||||
});
|
||||
|
||||
test("a destructive command targeting a different group does not lock this one", async () => {
|
||||
await draft.add({
|
||||
kind: "dismantleShipGroup",
|
||||
id: crypto.randomUUID(),
|
||||
groupId: "ffffffff-ffff-ffff-ffff-ffffffffffff",
|
||||
});
|
||||
const ui = mount(localGroup({ count: 3 }));
|
||||
expect(
|
||||
ui.queryByTestId("inspector-ship-group-actions-locked"),
|
||||
).toBeNull();
|
||||
});
|
||||
|
||||
test("removing the destructive command from the draft releases the lock", async () => {
|
||||
const groupId = "aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa";
|
||||
const cmdId = crypto.randomUUID();
|
||||
await draft.add({
|
||||
kind: "dismantleShipGroup",
|
||||
id: cmdId,
|
||||
groupId,
|
||||
});
|
||||
const ui = mount(localGroup({ id: groupId, count: 3 }));
|
||||
expect(ui.getByTestId("inspector-ship-group-actions-locked")).toBeInTheDocument();
|
||||
await draft.remove(cmdId);
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
ui.queryByTestId("inspector-ship-group-actions-locked"),
|
||||
).toBeNull();
|
||||
});
|
||||
expect(ui.getByTestId("inspector-ship-group-action-split")).not.toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user