Skip to content

Commit

Permalink
Merge pull request #1076 from nextcloud-libraries/feat/allow-destruct…
Browse files Browse the repository at this point in the history
…ive-actions
  • Loading branch information
skjnldsv authored Sep 11, 2024
2 parents ae0a991 + 9f67180 commit 50465ed
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 120 deletions.
237 changes: 118 additions & 119 deletions __tests__/fileAction.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
* SPDX-FileCopyrightText: 2023-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable no-new */
import { beforeEach, describe, expect, test, vi } from 'vitest'
import type { Node } from '../lib/files/node'
import type { View } from '../lib/navigation/view'

import { getFileActions, registerFileAction, FileAction, DefaultType } from '../lib/fileAction'
import { beforeEach, describe, expect, test, vi } from 'vitest'
import { getFileActions, registerFileAction, FileAction, DefaultType, FileActionData } from '../lib/fileAction'
import logger from '../lib/utils/logger'

describe('FileActions init', () => {
Expand All @@ -34,8 +33,8 @@ describe('FileActions init', () => {
})

expect(action.id).toBe('test')
expect(action.displayName([], {})).toBe('Test')
expect(action.iconSvgInline([], {})).toBe('<svg></svg>')
expect(action.displayName([], {} as unknown as View)).toBe('Test')
expect(action.iconSvgInline([], {} as unknown as View)).toBe('<svg></svg>')

registerFileAction(action)

Expand Down Expand Up @@ -95,131 +94,129 @@ describe('FileActions init', () => {

describe('Invalid FileAction creation', () => {
test('Invalid id', () => {
expect(() => {
new FileAction({
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as any as FileAction)
}).toThrowError('Invalid id')
expect(() => new FileAction({
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as unknown as FileAction),
).toThrowError('Invalid id')
})
test('Invalid displayName', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as any as FileAction)
}).toThrowError('Invalid displayName function')
expect(() => new FileAction({
id: 'test',
displayName: 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as unknown as FileAction),
).toThrowError('Invalid displayName function')
})
test('Invalid title', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
title: 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as any as FileAction)
}).toThrowError('Invalid title function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
title: 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
} as unknown as FileAction),
).toThrowError('Invalid title function')
})
test('Invalid iconSvgInline', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: '<svg></svg>',
exec: async () => true,
} as any as FileAction)
}).toThrowError('Invalid iconSvgInline function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: '<svg></svg>',
exec: async () => true,
} as unknown as FileAction),
).toThrowError('Invalid iconSvgInline function')
})
test('Invalid exec', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: false,
} as any as FileAction)
}).toThrowError('Invalid exec function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: false,
} as unknown as FileAction),
).toThrowError('Invalid exec function')
})
test('Invalid enabled', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
enabled: false,
} as any as FileAction)
}).toThrowError('Invalid enabled function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
enabled: false,
} as unknown as FileAction),
).toThrowError('Invalid enabled function')
})
test('Invalid execBatch', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
execBatch: false,
} as any as FileAction)
}).toThrowError('Invalid execBatch function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
execBatch: false,
} as unknown as FileAction),
).toThrowError('Invalid execBatch function')
})
test('Invalid order', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
order: 'invalid',
} as any as FileAction)
}).toThrowError('Invalid order')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
order: 'invalid',
} as unknown as FileAction),
).toThrowError('Invalid order')
})
test('Invalid parent', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
parent: true,
} as any as FileAction)
}).toThrowError('Invalid parent')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
parent: true,
} as unknown as FileAction),
).toThrowError('Invalid parent')
})
test('Invalid default', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
default: 'invalid',
} as any as FileAction)
}).toThrowError('Invalid default')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
default: 'invalid',
} as unknown as FileAction),
).toThrowError('Invalid default')
})
test('Invalid destructives', () => {
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
destructive: 'false',
} as unknown as FileActionData),
).toThrowError('Invalid destructive')
})
test('Invalid inline', () => {
expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
inline: true,
} as any as FileAction)
}).toThrowError('Invalid inline function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
inline: true,
} as unknown as FileAction),
).toThrowError('Invalid inline function')

expect(() => {
new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
inline: () => true,
renderInline: false,
} as any as FileAction)
}).toThrowError('Invalid renderInline function')
expect(() => new FileAction({
id: 'test',
displayName: () => 'Test',
iconSvgInline: () => '<svg></svg>',
exec: async () => true,
inline: () => true,
renderInline: false,
} as unknown as FileAction),
).toThrowError('Invalid renderInline function')
})
})

Expand All @@ -236,6 +233,7 @@ describe('FileActions creation', () => {
enabled: () => true,
order: 100,
parent: '123',
destructive: true,
default: DefaultType.DEFAULT,
inline: () => true,
renderInline: async () => {
Expand All @@ -246,16 +244,17 @@ describe('FileActions creation', () => {
})

expect(action.id).toBe('test')
expect(action.displayName([], {} as any)).toBe('Test')
expect(action.title?.([], {} as any)).toBe('Test title')
expect(action.iconSvgInline([], {} as any)).toBe('<svg></svg>')
await expect(action.exec({} as any, {} as any, '/')).resolves.toBe(true)
await expect(action.execBatch?.([], {} as any, '/')).resolves.toStrictEqual([true])
expect(action.enabled?.({} as any, {} as any)).toBe(true)
expect(action.displayName([], {} as unknown as View)).toBe('Test')
expect(action.title?.([], {} as unknown as View)).toBe('Test title')
expect(action.iconSvgInline([], {} as unknown as View)).toBe('<svg></svg>')
await expect(action.exec({} as unknown as Node, {} as unknown as View, '/')).resolves.toBe(true)
await expect(action.execBatch?.([], {} as unknown as View, '/')).resolves.toStrictEqual([true])
expect(action.enabled?.([], {} as unknown as View)).toBe(true)
expect(action.order).toBe(100)
expect(action.parent).toBe('123')
expect(action.destructive).toBe(true)
expect(action.default).toBe(DefaultType.DEFAULT)
expect(action.inline?.({} as any, {} as any)).toBe(true)
expect((await action.renderInline?.({} as any, {} as any))?.outerHTML).toBe('<span>test</span>')
expect(action.inline?.({} as unknown as Node, {} as unknown as View)).toBe(true)
expect((await action.renderInline?.({} as unknown as Node, {} as unknown as View))?.outerHTML).toBe('<span>test</span>')
})
})
16 changes: 15 additions & 1 deletion lib/fileAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export enum DefaultType {
HIDDEN = 'hidden',
}

interface FileActionData {
export interface FileActionData {
/** Unique ID */
id: string
/** Translatable string displayed in the menu */
Expand Down Expand Up @@ -42,6 +42,12 @@ interface FileActionData {
/** This action order in the list */
order?: number,

/**
* Set to true if this action is a destructive action, like "delete".
* This will change the appearance in the action menu more prominent (e.g. red colored)
*/
destructive?: boolean

/**
* This action's parent id in the list.
* If none found, will be displayed as a top-level action.
Expand Down Expand Up @@ -118,6 +124,10 @@ export class FileAction {
return this._action.default
}

get destructive() {
return this._action.destructive
}

get inline() {
return this._action.inline
}
Expand Down Expand Up @@ -160,6 +170,10 @@ export class FileAction {
throw new Error('Invalid order')
}

if (action.destructive !== undefined && typeof action.destructive !== 'boolean') {
throw new Error('Invalid destructive flag')
}

if ('parent' in action && typeof action.parent !== 'string') {
throw new Error('Invalid parent')
}
Expand Down

0 comments on commit 50465ed

Please sign in to comment.