Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/slow-gorillas-care.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@smartthings/cli-testlib": patch
---

mock withLocations by default
106 changes: 106 additions & 0 deletions packages/cli/src/__tests__/commands/installedapps.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { outputListing, withLocations, WithNamedLocation } from '@smartthings/cli-lib'
import { InstalledApp, InstalledAppsEndpoint, SmartThingsClient } from '@smartthings/core-sdk'
import InstalledAppsCommand from '../../commands/installedapps'
import { listTableFieldDefinitions, tableFieldDefinitions } from '../../lib/commands/installedapps/installedapps-util'


const MOCK_INSTALLED_APP = { installedAppId: 'installedAppId' } as InstalledApp
const MOCK_INSTALLED_APP_LIST = [MOCK_INSTALLED_APP]
const MOCK_INSTALLED_APP_WITH_LOCATION = {
installedAppId: 'installedAppId',
location: 'location',
} as InstalledApp & WithNamedLocation

describe('InstalledAppsCommand', () => {
const getSpy = jest.spyOn(InstalledAppsEndpoint.prototype, 'get').mockImplementation()
const listSpy = jest.spyOn(InstalledAppsEndpoint.prototype, 'list').mockImplementation()

const outputListingMock = jest.mocked(outputListing)
const withLocationsMock = jest.mocked(withLocations)

it('calls outputListing with correct config', async () => {
await expect(InstalledAppsCommand.run(['installedAppId'])).resolves.not.toThrow()

expect(outputListingMock).toBeCalledWith(
expect.any(InstalledAppsCommand),
expect.objectContaining({
primaryKeyName: 'installedAppId',
sortKeyName: 'displayName',
listTableFieldDefinitions,
tableFieldDefinitions,
}),
'installedAppId',
expect.any(Function),
expect.any(Function),
)
})

it('calls correct get endpoint', async () => {
await expect(InstalledAppsCommand.run([])).resolves.not.toThrow()

getSpy.mockResolvedValueOnce(MOCK_INSTALLED_APP)

const getFunction = outputListingMock.mock.calls[0][4]

await expect(getFunction('installedAppId')).resolves.toStrictEqual(MOCK_INSTALLED_APP)
expect(getSpy).toBeCalledWith('installedAppId')
})

it('calls correct list endpoint', async () => {
await expect(InstalledAppsCommand.run([])).resolves.not.toThrow()

listSpy.mockResolvedValueOnce(MOCK_INSTALLED_APP_LIST)

const listFunction = outputListingMock.mock.calls[0][3]

await expect(listFunction()).resolves.toEqual(MOCK_INSTALLED_APP_LIST)
expect(listSpy).toBeCalledWith({ locationId: undefined })
})

it('accepts location-id flag to filter list', async () => {
await expect(InstalledAppsCommand.run(['--location-id=locationId'])).resolves.not.toThrow()

let listFunction = outputListingMock.mock.calls[0][3]
await listFunction()

expect(listSpy).toBeCalledWith({ locationId: ['locationId'] })

outputListingMock.mockClear()
listSpy.mockClear()

await expect(InstalledAppsCommand.run(['-l=locationId', '-l=anotherLocationId'])).resolves.not.toThrow()

listFunction = outputListingMock.mock.calls[0][3]
await listFunction()

expect(listSpy).toBeCalledWith({ locationId: ['locationId', 'anotherLocationId'] })
})

it('includes location name when verbose flag is used', async () => {
await expect(InstalledAppsCommand.run(['--verbose'])).resolves.not.toThrow()

expect(outputListingMock).toBeCalledWith(
expect.anything(),
expect.objectContaining({
listTableFieldDefinitions: expect.arrayContaining(['location']),
}),
undefined,
expect.anything(),
expect.anything(),
)

const expectedList = [MOCK_INSTALLED_APP_WITH_LOCATION]

listSpy.mockResolvedValueOnce(MOCK_INSTALLED_APP_LIST)
withLocationsMock.mockResolvedValueOnce(expectedList)
const listFunction = outputListingMock.mock.calls[0][3]

await expect(listFunction()).resolves.toStrictEqual(expectedList)

expect(listSpy).toBeCalledTimes(1)
expect(withLocationsMock).toBeCalledWith(
expect.any(SmartThingsClient),
MOCK_INSTALLED_APP_LIST,
)
})
})
41 changes: 41 additions & 0 deletions packages/cli/src/__tests__/commands/installedapps/delete.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { selectFromList } from '@smartthings/cli-lib'
import { InstalledAppsEndpoint } from '@smartthings/core-sdk'
import InstalledAppDeleteCommand from '../../../commands/installedapps/delete'


