Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[studio] fix table rows indexes #1485 #1487

Merged
merged 1 commit into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions agdb_studio/src/components/base/table/AgdbTableHeader.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
import { shallowMount } from "@vue/test-utils";
import AgdbTableHeader from "./AgdbTableHeader.vue";
import { describe, it, expect } from "vitest";
import { INJECT_KEY_TABLE_NAME } from "@/composables/table/constants";
import { TABLE_NAME } from "@/tests/tableMocks";

describe("TableHeader", () => {
it("should render", () => {
const wrapper = shallowMount(AgdbTableHeader, {
props: {
tableKey: "table",
},
global: {
provide: {
[INJECT_KEY_TABLE_NAME]: { value: TABLE_NAME },
},
},
});
expect(wrapper.exists()).toBe(true);
});
it("should handle if tableKey is undefined", () => {
const wrapper = shallowMount(AgdbTableHeader, {
global: {
provide: {
[INJECT_KEY_TABLE_NAME]: undefined,
},
},
});
expect(wrapper.exists()).toBe(true);
});
Expand Down
58 changes: 58 additions & 0 deletions agdb_studio/src/components/base/table/AgdbTableRow.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe("TableRow", () => {
global: {
provide: {
[INJECT_KEY_COLUMNS]: { value: columnsMap },
[INJECT_KEY_TABLE_NAME]: { value: TABLE_NAME },
},
},
});
Expand Down Expand Up @@ -70,4 +71,61 @@ describe("TableRow", () => {
await wrapper.vm.$nextTick();
expect(wrapper.find(".expanded-row").exists()).toBe(true);
});

it("should not render expand button if rowDetailsComponent is not set", () => {
addTable({
name: "table_without_row_details",
columns: tableConfig,
});
const wrapper = mount(AgdbTableRow, {
props: {
columns: columnsMap,
row: {
role: "admin",
owner: "admin",
db: "app3",
db_type: "file",
size: 50,
backup: 0,
},
},
global: {
provide: {
[INJECT_KEY_COLUMNS]: { value: columnsMap },
[INJECT_KEY_TABLE_NAME]: {
value: "table_without_row_details",
},
},
stubs: {
transitions: false,
},
},
});
expect(wrapper.find(".expand-row").exists()).toBe(false);
});
it("should handle if tableKey is undefined", () => {
const wrapper = mount(AgdbTableRow, {
props: {
columns: columnsMap,
row: {
role: "admin",
owner: "admin",
db: "app3",
db_type: "file",
size: 50,
backup: 0,
},
},
global: {
provide: {
[INJECT_KEY_COLUMNS]: { value: columnsMap },
[INJECT_KEY_TABLE_NAME]: undefined,
},
stubs: {
transitions: false,
},
},
});
expect(wrapper.find(".expand-row").exists()).toBe(false);
});
});
56 changes: 49 additions & 7 deletions agdb_studio/src/components/db/DbTable.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,56 @@
import { shallowMount } from "@vue/test-utils";
import DbTable from "./DbTable.vue";
import { describe, it, expect } from "vitest";
import { describe, it, expect, vi } from "vitest";

const { databases, getDbName } = vi.hoisted(() => {
return {
databases: {
value: [
{
owner: "test_owner",
db: "test_db",
db_type: "memory",
role: "admin",
size: 2656,
backup: 0,
},
{
owner: "test_owner2",
db: "test_db2",
db_type: "memory",
role: "admin",
size: 2656,
backup: 0,
},
],
},

getDbName: vi.fn().mockImplementation((db) => {
return `${db.owner}/${db.db}`;
}),
};
});

vi.mock("@/composables/db/dbStore", () => {
return {
useDbStore: () => {
return {
databases,
getDbName,
};
},
};
});

