1 # -*- coding: utf-8 -*-
2 # Copyright (C) 2012-2021 by the Free Software Foundation, Inc.
4 # This file is part of Postorius.
6 # Postorius is free software: you can redistribute it and/or modify it under
7 # the terms of the GNU General Public License as published by the Free
8 # Software Foundation, either version 3 of the License, or (at your option)
10 # Postorius is distributed in the hope that it will be useful, but WITHOUT
11 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 # You should have received a copy of the GNU General Public License along with
16 # Postorius. If not, see <http://www.gnu.org/licenses/>.
20 from unittest
.mock
import MagicMock
22 from django
.conf
import settings
23 from django
.contrib
import messages
24 from django
.test
import TransactionTestCase
25 from django
.urls
import reverse
27 from django_mailman3
.lib
.mailman
import get_mailman_client
28 from django_mailman3
.tests
.utils
import get_flash_messages
29 from six
import PY3
, binary_type
, text_type
30 from six
.moves
.urllib_parse
import (
31 parse_qsl
, quote
, urlencode
, urlparse
, urlunparse
)
34 def get_test_file(*fileparts
):
35 return os
.path
.join(os
.path
.dirname(__file__
), "test_data", *fileparts
)
36 get_test_file
.__test
__ = False # noqa: E305
39 def reorder_request_params(request
):
40 def reorder_params(params
):
43 if isinstance(params
, binary_type
):
44 params
= params
.decode("ascii")
45 parsed
= parse_qsl(params
, encoding
="utf-8")
47 parsed
= parse_qsl(params
)
49 return urlencode(sorted(parsed
, key
=lambda kv
: kv
[0]))
51 # Parsing failed, it may be a simple string.
53 # sort the URL query-string by key names.
54 uri_parts
= urlparse(request
.uri
)
56 request
.uri
= urlunparse((
57 uri_parts
.scheme
, uri_parts
.netloc
, uri_parts
.path
,
58 uri_parts
.params
, reorder_params(uri_parts
.query
),
61 # convert the request body to text and sort the parameters.
62 if isinstance(request
.body
, binary_type
):
64 request
._body
= request
._body
.decode('utf-8')
65 except UnicodeDecodeError:
67 if isinstance(request
.body
, text_type
):
68 request
._body
= reorder_params(request
._body
.encode('utf-8'))
72 def filter_response_headers(response
):
73 for header
in ('Date', 'Server', 'date', 'server'):
74 # The headers are lowercase on Python 2 and capitalized on Python 3
75 if header
in response
['headers']:
76 del response
['headers'][header
]
80 def create_mock_domain(properties
=None):
81 """Create and return a mocked Domain.
83 :param properties: A dictionary of the domain's properties.
84 :type properties: dict
85 :return: A MagicMock object with the properties set.
88 mock_object
= MagicMock(name
='Domain')
89 mock_object
.contact_address
= ''
90 mock_object
.description
= ''
91 mock_object
.mail_host
= ''
92 mock_object
.lists
= []
93 if properties
is not None:
94 for key
in properties
:
95 setattr(mock_object
, key
, properties
[key
])
99 def create_mock_list(properties
=None):
100 """Create and return a mocked List.
102 :param properties: A dictionary of the list's properties.
103 :type properties: dict
104 :return: A MagicMock object with the properties set.
107 mock_object
= MagicMock(name
='List')
108 mock_object
.members
= []
109 mock_object
.moderators
= []
110 mock_object
.owners
= []
111 # like in mock_domain, some defaults need to be added...
112 if properties
is not None:
113 for key
in properties
:
114 setattr(mock_object
, key
, properties
[key
])
118 def create_mock_member(properties
=None):
119 """Create and return a mocked Member.
121 :param properties: A dictionary of the member's properties.
122 :type properties: dict
123 :return: A MagicMock object with the properties set.
126 mock_object
= MagicMock(name
='Member')
127 # like in mock_domain, some defaults need to be added...
128 if properties
is not None:
129 for key
in properties
:
130 setattr(mock_object
, key
, properties
[key
])
134 class ViewTestCase(TransactionTestCase
):
137 self
.mm_client
= get_mailman_client()
140 for d
in self
.mm_client
.domains
:
142 for u
in self
.mm_client
.users
:
145 def assertHasSuccessMessage(self
, response
, count
=1):
146 msgs
= get_flash_messages(response
)
147 self
.assertEqual(len(msgs
), count
)
149 msgs
[0].level
, messages
.SUCCESS
,
150 "%s: %s" % (messages
.DEFAULT_TAGS
[msgs
[0].level
], msgs
[0].message
))
151 return msgs
[0].message
153 def assertHasErrorMessage(self
, response
):
154 msgs
= get_flash_messages(response
)
155 self
.assertEqual(len(msgs
), 1)
157 msgs
[0].level
, messages
.ERROR
,
158 "%s: %s" % (messages
.DEFAULT_TAGS
[msgs
[0].level
], msgs
[0].message
))
159 return msgs
[0].message
161 def assertHasNoMessage(self
, response
):
162 msgs
= get_flash_messages(response
)
163 self
.assertEqual(len(msgs
), 0)
165 def assertRedirectsToLogin(self
, url
):
166 response
= self
.client
.get(url
)
167 self
.assertRedirects(response
, '{}?next={}'.format(
168 reverse(settings
.LOGIN_URL
), quote(url
)))
171 def write_response(cls
, response
):
172 """Print the response in a tmp file and open it in browser."""
173 tmp
= tempfile
.NamedTemporaryFile(suffix
='.html', delete
=False)
174 tmp
.write(response
.content
)
176 print(f
'Wrote response to file://{tmp.name}')