describe('InstalledAppDeleteCommand', () => {
const deleteSpy = jest.spyOn(InstalledAppsEndpoint.prototype, 'delete').mockImplementation()
const logSpy = jest.spyOn(InstalledAppDeleteCommand.prototype, 'log').mockImplementation()

const selectFromListMock = jest.mocked(selectFromList).mockResolvedValue('installedAppId')

it('prompts user to select app', async () => {
await expect(InstalledAppDeleteCommand.run(['installedAppId'])).resolves.not.toThrow()

expect(selectFromListMock).toBeCalledWith(
expect.any(InstalledAppDeleteCommand),
expect.objectContaining({
primaryKeyName: 'installedAppId',
sortKeyName: 'displayName',
listTableFieldDefinitions: ['displayName', 'installedAppType', 'installedAppStatus', 'installedAppId'],
}),
expect.objectContaining({
preselectedId: 'installedAppId',
listItems: expect.any(Function),
promptMessage: 'Select an installed app to delete.',
}),
)
})

it('calls correct delete endpoint', async () => {
await expect(InstalledAppDeleteCommand.run([])).resolves.not.toThrow()

expect(deleteSpy).toBeCalledWith('installedAppId')
})

it('logs to user on successful delete', async () => {
await expect(InstalledAppDeleteCommand.run([])).resolves.not.toThrow()

expect(logSpy).toBeCalledWith('Installed app installedAppId deleted.')
})
})
61 changes: 61 additions & 0 deletions packages/cli/src/__tests__/commands/installedapps/rename.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { formatAndWriteItem, selectFromList } from '@smartthings/cli-lib'
import { InstalledApp, InstalledAppsEndpoint } from '@smartthings/core-sdk'
import InstalledAppRenameCommand from '../../../commands/installedapps/rename'
import { listTableFieldDefinitions, tableFieldDefinitions } from '../../../lib/commands/installedapps/installedapps-util'


jest.mock('inquirer')

const MOCK_INSTALLED_APP = { installedAppId: 'installedAppId' } as InstalledApp

describe('InstalledAppRenameCommand', () => {
const selectFromListMock = jest.mocked(selectFromList).mockResolvedValue('installedAppId')
const formatAndWriteItemMock = jest.mocked(formatAndWriteItem)

const updateSpy = jest.spyOn(InstalledAppsEndpoint.prototype, 'update').mockImplementation()
jest.spyOn(InstalledAppsEndpoint.prototype, 'list').mockImplementation()

it('prompts user to select an app', async () => {
await expect(InstalledAppRenameCommand.run(['installedAppId', 'installedAppName'])).resolves.not.toThrow()

expect(selectFromListMock).toBeCalledWith(
expect.any(InstalledAppRenameCommand),
expect.objectContaining({
itemName: 'installed app',
primaryKeyName: 'installedAppId',
sortKeyName: 'displayName',
tableFieldDefinitions,
listTableFieldDefinitions,
}),
expect.objectContaining({
preselectedId: 'installedAppId',
listItems: expect.any(Function),
promptMessage: 'Select an app to rename.',
}),
)
})

it('calls correct update endpoint', async () => {
await expect(InstalledAppRenameCommand.run(['installedAppId', 'installedAppName'])).resolves.not.toThrow()

expect(updateSpy).toBeCalledWith('installedAppId', { displayName: 'installedAppName' })
})

it('outputs updated app to user', async () => {
updateSpy.mockResolvedValueOnce(MOCK_INSTALLED_APP)

await expect(InstalledAppRenameCommand.run(['installedAppId', 'installedAppName'])).resolves.not.toThrow()

expect(formatAndWriteItemMock).toBeCalledWith(
expect.any(InstalledAppRenameCommand),
expect.objectContaining({
itemName: 'installed app',
primaryKeyName: 'installedAppId',
sortKeyName: 'displayName',
tableFieldDefinitions,
listTableFieldDefinitions,
}),
MOCK_INSTALLED_APP,
)
})
})
16 changes: 2 additions & 14 deletions packages/cli/src/commands/installedapps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,10 @@ import { Flags } from '@oclif/core'

