mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 16:08:05 +00:00
Services followed configuration detection order, so frequently used services could not be kept together. Services can now be dragged before or after a sibling, with an insertion indicator and edge scrolling, or moved with Move Up/Down from the toolbar and context menu. The order is stored per project location and type, separately from configuration definitions. New configurations append, windows stay in sync, and a failed save keeps the order for the session with a warning. Sorting in Tree is opt-in, so other trees are unchanged. Co-authored-by: wangxga <212103502+wangxga@users.noreply.github.com>
96 lines
4.4 KiB
TypeScript
96 lines
4.4 KiB
TypeScript
import type { Page } from "@playwright/test";
|
|
import { expect, test } from "./fixtures";
|
|
|
|
/** Supply detected configurations without launching desktop processes. */
|
|
async function configure(page: Page, extra = 0) {
|
|
await page.evaluate(async (extra) => {
|
|
const storePath = "/src/features/run/store.ts";
|
|
const projectPath = "/src/platform/project.ts";
|
|
const { useRun } = await import(storePath);
|
|
const { useProject } = await import(projectPath);
|
|
localStorage.setItem(`openidea.services.runTypes:${useProject.getState().project.root}`, '["spring-boot","npm"]');
|
|
useRun.setState({
|
|
configs: [
|
|
["gateway", "spring-boot"],
|
|
["portal", "spring-boot"],
|
|
["web", "npm"],
|
|
["worker", "npm"],
|
|
...Array.from({ length: extra }, (_, i) => [`app-${i}`, "spring-boot"]),
|
|
].map(([id, kind]) => ({
|
|
id,
|
|
name: id,
|
|
kind,
|
|
command: kind,
|
|
args: [],
|
|
env: {},
|
|
cwd: null,
|
|
detected: true,
|
|
isTest: false,
|
|
debugAdapter: null,
|
|
})),
|
|
});
|
|
}, extra);
|
|
if (!(await page.getByRole("tree", { name: "Services", exact: true }).isVisible()))
|
|
await page.keyboard.press("Meta+8");
|
|
await expect(page.getByRole("tree", { name: "Services", exact: true })).toBeVisible();
|
|
}
|
|
|
|
async function move(page: Page, source: string, target: string) {
|
|
const tree = page.getByRole("tree", { name: "Services", exact: true });
|
|
const sourceRow = tree.getByRole("treeitem", { name: source, exact: true });
|
|
await sourceRow.click();
|
|
const from = await sourceRow.boundingBox();
|
|
const to = await tree.getByRole("treeitem", { name: target, exact: true }).boundingBox();
|
|
if (!from || !to) throw new Error("Service row is not visible");
|
|
await page.mouse.move(from.x + 70, from.y + from.height / 2);
|
|
await page.mouse.down();
|
|
await page.mouse.move(to.x + 70, to.y + 3, { steps: 8 });
|
|
await page.mouse.up();
|
|
}
|
|
|
|
test("service pointer ordering works for Java and npm and survives reload", async ({ workbench: page }) => {
|
|
await configure(page);
|
|
const tree = page.getByRole("tree", { name: "Services", exact: true });
|
|
await move(page, "portal", "gateway");
|
|
await expect(tree.getByRole("treeitem")).toHaveText(["Spring Boot", "portal", "gateway", "npm", "web", "worker"]);
|
|
await expect(tree.getByRole("treeitem", { name: "portal", exact: true })).toHaveAttribute("aria-selected", "true");
|
|
await move(page, "worker", "web");
|
|
await expect(tree.getByRole("treeitem")).toHaveText(["Spring Boot", "portal", "gateway", "npm", "worker", "web"]);
|
|
await page.reload();
|
|
await expect(page.getByRole("tree", { name: "Project" })).toBeVisible();
|
|
await configure(page);
|
|
await expect(tree.getByRole("treeitem")).toHaveText(["Spring Boot", "portal", "gateway", "npm", "worker", "web"]);
|
|
await tree.getByRole("treeitem", { name: "web", exact: true }).click();
|
|
await page.getByRole("button", { name: "Move Up", exact: true }).click();
|
|
await expect(tree.getByRole("treeitem")).toHaveText(["Spring Boot", "portal", "gateway", "npm", "web", "worker"]);
|
|
});
|
|
|
|
test("service dragging scrolls across virtual rows without losing source identity", async ({ workbench: page }) => {
|
|
await configure(page, 100);
|
|
const tree = page.getByRole("tree", { name: "Services", exact: true });
|
|
const source = tree.getByRole("treeitem", { name: "portal", exact: true });
|
|
await source.click();
|
|
const from = await source.boundingBox();
|
|
const bounds = await tree.boundingBox();
|
|
if (!from || !bounds) throw new Error("Service rows are not visible");
|
|
await page.mouse.move(from.x + 70, from.y + from.height / 2);
|
|
await page.mouse.down();
|
|
await page.mouse.move(bounds.x + 100, bounds.y + bounds.height - 5, { steps: 8 });
|
|
await expect.poll(() => tree.evaluate((el) => el.scrollTop)).toBeGreaterThan(1000);
|
|
const target = tree.getByRole("treeitem", { name: "app-99", exact: true });
|
|
await target.scrollIntoViewIfNeeded();
|
|
const to = await target.boundingBox();
|
|
if (!to) throw new Error("Drop target is not visible");
|
|
await page.mouse.move(to.x + 70, to.y + to.height - 3);
|
|
await page.mouse.up();
|
|
await expect(source).toHaveAttribute("aria-selected", "true");
|
|
await expect
|
|
.poll(() =>
|
|
page.evaluate(() => {
|
|
const key = Object.keys(localStorage).find((k) => k.startsWith("openidea.services.runOrder:"));
|
|
return key ? JSON.parse(localStorage.getItem(key)!)["spring-boot"].slice(-2) : [];
|
|
}),
|
|
)
|
|
.toEqual(["app-99", "portal"]);
|
|
});
|