mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	Note that I don't actually convert the checker from check_dict to check_dict_only, because that would be a user-facing change, but I think we can sweep a lot of things like this after the next release.
		
			
				
	
	
		
			232 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
		
			7.6 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
import re
 | 
						|
from typing import Any, Dict
 | 
						|
 | 
						|
import ujson
 | 
						|
from django.core.exceptions import ValidationError
 | 
						|
 | 
						|
from zerver.lib.test_classes import ZulipTestCase
 | 
						|
from zerver.lib.validator import check_widget_content
 | 
						|
from zerver.lib.widget import get_widget_data
 | 
						|
from zerver.models import SubMessage
 | 
						|
 | 
						|
 | 
						|
class WidgetContentTestCase(ZulipTestCase):
 | 
						|
    def test_validation(self) -> None:
 | 
						|
        def assert_error(obj: object, msg: str) -> None:
 | 
						|
            with self.assertRaisesRegex(ValidationError, re.escape(msg)):
 | 
						|
                check_widget_content(obj)
 | 
						|
 | 
						|
        assert_error(5,
 | 
						|
                     'widget_content is not a dict')
 | 
						|
 | 
						|
        assert_error({},
 | 
						|
                     'widget_type is not in widget_content')
 | 
						|
 | 
						|
        assert_error(dict(widget_type='whatever'),
 | 
						|
                     'extra_data is not in widget_content')
 | 
						|
 | 
						|
        assert_error(dict(widget_type='zform', extra_data=4),
 | 
						|
                     'extra_data is not a dict')
 | 
						|
 | 
						|
        assert_error(dict(widget_type='bogus', extra_data={}),
 | 
						|
                     'unknown widget type: bogus')
 | 
						|
 | 
						|
        extra_data: Dict[str, Any] = dict()
 | 
						|
        obj = dict(widget_type='zform', extra_data=extra_data)
 | 
						|
 | 
						|
        assert_error(obj, 'zform is missing type field')
 | 
						|
 | 
						|
        extra_data['type'] = 'bogus'
 | 
						|
        assert_error(obj, 'unknown zform type: bogus')
 | 
						|
 | 
						|
        extra_data['type'] = 'choices'
 | 
						|
        assert_error(obj, 'heading key is missing from extra_data')
 | 
						|
 | 
						|
        extra_data['heading'] = 'whatever'
 | 
						|
        assert_error(obj, 'choices key is missing from extra_data')
 | 
						|
 | 
						|
        extra_data['choices'] = 99
 | 
						|
        assert_error(obj, 'extra_data["choices"] is not a list')
 | 
						|
 | 
						|
        extra_data['choices'] = [99]
 | 
						|
        assert_error(obj, 'extra_data["choices"][0] is not a dict')
 | 
						|
 | 
						|
        extra_data['choices'] = [
 | 
						|
            dict(long_name='foo', reply='bar'),
 | 
						|
        ]
 | 
						|
        assert_error(obj, 'short_name key is missing from extra_data["choices"][0]')
 | 
						|
 | 
						|
        extra_data['choices'] = [
 | 
						|
            dict(short_name='a', long_name='foo', reply='bar'),
 | 
						|
        ]
 | 
						|
 | 
						|
        check_widget_content(obj)
 | 
						|
 | 
						|
    def test_message_error_handling(self) -> None:
 | 
						|
        sender = self.example_user('cordelia')
 | 
						|
        stream_name = 'Verona'
 | 
						|
 | 
						|
        payload = dict(
 | 
						|
            type="stream",
 | 
						|
            to=stream_name,
 | 
						|
            client='test suite',
 | 
						|
            topic='whatever',
 | 
						|
            content='whatever',
 | 
						|
        )
 | 
						|
 | 
						|
        payload['widget_content'] = '{{{{{{'  # unparsable
 | 
						|
        result = self.api_post(sender, "/api/v1/messages", payload)
 | 
						|
        self.assert_json_error_contains(result, 'Widgets: API programmer sent invalid JSON')
 | 
						|
 | 
						|
        bogus_data = dict(color='red', foo='bar', x=2)
 | 
						|
        payload['widget_content'] = ujson.dumps(bogus_data)
 | 
						|
        result = self.api_post(sender, "/api/v1/messages", payload)
 | 
						|
        self.assert_json_error_contains(result, 'Widgets: widget_type is not in widget_content')
 | 
						|
 | 
						|
    def test_get_widget_data_for_non_widget_messages(self) -> None:
 | 
						|
        # This is a pretty important test, despite testing the
 | 
						|
        # "negative" case.  We never want widgets to interfere
 | 
						|
        # with normal messages.
 | 
						|
 | 
						|
        test_messages = [
 | 
						|
            '',
 | 
						|
            '     ',
 | 
						|
            'this is an ordinary message',
 | 
						|
            '/bogus_command',
 | 
						|
            '/me shrugs',
 | 
						|
            'use /poll',
 | 
						|
        ]
 | 
						|
 | 
						|
        for message in test_messages:
 | 
						|
            self.assertEqual(get_widget_data(content=message), (None, None))
 | 
						|
 | 
						|
        # Add a positive check for context
 | 
						|
        self.assertEqual(get_widget_data(content='/tictactoe'), ('tictactoe', None))
 | 
						|
 | 
						|
    def test_explicit_widget_content(self) -> None:
 | 
						|
        # Users can send widget_content directly on messages
 | 
						|
        # using the `widget_content` field.
 | 
						|
 | 
						|
        sender = self.example_user('cordelia')
 | 
						|
        stream_name = 'Verona'
 | 
						|
        content = 'does-not-matter'
 | 
						|
        zform_data = dict(
 | 
						|
            type='choices',
 | 
						|
            heading='Options:',
 | 
						|
            choices=[],
 | 
						|
        )
 | 
						|
 | 
						|
        widget_content = dict(
 | 
						|
            widget_type='zform',
 | 
						|
            extra_data=zform_data,
 | 
						|
        )
 | 
						|
 | 
						|
        check_widget_content(widget_content)
 | 
						|
 | 
						|
        payload = dict(
 | 
						|
            type="stream",
 | 
						|
            to=stream_name,
 | 
						|
            client='test suite',
 | 
						|
            topic='whatever',
 | 
						|
            content=content,
 | 
						|
            widget_content=ujson.dumps(widget_content),
 | 
						|
        )
 | 
						|
        result = self.api_post(sender, "/api/v1/messages", payload)
 | 
						|
        self.assert_json_success(result)
 | 
						|
 | 
						|
        message = self.get_last_message()
 | 
						|
        self.assertEqual(message.content, content)
 | 
						|
 | 
						|
        expected_submessage_content = dict(
 | 
						|
            widget_type="zform",
 | 
						|
            extra_data=zform_data,
 | 
						|
        )
 | 
						|
 | 
						|
        submessage = SubMessage.objects.get(message_id=message.id)
 | 
						|
        self.assertEqual(submessage.msg_type, 'widget')
 | 
						|
        self.assertEqual(ujson.loads(submessage.content), expected_submessage_content)
 | 
						|
 | 
						|
    def test_tictactoe(self) -> None:
 | 
						|
        # The tictactoe widget is mostly useful as a code sample,
 | 
						|
        # and it also helps us get test coverage that could apply
 | 
						|
        # to future widgets.
 | 
						|
 | 
						|
        sender = self.example_user('cordelia')
 | 
						|
        stream_name = 'Verona'
 | 
						|
        content = '/tictactoe'
 | 
						|
 | 
						|
        payload = dict(
 | 
						|
            type="stream",
 | 
						|
            to=stream_name,
 | 
						|
            client='test suite',
 | 
						|
            topic='whatever',
 | 
						|
            content=content,
 | 
						|
        )
 | 
						|
        result = self.api_post(sender, "/api/v1/messages", payload)
 | 
						|
        self.assert_json_success(result)
 | 
						|
 | 
						|
        message = self.get_last_message()
 | 
						|
        self.assertEqual(message.content, content)
 | 
						|
 | 
						|
        expected_submessage_content = dict(
 | 
						|
            widget_type="tictactoe",
 | 
						|
            extra_data=None,
 | 
						|
        )
 | 
						|
 | 
						|
        submessage = SubMessage.objects.get(message_id=message.id)
 | 
						|
        self.assertEqual(submessage.msg_type, 'widget')
 | 
						|
        self.assertEqual(ujson.loads(submessage.content), expected_submessage_content)
 | 
						|
 | 
						|
    def test_poll_command_extra_data(self) -> None:
 | 
						|
        sender = self.example_user('cordelia')
 | 
						|
        stream_name = 'Verona'
 | 
						|
        # We test for both trailing and leading spaces, along with blank lines
 | 
						|
        # for the poll options.
 | 
						|
        content = '/poll What is your favorite color?\n\nRed\nGreen  \n\n   Blue\n - Yellow'
 | 
						|
 | 
						|
        payload = dict(
 | 
						|
            type="stream",
 | 
						|
            to=stream_name,
 | 
						|
            client='test suite',
 | 
						|
            topic='whatever',
 | 
						|
            content=content,
 | 
						|
        )
 | 
						|
        result = self.api_post(sender, "/api/v1/messages", payload)
 | 
						|
        self.assert_json_success(result)
 | 
						|
 | 
						|
        message = self.get_last_message()
 | 
						|
        self.assertEqual(message.content, content)
 | 
						|
 | 
						|
        expected_submessage_content = dict(
 | 
						|
            widget_type="poll",
 | 
						|
            extra_data=dict(
 | 
						|
                options=['Red', 'Green', 'Blue', 'Yellow'],
 | 
						|
                question="What is your favorite color?",
 | 
						|
            ),
 | 
						|
        )
 | 
						|
 | 
						|
        submessage = SubMessage.objects.get(message_id=message.id)
 | 
						|
        self.assertEqual(submessage.msg_type, 'widget')
 | 
						|
        self.assertEqual(ujson.loads(submessage.content), expected_submessage_content)
 | 
						|
 | 
						|
        # Now don't supply a question.
 | 
						|
 | 
						|
        content = '/poll'
 | 
						|
        payload['content'] = content
 | 
						|
        result = self.api_post(sender, "/api/v1/messages", payload)
 | 
						|
        self.assert_json_success(result)
 | 
						|
 | 
						|
        expected_submessage_content = dict(
 | 
						|
            widget_type="poll",
 | 
						|
            extra_data=dict(
 | 
						|
                options=[],
 | 
						|
                question='',
 | 
						|
            ),
 | 
						|
        )
 | 
						|
 | 
						|
        message = self.get_last_message()
 | 
						|
        self.assertEqual(message.content, content)
 | 
						|
        submessage = SubMessage.objects.get(message_id=message.id)
 | 
						|
        self.assertEqual(submessage.msg_type, 'widget')
 | 
						|
        self.assertEqual(ujson.loads(submessage.content), expected_submessage_content)
 |