import { InstalledApp, InstalledAppListOptions } from '@smartthings/core-sdk'

import { APICommand, outputListing, TableFieldDefinition, withLocations } from '@smartthings/cli-lib'
import { APICommand, outputListing, withLocations } from '@smartthings/cli-lib'
import { InstalledAppWithLocation, listTableFieldDefinitions, tableFieldDefinitions } from '../lib/commands/installedapps/installedapps-util'


export type InstalledAppWithLocation = InstalledApp & { location?: string }

export const listTableFieldDefinitions = ['displayName', 'installedAppType', 'installedAppStatus', 'installedAppId']
export const tableFieldDefinitions: TableFieldDefinition<InstalledApp>[] = [
'displayName', 'installedAppId', 'installedAppType', 'installedAppStatus',
'singleInstance', 'appId', 'locationId', 'singleInstance',
{
label: 'Classifications',
value: installedApp => installedApp.classifications?.join('\n') ?? '',
include: installedApp => !!installedApp.classifications,
},
]

export default class InstalledAppsCommand extends APICommand<typeof InstalledAppsCommand.flags> {
static description = 'get a specific app or a list of apps'

Expand Down
5 changes: 2 additions & 3 deletions packages/cli/src/commands/installedapps/rename.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@ import inquirer from 'inquirer'
import { InstalledAppListOptions } from '@smartthings/core-sdk'

import { APICommand, formatAndWriteItem, selectFromList, withLocations } from '@smartthings/cli-lib'
import { listTableFieldDefinitions, tableFieldDefinitions } from '../../lib/commands/installedapps/installedapps-util'

import { listTableFieldDefinitions, tableFieldDefinitions } from '../installedapps'


export default class DeviceComponentStatusCommand extends APICommand<typeof DeviceComponentStatusCommand.flags> {
export default class InstalledAppRenameCommand extends APICommand<typeof InstalledAppRenameCommand.flags> {
static description = 'renamed an installed app instance'

static flags = {
Expand Down
17 changes: 17 additions & 0 deletions packages/cli/src/lib/commands/installedapps/installedapps-util.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { TableFieldDefinition } from '@smartthings/cli-lib'
import { InstalledApp } from '@smartthings/core-sdk'


export type InstalledAppWithLocation = InstalledApp & { location?: string }

export const listTableFieldDefinitions = ['displayName', 'installedAppType', 'installedAppStatus', 'installedAppId']

export const tableFieldDefinitions: TableFieldDefinition<InstalledApp>[] = [
'displayName', 'installedAppId', 'installedAppType', 'installedAppStatus',
'singleInstance', 'appId', 'locationId', 'singleInstance',
{
label: 'Classifications',
value: installedApp => installedApp.classifications?.join('\n') ?? '',
include: installedApp => !!installedApp.classifications,
},
]
1 change: 1 addition & 0 deletions packages/testlib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ jest.mock('@smartthings/cli-lib', () => {
outputItem: jest.fn(),
resetManagedConfig: jest.fn(),
formatAndWriteItem: jest.fn(),
withLocations: jest.fn(),
withLocationsAndRooms: jest.fn(),
yamlExists: jest.fn(),
chooseDevice: jest.fn(),
Expand Down