diff --git a/README.md b/README.md index 9d955a7..92fea93 100644 --- a/README.md +++ b/README.md @@ -47,10 +47,15 @@ As environment variables: - ✔ Find widgets by name - ✔ Create a widget - ✔ Delete a widget +- ✔ List columns +- ✔ Find columns +- ✔ Create columns +- ✔ Delete columns - List cards - Search cards by title - Create a card - Delete a card +- Add some sort of warning when a user tries to delete the LAST column on a Widget, since that's guaranteed to throw a 403. ## Usage diff --git a/src/lib/entities/BravoColumn.ts b/src/lib/entities/BravoColumn.ts index cf6dd9f..c4b88f9 100644 --- a/src/lib/entities/BravoColumn.ts +++ b/src/lib/entities/BravoColumn.ts @@ -29,4 +29,11 @@ export class BravoColumn extends BravoEntity { equals(column: BravoColumn) { return this.hasSameConstructor(column) && this.columnId === column.columnId; } + + async delete() { + if (!this.deleted) { + await this._client.deleteColumn(this.widgetCommonId, this.columnId); + } + this._deleted = true; + } } diff --git a/src/lib/entities/BravoWidget.ts b/src/lib/entities/BravoWidget.ts index f798f8c..479cce7 100644 --- a/src/lib/entities/BravoWidget.ts +++ b/src/lib/entities/BravoWidget.ts @@ -1,15 +1,12 @@ import { DataFavroWidget } from '$types/FavroWidgetTypes.js'; import { BravoEntity } from '$lib/BravoEntity.js'; -import { selectRandom } from '$lib/utility.js'; -import { BravoColumn } from './BravoColumn.js'; -import { ArrayMatchFunction } from '$/types/Utility.js'; +import { selectRandom, stringsMatch } from '$lib/utility.js'; +import type { BravoColumn } from './BravoColumn.js'; +import type { ArrayMatchFunction } from '$/types/Utility.js'; export type OptionWidgetColor = typeof BravoWidget['colors'][number]; export class BravoWidget extends BravoEntity { - /** TODO: Move to cache! */ - private _columns?: BravoColumn[]; - get widgetCommonId() { return this._data.widgetCommonId; } @@ -29,19 +26,27 @@ export class BravoWidget extends BravoEntity { return this._data.editRole; } + async createColumn(name: string, options?: { position?: number }) { + return await this._client.createColumn(this.widgetCommonId, name, options); + } + async listColumns() { - if (!this._columns) { - this._columns = await this._client.listColumns(this.widgetCommonId); - } - return [...this._columns]; + return await this._client.listColumns(this.widgetCommonId); } async findColumn(matchFunction: ArrayMatchFunction) { return await this._client.findColumn(this.widgetCommonId, matchFunction); } - async deleteColumnById(columnId: string) { - return await this.findColumn((col) => col.columnId == columnId); + async findColumnByName(name: string, options?: { ignoreCase?: boolean }) { + const column = await this.findColumn((col) => { + return stringsMatch(col.name, name, options); + }); + return column; + } + + async deleteColumn(columnId: string) { + return await this._client.deleteColumn(this.widgetCommonId, columnId); } async delete() { diff --git a/src/test/client.ts b/src/test/client.ts index d378381..75153c3 100644 --- a/src/test/client.ts +++ b/src/test/client.ts @@ -2,8 +2,9 @@ import { BravoClient } from '$lib/BravoClient.js'; import { expect } from 'chai'; import fs from 'fs-extra'; import dotenv from 'dotenv'; -import { BravoCollection } from '$entities/BravoCollection.js'; -import { BravoWidget } from '$entities/BravoWidget.js'; +import type { BravoCollection } from '$entities/BravoCollection.js'; +import type { BravoWidget } from '$entities/BravoWidget.js'; +import type { BravoColumn } from '$/lib/entities/BravoColumn.js'; /** * @note A root .env file must be populated with the required @@ -12,9 +13,12 @@ import { BravoWidget } from '$entities/BravoWidget.js'; dotenv.config(); const organizationName = process.env.FAVRO_ORGANIZATION_NAME!; const myUserEmail = process.env.FAVRO_USER_EMAIL!; + const testCollectionName = process.env.BRAVO_TEST_COLLECTION_NAME || '___BRAVO_TEST_COLLECTION'; const testWidgetName = '___BRAVO_TEST_WIDGET'; +const testColumnName = '___BRAVO_TEST_COLUMN'; + const sandboxRoot = './sandbox'; const samplesRoot = './samples'; @@ -82,6 +86,7 @@ describe('BravoClient', function () { const client = new BravoClient(); let testWidget: BravoWidget; let testCollection: BravoCollection; + let testColumn: BravoColumn; // !!! // Tests are in a specific order to ensure that dependencies @@ -89,8 +94,27 @@ describe('BravoClient', function () { // do with good test design -- to minimize API calls (the limits // are low) the tests become dependent on the outcomes of prior tests. - before(function () { + before(async function () { + await client.setOrganizationIdByName(organizationName); + resetSandbox(); + // Clean up any leftover remote testing content + // (Since names aren't required to be unique, there could be quite a mess!) + // NOTE: + while (true) { + const collection = await client.findCollectionByName(testCollectionName); + if (!collection) { + break; + } + for (const widget of await collection.listWidgets()) { + if (!widget) { + break; + } + // TODO: delete cards + await widget.delete(); + } + await collection.delete(); + } }); it('can list organizations', async function () { @@ -112,7 +136,6 @@ describe('BravoClient', function () { }); it('can find all users for an organization, including self', async function () { - await client.setOrganizationIdByName(organizationName); const partialUsers = await client.listOrganizationMembers(); expect(partialUsers.length, 'has partial users').to.be.greaterThan(0); const fullUsers = await client.listFullUsers(); @@ -165,8 +188,30 @@ describe('BravoClient', function () { ).to.be.true; }); + it('can create a column', async function () { + testColumn = await testWidget.createColumn(testColumnName); + expect(testColumn).to.exist; + }); + it('can find a created column', async function () { + const foundColumn = await testWidget.findColumnByName(testColumnName); + assertBravoTestClaim(foundColumn); + expect(foundColumn!.equals(testColumn)).to.be.true; + }); + // TODO: NEXT HEIRARCHY LEVEL + it('can delete a created column', async function () { + // Can't delete the last column, so we need to make another to delete! + const deletableName = 'DELETE ME'; + const deletableColumn = await testWidget.createColumn(deletableName); + assertBravoTestClaim(deletableColumn); + await deletableColumn.delete(); + expect( + await testWidget.findColumnByName(deletableName), + 'Should not find deleted column', + ).to.be.undefined; + }); + it('can delete a created widget', async function () { await testWidget.delete(); await expectAsyncError(