From 943c547eddbb0d587166822b1d5ba6425adf4edc Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 1 Dec 2022 10:09:22 +0000 Subject: [PATCH 1/2] Update selected state format. This format will make implementation much simpler than having to parse JSON paths. --- .../src/databases/config/db-config.ts | 91 ++++++++++++++++-- .../config/data/workspace-databases.json | 4 +- .../databases/config/db-config-store.test.ts | 4 +- .../ql-vscode/workspace-databases-schema.json | 93 +++++++++++++++++-- 4 files changed, 170 insertions(+), 22 deletions(-) diff --git a/extensions/ql-vscode/src/databases/config/db-config.ts b/extensions/ql-vscode/src/databases/config/db-config.ts index 1d286d45147..688551cc2f2 100644 --- a/extensions/ql-vscode/src/databases/config/db-config.ts +++ b/extensions/ql-vscode/src/databases/config/db-config.ts @@ -10,14 +10,53 @@ export interface DbConfigDatabases { local: LocalDbConfig; } -export interface SelectedDbItem { - kind: SelectedDbItemKind; - value: string; -} +export type SelectedDbItem = + | SelectedLocalUserDefinedList + | SelectedLocalDatabase + | SelectedRemoteSystemDefinedList + | SelectedRemoteUserDefinedList + | SelectedRemoteOwner + | SelectedRemoteRepository; export enum SelectedDbItemKind { - ConfigDefined = "configDefined", + LocalUserDefinedList = "localUserDefinedList", + LocalDatabase = "localDatabase", RemoteSystemDefinedList = "remoteSystemDefinedList", + RemoteUserDefinedList = "remoteUserDefinedList", + RemoteOwner = "remoteOwner", + RemoteRepository = "remoteRepository", +} + +export interface SelectedLocalUserDefinedList { + kind: SelectedDbItemKind.LocalUserDefinedList; + listName: string; +} + +export interface SelectedLocalDatabase { + kind: SelectedDbItemKind.LocalDatabase; + databaseName: string; + listName?: string; +} + +export interface SelectedRemoteSystemDefinedList { + kind: SelectedDbItemKind.RemoteSystemDefinedList; + listName: string; +} + +export interface SelectedRemoteUserDefinedList { + kind: SelectedDbItemKind.RemoteUserDefinedList; + listName: string; +} + +export interface SelectedRemoteOwner { + kind: SelectedDbItemKind.RemoteOwner; + ownerName: string; +} + +export interface SelectedRemoteRepository { + kind: SelectedDbItemKind.RemoteRepository; + repositoryName: string; + listName?: string; } export interface RemoteDbConfig { @@ -70,10 +109,44 @@ export function cloneDbConfig(config: DbConfig): DbConfig { }, }, selected: config.selected - ? { - kind: config.selected.kind, - value: config.selected.value, - } + ? cloneDbConfigSelectedItem(config.selected) : undefined, }; } + +function cloneDbConfigSelectedItem(selected: SelectedDbItem): SelectedDbItem { + switch (selected.kind) { + case SelectedDbItemKind.LocalUserDefinedList: + return { + kind: SelectedDbItemKind.LocalUserDefinedList, + listName: selected.listName, + }; + case SelectedDbItemKind.LocalDatabase: + return { + kind: SelectedDbItemKind.LocalDatabase, + databaseName: selected.databaseName, + listName: selected.listName, + }; + case SelectedDbItemKind.RemoteSystemDefinedList: + return { + kind: SelectedDbItemKind.RemoteSystemDefinedList, + listName: selected.listName, + }; + case SelectedDbItemKind.RemoteUserDefinedList: + return { + kind: SelectedDbItemKind.RemoteUserDefinedList, + listName: selected.listName, + }; + case SelectedDbItemKind.RemoteOwner: + return { + kind: SelectedDbItemKind.RemoteOwner, + ownerName: selected.ownerName, + }; + case SelectedDbItemKind.RemoteRepository: + return { + kind: SelectedDbItemKind.RemoteRepository, + repositoryName: selected.repositoryName, + listName: selected.listName, + }; + } +} diff --git a/extensions/ql-vscode/test/pure-tests/databases/config/data/workspace-databases.json b/extensions/ql-vscode/test/pure-tests/databases/config/data/workspace-databases.json index 0d1a7b1ea7a..5264d380513 100644 --- a/extensions/ql-vscode/test/pure-tests/databases/config/data/workspace-databases.json +++ b/extensions/ql-vscode/test/pure-tests/databases/config/data/workspace-databases.json @@ -46,7 +46,7 @@ } }, "selected": { - "kind": "configDefined", - "value": "path.to.database" + "kind": "remoteUserDefinedList", + "listName": "repoList1" } } diff --git a/extensions/ql-vscode/test/pure-tests/databases/config/db-config-store.test.ts b/extensions/ql-vscode/test/pure-tests/databases/config/db-config-store.test.ts index d48c9cbdf9a..c518c6f8ae8 100644 --- a/extensions/ql-vscode/test/pure-tests/databases/config/db-config-store.test.ts +++ b/extensions/ql-vscode/test/pure-tests/databases/config/db-config-store.test.ts @@ -84,8 +84,8 @@ describe("db config store", () => { storagePath: "/path/to/database/", }); expect(config.selected).toEqual({ - kind: "configDefined", - value: "path.to.database", + kind: "remoteUserDefinedList", + listName: "repoList1", }); configStore.dispose(); diff --git a/extensions/ql-vscode/workspace-databases-schema.json b/extensions/ql-vscode/workspace-databases-schema.json index 027b231837b..a7281b45406 100644 --- a/extensions/ql-vscode/workspace-databases-schema.json +++ b/extensions/ql-vscode/workspace-databases-schema.json @@ -123,17 +123,92 @@ }, "selected": { "type": "object", - "properties": { - "kind": { - "type": "string", - "enum": ["configDefined", "remoteSystemDefinedList"] + "oneOf": [ + { + "properties": { + "kind": { + "type": "string", + "enum": ["localUserDefinedList"] + }, + "listName": { + "type": "string" + } + }, + "required": ["kind", "listName"], + "additionalProperties": false + }, + { + "properties": { + "kind": { + "type": "string", + "enum": ["localDatabase"] + }, + "databaseName": { + "type": "string" + }, + "listName": { + "type": "string" + } + }, + "required": ["kind", "databaseName"], + "additionalProperties": false + }, + { + "properties": { + "kind": { + "type": "string", + "enum": ["remoteSystemDefinedList"] + }, + "listName": { + "type": "string" + } + }, + "required": ["kind", "listName"], + "additionalProperties": false }, - "value": { - "type": "string" + { + "properties": { + "kind": { + "type": "string", + "enum": ["remoteUserDefinedList"] + }, + "listName": { + "type": "string" + } + }, + "required": ["kind", "listName"], + "additionalProperties": false + }, + { + "properties": { + "kind": { + "type": "string", + "enum": ["remoteOwner"] + }, + "ownerName": { + "type": "string" + } + }, + "required": ["kind", "ownerName"], + "additionalProperties": false + }, + { + "properties": { + "kind": { + "type": "string", + "enum": ["remoteRepository"] + }, + "repositoryName": { + "type": "string" + }, + "listName": { + "type": "string" + } + }, + "required": ["kind", "repositoryName"], + "additionalProperties": false } - }, - "required": ["kind", "value"], - "additionalProperties": false + ] } }, "required": ["databases"], From 7b54b6c98e386b7cb53a349d3a3b86e617a36fb7 Mon Sep 17 00:00:00 2001 From: Charis Kyriakou Date: Thu, 1 Dec 2022 10:11:37 +0000 Subject: [PATCH 2/2] Propagate the selected state from the db config to the tree view. --- extensions/ql-vscode/src/databases/db-item.ts | 53 +++++ .../src/databases/db-tree-creator.ts | 95 +++++++-- .../src/databases/ui/db-tree-view-item.ts | 11 ++ .../databases/db-panel.test.ts | 148 +++++++++++++- .../databases/db-tree-creator.test.ts | 185 +++++++++++++++++- 5 files changed, 465 insertions(+), 27 deletions(-) diff --git a/extensions/ql-vscode/src/databases/db-item.ts b/extensions/ql-vscode/src/databases/db-item.ts index 99bed2ce21a..76bda03c6bc 100644 --- a/extensions/ql-vscode/src/databases/db-item.ts +++ b/extensions/ql-vscode/src/databases/db-item.ts @@ -20,12 +20,14 @@ export type LocalDbItem = LocalListDbItem | LocalDatabaseDbItem; export interface LocalListDbItem { kind: DbItemKind.LocalList; + selected: boolean; listName: string; databases: LocalDatabaseDbItem[]; } export interface LocalDatabaseDbItem { kind: DbItemKind.LocalDatabase; + selected: boolean; databaseName: string; dateAdded: number; language: string; @@ -51,6 +53,7 @@ export type RemoteDbItem = export interface RemoteSystemDefinedListDbItem { kind: DbItemKind.RemoteSystemDefinedList; + selected: boolean; listName: string; listDisplayName: string; listDescription: string; @@ -58,16 +61,66 @@ export interface RemoteSystemDefinedListDbItem { export interface RemoteUserDefinedListDbItem { kind: DbItemKind.RemoteUserDefinedList; + selected: boolean; listName: string; repos: RemoteRepoDbItem[]; } export interface RemoteOwnerDbItem { kind: DbItemKind.RemoteOwner; + selected: boolean; ownerName: string; } export interface RemoteRepoDbItem { kind: DbItemKind.RemoteRepo; + selected: boolean; repoFullName: string; } + +export function isRemoteSystemDefinedListDbItem( + dbItem: DbItem, +): dbItem is RemoteSystemDefinedListDbItem { + return dbItem.kind === DbItemKind.RemoteSystemDefinedList; +} + +export function isRemoteUserDefinedListDbItem( + dbItem: DbItem, +): dbItem is RemoteUserDefinedListDbItem { + return dbItem.kind === DbItemKind.RemoteUserDefinedList; +} + +export function isRemoteOwnerDbItem( + dbItem: DbItem, +): dbItem is RemoteOwnerDbItem { + return dbItem.kind === DbItemKind.RemoteOwner; +} + +export function isRemoteRepoDbItem(dbItem: DbItem): dbItem is RemoteRepoDbItem { + return dbItem.kind === DbItemKind.RemoteRepo; +} + +export function isLocalListDbItem(dbItem: DbItem): dbItem is LocalListDbItem { + return dbItem.kind === DbItemKind.LocalList; +} + +export function isLocalDatabaseDbItem( + dbItem: DbItem, +): dbItem is LocalDatabaseDbItem { + return dbItem.kind === DbItemKind.LocalDatabase; +} + +export type SelectableDbItem = RemoteDbItem | LocalDbItem; + +export function isSelectableDbItem(dbItem: DbItem): dbItem is SelectableDbItem { + return SelectableDbItemKinds.includes(dbItem.kind); +} + +const SelectableDbItemKinds = [ + DbItemKind.LocalList, + DbItemKind.LocalDatabase, + DbItemKind.RemoteSystemDefinedList, + DbItemKind.RemoteUserDefinedList, + DbItemKind.RemoteOwner, + DbItemKind.RemoteRepo, +]; diff --git a/extensions/ql-vscode/src/databases/db-tree-creator.ts b/extensions/ql-vscode/src/databases/db-tree-creator.ts index e6999a9ca38..9728a544bf6 100644 --- a/extensions/ql-vscode/src/databases/db-tree-creator.ts +++ b/extensions/ql-vscode/src/databases/db-tree-creator.ts @@ -3,6 +3,7 @@ import { LocalDatabase, LocalList, RemoteRepositoryList, + SelectedDbItemKind, } from "./config/db-config"; import { DbItemKind, @@ -18,16 +19,20 @@ import { export function createRemoteTree(dbConfig: DbConfig): RootRemoteDbItem { const systemDefinedLists = [ - createSystemDefinedList(10), - createSystemDefinedList(100), - createSystemDefinedList(1000), + createSystemDefinedList(10, dbConfig), + createSystemDefinedList(100, dbConfig), + createSystemDefinedList(1000, dbConfig), ]; const userDefinedRepoLists = dbConfig.databases.remote.repositoryLists.map( - createUserDefinedList, + (r) => createRemoteUserDefinedList(r, dbConfig), + ); + const owners = dbConfig.databases.remote.owners.map((o) => + createOwnerItem(o, dbConfig), + ); + const repos = dbConfig.databases.remote.repositories.map((r) => + createRepoItem(r, dbConfig), ); - const owners = dbConfig.databases.remote.owners.map(createOwnerItem); - const repos = dbConfig.databases.remote.repositories.map(createRepoItem); return { kind: DbItemKind.RootRemote, @@ -41,8 +46,12 @@ export function createRemoteTree(dbConfig: DbConfig): RootRemoteDbItem { } export function createLocalTree(dbConfig: DbConfig): RootLocalDbItem { - const localLists = dbConfig.databases.local.lists.map(createLocalList); - const localDbs = dbConfig.databases.local.databases.map(createLocalDb); + const localLists = dbConfig.databases.local.lists.map((l) => + createLocalList(l, dbConfig), + ); + const localDbs = dbConfig.databases.local.databases.map((l) => + createLocalDb(l, dbConfig), + ); return { kind: DbItemKind.RootLocal, @@ -50,53 +59,105 @@ export function createLocalTree(dbConfig: DbConfig): RootLocalDbItem { }; } -function createSystemDefinedList(n: number): RemoteSystemDefinedListDbItem { +function createSystemDefinedList( + n: number, + dbConfig: DbConfig, +): RemoteSystemDefinedListDbItem { + const listName = `top_${n}`; + + const selected = + dbConfig.selected && + dbConfig.selected.kind === SelectedDbItemKind.RemoteSystemDefinedList && + dbConfig.selected.listName === listName; + return { kind: DbItemKind.RemoteSystemDefinedList, - listName: `top_${n}`, + listName: listName, listDisplayName: `Top ${n} repositories`, listDescription: `Top ${n} repositories of a language`, + selected: !!selected, }; } -function createUserDefinedList( +function createRemoteUserDefinedList( list: RemoteRepositoryList, + dbConfig: DbConfig, ): RemoteUserDefinedListDbItem { + const selected = + dbConfig.selected && + dbConfig.selected.kind === SelectedDbItemKind.RemoteUserDefinedList && + dbConfig.selected.listName === list.name; + return { kind: DbItemKind.RemoteUserDefinedList, listName: list.name, - repos: list.repositories.map((r) => createRepoItem(r)), + repos: list.repositories.map((r) => createRepoItem(r, dbConfig, list.name)), + selected: !!selected, }; } -function createOwnerItem(owner: string): RemoteOwnerDbItem { +function createOwnerItem(owner: string, dbConfig: DbConfig): RemoteOwnerDbItem { + const selected = + dbConfig.selected && + dbConfig.selected.kind === SelectedDbItemKind.RemoteOwner && + dbConfig.selected.ownerName === owner; + return { kind: DbItemKind.RemoteOwner, ownerName: owner, + selected: !!selected, }; } -function createRepoItem(repo: string): RemoteRepoDbItem { +function createRepoItem( + repo: string, + dbConfig: DbConfig, + listName?: string, +): RemoteRepoDbItem { + const selected = + dbConfig.selected && + dbConfig.selected.kind === SelectedDbItemKind.RemoteRepository && + dbConfig.selected.repositoryName === repo && + dbConfig.selected.listName === listName; + return { kind: DbItemKind.RemoteRepo, repoFullName: repo, + selected: !!selected, }; } -function createLocalList(list: LocalList): LocalListDbItem { +function createLocalList(list: LocalList, dbConfig: DbConfig): LocalListDbItem { + const selected = + dbConfig.selected && + dbConfig.selected.kind === SelectedDbItemKind.LocalUserDefinedList && + dbConfig.selected.listName === list.name; + return { kind: DbItemKind.LocalList, listName: list.name, - databases: list.databases.map(createLocalDb), + databases: list.databases.map((d) => createLocalDb(d, dbConfig, list.name)), + selected: !!selected, }; } -function createLocalDb(db: LocalDatabase): LocalDatabaseDbItem { +function createLocalDb( + db: LocalDatabase, + dbConfig: DbConfig, + listName?: string, +): LocalDatabaseDbItem { + const selected = + dbConfig.selected && + dbConfig.selected.kind === SelectedDbItemKind.LocalDatabase && + dbConfig.selected.databaseName === db.name && + dbConfig.selected.listName === listName; + return { kind: DbItemKind.LocalDatabase, databaseName: db.name, dateAdded: db.dateAdded, language: db.language, storagePath: db.storagePath, + selected: !!selected, }; } diff --git a/extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts b/extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts index a63ff851f00..67cb8edf361 100644 --- a/extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts +++ b/extensions/ql-vscode/src/databases/ui/db-tree-view-item.ts @@ -1,6 +1,7 @@ import * as vscode from "vscode"; import { DbItem, + isSelectableDbItem, LocalDatabaseDbItem, LocalListDbItem, RemoteOwnerDbItem, @@ -28,6 +29,16 @@ export class DbTreeViewItem extends vscode.TreeItem { public readonly children: DbTreeViewItem[], ) { super(label, collapsibleState); + + if (dbItem && isSelectableDbItem(dbItem)) { + if (dbItem.selected) { + // Define the resource id to drive the UI to render this item as selected. + this.resourceUri = vscode.Uri.parse("codeql://databases?selected=true"); + } else { + // Define a context value to drive the UI to show an action to select the item. + this.contextValue = "selectableDbItem"; + } + } } } diff --git a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts index cc56c18a5db..40462e827c8 100644 --- a/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts +++ b/extensions/ql-vscode/src/vscode-tests/minimal-workspace/databases/db-panel.test.ts @@ -1,7 +1,10 @@ import * as vscode from "vscode"; import * as path from "path"; import * as fs from "fs-extra"; -import { DbConfig } from "../../../databases/config/db-config"; +import { + DbConfig, + SelectedDbItemKind, +} from "../../../databases/config/db-config"; import { DbManager } from "../../../databases/db-manager"; import { DbConfigStore } from "../../../databases/config/db-config-store"; import { DbTreeDataProvider } from "../../../databases/ui/db-tree-data-provider"; @@ -307,6 +310,7 @@ describe("db panel", () => { dateAdded: 1668428293677, language: "cpp", storagePath: "/path/to/db1/", + selected: false, }, { kind: DbItemKind.LocalDatabase, @@ -314,6 +318,7 @@ describe("db panel", () => { dateAdded: 1668428472731, language: "cpp", storagePath: "/path/to/db2/", + selected: false, }, ]); checkLocalListItem(localListItems[1], "my-list-2", [ @@ -323,6 +328,7 @@ describe("db panel", () => { dateAdded: 1668428472731, language: "ruby", storagePath: "/path/to/db3/", + selected: false, }, ]); }); @@ -381,6 +387,7 @@ describe("db panel", () => { dateAdded: 1668428293677, language: "csharp", storagePath: "/path/to/db1/", + selected: false, }); checkLocalDatabaseItem(localDatabaseItems[1], { kind: DbItemKind.LocalDatabase, @@ -388,9 +395,134 @@ describe("db panel", () => { dateAdded: 1668428472731, language: "go", storagePath: "/path/to/db2/", + selected: false, }); }); + it("should mark selected remote db list as selected", async () => { + const dbConfig: DbConfig = { + databases: { + remote: { + repositoryLists: [ + { + name: "my-list-1", + repositories: ["owner1/repo1", "owner1/repo2"], + }, + { + name: "my-list-2", + repositories: ["owner2/repo1", "owner2/repo2"], + }, + ], + owners: [], + repositories: [], + }, + local: { + lists: [], + databases: [], + }, + }, + selected: { + kind: SelectedDbItemKind.RemoteUserDefinedList, + listName: "my-list-2", + }, + }; + + await saveDbConfig(dbConfig); + + const dbTreeItems = await dbTreeDataProvider.getChildren(); + + expect(dbTreeItems).toBeTruthy(); + const items = dbTreeItems!; + + const remoteRootNode = items[0]; + expect(remoteRootNode.dbItem).toBeTruthy(); + expect(remoteRootNode.dbItem?.kind).toEqual(DbItemKind.RootRemote); + + const list1 = remoteRootNode.children.find( + (c) => + c.dbItem?.kind === DbItemKind.RemoteUserDefinedList && + c.dbItem?.listName === "my-list-1", + ); + const list2 = remoteRootNode.children.find( + (c) => + c.dbItem?.kind === DbItemKind.RemoteUserDefinedList && + c.dbItem?.listName === "my-list-2", + ); + + expect(list1).toBeTruthy(); + expect(list2).toBeTruthy(); + expect(isTreeViewItemSelectable(list1!)).toBeTruthy(); + expect(isTreeViewItemSelected(list2!)).toBeTruthy(); + }); + + it("should mark selected remote db inside list as selected", async () => { + const dbConfig: DbConfig = { + databases: { + remote: { + repositoryLists: [ + { + name: "my-list-1", + repositories: ["owner1/repo1", "owner1/repo2"], + }, + { + name: "my-list-2", + repositories: ["owner1/repo1", "owner2/repo2"], + }, + ], + owners: [], + repositories: ["owner1/repo1"], + }, + local: { + lists: [], + databases: [], + }, + }, + selected: { + kind: SelectedDbItemKind.RemoteRepository, + repositoryName: "owner1/repo1", + listName: "my-list-2", + }, + }; + + await saveDbConfig(dbConfig); + + const dbTreeItems = await dbTreeDataProvider.getChildren(); + + expect(dbTreeItems).toBeTruthy(); + const items = dbTreeItems!; + + const remoteRootNode = items[0]; + expect(remoteRootNode.dbItem).toBeTruthy(); + expect(remoteRootNode.dbItem?.kind).toEqual(DbItemKind.RootRemote); + + const list2 = remoteRootNode.children.find( + (c) => + c.dbItem?.kind === DbItemKind.RemoteUserDefinedList && + c.dbItem?.listName === "my-list-2", + ); + expect(list2).toBeTruthy(); + + const repo1Node = list2?.children.find( + (c) => + c.dbItem?.kind === DbItemKind.RemoteRepo && + c.dbItem?.repoFullName === "owner1/repo1", + ); + expect(repo1Node).toBeTruthy(); + expect(isTreeViewItemSelected(repo1Node!)).toBeTruthy(); + + const repo2Node = list2?.children.find( + (c) => + c.dbItem?.kind === DbItemKind.RemoteRepo && + c.dbItem?.repoFullName === "owner2/repo2", + ); + expect(repo2Node).toBeTruthy(); + expect(isTreeViewItemSelectable(repo2Node!)).toBeTruthy(); + + for (const item of remoteRootNode.children) { + expect(isTreeViewItemSelectable(item)).toBeTruthy(); + } + }); + async function saveDbConfig(dbConfig: DbConfig): Promise { await fs.writeJson(dbConfigFilePath, dbConfig); @@ -475,4 +607,18 @@ describe("db panel", () => { expect(item.iconPath).toEqual(new vscode.ThemeIcon("database")); expect(item.collapsibleState).toBe(vscode.TreeItemCollapsibleState.None); } + + function isTreeViewItemSelectable(treeViewItem: DbTreeViewItem) { + return ( + treeViewItem.resourceUri === undefined && + treeViewItem.contextValue === "selectableDbItem" + ); + } + + function isTreeViewItemSelected(treeViewItem: DbTreeViewItem) { + return ( + treeViewItem.resourceUri?.query === "selected=true" && + treeViewItem.contextValue === undefined + ); + } }); diff --git a/extensions/ql-vscode/test/pure-tests/databases/db-tree-creator.test.ts b/extensions/ql-vscode/test/pure-tests/databases/db-tree-creator.test.ts index 4ee36e8c241..142012709a4 100644 --- a/extensions/ql-vscode/test/pure-tests/databases/db-tree-creator.test.ts +++ b/extensions/ql-vscode/test/pure-tests/databases/db-tree-creator.test.ts @@ -1,5 +1,13 @@ -import { DbConfig } from "../../../src/databases/config/db-config"; -import { DbItemKind } from "../../../src/databases/db-item"; +import { + DbConfig, + SelectedDbItemKind, +} from "../../../src/databases/config/db-config"; +import { + DbItemKind, + isRemoteOwnerDbItem, + isRemoteRepoDbItem, + isRemoteUserDefinedListDbItem, +} from "../../../src/databases/db-item"; import { createLocalTree, createRemoteTree, @@ -29,18 +37,21 @@ describe("db tree creator", () => { expect(dbTreeRoot.children.length).toBe(3); expect(dbTreeRoot.children[0]).toEqual({ kind: DbItemKind.RemoteSystemDefinedList, + selected: false, listName: "top_10", listDisplayName: "Top 10 repositories", listDescription: "Top 10 repositories of a language", }); expect(dbTreeRoot.children[1]).toEqual({ kind: DbItemKind.RemoteSystemDefinedList, + selected: false, listName: "top_100", listDisplayName: "Top 100 repositories", listDescription: "Top 100 repositories of a language", }); expect(dbTreeRoot.children[2]).toEqual({ kind: DbItemKind.RemoteSystemDefinedList, + selected: false, listName: "top_1000", listDisplayName: "Top 1000 repositories", listDescription: "Top 1000 repositories of a language", @@ -76,26 +87,30 @@ describe("db tree creator", () => { expect(dbTreeRoot).toBeTruthy(); expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote); const repositoryListNodes = dbTreeRoot.children.filter( - (child) => child.kind === DbItemKind.RemoteUserDefinedList, + isRemoteUserDefinedListDbItem, ); expect(repositoryListNodes.length).toBe(2); expect(repositoryListNodes[0]).toEqual({ kind: DbItemKind.RemoteUserDefinedList, + selected: false, listName: dbConfig.databases.remote.repositoryLists[0].name, repos: dbConfig.databases.remote.repositoryLists[0].repositories.map( (repo) => ({ kind: DbItemKind.RemoteRepo, + selected: false, repoFullName: repo, }), ), }); expect(repositoryListNodes[1]).toEqual({ kind: DbItemKind.RemoteUserDefinedList, + selected: false, listName: dbConfig.databases.remote.repositoryLists[1].name, repos: dbConfig.databases.remote.repositoryLists[1].repositories.map( (repo) => ({ kind: DbItemKind.RemoteRepo, + selected: false, repoFullName: repo, }), ), @@ -121,17 +136,17 @@ describe("db tree creator", () => { expect(dbTreeRoot).toBeTruthy(); expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote); - const ownerNodes = dbTreeRoot.children.filter( - (child) => child.kind === DbItemKind.RemoteOwner, - ); + const ownerNodes = dbTreeRoot.children.filter(isRemoteOwnerDbItem); expect(ownerNodes.length).toBe(2); expect(ownerNodes[0]).toEqual({ kind: DbItemKind.RemoteOwner, + selected: false, ownerName: dbConfig.databases.remote.owners[0], }); expect(ownerNodes[1]).toEqual({ kind: DbItemKind.RemoteOwner, + selected: false, ownerName: dbConfig.databases.remote.owners[1], }); }); @@ -155,25 +170,171 @@ describe("db tree creator", () => { expect(dbTreeRoot).toBeTruthy(); expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote); - const repoNodes = dbTreeRoot.children.filter( - (child) => child.kind === DbItemKind.RemoteRepo, - ); + const repoNodes = dbTreeRoot.children.filter(isRemoteRepoDbItem); expect(repoNodes.length).toBe(3); expect(repoNodes[0]).toEqual({ kind: DbItemKind.RemoteRepo, + selected: false, repoFullName: dbConfig.databases.remote.repositories[0], }); expect(repoNodes[1]).toEqual({ kind: DbItemKind.RemoteRepo, + selected: false, repoFullName: dbConfig.databases.remote.repositories[1], }); expect(repoNodes[2]).toEqual({ kind: DbItemKind.RemoteRepo, + selected: false, repoFullName: dbConfig.databases.remote.repositories[2], }); }); + + describe("selected db item", () => { + it("should allow selecting a remote user defined list node", () => { + const dbConfig: DbConfig = { + databases: { + remote: { + repositoryLists: [ + { + name: "my-list-1", + repositories: [ + "owner1/repo1", + "owner1/repo2", + "owner2/repo1", + ], + }, + ], + owners: [], + repositories: [], + }, + local: { + lists: [], + databases: [], + }, + }, + selected: { + kind: SelectedDbItemKind.RemoteUserDefinedList, + listName: "my-list-1", + }, + }; + + const dbTreeRoot = createRemoteTree(dbConfig); + + expect(dbTreeRoot).toBeTruthy(); + expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote); + const repositoryListNodes = dbTreeRoot.children.filter( + (child) => child.kind === DbItemKind.RemoteUserDefinedList, + ); + + expect(repositoryListNodes.length).toBe(1); + expect(repositoryListNodes[0].selected).toEqual(true); + }); + + it("should allow selecting a remote owner node", () => { + const dbConfig: DbConfig = { + databases: { + remote: { + repositoryLists: [], + owners: ["owner1", "owner2"], + repositories: [], + }, + local: { + lists: [], + databases: [], + }, + }, + selected: { + kind: SelectedDbItemKind.RemoteOwner, + ownerName: "owner1", + }, + }; + + const dbTreeRoot = createRemoteTree(dbConfig); + + expect(dbTreeRoot).toBeTruthy(); + expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote); + const ownerNodes = dbTreeRoot.children.filter( + (child) => child.kind === DbItemKind.RemoteOwner, + ); + + expect(ownerNodes.length).toBe(2); + expect(ownerNodes[0].selected).toEqual(true); + expect(ownerNodes[1].selected).toEqual(false); + }); + + it("should allow selecting a remote repo node", () => { + const dbConfig: DbConfig = { + databases: { + remote: { + repositoryLists: [], + owners: [], + repositories: ["owner1/repo1", "owner1/repo2"], + }, + local: { + lists: [], + databases: [], + }, + }, + selected: { + kind: SelectedDbItemKind.RemoteRepository, + repositoryName: "owner1/repo2", + }, + }; + + const dbTreeRoot = createRemoteTree(dbConfig); + + expect(dbTreeRoot).toBeTruthy(); + expect(dbTreeRoot.kind).toBe(DbItemKind.RootRemote); + const repoNodes = dbTreeRoot.children.filter(isRemoteRepoDbItem); + + expect(repoNodes.length).toBe(2); + expect(repoNodes[0].selected).toEqual(false); + expect(repoNodes[1].selected).toEqual(true); + }); + + it("should allow selecting a remote repo in a list", () => { + const dbConfig: DbConfig = { + databases: { + remote: { + repositoryLists: [ + { + name: "my-list-1", + repositories: ["owner1/repo1"], + }, + ], + owners: [], + repositories: ["owner1/repo2"], + }, + local: { + lists: [], + databases: [], + }, + }, + selected: { + kind: SelectedDbItemKind.RemoteRepository, + listName: "my-list-1", + repositoryName: "owner1/repo1", + }, + }; + + const dbTreeRoot = createRemoteTree(dbConfig); + + expect(dbTreeRoot).toBeTruthy(); + + const listNodes = dbTreeRoot.children.filter( + isRemoteUserDefinedListDbItem, + ); + + expect(listNodes.length).toBe(1); + expect(listNodes[0].selected).toEqual(false); + expect(listNodes[0].repos.length).toBe(1); + expect(listNodes[0].repos[0].repoFullName).toBe("owner1/repo1"); + expect(listNodes[0].repos[0].selected).toBe(true); + }); + }); }); + describe("createLocalTree", () => { it("should build root node", () => { const dbConfig: DbConfig = { @@ -252,9 +413,11 @@ describe("db tree creator", () => { expect(localListNodes.length).toBe(2); expect(localListNodes[0]).toEqual({ kind: DbItemKind.LocalList, + selected: false, listName: dbConfig.databases.local.lists[0].name, databases: dbConfig.databases.local.lists[0].databases.map((db) => ({ kind: DbItemKind.LocalDatabase, + selected: false, databaseName: db.name, dateAdded: db.dateAdded, language: db.language, @@ -263,9 +426,11 @@ describe("db tree creator", () => { }); expect(localListNodes[1]).toEqual({ kind: DbItemKind.LocalList, + selected: false, listName: dbConfig.databases.local.lists[1].name, databases: dbConfig.databases.local.lists[1].databases.map((db) => ({ kind: DbItemKind.LocalDatabase, + selected: false, databaseName: db.name, dateAdded: db.dateAdded, language: db.language, @@ -313,6 +478,7 @@ describe("db tree creator", () => { expect(localDatabaseNodes.length).toBe(2); expect(localDatabaseNodes[0]).toEqual({ kind: DbItemKind.LocalDatabase, + selected: false, databaseName: dbConfig.databases.local.databases[0].name, dateAdded: dbConfig.databases.local.databases[0].dateAdded, language: dbConfig.databases.local.databases[0].language, @@ -320,6 +486,7 @@ describe("db tree creator", () => { }); expect(localDatabaseNodes[1]).toEqual({ kind: DbItemKind.LocalDatabase, + selected: false, databaseName: dbConfig.databases.local.databases[1].name, dateAdded: dbConfig.databases.local.databases[1].dateAdded, language: dbConfig.databases.local.databases[1].language,