Skip to content

Commit cecdfd8

Browse files
committed
refactor: move devicepreferences:translations:create command to yargs
1 parent 255e913 commit cecdfd8

File tree

10 files changed

+295
-92
lines changed

10 files changed

+295
-92
lines changed

packages/cli/src/commands/devicepreferences/translations/create.ts

Lines changed: 0 additions & 33 deletions
This file was deleted.

packages/cli/src/lib/commands/devicepreferences/translations-util.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { jest } from '@jest/globals'
2+
3+
import type { ArgumentsCamelCase, Argv } from 'yargs'
4+
5+
import type {
6+
DevicePreferencesEndpoint,
7+
PreferenceLocalization,
8+
SmartThingsClient,
9+
} from '@smartthings/core-sdk'
10+
11+
import type { CommandArgs } from '../../../../commands/devicepreferences/translations/create.js'
12+
import type {
13+
APIOrganizationCommand,
14+
APIOrganizationCommandFlags,
15+
apiOrganizationCommand,
16+
apiOrganizationCommandBuilder,
17+
} from '../../../../lib/command/api-organization-command.js'
18+
import type {
19+
inputAndOutputItem,
20+
inputAndOutputItemBuilder,
21+
} from '../../../../lib/command/basic-io.js'
22+
import type { chooseDevicePreference } from '../../../../lib/command/util/devicepreferences-util.js'
23+
import {
24+
tableFieldDefinitions,
25+
} from '../../../../lib/command/util/devicepreferences/translations-util.js'
26+
import { buildArgvMock, buildArgvMockStub } from '../../../test-lib/builder-mock.js'
27+
28+
29+
const apiOrganizationCommandMock = jest.fn<typeof apiOrganizationCommand>()
30+
const apiOrganizationCommandBuilderMock = jest.fn<typeof apiOrganizationCommandBuilder>()
31+
jest.unstable_mockModule('../../../../lib/command/api-organization-command.js', () => ({
32+
apiOrganizationCommand: apiOrganizationCommandMock,
33+
apiOrganizationCommandBuilder: apiOrganizationCommandBuilderMock,
34+
}))
35+
36+
const inputAndOutputItemMock = jest.fn<typeof inputAndOutputItem>()
37+
const inputAndOutputItemBuilderMock = jest.fn<typeof inputAndOutputItemBuilder>()
38+
jest.unstable_mockModule('../../../../lib/command/basic-io.js', () => ({
39+
inputAndOutputItem: inputAndOutputItemMock,
40+
inputAndOutputItemBuilder: inputAndOutputItemBuilderMock,
41+
}))
42+
43+
const chooseDevicePreferenceMock = jest.fn<typeof chooseDevicePreference>()
44+
jest.unstable_mockModule('../../../../lib/command/util/devicepreferences-util.js', () => ({
45+
chooseDevicePreference: chooseDevicePreferenceMock,
46+
}))
47+
48+
49+
const {
50+
default: cmd,
51+
} = await import('../../../../commands/devicepreferences/translations/create.js')
52+
53+
54+
test('builder', () => {
55+
const yargsMock = buildArgvMockStub<object>()
56+
const {
57+
yargsMock: apiOrganizationCommandBuilderArgvMock,
58+
positionalMock,
59+
exampleMock,
60+
argvMock,
61+
} = buildArgvMock<APIOrganizationCommandFlags, CommandArgs>()
62+
63+
apiOrganizationCommandBuilderMock.mockReturnValueOnce(apiOrganizationCommandBuilderArgvMock)
64+
inputAndOutputItemBuilderMock.mockReturnValueOnce(argvMock)
65+
66+
const builder = cmd.builder as (yargs: Argv<object>) => Argv<CommandArgs>
67+
68+
expect(builder(yargsMock)).toBe(argvMock)
69+
70+
expect(apiOrganizationCommandBuilderMock).toHaveBeenCalledExactlyOnceWith(yargsMock)
71+
expect(inputAndOutputItemBuilderMock)
72+
.toHaveBeenCalledExactlyOnceWith(apiOrganizationCommandBuilderArgvMock)
73+
74+
expect(exampleMock).toHaveBeenCalledTimes(1)
75+
expect(positionalMock).toHaveBeenCalledTimes(1)
76+
})
77+
78+
test('handler', async () => {
79+
const apiDevicePreferencesCreateTranslationsMock =
80+
jest.fn<typeof DevicePreferencesEndpoint.prototype['createTranslations']>()
81+
82+
const clientMock = {
83+
devicePreferences: {
84+
createTranslations: apiDevicePreferencesCreateTranslationsMock,
85+
},
86+
} as unknown as SmartThingsClient
87+
const command = {
88+
client: clientMock,
89+
} as APIOrganizationCommand<CommandArgs>
90+
apiOrganizationCommandMock.mockResolvedValueOnce(command)
91+
chooseDevicePreferenceMock.mockResolvedValueOnce('chosen-id')
92+
93+
const inputArgv = {
94+
profile: 'default',
95+
devicePreferenceId: 'argv-id',
96+
} as unknown as ArgumentsCamelCase<CommandArgs>
97+
await expect(cmd.handler(inputArgv)).resolves.not.toThrow()
98+
99+
expect(apiOrganizationCommandMock).toHaveBeenCalledExactlyOnceWith(inputArgv)
100+
expect(chooseDevicePreferenceMock).toHaveBeenCalledExactlyOnceWith(command, 'argv-id')
101+
expect(inputAndOutputItemMock).toHaveBeenCalledExactlyOnceWith(
102+
command,
103+
{ tableFieldDefinitions },
104+
expect.any(Function),
105+
)
106+
107+
const create = inputAndOutputItemMock.mock.calls[0][2]
108+
109+
const translationCreate = { description: 'Translation To Create' } as PreferenceLocalization
110+
const createdTranslation = { description: 'Created Translation' } as PreferenceLocalization
111+
112+
apiDevicePreferencesCreateTranslationsMock.mockResolvedValueOnce(createdTranslation)
113+
114+
expect(await create(undefined, translationCreate)).toBe(createdTranslation)
115+
116+
expect(apiDevicePreferencesCreateTranslationsMock)
117+
.toHaveBeenCalledExactlyOnceWith('chosen-id', translationCreate)
118+
})
Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
import { jest } from '@jest/globals'
22

