Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions src/commands/switch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const createSwitchCommand = (program: BaseCommand) =>
program
.command('switch')
.description('Switch your active Netlify account')
.option('--email <email>', 'Switch to the account matching this email address')
.action(async (options: OptionValues, command: BaseCommand) => {
const { switchCommand } = await import('./switch.js')
await switchCommand(options, command)
Expand Down
16 changes: 15 additions & 1 deletion src/commands/switch/switch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,28 @@ import { login } from '../login/login.js'

const LOGIN_NEW = 'I would like to login to a new account'

export const switchCommand = async (_options: OptionValues, command: BaseCommand) => {
export const switchCommand = async (options: OptionValues, command: BaseCommand) => {
const availableUsersChoices = Object.values(command.netlify.globalConfig.get('users') || {}).reduce(
(prev, current) =>
// @ts-expect-error TS(2769) FIXME: No overload matches this call.
Object.assign(prev, { [current.id]: current.name ? `${current.name} (${current.email})` : current.email }),
{},
)

if (options.email) {
const matchedAccount = Object.entries(availableUsersChoices as Record<string, string>).find(([, label]) =>
label.includes(options.email),
)
if (matchedAccount) {
command.netlify.globalConfig.set('userId', matchedAccount[0])
log('')
log(`You're now using ${chalk.bold(matchedAccount[1])}.`)
return
}
Comment thread
sean-roberts marked this conversation as resolved.
log(`No account found matching ${chalk.bold(options.email)}, showing all available accounts.`)
log('')
}

const { accountSwitchChoice } = await inquirer.prompt([
{
type: 'list',
Expand Down
97 changes: 97 additions & 0 deletions tests/unit/commands/switch/switch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { beforeEach, describe, expect, test, vi } from 'vitest'

const { logMessages, mockPrompt, mockLogin } = vi.hoisted(() => ({
logMessages: [] as string[],
mockPrompt: vi.fn(),
mockLogin: vi.fn(),
}))

vi.mock('inquirer', () => ({
default: { prompt: mockPrompt },
}))

vi.mock('../../../../src/utils/command-helpers.js', async () => ({
...(await vi.importActual('../../../../src/utils/command-helpers.js')),
log: (...args: string[]) => {
logMessages.push(args.join(' '))
},
}))

vi.mock('../../../../src/commands/login/login.js', () => ({
login: mockLogin,
}))

import { switchCommand } from '../../../../src/commands/switch/switch.js'

const users = {
'user-1': { id: 'user-1', name: 'Alice', email: 'alice@example.com' },
'user-2': { id: 'user-2', name: 'Bob', email: 'bob@corp.com' },
}

const createCommand = (usersData = users) => {
const mockSet = vi.fn()
const command = {
netlify: {
globalConfig: {
get: vi.fn().mockReturnValue(usersData),
set: mockSet,
},
},
}
return { command, mockSet }
}

describe('switchCommand', () => {
beforeEach(() => {
logMessages.length = 0
vi.clearAllMocks()
})

test('--email auto-switches when a match is found', async () => {
const { command, mockSet } = createCommand()

await switchCommand({ email: 'alice@example.com' }, command as any)

Check failure on line 53 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check failure on line 53 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `BaseCommand`
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

expect(mockSet).toHaveBeenCalledWith('userId', 'user-1')
expect(logMessages.some((m) => m.includes('Alice'))).toBe(true)
expect(mockPrompt).not.toHaveBeenCalled()
})

test('--email falls through to prompt when no match is found', async () => {
const { command } = createCommand()
mockPrompt.mockResolvedValueOnce({ accountSwitchChoice: 'Bob (bob@corp.com)' })

await switchCommand({ email: 'nobody@example.com' }, command as any)

Check failure on line 64 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check failure on line 64 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `BaseCommand`

expect(logMessages.some((m) => m.includes('No account found matching'))).toBe(true)
expect(mockPrompt).toHaveBeenCalled()
})

test('--email matches partial email strings', async () => {
const { command, mockSet } = createCommand()

await switchCommand({ email: 'bob@corp' }, command as any)

Check failure on line 73 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check failure on line 73 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `BaseCommand`

expect(mockSet).toHaveBeenCalledWith('userId', 'user-2')
expect(mockPrompt).not.toHaveBeenCalled()
})

test('without --email shows interactive prompt', async () => {
const { command, mockSet } = createCommand()
mockPrompt.mockResolvedValueOnce({ accountSwitchChoice: 'Alice (alice@example.com)' })

await switchCommand({}, command as any)

Check failure on line 83 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check failure on line 83 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `BaseCommand`

expect(mockPrompt).toHaveBeenCalled()
expect(mockSet).toHaveBeenCalledWith('userId', 'user-1')
})

test('selecting login new triggers login flow', async () => {
const { command } = createCommand()
mockPrompt.mockResolvedValueOnce({ accountSwitchChoice: 'I would like to login to a new account' })

await switchCommand({}, command as any)

Check failure on line 93 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check failure on line 93 in tests/unit/commands/switch/switch.test.ts

View workflow job for this annotation

GitHub Actions / Lint

Unsafe argument of type `any` assigned to a parameter of type `BaseCommand`

expect(mockLogin).toHaveBeenCalledWith({ new: true }, command)
})
})
Loading