示例#1
0
class TestArgumentExperimental(unittest.TestCase):
    def setUp(self):
        from knack.help_files import helps

        class LoggerAction(argparse.Action):
            def __call__(self, parser, namespace, values, option_string=None):
                print("Side-effect from some original action!",
                      file=sys.stderr)

        class ExperimentalTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(ExperimentalTestCommandLoader,
                      self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('arg-test', 'example_arg_handler')
                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, 'arg-test') as c:
                    c.argument('arg1',
                               help='Arg1',
                               is_experimental=True,
                               action=LoggerAction)

                super(ExperimentalTestCommandLoader,
                      self).load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(
            commands_loader_cls=ExperimentalTestCommandLoader)

    @redirect_io
    def test_experimental_arguments_command_help(self):
        """ Ensure experimental arguments appear correctly in command help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('arg-test -h'.split())
        actual = self.io.getvalue()
        expected = """
Arguments
    --arg1 [Experimental] [Required] : Arg1.
        Argument '--arg1' is experimental and not covered by customer support. Please use with discretion.
""".format(self.cli_ctx.name)
        self.assertIn(remove_space(expected), remove_space(actual))

    @redirect_io
    def test_experimental_arguments_execute(self):
        """ Ensure deprecated arguments can be used. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split())
        actual = self.io.getvalue()
        experimental_expected = "Argument '--arg1' is experimental and not covered by customer support. " \
                                "Please use with discretion."
        self.assertIn(experimental_expected, actual)

        action_expected = "Side-effect from some original action!"
        self.assertIn(action_expected, actual)
示例#2
0
class TestArgumentPreview(unittest.TestCase):
    def setUp(self):

        from knack.help_files import helps

        class PreviewTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(PreviewTestCommandLoader, self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('arg-test', 'example_arg_handler')
                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, 'arg-test') as c:
                    c.argument('arg1', help='Arg1', is_preview=True)

                super(PreviewTestCommandLoader, self).load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader)

    @redirect_io
    def test_preview_arguments_command_help(self):
        """ Ensure preview arguments appear correctly in command help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('arg-test -h'.split())
        actual = self.io.getvalue()
        expected = """
Arguments
    --arg1 [Preview] [Required] : Arg1.
        Argument '--arg1' is in preview. It may be changed/removed in a future release.
""".format(self.cli_ctx.name)
        self.assertIn(expected, actual)

    @redirect_io
    def test_preview_arguments_execute(self):
        """ Ensure deprecated arguments can be used. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split())
        actual = self.io.getvalue()
        expected = "Argument '--arg1' is in preview. It may be changed/removed in a future release."
        self.assertIn(expected, actual)
示例#3
0
class TestCommandWithConfiguredDefaults(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        # Ensure initialization has occurred correctly
        logging.basicConfig(level=logging.DEBUG)

    @classmethod
    def tearDownClass(cls):
        logging.shutdown()

    def _set_up_command_table(self, required):
        class TestCommandsLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(TestCommandsLoader, self).load_command_table(args)
                with CommandGroup(self, 'foo',
                                  '{}#{{}}'.format(__name__)) as g:
                    g.command('list', 'list_foo')
                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, 'foo') as c:
                    c.argument('my_param',
                               options_list='--my-param',
                               configured_default='param',
                               required=required)
                super(TestCommandsLoader, self).load_arguments(command)

        self.cli_ctx = DummyCLI(commands_loader_cls=TestCommandsLoader)

    @mock.patch.dict(os.environ, {'CLI_DEFAULTS_PARAM': 'myVal'})
    @redirect_io
    def test_apply_configured_defaults_on_required_arg(self):
        self._set_up_command_table(required=True)
        self.cli_ctx.invoke('foo list'.split())
        actual = self.io.getvalue()
        expected = 'myVal'
        self.assertEqual(expected, actual)

    @redirect_io
    def test_no_configured_default_on_required_arg(self):
        self._set_up_command_table(required=True)
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('foo list'.split())
        actual = self.io.getvalue()
        expected = 'required: --my-param'
        if sys.version_info[0] == 2:
            expected = 'argument --my-param is required'
        self.assertEqual(expected in actual, True)

    @mock.patch.dict(os.environ, {'CLI_DEFAULTS_PARAM': 'myVal'})
    @redirect_io
    def test_apply_configured_defaults_on_optional_arg(self):
        self._set_up_command_table(required=False)
        self.cli_ctx.invoke('foo list'.split())
        actual = self.io.getvalue()
        expected = 'myVal'
        self.assertEqual(expected, actual)
