Файли
openidea/src/workbench/messages.test.tsx
T
l0ng-ai 81e0e8e2d0 feat: Align interactions with IntelliJ IDEA (#22)
* feat(nav): Complete file paths and flag unresolved web file references

Path completion in href/src/url()/@import, Markdown links and path-like strings, folders first. HTML and CSS paths that name no file get a 'Cannot resolve file' warning. File references resolve in the backend like IDEA's: relative to the file, /-rooted against the project and resource roots.

* feat: Align editor, workbench, run/debug and VCS interactions with IntelliJ IDEA

Keymap
- Let AltGr characters through instead of firing Ctrl+Alt shortcuts (Windows/Linux)
- Double Shift: strict press/release timing, ignored while typing or after Shift+click
- Chords wait 2s and swallow an unmatched second stroke
- Drop CodeMirror defaults that clash with IDEA keys; ⌘Enter is Split Line, add
  Scroll to Center and Code Block Start/End
- Add missing IDEA defaults (⌃J, ⌘Y, ⇧⌘C, ⌥⌘Y, ⇧⌘M, F9 Resume, ⌘R Rerun, …)

Editor
- Tab inserts at the caret; smart Backspace to the proper indent; Enter continues
  a `//` comment; per-language line comment placement
- Back/Forward records navigations only; ⌘F searches the selected text
- In-place rename; ⌃Space inserts a sole candidate; commit characters only for a
  focused lookup; usage highlighting survives edits; ⌥Enter offers fixes from the
  caret line; ⌘-hover shows declaration info only for navigable targets

Workbench
- Tool windows: primary and split area per side, six Move To anchors, layout migration
- ⌘W closes tool window content; Shift+Esc from the editor; closing a tab
  activates the left neighbour; preview tab and tab limit follow IDEA defaults
- Switcher lists open editors and tool windows; Recent Files Delete removes items
- Search Everywhere tab/scope behaviour; popups close when the window deactivates
- Project view: Copy/Move dialogs on paste, skippable usage search on delete,
  new-file input cancels on blur; Settings search Esc clears the filter
- Status bar breadcrumbs open a children popup; notification timeouts

Run / Debug / Build / Terminal
- Run asks to stop a running single-instance config; Rerun restarts silently;
  "Allow multiple instances" option
- Run chooser: Enter runs, Shift debugs, F4/Delete/digits
- Type into the console for stdin; Stop covers debug sessions
- Log (Shift+click) and temporary (Alt+click) breakpoints; independent suspend/log
  options; Evaluate prefill and history; changed-value highlighting; expandable hints
- Build window opens on failure and jumps to the first error
- Terminal confirms closing a busy tab and lets IDE shortcuts through

VCS
- Correct ours/theirs labels during rebase, cherry-pick and revert
- Smart/Force Checkout; remote branch checkout updates the local branch
- Partial commit by changed block; inline before-commit check results
- F7 continues into the next file; Rollback Lines; Commit and Push; Amend message swap
- Branch delete with Restore; force push confirmation; editable push target;
  update summary with the incoming commits

* chore: Regenerate bindings
2026-10-25 09:34:02 +08:00

88 рядки
3.5 KiB
TypeScript

import { afterEach, describe, expect, it } from "vitest";
import { act, fireEvent, render, screen } from "@testing-library/react";
import { usePlatform } from "@/platform/platformInfo";
import { ask, confirmChecked, MessagesHost, type MessageResult } from "./messages";
function askUsagesDetected() {
let result: MessageResult | undefined;
act(() => {
void ask({
title: "Usages Detected",
body: "Function 'unused' has 1 usage that is not safe to delete.",
buttons: [
{ id: "view", label: "View Usages", primary: true },
{ id: "delete", label: "Delete Anyway" },
],
}).then((r) => (result = r));
});
return () => result;
}
const footerLabels = () =>
[...screen.getByRole("dialog").querySelectorAll("[data-dialog-footer] button")].map((b) => b.textContent);
describe("message dialogs", () => {
const os = usePlatform.getState().os;
afterEach(() => usePlatform.setState({ os }));
it("macOS: Cancel · Delete Anyway · View Usages (default rightmost, focused); Enter picks the default", async () => {
usePlatform.setState({ os: "macos" });
render(<MessagesHost />);
const result = askUsagesDetected();
expect(footerLabels()).toEqual(["Cancel", "Delete Anyway", "View Usages"]);
expect(document.activeElement).toBe(screen.getByRole("button", { name: "View Usages" }));
await act(async () => void fireEvent.keyDown(screen.getByRole("dialog"), { key: "Enter" }));
expect(result()).toEqual({ button: "view", checked: false });
expect(screen.queryByRole("dialog")).toBeNull();
});
it("Windows: View Usages · Delete Anyway · Cancel; Escape and Cancel cancel", async () => {
usePlatform.setState({ os: "windows" });
render(<MessagesHost />);
let result = askUsagesDetected();
expect(footerLabels()).toEqual(["View Usages", "Delete Anyway", "Cancel"]);
await act(async () => void fireEvent.keyDown(document.body, { key: "Escape" }));
expect(result()?.button).toBe("cancel");
result = askUsagesDetected();
await act(async () => void fireEvent.click(screen.getByRole("button", { name: "Cancel" })));
expect(result()?.button).toBe("cancel");
expect(screen.queryByRole("dialog")).toBeNull();
});
it("reports the checkbox with the chosen button", async () => {
render(<MessagesHost />);
let result: { checked: boolean } | null | undefined;
act(() => {
void confirmChecked({
title: "Delete Image",
message: "Delete image 'x'?",
ok: "Delete",
checkbox: "Force",
}).then((r) => (result = r));
});
fireEvent.click(screen.getByRole("checkbox", { name: "Force" }));
await act(async () => void fireEvent.click(screen.getByRole("button", { name: "Delete" })));
expect(result).toEqual({ checked: true });
});
it("starts with the given checkbox state and can be dismissed from outside", async () => {
render(<MessagesHost />);
const abort = new AbortController();
let result: MessageResult | undefined;
act(() => {
void ask({
title: "Delete",
body: "Delete 'a'?",
checkbox: "Safe delete",
checked: true,
signal: abort.signal,
buttons: [{ id: "ok", label: "Delete", primary: true }],
}).then((r) => (result = r));
});
expect(screen.getByRole("checkbox", { name: "Safe delete" })).toBeChecked();
await act(async () => abort.abort());
expect(result).toEqual({ button: "dismissed", checked: true });
expect(screen.queryByRole("dialog")).toBeNull();
});
});