refactor(grid): extract table info panels

This commit is contained in:
t8y2 committed 2026-09-08 22:53:23 +08:00
1 parent cd6e0d6e92
commit 2e0d003538
3 files changed
+249 -180

No files matched your search

+26 -176
View File
@@ -78,13 +78,13 @@ import DataGridQueryControls from "@/components/grid/DataGridQueryControls.vue";
import DataGridFilterBuilder from "@/components/grid/DataGridFilterBuilder.vue";
import DataGridFilterWorkbench from "@/components/grid/DataGridFilterWorkbench.vue";
import DataGridTextFilterWorkbench from "@/components/grid/DataGridTextFilterWorkbench.vue";
import DataGridTableInfoPanels from "@/components/grid/DataGridTableInfoPanels.vue";
import TemporalCellEditor from "@/components/grid/TemporalCellEditor.vue";
import EnumCellEditor from "@/components/grid/EnumCellEditor.vue";
import DataGridReadonlyTextSelection from "@/components/grid/DataGridReadonlyTextSelection.vue";
import GridSnapshotDialog from "@/components/grid/GridSnapshotDialog.vue";
import type { QueryResult, ColumnInfo, ConstraintInfo, DatabaseType, ForeignKeyInfo, IndexInfo, TriggerInfo, TableInfoTab, QueryResultSourceColumnRef, QueryPageJumpProgress } from "@/types/database";
import { isQueryExecutionErrorResult } from "@/lib/query/queryResultError";
import { tableColumnDefaultDisplayValue } from "@/lib/table/tableColumnDefaultPresentation";
import { shouldNavigateFromTableInfoColumnClick } from "@/lib/table/tableInfoColumnNavigation";
import { tableInfoTabForDrawerToggle } from "@/lib/table/tableInfoTabPreference";
import * as api from "@/lib/backend/api";
@@ -13697,181 +13697,31 @@ function openGridSnapshot() {
</div>
</div>
<div v-if="activeTableInfoTab === 'columns'" class="flex-1 min-h-0 overflow-auto">
<div v-if="tableInfoColumnsLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="searchQuery && filteredColumns.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<table v-else class="w-full text-xs">
<thead class="sticky top-0 bg-muted text-muted-foreground">
<tr class="border-b">
<th class="text-left text-nowrap font-medium px-3 py-2 w-8">#</th>
<th class="text-left text-nowrap font-medium px-3 py-2">
{{ t("grid.columnName") }}
</th>
<th class="text-left text-nowrap font-medium px-3 py-2">
{{ t("grid.columnType") }}
</th>
<th class="text-left text-nowrap font-medium px-3 py-2">
{{ t("grid.tableInfoNullable") }}
</th>
<th class="text-left text-nowrap font-medium px-3 py-2">
{{ t("structureEditor.defaultValue") }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(column, index) in filteredColumns"
:key="column.name"
class="border-b cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-800/30"
role="button"
tabindex="0"
:title="column.name"
@click="onTableInfoColumnClick(column.name)"
@keydown.enter.prevent="scrollToTableInfoColumn(column.name)"
@keydown.space.prevent="scrollToTableInfoColumn(column.name)"
>
<td class="px-3 py-2 text-muted-foreground w-8">
{{ index + 1 }}
</td>
<td class="cursor-text select-text px-3 py-2 font-medium">
<span class="inline-flex items-center gap-1.5">
<KeyRound v-if="column.is_primary_key" class="h-3 w-3 text-amber-500" />
{{ column.name }}
</span>
<div v-if="column.comment" class="mt-0.5 text-[11px] text-muted-foreground truncate">
{{ column.comment }}
</div>
</td>
<td class="px-3 py-2 font-mono text-[11px] text-muted-foreground">
{{ gaussdbMColumnType(column.data_type) }}
</td>
<td class="px-3 py-2">
{{ column.is_nullable ? "YES" : "NO" }}
</td>
<td
data-table-info-column-default
class="max-w-56 px-3 py-2 font-mono text-[11px]"
:class="{
'text-muted-foreground/70': column.column_default == null,
}"
:title="column.column_default ?? undefined"
>
<span class="block max-w-56 truncate">{{ tableColumnDefaultDisplayValue(column.column_default) }}</span>
</td>
</tr>
</tbody>
</table>
</div>
<div v-else-if="activeTableInfoTab === 'indexes'" class="flex-1 min-h-0 overflow-auto">
<div v-if="indexesLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="indexesError" class="p-3 text-xs text-destructive">
{{ indexesError }}
</div>
<div v-else-if="searchQuery && filteredIndexes.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<div v-else-if="indexes.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoEmpty") }}
</div>
<div v-else class="divide-y">
<div v-for="index in filteredIndexes" :key="index.name" class="p-3 text-xs">
<div class="flex items-start gap-2">
<div class="min-w-0 flex-1">
<div class="font-medium truncate">{{ index.name }}</div>
<div class="mt-1 flex flex-wrap gap-1">
<span v-if="index.is_primary" class="rounded bg-amber-500/10 px-1.5 py-0.5 text-amber-600">PK</span>
<span v-if="index.is_unique" class="rounded bg-emerald-500/10 px-1.5 py-0.5 text-emerald-600">UNIQUE</span>
<span v-if="index.index_type" class="rounded bg-muted px-1.5 py-0.5 text-muted-foreground">{{ index.index_type }}</span>
</div>
<div class="mt-2 font-mono text-[11px] text-muted-foreground break-all">
{{ index.columns.join(", ") }}
</div>
</div>
<Button v-if="canManageMongoIndexes && !isProtectedMongoIndex(index)" variant="ghost" size="sm" class="h-7 shrink-0 px-2 text-[11px] text-destructive hover:text-destructive" @click="requestDropMongoIndex(index)">
<Trash2 class="mr-1 h-3 w-3" />
{{ t("contextMenu.dropIndex") }}
</Button>
</div>
</div>
</div>
</div>
<div v-else-if="activeTableInfoTab === 'foreignKeys'" class="flex-1 min-h-0 overflow-auto">
<div v-if="foreignKeysLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="foreignKeysError" class="p-3 text-xs text-destructive">
{{ foreignKeysError }}
</div>
<div v-else-if="searchQuery && filteredForeignKeys.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<div v-else-if="foreignKeys.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoEmpty") }}
</div>
<div v-else class="divide-y">
<div v-for="fk in filteredForeignKeys" :key="`${fk.name}:${fk.column}`" class="p-3 text-xs">
<div class="font-medium truncate">{{ fk.name }}</div>
<div class="mt-1 font-mono text-[11px] text-muted-foreground break-all">{{ fk.column }} -> {{ fk.ref_table }}.{{ fk.ref_column }}</div>
</div>
</div>
</div>
<div v-else-if="activeTableInfoTab === 'triggers'" class="flex-1 min-h-0 overflow-auto">
<div v-if="triggersLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="triggersError" class="p-3 text-xs text-destructive">
{{ triggersError }}
</div>
<div v-else-if="searchQuery && filteredTriggers.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<div v-else-if="triggers.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoEmpty") }}
</div>
<div v-else class="divide-y">
<div v-for="trigger in filteredTriggers" :key="trigger.name" class="p-3 text-xs">
<div class="font-medium truncate">{{ trigger.name }}</div>
<div class="mt-1 text-[11px] text-muted-foreground">{{ trigger.timing }} {{ trigger.event }}</div>
</div>
</div>
</div>
<div v-else-if="activeTableInfoTab === 'constraints'" class="flex-1 min-h-0 overflow-auto">
<div v-if="constraintsLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="constraintsError" class="p-3 text-xs text-destructive">
{{ constraintsError }}
</div>
<div v-else-if="searchQuery && filteredConstraints.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<div v-else-if="constraintsForTab.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoEmpty") }}
</div>
<div v-else class="divide-y">
<div v-for="constraint in filteredConstraints" :key="constraint.name" class="p-3 text-xs" :class="constraint.enabled ? '' : 'opacity-60'">
<div class="flex flex-wrap items-center gap-1.5">
<span class="font-medium truncate">{{ constraint.name }}</span>
<span class="rounded border px-1 py-px text-[10px] text-muted-foreground">{{ constraint.constraint_type }}</span>
<span v-if="!constraint.enabled" class="rounded border px-1 py-px text-[10px] text-muted-foreground">{{ t("grid.tableInfoConstraintDisabled") }}</span>
<span v-else-if="!constraint.valid" class="rounded border px-1 py-px text-[10px] text-muted-foreground">{{ t("grid.tableInfoConstraintNotValidated") }}</span>
</div>
<div v-if="constraint.columns.length" class="mt-1 font-mono text-[11px] text-muted-foreground break-all">{{ constraint.columns.join(", ") }}</div>
<div v-if="constraint.ref_table" class="mt-1 font-mono text-[11px] text-muted-foreground break-all">-> {{ constraint.ref_schema ? `${constraint.ref_schema}.` : "" }}{{ constraint.ref_table }}{{ constraint.ref_columns.length ? `(${constraint.ref_columns.join(", ")})` : "" }}</div>
<div v-if="constraint.definition" class="mt-1 font-mono text-[11px] text-muted-foreground break-all whitespace-pre-wrap">{{ constraint.definition }}</div>
</div>
</div>
</div>
<DataGridTableInfoPanels
v-if="activeTableInfoTab !== 'ddl'"
:active-tab="activeTableInfoTab"
:search-query="searchQuery"
:columns="filteredColumns"
:columns-loading="tableInfoColumnsLoading"
:indexes="filteredIndexes"
:indexes-loading="indexesLoading"
:indexes-error="indexesError"
:can-manage-mongo-indexes="canManageMongoIndexes"
:foreign-keys="filteredForeignKeys"
:foreign-keys-loading="foreignKeysLoading"
:foreign-keys-error="foreignKeysError"
:triggers="filteredTriggers"
:triggers-loading="triggersLoading"
:triggers-error="triggersError"
:constraints="filteredConstraints"
:constraints-loading="constraintsLoading"
:constraints-error="constraintsError"
:is-protected-mongo-index="isProtectedMongoIndex"
:format-column-type="gaussdbMColumnType"
@table-info-column-click="onTableInfoColumnClick"
@scroll-to-table-info-column="scrollToTableInfoColumn"
@request-drop-mongo-index="requestDropMongoIndex"
/>
<pre
v-else-if="activeTableInfoTab === 'ddl' && !ddlLoading"
@@ -0,0 +1,217 @@
<script setup lang="ts">
import { KeyRound, Loader2, Trash2 } from "@lucide/vue";
import { useI18n } from "vue-i18n";
import { Button } from "@/components/ui/button";
import { tableColumnDefaultDisplayValue } from "@/lib/table/tableColumnDefaultPresentation";
import type { ColumnInfo, ConstraintInfo, ForeignKeyInfo, IndexInfo, TableInfoTab, TriggerInfo } from "@/types/database";
interface DataGridTableInfoPanelsProps {
activeTab: TableInfoTab;
searchQuery: string;
columns: ColumnInfo[];
columnsLoading: boolean;
indexes: IndexInfo[];
indexesLoading: boolean;
indexesError: string;
canManageMongoIndexes: boolean;
foreignKeys: ForeignKeyInfo[];
foreignKeysLoading: boolean;
foreignKeysError: string;
triggers: TriggerInfo[];
triggersLoading: boolean;
triggersError: string;
constraints: ConstraintInfo[];
constraintsLoading: boolean;
constraintsError: string;
isProtectedMongoIndex: (index: IndexInfo) => boolean;
formatColumnType: (dataType: string) => string;
}
const props = defineProps<DataGridTableInfoPanelsProps>();
const emit = defineEmits<{
tableInfoColumnClick: [columnName: string];
scrollToTableInfoColumn: [columnName: string];
requestDropMongoIndex: [index: IndexInfo];
}>();
const { t } = useI18n();
</script>
<template>
<div v-if="props.activeTab === 'columns'" class="flex-1 min-h-0 overflow-auto">
<div v-if="props.columnsLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="props.searchQuery && props.columns.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<table v-else class="w-full text-xs">
<thead class="sticky top-0 bg-muted text-muted-foreground">
<tr class="border-b">
<th class="text-left text-nowrap font-medium px-3 py-2 w-8">#</th>
<th class="text-left text-nowrap font-medium px-3 py-2">
{{ t("grid.columnName") }}
</th>
<th class="text-left text-nowrap font-medium px-3 py-2">
{{ t("grid.columnType") }}
</th>
<th class="text-left text-nowrap font-medium px-3 py-2">
{{ t("grid.tableInfoNullable") }}
</th>
<th class="text-left text-nowrap font-medium px-3 py-2">
{{ t("structureEditor.defaultValue") }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="(column, index) in props.columns"
:key="column.name"
class="border-b cursor-pointer hover:bg-gray-200 dark:hover:bg-gray-800/30"
role="button"
tabindex="0"
:title="column.name"
@click="emit('tableInfoColumnClick', column.name)"
@keydown.enter.prevent="emit('scrollToTableInfoColumn', column.name)"
@keydown.space.prevent="emit('scrollToTableInfoColumn', column.name)"
>
<td class="px-3 py-2 text-muted-foreground w-8">
{{ index + 1 }}
</td>
<td class="cursor-text select-text px-3 py-2 font-medium">
<span class="inline-flex items-center gap-1.5">
<KeyRound v-if="column.is_primary_key" class="h-3 w-3 text-amber-500" />
{{ column.name }}
</span>
<div v-if="column.comment" class="mt-0.5 text-[11px] text-muted-foreground truncate">
{{ column.comment }}
</div>
</td>
<td class="px-3 py-2 font-mono text-[11px] text-muted-foreground">
{{ props.formatColumnType(column.data_type) }}
</td>
<td class="px-3 py-2">
{{ column.is_nullable ? "YES" : "NO" }}
</td>
<td
data-table-info-column-default
class="max-w-56 px-3 py-2 font-mono text-[11px]"
:class="{
'text-muted-foreground/70': column.column_default == null,
}"
:title="column.column_default ?? undefined"
>
<span class="block max-w-56 truncate">{{ tableColumnDefaultDisplayValue(column.column_default) }}</span>
</td>
</tr>
</tbody>
</table>
</div>
<div v-else-if="props.activeTab === 'indexes'" class="flex-1 min-h-0 overflow-auto">
<div v-if="props.indexesLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="props.indexesError" class="p-3 text-xs text-destructive">
{{ props.indexesError }}
</div>
<div v-else-if="props.searchQuery && props.indexes.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<div v-else-if="props.indexes.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoEmpty") }}
</div>
<div v-else class="divide-y">
<div v-for="index in props.indexes" :key="index.name" class="p-3 text-xs">
<div class="flex items-start gap-2">
<div class="min-w-0 flex-1">
<div class="font-medium truncate">{{ index.name }}</div>
<div class="mt-1 flex flex-wrap gap-1">
<span v-if="index.is_primary" class="rounded bg-amber-500/10 px-1.5 py-0.5 text-amber-600">PK</span>
<span v-if="index.is_unique" class="rounded bg-emerald-500/10 px-1.5 py-0.5 text-emerald-600">UNIQUE</span>
<span v-if="index.index_type" class="rounded bg-muted px-1.5 py-0.5 text-muted-foreground">{{ index.index_type }}</span>
</div>
<div class="mt-2 font-mono text-[11px] text-muted-foreground break-all">
{{ index.columns.join(", ") }}
</div>
</div>
<Button v-if="props.canManageMongoIndexes && !props.isProtectedMongoIndex(index)" variant="ghost" size="sm" class="h-7 shrink-0 px-2 text-[11px] text-destructive hover:text-destructive" @click="emit('requestDropMongoIndex', index)">
<Trash2 class="mr-1 h-3 w-3" />
{{ t("contextMenu.dropIndex") }}
</Button>
</div>
</div>
</div>
</div>
<div v-else-if="props.activeTab === 'foreignKeys'" class="flex-1 min-h-0 overflow-auto">
<div v-if="props.foreignKeysLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="props.foreignKeysError" class="p-3 text-xs text-destructive">
{{ props.foreignKeysError }}
</div>
<div v-else-if="props.searchQuery && props.foreignKeys.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<div v-else-if="props.foreignKeys.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoEmpty") }}
</div>
<div v-else class="divide-y">
<div v-for="foreignKey in props.foreignKeys" :key="`${foreignKey.name}:${foreignKey.column}`" class="p-3 text-xs">
<div class="font-medium truncate">{{ foreignKey.name }}</div>
<div class="mt-1 font-mono text-[11px] text-muted-foreground break-all">{{ foreignKey.column }} -> {{ foreignKey.ref_table }}.{{ foreignKey.ref_column }}</div>
</div>
</div>
</div>
<div v-else-if="props.activeTab === 'triggers'" class="flex-1 min-h-0 overflow-auto">
<div v-if="props.triggersLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="props.triggersError" class="p-3 text-xs text-destructive">
{{ props.triggersError }}
</div>
<div v-else-if="props.searchQuery && props.triggers.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<div v-else-if="props.triggers.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoEmpty") }}
</div>
<div v-else class="divide-y">
<div v-for="trigger in props.triggers" :key="trigger.name" class="p-3 text-xs">
<div class="font-medium truncate">{{ trigger.name }}</div>
<div class="mt-1 text-[11px] text-muted-foreground">{{ trigger.timing }} {{ trigger.event }}</div>
</div>
</div>
</div>
<div v-else-if="props.activeTab === 'constraints'" class="flex-1 min-h-0 overflow-auto">
<div v-if="props.constraintsLoading" class="h-full flex items-center justify-center">
<Loader2 class="w-4 h-4 animate-spin text-muted-foreground" />
</div>
<div v-else-if="props.constraintsError" class="p-3 text-xs text-destructive">
{{ props.constraintsError }}
</div>
<div v-else-if="props.searchQuery && props.constraints.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoNoResults") }}
</div>
<div v-else-if="props.constraints.length === 0" class="p-6 text-center text-xs text-muted-foreground">
{{ t("grid.tableInfoEmpty") }}
</div>
<div v-else class="divide-y">
<div v-for="constraint in props.constraints" :key="constraint.name" class="p-3 text-xs" :class="constraint.enabled ? '' : 'opacity-60'">
<div class="flex flex-wrap items-center gap-1.5">
<span class="font-medium truncate">{{ constraint.name }}</span>
<span class="rounded border px-1 py-px text-[10px] text-muted-foreground">{{ constraint.constraint_type }}</span>
<span v-if="!constraint.enabled" class="rounded border px-1 py-px text-[10px] text-muted-foreground">{{ t("grid.tableInfoConstraintDisabled") }}</span>
<span v-else-if="!constraint.valid" class="rounded border px-1 py-px text-[10px] text-muted-foreground">{{ t("grid.tableInfoConstraintNotValidated") }}</span>
</div>
<div v-if="constraint.columns.length" class="mt-1 font-mono text-[11px] text-muted-foreground break-all">{{ constraint.columns.join(", ") }}</div>
<div v-if="constraint.ref_table" class="mt-1 font-mono text-[11px] text-muted-foreground break-all">-> {{ constraint.ref_schema ? `${constraint.ref_schema}.` : "" }}{{ constraint.ref_table }}{{ constraint.ref_columns.length ? `(${constraint.ref_columns.join(", ")})` : "" }}</div>
<div v-if="constraint.definition" class="mt-1 font-mono text-[11px] text-muted-foreground break-all whitespace-pre-wrap">{{ constraint.definition }}</div>
</div>
</div>
</div>
</template>
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
import { describe, expect, it } from "vitest";
const dataGridSource = readFileSync(new URL("../DataGrid.vue", import.meta.url), "utf8");
const metadataLoaderSource = readFileSync(new URL("../../../composables/useDataGridTableMetadataLoaders.ts", import.meta.url), "utf8");
describe("DataGrid DDL search navigation", () => {
it("resets navigation when the raw search query changes", () => {
@@ -9,12 +10,13 @@ describe("DataGrid DDL search navigation", () => {
});
it("uses the shared DDL cache policy and exposes refresh controls", () => {
expect(dataGridSource).toContain('from "@/lib/metadata/objectDdlCache"');
expect(dataGridSource).toContain("async function fetchDdl(force = settingsStore.editorSettings.refreshDdlOnOpen)");
expect(dataGridSource).toMatch(/loadObjectDdl\([\s\S]*?objectType: tableObjectSourceKind\(props\.tableMeta\.tableType\),[\s\S]*?\{ force \},/);
expect(metadataLoaderSource).toContain('from "@/lib/metadata/objectDdlCache"');
expect(metadataLoaderSource).toContain("async function fetchDdl(force = options.settingsStore.editorSettings.refreshDdlOnOpen)");
expect(metadataLoaderSource).toMatch(/objectType: tableObjectSourceKind\(props\.tableMeta\.tableType\)/);
expect(metadataLoaderSource).toContain("loadObjectDdl(request, { force })");
expect(dataGridSource).toMatch(/async function refreshActiveTableInfo\(\)[\s\S]*?fetchDdl\(true\)[\s\S]*?fetchTableInfoColumns\(true\)[\s\S]*?fetchIndexes\(\)[\s\S]*?fetchForeignKeys\(\)[\s\S]*?fetchConstraints\(\)[\s\S]*?fetchTriggers\(\)/);
expect(dataGridSource).toContain('@click="refreshActiveTableInfo"');
expect(dataGridSource).toMatch(/loadObjectMetadataFacet\(request, "columns"/);
expect(metadataLoaderSource).toMatch(/loadObjectMetadataFacet\(request, "columns"/);
expect(dataGridSource).toMatch(/activeTableInfoTab\.value === "constraints"\) return constraintsLoading\.value/);
expect(dataGridSource).not.toContain("setTableInfoRefreshDdlOnOpen");
});