示例#4
0
class TestCommandPreview(unittest.TestCase):
    def setUp(self):

        from knack.help_files import helps

        class PreviewTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(PreviewTestCommandLoader, self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('cmd1', 'example_handler', is_preview=True)

                with CommandGroup(self,
                                  'grp1',
                                  '{}#{{}}'.format(__name__),
                                  is_preview=True) as g:
                    g.command('cmd1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, '') as c:
                    c.argument('arg1',
                               options_list=['--arg', '-a'],
                               required=False,
                               type=int,
                               choices=[1, 2, 3])
                    c.argument('arg2',
                               options_list=['-b'],
                               required=True,
                               choices=['a', 'b', 'c'])

                super(PreviewTestCommandLoader, self).load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader)

    @redirect_io
    def test_preview_command_group_help(self):
        """ Ensure preview commands appear correctly in group help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('-h'.split())
        actual = self.io.getvalue()
        expected = u"""
Group
    {}

Subgroups:
    grp1 [Preview] : A group.

Commands:
    cmd1 [Preview] : Short summary here.

""".format(self.cli_ctx.name)
        self.assertEqual(expected, actual)

    @redirect_io
    def test_preview_command_plain_execute(self):
        """ Ensure general warning displayed when running preview command. """
        self.cli_ctx.invoke('cmd1 -b b'.split())
        actual = self.io.getvalue()
        expected = "This command is in preview. It may be changed/removed in a future release."
        self.assertIn(expected, actual)
示例#5
0
class TestCommandGroupPreview(unittest.TestCase):
    def setUp(self):

        from knack.help_files import helps

        class PreviewTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(PreviewTestCommandLoader, self).load_command_table(args)

                with CommandGroup(self,
                                  'group1',
                                  '{}#{{}}'.format(__name__),
                                  is_preview=True) as g:
                    g.command('cmd1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, '') as c:
                    c.argument('arg1',
                               options_list=['--arg', '-a'],
                               required=False,
                               type=int,
                               choices=[1, 2, 3])
                    c.argument('arg2',
                               options_list=['-b'],
                               required=True,
                               choices=['a', 'b', 'c'])

                super(PreviewTestCommandLoader, self).load_arguments(command)

        helps['group1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader)

    @redirect_io
    def test_preview_command_group_help_plain(self):
        """ Ensure help warnings appear for preview command group help. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group1 -h'.split())
        actual = self.io.getvalue()
        expected = """
Group
    cli group1 : A group.
        This command group is in preview. It may be changed/removed in a future release.
Commands:
    cmd1 : Short summary here.

""".format(self.cli_ctx.name)
        self.assertEqual(expected, actual)

    @redirect_io
    def test_preview_command_implicitly(self):
        """ Ensure help warning displayed for command in preview because of a preview parent group. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group1 cmd1 -h'.split())
        actual = self.io.getvalue()
        expected = """
Command
    {} group1 cmd1 : Short summary here.
        Long summary here. Still long summary. Command group 'group1' is in preview. It may be
        changed/removed in a future release.
""".format(self.cli_ctx.name)
        self.assertIn(expected, actual)
示例#6
0
class TestHelp(unittest.TestCase):
    def setUp(self):

        from knack.help_files import helps

        class HelpTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(HelpTestCommandLoader, self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('n1', 'example_handler')
                    g.command('n2', 'example_handler')
                    g.command('n3', 'example_handler')
                    g.command('n4', 'example_handler')
                    g.command('n5', 'example_handler')

                with CommandGroup(self, 'group alpha',
                                  '{}#{{}}'.format(__name__)) as g:
                    g.command('n1', 'example_handler')

                with CommandGroup(self, 'group beta',
                                  '{}#{{}}'.format(__name__)) as g:
                    g.command('n1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                for scope in ['n1', 'group alpha', 'group beta']:
                    with ArgumentsContext(self, scope) as c:
                        c.argument('arg1',
                                   options_list=['--arg', '-a'],
                                   required=False,
                                   type=int,
                                   choices=[1, 2, 3])
                        c.argument('arg2',
                                   options_list=['-b'],
                                   required=True,
                                   choices=['a', 'b', 'c'])

                for scope in ['n4', 'n5']:
                    with ArgumentsContext(self, scope) as c:
                        c.argument('arg1', options_list=['--foobar'])
                        c.argument('arg2',
                                   options_list=['--foobar2'],
                                   required=True)
                        c.argument('arg3',
                                   options_list=['--foobar3'],
                                   help='the foobar3')

                super(HelpTestCommandLoader, self).load_arguments(command)

        helps['n2'] = """
    type: command
    short-summary: YAML short summary.
    long-summary: YAML long summary. More summary.
"""

        helps['n3'] = """
    type: command
    long-summary: |
        line1
        line2
"""

        helps['n4'] = """
    type: command
    parameters:
        - name: --foobar
          type: string
          required: false
          short-summary: one line partial sentence
          long-summary: text, markdown, etc.
          populator-commands:
            - mycli abc xyz
            - default
        - name: --foobar2
          type: string
          required: true
          short-summary: one line partial sentence
          long-summary: paragraph(s)
"""

        helps['n5'] = """
    type: command
    short-summary: this module does xyz one-line or so
    long-summary: |
        this module.... kjsdflkj... klsfkj paragraph1
        this module.... kjsdflkj... klsfkj paragraph2
    parameters:
        - name: --foobar
          type: string
          required: false
          short-summary: one line partial sentence
          long-summary: text, markdown, etc.
          populator-commands:
            - mycli abc xyz
            - default
        - name: --foobar2
          type: string
          required: true
          short-summary: one line partial sentence
          long-summary: paragraph(s)
    examples:
        - name: foo example
          text: example details
"""

        helps['group alpha'] = """
    type: group
    short-summary: this module does xyz one-line or so
    long-summary: |
        this module.... kjsdflkj... klsfkj paragraph1
        this module.... kjsdflkj... klsfkj paragraph2
"""

        helps['group alpha n1'] = """
    short-summary: this module does xyz one-line or so
    long-summary: |
        this module.... kjsdflkj... klsfkj paragraph1
        this module.... kjsdflkj... klsfkj paragraph2
    parameters:
        - name: --arg -a
          type: string
          required: false
          short-summary: one line partial sentence
          long-summary: text, markdown, etc.
          populator-commands:
            - mycli abc xyz
            - default
        - name: -b
          type: string
          required: true
          short-summary: one line partial sentence
          long-summary: paragraph(s)
    examples:
        - name: foo example
          text: example details
"""

        self.cli_ctx = DummyCLI(commands_loader_cls=HelpTestCommandLoader)

    @redirect_io
    def test_choice_list_with_ints(self):
        """ Ensure choice_list works with integer lists. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n1 -h'.split())
        actual = io.getvalue()
        expected = 'Allowed values: 1, 2, 3'
        self.assertIn(expected, actual)

    @redirect_io
    def test_help_param(self):
        """ Ensure both --help and -h produce the same output. """

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n1 -h'.split())

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n1 --help'.split())

    @redirect_io
    def test_help_long_and_short_description_from_docstring(self):
        """ Ensure the first sentence of a docstring is parsed as the short summary and subsequent text is interpretted
        as the long summary. """

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n1 -h'.split())
        actual = io.getvalue()
        expected = '\nCommand\n    {} n1 : Short summary here.\n        Long summary here. Still long summary.'.format(
            self.cli_ctx.name)
        self.assertTrue(actual.startswith(expected))

    @redirect_io
    def test_help_long_and_short_description_from_yaml(self):
        """ Ensure the YAML version of short and long summary display correctly and override any values that may
        have been obtained through reflection. """

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n2 -h'.split())
        actual = io.getvalue()
        expected = '\nCommand\n    {} n2 : YAML short summary.\n        YAML long summary. More summary.'.format(
            self.cli_ctx.name)
        self.assertTrue(actual.startswith(expected))

    @redirect_io
    def test_help_long_description_multi_line(self):
        """ Ensure that multi-line help in the YAML is displayed correctly. """

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n3 -h'.split())
        actual = io.getvalue()
        expected = '\nCommand\n    {} n3 : Short summary here.\n        Line1\n        line2.\n'.format(
            self.cli_ctx.name)
        self.assertTrue(actual.startswith(expected))

    @redirect_io
    def test_help_params_documentations(self):
        """ Ensure argument help is rendered according to the YAML spec. """

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n4 -h'.split())
        expected = """
Command
    {} n4 : Short summary here.

Arguments
    --foobar  [Required] : One line partial sentence.  Values from: mycli abc xyz, default.
        Text, markdown, etc.
    --foobar2 [Required] : One line partial sentence.
        Paragraph(s).
    --foobar3            : The foobar3.
"""
        actual = io.getvalue()
        expected = expected.format(self.cli_ctx.name)
        self.assertTrue(actual.startswith(expected))

    @redirect_io
    def test_help_full_documentations(self):
        """ Test all features of YAML format. """

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n5 -h'.split())
        expected = """
Command
    {} n5 : This module does xyz one-line or so.
        This module.... kjsdflkj... klsfkj paragraph1
        this module.... kjsdflkj... klsfkj paragraph2.

Arguments
    --foobar  [Required] : One line partial sentence.  Values from: mycli abc xyz, default.
        Text, markdown, etc.
    --foobar2 [Required] : One line partial sentence.
        Paragraph(s).
    --foobar3            : The foobar3.

Global Arguments
    --debug              : Increase logging verbosity to show all debug logs.
    --help -h            : Show this help message and exit.
    --output -o          : Output format.  Allowed values: json, jsonc, table, tsv.  Default: json.
    --query              : JMESPath query string. See http://jmespath.org/ for more information and
                           examples.
    --verbose            : Increase logging verbosity. Use --debug for full debug logs.

Examples
    foo example
        example details

"""
        actual = io.getvalue()
        expected = expected.format(self.cli_ctx.name)
        self.assertTrue(actual.startswith(expected))

    @redirect_io
    def test_help_with_param_specified(self):
        """ Ensure help appears even if some arguments are specified. """

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n1 --arg 1 -h'.split())
        expected = """
Command
    {} n1 : Short summary here.
        Long summary here. Still long summary.

Arguments
    -b [Required] : Allowed values: a, b, c.
    --arg -a      : Allowed values: 1, 2, 3.
    --arg3

Global Arguments
    --debug       : Increase logging verbosity to show all debug logs.
    --help -h     : Show this help message and exit.
    --output -o   : Output format.  Allowed values: json, jsonc, table, tsv.  Default: json.
    --query       : JMESPath query string. See http://jmespath.org/ for more information and
                    examples.
    --verbose     : Increase logging verbosity. Use --debug for full debug logs.

"""
        actual = io.getvalue()
        expected = expected.format(self.cli_ctx.name)
        self.assertEqual(actual, expected)

    @redirect_io
    def test_help_group_children(self):
        """ Ensure subgroups appear correctly. """

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group -h'.split())

        expected = """
Group
    {} group

Subgroups:
    alpha : This module does xyz one-line or so.
    beta

"""
        actual = io.getvalue()
        expected = expected.format(self.cli_ctx.name)
        self.assertEqual(actual, expected)

    @redirect_io
    def test_help_missing_params(self):
        """ Ensure the appropriate error is thrown when a required argument is missing. """

        # work around an argparse behavior where output is not printed and SystemExit
        # is not raised on Python 2.7.9
        if sys.version_info < (2, 7, 10):
            try:
                self.cli_ctx.invoke('n1 -a 1 --arg 2'.split())
            except SystemExit:
                pass
        else:
            with self.assertRaises(SystemExit):
                self.cli_ctx.invoke('n1 -a 1 --arg 2'.split())

            actual = io.getvalue()
            self.assertTrue('required' in actual and '-b' in actual)

    @redirect_io
    def test_help_extra_params(self):
        """ Ensure appropriate error is thrown when an extra argument is used. """

        # work around an argparse behavior where output is not printed and SystemExit
        # is not raised on Python 2.7.9
        if sys.version_info < (2, 7, 10):
            try:
                self.cli_ctx.invoke('n1 -a 1 -b c -c extra'.split())
            except SystemExit:
                pass
        else:
            with self.assertRaises(SystemExit):
                self.cli_ctx.invoke('n1 -a 1 -b c -c extra'.split())

        actual = io.getvalue()
        expected = 'unrecognized arguments: -c extra'
        self.assertIn(expected, actual)

    @redirect_io
    def test_help_group_help(self):
        """ Ensure group help appears correctly. """

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group alpha -h'.split())
        expected = """
Group
    {} group alpha : This module does xyz one-line or so.
        This module.... kjsdflkj... klsfkj paragraph1
        this module.... kjsdflkj... klsfkj paragraph2.

Commands:
    n1 : This module does xyz one-line or so.

"""
        actual = io.getvalue()
        expected = expected.format(self.cli_ctx.name)
        self.assertEqual(actual, expected)

    @redirect_io
    @mock.patch('knack.cli.CLI.register_event')
    def test_help_global_params(self, _):
        """ Ensure global parameters can be added and display correctly. """
        def register_globals(_, **kwargs):
            arg_group = kwargs.get('arg_group')
            arg_group.add_argument('--exampl',
                                   help='This is a new global argument.')

        self.cli_ctx._event_handlers[EVENT_PARSER_GLOBAL_CREATE].append(
            register_globals)  # pylint: disable=protected-access

        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('n1 -h'.split())
        s = """
Command
    {} n1 : Short summary here.
        Long summary here. Still long summary.

Arguments
    -b [Required] : Allowed values: a, b, c.
    --arg -a      : Allowed values: 1, 2, 3.
    --arg3

Global Arguments
    --debug       : Increase logging verbosity to show all debug logs.
    --exampl      : This is a new global argument.
    --help -h     : Show this help message and exit.
    --output -o   : Output format.  Allowed values: json, jsonc, table, tsv.  Default: json.
    --query       : JMESPath query string. See http://jmespath.org/ for more information and
                    examples.
    --verbose     : Increase logging verbosity. Use --debug for full debug logs.

"""
        actual = io.getvalue()
        expected = s.format(self.cli_ctx.name)
        self.assertEqual(actual, expected)
示例#7
0
class TestCommandDeprecation(unittest.TestCase):

    def setUp(self):

        from knack.help_files import helps

        class DeprecationTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(DeprecationTestCommandLoader, self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('cmd1', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd1'))
                    g.command('cmd2', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd2', hide='1.0.0'))
                    g.command('cmd3', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd3', hide='0.1.0'))
                    g.command('cmd4', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd4', expiration='1.0.0'))
                    g.command('cmd5', 'example_handler', deprecate_info=g.deprecate(redirect='alt-cmd5', expiration='0.1.0'))

                with CommandGroup(self, 'grp1', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-grp1')) as g:
                    g.command('cmd1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, '') as c:
                    c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3])
                    c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c'])

                super(DeprecationTestCommandLoader, self).load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=DeprecationTestCommandLoader)

    @redirect_io
    def test_deprecate_command_group_help(self):
        """ Ensure deprecated commands appear (or don't appear) correctly in group help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('-h'.split())
        actual = self.io.getvalue()
        expected = u"""
Group
    {}

Subgroups:
    grp1 [Deprecated] : A group.

Commands:
    cmd1 [Deprecated] : Short summary here.
    cmd2 [Deprecated] : Short summary here.
    cmd4 [Deprecated] : Short summary here.

""".format(self.cli_ctx.name)
        self.assertEqual(expected, actual)

    @redirect_io
    def test_deprecate_command_help_hidden(self):
        """ Ensure hidden deprecated command can be used. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('cmd3 -h'.split())
        actual = self.io.getvalue()
        expected = """
Command
    {} cmd3 : Short summary here.
        Long summary here. Still long summary.
        This command has been deprecated and will be removed in a future release. Use 'alt-
        cmd3' instead.

Arguments
    -b      [Required] : Allowed values: a, b, c.
    --arg -a           : Allowed values: 1, 2, 3.
    --arg3
""".format(self.cli_ctx.name)
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_command_plain_execute(self):
        """ Ensure general warning displayed when running deprecated command. """
        self.cli_ctx.invoke('cmd1 -b b'.split())
        actual = self.io.getvalue()
        expected = "This command has been deprecated and will be removed in a future release. Use 'alt-cmd1' instead."
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_command_hidden_execute(self):
        """ Ensure general warning displayed when running hidden deprecated command. """
        self.cli_ctx.invoke('cmd3 -b b'.split())
        actual = self.io.getvalue()
        expected = "This command has been deprecated and will be removed in a future release. Use 'alt-cmd3' instead."
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_command_expiring_execute(self):
        """ Ensure specific warning displayed when running expiring deprecated command. """
        self.cli_ctx.invoke('cmd4 -b b'.split())
        actual = self.io.getvalue()
        expected = "This command has been deprecated and will be removed in version '1.0.0'. Use 'alt-cmd4' instead."
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_command_expiring_execute_no_color(self):
        """ Ensure warning is displayed without color. """
        self.cli_ctx.enable_color = False
        self.cli_ctx.invoke('cmd4 -b b'.split())
        actual = self.io.getvalue()
        expected = "WARNING: This command has been deprecated and will be removed in version '1.0.0'"
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_command_expired_execute(self):
        """ Ensure expired command cannot be reached. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('cmd5 -h'.split())
        actual = self.io.getvalue()
        expected = """cli: 'cmd5' is not in the 'cli' command group."""
        self.assertIn(expected, actual)

    @redirect_io
    @disable_color
    def test_deprecate_command_expired_execute_no_color(self):
        """ Ensure error is displayed without color. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('cmd5 -h'.split())
        actual = self.io.getvalue()
        expected = """ERROR: cli: 'cmd5' is not in the 'cli' command group."""
        self.assertIn(expected, actual)
示例#8
0
class TestArgumentDeprecation(unittest.TestCase):

    def setUp(self):

        from knack.help_files import helps

        class DeprecationTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(DeprecationTestCommandLoader, self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('arg-test', 'example_arg_handler')
                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, 'arg-test') as c:
                    c.argument('arg1', help='Arg1', deprecate_info=c.deprecate())
                    c.argument('opt1', help='Opt1', options_list=['--opt1', c.deprecate(redirect='--opt1', target='--alt1')])
                    c.argument('arg2', help='Arg2', deprecate_info=c.deprecate(hide='1.0.0'))
                    c.argument('opt2', help='Opt2', options_list=['--opt2', c.deprecate(redirect='--opt2', target='--alt2', hide='1.0.0')])
                    c.argument('arg3', help='Arg3', deprecate_info=c.deprecate(hide='0.1.0'))
                    c.argument('opt3', help='Opt3', options_list=['--opt3', c.deprecate(redirect='--opt3', target='--alt3', hide='0.1.0')])
                    c.argument('arg4', deprecate_info=c.deprecate(expiration='1.0.0'))
                    c.argument('opt4', options_list=['--opt4', c.deprecate(redirect='--opt4', target='--alt4', expiration='1.0.0')])
                    c.argument('arg5', deprecate_info=c.deprecate(expiration='0.1.0'))
                    c.argument('opt5', options_list=['--opt5', c.deprecate(redirect='--opt5', target='--alt5', expiration='0.1.0')])

                super(DeprecationTestCommandLoader, self).load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=DeprecationTestCommandLoader)

    @redirect_io
    def test_deprecate_arguments_command_help(self):
        """ Ensure deprecated arguments and options appear (or don't appear)
        correctly in command help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('arg-test -h'.split())
        actual = self.io.getvalue()
        expected = """
Command
    {} arg-test

Arguments
    --alt1 [Deprecated] [Required] : Opt1.
        Option '--alt1' has been deprecated and will be removed in a future release. Use '--
        opt1' instead.
    --arg1 [Deprecated] [Required] : Arg1.
        Argument 'arg1' has been deprecated and will be removed in a future release.
    --opt1              [Required] : Opt1.
    --alt2            [Deprecated] : Opt2.
        Option '--alt2' has been deprecated and will be removed in a future release. Use '--
        opt2' instead.
    --alt4            [Deprecated]
        Option '--alt4' has been deprecated and will be removed in version '1.0.0'. Use '--
        opt4' instead.
    --arg2            [Deprecated] : Arg2.
        Argument 'arg2' has been deprecated and will be removed in a future release.
    --arg4            [Deprecated]
        Argument 'arg4' has been deprecated and will be removed in version '1.0.0'.
    --opt2                         : Opt2.
    --opt3                         : Opt3.
    --opt4
    --opt5
""".format(self.cli_ctx.name)
        self.assertTrue(actual.startswith(expected))

    @redirect_io
    def test_deprecate_arguments_execute(self):
        """ Ensure deprecated arguments can be used. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split())
        actual = self.io.getvalue()
        expected = "Argument 'arg1' has been deprecated and will be removed in a future release."
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_arguments_execute_hidden(self):
        """ Ensure hidden deprecated arguments can be used. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --arg3 bar'.split())
        actual = self.io.getvalue()
        expected = "Argument 'arg3' has been deprecated and will be removed in a future release."
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_arguments_execute_expiring(self):
        """ Ensure hidden deprecated arguments can be used. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --arg4 bar'.split())
        actual = self.io.getvalue()
        expected = "Argument 'arg4' has been deprecated and will be removed in version '1.0.0'."
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_arguments_execute_expired(self):
        """ Ensure expired deprecated arguments can't be used. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --arg5 foo'.split())
        actual = self.io.getvalue()
        expected = 'unrecognized arguments: --arg5 foo'
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_options_execute(self):
        """ Ensure deprecated options can be used with a warning. """
        self.cli_ctx.invoke('arg-test --arg1 foo --alt1 bar'.split())
        actual = self.io.getvalue()
        expected = "Option '--alt1' has been deprecated and will be removed in a future release. Use '--opt1' instead."
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_options_execute_non_deprecated(self):
        """ Ensure non-deprecated options don't show warning. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split())
        actual = self.io.getvalue()
        expected = "Option '--alt1' has been deprecated and will be removed in a future release. Use '--opt1' instead."
        self.assertNotIn(expected, actual)

    @redirect_io
    def test_deprecate_options_execute_hidden(self):
        """ Ensure hidden deprecated options can be used with warning. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --alt3 bar'.split())
        actual = self.io.getvalue()
        expected = "Option '--alt3' has been deprecated and will be removed in a future release. Use '--opt3' instead."
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_options_execute_hidden_non_deprecated(self):
        """ Ensure hidden non-deprecated optionss can be used without warning. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --opt3 bar'.split())
        actual = self.io.getvalue()
        expected = "Option '--alt3' has been deprecated and will be removed in a future release. Use '--opt3' instead."
        self.assertNotIn(expected, actual)

    @redirect_io
    def test_deprecate_options_execute_expired(self):
        """ Ensure expired deprecated options can't be used. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --alt5 foo'.split())
        actual = self.io.getvalue()
        expected = 'unrecognized arguments: --alt5 foo'
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_options_execute_expired_non_deprecated(self):
        """ Ensure non-expired options can be used without warning. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --opt5 foo'.split())
        actual = self.io.getvalue()
        self.assertTrue(u'--alt5' not in actual and u'--opt5' not in actual)

    @redirect_io
    def test_deprecate_options_execute_expiring(self):
        """ Ensure expiring options can be used with warning. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --alt4 bar'.split())
        actual = self.io.getvalue()
        expected = "Option '--alt4' has been deprecated and will be removed in version '1.0.0'. Use '--opt4' instead."
        self.assertIn(expected, actual)

    @redirect_io
    @disable_color
    def test_deprecate_options_execute_expiring_no_color(self):
        """ Ensure error is displayed without color. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --alt4 bar'.split())
        actual = self.io.getvalue()
        expected = "WARNING: Option '--alt4' has been deprecated and will be removed in version '1.0.0'. Use '--opt4' instead."
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_options_execute_expiring_non_deprecated(self):
        """ Ensure non-expiring options can be used without warning. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar --opt4 bar'.split())
        actual = self.io.getvalue()
        expected = "Option '--alt4' has been deprecated and will be removed in version '1.0.0'. Use '--opt4' instead."
        self.assertNotIn(expected, actual)
示例#9
0
class TestCommandGroupDeprecation(unittest.TestCase):

    def setUp(self):

        from knack.help_files import helps

        class DeprecationTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(DeprecationTestCommandLoader, self).load_command_table(args)

                with CommandGroup(self, 'group1', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group1')) as g:
                    g.command('cmd1', 'example_handler')

                with CommandGroup(self, 'group2', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group2', hide='1.0.0')) as g:
                    g.command('cmd1', 'example_handler')

                with CommandGroup(self, 'group3', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group3', hide='0.1.0')) as g:
                    g.command('cmd1', 'example_handler')

                with CommandGroup(self, 'group4', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group4', expiration='1.0.0')) as g:
                    g.command('cmd1', 'example_handler')

                with CommandGroup(self, 'group5', '{}#{{}}'.format(__name__), deprecate_info=self.deprecate(redirect='alt-group5', expiration='0.1.0')) as g:
                    g.command('cmd1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, '') as c:
                    c.argument('arg1', options_list=['--arg', '-a'], required=False, type=int, choices=[1, 2, 3])
                    c.argument('arg2', options_list=['-b'], required=True, choices=['a', 'b', 'c'])

                super(DeprecationTestCommandLoader, self).load_arguments(command)

        helps['group1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=DeprecationTestCommandLoader)

    @redirect_io
    def test_deprecate_command_group_help_plain(self):
        """ Ensure help warnings appear for deprecated command group help. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group1 -h'.split())
        actual = self.io.getvalue()
        expected = """
Group
    cli group1 : A group.
        This command group has been deprecated and will be removed in a future release. Use
        'alt-group1' instead.

Commands:
    cmd1 : Short summary here.

""".format(self.cli_ctx.name)
        self.assertEqual(expected, actual)

    @redirect_io
    def test_deprecate_command_group_help_hidden(self):
        """ Ensure hidden deprecated command can be used. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group3 -h'.split())
        actual = self.io.getvalue()
        expected = """
Group
    {} group3
        This command group has been deprecated and will be removed in a future release. Use
        'alt-group3' instead.

Commands:
    cmd1 : Short summary here.

""".format(self.cli_ctx.name)
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_command_group_help_expiring(self):
        """ Ensure specific warning displayed when running expiring deprecated command. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group4 -h'.split())
        actual = self.io.getvalue()
        expected = """
Group
    {} group4
        This command group has been deprecated and will be removed in version '1.0.0'. Use
        'alt-group4' instead.
""".format(self.cli_ctx.name)
        self.assertIn(expected, actual)

    @redirect_io
    @disable_color
    def test_deprecate_command_group_help_expiring_no_color(self):
        """ Ensure warning is displayed without color. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group4 -h'.split())
        actual = self.io.getvalue()
        expected = """
Group
    cli group4
        WARNING: This command group has been deprecated and will be removed in version \'1.0.0\'. Use
        'alt-group4' instead.
""".format(self.cli_ctx.name)
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_command_group_expired(self):
        """ Ensure expired command cannot be reached. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group5 -h'.split())
        actual = self.io.getvalue()
        expected = """The most similar choices to 'group5'"""
        self.assertIn(expected, actual)

    @redirect_io
    def test_deprecate_command_implicitly(self):
        """ Ensure help warning displayed for command deprecated because of a deprecated parent group. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group1 cmd1 -h'.split())
        actual = self.io.getvalue()
        expected = """
Command
    {} group1 cmd1 : Short summary here.
        Long summary here. Still long summary.
        This command is implicitly deprecated because command group 'group1' is deprecated and
        will be removed in a future release. Use 'alt-group1' instead.
""".format(self.cli_ctx.name)
        self.assertIn(expected, actual)
示例#10
0
class TestCommandExperimental(unittest.TestCase):
    def setUp(self):

        from knack.help_files import helps

        class ExperimentalTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(ExperimentalTestCommandLoader,
                      self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('cmd1', 'example_handler', is_experimental=True)

                with CommandGroup(self,
                                  'grp1',
                                  '{}#{{}}'.format(__name__),
                                  is_experimental=True) as g:
                    g.command('cmd1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, '') as c:
                    c.argument('arg1',
                               options_list=['--arg', '-a'],
                               required=False,
                               type=int,
                               choices=[1, 2, 3])
                    c.argument('arg2',
                               options_list=['-b'],
                               required=True,
                               choices=['a', 'b', 'c'])

                super(ExperimentalTestCommandLoader,
                      self).load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(
            commands_loader_cls=ExperimentalTestCommandLoader)

    @redirect_io
    def test_experimental_command_implicitly_execute(self):
        """ Ensure general warning displayed when running command from an experimental parent group. """
        self.cli_ctx.invoke('grp1 cmd1 -b b'.split())
        actual = self.io.getvalue()
        expected = "Command group 'grp1' is experimental and not covered by customer support. " \
                   "Please use with discretion."
        self.assertIn(remove_space(expected), remove_space(actual))

    @redirect_io
    def test_experimental_command_group_help(self):
        """ Ensure experimental commands appear correctly in group help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('-h'.split())
        actual = self.io.getvalue()
        expected = u"""
Group
    {}

Subgroups:
    grp1 [Experimental] : A group.

Commands:
    cmd1 [Experimental] : Short summary here.

""".format(self.cli_ctx.name)
        self.assertEqual(expected, actual)

    @redirect_io
    def test_experimental_command_plain_execute(self):
        """ Ensure general warning displayed when running experimental command. """
        self.cli_ctx.invoke('cmd1 -b b'.split())
        actual = self.io.getvalue()
        expected = "This command is experimental and not covered by customer support. Please use with discretion."
        self.assertIn(remove_space(expected), remove_space(actual))
示例#11
0
class TestCommandGroupExperimental(unittest.TestCase):
    def setUp(self):

        from knack.help_files import helps

        class ExperimentalTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(ExperimentalTestCommandLoader,
                      self).load_command_table(args)

                with CommandGroup(self,
                                  'group1',
                                  '{}#{{}}'.format(__name__),
                                  is_experimental=True) as g:
                    g.command('cmd1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, '') as c:
                    c.argument('arg1',
                               options_list=['--arg', '-a'],
                               required=False,
                               type=int,
                               choices=[1, 2, 3])
                    c.argument('arg2',
                               options_list=['-b'],
                               required=True,
                               choices=['a', 'b', 'c'])

                super(ExperimentalTestCommandLoader,
                      self).load_arguments(command)

        helps['group1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(
            commands_loader_cls=ExperimentalTestCommandLoader)

    @redirect_io
    def test_experimental_command_group_help_plain(self):
        """ Ensure help warnings appear for experimental command group help. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group1 -h'.split())
        actual = self.io.getvalue()
        expected = """
Group
    cli group1 : A group.
        This command group is experimental and not covered by customer support. Please use with discretion.
Commands:
    cmd1 : Short summary here.

""".format(self.cli_ctx.name)
        self.assertIn(remove_space(expected), remove_space(actual))

    @redirect_io
    def test_experimental_command_implicitly(self):
        """ Ensure help warning displayed for command in experimental because of a experimental parent group. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('group1 cmd1 -h'.split())
        actual = self.io.getvalue()
        expected = """
Command
    {} group1 cmd1 : Short summary here.
        Long summary here. Still long summary.
        Command group 'group1' is experimental and not covered by customer support. Please use with discretion.
""".format(self.cli_ctx.name)
        self.assertIn(remove_space(expected), remove_space(actual))
示例#12
0
class TestArgumentPreview(unittest.TestCase):
    def setUp(self):
        from knack.help_files import helps

        class LoggerAction(argparse.Action):
            def __call__(self, parser, namespace, values, option_string=None):
                print("Side-effect from some original action!",
                      file=sys.stderr)

        class PreviewTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super(PreviewTestCommandLoader, self).load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('arg-test', 'example_arg_handler')
                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, 'arg-test') as c:
                    c.argument('arg1',
                               help='Arg1',
                               is_preview=True,
                               action=LoggerAction)

                super(PreviewTestCommandLoader, self).load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader)

    @redirect_io
    def test_preview_arguments_command_help(self):
        """ Ensure preview arguments appear correctly in command help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('arg-test -h'.split())
        actual = self.io.getvalue()
        expected = """
Arguments
    --arg1 [Preview] [Required] : Arg1.
        Argument '--arg1' is in preview. It may be changed/removed in a future release.
""".format(self.cli_ctx.name)
        self.assertIn(expected, actual)

    @redirect_io
    @disable_color
    def test_preview_arguments_command_help_no_color(self):
        """ Ensure warning is displayed without color. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('arg-test -h'.split())
        actual = self.io.getvalue()
        expected = """
Arguments
    --arg1 [Preview] [Required] : Arg1.
        WARNING: Argument '--arg1' is in preview. It may be changed/removed in a future release.
""".format(self.cli_ctx.name)
        self.assertIn(expected, actual)

    @redirect_io
    def test_preview_arguments_execute(self):
        """ Ensure deprecated arguments can be used. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split())
        actual = self.io.getvalue()
        preview_expected = "Argument '--arg1' is in preview. It may be changed/removed in a future release."
        self.assertIn(preview_expected, actual)

        action_expected = "Side-effect from some original action!"
        self.assertIn(action_expected, actual)

    @redirect_io
    @disable_color
    def test_preview_arguments_execute_no_color(self):
        """ Ensure warning is displayed without color. """
        self.cli_ctx.invoke('arg-test --arg1 foo --opt1 bar'.split())
        actual = self.io.getvalue()
        preview_expected = "WARNING: Argument '--arg1' is in preview. It may be changed/removed in a future release."
        self.assertIn(preview_expected, actual)

        action_expected = "Side-effect from some original action!"
        self.assertIn(action_expected, actual)

    @redirect_io
    def test_preview_arguments_execute_only_show_error(self):
        """ Ensure warning is suppressed when using preview arguments. """
        self.cli_ctx.invoke(
            'arg-test --arg1 foo --opt1 bar --only-show-errors'.split())
        actual = self.io.getvalue()
        self.assertNotIn("preview", actual)

        action_expected = "Side-effect from some original action!"
        self.assertIn(action_expected, actual)
示例#13
0
class TestCommandPreview(unittest.TestCase):
    def setUp(self):

        from knack.help_files import helps

        class PreviewTestCommandLoader(CLICommandsLoader):
            def load_command_table(self, args):
                super().load_command_table(args)
                with CommandGroup(self, '', '{}#{{}}'.format(__name__)) as g:
                    g.command('cmd1', 'example_handler', is_preview=True)

                with CommandGroup(self,
                                  'grp1',
                                  '{}#{{}}'.format(__name__),
                                  is_preview=True) as g:
                    g.command('cmd1', 'example_handler')

                return self.command_table

            def load_arguments(self, command):
                with ArgumentsContext(self, '') as c:
                    c.argument('arg1',
                               options_list=['--arg', '-a'],
                               required=False,
                               type=int,
                               choices=[1, 2, 3])
                    c.argument('arg2',
                               options_list=['-b'],
                               required=True,
                               choices=['a', 'b', 'c'])

                super().load_arguments(command)

        helps['grp1'] = """
    type: group
    short-summary: A group.
"""
        self.cli_ctx = DummyCLI(commands_loader_cls=PreviewTestCommandLoader)

    @redirect_io
    def test_preview_command_group_help(self):
        """ Ensure preview commands appear correctly in group help view. """
        with self.assertRaises(SystemExit):
            self.cli_ctx.invoke('-h'.split())
        actual = self.io.getvalue()
        expected = u"""
Group
    {}

Subgroups:
    grp1 [Preview] : A group.

Commands:
    cmd1 [Preview] : Short summary here.

""".format(self.cli_ctx.name)
        self.assertEqual(expected, actual)

    @redirect_io
    def test_preview_command_plain_execute(self):
        """ Ensure general warning displayed when running preview command. """
        self.cli_ctx.invoke('cmd1 -b b'.split())
        actual = self.io.getvalue()
        expected = "This command is in preview. It may be changed/removed in a future release."
        self.assertIn(expected, actual)

    @redirect_io
    def test_preview_command_plain_execute_only_show_error(self):
        """ Ensure warning is suppressed when running preview command. """
        # Directly use --only-show-errors
        self.cli_ctx.invoke('cmd1 -b b --only-show-errors'.split())
        actual = self.io.getvalue()
        self.assertNotIn("preview", actual)

        # Apply --only-show-errors with config
        self.cli_ctx.only_show_errors = True
        self.cli_ctx.config.set_value('core', 'only_show_errors', 'True')
        self.cli_ctx.invoke('cmd1 -b b'.split())
        actual = self.io.getvalue()
        self.assertNotIn("preview", actual)
        self.cli_ctx.config.set_value('core', 'only_show_errors', '')
        self.cli_ctx.only_show_errors = False

    @redirect_io
    @disable_color
    def test_preview_command_plain_execute_no_color(self):
        """ Ensure warning is displayed without color. """
        self.cli_ctx.invoke('cmd1 -b b'.split())
        actual = self.io.getvalue()
        self.assertIn(
            "WARNING: This command is in preview. It may be changed/removed in a future release.",
            actual)

    @redirect_io
    def test_preview_command_implicitly_execute(self):
        """ Ensure general warning displayed when running command from a preview parent group. """
        self.cli_ctx.invoke('grp1 cmd1 -b b'.split())
        actual = self.io.getvalue()
        expected = "Command group 'grp1' is in preview. It may be changed/removed in a future release."
        self.assertIn(expected, actual)

    @redirect_io
    @disable_color
    def test_preview_command_implicitly_no_color(self):
        """ Ensure warning is displayed without color. """
        self.cli_ctx.invoke('grp1 cmd1 -b b'.split())
        actual = self.io.getvalue()
        expected = "WARNING: Command group 'grp1' is in preview. It may be changed/removed in a future release."
        self.assertIn(expected, actual)