3-
import { DevicePreference, DevicePreferencesEndpoint, SmartThingsClient } from '@smartthings/core-sdk'
3+
import type {
4+
DevicePreference,
5+
DevicePreferencesEndpoint,
6+
SmartThingsClient,
7+
} from '@smartthings/core-sdk'
48

5-
import { APICommand } from '../../../../lib/command/api-command.js'
6-
import { SelectFromListFlags, selectFromList } from '../../../../lib/command/select.js'
7-
import { ValueTableFieldDefinition } from '../../../../lib/table-generator.js'
9+
import type { ValueTableFieldDefinition } from '../../../../lib/table-generator.js'
10+
import type { ChooseFunction, createChooseFn } from '../../../../lib/command/util/util-util.js'
811

912

10-
const selectFromListMock = jest.fn<typeof selectFromList>()
11-
jest.unstable_mockModule('../../../../lib/command/select.js', () => ({
12-
selectFromList: selectFromListMock,
13+
const createChooseFnMock = jest.fn<typeof createChooseFn<DevicePreference>>()
14+
jest.unstable_mockModule('../../../../lib/command/util/util-util.js', () => ({
15+
createChooseFn: createChooseFnMock,
1316
}))
1417

1518

1619
const {
17-
chooseDevicePreference,
20+
chooseDevicePreferenceFn,
1821
tableFieldDefinitions,
1922
} = await import('../../../../lib/command/util/devicepreferences-util.js')
2023

@@ -39,29 +42,29 @@ describe('tableFieldDefinitions options definition', () => {
3942
})
4043

4144
test('chooseDevicePreference', async () => {
42-
selectFromListMock.mockResolvedValueOnce('chosen-id')
43-
const listMock = jest.fn<typeof DevicePreferencesEndpoint.prototype.list>()
44-
const client = { devicePreferences: { list: listMock } } as unknown as SmartThingsClient
45-
const command = { client } as APICommand<SelectFromListFlags>
45+
const chooseAppMock = jest.fn<ChooseFunction<DevicePreference>>()
46+
createChooseFnMock.mockReturnValueOnce(chooseAppMock)
4647

47-
expect(await chooseDevicePreference(command, 'preselected-id')).toBe('chosen-id')
48+
const chooseApp = chooseDevicePreferenceFn()
4849

49-
expect(selectFromListMock).toHaveBeenCalledTimes(1)
50-
expect(selectFromListMock).toHaveBeenCalledWith(
51-
command,
50+
expect(chooseApp).toBe(chooseAppMock)
51+
52+
expect(createChooseFnMock).toHaveBeenCalledExactlyOnceWith(
5253
expect.objectContaining({ itemName: 'device preference' }),
53-
{
54-
preselectedId: 'preselected-id',
55-
listItems: expect.any(Function),
56-
},
54+
expect.any(Function),
5755
)
5856

59-
const listItems = selectFromListMock.mock.calls[0][2].listItems
6057
const devicePreferenceList = [{ preferenceId: 'device-preference-id' } as DevicePreference]
61-
listMock.mockResolvedValueOnce(devicePreferenceList)
58+
const apiDevicePreferencesListMock = jest.fn<typeof DevicePreferencesEndpoint.prototype.list>()
59+
.mockResolvedValueOnce(devicePreferenceList)
60+
const listItems = createChooseFnMock.mock.calls[0][1]
61+
const client = {
62+
devicePreferences: {
63+
list: apiDevicePreferencesListMock,
64+
},
65+
} as unknown as SmartThingsClient
6266

63-
expect(await listItems()).toBe(devicePreferenceList)
67+
expect(await listItems(client)).toBe(devicePreferenceList)
6468

65-
expect(listMock).toHaveBeenCalledTimes(1)
66-
expect(listMock).toHaveBeenCalledWith()
69+
expect(apiDevicePreferencesListMock).toHaveBeenCalledExactlyOnceWith()
6770
})
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import type { PreferenceLocalization } from '@smartthings/core-sdk'
2+
3+
import { ValueTableFieldDefinition } from '../../../../../lib/table-generator.js'
4+
import {
5+
tableFieldDefinitions,
6+
} from '../../../../../lib/command/util/devicepreferences/translations-util.js'
7+
8+
9+
const unpopulatedOptions: (Record<string, { label: string }> | null | undefined)[] = [
10+
null,
11+
undefined,
12+
{},
13+
]
14+
15+
const populatedOptions: ({
16+
options: Record<string, { label: string }>
17+
expected: string
18+
})[] = [
19+
{ options: { key: { label: 'Translation' } }, expected: 'key: Translation' },
20+
{
21+
options: { key1: { label: 'Translation 1' }, key2: { label: 'Translation 2' } },
22+
expected: 'key1: Translation 1\nkey2: Translation 2',
23+
},
24+
]
25+
26+
const definition = (tableFieldDefinitions[3] as ValueTableFieldDefinition<PreferenceLocalization>)
27+
28+
describe('options include function', () => {
29+
const include = definition.include
30+
31+
it.each(unpopulatedOptions)('returns false for unpopulated options', (options) => {
32+
expect(include?.({ options } as PreferenceLocalization)).toBeFalse()
33+
})
34+
35+
it.each(populatedOptions)('returns true for populated options', ({ options }) => {
36+
expect(include?.({ options } as PreferenceLocalization)).toBeTrue()
37+
})
38+
})
39+
40+
describe('options value function', () => {
41+
const value = definition.value
42+
43+
it.each(unpopulatedOptions)('returns falsy for unpopulated options', (options) => {
44+
expect(value?.({ options } as PreferenceLocalization)).toBeFalsy()
45+
})
46+
47+
it.each(populatedOptions)(
48+
'returns string representation for populated options',
49+
({ options, expected }) => {
50+
expect(value?.({ options } as PreferenceLocalization)).toBe(expected)
51+
},
52+
)
53+
})
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { ArgumentsCamelCase, Argv, CommandModule } from 'yargs'
2+
3+
import { PreferenceLocalization } from '@smartthings/core-sdk'
4+
5+
import {
6+
apiOrganizationCommand,
7+
apiOrganizationCommandBuilder,
8+
type APIOrganizationCommandFlags,
9+
} from '../../../lib/command/api-organization-command.js'
10+
import {
11+
inputAndOutputItem,
12+
inputAndOutputItemBuilder,
13+
InputAndOutputItemFlags,
14+
} from '../../../lib/command/basic-io.js'
15+
import { chooseDevicePreference } from '../../../lib/command/util/devicepreferences-util.js'
16+
import {
17+
tableFieldDefinitions,
18+
} from '../../../lib/command/util/devicepreferences/translations-util.js'
19+
20+
21+
export type CommandArgs =
22+
& APIOrganizationCommandFlags
23+
& InputAndOutputItemFlags
24+
& {
25+
devicePreferenceId?: string
26+
}
27+
28+
const command = 'devicepreferences:translations:create [device-preference-id]'
29+
30+
const describe = 'create a translation for a device preference'
31+
32+
const builder = (yargs: Argv): Argv<CommandArgs> =>
33+
inputAndOutputItemBuilder(apiOrganizationCommandBuilder(yargs))
34+
.positional('device-preference-id', { describe: 'device preference id', type: 'string' })
35+
.example([
36+
[
37+
'$0 devicepreferences:translations:create -i preferenceTranslation.json',
38+
'create a translation as defined in preferenceTranslation.json',
39+
],
40+
])
41+
42+
const handler = async (argv: ArgumentsCamelCase<CommandArgs>): Promise<void> => {
43+
const command = await apiOrganizationCommand(argv)
44+
45+
const preferenceId = await chooseDevicePreference(command, argv.devicePreferenceId)
46+
47+
await inputAndOutputItem<PreferenceLocalization, PreferenceLocalization>(
48+
command,
49+
{ tableFieldDefinitions },
50+
(_, translation) =>
51+
command.client.devicePreferences.createTranslations(preferenceId, translation),
52+
)
53+
}
54+
55+
const cmd: CommandModule<object, CommandArgs> = { command, describe, builder, handler }
56+
export default cmd

src/commands/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { CommandModule } from 'yargs'
33
import appsCommand from './apps.js'
44
import configCommand from './config.js'
55
import devicepreferencesCommand from './devicepreferences.js'
6+
import devicepreferencesTranslationsCreateCommand from './devicepreferences/translations/create.js'
67
import deviceprofilesCommand from './deviceprofiles.js'
78
import devicesCapabilityStatusCommand from './devices/capability-status.js'
89
import devicesPreferencesCommand from './devices/preferences.js'
@@ -18,6 +19,7 @@ export const commands: CommandModule<object, any>[] = [
1819
appsCommand,
1920
configCommand,
2021
devicepreferencesCommand,
22+
devicepreferencesTranslationsCreateCommand,
2123
deviceprofilesCommand,
2224
devicesCapabilityStatusCommand,
2325
devicesPreferencesCommand,

0 commit comments

Comments
 (0)