Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Example for interactive command testing with v4? #552

Closed
Makeshift opened this issue May 25, 2024 · 1 comment · Fixed by #564
Closed

Example for interactive command testing with v4? #552

Makeshift opened this issue May 25, 2024 · 1 comment · Fixed by #564

Comments

@Makeshift
Copy link

Is your feature request related to a problem? Please describe.
I'm developing a highly-interactive CLI application with oclif that utilises inquirer to perform setup with interaction from the user.

I'd like to test my CLI from the perspective of a potential user, exploring the results from answering these questions.

Without direct support for .stdin() in v4, it isn't clear to me how to achieve this with mock-stdin as suggested in the v4 migration docs.

Describe the solution you'd like
An example of how to use v4 to run tests against interactive CLIs.

Describe alternatives you've considered

  • Revert to v3
  • Test oclif and inquirer separately, implementing stubs to skip questions when testing oclif and using @inquirer/testing to render and test the outputs when testing interaction. I suspect this will be the suggested alternative, but I feel that it would result in not-particularly-representative tests for my application.

Additional context
Here's some code examples of what I'm working with currently:

test/commands/login.test.ts
import { runCommand } from '@oclif/test'
import { expect } from 'chai'
import { dirname, join } from 'node:path'
import { describe } from 'node:test'
import { fileURLToPath } from 'node:url'
// import { render } from '@inquirer/testing'
import { stdin as fstdin } from 'mock-stdin'

const _dirname = dirname(fileURLToPath(import.meta.url))
const root = join(_dirname, '..', '..')
const stdin = fstdin()

describe('command:init', () => {
  it('runs login', async () => {
    const { error, result, stderr, stdout } = await runCommand('login', { root })
    // of course, this won't work because the test fails before this due to it hanging waiting for user input
    stdin.send('\n')
    expect(result).to.equal(true)
  })
})
commands/login.ts
import { Command } from '@oclif/core'
import { confirm } from '@inquirer/prompts'

export class Login extends Command {
  static description = 'Placeholder'

  async run (): Promise<boolean> {
    const result = await confirm({
      message: 'Does this work?'
    })
    return result
  }
}
@mdonnalley
Copy link
Contributor

mdonnalley commented Jun 11, 2024

@Makeshift Sorry I missed this!

To make your test work you should use captureOutput instead of runCommand. This will allow you to send the stdin using mock-stdin before running the command:

import {captureOutput} from '@oclif/test'
import {expect} from 'chai'
import {stdin as fstdin} from 'mock-stdin'
import {describe} from 'node:test'

import {Login} from '../../src/commands/login.js'

const stdin = fstdin()

describe('command:init', () => {
  it('runs login', async () => {
    const {result} = await captureOutput(async () => {
      setTimeout(() => stdin.send('y\n'), 10)
      return Login.run()
    })
    expect(result).to.equal(true)
  })
})

Test oclif and inquirer separately, implementing stubs to skip questions when testing oclif and using @inquirer/testing to render and test the outputs when testing interaction. I suspect this will be the suggested alternative, but I feel that it would result in not-particularly-representative tests for my application.

I think this is the best approach to be honest (and is really the only approach if you have more elaborate prompts that a single confirmation). But for single prompts, I think the example above would suffice for most people

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants