feat(ui-calculator): input validation, load caps, ceil display, modernization layout
- custom load capped at cargo capacity (error when exceeded); full load shows the cargo capacity; zero cargo pins load to empty and disables the toggle - per-input red border + tooltip for every invalid value (blocks, techs, load, MAT, modernization target); no value may be negative; locking a speed is disabled when drive is zero - display every computed number (results + goal-seek back-solved input) rounded up to 3 decimals via a shared pkg/calc Ceil3 bridged to wasm; engine keeps its own round-to-nearest util.Fixed* - modernization total upgrade cost spans two columns (single line) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -211,4 +211,71 @@ describe("calculator-tab", () => {
|
||||
ui.getByTestId("calculator-ships-per-turn"),
|
||||
).not.toHaveTextContent("—");
|
||||
});
|
||||
|
||||
test("zero cargo disables the load toggle", async () => {
|
||||
const ui = mount();
|
||||
await setBlock(ui, "drive", 10);
|
||||
await setBlock(ui, "shields", 5);
|
||||
await setBlock(ui, "cargo", 0);
|
||||
expect(ui.getByTestId("calculator-load-full")).toBeDisabled();
|
||||
expect(ui.getByTestId("calculator-load-custom")).toBeDisabled();
|
||||
});
|
||||
|
||||
test("full load shows the cargo capacity", async () => {
|
||||
const ui = mount();
|
||||
await setBlock(ui, "drive", 10);
|
||||
await setBlock(ui, "shields", 5);
|
||||
await setBlock(ui, "cargo", 5);
|
||||
// A fresh design starts with cargo 0, which pins load to empty;
|
||||
// pick full now that there is a hold.
|
||||
await fireEvent.click(ui.getByTestId("calculator-load-full"));
|
||||
// capacity = cargoTech(1) * (5 + 25/20) = 6.25.
|
||||
expect(ui.getByTestId("calculator-full-capacity")).toHaveTextContent("6.25");
|
||||
});
|
||||
|
||||
test("flags a custom load above cargo capacity", async () => {
|
||||
const ui = mount();
|
||||
await setBlock(ui, "drive", 10);
|
||||
await setBlock(ui, "shields", 5);
|
||||
await setBlock(ui, "cargo", 5);
|
||||
await fireEvent.click(ui.getByTestId("calculator-load-custom"));
|
||||
await fireEvent.input(ui.getByTestId("calculator-custom-load"), {
|
||||
target: { value: "100" },
|
||||
});
|
||||
expect(ui.getByTestId("calculator-custom-load")).toHaveAttribute(
|
||||
"aria-invalid",
|
||||
"true",
|
||||
);
|
||||
});
|
||||
|
||||
test("marks an invalid block value with aria-invalid", async () => {
|
||||
const ui = mount();
|
||||
// 0.5 is neither 0 nor ≥ 1.
|
||||
await setBlock(ui, "drive", 0.5);
|
||||
expect(ui.getByTestId("calculator-block-drive")).toHaveAttribute(
|
||||
"aria-invalid",
|
||||
"true",
|
||||
);
|
||||
});
|
||||
|
||||
test("disables the speed lock when drive is zero", async () => {
|
||||
const ui = mount();
|
||||
await setBlock(ui, "drive", 0);
|
||||
await setBlock(ui, "shields", 5);
|
||||
await setBlock(ui, "cargo", 5);
|
||||
expect(ui.getByTestId("calculator-lock-speedEmpty")).toBeDisabled();
|
||||
});
|
||||
|
||||
test("displays computed values rounded up to three decimals", async () => {
|
||||
const ui = mount();
|
||||
await setBlock(ui, "drive", 7);
|
||||
await setBlock(ui, "shields", 3);
|
||||
await setBlock(ui, "cargo", 1);
|
||||
// empty mass = 11; max speed = 11 * driveTech... use a value that is
|
||||
// not already 3-decimal: speedEmpty = 20*7*1.2 / 11 = 15.2727…
|
||||
// ceil to 3 → 15.273.
|
||||
expect(ui.getByTestId("calculator-out-speedEmpty")).toHaveTextContent(
|
||||
"15.273",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user