fix(tests): remove low-value source snapshots and define test standards
This commit is contained in:
80 files changed
+14
-2225
No files matched your search
-18
@@ -1,18 +0,0 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const source = readFileSync(new URL("../AiAssistant.vue", import.meta.url), "utf8");
|
||||
|
||||
describe("AI assistant explain_query canvas containment", () => {
|
||||
it("bounds the embedded ExplainPlanViewer to a fixed, clipped height", () => {
|
||||
const match = source.match(/<div v-if="step\.toolName === 'explain_query' && step\.explainData[^>]*class="([^"]*)"/);
|
||||
expect(match, "explain_query wrapper div not found").not.toBeNull();
|
||||
const wrapperClass = match![1];
|
||||
expect(wrapperClass).toContain("h-64");
|
||||
expect(wrapperClass).toContain("overflow-hidden");
|
||||
});
|
||||
|
||||
it("does not rely on an unresolvable max-height alone to contain the canvas", () => {
|
||||
expect(source).not.toContain('<ExplainPlanViewer :plan="parseExplainFromData(step.explainData, connection.db_type)" class="max-h-64" />');
|
||||
});
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const source = readFileSync(new URL("../AiAssistant.vue", import.meta.url), "utf8");
|
||||
|
||||
describe("AI assistant maximize control", () => {
|
||||
it("exposes a parent-controlled maximize toggle with accessible labels", () => {
|
||||
expect(source).toContain("maximized?: boolean;");
|
||||
expect(source).toContain("toggleMaximize: [];");
|
||||
expect(source).toContain(":title=\"props.maximized ? t('ai.restore') : t('ai.maximize')\"");
|
||||
expect(source).toContain(":aria-label=\"props.maximized ? t('ai.restore') : t('ai.maximize')\"");
|
||||
expect(source).toContain(':aria-pressed="props.maximized"');
|
||||
expect(source).toContain("@click=\"emit('toggleMaximize')\"");
|
||||
expect(source).toContain('<Minimize2 v-if="props.maximized"');
|
||||
expect(source).toContain("<Maximize2 v-else");
|
||||
});
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const source = readFileSync(new URL("../AiAssistant.vue", import.meta.url), "utf8");
|
||||
|
||||
describe("AI assistant message copy actions", () => {
|
||||
it("exposes copy actions below user and assistant messages", () => {
|
||||
expect(source).toContain('data-ai-message-copy="user"');
|
||||
expect(source).toContain('data-ai-message-copy="assistant"');
|
||||
});
|
||||
|
||||
it("routes message copies through the body-only copy contract", () => {
|
||||
expect(source).toContain("resolveAiMessageCopyText(msg, isStreamingMessage(msg))");
|
||||
expect(source).toContain("copyAiContent(text, `message:${index}`)");
|
||||
});
|
||||
|
||||
it("keeps code-block and message feedback keys separate", () => {
|
||||
expect(source).toContain("copyAiContent(seg.content, `code:${i}:${j}`)");
|
||||
expect(source).toContain("messageCopyKey(i)");
|
||||
});
|
||||
});
|
||||
-120
@@ -1,120 +0,0 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const dialogSource = readFileSync(new URL("../EditorSettingsDialog.vue", import.meta.url), "utf8");
|
||||
const templateSource = dialogSource.slice(dialogSource.indexOf("<template>"));
|
||||
const styleSource = dialogSource.slice(dialogSource.indexOf("<style>"), dialogSource.indexOf("</style>"));
|
||||
|
||||
function sourceIndexForKey(key: string): number {
|
||||
const index = templateSource.indexOf(`t("settings.${key}")`);
|
||||
if (index < 0) throw new Error(`Missing settings key: ${key}`);
|
||||
return index;
|
||||
}
|
||||
|
||||
function tagEnd(source: string, start: number): number {
|
||||
let quote: '"' | "'" | null = null;
|
||||
for (let index = start; index < source.length; index += 1) {
|
||||
const char = source[index];
|
||||
if (quote) {
|
||||
if (char === quote && source[index - 1] !== "\\") quote = null;
|
||||
} else if (char === '"' || char === "'") {
|
||||
quote = char;
|
||||
} else if (char === ">") {
|
||||
return index;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function borderedSettingDivForKey(key: string): string {
|
||||
const keyIndex = sourceIndexForKey(key);
|
||||
let start = templateSource.lastIndexOf("<div", keyIndex);
|
||||
while (start >= 0) {
|
||||
const end = tagEnd(templateSource, start);
|
||||
const tag = templateSource.slice(start, end + 1);
|
||||
if (tag.includes("rounded") && tag.includes("border")) return tag;
|
||||
start = templateSource.lastIndexOf("<div", start - 1);
|
||||
}
|
||||
throw new Error(`Missing bordered setting div for settings key: ${key}`);
|
||||
}
|
||||
|
||||
function divTagForAttribute(attribute: string): string {
|
||||
const index = templateSource.indexOf(attribute);
|
||||
if (index < 0) throw new Error(`Missing template attribute: ${attribute}`);
|
||||
let start = templateSource.lastIndexOf("<div", index);
|
||||
while (start >= 0) {
|
||||
const end = tagEnd(templateSource, start);
|
||||
if (end >= index) return templateSource.slice(start, end + 1);
|
||||
start = templateSource.lastIndexOf("<div", start - 1);
|
||||
}
|
||||
throw new Error(`Missing div for template attribute: ${attribute}`);
|
||||
}
|
||||
|
||||
function settingItemDivForText(text: string): string {
|
||||
const index = templateSource.indexOf(text);
|
||||
if (index < 0) throw new Error(`Missing template text: ${text}`);
|
||||
let start = templateSource.lastIndexOf("<div", index);
|
||||
while (start >= 0) {
|
||||
const end = tagEnd(templateSource, start);
|
||||
const tag = templateSource.slice(start, end + 1);
|
||||
if (tag.includes("settings-item")) return tag;
|
||||
start = templateSource.lastIndexOf("<div", start - 1);
|
||||
}
|
||||
throw new Error(`Missing settings item for template text: ${text}`);
|
||||
}
|
||||
|
||||
function borderedDivForText(text: string): string {
|
||||
const index = templateSource.indexOf(text);
|
||||
if (index < 0) throw new Error(`Missing template text: ${text}`);
|
||||
let start = templateSource.lastIndexOf("<div", index);
|
||||
while (start >= 0) {
|
||||
const end = tagEnd(templateSource, start);
|
||||
const tag = templateSource.slice(start, end + 1);
|
||||
if (tag.includes("rounded") && tag.includes("border")) return tag;
|
||||
start = templateSource.lastIndexOf("<div", start - 1);
|
||||
}
|
||||
throw new Error(`Missing bordered div for template text: ${text}`);
|
||||
}
|
||||
|
||||
describe("EditorSettingsDialog standard item hover feedback", () => {
|
||||
it("marks representative editor, navigation, data, toolbar, and update setting rows for the shared hover treatment", () => {
|
||||
for (const key of ["showStatementRunButtons", "externalSqlEditorMaxMb", "dataGridTypeColorScheme", "exclusiveRightSidebarPanels", "updateDownloadSource"] as const) {
|
||||
expect(borderedSettingDivForKey(key)).toContain("settings-item");
|
||||
}
|
||||
});
|
||||
|
||||
it("keeps hover feedback off rows while their controls are disabled", () => {
|
||||
expect(settingItemDivForText('id="web-sql-file-upload-max-mb"')).toContain("'settings-item-disabled': !webSqlFileUploadMaxMbLoaded || webSqlFileUploadMaxMbLoading");
|
||||
expect(settingItemDivForText('id="mcp-http-enabled"')).toContain("'settings-item-disabled': mcpHttpLoading || mcpHttpSaving");
|
||||
expect(styleSource).toMatch(/\.settings-item:not\(\.settings-item-disabled\):not\(\.opacity-50\):hover/);
|
||||
});
|
||||
|
||||
it("provides legacy color fallbacks before progressively enhancing hover colors", () => {
|
||||
const fallbackStart = styleSource.indexOf(".settings-item:not(.settings-item-disabled):not(.opacity-50):hover");
|
||||
const enhancementStart = styleSource.indexOf("@supports (background: color-mix(in oklab, black, white))");
|
||||
expect(fallbackStart).toBeGreaterThan(-1);
|
||||
expect(enhancementStart).toBeGreaterThan(fallbackStart);
|
||||
expect(styleSource.slice(fallbackStart, enhancementStart)).toContain("background-color: var(--muted);");
|
||||
expect(styleSource.slice(fallbackStart, enhancementStart)).toContain("border-color: var(--muted-foreground);");
|
||||
expect(styleSource.slice(enhancementStart)).toContain("color-mix(in oklab");
|
||||
});
|
||||
|
||||
it("limits shared hover feedback to independent rows instead of composite and informational containers", () => {
|
||||
expect(divTagForAttribute('data-settings-search-id="data-grid-filter-view"')).not.toContain("settings-item");
|
||||
expect(borderedSettingDivForKey("dataGridKeepFilterEditorExpanded")).toContain("settings-item");
|
||||
|
||||
for (const text of ['t("settings.mcpHttpWebServiceTitle")', 't("settings.mcpHttpUnsavedChangesHint")', "{{ mcpStatus.bin_path }}", 't("settings.mcpCursorConfigPath")', 't("settings.mcpQoderConfigPath")', 't("settings.supportInfoTitle")'] as const) {
|
||||
expect(borderedDivForText(text)).not.toContain("settings-item");
|
||||
}
|
||||
});
|
||||
|
||||
it("uses direct selectors without styling nested descendants", () => {
|
||||
expect(styleSource).not.toContain(".settings-item *");
|
||||
expect(styleSource).not.toContain(".settings-item:hover *");
|
||||
});
|
||||
|
||||
it("leaves choice cards and shortcut rows on their existing interaction styles", () => {
|
||||
expect(borderedSettingDivForKey("showStatementRunButtons")).not.toContain("settings-choice-card");
|
||||
expect(templateSource).toContain("settings-shortcut-row group");
|
||||
});
|
||||
});
|
||||
@@ -1,23 +0,0 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
// reka-ui's SelectItem throws on value="" (SelectItem.js: "A <SelectItem /> must
|
||||
// have a value prop that is not an empty string"). The throw happens while the
|
||||
// select's dismissable layer is up, and the leftover layer then blocks every
|
||||
// click in the app — dialogs can no longer be closed. These dialogs use
|
||||
// sentinel values instead; keep it that way.
|
||||
const multiDbDialogSource = readFileSync(resolve(process.cwd(), "apps/desktop/src/components/editor/MultiDbExecuteDialog.vue"), "utf8");
|
||||
const tagPanelSource = readFileSync(resolve(process.cwd(), "apps/desktop/src/components/config/TagManagementPanel.vue"), "utf8");
|
||||
|
||||
describe("Select empty-value regression guard", () => {
|
||||
it("keeps the multi-db target-group select free of empty SelectItem values", () => {
|
||||
expect(multiDbDialogSource).not.toMatch(/<SelectItem\s+value=""/);
|
||||
expect(multiDbDialogSource).toContain("NO_GROUP_VALUE");
|
||||
});
|
||||
|
||||
it("keeps the tag environment select free of empty SelectItem values", () => {
|
||||
expect(tagPanelSource).not.toMatch(/<SelectItem\s+value=""/);
|
||||
expect(tagPanelSource).toContain("ALL_ENVIRONMENTS");
|
||||
});
|
||||
});
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const dialogSource = readFileSync(new URL("../MultiDbExecuteDialog.vue", import.meta.url), "utf8");
|
||||
|
||||
describe("MultiDbExecuteDialog footer layout", () => {
|
||||
it("keeps the target group manager close button aligned with the main dialog footer", () => {
|
||||
expect(dialogSource).toContain('<DialogFooter class="mx-0 mb-0 shrink-0 border-t px-5 py-3">\n <Button variant="outline" @click="manageGroups = false">');
|
||||
});
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const appSource = readFileSync(new URL("../../../App.vue", import.meta.url), "utf8");
|
||||
const composableSource = readFileSync(new URL("../../../composables/useBackgroundImage.ts", import.meta.url), "utf8");
|
||||
const globalsCss = readFileSync(new URL("../../../../src/styles/globals.css", import.meta.url), "utf8");
|
||||
const queryEditorSource = readFileSync(new URL("../QueryEditor.vue", import.meta.url), "utf8");
|
||||
const welcomeScreenSource = readFileSync(new URL("../../layout/WelcomeScreen.vue", import.meta.url), "utf8");
|
||||
|
||||
describe("global background image wiring", () => {
|
||||
it("mounts a single app-level background layer behind all content", () => {
|
||||
expect(appSource).toContain("data-app-background");
|
||||
expect(appSource).toMatch(/pointer-events-none fixed inset-0 -z-10/);
|
||||
expect(appSource).toContain("useBackgroundImage(settingsStore)");
|
||||
});
|
||||
|
||||
it("toggles the wallpaper class on <html> and clears CodeMirror backgrounds app-wide", () => {
|
||||
expect(composableSource).toContain('BACKGROUND_IMAGE_ACTIVE_CLASS = "dbx-bg-active"');
|
||||
expect(globalsCss).toContain("html.dbx-bg-active .cm-editor");
|
||||
expect(globalsCss).toContain("html.dbx-bg-active .cm-gutters");
|
||||
});
|
||||
|
||||
it("keeps card and popover surfaces opaque (readability contract)", () => {
|
||||
expect(composableSource).toContain("BACKGROUND_IMAGE_SURFACE_VARS");
|
||||
const appBackground = readFileSync(new URL("../../../lib/app/appBackgroundImage.ts", import.meta.url), "utf8");
|
||||
expect(appBackground).toContain('"--background"');
|
||||
expect(appBackground).toContain('"--sidebar"');
|
||||
expect(appBackground).not.toMatch(/"--card"|"--popover"/);
|
||||
});
|
||||
|
||||
it("removed the scoped per-area mode (editor/welcome no longer render their own layers)", () => {
|
||||
for (const source of [queryEditorSource, welcomeScreenSource]) {
|
||||
expect(source).not.toContain("useBackgroundImage");
|
||||
expect(source).not.toContain("data-query-editor-bg");
|
||||
expect(source).not.toContain("data-welcome-bg");
|
||||
}
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user