describe("DbTable", () => {
it("should render", () => {
const wrapper = shallowMount(DbTable, {
props: {
tableKey: "table",
},
});
it("should create table and render databases", () => {
const wrapper = shallowMount(DbTable);
expect(wrapper.exists()).toBe(true);
});

it("should render message when no databases", () => {
databases.value = [];
const wrapper = shallowMount(DbTable);
expect(wrapper.text()).toContain("No databases found");
});
});
4 changes: 3 additions & 1 deletion agdb_studio/src/components/db/DbTable.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,16 @@ import { setTableData } from "@/composables/table/tableData";
import { watchEffect } from "vue";
import { dbColumns } from "@/composables/db/dbConfig";
const { databases } = useDbStore();
const { databases, getDbName } = useDbStore();
const TABLE_KEY = Symbol("databases");
addTable({
name: TABLE_KEY,
columns: dbColumns,
rowDetailsComponent: "DbDetails",
uniqueKey: (row) =>
getDbName({ owner: row.owner.toString(), db: row.db.toString() }),
});
watchEffect(() => {
Expand Down
1 change: 0 additions & 1 deletion agdb_studio/src/composables/db/dbConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const dbActions: Action[] = [
label: "Backup",
action: ({ params }: DbActionProps) =>
client.value?.db_backup(params).then(() => {
console.log("Backup created", params);
addNotification({
type: "success",
title: "Backup created",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,6 @@ const testNotifications: AddNotificationProps[] = [
{ type: "warning", title: "Warning without message" },
];

// const testAddNotification = () => {
// testNotifications.forEach((notification) => {
// addNotification(notification);
// });
// };
// testAddNotification();

describe("notificationStore", () => {
beforeEach(() => {
vi.useFakeTimers();
Expand Down
3 changes: 3 additions & 0 deletions agdb_studio/src/composables/table/tableConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ type AddTableProps<T extends TRow> = {
name: Symbol | string;
columns: Column<T>[];
rowDetailsComponent?: AsyncComponent;
uniqueKey?: string | ((row: T) => string);
};

const addTable = ({
name,
columns,
rowDetailsComponent,
uniqueKey,
}: AddTableProps<TRow>): void => {
const columnMap = new Map<string, Column<TRow>>();
columns.forEach((column) => {
Expand All @@ -25,6 +27,7 @@ const addTable = ({
columns: columnMap,
data: new Map(),
rowDetailsComponent,
uniqueKey,
});
};

Expand Down
37 changes: 27 additions & 10 deletions agdb_studio/src/composables/table/tableData.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ import {
import { TABLE_NAME, tableConfig, tableData } from "@/tests/tableMocks";
import { addFilter, getTableFilter, setSort } from "./tableFilter";
import { describe, beforeEach, it, expect } from "vitest";
import type { TRow } from "./types";

describe("tableData", () => {
addTable({ name: TABLE_NAME, columns: tableConfig });
addTable({
name: TABLE_NAME,
columns: tableConfig,
uniqueKey: (row: TRow) =>
`${row.owner.toString()}/${row.db.toString()}`,
});

beforeEach(() => {
const table = getTable(TABLE_NAME);
Expand All @@ -38,13 +44,24 @@ describe("tableData", () => {
});
setTableData("table_without_unique_key", tableData);
const table = getTable("table_without_unique_key");
expect([...(table?.data?.keys() ?? [])]).toStrictEqual([
"0",
"1",
"2",
"3",
"4",
expect(table?.data?.size).toBe(5);
});
it("should set table data with custom string unique key", () => {
addTable({
name: "table_with_string_unique_key",
columns: [
{ key: "key", title: "Key" },
{ key: "value", title: "Value" },
],
uniqueKey: "key",
});
setTableData("table_with_string_unique_key", [
{ key: "key1", value: "value1" },
{ key: "key2", value: "value2" },
{ key: "key3", value: "value3" },
]);
const table = getTable("table_with_string_unique_key");
expect(table?.data?.size).toBe(3);
});
});

Expand All @@ -69,11 +86,11 @@ describe("tableData", () => {
it("should remove row", () => {
const table = getTable(TABLE_NAME);
setTableData(TABLE_NAME, tableData);
expect(table?.data?.get("1")).toBeDefined();
expect(table?.data?.get("user/app1")).toBeDefined();
expect(table?.data?.size).toBe(5);
removeRow(TABLE_NAME, "1");
removeRow(TABLE_NAME, "user/app1");
expect(table?.data?.size).toBe(4);
expect(table?.data?.get("1")).toBeUndefined();
expect(table?.data?.get("user/app1")).toBeUndefined();
});
});

Expand Down
20 changes: 14 additions & 6 deletions agdb_studio/src/composables/table/tableData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@ import type { TRow } from "./types";
import { getTable } from "./tableConfig";
import { getTableFilter } from "./tableFilter";

const getRowKey = <T extends TRow>(
row: T,
uniqueKey?: string | ((row: T) => string),
): string => {
const rowKey =
typeof uniqueKey === "string"
? String(row[uniqueKey])
: uniqueKey?.(row);
return rowKey ?? (Date.now() + Math.random()).toString();
};

const setTableData = <T extends TRow>(
tableName: Symbol | string,
data: T[],
Expand All @@ -16,18 +27,15 @@ const setTableData = <T extends TRow>(
table.columns.forEach((column) => {
rowData[column.key] = data[rowIndex][column.key];
});
const rowKey = rowIndex;

table.data.set(rowKey, rowData);
table.data.set(getRowKey(rowData, table.uniqueKey), rowData);
}
};

const addRow = <T extends TRow>(tableName: Symbol | string, row: T): void => {
const table = getTable<T>(tableName);
const rowKey = table?.data?.size.toString();
if (!rowKey) {
return;
}

const rowKey = getRowKey(row, table?.uniqueKey);

table?.data?.set(rowKey, row);
};
Expand Down
1 change: 1 addition & 0 deletions agdb_studio/src/composables/table/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ export type Table<T extends TRow> = {
columns: Map<string, Column<T>>;
data?: Map<string, T>;
rowDetailsComponent?: AsyncComponent;
uniqueKey?: string | ((row: T) => string);
};
Loading