mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-06 16:08:05 +00:00
Configuration navigation from YAML/properties still waited for a cold index the first time and repeated the lookup between the ⌘-hover probe and the click. - Trusted projects with a Maven/Gradle/Eclipse build file start Java shortly after opening. Once JDT reports ServiceReady, the app calls the new openidea.java.configuration.warmup command: a low-priority background job builds the configuration index one project at a time (open YAML/properties modules first), waits for workspace builds, has a 30 s budget per project, and only fills free cache room. - Index builds take a cancellable per-project lock instead of one global lock, so a background build never delays another project, and a warmed snapshot is dropped if the Java model changed meanwhile. - In YAML/properties the editor underlines the whole key (hyphens and dots included), hover and Go to Definition share one bounded request per key, and the declaration chooser names the whole key. Bundle java-usages 1.6.0 is rebuilt reproducibly with build.py against JDT LS 1.63.0-202610191322 (SHA-256 33cfa1bb…). The real-JDT harness checks that the first lookup reuses the warmed index. Co-authored-by: qjlqyf <109515705+qjlqyf@users.noreply.github.com>
92 lines
3.9 KiB
TypeScript
92 lines
3.9 KiB
TypeScript
import { writeFile } from "node:fs/promises";
|
|
import { test, expect, type Page } from "@playwright/test";
|
|
|
|
async function keyPosition(page: Page) {
|
|
const line = page.locator("#config .cm-line").filter({ hasText: "driver-class-name" });
|
|
const box = (await line.boundingBox())!;
|
|
// Skip the two-space indentation, land within the actual key.
|
|
return { x: box.x + 45, y: box.y + box.height / 2 };
|
|
}
|
|
async function open(page: Page) {
|
|
await page.addInitScript(() => Object.defineProperty(Navigator.prototype, "platform", { get: () => "MacIntel" }));
|
|
await page.goto("/e2e/configurationNavigation.html");
|
|
await expect(page.locator("#config .cm-content")).toContainText("driver-class-name");
|
|
}
|
|
async function metrics(page: Page) {
|
|
return page.evaluate(() =>
|
|
(
|
|
window as unknown as {
|
|
navigationMetrics: () => {
|
|
requests: number;
|
|
hoverRequests: number;
|
|
latency: number[];
|
|
injectedServerDelayMs: number;
|
|
};
|
|
}
|
|
).navigationMetrics(),
|
|
);
|
|
}
|
|
|
|
test("modifier hover and repeated clicks share results; editing Java invalidates them", async ({ page }, info) => {
|
|
const errors: string[] = [];
|
|
page.on("pageerror", (e) => errors.push(e.message));
|
|
await open(page);
|
|
const key = await keyPosition(page);
|
|
await page.keyboard.down("Meta");
|
|
await page.mouse.move(key.x, key.y);
|
|
await expect(page.locator(".cm-lsp-link")).toHaveText("driver-class-name");
|
|
await expect(page.locator(".cm-lsp-link")).toHaveCSS("cursor", "pointer");
|
|
await expect(page.locator(".cm-lsp-link")).toHaveCSS("color", "rgb(33, 92, 200)");
|
|
expect((await metrics(page)).hoverRequests).toBe(0);
|
|
await expect(page.locator(".cm-lsp-nav-info")).toHaveCount(0);
|
|
await page.screenshot({ path: info.outputPath("configuration-link.png") });
|
|
await page.mouse.click(key.x, key.y);
|
|
await expect(page.locator("#opened")).toHaveText("/fixture/Settings.java:0");
|
|
await page.keyboard.up("Meta");
|
|
for (let i = 0; i < 10; i++) {
|
|
await page.getByRole("button", { name: "Back to configuration" }).click();
|
|
await page.keyboard.down("Meta");
|
|
await page.mouse.click(key.x, key.y);
|
|
await expect(page.locator("#java")).toBeVisible();
|
|
await page.keyboard.up("Meta");
|
|
}
|
|
const warm = await metrics(page);
|
|
expect(warm.requests).toBe(1);
|
|
expect(warm.latency).toHaveLength(11);
|
|
const evidence = info.outputPath("warm-navigation.json");
|
|
await writeFile(evidence, JSON.stringify(warm, null, 2));
|
|
await info.attach("warm-navigation.json", { path: evidence, contentType: "application/json" });
|
|
await page.getByRole("button", { name: "Edit Java target" }).click();
|
|
await page.getByRole("button", { name: "Back to configuration" }).click();
|
|
await page.keyboard.down("Meta");
|
|
await page.mouse.click(key.x, key.y);
|
|
await expect(page.locator("#opened")).toHaveText("/fixture/Settings.java:1");
|
|
expect((await metrics(page)).requests).toBe(2);
|
|
expect(errors).toEqual([]);
|
|
});
|
|
|
|
test("clicking before the hover query completes shares the in-flight request", async ({ page }, info) => {
|
|
await open(page);
|
|
const key = await keyPosition(page);
|
|
await page.keyboard.down("Meta");
|
|
await page.mouse.move(key.x, key.y);
|
|
await expect(page.locator("#requests")).toHaveText("1");
|
|
await page.mouse.click(key.x, key.y);
|
|
await expect(page.locator("#opened")).toHaveText("/fixture/Settings.java:0");
|
|
const result = await metrics(page);
|
|
expect(result.requests).toBe(1);
|
|
await info.attach("inflight-navigation.json", {
|
|
body: JSON.stringify(result, null, 2),
|
|
contentType: "application/json",
|
|
});
|
|
});
|
|
|
|
test("ordinary hover still shows configuration documentation", async ({ page }) => {
|
|
await open(page);
|
|
const key = await keyPosition(page);
|
|
await page.mouse.move(key.x, key.y);
|
|
await expect(page.locator(".cm-lsp-hover")).toContainText("dingtalk.driver-class-name String");
|
|
expect((await metrics(page)).requests).toBe(0);
|
|
expect((await metrics(page)).hoverRequests).toBeGreaterThan(0);
|
|
});
|